diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 40838a3e06d4a08b151fde136c9289e409db3196..bc746a53440e0b4dbf0accf53e934e54df9c7c75 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -16,12 +16,11 @@ include:
   - local: '/src/context/.gitlab-ci.yml'
   - local: '/src/device/.gitlab-ci.yml'
   - local: '/src/service/.gitlab-ci.yml'
-  # - local: '/src/dbscanserving/.gitlab-ci.yml'
-  # - local: '/src/opticalattackmitigator/.gitlab-ci.yml'
-  # - local: '/src/opticalcentralizedattackdetector/.gitlab-ci.yml'
-  #- local: '/src/tester_integration/.gitlab-ci.yml'
-  #- local: '/src/tester_functional/.gitlab-ci.yml'
+  - local: '/src/dbscanserving/.gitlab-ci.yml'
+  - local: '/src/opticalattackmitigator/.gitlab-ci.yml'
+  - local: '/src/opticalcentralizedattackdetector/.gitlab-ci.yml'
   - local: '/src/automation/.gitlab-ci.yml'
-  # - local: '/src/l3_distributedattackdetector/.gitlab-ci.yml'
-  # - local: '/src/l3_centralizedattackdetector/.gitlab-ci.yml'
-  # - local: '/src/l3_attackmitigator/.gitlab-ci.yml'
+  - local: '/src/webui/.gitlab-ci.yml'
+  - local: '/src/l3_distributedattackdetector/.gitlab-ci.yml'
+  - local: '/src/l3_centralizedattackdetector/.gitlab-ci.yml'
+  - local: '/src/l3_attackmitigator/.gitlab-ci.yml'
diff --git a/INSTALL.md b/INSTALL.md
index 84e3c80680c3bb0c87ff3254cac85b2c6fdde34c..670af487313498ad60f8c0fc89029218efe29405 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -1,22 +1,4 @@
 # TeraFlow OS SDN Controller Installation Instructions 
-Assuming you have a running Kubernetes deployment installed following the instructions provided in [Wiki: Installing Kubernetes on your Linux machine](../../../wikis/Installing-Kubernetes-on-your-Linux-machine), the following instructions will let you deploy TeraFlow OS SDN Controller in your local Kubernetes environment.
+Assuming you have a running Kubernetes deployment installed following the instructions provided in [Wiki: Installing Kubernetes on your Linux machine](https://gitlab.com/teraflow-h2020/controller/-/wikis/Installing-Kubernetes-on-your-Linux-machine), the following instructions will let you deploy TeraFlow OS SDN Controller in your local Kubernetes environment.
 
-Among others, it is assumed that you can run the command <code> kubectl cluster-info</code> in the machine you use for running the deployment script below.
-
-
-## 1. Clone the repository
-Create a folder (e.g., ~/deploy-test/teraflow/controller) to place the source code, move to that folder, clone the repository, and checkout the target branch you want to deploy (e.g., master or develop):
-
-<code>mkdir -p ~/deploy-test/teraflow/controller; cd ~/deploy-test/teraflow/controller</code>
-
-<code>git clone https://gitlab.com/teraflow-h2020/controller.git .</code>
-
-<code>git checkout develop</code>
-
-
-## 2. Adapt deployment script
-Find the script <code>deploy_to_kubernetes.sh</code> and modify the configuration settings at the top of the script according to your local Kubernetes environment.
-
-
-## 3. Run the deployment script
-Execute the script <code>deploy_to_kubernetes.sh</code> to build and deploy the components at your local Kubernetes environment.
+Then, follow the instructions in [Wiki: Deploying a TeraFlow OS test instance](https://gitlab.com/teraflow-h2020/controller/-/wikis/Deploying-a-TeraFlow-OS-test-instance) to deploy your instance of TeraFlow OS.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..20f197da064d771be1fa7fb37e3fd35ca48da26c
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright 2022 teraflow
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/README.md b/README.md
index 77f581608a78940dd5f7f00f65dcbf7ae1d7fbc6..5670a90c9f95ce5d7290d3c9c884b3c943b4459d 100644
--- a/README.md
+++ b/README.md
@@ -7,4 +7,13 @@ Branch "master" : [![pipeline status](https://gitlab.com/teraflow-h2020/controll
 Branch "develop" : [![pipeline status](https://gitlab.com/teraflow-h2020/controller/badges/develop/pipeline.svg)](https://gitlab.com/teraflow-h2020/controller/-/commits/develop) [![coverage report](https://gitlab.com/teraflow-h2020/controller/badges/develop/coverage.svg)](https://gitlab.com/teraflow-h2020/controller/-/commits/develop)
 
 # Installation Instructions
-To install TeraFlow OS SDN Controller in your local Kubernetes deployment, we assume you deployed Kubernetes following the instructions provided in [Wiki: Installing Kubernetes on your Linux machine](../../../wikis/Installing-Kubernetes-on-your-Linux-machine). Then, follow instructions in [INSTALL.md](./INSTALL.md).
+To install TeraFlow OS SDN Controller in your local Kubernetes deployment, we assume you deployed Kubernetes following the instructions provided in [Wiki: Installing Kubernetes on your Linux machine](https://gitlab.com/teraflow-h2020/controller/-/wikis/Installing-Kubernetes-on-your-Linux-machine).
+
+Then, follow the instructions in [Wiki: Deploying a TeraFlow OS test instance](https://gitlab.com/teraflow-h2020/controller/-/wikis/Deploying-a-TeraFlow-OS-test-instance) to deploy your instance of TeraFlow OS.
+
+# Functional Tests
+A functional test has been defined to enable experimentation with the TeraFlow OS:
+
+__Important:__ The OpenConfigDriver, the P4Driver, and the TrandportApiDriver have to be considered as experimental. The configuration and monitoring capabilities they support are limited or partially implemented. Use them with care.
+
+[Demo at OFC'22 (Bootstrap devices, Manage L3VPN services, Monitor Device Endpoints)](./src/tests/ofc22)
diff --git a/clean_testing_environment.sh b/clean_testing_environment.sh
index 2cec5c5a4d290bf6fd5f314c4f37d2d7db0232ae..09bff95d82fe37a86d3a7c6569c6e3d0a00d64db 100755
--- a/clean_testing_environment.sh
+++ b/clean_testing_environment.sh
@@ -1,4 +1,18 @@
 #!/usr/bin/env bash
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 
 find . -iname __pycache__ | xargs -r rm -rf
 find . -iname .benchmarks | xargs -r rm -rf
diff --git a/configure_dashboards.sh b/configure_dashboards.sh
new file mode 100755
index 0000000000000000000000000000000000000000..4a32d76deb79bb514b6d547c0e3e2b87ec269e77
--- /dev/null
+++ b/configure_dashboards.sh
@@ -0,0 +1,68 @@
+#!/bin/bash
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+# If not already set, set the name of the Kubernetes namespace and hostname to deploy to.
+K8S_NAMESPACE=${K8S_NAMESPACE:-'tf-dev'}
+K8S_HOSTNAME=${K8S_HOSTNAME:-'kubernetes-master'}
+
+INFLUXDB_USER=$(kubectl --namespace $K8S_NAMESPACE get secrets influxdb-secrets -o jsonpath='{.data.INFLUXDB_ADMIN_USER}' | base64 --decode)
+INFLUXDB_PASSWORD=$(kubectl --namespace $K8S_NAMESPACE get secrets influxdb-secrets -o jsonpath='{.data.INFLUXDB_ADMIN_PASSWORD}' | base64 --decode)
+INFLUXDB_DATABASE=$(kubectl --namespace $K8S_NAMESPACE get secrets influxdb-secrets -o jsonpath='{.data.INFLUXDB_DB}' | base64 --decode)
+
+# GRAFANA_HOSTNAME=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}')
+# GRAFANA_HOSTNAME=`kubectl get service/webuiservice-public -n ${K8S_NAMESPACE} -o jsonpath='{.spec.clusterIP}'`
+GRAFANA_HOSTNAME=`kubectl get nodes --selector=node-role.kubernetes.io/master -o jsonpath='{$.items[*].status.addresses[?(@.type=="InternalIP")].address}'`
+GRAFANA_PORT=$(kubectl get service webuiservice-public --namespace $K8S_NAMESPACE -o 'jsonpath={.spec.ports[?(@.port==3000)].nodePort}')
+#GRAFANA_PORT=`kubectl get service/webuiservice-public -n ${K8S_NAMESPACE} -o jsonpath='{.spec.ports[1].nodePort}'`
+GRAFANA_USERNAME="admin"
+GRAFANA_PASSWORD=${GRAFANA_PASSWORD:-"admin123+"}
+GRAFANA_URL="http://${GRAFANA_USERNAME}:${GRAFANA_PASSWORD}@${GRAFANA_HOSTNAME}:${GRAFANA_PORT}"
+echo "Connecting to grafana at URL: ${GRAFANA_URL}..."
+
+# Configure Grafana Admin Password
+# Ref: https://grafana.com/docs/grafana/latest/http_api/user/#change-password
+curl -X PUT -H "Content-Type: application/json" -d '{
+  "oldPassword": "admin",
+  "newPassword": "'${GRAFANA_PASSWORD}'",
+  "confirmNew" : "'${GRAFANA_PASSWORD}'"
+}' http://admin:admin@${GRAFANA_HOSTNAME}:${GRAFANA_PORT}/api/user/password
+echo
+
+# Create InfluxDB DataSource
+# Ref: https://grafana.com/docs/grafana/latest/http_api/data_source/
+curl -X POST -H "Content-Type: application/json" -d '{
+  "type"     : "influxdb",
+  "name"     : "InfluxDB",
+  "url"      : "http://monitoringservice:8086",
+  "access"   : "proxy",
+  "basicAuth": false,
+  "user"     : "'"$INFLUXDB_USER"'",
+  "password" : "'"$INFLUXDB_PASSWORD"'",
+  "isDefault": true,
+  "database" : "'"$INFLUXDB_DATABASE"'"
+}' ${GRAFANA_URL}/api/datasources
+echo
+
+# Create Monitoring Dashboard
+# Ref: https://grafana.com/docs/grafana/latest/http_api/dashboard/
+curl -X POST -H "Content-Type: application/json" \
+  -d '@src/webui/grafana_dashboard.json' \
+  ${GRAFANA_URL}/api/dashboards/db
+echo
+
+DASHBOARD_URL="${GRAFANA_URL}/api/dashboards/uid/tf-l3-monit"
+DASHBOARD_ID=$(python -c 'import json, requests; print(requests.get("'${DASHBOARD_URL}'").json()["dashboard"]["id"])')
+curl -X POST ${GRAFANA_URL}/api/user/stars/dashboard/${DASHBOARD_ID}
diff --git a/data/.gitignore b/data/.gitignore
index 6722cd96e785ac093e976a48df83409bf618b0a1..5ca4d5198fe878058d83a415a3fed261375b42a3 100644
--- a/data/.gitignore
+++ b/data/.gitignore
@@ -1 +1,2 @@
+*.dot
 *.xml
diff --git a/data/topo_nsfnet.json b/data/topo_nsfnet.json
deleted file mode 100644
index f2cbb53a8f3f9648c4a8b60f97c2212485ac5cc9..0000000000000000000000000000000000000000
--- a/data/topo_nsfnet.json
+++ /dev/null
@@ -1,136 +0,0 @@
-{
-    "topoId": {
-        "contextId": {"contextUuid": {"uuid": "default"}},
-        "topoId": {"uuid": "topo0-nsfnet"}
-    },
-    "device" : [
-        {"device_id": {"device_id": {"uuid": "1" }}, "device_type": "ROADM", "device_config": {"device_config": ""}, "devOperationalStatus": 1, "endpointList" : [
-            {"port_id": {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "1" }}, "port_id": {"uuid" : "101"}}, "port_type": "LINE"}
-        ]},
-        {"device_id": {"device_id": {"uuid": "2" }}, "device_type": "ROADM", "device_config": {"device_config": ""}, "devOperationalStatus": 1, "endpointList" : [
-            {"port_id": {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "2" }}, "port_id": {"uuid" : "201"}}, "port_type": "LINE"}
-        ]},
-        {"device_id": {"device_id": {"uuid": "3" }}, "device_type": "ROADM", "device_config": {"device_config": ""}, "devOperationalStatus": 1, "endpointList" : [
-            {"port_id": {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "3" }}, "port_id": {"uuid" : "301"}}, "port_type": "LINE"}
-        ]},
-        {"device_id": {"device_id": {"uuid": "4" }}, "device_type": "ROADM", "device_config": {"device_config": ""}, "devOperationalStatus": 1, "endpointList" : [
-            {"port_id": {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "4" }}, "port_id": {"uuid" : "401"}}, "port_type": "LINE"}
-        ]},
-        {"device_id": {"device_id": {"uuid": "5" }}, "device_type": "ROADM", "device_config": {"device_config": ""}, "devOperationalStatus": 1, "endpointList" : [
-            {"port_id": {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "5" }}, "port_id": {"uuid" : "501"}}, "port_type": "LINE"}
-        ]},
-        {"device_id": {"device_id": {"uuid": "6" }}, "device_type": "ROADM", "device_config": {"device_config": ""}, "devOperationalStatus": 1, "endpointList" : [
-            {"port_id": {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "6" }}, "port_id": {"uuid" : "601"}}, "port_type": "LINE"}
-        ]},
-        {"device_id": {"device_id": {"uuid": "7" }}, "device_type": "ROADM", "device_config": {"device_config": ""}, "devOperationalStatus": 1, "endpointList" : [
-            {"port_id": {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "7" }}, "port_id": {"uuid" : "701"}}, "port_type": "LINE"}
-        ]},
-        {"device_id": {"device_id": {"uuid": "8" }}, "device_type": "ROADM", "device_config": {"device_config": ""}, "devOperationalStatus": 1, "endpointList" : [
-            {"port_id": {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "8" }}, "port_id": {"uuid" : "801"}}, "port_type": "LINE"}
-        ]},
-        {"device_id": {"device_id": {"uuid": "9" }}, "device_type": "ROADM", "device_config": {"device_config": ""}, "devOperationalStatus": 1, "endpointList" : [
-            {"port_id": {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "9" }}, "port_id": {"uuid" : "901"}}, "port_type": "LINE"}
-        ]},
-        {"device_id": {"device_id": {"uuid": "10"}}, "device_type": "ROADM", "device_config": {"device_config": ""}, "devOperationalStatus": 1, "endpointList" : [
-            {"port_id": {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "10"}}, "port_id": {"uuid" : "1001"}}, "port_type": "LINE"}
-        ]},
-        {"device_id": {"device_id": {"uuid": "11"}}, "device_type": "ROADM", "device_config": {"device_config": ""}, "devOperationalStatus": 1, "endpointList" : [
-            {"port_id": {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "11"}}, "port_id": {"uuid" : "1101"}}, "port_type": "LINE"}
-        ]},
-        {"device_id": {"device_id": {"uuid": "12"}}, "device_type": "ROADM", "device_config": {"device_config": ""}, "devOperationalStatus": 1, "endpointList" : [
-            {"port_id": {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "12"}}, "port_id": {"uuid" : "1201"}}, "port_type": "LINE"}
-        ]},
-        {"device_id": {"device_id": {"uuid": "13"}}, "device_type": "ROADM", "device_config": {"device_config": ""}, "devOperationalStatus": 1, "endpointList" : [
-            {"port_id": {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "13"}}, "port_id": {"uuid" : "1301"}}, "port_type": "LINE"}
-        ]},
-        {"device_id": {"device_id": {"uuid": "14"}}, "device_type": "ROADM", "device_config": {"device_config": ""}, "devOperationalStatus": 1, "endpointList" : [
-            {"port_id": {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "14"}}, "port_id": {"uuid" : "1401"}}, "port_type": "LINE"}
-        ]} 
-    ],
-    "link" : [
-        {"endpointList" : [
-            {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "1" }}, "port_id": {"uuid": "101" }},
-            {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "2" }}, "port_id": {"uuid": "201" }}
-        ]},
-        {"endpointList" : [
-            {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "1" }}, "port_id": {"uuid": "101" }},
-            {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "3" }}, "port_id": {"uuid": "301" }}
-        ]},
-        {"endpointList" : [
-            {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "1" }}, "port_id": {"uuid": "101" }},
-            {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "8" }}, "port_id": {"uuid": "801" }}
-        ]},
-        {"endpointList" : [
-            {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "2" }}, "port_id": {"uuid": "201" }},
-            {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "3" }}, "port_id": {"uuid": "301" }}
-        ]},
-        {"endpointList" : [
-            {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "2" }}, "port_id": {"uuid": "201" }},
-            {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "4" }}, "port_id": {"uuid": "401" }}
-        ]},
-        {"endpointList" : [
-            {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "3" }}, "port_id": {"uuid": "301" }},
-            {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "6" }}, "port_id": {"uuid": "601" }}
-        ]},
-        {"endpointList" : [
-            {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "4" }}, "port_id": {"uuid": "401" }},
-            {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "5" }}, "port_id": {"uuid": "501" }}
-        ]},
-        {"endpointList" : [
-            {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "4" }}, "port_id": {"uuid": "401" }},
-            {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "11"}}, "port_id": {"uuid": "1101"}}
-        ]},
-        {"endpointList" : [
-            {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "5" }}, "port_id": {"uuid": "501" }},
-            {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "6" }}, "port_id": {"uuid": "601" }}
-        ]},         
-        {"endpointList" : [
-            {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "5" }}, "port_id": {"uuid": "501" }},
-            {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "7" }}, "port_id": {"uuid": "701" }}
-        ]},                                                          
-        {"endpointList" : [
-            {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "6" }}, "port_id": {"uuid": "601" }},
-            {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "10"}}, "port_id": {"uuid": "1001"}}
-        ]},                                                  
-        {"endpointList" : [
-            {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "6" }}, "port_id": {"uuid": "601" }},
-            {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "13"}}, "port_id": {"uuid": "1301"}}
-        ]},                                                  
-        {"endpointList" : [
-            {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "7" }}, "port_id": {"uuid": "701" }},
-            {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "8" }}, "port_id": {"uuid": "801" }}
-        ]},                                                  
-        {"endpointList" : [
-            {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "8" }}, "port_id": {"uuid": "801" }},
-            {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "9" }}, "port_id": {"uuid": "901" }}
-        ]},                                                  
-        {"endpointList" : [
-            {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "9" }}, "port_id": {"uuid": "901" }},
-            {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "10"}}, "port_id": {"uuid": "1001"}}
-        ]}, 
-        {"endpointList" : [
-            {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "9" }}, "port_id": {"uuid": "901" }},
-            {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "12"}}, "port_id": {"uuid": "1201"}}
-        ]},   
-        {"endpointList" : [
-            {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "9" }}, "port_id": {"uuid": "901" }},
-            {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "14"}}, "port_id": {"uuid": "1401"}}
-        ]},
-        {"endpointList" : [
-            {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "11"}}, "port_id": {"uuid": "1101"}},
-            {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "12"}}, "port_id": {"uuid": "1201"}}
-        ]},   
-        {"endpointList" : [
-            {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "11"}}, "port_id": {"uuid": "1101"}},
-            {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "14"}}, "port_id": {"uuid": "1401"}}
-        ]},     
-        {"endpointList" : [
-            {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "12"}}, "port_id": {"uuid": "1201"}},
-            {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "13"}}, "port_id": {"uuid": "1301"}}
-        ]},
-        {"endpointList" : [
-            {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "13"}}, "port_id": {"uuid": "1301"}},
-            {"topoId": {"topoId": {"uuid": "topo0-nsfnet"}}, "dev_id": {"device_id": {"uuid": "14"}}, "port_id": {"uuid": "1401"}}
-        ]}      
-    ]
-}
diff --git a/deploy_in_kubernetes.sh b/deploy_in_kubernetes.sh
index c4019c7d790930f65aac3c898f75f605fb35611e..0da87dbe5dc92d785b2a1b3aedda85288231b03f 100755
--- a/deploy_in_kubernetes.sh
+++ b/deploy_in_kubernetes.sh
@@ -1,29 +1,48 @@
 #!/bin/bash
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 
 ########################################################################################################################
 # Define your deployment settings here
 ########################################################################################################################
 
-# Set the URL of your local Docker registry where the images will be uploaded to. Leave it blank if you do not want to
-# use any Docker registry.
-REGISTRY_IMAGE=""
-#REGISTRY_IMAGE="http://my-container-registry.local/"
+# If not already set, set the URL of your local Docker registry where the images will be uploaded to. Leave it blank if
+# you do not want to use any Docker registry.
+export REGISTRY_IMAGE=${REGISTRY_IMAGE:-""}
+#export REGISTRY_IMAGE="http://my-container-registry.local/"
+
+# If not already set, set the list of components you want to build images for, and deploy.
+export COMPONENTS=${COMPONENTS:-"context device automation policy service compute monitoring dbscanserving opticalattackmitigator opticalcentralizedattackdetector webui"}
 
-# Set the list of components you want to build images for, and deploy.
-COMPONENTS="context device automation policy service compute monitoring dbscanserving opticalattackmitigator opticalcentralizedattackdetector"
+# If not already set, set the tag you want to use for your images.
+export IMAGE_TAG=${IMAGE_TAG:-"tf-dev"}
 
-# Set the tag you want to use for your images.
-IMAGE_TAG="tf-dev"
+# If not already set, set the name of the Kubernetes namespace to deploy to.
+export K8S_NAMESPACE=${K8S_NAMESPACE:-"tf-dev"}
 
-# Set the name of the Kubernetes namespace to deploy to.
-K8S_NAMESPACE="tf-dev"
+# If not already set, set the name of the Kubernetes hostname to deploy to.
+export K8S_HOSTNAME=${K8S_HOSTNAME:-"kubernetes-master"}
+
+# If not already set, set additional manifest files to be applied after the deployment
+export EXTRA_MANIFESTS=${EXTRA_MANIFESTS:-""}
 
 ########################################################################################################################
 # Automated steps start here
 ########################################################################################################################
 
 # Constants
-CLONE_URL="https://gitlab.com/teraflow-h2020/controller.git"
 GITLAB_REPO_URL="registry.gitlab.com/teraflow-h2020/controller"
 TMP_FOLDER="./tmp"
 
@@ -33,16 +52,24 @@ mkdir -p $TMP_MANIFESTS_FOLDER
 TMP_LOGS_FOLDER="$TMP_FOLDER/logs"
 mkdir -p $TMP_LOGS_FOLDER
 
-# Re-create the namespace to prevent being affected by garbage on it
+echo "Deleting and Creating a new namespace..."
 kubectl delete namespace $K8S_NAMESPACE
 kubectl create namespace $K8S_NAMESPACE
 printf "\n"
 
-# creating the secrets for the influxdb deployment
+echo "Creating secrets for InfluxDB..."
 #TODO: make sure to change this when having a production deployment
 kubectl create secret generic influxdb-secrets --namespace=$K8S_NAMESPACE --from-literal=INFLUXDB_DB="monitoring" --from-literal=INFLUXDB_ADMIN_USER="teraflow" --from-literal=INFLUXDB_ADMIN_PASSWORD="teraflow" --from-literal=INFLUXDB_HTTP_AUTH_ENABLED="True"
 kubectl create secret generic monitoring-secrets --namespace=$K8S_NAMESPACE --from-literal=INFLUXDB_DATABASE="monitoring" --from-literal=INFLUXDB_USER="teraflow" --from-literal=INFLUXDB_PASSWORD="teraflow" --from-literal=INFLUXDB_HOSTNAME="localhost"
+printf "\n"
 
+echo "Pulling/Updating Docker images..."
+docker pull redis:6.2
+docker pull influxdb:1.8
+docker pull grafana/grafana:8.2.6
+printf "\n"
+
+echo "Deploying components..."
 for COMPONENT in $COMPONENTS; do
     echo "Processing '$COMPONENT' component..."
     IMAGE_NAME="$COMPONENT:$IMAGE_TAG"
@@ -85,9 +112,31 @@ for COMPONENT in $COMPONENTS; do
     echo "  Deploying '$COMPONENT' component to Kubernetes..."
     DEPLOY_LOG="$TMP_LOGS_FOLDER/push_${COMPONENT}.log"
     kubectl --namespace $K8S_NAMESPACE apply -f "$MANIFEST" > "$DEPLOY_LOG"
+    kubectl --namespace $K8S_NAMESPACE scale deployment --replicas=0 ${COMPONENT}service >> "$DEPLOY_LOG"
+    kubectl --namespace $K8S_NAMESPACE scale deployment --replicas=1 ${COMPONENT}service >> "$DEPLOY_LOG"
     printf "\n"
 done
 
+echo "Deploying extra manifests..."
+for EXTRA_MANIFEST in $EXTRA_MANIFESTS; do
+    echo "Processing manifest '$EXTRA_MANIFEST'..."
+    kubectl --namespace $K8S_NAMESPACE apply -f $EXTRA_MANIFEST
+    printf "\n"
+done
+
+# By now, leave this control here. Some component dependencies are not well handled
+for COMPONENT in $COMPONENTS; do
+    echo "Waiting for '$COMPONENT' component..."
+    kubectl wait --namespace $K8S_NAMESPACE --for='condition=available' --timeout=300s deployment/${COMPONENT}service
+    printf "\n"
+done
+
+echo "Configuring DataStores and Dashboards..."
+./configure_dashboards.sh
+printf "\n\n"
+
+echo "Reporting Deployment..."
 kubectl --namespace $K8S_NAMESPACE get all
+printf "\n"
 
-echo "Done!"
\ No newline at end of file
+echo "Done!"
diff --git a/install_development_dependencies.sh b/install_development_dependencies.sh
index 4b66f9c92468cc3b6930e8c4a5372a611b2b965e..55b52803bd10950e18695eb39fadfbe98295aee0 100755
--- a/install_development_dependencies.sh
+++ b/install_development_dependencies.sh
@@ -1,4 +1,18 @@
 #!/bin/bash
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 
 # installing basic tools
 pip install --upgrade pip setuptools wheel pip-tools pylint pytest pytest-benchmark coverage grpcio-tools
@@ -7,7 +21,7 @@ pip install --upgrade pip setuptools wheel pip-tools pylint pytest pytest-benchm
 echo "" > requirements.in
 
 #TODO: include here your component
-COMPONENTS="compute context device service monitoring opticalcentralizedattackdetector opticalattackmitigator dbscanserving"
+COMPONENTS="compute context device service monitoring opticalcentralizedattackdetector opticalattackmitigator dbscanserving webui"
 
 # compiling dependencies from all components
 for component in $COMPONENTS
diff --git a/manifests/.gitlab-ci.yml b/manifests/.gitlab-ci.yml
index 9fc872ca61752d1be012aa4b866b3b592171e2cd..d20b67e531c33bfeae9c796ed95488b4c81d0fe4 100644
--- a/manifests/.gitlab-ci.yml
+++ b/manifests/.gitlab-ci.yml
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 # Deployment of the dependency services in Kubernetes Cluster
 
 dependencies all:
diff --git a/manifests/computeservice.yaml b/manifests/computeservice.yaml
index 73380f75daeb7d60891c3d093ca7651ba4280e58..0c8d0a6724a49f4dec0f903570ff04e49cb4e793 100644
--- a/manifests/computeservice.yaml
+++ b/manifests/computeservice.yaml
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 apiVersion: apps/v1
 kind: Deployment
 metadata:
@@ -21,7 +35,7 @@ spec:
         - containerPort: 9090
         env:
         - name: LOG_LEVEL
-          value: "DEBUG"
+          value: "INFO"
         readinessProbe:
           exec:
             command: ["/bin/grpc_health_probe", "-addr=:9090"]
@@ -53,24 +67,3 @@ spec:
     protocol: TCP
     port: 9090
     targetPort: 9090
----
-apiVersion: v1
-kind: Service
-metadata:
-  name: computeservice-public
-  labels:
-    app: computeservice
-spec:
-  type: NodePort
-  selector:
-    app: computeservice
-  ports:
-  - name: http
-    protocol: TCP
-    port: 8080
-    targetPort: 8080
-  - name: grpc
-    protocol: TCP
-    port: 9090
-    targetPort: 9090
----
diff --git a/manifests/contextservice.yaml b/manifests/contextservice.yaml
index 7ccf3e4f0dc9abb41f228d4fb7c4ec18fad93954..04da586dfeb25a01a6f5267aa31441498ce4f2cc 100644
--- a/manifests/contextservice.yaml
+++ b/manifests/contextservice.yaml
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 apiVersion: apps/v1
 kind: Deployment
 metadata:
@@ -41,7 +55,7 @@ spec:
         - name: LOG_LEVEL
           value: "INFO"
         - name: POPULATE_FAKE_DATA
-          value: "true"
+          value: "false"
         readinessProbe:
           exec:
             command: ["/bin/grpc_health_probe", "-addr=:1010"]
@@ -73,20 +87,3 @@ spec:
     protocol: TCP
     port: 8080
     targetPort: 8080
----
-apiVersion: v1
-kind: Service
-metadata:
-  name: contextservice-public
-  labels:
-    app: contextservice
-spec:
-  type: NodePort
-  selector:
-    app: contextservice
-  ports:
-  - name: http
-    protocol: TCP
-    port: 8080
-    targetPort: 8080
----
diff --git a/manifests/dbscanservingservice.yaml b/manifests/dbscanservingservice.yaml
index ca8982db25f0fe94588418dc67787f63babc9826..9553ed556bddaa437d89881f0c4220ae6e418239 100644
--- a/manifests/dbscanservingservice.yaml
+++ b/manifests/dbscanservingservice.yaml
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 apiVersion: apps/v1
 kind: Deployment
 metadata:
@@ -47,20 +61,3 @@ spec:
   - name: grpc
     port: 10006
     targetPort: 10006
----
-apiVersion: v1
-kind: Service
-metadata:
-  name: dbscanservingservice-public
-  labels:
-    app: dbscanservingservice
-spec:
-  type: NodePort
-  selector:
-    app: dbscanservingservice
-  ports:
-  - name: http
-    protocol: TCP
-    port: 10006
-    targetPort: 10006
----
diff --git a/manifests/deviceservice.yaml b/manifests/deviceservice.yaml
index 35c19279027ca7bed1f0ab79a9c34a44b1e50e3c..171394f7c43b2447e898902c78d5276fe1bcbc7c 100644
--- a/manifests/deviceservice.yaml
+++ b/manifests/deviceservice.yaml
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 apiVersion: apps/v1
 kind: Deployment
 metadata:
@@ -20,7 +34,7 @@ spec:
         - containerPort: 2020
         env:
         - name: LOG_LEVEL
-          value: "DEBUG"
+          value: "INFO"
         readinessProbe:
           exec:
             command: ["/bin/grpc_health_probe", "-addr=:2020"]
diff --git a/manifests/l3_attackmitigatorservice.yaml b/manifests/l3_attackmitigatorservice.yaml
index 34f660324066c7a06b8debff276ed5444027fe4e..2240776ebb2e234b58febe9520a4b9e07d42b6d4 100644
--- a/manifests/l3_attackmitigatorservice.yaml
+++ b/manifests/l3_attackmitigatorservice.yaml
@@ -1,15 +1,29 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 apiVersion: apps/v1
 kind: Deployment
 metadata:
-  name: l3_attackmitigatorservice
+  name: l3-attackmitigatorservice
 spec:
   selector:
     matchLabels:
-      app: l3_attackmitigatorservice
+      app: l3-attackmitigatorservice
   template:
     metadata:
       labels:
-        app: l3_attackmitigatorservice
+        app: l3-attackmitigatorservice
     spec:
       terminationGracePeriodSeconds: 5
       containers:
@@ -38,11 +52,11 @@ spec:
 apiVersion: v1
 kind: Service
 metadata:
-  name: l3_attackmitigatorservice
+  name: l3-attackmitigatorservice
 spec:
   type: ClusterIP
   selector:
-    app: l3_attackmitigatorservice
+    app: l3-attackmitigatorservice
   ports:
   - name: grpc
     port: 10002
diff --git a/manifests/l3_centralizedattackdetectorservice.yaml b/manifests/l3_centralizedattackdetectorservice.yaml
index 0393d83c29591d2fa4cc1a2b52abbcdc760de23a..fa7ee9dccd99982d35d7f7705e463ecee30c7c9b 100644
--- a/manifests/l3_centralizedattackdetectorservice.yaml
+++ b/manifests/l3_centralizedattackdetectorservice.yaml
@@ -1,15 +1,29 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 apiVersion: apps/v1
 kind: Deployment
 metadata:
-  name: l3_centralizedattackdetectorservice
+  name: l3-centralizedattackdetectorservice
 spec:
   selector:
     matchLabels:
-      app: l3_centralizedattackdetectorservice
+      app: l3-centralizedattackdetectorservice
   template:
     metadata:
       labels:
-        app: l3_centralizedattackdetectorservice
+        app: l3-centralizedattackdetectorservice
     spec:
       terminationGracePeriodSeconds: 5
       containers:
@@ -38,11 +52,11 @@ spec:
 apiVersion: v1
 kind: Service
 metadata:
-  name: l3_centralizedattackdetectorservice
+  name: l3-centralizedattackdetectorservice
 spec:
   type: ClusterIP
   selector:
-    app: l3_centralizedattackdetectorservice
+    app: l3-centralizedattackdetectorservice
   ports:
   - name: grpc
     port: 10001
diff --git a/manifests/l3_distributedattackdetectorservice.yaml b/manifests/l3_distributedattackdetectorservice.yaml
index eff047b1b97733f808df01a23a11a930515eb3e5..6b28f68dd5e08561eb29e4512af330b26f6408cf 100644
--- a/manifests/l3_distributedattackdetectorservice.yaml
+++ b/manifests/l3_distributedattackdetectorservice.yaml
@@ -1,15 +1,29 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 apiVersion: apps/v1
 kind: Deployment
 metadata:
-  name: l3_distributedattackdetectorservice
+  name: l3-distributedattackdetectorservice
 spec:
   selector:
     matchLabels:
-      app: l3_distributedattackdetectorservice
+      app: l3-distributedattackdetectorservice
   template:
     metadata:
       labels:
-        app: l3_distributedattackdetectorservice
+        app: l3-distributedattackdetectorservice
     spec:
       terminationGracePeriodSeconds: 5
       containers:
@@ -38,11 +52,11 @@ spec:
 apiVersion: v1
 kind: Service
 metadata:
-  name: l3_distributedattackdetectorservice
+  name: l3-distributedattackdetectorservice
 spec:
   type: ClusterIP
   selector:
-    app: l3_distributedattackdetectorservice
+    app: l3-distributedattackdetectorservice
   ports:
   - name: grpc
     port: 10000
diff --git a/manifests/monitoringservice.yaml b/manifests/monitoringservice.yaml
index d1b4023fa1db0fcae1e8b6b83ceb665151c35822..e6fa36d1a68e4e0f85776b511631b0b619ec100c 100644
--- a/manifests/monitoringservice.yaml
+++ b/manifests/monitoringservice.yaml
@@ -1,16 +1,30 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 apiVersion: apps/v1
 kind: Deployment
 metadata:
-  name: monitoring
+  name: monitoringservice
 spec:
   selector:
     matchLabels:
-      app: monitoring
+      app: monitoringservice
   replicas: 1
   template:
     metadata:
       labels:
-        app: monitoring
+        app: monitoringservice
     spec:
       terminationGracePeriodSeconds: 5
       restartPolicy: Always
@@ -60,11 +74,11 @@ spec:
 apiVersion: v1
 kind: Service
 metadata:
-  name: monitoring
+  name: monitoringservice
 spec:
   type: ClusterIP
   selector:
-    app: monitoring
+    app: monitoringservice
   ports:
   - name: grpc
     protocol: TCP
@@ -74,4 +88,3 @@ spec:
     protocol: TCP
     port: 8086
     targetPort: 8086
----
diff --git a/manifests/opticalattackmitigatorservice.yaml b/manifests/opticalattackmitigatorservice.yaml
index 1cc03ba0575edcc66e1cf3f9b57fd161a763a696..afe2e4069fbae2fd3b5300da614b4deb5d785fab 100644
--- a/manifests/opticalattackmitigatorservice.yaml
+++ b/manifests/opticalattackmitigatorservice.yaml
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 apiVersion: apps/v1
 kind: Deployment
 metadata:
@@ -47,20 +61,3 @@ spec:
   - name: grpc
     port: 10007
     targetPort: 10007
----
-apiVersion: v1
-kind: Service
-metadata:
-  name: opticalattackmitigatorservice-public
-  labels:
-    app: opticalattackmitigatorservice
-spec:
-  type: NodePort
-  selector:
-    app: opticalattackmitigatorservice
-  ports:
-  - name: http
-    protocol: TCP
-    port: 10007
-    targetPort: 10007
----
diff --git a/manifests/opticalcentralizedattackdetectorservice.yaml b/manifests/opticalcentralizedattackdetectorservice.yaml
index d3ceb3ea49ac1421e14faf00dbb81b1fdaff5537..664bcb54348e533ff40c7f882b5668f727a39053 100644
--- a/manifests/opticalcentralizedattackdetectorservice.yaml
+++ b/manifests/opticalcentralizedattackdetectorservice.yaml
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 apiVersion: apps/v1
 kind: Deployment
 metadata:
@@ -47,20 +61,3 @@ spec:
   - name: grpc
     port: 10005
     targetPort: 10005
----
-apiVersion: v1
-kind: Service
-metadata:
-  name: opticalcentralizedattackdetectorservice-public
-  labels:
-    app: opticalcentralizedattackdetectorservice
-spec:
-  type: NodePort
-  selector:
-    app: opticalcentralizedattackdetectorservice
-  ports:
-  - name: grpc
-    protocol: TCP
-    port: 10005
-    targetPort: 10005
----
diff --git a/manifests/prometheus.yaml b/manifests/prometheus.yaml
index 72e2e65c8c8b266ebdfef974c6795d12f1c682c4..221123eea5cf8ba61d078ccfccd697d7c6c01127 100644
--- a/manifests/prometheus.yaml
+++ b/manifests/prometheus.yaml
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 ---
 kind: ConfigMap
 apiVersion: v1
diff --git a/manifests/serviceservice.yaml b/manifests/serviceservice.yaml
index e9337807d9bbcec32fee2fe6f4ab04e4b4dba1aa..75832b94fa2a6ba97617641e7b249157508614bf 100644
--- a/manifests/serviceservice.yaml
+++ b/manifests/serviceservice.yaml
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 apiVersion: apps/v1
 kind: Deployment
 metadata:
@@ -20,7 +34,7 @@ spec:
         - containerPort: 3030
         env:
         - name: LOG_LEVEL
-          value: "DEBUG"
+          value: "INFO"
         readinessProbe:
           exec:
             command: ["/bin/grpc_health_probe", "-addr=:3030"]
diff --git a/manifests/webuiservice.yaml b/manifests/webuiservice.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..be7ad73d7ca92c7deabbb0a312d8f4b3889af28a
--- /dev/null
+++ b/manifests/webuiservice.yaml
@@ -0,0 +1,105 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: webuiservice
+spec:
+  selector:
+    matchLabels:
+      app: webuiservice
+  template:
+    metadata:
+      labels:
+        app: webuiservice
+    spec:
+      terminationGracePeriodSeconds: 5
+      securityContext:
+        fsGroup: 472
+        supplementalGroups:
+          - 0
+      containers:
+      - name: server
+        image: registry.gitlab.com/teraflow-h2020/controller/webui:latest
+        imagePullPolicy: Always
+        ports:
+        - containerPort: 8004 # TODO: define the real port
+        env:
+        - name: LOG_LEVEL
+          value: "DEBUG"
+        readinessProbe:
+          httpGet:
+            path: /healthz/ready
+            port: 8004
+          initialDelaySeconds: 5
+          timeoutSeconds: 1
+        livenessProbe:
+          httpGet:
+            path: /healthz/live
+            port: 8004
+          initialDelaySeconds: 5
+          timeoutSeconds: 1
+        resources:
+          requests:
+            cpu: 250m
+            memory: 512Mi
+          limits:
+            cpu: 700m
+            memory: 1024Mi
+      - name: grafana
+        image: grafana/grafana:8.2.6
+        imagePullPolicy: IfNotPresent
+        ports:
+          - containerPort: 3000
+            name: http-grafana
+            protocol: TCP
+        readinessProbe:
+          failureThreshold: 3
+          httpGet:
+            path: /robots.txt
+            port: 3000
+            scheme: HTTP
+          initialDelaySeconds: 5
+          periodSeconds: 5
+          successThreshold: 1
+          timeoutSeconds: 2
+        livenessProbe:
+          failureThreshold: 3
+          initialDelaySeconds: 5
+          periodSeconds: 5
+          successThreshold: 1
+          tcpSocket:
+            port: 3000
+          timeoutSeconds: 1
+        resources:
+          requests:
+            cpu: 250m
+            memory: 750Mi
+          limits:
+            cpu: 700m
+            memory: 1024Mi
+---
+apiVersion: v1
+kind: Service
+metadata:
+  name: webuiservice
+spec:
+  type: ClusterIP
+  selector:
+    app: webuiservice
+  ports:
+  - name: http
+    port: 8004
+    targetPort: 8004
diff --git a/ofc22 b/ofc22
new file mode 120000
index 0000000000000000000000000000000000000000..1102ec2a635bd3fcc1142dd2982d4e3224ccaca5
--- /dev/null
+++ b/ofc22
@@ -0,0 +1 @@
+src/tests/ofc22/
\ No newline at end of file
diff --git a/open_dashboard.sh b/open_dashboard.sh
new file mode 100755
index 0000000000000000000000000000000000000000..8291a22c75cd2c2b83bedcab2ac0167c56c966a6
--- /dev/null
+++ b/open_dashboard.sh
@@ -0,0 +1,29 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#/bin/bash
+
+# this script opens the dashboard
+
+K8S_NAMESPACE=${K8S_NAMESPACE:-'tf-dev'}
+
+GRAFANA_IP=$(kubectl get service/webuiservice -n ${K8S_NAMESPACE} -o jsonpath='{.spec.clusterIP}')
+GRAFANA_PORT=$(kubectl get service webuiservice-public --namespace $K8S_NAMESPACE -o 'jsonpath={.spec.ports[?(@.port==3000)].nodePort}')
+URL=http://${GRAFANA_IP}:${GRAFANA_PORT}
+
+echo Opening Dashboard on URL ${URL}
+
+# curl -kL ${URL}
+
+python3 -m webbrowser ${URL}
diff --git a/open_webui.sh b/open_webui.sh
new file mode 100755
index 0000000000000000000000000000000000000000..e4dfdb709ef5008091f3f73357087272dfd7c34e
--- /dev/null
+++ b/open_webui.sh
@@ -0,0 +1,37 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# this script opens the webui
+
+K8S_NAMESPACE=${K8S_NAMESPACE:-'tf-dev'}
+
+WEBUI_SERVICE_NAME="webuiservice-public"
+WEBUI_PROTO=`kubectl get service ${WEBUI_SERVICE_NAME} -n ${K8S_NAMESPACE} -o jsonpath='{.spec.ports[0].name}'`
+WEBUI_IP=`kubectl get service ${WEBUI_SERVICE_NAME} -n ${K8S_NAMESPACE} -o jsonpath='{.spec.clusterIP}'`
+# WEBUI_PORT=$(kubectl get service ${WEBUI_SERVICE_NAME} --namespace $K8S_NAMESPACE -o 'jsonpath={.spec.ports[?(@.port==8004)].nodePort}')
+WEBUI_PORT=8004
+# GRAFANA_PORT=$(kubectl get service ${WEBUI_SERVICE_NAME} --namespace $K8S_NAMESPACE -o 'jsonpath={.spec.ports[?(@.port==3000)].nodePort}')
+GRAFANA_PORT=3000
+
+# Open WebUI
+URL=${WEBUI_PROTO}://${WEBUI_IP}:${WEBUI_PORT}
+echo Opening web UI on URL ${URL}
+# curl -kL ${URL}
+python3 -m webbrowser ${URL}
+
+# Open Dashboard
+URL=${WEBUI_PROTO}://${WEBUI_IP}:${GRAFANA_PORT}
+echo Opening Dashboard on URL ${URL}
+# curl -kL ${URL}
+python3 -m webbrowser ${URL}
diff --git a/proto/attack_mitigator.proto b/proto/attack_mitigator.proto
index b65857f3b1a2ab86289735704c14ddc89e30d3d7..e538a9d2e62f7bc0cf91497b6b13414c36e1bc41 100644
--- a/proto/attack_mitigator.proto
+++ b/proto/attack_mitigator.proto
@@ -1,3 +1,17 @@
+// Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 // protocol buffers documentation: https://developers.google.com/protocol-buffers/docs/proto3
 syntax = "proto3";
 package attack_mitigator;
diff --git a/proto/automation.proto b/proto/automation.proto
index 95526d36d75047f0f351d77b2dfa760934883dfc..02aba0a9cd3d5867a8c7f5d6581ade426ea0c290 100644
--- a/proto/automation.proto
+++ b/proto/automation.proto
@@ -1,3 +1,17 @@
+// Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 syntax = "proto3";
 package automation;
 
diff --git a/proto/centralized_attack_detector.proto b/proto/centralized_attack_detector.proto
index b2547fae525e757b31d3192fc970a53f8af1042d..4ce34cfa443b67aa8d060802dca6c9c29b2f7087 100644
--- a/proto/centralized_attack_detector.proto
+++ b/proto/centralized_attack_detector.proto
@@ -1,3 +1,17 @@
+// Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 // protocol buffers documentation: https://developers.google.com/protocol-buffers/docs/proto3
 syntax = "proto3";
 package centralized_attack_detector;
diff --git a/proto/compile.sh b/proto/compile.sh
index 9a28ac860259c57a25cd304a9089451c2a08525d..520ead69a95cdfd1a076aee221d672f81810b5f0 100755
--- a/proto/compile.sh
+++ b/proto/compile.sh
@@ -1,4 +1,18 @@
 #!/bin/bash
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 python3 -m grpc_tools.protoc -I=./ --python_out=../src/ --grpc_python_out=../src/ *.proto
 #requires installation of protoc-gen-uml
 export PATH=${HOME}/protoc-gen-uml/target/universal/stage/bin:$PATH
diff --git a/proto/compute.proto b/proto/compute.proto
index a44bde55ef94b5b4a827a4724e81c29e07a7e421..0e7c7a7386b5c8d5924f93745bc9aa3039b60703 100644
--- a/proto/compute.proto
+++ b/proto/compute.proto
@@ -1,3 +1,17 @@
+// Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 syntax = "proto3";
 package compute;
 
diff --git a/proto/context.proto b/proto/context.proto
index 2e2154b7d3336b1ce4ed36ed3323bf0325f467ee..057f44c9b0207651495597fcbf345c1ca1f521ca 100644
--- a/proto/context.proto
+++ b/proto/context.proto
@@ -1,3 +1,17 @@
+// Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 syntax = "proto3";
 package context;
 
diff --git a/proto/dbscanserving.proto b/proto/dbscanserving.proto
index f2c63b15a00481290cfd46ab73940d2f399e01d5..d7d0512b6c5215f05f0c2568a0271e11f8e03a6c 100644
--- a/proto/dbscanserving.proto
+++ b/proto/dbscanserving.proto
@@ -1,3 +1,17 @@
+// Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 syntax = "proto3";
 
 package dbscanserving;
diff --git a/proto/device.proto b/proto/device.proto
index a7f3e44b131908a8931a1ef5257d0261b37537a4..f41e2891a85301da445367db2cf96f9a2d97d313 100644
--- a/proto/device.proto
+++ b/proto/device.proto
@@ -1,3 +1,17 @@
+// Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 syntax = "proto3";
 package device;
 
diff --git a/proto/distributed_cybersecurity.proto b/proto/distributed_cybersecurity.proto
index 69d480a69573510efd32b6fcfb746e9d56bd615e..664e1383f162ee874d1ef89b0e59c70ac2ee62e1 100644
--- a/proto/distributed_cybersecurity.proto
+++ b/proto/distributed_cybersecurity.proto
@@ -1,3 +1,17 @@
+// Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 // protocol buffers documentation: https://developers.google.com/protocol-buffers/docs/proto3
 syntax = "proto3";
 package distributed_cybersecurity;
diff --git a/proto/dlt.proto b/proto/dlt.proto
index f6dacfbb83eca04714b6795d4875f9fab5d08e0b..29a0db198313533393a8f87a1fc328c9335e0a0a 100644
--- a/proto/dlt.proto
+++ b/proto/dlt.proto
@@ -1,3 +1,17 @@
+// Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 //Example of topology
 syntax = "proto3";
 package dlt;
diff --git a/proto/health.proto b/proto/health.proto
index 378db950b3b49332ad990aa4aea499179bbe5035..0252f5d877343864a75ff641d4a019e7cb6de499 100644
--- a/proto/health.proto
+++ b/proto/health.proto
@@ -1,3 +1,17 @@
+// Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 syntax = "proto3";
 
 package grpc.health.v1;
diff --git a/proto/interdomain.proto b/proto/interdomain.proto
index 7e7f6c10e93ab1ea5563b13d1cad27fb0a2ee90a..7088586e27bad7cd241f0ca17353e621144073e9 100644
--- a/proto/interdomain.proto
+++ b/proto/interdomain.proto
@@ -1,3 +1,17 @@
+// Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 //Example of topology
 syntax = "proto3";
 package interdomain;
diff --git a/proto/kpi_sample_types.proto b/proto/kpi_sample_types.proto
index e06527131007ab278aa92bb1e392aa9e49af9a96..7445a0f25a57df9793bd8761da024581988cf9e6 100644
--- a/proto/kpi_sample_types.proto
+++ b/proto/kpi_sample_types.proto
@@ -1,3 +1,17 @@
+// Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 syntax = "proto3";
 package kpi_sample_types;
 
diff --git a/proto/l3_attackmitigator.proto b/proto/l3_attackmitigator.proto
index 7d76408f892eb960cc61b54596c39833dbeed173..39333718a5904d1617ba827d35149687ff903b65 100644
--- a/proto/l3_attackmitigator.proto
+++ b/proto/l3_attackmitigator.proto
@@ -1,3 +1,17 @@
+// Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 syntax = "proto3";
 
 import "context.proto";
diff --git a/proto/l3_centralizedattackdetector.proto b/proto/l3_centralizedattackdetector.proto
index 6a7dff8939a7c47cfde59038a21706a433c40614..3cb1b0991b1dc4df2c268d511c0ebd6f9f9d80aa 100644
--- a/proto/l3_centralizedattackdetector.proto
+++ b/proto/l3_centralizedattackdetector.proto
@@ -1,3 +1,17 @@
+// Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 syntax = "proto3";
 
 service L3Centralizedattackdetector {
diff --git a/proto/monitoring.proto b/proto/monitoring.proto
index 4b1b930438045d9199f31de2e408e2c56e91d848..293eb982a758e9869d8310134bd65bca6c8dfd32 100644
--- a/proto/monitoring.proto
+++ b/proto/monitoring.proto
@@ -1,3 +1,17 @@
+// Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 syntax = "proto3";
 package monitoring;
 
diff --git a/proto/optical_attack_mitigator.proto b/proto/optical_attack_mitigator.proto
index d75a845dd10f605d6894a610f3f82bbef27b8297..881f9fde375bb67b0362ca634ad84b45fb60220b 100644
--- a/proto/optical_attack_mitigator.proto
+++ b/proto/optical_attack_mitigator.proto
@@ -1,3 +1,17 @@
+// Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 // protocol buffers documentation: https://developers.google.com/protocol-buffers/docs/proto3
 syntax = "proto3";
 package optical_attack_mitigator;
diff --git a/proto/optical_centralized_attack_detector.proto b/proto/optical_centralized_attack_detector.proto
index 99cc1ce6fabc662de078f58985eae2d4eeb3d24c..d480054f2ca3b1cf32cbb40c51df51c562f9b4d5 100644
--- a/proto/optical_centralized_attack_detector.proto
+++ b/proto/optical_centralized_attack_detector.proto
@@ -1,3 +1,17 @@
+// Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 // protocol buffers documentation: https://developers.google.com/protocol-buffers/docs/proto3
 syntax = "proto3";
 package centralized_attack_detector;
diff --git a/proto/policy.proto b/proto/policy.proto
index 6a5874125820c576e75a5adb547173754b6e8316..447eda2e19188a39a1980181d3018a25ed9f83c7 100644
--- a/proto/policy.proto
+++ b/proto/policy.proto
@@ -1,3 +1,17 @@
+// Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 syntax = "proto3";
 package policy;
 
diff --git a/proto/service.proto b/proto/service.proto
index 98e827c8ac4bfa5e1a8d970e6d8543e1fecdecb0..90a2f2ecf32dcda0366281224d31725fb4f95622 100644
--- a/proto/service.proto
+++ b/proto/service.proto
@@ -1,11 +1,24 @@
+// Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 syntax = "proto3";
 package service;
 
 import "context.proto";
 
 service ServiceService {
-  rpc CreateService    (context.Service  ) returns (context.ServiceId     ) {}
-  rpc UpdateService    (context.Service  ) returns (context.ServiceId     ) {}
-  rpc DeleteService    (context.ServiceId) returns (context.Empty         ) {}
-  rpc GetConnectionList(context.ServiceId) returns (context.ConnectionList) {}
+  rpc CreateService(context.Service  ) returns (context.ServiceId) {}
+  rpc UpdateService(context.Service  ) returns (context.ServiceId) {}
+  rpc DeleteService(context.ServiceId) returns (context.Empty    ) {}
 }
diff --git a/proto/slice.proto b/proto/slice.proto
index ab83b15cfe45316ef7cf7cf43a5c857d313d6043..73e945a40aa2e23d1780045600df2e3c794333f8 100644
--- a/proto/slice.proto
+++ b/proto/slice.proto
@@ -1,3 +1,17 @@
+// Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 //Example of topology
 syntax = "proto3";
 package slice;
diff --git a/proto/te.proto b/proto/te.proto
index a0233fab09a024e217a19b46681562db0c0541e1..f811f86fed64220cd98d1d8439cca5af3dd8548a 100644
--- a/proto/te.proto
+++ b/proto/te.proto
@@ -1,3 +1,17 @@
+// Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 syntax = "proto3";
 package te;
 
diff --git a/report_coverage_all.sh b/report_coverage_all.sh
deleted file mode 100755
index 66e393747ee86afee9e407da902ea3fb0421d707..0000000000000000000000000000000000000000
--- a/report_coverage_all.sh
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/bin/bash
-
-PROJECTDIR=`pwd`
-
-cd $(dirname $0)/src
-RCFILE=$PROJECTDIR/coverage/.coveragerc
-
-echo
-echo "Coverage report:"
-echo "----------------"
-coverage report --rcfile=$RCFILE --sort cover --show-missing --skip-covered
-#coverage html --rcfile=$RCFILE
-#coverage xml --rcfile=$RCFILE
diff --git a/report_coverage_common.sh b/report_coverage_common.sh
deleted file mode 100755
index be7224ad610ddf2c266fa485c6535f165cdc72ca..0000000000000000000000000000000000000000
--- a/report_coverage_common.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/bash
-
-./report_coverage_all.sh | grep --color -E -i "^common/.*$|$"
diff --git a/report_coverage_compute.sh b/report_coverage_compute.sh
deleted file mode 100755
index b63d279ed587a6fce66d08bd71820bfa96e95140..0000000000000000000000000000000000000000
--- a/report_coverage_compute.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/bash
-
-./report_coverage_all.sh | grep --color -E -i "^compute/.*$|$"
diff --git a/report_coverage_context.sh b/report_coverage_context.sh
deleted file mode 100755
index 3a404a62698cdd95f94c9ed7d4c8b4b073778d08..0000000000000000000000000000000000000000
--- a/report_coverage_context.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/bash
-
-./report_coverage_all.sh | grep --color -E -i "^context/.*$|$"
diff --git a/report_coverage_device.sh b/report_coverage_device.sh
deleted file mode 100755
index be2612d89ce56d518d992327f93a24853e591a4d..0000000000000000000000000000000000000000
--- a/report_coverage_device.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/bash
-
-./report_coverage_all.sh | grep --color -E -i "^device/.*$|$"
diff --git a/report_coverage_l3_attackmitigator.sh b/report_coverage_l3_attackmitigator.sh
deleted file mode 100755
index 74df2c280b94af016d974a56e1019e42a54d6753..0000000000000000000000000000000000000000
--- a/report_coverage_l3_attackmitigator.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/bash
-
-./report_coverage_all.sh | grep --color -E -i "^l3_attackmitigator/.*$|$"
diff --git a/report_coverage_l3_centralizedattackdetector.sh b/report_coverage_l3_centralizedattackdetector.sh
deleted file mode 100755
index 46faba45d245bd951c80652af8f3eed8e56a8ab6..0000000000000000000000000000000000000000
--- a/report_coverage_l3_centralizedattackdetector.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/bash
-
-./report_coverage_all.sh | grep --color -E -i "^l3_centralizedattackdetector/.*$|$"
diff --git a/report_coverage_l3_distributedattackdetector.sh b/report_coverage_l3_distributedattackdetector.sh
deleted file mode 100755
index eff6b1ce5e96f4500ae0efc58e6da586cf36fd25..0000000000000000000000000000000000000000
--- a/report_coverage_l3_distributedattackdetector.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/bash
-
-./report_coverage_all.sh | grep --color -E -i "^l3_distributedattackdetector/.*$|$"
diff --git a/report_coverage_service.sh b/report_coverage_service.sh
deleted file mode 100755
index 160f52f126a78be5338456667a97ec9184be421a..0000000000000000000000000000000000000000
--- a/report_coverage_service.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/bash
-
-./report_coverage_all.sh | grep --color -E -i "^service/.*$|$"
diff --git a/run_tests_in_kubernetes.sh b/run_tests_in_kubernetes.sh
deleted file mode 100755
index 7cbbda122078ae1443fe9db8c51d7f47cd92c9d6..0000000000000000000000000000000000000000
--- a/run_tests_in_kubernetes.sh
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/bash
-
-IMAGE_NAME='integration_tester'
-IMAGE_TAG='latest'
-CI_REGISTRY_IMAGE='registry.gitlab.com/teraflow-h2020/controller'
-
-kubectl delete pod $(echo $IMAGE_NAME | sed -r 's/[^a-zA-Z0-9\.\-]/-/g') --wait=true --ignore-not-found=true
-kubectl get all
-kubectl run $(echo $IMAGE_NAME | sed -r 's/[^a-zA-Z0-9\.\-]/-/g') --image "$CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG" --restart=Never --rm -i
-kubectl get all
diff --git a/run_tests_locally.sh b/run_tests_locally.sh
deleted file mode 100755
index eb421c608578dfbc1cb7ef954e319d76437f3f43..0000000000000000000000000000000000000000
--- a/run_tests_locally.sh
+++ /dev/null
@@ -1,61 +0,0 @@
-#!/bin/bash
-
-PROJECTDIR=`pwd`
-
-cd $PROJECTDIR/src
-RCFILE=$PROJECTDIR/coverage/.coveragerc
-COVERAGEFILE=$PROJECTDIR/coverage/.coverage
-
-# configure the correct folder on the .coveragerc file
-cat $PROJECTDIR/coverage/.coveragerc.template | sed s+~/teraflow/controller+$PROJECTDIR+g > $RCFILE
-
-# Run unitary tests and analyze coverage of code at same time
-
-# Populate environment variables for context to use Redis in a development machine running Kubernetes
-# Uncomment below lines to create a Redis instance within Context for testing purposes.
-#kubectl delete namespace tf-dev
-#kubectl create namespace tf-dev
-#kubectl --namespace tf-dev apply -f ../manifests/contextservice.yaml
-#kubectl --namespace tf-dev expose deployment contextservice --port=6379 --type=NodePort --name=redis-tests
-#echo "Waiting 10 seconds for Redis to start..."
-#sleep 10
-export REDIS_SERVICE_HOST=$(kubectl get node kubernetes-master -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}')
-export REDIS_SERVICE_PORT=$(kubectl get service redis-tests --namespace tf-dev -o 'jsonpath={.spec.ports[?(@.port==6379)].nodePort}')
-
-# First destroy old coverage file
-rm -f $COVERAGEFILE
-
-coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \
-    common/orm/tests/test_unitary.py \
-    common/message_broker/tests/test_unitary.py \
-    common/rpc_method_wrapper/tests/test_unitary.py
-
-coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \
-    context/tests/test_unitary.py
-
-coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \
-    device/tests/test_unitary.py
-
-coverage run --rcfile=$RCFILE --append -m pytest -s --log-level=INFO --verbose \
-    l3_centralizedattackdetector/tests/test_unitary.py
-
-coverage run --rcfile=$RCFILE --append -m pytest -s --log-level=INFO --verbose \
-    l3_distributedattackdetector/tests/test_unitary.py
-
-coverage run --rcfile=$RCFILE --append -m pytest -s --log-level=INFO --verbose \
-    l3_attackmitigator/tests/test_unitary.py
-
-coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \
-    opticalcentralizedattackdetector/tests/test_unitary.py
-
-coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \
-    dbscanserving/tests/test_unitary.py
-
-coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \
-    opticalattackmitigator/tests/test_unitary.py
-
-coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \
-    service/tests/test_unitary.py
-
-coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \
-    compute/tests/test_unitary.py
diff --git a/scripts/add_license_header_to_files.sh b/scripts/add_license_header_to_files.sh
new file mode 100755
index 0000000000000000000000000000000000000000..4da23894414cfc57cda9ea1277e144f8e7244056
--- /dev/null
+++ b/scripts/add_license_header_to_files.sh
@@ -0,0 +1,31 @@
+#!/bin/bash
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+docker pull ghcr.io/google/addlicense:latest
+docker run -it -v ${PWD}:/src ghcr.io/google/addlicense \
+    -l apache -c "H2020 TeraFlow (https://www.teraflow-h2020.eu/)" -y 2021-2023 \
+    -ignore "data/*" -ignore "data/**" -ignore "tmp/*" -ignore "tmp/**" -ignore "manifests/cttc-ols/*" \
+    -ignore "coverage/*" -ignore "coverage/**" -ignore ".vscode/*" -ignore ".vscode/**" \
+    -ignore ".git/*" -ignore ".git/**" -ignore "proto/uml/*" -ignore "proto/uml/**" \
+    -ignore "src/**/__pycache__/*" -ignore "src/**/__pycache__/**" \
+    -ignore "src/.benchmarks/*" -ignore "src/.benchmarks/**" -ignore ".benchmarks/*" -ignore ".benchmarks/**" \
+    -ignore "src/.pytest_cache/*" -ignore "src/.pytest_cache/**" -ignore ".pytest_cache/*" -ignore ".pytest_cache/**" \
+    -ignore "src/**/target/generated-sources/grpc/*" -ignore "src/**/target/generated-sources/grpc/**" \
+    -ignore "src/**/*_pb2.py" -ignore "src/**/*_pb2_grpc.py" \
+    -ignore "src/device/service/drivers/openconfig/templates/**/*.xml" \
+    -ignore "src/dlt/*" -ignore "src/dlt/**" \
+    -ignore "src/**/.mvn/*" -ignore "src/**/.mvn/**" \
+    *
diff --git a/scripts/report_coverage_all.sh b/scripts/report_coverage_all.sh
new file mode 100755
index 0000000000000000000000000000000000000000..a7e4797f3118a03c5f4db7eb384a67bdea4d795a
--- /dev/null
+++ b/scripts/report_coverage_all.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+PROJECTDIR=`pwd`
+
+cd $(dirname $0)/src
+RCFILE=$PROJECTDIR/coverage/.coveragerc
+
+echo
+echo "Coverage report:"
+echo "----------------"
+coverage report --rcfile=$RCFILE --sort cover --show-missing --skip-covered
+#coverage html --rcfile=$RCFILE
+#coverage xml --rcfile=$RCFILE
diff --git a/scripts/report_coverage_common.sh b/scripts/report_coverage_common.sh
new file mode 100755
index 0000000000000000000000000000000000000000..f69c57a01aa83a75263a2dc888951aeaf4867ca2
--- /dev/null
+++ b/scripts/report_coverage_common.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+./report_coverage_all.sh | grep --color -E -i "^common/.*$|$"
diff --git a/scripts/report_coverage_compute.sh b/scripts/report_coverage_compute.sh
new file mode 100755
index 0000000000000000000000000000000000000000..15e9e3422e9c2674206dfd17fec9a4396ea77cca
--- /dev/null
+++ b/scripts/report_coverage_compute.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+./report_coverage_all.sh | grep --color -E -i "^compute/.*$|$"
diff --git a/scripts/report_coverage_context.sh b/scripts/report_coverage_context.sh
new file mode 100755
index 0000000000000000000000000000000000000000..b39c45833189ca755a088983e9fbe341bb6dd471
--- /dev/null
+++ b/scripts/report_coverage_context.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+./report_coverage_all.sh | grep --color -E -i "^context/.*$|$"
diff --git a/scripts/report_coverage_device.sh b/scripts/report_coverage_device.sh
new file mode 100755
index 0000000000000000000000000000000000000000..d7a1dc0841f9fb2fa495363b248747668d704886
--- /dev/null
+++ b/scripts/report_coverage_device.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+./report_coverage_all.sh | grep --color -E -i "^device/.*$|$"
diff --git a/scripts/report_coverage_l3_attackmitigator.sh b/scripts/report_coverage_l3_attackmitigator.sh
new file mode 100755
index 0000000000000000000000000000000000000000..c34283dbb91e21099ac5246566a3698c280b4725
--- /dev/null
+++ b/scripts/report_coverage_l3_attackmitigator.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+./report_coverage_all.sh | grep --color -E -i "^l3_attackmitigator/.*$|$"
diff --git a/scripts/report_coverage_l3_centralizedattackdetector.sh b/scripts/report_coverage_l3_centralizedattackdetector.sh
new file mode 100755
index 0000000000000000000000000000000000000000..1060bae05eb383aea5d9caf6bd1b1a9e5e4cfeaa
--- /dev/null
+++ b/scripts/report_coverage_l3_centralizedattackdetector.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+./report_coverage_all.sh | grep --color -E -i "^l3_centralizedattackdetector/.*$|$"
diff --git a/scripts/report_coverage_l3_distributedattackdetector.sh b/scripts/report_coverage_l3_distributedattackdetector.sh
new file mode 100755
index 0000000000000000000000000000000000000000..94c2a16f4c6ff9b0a6adfd0a45b59a208297c327
--- /dev/null
+++ b/scripts/report_coverage_l3_distributedattackdetector.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+./report_coverage_all.sh | grep --color -E -i "^l3_distributedattackdetector/.*$|$"
diff --git a/scripts/report_coverage_service.sh b/scripts/report_coverage_service.sh
new file mode 100755
index 0000000000000000000000000000000000000000..4a3c5cc067d009d6354939c06d9002f038bc0441
--- /dev/null
+++ b/scripts/report_coverage_service.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+./report_coverage_all.sh | grep --color -E -i "^service/.*$|$"
diff --git a/scripts/run_tests_in_kubernetes.sh b/scripts/run_tests_in_kubernetes.sh
new file mode 100755
index 0000000000000000000000000000000000000000..fc0e1425731bf333a88eca6b65a3fc12122c7768
--- /dev/null
+++ b/scripts/run_tests_in_kubernetes.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+IMAGE_NAME='integration_tester'
+IMAGE_TAG='latest'
+CI_REGISTRY_IMAGE='registry.gitlab.com/teraflow-h2020/controller'
+
+kubectl delete pod $(echo $IMAGE_NAME | sed -r 's/[^a-zA-Z0-9\.\-]/-/g') --wait=true --ignore-not-found=true
+kubectl get all
+kubectl run $(echo $IMAGE_NAME | sed -r 's/[^a-zA-Z0-9\.\-]/-/g') --image "$CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG" --restart=Never --rm -i
+kubectl get all
diff --git a/scripts/run_tests_locally-compute.sh b/scripts/run_tests_locally-compute.sh
new file mode 100755
index 0000000000000000000000000000000000000000..48ce6e232a8005ee37fce8a0dbd9f7aed4cf83dc
--- /dev/null
+++ b/scripts/run_tests_locally-compute.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+PROJECTDIR=`pwd`
+
+cd $PROJECTDIR/src
+RCFILE=$PROJECTDIR/coverage/.coveragerc
+
+# Run unitary tests and analyze coverage of code at same time
+
+# Useful flags for pytest:
+#-o log_cli=true -o log_file=service.log -o log_file_level=DEBUG
+
+coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \
+    compute/tests/test_unitary.py
diff --git a/scripts/run_tests_locally-device.sh b/scripts/run_tests_locally-device.sh
new file mode 100755
index 0000000000000000000000000000000000000000..ba6c0b6a58031720addc17cc0de9169e592099f5
--- /dev/null
+++ b/scripts/run_tests_locally-device.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+PROJECTDIR=`pwd`
+
+cd $PROJECTDIR/src
+RCFILE=$PROJECTDIR/coverage/.coveragerc
+
+# Run unitary tests and analyze coverage of code at same time
+
+# Useful flags for pytest:
+#-o log_cli=true -o log_file=device.log -o log_file_level=DEBUG
+
+coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \
+    device/tests/test_unitary.py
diff --git a/scripts/run_tests_locally-service.sh b/scripts/run_tests_locally-service.sh
new file mode 100755
index 0000000000000000000000000000000000000000..853eb97673e9e2a3a3fa28d025bd8af9ef4ea6cf
--- /dev/null
+++ b/scripts/run_tests_locally-service.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+PROJECTDIR=`pwd`
+
+cd $PROJECTDIR/src
+RCFILE=$PROJECTDIR/coverage/.coveragerc
+
+# Run unitary tests and analyze coverage of code at same time
+
+# Useful flags for pytest:
+#-o log_cli=true -o log_file=service.log -o log_file_level=DEBUG
+
+coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \
+    service/tests/test_unitary.py
diff --git a/scripts/run_tests_locally.sh b/scripts/run_tests_locally.sh
new file mode 100755
index 0000000000000000000000000000000000000000..1655d875e4f3cbb5922b773094422d32dff1ee2e
--- /dev/null
+++ b/scripts/run_tests_locally.sh
@@ -0,0 +1,96 @@
+#!/bin/bash
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+PROJECTDIR=`pwd`
+
+cd $PROJECTDIR/src
+RCFILE=$PROJECTDIR/coverage/.coveragerc
+COVERAGEFILE=$PROJECTDIR/coverage/.coverage
+
+# configure the correct folder on the .coveragerc file
+cat $PROJECTDIR/coverage/.coveragerc.template | sed s+~/teraflow/controller+$PROJECTDIR+g > $RCFILE
+
+# Run unitary tests and analyze coverage of code at same time
+
+# Set the name of the Kubernetes namespace and hostname to use.
+K8S_NAMESPACE="tf-dev"
+K8S_HOSTNAME="kubernetes-master"
+# Populate environment variables for context to use Redis in a development machine running Kubernetes
+# Uncomment below lines to create a Redis instance within Context for testing purposes.
+#kubectl delete namespace $K8S_NAMESPACE
+#kubectl create namespace $K8S_NAMESPACE
+#kubectl --namespace $K8S_NAMESPACE apply -f ../manifests/contextservice.yaml
+#kubectl --namespace $K8S_NAMESPACE apply -f ../manifests/monitoringservice.yaml
+#kubectl create secret generic influxdb-secrets --namespace=$K8S_NAMESPACE --from-literal=INFLUXDB_DB="monitoring" --from-literal=INFLUXDB_ADMIN_USER="teraflow" --from-literal=INFLUXDB_ADMIN_PASSWORD="teraflow" --from-literal=INFLUXDB_HTTP_AUTH_ENABLED="True"
+#kubectl create secret generic monitoring-secrets --namespace=$K8S_NAMESPACE --from-literal=INFLUXDB_DATABASE="monitoring" --from-literal=INFLUXDB_USER="teraflow" --from-literal=INFLUXDB_PASSWORD="teraflow" --from-literal=INFLUXDB_HOSTNAME="localhost"
+#kubectl --namespace $K8S_NAMESPACE expose deployment contextservice --port=6379 --type=NodePort --name=redis-tests
+#kubectl --namespace $K8S_NAMESPACE expose deployment monitoringservice --port=8086 --type=NodePort --name=influx-tests
+#echo "Waiting 10 seconds for Redis/Influx to start..."
+#sleep 10
+export REDIS_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}')
+export REDIS_SERVICE_PORT=$(kubectl get service redis-tests --namespace $K8S_NAMESPACE -o 'jsonpath={.spec.ports[?(@.port==6379)].nodePort}')
+export INFLUXDB_HOSTNAME=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}')
+export INFLUXDB_PORT=$(kubectl get service influx-tests --namespace $K8S_NAMESPACE -o 'jsonpath={.spec.ports[?(@.port==8086)].nodePort}')
+export INFLUXDB_USER=$(kubectl --namespace $K8S_NAMESPACE get secrets influxdb-secrets -o jsonpath='{.data.INFLUXDB_ADMIN_USER}' | base64 --decode)
+export INFLUXDB_PASSWORD=$(kubectl --namespace $K8S_NAMESPACE get secrets influxdb-secrets -o jsonpath='{.data.INFLUXDB_ADMIN_PASSWORD}' | base64 --decode)
+export INFLUXDB_DATABASE=$(kubectl --namespace $K8S_NAMESPACE get secrets influxdb-secrets -o jsonpath='{.data.INFLUXDB_DB}' | base64 --decode)
+
+# First destroy old coverage file
+rm -f $COVERAGEFILE
+
+# Useful flags for pytest:
+#-o log_cli=true -o log_file=device.log -o log_file_level=DEBUG
+
+coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \
+    common/orm/tests/test_unitary.py \
+    common/message_broker/tests/test_unitary.py \
+    common/rpc_method_wrapper/tests/test_unitary.py
+
+coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \
+    context/tests/test_unitary.py
+
+coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \
+    device/tests/test_unitary.py
+
+coverage run --rcfile=$RCFILE --append -m pytest -s --log-level=INFO --verbose \
+    l3_centralizedattackdetector/tests/test_unitary.py
+
+coverage run --rcfile=$RCFILE --append -m pytest -s --log-level=INFO --verbose \
+    l3_distributedattackdetector/tests/test_unitary.py
+
+coverage run --rcfile=$RCFILE --append -m pytest -s --log-level=INFO --verbose \
+    l3_attackmitigator/tests/test_unitary.py
+
+coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \
+    opticalcentralizedattackdetector/tests/test_unitary.py
+
+coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \
+    dbscanserving/tests/test_unitary.py
+
+coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \
+    opticalattackmitigator/tests/test_unitary.py
+
+coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \
+    service/tests/test_unitary.py
+
+coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \
+    compute/tests/test_unitary.py
+
+coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \
+    webui/tests/test_unitary.py
+
+coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \
+    monitoring/tests/test_unitary.py
diff --git a/src/__init__.py b/src/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/__init__.py
+++ b/src/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/automation/.gitlab-ci.yml b/src/automation/.gitlab-ci.yml
index 0e87fa4b3a726ddf9ae79c717487ae7a7fedd0e6..76cd4ea0d07496af864275cd63c5fbd8f94b7659 100644
--- a/src/automation/.gitlab-ci.yml
+++ b/src/automation/.gitlab-ci.yml
@@ -1,13 +1,35 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+variables:
+  IMAGE_NAME: 'automation'
+  REPORTS_PATH: "src/${IMAGE_NAME}/reports"
+  BUILD_ENV: build.env
+
 # Package application needed to run tests & build the image on next stage
 build automation:
-  variables:
-    IMAGE_NAME: 'automation' # name of the microservice
-    IMAGE_TAG: '0.0.1' # tag of the container image (production, development, etc)
   stage: build
   script:
+    - export IMAGE_TAG=$(grep -m1 '<version>' ./src/$IMAGE_NAME/pom.xml | grep -oP  '(?<=>).*(?=<)')
+    - echo "IMAGE_TAG=${IMAGE_TAG}" >> ${BUILD_ENV}
+    - cat ${BUILD_ENV}
     - docker build -t "$IMAGE_NAME:$IMAGE_TAG" -f ./src/$IMAGE_NAME/src/main/docker/Dockerfile.multistage.jvm ./src/$IMAGE_NAME/ --target builder
   after_script:
     - docker images --filter="dangling=true" --quiet | xargs -r docker rmi
+  artifacts:
+    reports:
+      dotenv: ${BUILD_ENV}
   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"'
@@ -19,20 +41,37 @@ build automation:
 # Run tests, build & push the image
 unit_test automation:
   variables:
-    IMAGE_NAME: 'automation' # name of the microservice
-    IMAGE_TAG: '0.0.1' # tag of the container image (production, development, etc)
+    REPORTS_CONTAINER: "${IMAGE_NAME}-reports"
   stage: unit_test
   needs:
     - build automation
   before_script:
     - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
+    - docker rm ${REPORTS_CONTAINER} || true
   script:
+    - echo "Running tests for image ${IMAGE_TAG}"
     - docker build -t "$IMAGE_NAME:$IMAGE_TAG" -f ./src/$IMAGE_NAME/src/main/docker/Dockerfile.multistage.jvm ./src/$IMAGE_NAME/ --target unit-test
+    # Transfer JaCoCo and Surefire reports from within tests image
+    - docker create --name ${REPORTS_CONTAINER} "$IMAGE_NAME:$IMAGE_TAG"
+    - mkdir -p ${REPORTS_PATH}
+    - docker cp ${REPORTS_CONTAINER}:/app/target/site/jacoco/index.html ${REPORTS_PATH}/coverage.html
+    - docker cp ${REPORTS_CONTAINER}:/app/target/site/jacoco/jacoco.xml ${REPORTS_PATH}/jacoco.xml
+    - docker cp ${REPORTS_CONTAINER}:/app/target/surefire-reports/ ${REPORTS_PATH}/
+    - cat ${REPORTS_PATH}/coverage.html | grep -o 'Total[^%]*%' | sed 's/<.*>/ /; s/Total/JaCoCo Coverage Total:/'
+    - docker run -v "$(pwd)/src/${IMAGE_NAME}:/${IMAGE_NAME}" --rm registry.gitlab.com/haynes/jacoco2cobertura:1.0.7 python /opt/cover2cover.py ${IMAGE_NAME}/reports/jacoco.xml ${IMAGE_NAME}/src/main/java > ${REPORTS_PATH}/cobertura.xml
+    # Build final image
     - docker build -t "$IMAGE_NAME:$IMAGE_TAG" -f ./src/$IMAGE_NAME/src/main/docker/Dockerfile.multistage.jvm ./src/$IMAGE_NAME/ --target release
     - 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 rm -f $IMAGE_NAME
+    - docker rm ${REPORTS_CONTAINER}
+    - docker rm -f "$IMAGE_NAME:$IMAGE_TAG"
+  coverage: '/JaCoCo Coverage Total: ([0-9]{1,3})%/'
+  artifacts:
+    reports:
+      cobertura: ${REPORTS_PATH}/cobertura.xml
+      junit:
+        - ${REPORTS_PATH}/surefire-reports/TEST-*.xml
   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"'
@@ -50,12 +89,13 @@ deploy automation:
   script:
     - kubectl version
     - kubectl get all
+    - kubectl delete --ignore-not-found=true -f "manifests/automationservice.yaml"
     - kubectl apply -f "manifests/automationservice.yaml"
     - kubectl delete pods --selector app=automationservice
     - kubectl get all
   rules:
     - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && ($CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "develop" || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == $CI_DEFAULT_BRANCH)'
-      when: manual    
+      when: manual
     - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "develop"'
       when: manual
 
diff --git a/src/automation/pom.xml b/src/automation/pom.xml
index 042185673a6fd069252fcbe655d33a83ca7b9ff5..bedee7b7f23296fa4adb7585191d19b5a917d689 100644
--- a/src/automation/pom.xml
+++ b/src/automation/pom.xml
@@ -1,11 +1,27 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
 
   <groupId>eu.teraflow</groupId>
   <artifactId>automation</artifactId>
-  <version>0.0.1</version>
+  <version>0.2.0</version>
   <name>TeraFlow Automation Component</name>
   <description>TeraFlow Automation Component</description>
 
@@ -274,6 +290,15 @@
           <groupId>org.jacoco</groupId>
           <artifactId>jacoco-maven-plugin</artifactId>
           <version>${jacoco.version}</version>
+          <configuration>
+          <excludes>
+            <exclude>automation/*</exclude>
+            <exclude>context/*</exclude>
+            <exclude>device/*</exclude>
+            <exclude>monitoring/*</exclude>
+            <exclude>kpi_sample_types/*</exclude>
+          </excludes>
+          </configuration>
           <executions>
             <execution>
               <id>instrument-unit-tests</id>
@@ -400,7 +425,7 @@
             <execution>
               <phase>compile</phase>
               <goals>
-                <goal>check</goal>
+                <goal>apply</goal>
               </goals>
             </execution>
           </executions>
diff --git a/src/automation/src/main/docker/Dockerfile.multistage.jvm b/src/automation/src/main/docker/Dockerfile.multistage.jvm
index 696c8e18468b1cd57036d3f340f315cd69cc9333..111527d46d08891480894240f381e7bd6501f051 100644
--- a/src/automation/src/main/docker/Dockerfile.multistage.jvm
+++ b/src/automation/src/main/docker/Dockerfile.multistage.jvm
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 # Multi-stage Docker image build
 
 # Stage 1
@@ -15,7 +29,7 @@ RUN mvn --errors --batch-mode package -Dmaven.test.skip=true
 # Stage 2
 FROM builder AS unit-test
 
-RUN mvn --errors --batch-mode test
+RUN mvn --errors --batch-mode -Pgenerate-consolidated-coverage verify
 
 # Stage 3
 FROM registry.access.redhat.com/ubi8/ubi-minimal:8.4 AS release
diff --git a/src/automation/src/main/java/eu/teraflow/automation/AutomationConfiguration.java b/src/automation/src/main/java/eu/teraflow/automation/AutomationConfiguration.java
index 7d50b7d5138e59f102a76970c7d8e94d08a61fef..55f5cce25e8a03a1a5c0da567f16bd858c668d0f 100644
--- a/src/automation/src/main/java/eu/teraflow/automation/AutomationConfiguration.java
+++ b/src/automation/src/main/java/eu/teraflow/automation/AutomationConfiguration.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package eu.teraflow.automation;
 
 import io.smallrye.config.ConfigMapping;
diff --git a/src/automation/src/main/java/eu/teraflow/automation/AutomationGateway.java b/src/automation/src/main/java/eu/teraflow/automation/AutomationGateway.java
index 045ff44928a52f296ed8290b37eaf385857f52b6..1533643d14cb0d78ababe57242a4f01a50c3b0b8 100644
--- a/src/automation/src/main/java/eu/teraflow/automation/AutomationGateway.java
+++ b/src/automation/src/main/java/eu/teraflow/automation/AutomationGateway.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package eu.teraflow.automation;
 
 import automation.AutomationService;
diff --git a/src/automation/src/main/java/eu/teraflow/automation/AutomationGatewayImpl.java b/src/automation/src/main/java/eu/teraflow/automation/AutomationGatewayImpl.java
index 8e98051560d736e1305e6392c033666c40ddebf5..7f403459a2ff28809046a292c7c64d413c066243 100644
--- a/src/automation/src/main/java/eu/teraflow/automation/AutomationGatewayImpl.java
+++ b/src/automation/src/main/java/eu/teraflow/automation/AutomationGatewayImpl.java
@@ -1,9 +1,25 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package eu.teraflow.automation;
 
 import automation.Automation;
 import context.ContextOuterClass;
-import eu.teraflow.automation.device.model.DeviceId;
-import eu.teraflow.automation.device.model.Uuid;
+import eu.teraflow.automation.device.model.Device;
+import eu.teraflow.automation.model.DeviceRoleId;
 import io.quarkus.grpc.GrpcService;
 import io.smallrye.mutiny.Uni;
 import javax.inject.Inject;
@@ -12,10 +28,12 @@ import javax.inject.Inject;
 public class AutomationGatewayImpl implements AutomationGateway {
 
     private final AutomationService automationService;
+    private final Serializer serializer;
 
     @Inject
-    public AutomationGatewayImpl(AutomationService automationService) {
+    public AutomationGatewayImpl(AutomationService automationService, Serializer serializer) {
         this.automationService = automationService;
+        this.serializer = serializer;
     }
 
     @Override
@@ -32,15 +50,13 @@ public class AutomationGatewayImpl implements AutomationGateway {
 
     @Override
     public Uni<Automation.DeviceRoleState> ztpAdd(Automation.DeviceRole request) {
+        final var devRoleId = request.getDevRoleId().getDevRoleId().getUuid();
+        final var deviceId = serializer.deserialize(request.getDevRoleId().getDevId());
 
-        automationService.addDevice(getDeviceId(request.getDevRoleId().getDevId()));
-
-        return Uni.createFrom()
-                .item(
-                        () ->
-                                Automation.DeviceRoleState.newBuilder()
-                                        .setDevRoleId(request.getDevRoleId())
-                                        .build());
+        return automationService
+                .addDevice(deviceId)
+                .onItem()
+                .transform( device -> transformToDeviceRoleState(device, devRoleId));
     }
 
     @Override
@@ -68,10 +84,15 @@ public class AutomationGatewayImpl implements AutomationGateway {
         return Uni.createFrom().item(() -> Automation.DeviceDeletionResult.newBuilder().build());
     }
 
-    private DeviceId getDeviceId(ContextOuterClass.DeviceId serializedDeviceId) {
+    // TODO When `DeviceRoleState` domain object will be created, move this method to Serializer class and create related tests
+    private Automation.DeviceRoleState transformToDeviceRoleState(Device device, String devRoleId){
 
-        Uuid uuid = new Uuid(serializedDeviceId.getDeviceUuid().getUuid());
+        final var deviceRoleId = new DeviceRoleId(devRoleId, device.getDeviceId());
+        final var serializeDeviceRoleId = serializer.serialize(deviceRoleId);
 
-        return new DeviceId(uuid);
+        return Automation.DeviceRoleState.newBuilder()
+                .setDevRoleId(serializeDeviceRoleId)
+                .setDevRoleState(Automation.ZtpDeviceState.ZTP_DEV_STATE_CREATED)
+                .build();
     }
-}
+}
\ No newline at end of file
diff --git a/src/automation/src/main/java/eu/teraflow/automation/AutomationService.java b/src/automation/src/main/java/eu/teraflow/automation/AutomationService.java
index 1c76fc4876bc31a49c639c34560bbd81621d5020..38d7420ed3ba865ae141843c9ba41a6a345e8f31 100644
--- a/src/automation/src/main/java/eu/teraflow/automation/AutomationService.java
+++ b/src/automation/src/main/java/eu/teraflow/automation/AutomationService.java
@@ -1,10 +1,25 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package eu.teraflow.automation;
 
-import eu.teraflow.automation.device.Device;
-import eu.teraflow.automation.device.model.DeviceId;
+import eu.teraflow.automation.device.model.Device;
 import io.smallrye.mutiny.Uni;
 
 public interface AutomationService {
 
-    Uni<Device> addDevice(DeviceId deviceId);
+    Uni<Device> addDevice(String deviceId);
 }
diff --git a/src/automation/src/main/java/eu/teraflow/automation/AutomationServiceImpl.java b/src/automation/src/main/java/eu/teraflow/automation/AutomationServiceImpl.java
index fc62e77ec436f5274f19c6156eb260db90b270aa..433dffbc727f685fb0d8f80ae0c2e4dc559bae87 100644
--- a/src/automation/src/main/java/eu/teraflow/automation/AutomationServiceImpl.java
+++ b/src/automation/src/main/java/eu/teraflow/automation/AutomationServiceImpl.java
@@ -1,9 +1,24 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package eu.teraflow.automation;
 
 import eu.teraflow.automation.context.ContextService;
-import eu.teraflow.automation.device.Device;
 import eu.teraflow.automation.device.DeviceService;
-import eu.teraflow.automation.device.model.*;
+import eu.teraflow.automation.device.model.Device;
 import io.smallrye.mutiny.Uni;
 import javax.enterprise.context.ApplicationScoped;
 import javax.inject.Inject;
@@ -23,34 +38,46 @@ public class AutomationServiceImpl implements AutomationService {
     }
 
     @Override
-    public Uni<Device> addDevice(DeviceId deviceId) {
+    public Uni<Device> addDevice(String deviceId) {
 
-        final var deviceUni = contextService.getDevice(deviceId);
+        final var deserializedDeviceUni = contextService.getDevice(deviceId);
 
-        deviceUni
+        deserializedDeviceUni
+                // TODO fix subscribe
                 .subscribe()
                 .with(
                         device -> {
-                            if (!(device
-                                    .getDeviceOperationalStatus()
-                                    .toString()
-                                    .equals(DeviceOperationalStatus.ENABLED.toString()))) {
+                            final var id = deviceId;
+
+                            if (!device.isEnabled()) {
+                                LOGGER.infof("Retrieved %s", device);
 
-                                final var initialConfig = deviceService.getInitialConfiguration(deviceId);
+                                final var initialConfiguration =
+                                        deviceService.getInitialConfiguration(device.getDeviceId());
 
-                                device.setDeviceOperationalStatus(DeviceOperationalStatus.ENABLED);
+                                device.enableDevice();
+                                LOGGER.infof("Enabled device [%s]", id);
 
-                                initialConfig
+                                initialConfiguration
                                         .subscribe()
                                         .with(
                                                 deviceConfig -> {
                                                     device.setDeviceConfiguration(deviceConfig);
-                                                    deviceService.configureDevice(device);
-                                                    LOGGER.infof("Received response %s", device);
+                                                    final var configuredDeviceIdUni = deviceService.configureDevice(device);
+
+                                                    configuredDeviceIdUni
+                                                            .subscribe()
+                                                            .with(
+                                                                    configuredDeviceId ->
+                                                                            LOGGER.infof(
+                                                                                    "Device [%s] has been enabled and configured successfully with %s.\n",
+                                                                                    id, deviceConfig));
                                                 });
+                            } else {
+                                LOGGER.infof("%s has been already enabled. Ignoring...", device);
                             }
                         });
 
-        return deviceUni;
+        return deserializedDeviceUni;
     }
 }
diff --git a/src/automation/src/main/java/eu/teraflow/automation/ContextSubscriber.java b/src/automation/src/main/java/eu/teraflow/automation/ContextSubscriber.java
index 195634d00bb17debb623e164d975288488a9a240..cb80a6428fbab7ca69952eb5ead760ae815e99c6 100644
--- a/src/automation/src/main/java/eu/teraflow/automation/ContextSubscriber.java
+++ b/src/automation/src/main/java/eu/teraflow/automation/ContextSubscriber.java
@@ -1,9 +1,23 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package eu.teraflow.automation;
 
 import eu.teraflow.automation.context.ContextService;
-import eu.teraflow.automation.device.model.DeviceEvent;
 import io.quarkus.runtime.StartupEvent;
-import io.smallrye.mutiny.Multi;
 import java.time.Duration;
 import javax.enterprise.context.ApplicationScoped;
 import javax.enterprise.event.Observes;
@@ -31,28 +45,53 @@ public class ContextSubscriber {
 
     public void listenForDeviceEvents() {
 
-        Multi<DeviceEvent> deviceEventsMulti =
-                contextService
-                        .getDeviceEvents()
-                        .onFailure()
-                        .retry()
-                        .withBackOff(Duration.ofSeconds(1))
-                        .withJitter(0.2)
-                        .atMost(10);
-
-        deviceEventsMulti
-                .onItem()
-                .transformToUniAndConcatenate(
-                        deviceEvent -> automationService.addDevice(deviceEvent.getDeviceId()));
+        contextService
+                .getDeviceEvents()
+                .onFailure()
+                .retry()
+                .withBackOff(Duration.ofSeconds(1))
+                .withJitter(0.2)
+                .atMost(10)
+                .onFailure()
+                .recoverWithCompletion()
+                .subscribe()
+                .with(
+                        deviceEvent -> {
+                            LOGGER.debugf("Received %s via contextService:getDeviceEvents", deviceEvent);
+                            if (deviceEvent == null || deviceEvent.getEvent() == null) {
+                                LOGGER.warn("Received device event is null, ignoring...");
+                                return;
+                            }
+                            final var eventType = deviceEvent.getEvent().getEventTypeEnum();
+                            final var deviceId = deviceEvent.getDeviceId();
+                            final var event = deviceEvent.getEvent();
+
+                            switch (eventType) {
+                                case CREATE:
+                                    LOGGER.infof("Received %s for device [%s]", event, deviceId);
+                                    automationService.addDevice(deviceEvent.getDeviceId());
+                                    break;
+
+                                case UPDATE:
+                                case REMOVE:
+                                case UNDEFINED:
+                                    {
+                                        LOGGER.warnf(
+                                                "Received %s for device [%s]. [%s] event handling is not yet implemented, ignoring...",
+                                                event, deviceId, eventType);
+                                    }
+                                    break;
+                            }
+                        });
     }
 
     void onStart(@Observes StartupEvent ev) {
 
         if (automationConfiguration.shouldSubscribeToContextComponent()) {
-            LOGGER.info("Listening for Device events...");
+            LOGGER.info("Subscribing to Context service for device events...");
             listenForDeviceEvents();
         } else {
-            LOGGER.info("Not listening for Device events...");
+            LOGGER.info("Not subscribing to Context service for device events...");
         }
     }
 }
diff --git a/src/automation/src/main/java/eu/teraflow/automation/Serializer.java b/src/automation/src/main/java/eu/teraflow/automation/Serializer.java
new file mode 100644
index 0000000000000000000000000000000000000000..1cabde5bf714b4427e4abb5551b39f30b5474a79
--- /dev/null
+++ b/src/automation/src/main/java/eu/teraflow/automation/Serializer.java
@@ -0,0 +1,307 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package eu.teraflow.automation;
+
+import automation.Automation;
+import context.ContextOuterClass;
+import context.ContextOuterClass.DeviceId;
+import context.ContextOuterClass.DeviceOperationalStatusEnum;
+import context.ContextOuterClass.Uuid;
+import eu.teraflow.automation.context.model.Event;
+import eu.teraflow.automation.context.model.EventTypeEnum;
+import eu.teraflow.automation.device.model.ConfigActionEnum;
+import eu.teraflow.automation.device.model.ConfigRule;
+import eu.teraflow.automation.device.model.Device;
+import eu.teraflow.automation.device.model.DeviceConfig;
+import eu.teraflow.automation.device.model.DeviceEvent;
+import eu.teraflow.automation.device.model.DeviceOperationalStatus;
+import eu.teraflow.automation.model.DeviceRole;
+import eu.teraflow.automation.model.DeviceRoleId;
+import eu.teraflow.automation.model.DeviceRoleType;
+import java.util.stream.Collectors;
+import javax.inject.Singleton;
+
+@Singleton
+public class Serializer {
+
+    public DeviceId serializeDeviceId(String expectedDeviceId) {
+        final var builder = DeviceId.newBuilder();
+        final var uuid = serializeUuid(expectedDeviceId);
+
+        builder.setDeviceUuid(uuid);
+
+        return builder.build();
+    }
+
+    public String deserialize(DeviceId deviceId) {
+        return deviceId.getDeviceUuid().getUuid();
+    }
+
+    public Automation.DeviceRoleId serialize(DeviceRoleId deviceRoleId) {
+        final var builder = Automation.DeviceRoleId.newBuilder();
+
+        final var deviceRoleDevRoleId = deviceRoleId.getId();
+        final var deviceRoleDeviceId = deviceRoleId.getDeviceId();
+
+        final var deviceRoleDevRoleIdUuid = serializeUuid(deviceRoleDevRoleId);
+        final var deviceRoleDeviceIdUuid = serializeUuid(deviceRoleDeviceId);
+
+        final var deviceId = DeviceId.newBuilder().setDeviceUuid(deviceRoleDeviceIdUuid);
+
+        builder.setDevRoleId(deviceRoleDevRoleIdUuid);
+        builder.setDevId(deviceId);
+
+        return builder.build();
+    }
+
+    public DeviceRoleId deserialize(Automation.DeviceRoleId deviceRoleId) {
+        final var devRoleId = deserialize(deviceRoleId.getDevRoleId());
+        final var devId = deserialize(deviceRoleId.getDevId());
+
+        return new DeviceRoleId(devRoleId, devId);
+    }
+
+    public Automation.DeviceRoleType serialize(DeviceRoleType deviceRoleType) {
+        switch (deviceRoleType) {
+            case NONE:
+                return Automation.DeviceRoleType.NONE;
+            case DEV_OPS:
+                return Automation.DeviceRoleType.DEV_OPS;
+            case DEV_CONF:
+                return Automation.DeviceRoleType.DEV_CONF;
+            case PIPELINE_CONF:
+                return Automation.DeviceRoleType.PIPELINE_CONF;
+            default:
+                return Automation.DeviceRoleType.UNRECOGNIZED;
+        }
+    }
+
+    public DeviceRoleType deserialize(Automation.DeviceRoleType serializedDeviceRoleType) {
+        switch (serializedDeviceRoleType) {
+            case DEV_OPS:
+                return DeviceRoleType.DEV_OPS;
+            case DEV_CONF:
+                return DeviceRoleType.DEV_CONF;
+            case PIPELINE_CONF:
+                return DeviceRoleType.PIPELINE_CONF;
+            case NONE:
+            case UNRECOGNIZED:
+            default:
+                return DeviceRoleType.NONE;
+        }
+    }
+
+    public Automation.DeviceRole serialize(DeviceRole deviceRole) {
+        final var builder = Automation.DeviceRole.newBuilder();
+        final var serializedDeviceRoleId = serialize(deviceRole.getDeviceRoleId());
+        final var serializedDeviceRoleType = serialize(deviceRole.getType());
+
+        builder.setDevRoleId(serializedDeviceRoleId);
+        builder.setDevRoleType(serializedDeviceRoleType);
+
+        return builder.build();
+    }
+
+    public DeviceRole deserialize(Automation.DeviceRole deviceRole) {
+        final var deviceRoleId = deserialize(deviceRole.getDevRoleId());
+        final var deviceRoleType = deserialize(deviceRole.getDevRoleType());
+
+        return new DeviceRole(deviceRoleId, deviceRoleType);
+    }
+
+    public ContextOuterClass.EventTypeEnum serialize(EventTypeEnum eventTypeEnum) {
+        switch (eventTypeEnum) {
+            case CREATE:
+                return ContextOuterClass.EventTypeEnum.EVENTTYPE_CREATE;
+            case REMOVE:
+                return ContextOuterClass.EventTypeEnum.EVENTTYPE_REMOVE;
+            case UPDATE:
+                return ContextOuterClass.EventTypeEnum.EVENTTYPE_UPDATE;
+            case UNDEFINED:
+                return ContextOuterClass.EventTypeEnum.EVENTTYPE_UNDEFINED;
+            default:
+                return ContextOuterClass.EventTypeEnum.UNRECOGNIZED;
+        }
+    }
+
+    public EventTypeEnum deserialize(ContextOuterClass.EventTypeEnum serializedEventType) {
+        switch (serializedEventType) {
+            case EVENTTYPE_CREATE:
+                return EventTypeEnum.CREATE;
+            case EVENTTYPE_REMOVE:
+                return EventTypeEnum.REMOVE;
+            case EVENTTYPE_UPDATE:
+                return EventTypeEnum.UPDATE;
+            case EVENTTYPE_UNDEFINED:
+            case UNRECOGNIZED:
+            default:
+                return EventTypeEnum.UNDEFINED;
+        }
+    }
+
+    public ContextOuterClass.Event serialize(Event event) {
+        final var builder = ContextOuterClass.Event.newBuilder();
+
+        final var eventType = serialize(event.getEventTypeEnum());
+        builder.setEventType(eventType);
+        builder.setTimestamp(event.getTimestamp());
+
+        return builder.build();
+    }
+
+    public Event deserialize(ContextOuterClass.Event serializedEvent) {
+        final var timestamp = serializedEvent.getTimestamp();
+        final var eventType = deserialize(serializedEvent.getEventType());
+
+        return new Event(timestamp, eventType);
+    }
+
+    public ContextOuterClass.DeviceEvent serialize(DeviceEvent deviceEvent) {
+        final var builder = ContextOuterClass.DeviceEvent.newBuilder();
+        final var deviceIdUuid = serializeUuid(deviceEvent.getDeviceId());
+        final var deviceId = DeviceId.newBuilder().setDeviceUuid(deviceIdUuid);
+
+        builder.setDeviceId(deviceId);
+        builder.setEvent(serialize(deviceEvent.getEvent()));
+
+        return builder.build();
+    }
+
+    public DeviceEvent deserialize(ContextOuterClass.DeviceEvent deviceEvent) {
+        final var deviceId = deserialize(deviceEvent.getDeviceId());
+        final var event = deserialize(deviceEvent.getEvent());
+
+        return new DeviceEvent(deviceId, event);
+    }
+
+    public ContextOuterClass.ConfigActionEnum serialize(ConfigActionEnum configAction) {
+        switch (configAction) {
+            case SET:
+                return ContextOuterClass.ConfigActionEnum.CONFIGACTION_SET;
+            case DELETE:
+                return ContextOuterClass.ConfigActionEnum.CONFIGACTION_DELETE;
+            case UNDEFINED:
+            default:
+                return ContextOuterClass.ConfigActionEnum.CONFIGACTION_UNDEFINED;
+        }
+    }
+
+    public ConfigActionEnum deserialize(ContextOuterClass.ConfigActionEnum serializedConfigAction) {
+        switch (serializedConfigAction) {
+            case CONFIGACTION_SET:
+                return ConfigActionEnum.SET;
+            case CONFIGACTION_DELETE:
+                return ConfigActionEnum.DELETE;
+            case UNRECOGNIZED:
+            case CONFIGACTION_UNDEFINED:
+            default:
+                return ConfigActionEnum.UNDEFINED;
+        }
+    }
+
+    public ContextOuterClass.ConfigRule serialize(ConfigRule configRule) {
+        final var builder = ContextOuterClass.ConfigRule.newBuilder();
+
+        builder.setAction(serialize(configRule.getConfigActionEnum()));
+        builder.setResourceKey(configRule.getResourceKey());
+        builder.setResourceValue(configRule.getResourceValue());
+
+        return builder.build();
+    }
+
+    public ConfigRule deserialize(ContextOuterClass.ConfigRule configRule) {
+        final var configActionEnum = deserialize(configRule.getAction());
+
+        return new ConfigRule(
+                configActionEnum, configRule.getResourceKey(), configRule.getResourceValue());
+    }
+
+    public ContextOuterClass.DeviceConfig serialize(DeviceConfig deviceConfig) {
+        final var builder = ContextOuterClass.DeviceConfig.newBuilder();
+
+        final var serializedConfigRules =
+                deviceConfig.getConfigRules().stream().map(this::serialize).collect(Collectors.toList());
+        builder.addAllConfigRules(serializedConfigRules);
+
+        return builder.build();
+    }
+
+    public DeviceConfig deserialize(ContextOuterClass.DeviceConfig deviceConfig) {
+        final var configRules =
+                deviceConfig.getConfigRulesList().stream()
+                        .map(this::deserialize)
+                        .collect(Collectors.toList());
+
+        return new DeviceConfig(configRules);
+    }
+
+    public ContextOuterClass.DeviceOperationalStatusEnum serialize(DeviceOperationalStatus opStatus) {
+        switch (opStatus) {
+            case ENABLED:
+                return DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_ENABLED;
+            case DISABLED:
+                return DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_DISABLED;
+            case UNDEFINED:
+            default:
+                return DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_UNDEFINED;
+        }
+    }
+
+    public DeviceOperationalStatus deserialize(
+            ContextOuterClass.DeviceOperationalStatusEnum opStatus) {
+        switch (opStatus) {
+            case DEVICEOPERATIONALSTATUS_ENABLED:
+                return DeviceOperationalStatus.ENABLED;
+            case DEVICEOPERATIONALSTATUS_DISABLED:
+                return DeviceOperationalStatus.DISABLED;
+            case DEVICEOPERATIONALSTATUS_UNDEFINED:
+            case UNRECOGNIZED:
+            default:
+                return DeviceOperationalStatus.UNDEFINED;
+        }
+    }
+
+    public ContextOuterClass.Device serialize(Device device) {
+        final var builder = ContextOuterClass.Device.newBuilder();
+        final var deviceIdUuid = serializeUuid(device.getDeviceId());
+        final var deviceId = DeviceId.newBuilder().setDeviceUuid(deviceIdUuid);
+
+        builder.setDeviceId(deviceId);
+        builder.setDeviceType(device.getDeviceType());
+        builder.setDeviceConfig(serialize(device.getDeviceConfig()));
+        builder.setDeviceOperationalStatus(serialize(device.getDeviceOperationalStatus()));
+
+        return builder.build();
+    }
+
+    public Device deserialize(ContextOuterClass.Device device) {
+        final var id = deserialize(device.getDeviceId());
+        final var type = device.getDeviceType();
+        final var config = deserialize(device.getDeviceConfig());
+        final var operationalStatus = deserialize(device.getDeviceOperationalStatus());
+
+        return new Device(id, type, config, operationalStatus);
+    }
+
+    public Uuid serializeUuid(String uuid) {
+        return Uuid.newBuilder().setUuid(uuid).build();
+    }
+
+    public String deserialize(Uuid uuid) {
+        return uuid.getUuid();
+    }
+}
diff --git a/src/automation/src/main/java/eu/teraflow/automation/SimpleLivenessCheck.java b/src/automation/src/main/java/eu/teraflow/automation/SimpleLivenessCheck.java
index 28c896fbd97a63df67d5986a6d1a2fc53b1c62c6..056b844d5e702db37cf51cce7f86a9e387fa9c12 100644
--- a/src/automation/src/main/java/eu/teraflow/automation/SimpleLivenessCheck.java
+++ b/src/automation/src/main/java/eu/teraflow/automation/SimpleLivenessCheck.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package eu.teraflow.automation;
 
 import javax.enterprise.context.ApplicationScoped;
diff --git a/src/automation/src/main/java/eu/teraflow/automation/SimpleReadinessCheck.java b/src/automation/src/main/java/eu/teraflow/automation/SimpleReadinessCheck.java
index e542a3582855ae4a81273520f4075f9038f61d43..ef1b4c4efceeeb042adf50f0b20a688c5e1004da 100644
--- a/src/automation/src/main/java/eu/teraflow/automation/SimpleReadinessCheck.java
+++ b/src/automation/src/main/java/eu/teraflow/automation/SimpleReadinessCheck.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package eu.teraflow.automation;
 
 import javax.enterprise.context.ApplicationScoped;
diff --git a/src/automation/src/main/java/eu/teraflow/automation/context/ContextGateway.java b/src/automation/src/main/java/eu/teraflow/automation/context/ContextGateway.java
index e08a706edb8fda13ee6300fc55f3ab6a30043572..83bcc1afddeeeea8545b64f06d4157457d951e84 100644
--- a/src/automation/src/main/java/eu/teraflow/automation/context/ContextGateway.java
+++ b/src/automation/src/main/java/eu/teraflow/automation/context/ContextGateway.java
@@ -1,14 +1,29 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package eu.teraflow.automation.context;
 
-import eu.teraflow.automation.device.Device;
+import eu.teraflow.automation.device.model.Device;
 import eu.teraflow.automation.device.model.DeviceEvent;
-import eu.teraflow.automation.device.model.DeviceId;
 import io.smallrye.mutiny.Multi;
 import io.smallrye.mutiny.Uni;
 
 public interface ContextGateway {
 
-    Uni<Device> getDevice(DeviceId deviceId);
+    Uni<Device> getDevice(String deviceId);
 
     Multi<DeviceEvent> getDeviceEvents();
 }
diff --git a/src/automation/src/main/java/eu/teraflow/automation/context/ContextGatewayImpl.java b/src/automation/src/main/java/eu/teraflow/automation/context/ContextGatewayImpl.java
index 6b0ff132efe595f4ba4c618e5ec1697931c90022..1b11d09a4ce26c3c98af65aa4d5a13f45c049bb7 100644
--- a/src/automation/src/main/java/eu/teraflow/automation/context/ContextGatewayImpl.java
+++ b/src/automation/src/main/java/eu/teraflow/automation/context/ContextGatewayImpl.java
@@ -1,108 +1,64 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package eu.teraflow.automation.context;
 
 import context.ContextOuterClass;
-import context.ContextService;
-import eu.teraflow.automation.context.model.Event;
-import eu.teraflow.automation.context.model.EventTypeEnum;
-import eu.teraflow.automation.device.Device;
+import context.MutinyContextServiceGrpc.MutinyContextServiceStub;
+import eu.teraflow.automation.Serializer;
 import eu.teraflow.automation.device.model.*;
+import eu.teraflow.automation.device.model.Device;
 import io.quarkus.grpc.GrpcClient;
 import io.smallrye.mutiny.Multi;
 import io.smallrye.mutiny.Uni;
 import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+import org.jboss.logging.Logger;
 
 @ApplicationScoped
 public class ContextGatewayImpl implements ContextGateway {
+    private static final Logger LOGGER = Logger.getLogger(ContextGatewayImpl.class);
 
     @GrpcClient("context")
-    ContextService delegate;
-
-    @Override
-    public Multi<DeviceEvent> getDeviceEvents() {
+    MutinyContextServiceStub streamingDelegateContext;
 
-        final var serializedEmpty = getSerializedEmpty();
-        final var deviceEventsMulti = delegate.getDeviceEvents(serializedEmpty);
+    private final Serializer serializer;
 
-        return deviceEventsMulti.onItem().transform(this::getDeviceEvent);
+    @Inject
+    public ContextGatewayImpl(Serializer serializer) {
+        this.serializer = serializer;
     }
 
     @Override
-    public Uni<Device> getDevice(DeviceId deviceId) {
-        final var serializedDeviceId = getSerializedDeviceId(deviceId);
-        final var serializedDeviceUni = delegate.getDevice(serializedDeviceId);
-
-        return serializedDeviceUni.onItem().transform(this::getDevice);
-    }
-
-    private ContextOuterClass.Empty getSerializedEmpty() {
-
-        return ContextOuterClass.Empty.newBuilder().build();
-    }
-
-    private DeviceEvent getDeviceEvent(ContextOuterClass.DeviceEvent deviceEvent) {
-
-        final var serializedDeviceId = deviceEvent.getDeviceId();
-        final var deviceId = getDeviceId(serializedDeviceId);
-
-        final var serializedEvent = deviceEvent.getEvent();
-        final var event = getEvent(serializedEvent);
-
-        return new DeviceEvent(event, deviceId);
-    }
-
-    private Event getEvent(ContextOuterClass.Event event) {
-        return new Event(event.getTimestamp(), getEventType(event.getEventType()));
-    }
-
-    private EventTypeEnum getEventType(ContextOuterClass.EventTypeEnum eventType) {
-
-        if (eventType == ContextOuterClass.EventTypeEnum.EVENTTYPE_CREATE) {
-            return EventTypeEnum.CREATE;
-        } else if (eventType == ContextOuterClass.EventTypeEnum.EVENTTYPE_REMOVE) {
-            return EventTypeEnum.REMOVE;
-        } else if (eventType == ContextOuterClass.EventTypeEnum.EVENTTYPE_UPDATE) {
-            return EventTypeEnum.UPDATE;
-        } else {
-            return EventTypeEnum.UNDEFINED;
-        }
-    }
-
-    private Device getDevice(ContextOuterClass.Device device) {
-
-        final var deviceId = getDeviceId(device.getDeviceId());
-        final var deviceType = getDeviceType(device.getDeviceType());
-        final var deviceOperationalStatus =
-                getDeviceOperationalStatus(device.getDeviceOperationalStatus());
-
-        return new Device(deviceId, deviceType, deviceOperationalStatus);
-    }
-
-    private DeviceId getDeviceId(ContextOuterClass.DeviceId deviceId) {
-        Uuid uuid = new Uuid(deviceId.getDeviceUuid().getUuid());
-        return new DeviceId(uuid);
-    }
+    public Multi<DeviceEvent> getDeviceEvents() {
+        final var serializedEmpty = ContextOuterClass.Empty.newBuilder().build();
 
-    private DeviceType getDeviceType(String deviceType) {
-        return new DeviceType(deviceType);
+        return streamingDelegateContext
+                .getDeviceEvents(serializedEmpty)
+                .onItem()
+                .transform(serializer::deserialize);
     }
 
-    private DeviceOperationalStatus getDeviceOperationalStatus(
-            ContextOuterClass.DeviceOperationalStatusEnum deviceOperationalStatusEnum) {
-
-        if (deviceOperationalStatusEnum
-                == ContextOuterClass.DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_ENABLED) {
-            return DeviceOperationalStatus.ENABLED;
-        } else if (deviceOperationalStatusEnum
-                == ContextOuterClass.DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_DISABLED) {
-            return DeviceOperationalStatus.DISABLED;
-        } else {
-            return DeviceOperationalStatus.UNDEFINED;
-        }
-    }
+    @Override
+    public Uni<Device> getDevice(String deviceId) {
+        final var serializedDeviceId = serializer.serializeDeviceId(deviceId);
 
-    private ContextOuterClass.DeviceId getSerializedDeviceId(DeviceId deviceId) {
-        final var deviceIdUuid =
-                ContextOuterClass.Uuid.newBuilder().setUuid(deviceId.toString()).build();
-        return ContextOuterClass.DeviceId.newBuilder().setDeviceUuid(deviceIdUuid).build();
+        return streamingDelegateContext
+                .getDevice(serializedDeviceId)
+                .onItem()
+                .transform(serializer::deserialize);
     }
 }
diff --git a/src/automation/src/main/java/eu/teraflow/automation/context/ContextService.java b/src/automation/src/main/java/eu/teraflow/automation/context/ContextService.java
index 2a2cd61af345e71a216ef089c2a0ded5cf809bfa..8b690782aec7ca1ebf23af4c2d91946330314be6 100644
--- a/src/automation/src/main/java/eu/teraflow/automation/context/ContextService.java
+++ b/src/automation/src/main/java/eu/teraflow/automation/context/ContextService.java
@@ -1,14 +1,29 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package eu.teraflow.automation.context;
 
-import eu.teraflow.automation.device.Device;
+import eu.teraflow.automation.device.model.Device;
 import eu.teraflow.automation.device.model.DeviceEvent;
-import eu.teraflow.automation.device.model.DeviceId;
 import io.smallrye.mutiny.Multi;
 import io.smallrye.mutiny.Uni;
 
 public interface ContextService {
 
-    Uni<Device> getDevice(DeviceId deviceId);
+    Uni<Device> getDevice(String deviceId);
 
     Multi<DeviceEvent> getDeviceEvents();
 }
diff --git a/src/automation/src/main/java/eu/teraflow/automation/context/ContextServiceImpl.java b/src/automation/src/main/java/eu/teraflow/automation/context/ContextServiceImpl.java
index 3277964e869b382cf1ba392f8d9ac4d9bbc1459a..38a938833628cceb5d67c3f51daa06e93fa464d4 100644
--- a/src/automation/src/main/java/eu/teraflow/automation/context/ContextServiceImpl.java
+++ b/src/automation/src/main/java/eu/teraflow/automation/context/ContextServiceImpl.java
@@ -1,8 +1,23 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package eu.teraflow.automation.context;
 
-import eu.teraflow.automation.device.Device;
+import eu.teraflow.automation.device.model.Device;
 import eu.teraflow.automation.device.model.DeviceEvent;
-import eu.teraflow.automation.device.model.DeviceId;
 import io.smallrye.mutiny.Multi;
 import io.smallrye.mutiny.Uni;
 import javax.enterprise.context.ApplicationScoped;
@@ -19,14 +34,12 @@ public class ContextServiceImpl implements ContextService {
     }
 
     @Override
-    public Uni<Device> getDevice(DeviceId deviceId) {
-
+    public Uni<Device> getDevice(String deviceId) {
         return contextGateway.getDevice(deviceId);
     }
 
     @Override
     public Multi<DeviceEvent> getDeviceEvents() {
-
         return contextGateway.getDeviceEvents();
     }
 }
diff --git a/src/automation/src/main/java/eu/teraflow/automation/context/model/Event.java b/src/automation/src/main/java/eu/teraflow/automation/context/model/Event.java
index fa09c7899ab78d149a879ac616a8382a85a4f27d..974d8dc93ae83c7fccff234753edb3e9fa0ae05b 100644
--- a/src/automation/src/main/java/eu/teraflow/automation/context/model/Event.java
+++ b/src/automation/src/main/java/eu/teraflow/automation/context/model/Event.java
@@ -1,9 +1,26 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package eu.teraflow.automation.context.model;
 
 public class Event {
 
-    private double timestamp;
-    private EventTypeEnum eventType;
+    // TODO convert double to meaningful timestamp type
+    private final double timestamp;
+    private final EventTypeEnum eventType;
 
     public Event(double timestamp, EventTypeEnum eventType) {
         this.timestamp = timestamp;
@@ -17,4 +34,11 @@ public class Event {
     public EventTypeEnum getEventTypeEnum() {
         return eventType;
     }
+
+    @Override
+    public String toString() {
+        return String.format(
+                "%s{timestamp=\"%f\", eventType=\"%s\"}",
+                getClass().getSimpleName(), timestamp, eventType.toString());
+    }
 }
diff --git a/src/automation/src/main/java/eu/teraflow/automation/context/model/EventTypeEnum.java b/src/automation/src/main/java/eu/teraflow/automation/context/model/EventTypeEnum.java
index d29b293715ef7ac4d0a2b7b008c5c7afd106c586..4c723b29838ef8d4f2bddbfeba8ae4deda67a60a 100644
--- a/src/automation/src/main/java/eu/teraflow/automation/context/model/EventTypeEnum.java
+++ b/src/automation/src/main/java/eu/teraflow/automation/context/model/EventTypeEnum.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package eu.teraflow.automation.context.model;
 
 public enum EventTypeEnum {
diff --git a/src/automation/src/main/java/eu/teraflow/automation/device/Device.java b/src/automation/src/main/java/eu/teraflow/automation/device/Device.java
deleted file mode 100644
index d2abfee95e2b68239c6fbebfa4af2e0380c796cd..0000000000000000000000000000000000000000
--- a/src/automation/src/main/java/eu/teraflow/automation/device/Device.java
+++ /dev/null
@@ -1,65 +0,0 @@
-package eu.teraflow.automation.device;
-
-import eu.teraflow.automation.device.model.DeviceConfig;
-import eu.teraflow.automation.device.model.DeviceId;
-import eu.teraflow.automation.device.model.DeviceOperationalStatus;
-import eu.teraflow.automation.device.model.DeviceType;
-
-public class Device {
-
-    private final DeviceId deviceId;
-    private final DeviceType deviceType;
-    private DeviceConfig deviceConfig;
-    private DeviceOperationalStatus deviceOperationalStatus;
-
-    public Device(
-            DeviceId deviceId,
-            DeviceType deviceType,
-            DeviceConfig deviceConfig,
-            DeviceOperationalStatus deviceOperationalStatus) {
-
-        this.deviceId = deviceId;
-        this.deviceType = deviceType;
-        this.deviceConfig = deviceConfig;
-        this.deviceOperationalStatus = deviceOperationalStatus;
-    }
-
-    public Device(
-            DeviceId deviceId, DeviceType deviceType, DeviceOperationalStatus deviceOperationalStatus) {
-
-        this.deviceId = deviceId;
-        this.deviceType = deviceType;
-        this.deviceOperationalStatus = deviceOperationalStatus;
-    }
-
-    public DeviceId getDeviceId() {
-        return deviceId;
-    }
-
-    public DeviceType getDeviceType() {
-        return deviceType;
-    }
-
-    public DeviceConfig getDeviceConfig() {
-        return deviceConfig;
-    }
-
-    public DeviceOperationalStatus getDeviceOperationalStatus() {
-        return deviceOperationalStatus;
-    }
-
-    public void setDeviceOperationalStatus(DeviceOperationalStatus status) {
-
-        this.deviceOperationalStatus = status;
-    }
-
-    public void setDeviceConfiguration(DeviceConfig deviceConfig) {
-
-        this.deviceConfig = deviceConfig;
-    }
-
-    public boolean isConfigured() {
-
-        return this.deviceOperationalStatus == DeviceOperationalStatus.ENABLED;
-    }
-}
diff --git a/src/automation/src/main/java/eu/teraflow/automation/device/DeviceGateway.java b/src/automation/src/main/java/eu/teraflow/automation/device/DeviceGateway.java
index ed9a2961c4926aa5dfb66b17bfb1692a0cc37e68..6a285c14cfc6f55e1809a6523603396701612872 100644
--- a/src/automation/src/main/java/eu/teraflow/automation/device/DeviceGateway.java
+++ b/src/automation/src/main/java/eu/teraflow/automation/device/DeviceGateway.java
@@ -1,11 +1,27 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package eu.teraflow.automation.device;
 
+import eu.teraflow.automation.device.model.Device;
 import eu.teraflow.automation.device.model.DeviceConfig;
-import eu.teraflow.automation.device.model.DeviceId;
 import io.smallrye.mutiny.Uni;
 
 public interface DeviceGateway {
-    Uni<DeviceConfig> getInitialConfiguration(DeviceId deviceId);
+    Uni<DeviceConfig> getInitialConfiguration(String deviceId);
 
-    Uni<DeviceId> configureDevice(Device device);
+    Uni<String> configureDevice(Device device);
 }
diff --git a/src/automation/src/main/java/eu/teraflow/automation/device/DeviceGatewayImpl.java b/src/automation/src/main/java/eu/teraflow/automation/device/DeviceGatewayImpl.java
index 78e72598e681f4f007e541423b0cc64694f7ea3d..e10bd5ee04c6252d0b5af065ff6f60fcbe9f67e8 100644
--- a/src/automation/src/main/java/eu/teraflow/automation/device/DeviceGatewayImpl.java
+++ b/src/automation/src/main/java/eu/teraflow/automation/device/DeviceGatewayImpl.java
@@ -1,138 +1,63 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package eu.teraflow.automation.device;
 
 import context.ContextOuterClass;
-import context.ContextOuterClass.DeviceConfig;
-import context.ContextOuterClass.Uuid;
 import device.DeviceService;
-import eu.teraflow.automation.device.model.*;
+import eu.teraflow.automation.Serializer;
+import eu.teraflow.automation.device.model.Device;
+import eu.teraflow.automation.device.model.DeviceConfig;
 import io.quarkus.grpc.GrpcClient;
 import io.smallrye.mutiny.Uni;
-import java.util.ArrayList;
-import java.util.List;
 import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+import org.jboss.logging.Logger;
 
 @ApplicationScoped
 public class DeviceGatewayImpl implements DeviceGateway {
+    private static final Logger LOGGER = Logger.getLogger(DeviceGatewayImpl.class);
 
     @GrpcClient("device")
     DeviceService deviceDelegate;
 
-    @Override
-    public Uni<eu.teraflow.automation.device.model.DeviceConfig> getInitialConfiguration(
-            DeviceId deviceId) {
-        final var serializedId = getSerializedDeviceId(deviceId);
-        final var serializedDeviceConfigUni = deviceDelegate.getInitialConfig(serializedId);
+    private final Serializer serializer;
 
-        return serializedDeviceConfigUni.onItem().transform(this::getDeviceConfig);
+    @Inject
+    public DeviceGatewayImpl(Serializer serializer) {
+        this.serializer = serializer;
     }
 
     @Override
-    public Uni<DeviceId> configureDevice(Device device) {
-        final var deviceBuilder = ContextOuterClass.Device.newBuilder();
-
-        final var serializedId = getSerializedDeviceId(device.getDeviceId());
-        deviceBuilder.setDeviceId(serializedId);
-
-        final var serializedConfigUni = getSerializedConfigUni(device.getDeviceConfig());
-        final var serializedConfig = serializedConfigUni.await().indefinitely();
-        deviceBuilder.setDeviceConfig(serializedConfig);
-
-        final var serializedDevice = deviceBuilder.build();
-        final var deviceIdUni = deviceDelegate.configureDevice(serializedDevice);
-
-        return deviceIdUni.onItem().transform(this::getDeviceId);
-    }
-
-    private List<ContextOuterClass.ConfigRule> getSerializedConfigRule(
-            List<ConfigRule> configRuleList) {
+    public Uni<DeviceConfig> getInitialConfiguration(String deviceId) {
+        final var serializedDeviceId = serializer.serializeDeviceId(deviceId);
 
-        List<ContextOuterClass.ConfigRule> serializedConfigRuleList = new ArrayList<>();
-        for (ConfigRule item : configRuleList) {
-            final var configActionEnum = getSerializedConfigActionEnum(item.getConfigActionEnum());
-            final var resourceKey = item.getResourceKey();
-            final var resourceValue = item.getResourceValue();
-            final var serializedConfigRule =
-                    ContextOuterClass.ConfigRule.newBuilder()
-                            .setAction(configActionEnum)
-                            .setResourceKey(resourceKey)
-                            .setResourceValue(resourceValue)
-                            .build();
-            serializedConfigRuleList.add(serializedConfigRule);
-        }
-        return serializedConfigRuleList;
+        return deviceDelegate
+                .getInitialConfig(serializedDeviceId)
+                .onItem()
+                .transform(serializer::deserialize);
     }
 
-    private ContextOuterClass.ConfigActionEnum getSerializedConfigActionEnum(
-            ConfigActionEnum configActionEnum) {
-
-        if (configActionEnum == ConfigActionEnum.SET) {
-            return ContextOuterClass.ConfigActionEnum.CONFIGACTION_SET;
-        } else if (configActionEnum == ConfigActionEnum.UNDEFINED) {
-            return ContextOuterClass.ConfigActionEnum.CONFIGACTION_UNDEFINED;
-        } else {
-            return ContextOuterClass.ConfigActionEnum.CONFIGACTION_DELETE;
-        }
-    }
-
-    private ConfigActionEnum getConfigActionEnum(
-            ContextOuterClass.ConfigActionEnum configActionEnum) {
-
-        if (configActionEnum == ContextOuterClass.ConfigActionEnum.CONFIGACTION_SET) {
-            return ConfigActionEnum.SET;
-        } else if (configActionEnum == ContextOuterClass.ConfigActionEnum.CONFIGACTION_UNDEFINED) {
-            return ConfigActionEnum.UNDEFINED;
-        } else {
-            return ConfigActionEnum.DELETE;
-        }
-    }
-
-    private ContextOuterClass.DeviceId getSerializedDeviceId(DeviceId deviceId) {
-        final var deviceIdUuid = Uuid.newBuilder().setUuid(deviceId.toString()).build();
-        return ContextOuterClass.DeviceId.newBuilder().setDeviceUuid(deviceIdUuid).build();
-    }
-
-    private DeviceId getDeviceId(ContextOuterClass.DeviceId deviceId) {
-        eu.teraflow.automation.device.model.Uuid uuid =
-                new eu.teraflow.automation.device.model.Uuid(deviceId.getDeviceUuid().getUuid());
-        return new DeviceId(uuid);
-    }
-
-    private eu.teraflow.automation.device.model.DeviceConfig getDeviceConfig(
-            ContextOuterClass.DeviceConfig deviceConfig) {
-
-        final var serializedConfigRuleList = deviceConfig.getConfigRulesList();
-        final var configRuleList = getConfigRuleList(serializedConfigRuleList);
-        return new eu.teraflow.automation.device.model.DeviceConfig(configRuleList);
-    }
-
-    private List<ConfigRule> getConfigRuleList(
-            List<ContextOuterClass.ConfigRule> serializedConfigRuleList) {
-
-        List<ConfigRule> configRuleList = new ArrayList<>();
-
-        for (ContextOuterClass.ConfigRule item : serializedConfigRuleList) {
-
-            ConfigRule configRule =
-                    new ConfigRule(
-                            getConfigActionEnum(item.getAction()),
-                            item.getResourceKey(),
-                            item.getResourceValue());
-            configRuleList.add(configRule);
-        }
-        return configRuleList;
-    }
-
-    private Uni<DeviceConfig> getSerializedConfigUni(
-            eu.teraflow.automation.device.model.DeviceConfig deviceConfig) {
-
-        final var serializedDeviceConfig = DeviceConfig.newBuilder();
-
-        final var configRuleList = deviceConfig.getConfigRules();
-        final var serializedConfigRuleList = getSerializedConfigRule(configRuleList);
-        for (int i = 0; i < serializedConfigRuleList.size(); i++) {
-            serializedDeviceConfig.setConfigRules(i, serializedConfigRuleList.get(i));
-        }
+    @Override
+    public Uni<String> configureDevice(Device device) {
+        final var serializedDevice = serializer.serialize(device);
 
-        return Uni.createFrom().item(serializedDeviceConfig.build());
+        return deviceDelegate
+                .configureDevice(serializedDevice)
+                .onItem()
+                .transform(serializer::deserialize);
     }
 }
diff --git a/src/automation/src/main/java/eu/teraflow/automation/device/DeviceService.java b/src/automation/src/main/java/eu/teraflow/automation/device/DeviceService.java
index 0ada5cde27c779e13e0f0df745c7ac118576505e..1d982bbd5d6e8c01211b4c607232777711e88192 100644
--- a/src/automation/src/main/java/eu/teraflow/automation/device/DeviceService.java
+++ b/src/automation/src/main/java/eu/teraflow/automation/device/DeviceService.java
@@ -1,12 +1,28 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package eu.teraflow.automation.device;
 
+import eu.teraflow.automation.device.model.Device;
 import eu.teraflow.automation.device.model.DeviceConfig;
-import eu.teraflow.automation.device.model.DeviceId;
 import io.smallrye.mutiny.Uni;
 
 public interface DeviceService {
 
-    Uni<DeviceConfig> getInitialConfiguration(DeviceId deviceId);
+    Uni<DeviceConfig> getInitialConfiguration(String deviceId);
 
-    Uni<DeviceId> configureDevice(Device device);
+    Uni<String> configureDevice(Device device);
 }
diff --git a/src/automation/src/main/java/eu/teraflow/automation/device/DeviceServiceImpl.java b/src/automation/src/main/java/eu/teraflow/automation/device/DeviceServiceImpl.java
index 57c118255f3850f6f0f860c280fc88ac603a8c61..992c9e07a4daa6620ed92d5aacf1edd5c4f5127c 100644
--- a/src/automation/src/main/java/eu/teraflow/automation/device/DeviceServiceImpl.java
+++ b/src/automation/src/main/java/eu/teraflow/automation/device/DeviceServiceImpl.java
@@ -1,7 +1,23 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package eu.teraflow.automation.device;
 
+import eu.teraflow.automation.device.model.Device;
 import eu.teraflow.automation.device.model.DeviceConfig;
-import eu.teraflow.automation.device.model.DeviceId;
 import io.smallrye.mutiny.Uni;
 import javax.enterprise.context.ApplicationScoped;
 import javax.inject.Inject;
@@ -17,13 +33,13 @@ public class DeviceServiceImpl implements DeviceService {
     }
 
     @Override
-    public Uni<DeviceConfig> getInitialConfiguration(DeviceId deviceId) {
+    public Uni<DeviceConfig> getInitialConfiguration(String deviceId) {
 
         return deviceGateway.getInitialConfiguration(deviceId);
     }
 
     @Override
-    public Uni<DeviceId> configureDevice(Device device) {
+    public Uni<String> configureDevice(Device device) {
 
         return deviceGateway.configureDevice(device);
     }
diff --git a/src/automation/src/main/java/eu/teraflow/automation/device/model/ConfigActionEnum.java b/src/automation/src/main/java/eu/teraflow/automation/device/model/ConfigActionEnum.java
index 286bae16da5187bdfc09dabdd2a0e1681f46c0d3..103703c22a522035710c4aa214074229d7edb56c 100644
--- a/src/automation/src/main/java/eu/teraflow/automation/device/model/ConfigActionEnum.java
+++ b/src/automation/src/main/java/eu/teraflow/automation/device/model/ConfigActionEnum.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package eu.teraflow.automation.device.model;
 
 public enum ConfigActionEnum {
diff --git a/src/automation/src/main/java/eu/teraflow/automation/device/model/ConfigRule.java b/src/automation/src/main/java/eu/teraflow/automation/device/model/ConfigRule.java
index 7c9eecd684c40f400df480f7511881da058944e4..b419984b77ea122051fcee3eff9531de484b35aa 100644
--- a/src/automation/src/main/java/eu/teraflow/automation/device/model/ConfigRule.java
+++ b/src/automation/src/main/java/eu/teraflow/automation/device/model/ConfigRule.java
@@ -1,10 +1,26 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package eu.teraflow.automation.device.model;
 
 public class ConfigRule {
 
-    private ConfigActionEnum configActionEnum;
-    private String resourceKey;
-    private String resourceValue;
+    private final ConfigActionEnum configActionEnum;
+    private final String resourceKey;
+    private final String resourceValue;
 
     public ConfigRule(ConfigActionEnum configActionEnum, String resourceKey, String resourceValue) {
         this.configActionEnum = configActionEnum;
@@ -23,4 +39,10 @@ public class ConfigRule {
     public String getResourceValue() {
         return resourceValue;
     }
+
+    @Override
+    public String toString() {
+        return String.format(
+                "%s<%s, %s,%s>", getClass().getSimpleName(), configActionEnum, resourceKey, resourceValue);
+    }
 }
diff --git a/src/automation/src/main/java/eu/teraflow/automation/device/model/Device.java b/src/automation/src/main/java/eu/teraflow/automation/device/model/Device.java
new file mode 100644
index 0000000000000000000000000000000000000000..1cd0f9c324f378e2ecc73d5a24ccdaf093b69268
--- /dev/null
+++ b/src/automation/src/main/java/eu/teraflow/automation/device/model/Device.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package eu.teraflow.automation.device.model;
+
+public class Device {
+
+    private final String deviceId;
+    private final String deviceType;
+    private DeviceConfig deviceConfig;
+    private DeviceOperationalStatus deviceOperationalStatus;
+
+    public Device(
+            String deviceId,
+            String deviceType,
+            DeviceConfig deviceConfig,
+            DeviceOperationalStatus deviceOperationalStatus) {
+
+        this.deviceId = deviceId;
+        this.deviceType = deviceType;
+        this.deviceConfig = deviceConfig;
+        this.deviceOperationalStatus = deviceOperationalStatus;
+    }
+
+    public Device(
+            String deviceId, String deviceType, DeviceOperationalStatus deviceOperationalStatus) {
+        this.deviceId = deviceId;
+        this.deviceType = deviceType;
+        this.deviceOperationalStatus = deviceOperationalStatus;
+    }
+
+    public boolean isEnabled() {
+        return deviceOperationalStatus == DeviceOperationalStatus.ENABLED;
+    }
+
+    public void enableDevice() {
+        this.deviceOperationalStatus = DeviceOperationalStatus.ENABLED;
+    }
+
+    public String getDeviceId() {
+        return deviceId;
+    }
+
+    public String getDeviceType() {
+        return deviceType;
+    }
+
+    public DeviceConfig getDeviceConfig() {
+        return deviceConfig;
+    }
+
+    public DeviceOperationalStatus getDeviceOperationalStatus() {
+        return deviceOperationalStatus;
+    }
+
+    public void setDeviceConfiguration(DeviceConfig deviceConfig) {
+        this.deviceConfig = deviceConfig;
+    }
+
+    @Override
+    public String toString() {
+        return String.format(
+                "%s{id=\"%s\", type=\"%s\", operationalStatus=\"%s\", config=%s",
+                getClass().getSimpleName(), deviceId, deviceType, deviceOperationalStatus, deviceConfig);
+    }
+}
diff --git a/src/automation/src/main/java/eu/teraflow/automation/device/model/DeviceConfig.java b/src/automation/src/main/java/eu/teraflow/automation/device/model/DeviceConfig.java
index c70e743dcba0a05a4962e5c113115ce0d0bf7c95..68248136a34d4c0a0b6c519c4eeec54596f6c962 100644
--- a/src/automation/src/main/java/eu/teraflow/automation/device/model/DeviceConfig.java
+++ b/src/automation/src/main/java/eu/teraflow/automation/device/model/DeviceConfig.java
@@ -1,10 +1,27 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package eu.teraflow.automation.device.model;
 
 import java.util.List;
+import java.util.stream.Collectors;
 
 public class DeviceConfig {
 
-    private List<ConfigRule> configRules;
+    private final List<ConfigRule> configRules;
 
     public DeviceConfig(List<ConfigRule> configRules) {
 
@@ -14,4 +31,11 @@ public class DeviceConfig {
     public List<ConfigRule> getConfigRules() {
         return configRules;
     }
+
+    @Override
+    public String toString() {
+        final var configRulesDescription =
+                configRules.stream().map(ConfigRule::toString).collect(Collectors.joining(", "));
+        return String.format("%s[%s]", getClass().getSimpleName(), configRulesDescription);
+    }
 }
diff --git a/src/automation/src/main/java/eu/teraflow/automation/device/model/DeviceEvent.java b/src/automation/src/main/java/eu/teraflow/automation/device/model/DeviceEvent.java
index ef3f8ae76e5ab57e5077629f36c0c088e4963ba4..6589e224e5dac191435d915b50d410ff40ae1afb 100644
--- a/src/automation/src/main/java/eu/teraflow/automation/device/model/DeviceEvent.java
+++ b/src/automation/src/main/java/eu/teraflow/automation/device/model/DeviceEvent.java
@@ -1,13 +1,29 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package eu.teraflow.automation.device.model;
 
 import eu.teraflow.automation.context.model.Event;
 
 public class DeviceEvent {
 
-    private Event event;
-    private DeviceId deviceId;
+    private final Event event;
+    private final String deviceId;
 
-    public DeviceEvent(Event event, DeviceId deviceId) {
+    public DeviceEvent(String deviceId, Event event) {
         this.event = event;
         this.deviceId = deviceId;
     }
@@ -16,7 +32,12 @@ public class DeviceEvent {
         return event;
     }
 
-    public DeviceId getDeviceId() {
+    public String getDeviceId() {
         return deviceId;
     }
+
+    @Override
+    public String toString() {
+        return String.format("%s[%s, %s]", getClass().getSimpleName(), deviceId, event.toString());
+    }
 }
diff --git a/src/automation/src/main/java/eu/teraflow/automation/device/model/DeviceId.java b/src/automation/src/main/java/eu/teraflow/automation/device/model/DeviceId.java
deleted file mode 100644
index b52672631c764b73266c74b36830c6c2c6a2d713..0000000000000000000000000000000000000000
--- a/src/automation/src/main/java/eu/teraflow/automation/device/model/DeviceId.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package eu.teraflow.automation.device.model;
-
-public class DeviceId {
-    private Uuid id;
-
-    public DeviceId(Uuid id) {
-        this.id = id;
-    }
-
-    public Uuid getId() {
-        return id;
-    }
-}
diff --git a/src/automation/src/main/java/eu/teraflow/automation/device/model/DeviceOperationalStatus.java b/src/automation/src/main/java/eu/teraflow/automation/device/model/DeviceOperationalStatus.java
index 11e0c1b7154192b2f1595b23f7b4bf9d259af784..397c5276c108924c14c58d5213044f264576a9db 100644
--- a/src/automation/src/main/java/eu/teraflow/automation/device/model/DeviceOperationalStatus.java
+++ b/src/automation/src/main/java/eu/teraflow/automation/device/model/DeviceOperationalStatus.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package eu.teraflow.automation.device.model;
 
 public enum DeviceOperationalStatus {
diff --git a/src/automation/src/main/java/eu/teraflow/automation/device/model/DeviceType.java b/src/automation/src/main/java/eu/teraflow/automation/device/model/DeviceType.java
deleted file mode 100644
index 2d61875d41ac98fad4aa80147910e4cdd1a869d7..0000000000000000000000000000000000000000
--- a/src/automation/src/main/java/eu/teraflow/automation/device/model/DeviceType.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package eu.teraflow.automation.device.model;
-
-public class DeviceType {
-    String deviceType;
-
-    public DeviceType(String deviceType) {
-
-        this.deviceType = deviceType;
-    }
-
-    public String getDeviceType() {
-        return deviceType;
-    }
-}
diff --git a/src/automation/src/main/java/eu/teraflow/automation/device/model/Uuid.java b/src/automation/src/main/java/eu/teraflow/automation/device/model/Uuid.java
deleted file mode 100644
index 0a58ee6a8db3696dd14600094ddfa42ac10e3058..0000000000000000000000000000000000000000
--- a/src/automation/src/main/java/eu/teraflow/automation/device/model/Uuid.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package eu.teraflow.automation.device.model;
-
-public class Uuid {
-
-    private String id;
-
-    public Uuid(String id) {
-        this.id = id;
-    }
-
-    public String getId() {
-        return id;
-    }
-}
diff --git a/src/automation/src/main/java/eu/teraflow/automation/model/DeviceRole.java b/src/automation/src/main/java/eu/teraflow/automation/model/DeviceRole.java
index 8706cad56b5b6efba6a3d585bb898340af8377ed..fb538fee298e6cc3ec521f474a14a7252c03bc4f 100644
--- a/src/automation/src/main/java/eu/teraflow/automation/model/DeviceRole.java
+++ b/src/automation/src/main/java/eu/teraflow/automation/model/DeviceRole.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package eu.teraflow.automation.model;
 
 public class DeviceRole {
diff --git a/src/automation/src/main/java/eu/teraflow/automation/model/DeviceRoleId.java b/src/automation/src/main/java/eu/teraflow/automation/model/DeviceRoleId.java
index d795fed4a1b0ae1d7edf12d835c3ae3df9b6bb45..d0b7e6a2ae1e7795f90ae8fc00c8488dedab7567 100644
--- a/src/automation/src/main/java/eu/teraflow/automation/model/DeviceRoleId.java
+++ b/src/automation/src/main/java/eu/teraflow/automation/model/DeviceRoleId.java
@@ -1,23 +1,36 @@
-package eu.teraflow.automation.model;
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 
-import eu.teraflow.automation.device.model.DeviceId;
-import eu.teraflow.automation.device.model.Uuid;
+package eu.teraflow.automation.model;
 
 public class DeviceRoleId {
 
-    private final Uuid devRoleId;
-    private final DeviceId deviceId;
+    private final String id;
+    private final String deviceId;
 
-    public DeviceRoleId(Uuid devRoleId, DeviceId deviceId) {
-        this.devRoleId = devRoleId;
+    public DeviceRoleId(String id, String deviceId) {
+        this.id = id;
         this.deviceId = deviceId;
     }
 
-    public Uuid getDevRoleId() {
-        return devRoleId;
+    public String getId() {
+        return id;
     }
 
-    public DeviceId getDeviceId() {
+    public String getDeviceId() {
         return deviceId;
     }
 }
diff --git a/src/automation/src/main/java/eu/teraflow/automation/model/DeviceRoleState.java b/src/automation/src/main/java/eu/teraflow/automation/model/DeviceRoleState.java
deleted file mode 100644
index 71c5757d69f94c2d92df2c94fcd2fbf89ba33950..0000000000000000000000000000000000000000
--- a/src/automation/src/main/java/eu/teraflow/automation/model/DeviceRoleState.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package eu.teraflow.automation.model;
-
-public class DeviceRoleState {
-
-    private final DeviceRoleId deviceRoleId;
-    private final DeviceState deviceState;
-
-    public DeviceRoleState(DeviceRoleId deviceRoleId, DeviceState deviceState) {
-
-        this.deviceRoleId = deviceRoleId;
-        this.deviceState = deviceState;
-    }
-
-    public DeviceRoleId getDeviceRoleId() {
-        return deviceRoleId;
-    }
-
-    public DeviceState getDeviceState() {
-        return deviceState;
-    }
-}
diff --git a/src/automation/src/main/java/eu/teraflow/automation/model/DeviceRoleType.java b/src/automation/src/main/java/eu/teraflow/automation/model/DeviceRoleType.java
index 411f9eb603ac9f5e4d737e2697755bfc206322d4..e92c88c4ac3e46eb0e53e5bae0a361299f547992 100644
--- a/src/automation/src/main/java/eu/teraflow/automation/model/DeviceRoleType.java
+++ b/src/automation/src/main/java/eu/teraflow/automation/model/DeviceRoleType.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package eu.teraflow.automation.model;
 
 public enum DeviceRoleType {
diff --git a/src/automation/src/main/java/eu/teraflow/automation/model/DeviceState.java b/src/automation/src/main/java/eu/teraflow/automation/model/DeviceState.java
index 279faeb5d8bd0d0dcad036883f4107fe871261ae..53a6b2712930f698987ce3c6a60277605fb604a1 100644
--- a/src/automation/src/main/java/eu/teraflow/automation/model/DeviceState.java
+++ b/src/automation/src/main/java/eu/teraflow/automation/model/DeviceState.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package eu.teraflow.automation.model;
 
 public enum DeviceState {
diff --git a/src/automation/src/main/resources/application.yaml b/src/automation/src/main/resources/application.yaml
deleted file mode 100644
index 8d5ae6ed513f7d20227b217fd803c809c4f5b2c4..0000000000000000000000000000000000000000
--- a/src/automation/src/main/resources/application.yaml
+++ /dev/null
@@ -1,42 +0,0 @@
-automation:
-  should-subscribe-to-context-component: true
-quarkus:
-  grpc:
-    server:
-      port: 9999
-      enable-reflection-service: true
-    clients:
-      context:
-        host: ${quarkus.kubernetes.env.vars.context-service-host}
-      device:
-        host: ${quarkus.kubernetes.env.vars.device-service-host}
-  http:
-    port: 8080
-  container-image:
-    group: teraflow-h2020
-    name: controller/automation
-    tag: 0.0.1
-    registry: registry.gitlab.com
-  kubernetes:
-    name: automationservice
-    image-pull-policy: Always
-    service-type: NodePort
-    labels:
-      app: automationservice
-    readiness-probe:
-      initial-delay: 5s
-      period: 45s
-    liveness-probe:
-      initial-delay: 5s
-      period: 45s
-    ports:
-      http:
-        host-port: 8080
-        container-port: 8080
-      grpc-server:
-        host-port: 9999
-        container-port: 9999
-    env:
-      vars:
-        context-service-host: ContextService
-        device-service-host: DeviceService
\ No newline at end of file
diff --git a/src/automation/src/main/resources/application.yml b/src/automation/src/main/resources/application.yml
new file mode 100644
index 0000000000000000000000000000000000000000..24280803e81ecb90933a7ccd531a754b034cabb7
--- /dev/null
+++ b/src/automation/src/main/resources/application.yml
@@ -0,0 +1,62 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+automation:
+  should-subscribe-to-context-component: true
+quarkus:
+  banner:
+    path: teraflow-automation-banner.txt
+  grpc:
+    server:
+      port: 9999
+      enable-reflection-service: true
+    clients:
+      context:
+        host: ${quarkus.kubernetes.env.vars.context-service-host}
+        port: 1010
+      device:
+        host: ${quarkus.kubernetes.env.vars.device-service-host}
+        port: 2020
+
+  http:
+    port: 8080
+
+  container-image:
+    group: teraflow-h2020
+    name: controller/automation
+    registry: registry.gitlab.com
+
+  kubernetes:
+    name: automationservice
+    image-pull-policy: Always
+    labels:
+      app: automationservice
+    add-version-to-label-selectors: false
+    readiness-probe:
+      initial-delay: 2s
+      period: 10s
+    liveness-probe:
+      initial-delay: 2s
+      period: 10s
+    ports:
+      http:
+        host-port: 8080
+        container-port: 8080
+      grpc-server:
+        host-port: 9999
+        container-port: 9999
+    env:
+      vars:
+        context-service-host: "contextservice"
+        device-service-host: "deviceservice"
diff --git a/src/automation/src/main/resources/teraflow-automation-banner.txt b/src/automation/src/main/resources/teraflow-automation-banner.txt
new file mode 100644
index 0000000000000000000000000000000000000000..3792524170f5b74de9add7340ab32940d7f65a64
--- /dev/null
+++ b/src/automation/src/main/resources/teraflow-automation-banner.txt
@@ -0,0 +1,9 @@
+
+  _______             ______ _                               _                        _   _
+ |__   __|           |  ____| |                   /\        | |                      | | (_)
+    | | ___ _ __ __ _| |__  | | _____      __    /  \  _   _| |_ ___  _ __ ___   __ _| |_ _  ___  _ __
+    | |/ _ \ '__/ _` |  __| | |/ _ \ \ /\ / /   / /\ \| | | | __/ _ \| '_ ` _ \ / _` | __| |/ _ \| '_ \
+    | |  __/ | | (_| | |    | | (_) \ V  V /   / ____ \ |_| | || (_) | | | | | | (_| | |_| | (_) | | | |
+    |_|\___|_|  \__,_|_|    |_|\___/ \_/\_/   /_/    \_\__,_|\__\___/|_| |_| |_|\__,_|\__|_|\___/|_| |_|
+
+
diff --git a/src/automation/src/test/java/eu/teraflow/automation/AutomationFunctionalServiceTest.java b/src/automation/src/test/java/eu/teraflow/automation/AutomationFunctionalServiceTest.java
index 81d568f25481272c97e6543841561b7e61789ad5..f584a86ca72885df1249ea6d956384b50e129268 100644
--- a/src/automation/src/test/java/eu/teraflow/automation/AutomationFunctionalServiceTest.java
+++ b/src/automation/src/test/java/eu/teraflow/automation/AutomationFunctionalServiceTest.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package eu.teraflow.automation;
 
 import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
@@ -5,9 +21,9 @@ import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
 import automation.Automation;
 import context.ContextOuterClass;
 import eu.teraflow.automation.context.ContextGateway;
-import eu.teraflow.automation.device.Device;
 import eu.teraflow.automation.device.DeviceGateway;
 import eu.teraflow.automation.device.model.*;
+import eu.teraflow.automation.device.model.Device;
 import io.quarkus.test.junit.QuarkusTest;
 import io.quarkus.test.junit.mockito.InjectMock;
 import io.smallrye.mutiny.Uni;
@@ -51,9 +67,8 @@ public class AutomationFunctionalServiceTest {
                         .setDevId(outDeviceId)
                         .build();
 
-        Uuid uuid = new Uuid(outDeviceRoleId.getDevId().toString());
-        DeviceId deviceId = new DeviceId(uuid);
-        DeviceType deviceType = new DeviceType("cisco");
+        String deviceId = outDeviceRoleId.getDevRoleId().toString();
+        String deviceType = "cisco";
 
         ConfigRule configRule1 = new ConfigRule(ConfigActionEnum.UNDEFINED, "1", "1");
         ConfigRule configRule2 = new ConfigRule(ConfigActionEnum.SET, "2", "2");
@@ -63,7 +78,7 @@ public class AutomationFunctionalServiceTest {
 
         DeviceConfig expectedDeviceConfig = new DeviceConfig(configRuleList);
         Uni<DeviceConfig> expectedDeviceConfigUni = Uni.createFrom().item(expectedDeviceConfig);
-        Uni<DeviceId> expectedDeviceId = Uni.createFrom().item(deviceId);
+        Uni<String> expectedDeviceId = Uni.createFrom().item(deviceId);
 
         Device device = new Device(deviceId, deviceType, DeviceOperationalStatus.DISABLED);
         Uni<Device> deviceUni = Uni.createFrom().item(device);
@@ -93,9 +108,8 @@ public class AutomationFunctionalServiceTest {
                                 assertThat(rulesList.get(i).getResourceKey()).isEqualTo(String.valueOf(i + 1));
                                 assertThat(rulesList.get(i).getResourceValue()).isEqualTo(String.valueOf(i + 1));
                             }
-
-                            assertThat(deviceConfig.getDeviceId().getId().getId())
-                                    .isEqualTo(deviceId.getId().getId());
+                            assertThat(deviceConfig.getDeviceType()).isEqualTo("cisco");
+                            assertThat(deviceConfig.getDeviceId()).isEqualTo(deviceId);
                         });
     }
 
@@ -121,9 +135,8 @@ public class AutomationFunctionalServiceTest {
                         .setDevId(outDeviceId)
                         .build();
 
-        Uuid uuid = new Uuid(outDeviceRoleId.getDevId().toString());
-        DeviceId deviceId = new DeviceId(uuid);
-        DeviceType deviceType = new DeviceType("ztp");
+        String deviceId = outDeviceRoleId.getDevId().toString();
+        String deviceType = "ztp";
 
         List<ConfigRule> configRuleList = new ArrayList<>();
 
@@ -163,9 +176,8 @@ public class AutomationFunctionalServiceTest {
                                 assertThat(configRule.getResourceValue())
                                         .isEqualTo(expectedConfigRule.getResourceValue());
                             }
-
-                            assertThat(deviceConfig.getDeviceId().getId().getId())
-                                    .isEqualTo(deviceId.getId().getId());
+                            assertThat(deviceConfig.getDeviceType()).isEqualTo("ztp");
+                            assertThat(deviceConfig.getDeviceId()).isEqualTo(deviceId);
                         });
     }
 }
diff --git a/src/automation/src/test/java/eu/teraflow/automation/AutomationServiceTest.java b/src/automation/src/test/java/eu/teraflow/automation/AutomationServiceTest.java
index 85e056b87e87f1459d5fe02e902774aa1f1f82c3..56c2743216d7204da4f4b4bd64b6281accb5e604 100644
--- a/src/automation/src/test/java/eu/teraflow/automation/AutomationServiceTest.java
+++ b/src/automation/src/test/java/eu/teraflow/automation/AutomationServiceTest.java
@@ -1,28 +1,43 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package eu.teraflow.automation;
 
 import static org.assertj.core.api.Assertions.assertThat;
 
 import automation.Automation;
-import automation.Automation.DeviceRole;
-import automation.Automation.DeviceRoleId;
 import automation.AutomationService;
 import context.ContextOuterClass;
-import context.ContextOuterClass.Uuid;
+import eu.teraflow.automation.model.DeviceRole;
+import eu.teraflow.automation.model.DeviceRoleId;
 import eu.teraflow.automation.context.ContextGateway;
-import eu.teraflow.automation.device.Device;
 import eu.teraflow.automation.device.DeviceGateway;
 import eu.teraflow.automation.device.model.*;
+import eu.teraflow.automation.device.model.Device;
+import eu.teraflow.automation.model.DeviceRoleType;
 import io.quarkus.grpc.GrpcClient;
 import io.quarkus.test.junit.QuarkusTest;
 import io.quarkus.test.junit.mockito.InjectMock;
 import io.smallrye.mutiny.Uni;
-import java.util.ArrayList;
 import java.util.List;
-import java.util.UUID;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
+import javax.inject.Inject;
 import org.jboss.logging.Logger;
 import org.junit.jupiter.api.Test;
 import org.mockito.Mockito;
@@ -32,114 +47,113 @@ class AutomationServiceTest {
     private static final Logger LOGGER = Logger.getLogger(AutomationServiceTest.class);
 
     @GrpcClient AutomationService client;
+    private final Serializer serializer;
 
     @InjectMock DeviceGateway deviceGateway;
     @InjectMock ContextGateway contextGateway;
 
+    @Inject
+    AutomationServiceTest(Serializer serializer) {
+        this.serializer = serializer;
+    }
+
     @Test
     void shouldAddDeviceRole() throws ExecutionException, InterruptedException, TimeoutException {
-        CompletableFuture<String> message = new CompletableFuture<>();
-
-        final var serializedUuid =
-                Uuid.newBuilder()
-                        .setUuid(UUID.fromString("0f14d0ab-9608-7862-a9e4-5ed26688389b").toString())
-                        .build();
-        final var deviceRoleId = DeviceRoleId.newBuilder().setDevRoleId(serializedUuid).build();
-        final var deviceRole = DeviceRole.newBuilder().setDevRoleId(deviceRoleId).build();
-
-        eu.teraflow.automation.device.model.Uuid uuid =
-                new eu.teraflow.automation.device.model.Uuid(deviceRoleId.getDevId().toString());
-        DeviceId deviceId = new DeviceId(uuid);
-        DeviceType deviceType = new DeviceType("ztp");
-
-        List<ConfigRule> configRuleList = new ArrayList<>();
-
-        ConfigRule configRule1 =
-                new ConfigRule(ConfigActionEnum.UNDEFINED, "001", "initial-configuration");
-        configRuleList.add(configRule1);
-
-        DeviceConfig deviceConfig = new DeviceConfig(configRuleList);
-        //        Uni<DeviceConfig> deviceConfigUni = Uni.createFrom().item(deviceConfig);
-        Uni<DeviceId> expectedDeviceId = Uni.createFrom().item(deviceId);
+        final var message = new CompletableFuture<>();
+        final var DEVICE_ID = "0f14d0ab-9608-7862-a9e4-5ed26688389b";
+        final var DEVICE_ROLE_ID = "0f14d0ab-9608-7862-a9e4-5ed26688389a";
+        final var DEVICE_TYPE = "ztp";
+
+        final var emptyDeviceConfig = new DeviceConfig(List.of());
+        final var disabledDevice =
+                new Device(DEVICE_ID, DEVICE_TYPE, emptyDeviceConfig, DeviceOperationalStatus.DISABLED);
+        Mockito.when(contextGateway.getDevice(Mockito.any()))
+                .thenReturn(Uni.createFrom().item(disabledDevice));
+
+        final var configRule = new ConfigRule(ConfigActionEnum.SET, "001", "initial-configuration");
+        final var initialDeviceConfig = new DeviceConfig(List.of(configRule));
+        Mockito.when(deviceGateway.getInitialConfiguration(Mockito.any()))
+                .thenReturn(Uni.createFrom().item(initialDeviceConfig));
 
-        DeviceConfig expectedDeviceConfig = new DeviceConfig(configRuleList);
-        Uni<DeviceConfig> expectedDeviceConfigUni = Uni.createFrom().item(expectedDeviceConfig);
+        Mockito.when(deviceGateway.configureDevice(Mockito.any()))
+                .thenReturn(Uni.createFrom().item(DEVICE_ID));
 
-        Device device =
-                new Device(deviceId, deviceType, deviceConfig, DeviceOperationalStatus.DISABLED);
-        Uni<Device> deviceUni = Uni.createFrom().item(device);
-
-        Mockito.when(contextGateway.getDevice(Mockito.any())).thenReturn(deviceUni);
-        Mockito.when(deviceGateway.getInitialConfiguration(Mockito.any()))
-                .thenReturn(expectedDeviceConfigUni);
-        Mockito.when(deviceGateway.configureDevice(Mockito.any())).thenReturn(expectedDeviceId);
+        final var deviceRoleId = new DeviceRoleId(DEVICE_ROLE_ID, DEVICE_ID);
+        final var deviceRoleType = DeviceRoleType.DEV_OPS;
+        final var deviceRole = new DeviceRole(deviceRoleId, deviceRoleType);
+        final var serializedDeviceRole = serializer.serialize(deviceRole);
 
         client
-                .ztpAdd(deviceRole)
+                .ztpAdd(serializedDeviceRole)
                 .subscribe()
                 .with(
                         deviceRoleState -> {
-                            LOGGER.infof("Received response %s", deviceRoleState);
-                            message.complete(deviceRoleState.getDevRoleId().getDevRoleId().toString());
+                            LOGGER.infof("Received %s", deviceRoleState);
+                            final var devRoleId = deviceRoleState.getDevRoleId();
+
+                            final var deviceRoleIdUuid = serializer.deserialize(devRoleId);
+
+                            assertThat(deviceRoleIdUuid.getId()).isEqualTo(DEVICE_ROLE_ID);
+
+                            final var deviceId = serializer.deserialize(devRoleId.getDevId());
+                            assertThat(deviceId).isEqualTo(DEVICE_ID);
+
+                            final var devRoleUuid = serializer.deserialize(devRoleId.getDevRoleId());
+                            message.complete(devRoleUuid);
                         });
-        assertThat(message.get(5, TimeUnit.SECONDS)).isEqualTo(deviceRoleId.getDevRoleId().toString());
+        assertThat(message.get(5, TimeUnit.SECONDS)).isEqualTo(DEVICE_ROLE_ID);
     }
 
     @Test
     void shouldUpdateDeviceRole() throws ExecutionException, InterruptedException, TimeoutException {
         CompletableFuture<String> message = new CompletableFuture<>();
+        final var DEVICE_ID = "0f14d0ab-9608-7862-a9e4-5ed26688389b";
+        final var DEVICE_ROLE_ID = "0f14d0ab-9608-7862-a9e4-5ed26688389a";
 
-        final var uuid =
-                Uuid.newBuilder()
-                        .setUuid(UUID.fromString("0f14d0ab-9605-7862-a9e4-5ed26688389b").toString())
-                        .build();
-        final var deviceRoleId = DeviceRoleId.newBuilder().setDevRoleId(uuid).build();
-        final var deviceRole = DeviceRole.newBuilder().setDevRoleId(deviceRoleId).build();
+        final var deviceRoleId = new DeviceRoleId(DEVICE_ROLE_ID, DEVICE_ID);
+        final var deviceRoleType = DeviceRoleType.DEV_CONF;
+        final var deviceRole = new DeviceRole(deviceRoleId, deviceRoleType);
+        final var serializedDeviceRole = serializer.serialize(deviceRole);
 
         client
-                .ztpUpdate(deviceRole)
+                .ztpUpdate(serializedDeviceRole)
                 .subscribe()
                 .with(
                         deviceRoleState -> {
                             LOGGER.infof("Received response %s", deviceRoleState);
                             message.complete(deviceRoleState.getDevRoleId().toString());
                         });
-        assertThat(message.get(5, TimeUnit.SECONDS)).contains("0f14d0ab-9605-7862-a9e4-5ed26688389b");
+        assertThat(message.get(5, TimeUnit.SECONDS)).contains(DEVICE_ID);
     }
 
     @Test
     void shouldGetDeviceRole() throws ExecutionException, InterruptedException, TimeoutException {
-
         CompletableFuture<String> message = new CompletableFuture<>();
+        final var DEVICE_ID = "0f14d0ab-9608-7862-a9e4-5ed26688389b";
+        final var DEVICE_ROLE_ID = "0f14d0ab-9608-7862-a9e4-5ed26688389a";
 
-        final var uuid =
-                Uuid.newBuilder()
-                        .setUuid(UUID.fromString("0f14d0ab-9605-4a62-a5c4-5ed26688389b").toString())
-                        .build();
-        final var deviceRoleId = DeviceRoleId.newBuilder().setDevRoleId(uuid).build();
+        final var deviceRoleId = new DeviceRoleId(DEVICE_ROLE_ID, DEVICE_ID);
+        final var serializeDeviceRoleId = serializer.serialize(deviceRoleId);
 
         client
-                .ztpGetDeviceRole(deviceRoleId)
+                .ztpGetDeviceRole(serializeDeviceRoleId)
                 .subscribe()
                 .with(
                         deviceRole -> {
                             LOGGER.infof("Received response %s", deviceRole);
+                            assertThat(deviceRole.getDevRoleId().getDevId().getDeviceUuid().getUuid())
+                                    .isEqualTo(DEVICE_ID);
                             message.complete(deviceRole.getDevRoleId().toString());
                         });
-        assertThat(message.get(5, TimeUnit.SECONDS)).contains("0f14d0ab-9605-4a62-a5c4-5ed26688389b");
+        assertThat(message.get(5, TimeUnit.SECONDS)).contains(DEVICE_ROLE_ID);
     }
 
     @Test
     void shouldGetAllDeviceRolesByDeviceId()
             throws ExecutionException, InterruptedException, TimeoutException {
-
         CompletableFuture<String> message = new CompletableFuture<>();
 
-        final var uuid =
-                Uuid.newBuilder()
-                        .setUuid(UUID.fromString("1b14d0ab-9605-4a62-a9e4-5ed26688389b").toString())
-                        .build();
-        final var deviceId = ContextOuterClass.DeviceId.newBuilder().setDeviceUuid(uuid).build();
+        final var deviceId = serializer.serializeDeviceId("0f14d0ab-9605-7862-a9e4-5ed26688389b");
 
         client
                 .ztpGetDeviceRolesByDeviceId(deviceId)
@@ -155,12 +169,11 @@ class AutomationServiceTest {
     @Test
     void shouldDeleteDeviceRole() throws ExecutionException, InterruptedException, TimeoutException {
         CompletableFuture<String> message = new CompletableFuture<>();
-        final var uuid =
-                Uuid.newBuilder()
-                        .setUuid(UUID.fromString("0c15d0ab-9605-4a62-a5c4-5ed26688389b").toString())
-                        .build();
-        final var deviceRoleId = DeviceRoleId.newBuilder().setDevRoleId(uuid).build();
-        final var deviceRole = DeviceRole.newBuilder().setDevRoleId(deviceRoleId).build();
+        final var UUID_VALUE = "0f14d0ab-9605-7862-a9e4-5ed26688389b";
+
+        final var uuid = serializer.serializeUuid(UUID_VALUE);
+        final var deviceRoleId = Automation.DeviceRoleId.newBuilder().setDevRoleId(uuid).build();
+        final var deviceRole = Automation.DeviceRole.newBuilder().setDevRoleId(deviceRoleId).build();
 
         client
                 .ztpDelete(deviceRole)
@@ -170,13 +183,12 @@ class AutomationServiceTest {
                             LOGGER.infof("Received response %s", deviceRoleState);
                             message.complete(deviceRoleState.getDevRoleId().toString());
                         });
-        assertThat(message.get(5, TimeUnit.SECONDS)).contains("0c15d0ab-9605-4a62-a5c4-5ed26688389b");
+        assertThat(message.get(5, TimeUnit.SECONDS)).contains(UUID_VALUE);
     }
 
     @Test
     void shouldDeleteAllDevicesRolesByDeviceId()
             throws ExecutionException, InterruptedException, TimeoutException {
-
         CompletableFuture<String> message = new CompletableFuture<>();
         final var empty = Automation.Empty.newBuilder().build();
 
diff --git a/src/automation/src/test/java/eu/teraflow/automation/SerializerTest.java b/src/automation/src/test/java/eu/teraflow/automation/SerializerTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..4d4a57da7286997c1194ccc5d711ab9999d39567
--- /dev/null
+++ b/src/automation/src/test/java/eu/teraflow/automation/SerializerTest.java
@@ -0,0 +1,509 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package eu.teraflow.automation;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import automation.Automation;
+import context.ContextOuterClass;
+import context.ContextOuterClass.DeviceId;
+import context.ContextOuterClass.DeviceOperationalStatusEnum;
+import context.ContextOuterClass.Uuid;
+import eu.teraflow.automation.context.model.Event;
+import eu.teraflow.automation.context.model.EventTypeEnum;
+import eu.teraflow.automation.device.model.ConfigActionEnum;
+import eu.teraflow.automation.device.model.ConfigRule;
+import eu.teraflow.automation.device.model.Device;
+import eu.teraflow.automation.device.model.DeviceConfig;
+import eu.teraflow.automation.device.model.DeviceEvent;
+import eu.teraflow.automation.device.model.DeviceOperationalStatus;
+import eu.teraflow.automation.model.DeviceRole;
+import eu.teraflow.automation.model.DeviceRoleId;
+import eu.teraflow.automation.model.DeviceRoleType;
+import io.quarkus.test.junit.QuarkusTest;
+import java.util.List;
+import java.util.stream.Stream;
+import javax.inject.Inject;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+@QuarkusTest
+class SerializerTest {
+
+    @Inject Serializer serializer;
+
+    @Test
+    void shouldSerializeDeviceId() {
+        final var expectedDeviceId = "expectedDeviceId";
+
+        final var deviceIdUuid = serializer.serializeUuid(expectedDeviceId);
+        final var deviceId = ContextOuterClass.DeviceId.newBuilder().setDeviceUuid(deviceIdUuid).build();
+
+        final var serializedDeviceId = serializer.serializeDeviceId(expectedDeviceId);
+
+        assertThat(serializedDeviceId).usingRecursiveComparison().isEqualTo(deviceId);
+    }
+
+    @Test
+    void shouldDeserializeDeviceId() {
+        final var expectedDeviceId = "expectedDeviceId";
+
+        final var serializedDeviceIdUuid = serializer.serializeUuid("expectedDeviceId");
+        final var serializedDeviceId = DeviceId.newBuilder().setDeviceUuid(serializedDeviceIdUuid).build();
+
+        final var deviceId = serializer.deserialize(serializedDeviceId);
+
+        assertThat(deviceId).isEqualTo(expectedDeviceId);
+    }
+
+    @Test
+    void shouldSerializeDeviceRoleId() {
+        final var expectedDevRoleId = "expectedDevRoleId";
+        final var expectedDeviceId = "expectedDeviceId";
+
+        final var deviceRoleId = new DeviceRoleId(expectedDevRoleId, expectedDeviceId);
+        final var serializedDeviceRoleIdUuid = serializer.serializeUuid(expectedDevRoleId);
+        final var serializedDeviceRoleDeviceIdUuid = serializer.serializeUuid(expectedDeviceId);
+        final var serializedDeviceRoleDeviceId = ContextOuterClass.DeviceId.newBuilder().setDeviceUuid(serializedDeviceRoleDeviceIdUuid).build();
+
+        final var expectedDeviceRoleId =
+                Automation.DeviceRoleId.newBuilder()
+                        .setDevRoleId(serializedDeviceRoleIdUuid)
+                        .setDevId(serializedDeviceRoleDeviceId)
+                        .build();
+
+        final var serializedDevRoleId =
+                serializer.serialize(deviceRoleId);
+
+        assertThat(serializedDevRoleId).usingRecursiveComparison().isEqualTo(expectedDeviceRoleId);
+    }
+
+    @Test
+    void shouldDeserializeDeviceRoleId() {
+        final var expectedDevRoleId = "expectedDevRoleId";
+        final var expectedDeviceId = "expectedDeviceId";
+
+        final var expectedDeviceRoleId = new DeviceRoleId(expectedDevRoleId, expectedDeviceId);
+
+        final var serializedDeviceRoleId = serializer.serialize(expectedDeviceRoleId);
+        final var deviceRoleId = serializer.deserialize(serializedDeviceRoleId);
+
+        assertThat(deviceRoleId).usingRecursiveComparison().isEqualTo(expectedDeviceRoleId);
+    }
+
+    private static Stream<Arguments> provideDeviceRoleType() {
+        return Stream.of(
+                Arguments.of(DeviceRoleType.DEV_OPS, Automation.DeviceRoleType.DEV_OPS),
+                Arguments.of(DeviceRoleType.DEV_CONF, Automation.DeviceRoleType.DEV_CONF),
+                Arguments.of(DeviceRoleType.NONE, Automation.DeviceRoleType.NONE),
+                Arguments.of(DeviceRoleType.PIPELINE_CONF, Automation.DeviceRoleType.PIPELINE_CONF));
+    }
+
+    @ParameterizedTest
+    @MethodSource("provideDeviceRoleType")
+    void shouldSerializeDeviceRoleType(
+            DeviceRoleType deviceRoleType, Automation.DeviceRoleType expectedSerializedType) {
+        final var serializedType = serializer.serialize(deviceRoleType);
+        assertThat(serializedType.getNumber()).isEqualTo(expectedSerializedType.getNumber());
+    }
+
+    @ParameterizedTest
+    @MethodSource("provideDeviceRoleType")
+    void shouldDeserializeDeviceRoleType(
+            DeviceRoleType expectedDeviceRoleType,
+            Automation.DeviceRoleType serializedDeviceRoleTypeType) {
+
+        final var deviceRoleType = serializer.deserialize(serializedDeviceRoleTypeType);
+
+        assertThat(deviceRoleType).isEqualTo(expectedDeviceRoleType);
+    }
+
+    @Test
+    void shouldSerializeDeviceRole() {
+        final var expectedDevRoleId = "expectedDevRoleId";
+        final var expectedDeviceId = "expectedDeviceId";
+
+        final var serializedDeviceRoleDevRoleIdUuid = serializer.serializeUuid(expectedDevRoleId);
+        final var serializedDeviceRoleDeviceId = serializer.serializeDeviceId(expectedDeviceId);
+
+        final var expectedDeviceRoleId = Automation.DeviceRoleId.newBuilder()
+                .setDevRoleId(serializedDeviceRoleDevRoleIdUuid)
+                .setDevId(serializedDeviceRoleDeviceId)
+                .build();
+
+        final var expectedDeviceRoleType = Automation.DeviceRoleType.PIPELINE_CONF;
+
+        final var expectedDeviceRole = Automation.DeviceRole.newBuilder()
+                .setDevRoleId(expectedDeviceRoleId)
+                .setDevRoleType(expectedDeviceRoleType)
+                .build();
+
+        final var deviceRoleId = new DeviceRoleId(expectedDevRoleId,expectedDeviceId);
+        final var deviceRoleType = DeviceRoleType.PIPELINE_CONF;
+
+        final var deviceRole = new DeviceRole(deviceRoleId, deviceRoleType);
+        final var serializedDeviceRole = serializer.serialize(deviceRole);
+
+        assertThat(serializedDeviceRole).usingRecursiveComparison().isEqualTo(expectedDeviceRole);
+    }
+
+    @Test
+    void shouldDeserializeDeviceRole() {
+        final var expectedDevRoleId = "expectedDevRoleId";
+        final var expectedDeviceId = "expectedDeviceId";
+
+        final var expectedDeviceRoleId = new DeviceRoleId(expectedDevRoleId, expectedDeviceId);
+        final var expectedDeviceRoleType = DeviceRoleType.NONE;
+
+        final var expectedDeviceRole = new DeviceRole(expectedDeviceRoleId, expectedDeviceRoleType);
+
+        final var serializedDeviceRoleId = serializer.serialize(expectedDeviceRoleId);
+        final var serializedDeviceRoleType = serializer.serialize(expectedDeviceRoleType);
+
+        final var serializedDeviceRole =
+                Automation.DeviceRole.newBuilder()
+                        .setDevRoleId(serializedDeviceRoleId)
+                        .setDevRoleType(serializedDeviceRoleType)
+                        .build();
+
+        final var deviceRole = serializer.deserialize(serializedDeviceRole);
+
+        assertThat(deviceRole).usingRecursiveComparison().isEqualTo(expectedDeviceRole);
+    }
+
+    private static Stream<Arguments> provideEventTypeEnum() {
+        return Stream.of(
+                Arguments.of(EventTypeEnum.CREATE, ContextOuterClass.EventTypeEnum.EVENTTYPE_CREATE),
+                Arguments.of(EventTypeEnum.REMOVE, ContextOuterClass.EventTypeEnum.EVENTTYPE_REMOVE),
+                Arguments.of(EventTypeEnum.UNDEFINED, ContextOuterClass.EventTypeEnum.EVENTTYPE_UNDEFINED),
+                Arguments.of(EventTypeEnum.UPDATE, ContextOuterClass.EventTypeEnum.EVENTTYPE_UPDATE));
+    }
+
+    @ParameterizedTest
+    @MethodSource("provideEventTypeEnum")
+    void shouldSerializeEventType(
+            EventTypeEnum eventType, ContextOuterClass.EventTypeEnum expectedSerializedType) {
+        final var serializedType = serializer.serialize(eventType);
+        assertThat(serializedType.getNumber()).isEqualTo(expectedSerializedType.getNumber());
+    }
+
+    @ParameterizedTest
+    @MethodSource("provideEventTypeEnum")
+    void shouldDeserializeEventType(
+            EventTypeEnum expectedEventType, ContextOuterClass.EventTypeEnum serializedEventType) {
+        final var eventType = serializer.deserialize(serializedEventType);
+        assertThat(eventType).isEqualTo(expectedEventType);
+    }
+
+    @Test
+    void shouldSerializeEvent() {
+        final var expectedEvent =
+                ContextOuterClass.Event.newBuilder()
+                        .setTimestamp(1)
+                        .setEventType(ContextOuterClass.EventTypeEnum.EVENTTYPE_CREATE)
+                        .build();
+
+        final var event = new Event(1, EventTypeEnum.CREATE);
+        final var serializedEvent = serializer.serialize(event);
+
+        assertThat(serializedEvent).usingRecursiveComparison().isEqualTo(expectedEvent);
+    }
+
+    @Test
+    void shouldDeserializeEvent() {
+        final var expectedEvent = new Event(1, EventTypeEnum.CREATE);
+
+        final var serializedEvent =
+                ContextOuterClass.Event.newBuilder()
+                        .setTimestamp(1)
+                        .setEventType(ContextOuterClass.EventTypeEnum.EVENTTYPE_CREATE)
+                        .build();
+        final var event = serializer.deserialize(serializedEvent);
+
+        assertThat(event).usingRecursiveComparison().isEqualTo(expectedEvent);
+    }
+
+    @Test
+    void shouldSerializeDeviceEvent() {
+        final var expectedUuid = Uuid.newBuilder().setUuid("deviceId");
+        final var expectedDeviceId = DeviceId.newBuilder().setDeviceUuid(expectedUuid).build();
+        final var expectedEvent =
+                ContextOuterClass.Event.newBuilder()
+                        .setTimestamp(1)
+                        .setEventType(ContextOuterClass.EventTypeEnum.EVENTTYPE_CREATE)
+                        .build();
+        final var expectedDeviceEvent =
+                ContextOuterClass.DeviceEvent.newBuilder()
+                        .setDeviceId(expectedDeviceId)
+                        .setEvent(expectedEvent)
+                        .build();
+
+        final var creationEvent = new Event(1, EventTypeEnum.CREATE);
+        final var deviceEvent = new DeviceEvent("deviceId", creationEvent);
+        final var serializedDeviceEvent = serializer.serialize(deviceEvent);
+
+        assertThat(serializedDeviceEvent).usingRecursiveComparison().isEqualTo(expectedDeviceEvent);
+    }
+
+    @Test
+    void shouldDeserializeDeviceEvent() {
+        final var dummyDeviceId = "deviceId";
+        final var expectedEventType = EventTypeEnum.REMOVE;
+        final var expectedTimestamp = 1;
+        final var creationEvent = new Event(expectedTimestamp, expectedEventType);
+        final var expectedDeviceEvent = new DeviceEvent(dummyDeviceId, creationEvent);
+
+        final var deviceUuid = Uuid.newBuilder().setUuid("deviceId");
+        final var deviceId = DeviceId.newBuilder().setDeviceUuid(deviceUuid).build();
+        final var event =
+                ContextOuterClass.Event.newBuilder()
+                        .setTimestamp(1)
+                        .setEventType(ContextOuterClass.EventTypeEnum.EVENTTYPE_REMOVE)
+                        .build();
+        final var serializedDeviceEvent =
+                ContextOuterClass.DeviceEvent.newBuilder().setDeviceId(deviceId).setEvent(event).build();
+        final var deviceEvent = serializer.deserialize(serializedDeviceEvent);
+
+        assertThat(deviceEvent).usingRecursiveComparison().isEqualTo(expectedDeviceEvent);
+    }
+
+    private static Stream<Arguments> provideConfigActionEnum() {
+        return Stream.of(
+                Arguments.of(ConfigActionEnum.SET, ContextOuterClass.ConfigActionEnum.CONFIGACTION_SET),
+                Arguments.of(
+                        ConfigActionEnum.DELETE, ContextOuterClass.ConfigActionEnum.CONFIGACTION_DELETE),
+                Arguments.of(
+                        ConfigActionEnum.UNDEFINED, ContextOuterClass.ConfigActionEnum.CONFIGACTION_UNDEFINED));
+    }
+
+    @ParameterizedTest
+    @MethodSource("provideConfigActionEnum")
+    void shouldSerializeConfigActionEnum(
+            ConfigActionEnum configAction, ContextOuterClass.ConfigActionEnum expectedConfigAction) {
+        final var serializedConfigAction = serializer.serialize(configAction);
+        assertThat(serializedConfigAction.getNumber()).isEqualTo(expectedConfigAction.getNumber());
+    }
+
+    @ParameterizedTest
+    @MethodSource("provideConfigActionEnum")
+    void shouldDeserializeConfigActionEnum(
+            ConfigActionEnum expectedConfigAction,
+            ContextOuterClass.ConfigActionEnum serializedConfigAction) {
+        final var configAction = serializer.deserialize(serializedConfigAction);
+        assertThat(configAction).isEqualTo(expectedConfigAction);
+    }
+
+    @Test
+    void shouldSerializeConfigRule() {
+        final var expectedConfigRule =
+                ContextOuterClass.ConfigRule.newBuilder()
+                        .setAction(ContextOuterClass.ConfigActionEnum.CONFIGACTION_SET)
+                        .setResourceKey("resourceKey")
+                        .setResourceValue("resourceValue")
+                        .build();
+
+        final var configRule = new ConfigRule(ConfigActionEnum.SET, "resourceKey", "resourceValue");
+        final var serializedConfigRule = serializer.serialize(configRule);
+
+        assertThat(serializedConfigRule).usingRecursiveComparison().isEqualTo(expectedConfigRule);
+    }
+
+    @Test
+    void shouldDeserializeConfigRule() {
+        final var expectedConfigRule =
+                new ConfigRule(ConfigActionEnum.DELETE, "resourceKey", "resourceValue");
+
+        final var serializedConfigRule =
+                ContextOuterClass.ConfigRule.newBuilder()
+                        .setAction(ContextOuterClass.ConfigActionEnum.CONFIGACTION_DELETE)
+                        .setResourceKey("resourceKey")
+                        .setResourceValue("resourceValue")
+                        .build();
+        final var configRule = serializer.deserialize(serializedConfigRule);
+
+        assertThat(configRule).usingRecursiveComparison().isEqualTo(expectedConfigRule);
+    }
+
+    @Test
+    void shouldSerializeDeviceConfig() {
+        final var expectedConfigRuleA =
+                ContextOuterClass.ConfigRule.newBuilder()
+                        .setAction(ContextOuterClass.ConfigActionEnum.CONFIGACTION_SET)
+                        .setResourceKey("resourceKeyA")
+                        .setResourceValue("resourceValueA")
+                        .build();
+        final var expectedConfigRuleB =
+                ContextOuterClass.ConfigRule.newBuilder()
+                        .setAction(ContextOuterClass.ConfigActionEnum.CONFIGACTION_DELETE)
+                        .setResourceKey("resourceKeyB")
+                        .setResourceValue("resourceValueB")
+                        .build();
+        final var expectedDeviceConfig =
+                ContextOuterClass.DeviceConfig.newBuilder()
+                        .addAllConfigRules(List.of(expectedConfigRuleA, expectedConfigRuleB))
+                        .build();
+
+        final var configRuleA = new ConfigRule(ConfigActionEnum.SET, "resourceKeyA", "resourceValueA");
+        final var configRuleB =
+                new ConfigRule(ConfigActionEnum.DELETE, "resourceKeyB", "resourceValueB");
+        final var deviceConfig = new DeviceConfig(List.of(configRuleA, configRuleB));
+        final var serializedDeviceConfig = serializer.serialize(deviceConfig);
+
+        assertThat(serializedDeviceConfig).usingRecursiveComparison().isEqualTo(expectedDeviceConfig);
+    }
+
+    @Test
+    void shouldDeserializeDeviceConfig() {
+        final var expectedConfigRuleA =
+                new ConfigRule(ConfigActionEnum.SET, "resourceKeyA", "resourceValueA");
+        final var expectedConfigRuleB =
+                new ConfigRule(ConfigActionEnum.DELETE, "resourceKeyB", "resourceValueB");
+        final var expectedDeviceConfig =
+                new DeviceConfig(List.of(expectedConfigRuleA, expectedConfigRuleB));
+
+        final var configRuleA =
+                ContextOuterClass.ConfigRule.newBuilder()
+                        .setAction(ContextOuterClass.ConfigActionEnum.CONFIGACTION_SET)
+                        .setResourceKey("resourceKeyA")
+                        .setResourceValue("resourceValueA")
+                        .build();
+        final var configRuleB =
+                ContextOuterClass.ConfigRule.newBuilder()
+                        .setAction(ContextOuterClass.ConfigActionEnum.CONFIGACTION_DELETE)
+                        .setResourceKey("resourceKeyB")
+                        .setResourceValue("resourceValueB")
+                        .build();
+        final var serializedDeviceConfig =
+                ContextOuterClass.DeviceConfig.newBuilder()
+                        .addAllConfigRules(List.of(configRuleA, configRuleB))
+                        .build();
+        final var deviceConfig = serializer.deserialize(serializedDeviceConfig);
+
+        assertThat(deviceConfig).usingRecursiveComparison().isEqualTo(expectedDeviceConfig);
+    }
+
+    private static Stream<Arguments> provideOperationalStatusEnum() {
+        return Stream.of(
+                Arguments.of(
+                        DeviceOperationalStatus.ENABLED,
+                        DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_ENABLED),
+                Arguments.of(
+                        DeviceOperationalStatus.DISABLED,
+                        DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_DISABLED),
+                Arguments.of(
+                        DeviceOperationalStatus.UNDEFINED,
+                        DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_UNDEFINED));
+    }
+
+    @ParameterizedTest
+    @MethodSource("provideOperationalStatusEnum")
+    void shouldSerializeOperationalStatusEnum(
+            DeviceOperationalStatus opStatus,
+            ContextOuterClass.DeviceOperationalStatusEnum expectedOpStatus) {
+        final var serializedOpStatus = serializer.serialize(opStatus);
+        assertThat(serializedOpStatus.getNumber()).isEqualTo(expectedOpStatus.getNumber());
+    }
+
+    @ParameterizedTest
+    @MethodSource("provideOperationalStatusEnum")
+    void shouldDeserializeOperationalStatusEnum(
+            DeviceOperationalStatus expectedOpStatus,
+            ContextOuterClass.DeviceOperationalStatusEnum serializedOpStatus) {
+        final var operationalStatus = serializer.deserialize(serializedOpStatus);
+        assertThat(operationalStatus).isEqualTo(expectedOpStatus);
+    }
+
+    @Test
+    void shouldSerializeDevice() {
+        final var expectedConfigRule =
+                ContextOuterClass.ConfigRule.newBuilder()
+                        .setAction(ContextOuterClass.ConfigActionEnum.CONFIGACTION_SET)
+                        .setResourceKey("resourceKey")
+                        .setResourceValue("resourceValue")
+                        .build();
+        final var expectedDeviceConfig =
+                ContextOuterClass.DeviceConfig.newBuilder().addConfigRules(expectedConfigRule).build();
+        final var deviceBuilder = ContextOuterClass.Device.newBuilder();
+
+        final var serializedDeviceId = serializer.serializeDeviceId("deviceId");
+
+        deviceBuilder.setDeviceId(serializedDeviceId);
+        deviceBuilder.setDeviceType("deviceType");
+        deviceBuilder.setDeviceConfig(expectedDeviceConfig);
+        deviceBuilder.setDeviceOperationalStatus(serializer.serialize(DeviceOperationalStatus.ENABLED));
+        final var expectedDevice = deviceBuilder.build();
+
+        final var deviceConfig =
+                new DeviceConfig(
+                        List.of(new ConfigRule(ConfigActionEnum.SET, "resourceKey", "resourceValue")));
+        final var device =
+                new Device("deviceId", "deviceType", deviceConfig, DeviceOperationalStatus.ENABLED);
+        final var serializedDevice = serializer.serialize(device);
+
+        assertThat(serializedDevice).usingRecursiveComparison().isEqualTo(expectedDevice);
+    }
+
+    @Test
+    void shouldDeserializeDevice() {
+        final var expectedConfig =
+                new DeviceConfig(
+                        List.of(new ConfigRule(ConfigActionEnum.DELETE, "resourceKey", "resourceValue")));
+        final var expectedDevice =
+                new Device("deviceId", "deviceType", expectedConfig, DeviceOperationalStatus.ENABLED);
+
+        final var configRule =
+                ContextOuterClass.ConfigRule.newBuilder()
+                        .setAction(ContextOuterClass.ConfigActionEnum.CONFIGACTION_DELETE)
+                        .setResourceKey("resourceKey")
+                        .setResourceValue("resourceValue")
+                        .build();
+        final var deviceConfig =
+                ContextOuterClass.DeviceConfig.newBuilder().addConfigRules(configRule).build();
+
+        final var serializedDeviceId = serializer.serializeDeviceId("deviceId");
+
+        final var deviceBuilder = ContextOuterClass.Device.newBuilder();
+        deviceBuilder.setDeviceId(serializedDeviceId);
+        deviceBuilder.setDeviceType("deviceType");
+        deviceBuilder.setDeviceConfig(deviceConfig);
+        deviceBuilder.setDeviceOperationalStatus(serializer.serialize(DeviceOperationalStatus.ENABLED));
+        final var serializedDevice = deviceBuilder.build();
+        final var device = serializer.deserialize(serializedDevice);
+
+        assertThat(device).usingRecursiveComparison().isEqualTo(expectedDevice);
+    }
+
+    @Test
+    void shouldSerializeUuid() {
+        final var expectedUuid = "uuid";
+
+        final var serializeUuid = serializer.serializeUuid("uuid");
+
+        assertThat(serializeUuid.getUuid()).isEqualTo(expectedUuid);
+    }
+
+    @Test
+    void shouldDeserializeUuid() {
+        final var expectedUuid = "uuid";
+
+        final var uuid = serializer.deserialize(Uuid.newBuilder().setUuid("uuid").build());
+
+        assertThat(uuid).isEqualTo(expectedUuid);
+    }
+}
diff --git a/src/automation/target/generated-sources/grpc/context/ContextOuterClass.java b/src/automation/target/generated-sources/grpc/context/ContextOuterClass.java
index 3decce929e0eb9ac2e50c23f8aa3dba36b4321bf..168ddf78b3a0233f3157e984fb7d925783bcd3b0 100644
--- a/src/automation/target/generated-sources/grpc/context/ContextOuterClass.java
+++ b/src/automation/target/generated-sources/grpc/context/ContextOuterClass.java
@@ -27653,79 +27653,49 @@ public final class ContextOuterClass {
 
   }
 
-  public interface EndPointIdOrBuilder extends
-      // @@protoc_insertion_point(interface_extends:context.EndPointId)
+  public interface ConnectionIdOrBuilder extends
+      // @@protoc_insertion_point(interface_extends:context.ConnectionId)
       com.google.protobuf.MessageOrBuilder {
 
     /**
-     * <code>.context.TopologyId topology_id = 1;</code>
-     * @return Whether the topologyId field is set.
-     */
-    boolean hasTopologyId();
-    /**
-     * <code>.context.TopologyId topology_id = 1;</code>
-     * @return The topologyId.
-     */
-    context.ContextOuterClass.TopologyId getTopologyId();
-    /**
-     * <code>.context.TopologyId topology_id = 1;</code>
-     */
-    context.ContextOuterClass.TopologyIdOrBuilder getTopologyIdOrBuilder();
-
-    /**
-     * <code>.context.DeviceId device_id = 2;</code>
-     * @return Whether the deviceId field is set.
-     */
-    boolean hasDeviceId();
-    /**
-     * <code>.context.DeviceId device_id = 2;</code>
-     * @return The deviceId.
-     */
-    context.ContextOuterClass.DeviceId getDeviceId();
-    /**
-     * <code>.context.DeviceId device_id = 2;</code>
-     */
-    context.ContextOuterClass.DeviceIdOrBuilder getDeviceIdOrBuilder();
-
-    /**
-     * <code>.context.Uuid endpoint_uuid = 3;</code>
-     * @return Whether the endpointUuid field is set.
+     * <code>.context.Uuid connection_uuid = 1;</code>
+     * @return Whether the connectionUuid field is set.
      */
-    boolean hasEndpointUuid();
+    boolean hasConnectionUuid();
     /**
-     * <code>.context.Uuid endpoint_uuid = 3;</code>
-     * @return The endpointUuid.
+     * <code>.context.Uuid connection_uuid = 1;</code>
+     * @return The connectionUuid.
      */
-    context.ContextOuterClass.Uuid getEndpointUuid();
+    context.ContextOuterClass.Uuid getConnectionUuid();
     /**
-     * <code>.context.Uuid endpoint_uuid = 3;</code>
+     * <code>.context.Uuid connection_uuid = 1;</code>
      */
-    context.ContextOuterClass.UuidOrBuilder getEndpointUuidOrBuilder();
+    context.ContextOuterClass.UuidOrBuilder getConnectionUuidOrBuilder();
   }
   /**
    * <pre>
-   * ----- Endpoint ------------------------------------------------------------------------------------------------------
+   * ----- Connection ----------------------------------------------------------------------------------------------------
    * </pre>
    *
-   * Protobuf type {@code context.EndPointId}
+   * Protobuf type {@code context.ConnectionId}
    */
-  public static final class EndPointId extends
+  public static final class ConnectionId extends
       com.google.protobuf.GeneratedMessageV3 implements
-      // @@protoc_insertion_point(message_implements:context.EndPointId)
-      EndPointIdOrBuilder {
+      // @@protoc_insertion_point(message_implements:context.ConnectionId)
+      ConnectionIdOrBuilder {
   private static final long serialVersionUID = 0L;
-    // Use EndPointId.newBuilder() to construct.
-    private EndPointId(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
+    // Use ConnectionId.newBuilder() to construct.
+    private ConnectionId(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
       super(builder);
     }
-    private EndPointId() {
+    private ConnectionId() {
     }
 
     @java.lang.Override
     @SuppressWarnings({"unused"})
     protected java.lang.Object newInstance(
         UnusedPrivateParameter unused) {
-      return new EndPointId();
+      return new ConnectionId();
     }
 
     @java.lang.Override
@@ -27733,7 +27703,7 @@ public final class ContextOuterClass {
     getUnknownFields() {
       return this.unknownFields;
     }
-    private EndPointId(
+    private ConnectionId(
         com.google.protobuf.CodedInputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
@@ -27752,40 +27722,14 @@ public final class ContextOuterClass {
               done = true;
               break;
             case 10: {
-              context.ContextOuterClass.TopologyId.Builder subBuilder = null;
-              if (topologyId_ != null) {
-                subBuilder = topologyId_.toBuilder();
-              }
-              topologyId_ = input.readMessage(context.ContextOuterClass.TopologyId.parser(), extensionRegistry);
-              if (subBuilder != null) {
-                subBuilder.mergeFrom(topologyId_);
-                topologyId_ = subBuilder.buildPartial();
-              }
-
-              break;
-            }
-            case 18: {
-              context.ContextOuterClass.DeviceId.Builder subBuilder = null;
-              if (deviceId_ != null) {
-                subBuilder = deviceId_.toBuilder();
-              }
-              deviceId_ = input.readMessage(context.ContextOuterClass.DeviceId.parser(), extensionRegistry);
-              if (subBuilder != null) {
-                subBuilder.mergeFrom(deviceId_);
-                deviceId_ = subBuilder.buildPartial();
-              }
-
-              break;
-            }
-            case 26: {
               context.ContextOuterClass.Uuid.Builder subBuilder = null;
-              if (endpointUuid_ != null) {
-                subBuilder = endpointUuid_.toBuilder();
+              if (connectionUuid_ != null) {
+                subBuilder = connectionUuid_.toBuilder();
               }
-              endpointUuid_ = input.readMessage(context.ContextOuterClass.Uuid.parser(), extensionRegistry);
+              connectionUuid_ = input.readMessage(context.ContextOuterClass.Uuid.parser(), extensionRegistry);
               if (subBuilder != null) {
-                subBuilder.mergeFrom(endpointUuid_);
-                endpointUuid_ = subBuilder.buildPartial();
+                subBuilder.mergeFrom(connectionUuid_);
+                connectionUuid_ = subBuilder.buildPartial();
               }
 
               break;
@@ -27811,93 +27755,41 @@ public final class ContextOuterClass {
     }
     public static final com.google.protobuf.Descriptors.Descriptor
         getDescriptor() {
-      return context.ContextOuterClass.internal_static_context_EndPointId_descriptor;
+      return context.ContextOuterClass.internal_static_context_ConnectionId_descriptor;
     }
 
     @java.lang.Override
     protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
         internalGetFieldAccessorTable() {
-      return context.ContextOuterClass.internal_static_context_EndPointId_fieldAccessorTable
+      return context.ContextOuterClass.internal_static_context_ConnectionId_fieldAccessorTable
           .ensureFieldAccessorsInitialized(
-              context.ContextOuterClass.EndPointId.class, context.ContextOuterClass.EndPointId.Builder.class);
-    }
-
-    public static final int TOPOLOGY_ID_FIELD_NUMBER = 1;
-    private context.ContextOuterClass.TopologyId topologyId_;
-    /**
-     * <code>.context.TopologyId topology_id = 1;</code>
-     * @return Whether the topologyId field is set.
-     */
-    @java.lang.Override
-    public boolean hasTopologyId() {
-      return topologyId_ != null;
-    }
-    /**
-     * <code>.context.TopologyId topology_id = 1;</code>
-     * @return The topologyId.
-     */
-    @java.lang.Override
-    public context.ContextOuterClass.TopologyId getTopologyId() {
-      return topologyId_ == null ? context.ContextOuterClass.TopologyId.getDefaultInstance() : topologyId_;
-    }
-    /**
-     * <code>.context.TopologyId topology_id = 1;</code>
-     */
-    @java.lang.Override
-    public context.ContextOuterClass.TopologyIdOrBuilder getTopologyIdOrBuilder() {
-      return getTopologyId();
-    }
-
-    public static final int DEVICE_ID_FIELD_NUMBER = 2;
-    private context.ContextOuterClass.DeviceId deviceId_;
-    /**
-     * <code>.context.DeviceId device_id = 2;</code>
-     * @return Whether the deviceId field is set.
-     */
-    @java.lang.Override
-    public boolean hasDeviceId() {
-      return deviceId_ != null;
-    }
-    /**
-     * <code>.context.DeviceId device_id = 2;</code>
-     * @return The deviceId.
-     */
-    @java.lang.Override
-    public context.ContextOuterClass.DeviceId getDeviceId() {
-      return deviceId_ == null ? context.ContextOuterClass.DeviceId.getDefaultInstance() : deviceId_;
-    }
-    /**
-     * <code>.context.DeviceId device_id = 2;</code>
-     */
-    @java.lang.Override
-    public context.ContextOuterClass.DeviceIdOrBuilder getDeviceIdOrBuilder() {
-      return getDeviceId();
+              context.ContextOuterClass.ConnectionId.class, context.ContextOuterClass.ConnectionId.Builder.class);
     }
 
-    public static final int ENDPOINT_UUID_FIELD_NUMBER = 3;
-    private context.ContextOuterClass.Uuid endpointUuid_;
+    public static final int CONNECTION_UUID_FIELD_NUMBER = 1;
+    private context.ContextOuterClass.Uuid connectionUuid_;
     /**
-     * <code>.context.Uuid endpoint_uuid = 3;</code>
-     * @return Whether the endpointUuid field is set.
+     * <code>.context.Uuid connection_uuid = 1;</code>
+     * @return Whether the connectionUuid field is set.
      */
     @java.lang.Override
-    public boolean hasEndpointUuid() {
-      return endpointUuid_ != null;
+    public boolean hasConnectionUuid() {
+      return connectionUuid_ != null;
     }
     /**
-     * <code>.context.Uuid endpoint_uuid = 3;</code>
-     * @return The endpointUuid.
+     * <code>.context.Uuid connection_uuid = 1;</code>
+     * @return The connectionUuid.
      */
     @java.lang.Override
-    public context.ContextOuterClass.Uuid getEndpointUuid() {
-      return endpointUuid_ == null ? context.ContextOuterClass.Uuid.getDefaultInstance() : endpointUuid_;
+    public context.ContextOuterClass.Uuid getConnectionUuid() {
+      return connectionUuid_ == null ? context.ContextOuterClass.Uuid.getDefaultInstance() : connectionUuid_;
     }
     /**
-     * <code>.context.Uuid endpoint_uuid = 3;</code>
+     * <code>.context.Uuid connection_uuid = 1;</code>
      */
     @java.lang.Override
-    public context.ContextOuterClass.UuidOrBuilder getEndpointUuidOrBuilder() {
-      return getEndpointUuid();
+    public context.ContextOuterClass.UuidOrBuilder getConnectionUuidOrBuilder() {
+      return getConnectionUuid();
     }
 
     private byte memoizedIsInitialized = -1;
@@ -27914,14 +27806,8 @@ public final class ContextOuterClass {
     @java.lang.Override
     public void writeTo(com.google.protobuf.CodedOutputStream output)
                         throws java.io.IOException {
-      if (topologyId_ != null) {
-        output.writeMessage(1, getTopologyId());
-      }
-      if (deviceId_ != null) {
-        output.writeMessage(2, getDeviceId());
-      }
-      if (endpointUuid_ != null) {
-        output.writeMessage(3, getEndpointUuid());
+      if (connectionUuid_ != null) {
+        output.writeMessage(1, getConnectionUuid());
       }
       unknownFields.writeTo(output);
     }
@@ -27932,17 +27818,9 @@ public final class ContextOuterClass {
       if (size != -1) return size;
 
       size = 0;
-      if (topologyId_ != null) {
-        size += com.google.protobuf.CodedOutputStream
-          .computeMessageSize(1, getTopologyId());
-      }
-      if (deviceId_ != null) {
-        size += com.google.protobuf.CodedOutputStream
-          .computeMessageSize(2, getDeviceId());
-      }
-      if (endpointUuid_ != null) {
+      if (connectionUuid_ != null) {
         size += com.google.protobuf.CodedOutputStream
-          .computeMessageSize(3, getEndpointUuid());
+          .computeMessageSize(1, getConnectionUuid());
       }
       size += unknownFields.getSerializedSize();
       memoizedSize = size;
@@ -27954,25 +27832,15 @@ public final class ContextOuterClass {
       if (obj == this) {
        return true;
       }
-      if (!(obj instanceof context.ContextOuterClass.EndPointId)) {
+      if (!(obj instanceof context.ContextOuterClass.ConnectionId)) {
         return super.equals(obj);
       }
-      context.ContextOuterClass.EndPointId other = (context.ContextOuterClass.EndPointId) obj;
+      context.ContextOuterClass.ConnectionId other = (context.ContextOuterClass.ConnectionId) obj;
 
-      if (hasTopologyId() != other.hasTopologyId()) return false;
-      if (hasTopologyId()) {
-        if (!getTopologyId()
-            .equals(other.getTopologyId())) return false;
-      }
-      if (hasDeviceId() != other.hasDeviceId()) return false;
-      if (hasDeviceId()) {
-        if (!getDeviceId()
-            .equals(other.getDeviceId())) return false;
-      }
-      if (hasEndpointUuid() != other.hasEndpointUuid()) return false;
-      if (hasEndpointUuid()) {
-        if (!getEndpointUuid()
-            .equals(other.getEndpointUuid())) return false;
+      if (hasConnectionUuid() != other.hasConnectionUuid()) return false;
+      if (hasConnectionUuid()) {
+        if (!getConnectionUuid()
+            .equals(other.getConnectionUuid())) return false;
       }
       if (!unknownFields.equals(other.unknownFields)) return false;
       return true;
@@ -27985,86 +27853,78 @@ public final class ContextOuterClass {
       }
       int hash = 41;
       hash = (19 * hash) + getDescriptor().hashCode();
-      if (hasTopologyId()) {
-        hash = (37 * hash) + TOPOLOGY_ID_FIELD_NUMBER;
-        hash = (53 * hash) + getTopologyId().hashCode();
-      }
-      if (hasDeviceId()) {
-        hash = (37 * hash) + DEVICE_ID_FIELD_NUMBER;
-        hash = (53 * hash) + getDeviceId().hashCode();
-      }
-      if (hasEndpointUuid()) {
-        hash = (37 * hash) + ENDPOINT_UUID_FIELD_NUMBER;
-        hash = (53 * hash) + getEndpointUuid().hashCode();
+      if (hasConnectionUuid()) {
+        hash = (37 * hash) + CONNECTION_UUID_FIELD_NUMBER;
+        hash = (53 * hash) + getConnectionUuid().hashCode();
       }
       hash = (29 * hash) + unknownFields.hashCode();
       memoizedHashCode = hash;
       return hash;
     }
 
-    public static context.ContextOuterClass.EndPointId parseFrom(
+    public static context.ContextOuterClass.ConnectionId parseFrom(
         java.nio.ByteBuffer data)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data);
     }
-    public static context.ContextOuterClass.EndPointId parseFrom(
+    public static context.ContextOuterClass.ConnectionId parseFrom(
         java.nio.ByteBuffer data,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data, extensionRegistry);
     }
-    public static context.ContextOuterClass.EndPointId parseFrom(
+    public static context.ContextOuterClass.ConnectionId parseFrom(
         com.google.protobuf.ByteString data)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data);
     }
-    public static context.ContextOuterClass.EndPointId parseFrom(
+    public static context.ContextOuterClass.ConnectionId parseFrom(
         com.google.protobuf.ByteString data,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data, extensionRegistry);
     }
-    public static context.ContextOuterClass.EndPointId parseFrom(byte[] data)
+    public static context.ContextOuterClass.ConnectionId parseFrom(byte[] data)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data);
     }
-    public static context.ContextOuterClass.EndPointId parseFrom(
+    public static context.ContextOuterClass.ConnectionId parseFrom(
         byte[] data,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data, extensionRegistry);
     }
-    public static context.ContextOuterClass.EndPointId parseFrom(java.io.InputStream input)
+    public static context.ContextOuterClass.ConnectionId parseFrom(java.io.InputStream input)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseWithIOException(PARSER, input);
     }
-    public static context.ContextOuterClass.EndPointId parseFrom(
+    public static context.ContextOuterClass.ConnectionId parseFrom(
         java.io.InputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseWithIOException(PARSER, input, extensionRegistry);
     }
-    public static context.ContextOuterClass.EndPointId parseDelimitedFrom(java.io.InputStream input)
+    public static context.ContextOuterClass.ConnectionId parseDelimitedFrom(java.io.InputStream input)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseDelimitedWithIOException(PARSER, input);
     }
-    public static context.ContextOuterClass.EndPointId parseDelimitedFrom(
+    public static context.ContextOuterClass.ConnectionId parseDelimitedFrom(
         java.io.InputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseDelimitedWithIOException(PARSER, input, extensionRegistry);
     }
-    public static context.ContextOuterClass.EndPointId parseFrom(
+    public static context.ContextOuterClass.ConnectionId parseFrom(
         com.google.protobuf.CodedInputStream input)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseWithIOException(PARSER, input);
     }
-    public static context.ContextOuterClass.EndPointId parseFrom(
+    public static context.ContextOuterClass.ConnectionId parseFrom(
         com.google.protobuf.CodedInputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws java.io.IOException {
@@ -28077,7 +27937,7 @@ public final class ContextOuterClass {
     public static Builder newBuilder() {
       return DEFAULT_INSTANCE.toBuilder();
     }
-    public static Builder newBuilder(context.ContextOuterClass.EndPointId prototype) {
+    public static Builder newBuilder(context.ContextOuterClass.ConnectionId prototype) {
       return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
     }
     @java.lang.Override
@@ -28094,29 +27954,29 @@ public final class ContextOuterClass {
     }
     /**
      * <pre>
-     * ----- Endpoint ------------------------------------------------------------------------------------------------------
+     * ----- Connection ----------------------------------------------------------------------------------------------------
      * </pre>
      *
-     * Protobuf type {@code context.EndPointId}
+     * Protobuf type {@code context.ConnectionId}
      */
     public static final class Builder extends
         com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
-        // @@protoc_insertion_point(builder_implements:context.EndPointId)
-        context.ContextOuterClass.EndPointIdOrBuilder {
+        // @@protoc_insertion_point(builder_implements:context.ConnectionId)
+        context.ContextOuterClass.ConnectionIdOrBuilder {
       public static final com.google.protobuf.Descriptors.Descriptor
           getDescriptor() {
-        return context.ContextOuterClass.internal_static_context_EndPointId_descriptor;
+        return context.ContextOuterClass.internal_static_context_ConnectionId_descriptor;
       }
 
       @java.lang.Override
       protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
           internalGetFieldAccessorTable() {
-        return context.ContextOuterClass.internal_static_context_EndPointId_fieldAccessorTable
+        return context.ContextOuterClass.internal_static_context_ConnectionId_fieldAccessorTable
             .ensureFieldAccessorsInitialized(
-                context.ContextOuterClass.EndPointId.class, context.ContextOuterClass.EndPointId.Builder.class);
+                context.ContextOuterClass.ConnectionId.class, context.ContextOuterClass.ConnectionId.Builder.class);
       }
 
-      // Construct using context.ContextOuterClass.EndPointId.newBuilder()
+      // Construct using context.ContextOuterClass.ConnectionId.newBuilder()
       private Builder() {
         maybeForceBuilderInitialization();
       }
@@ -28134,23 +27994,11 @@ public final class ContextOuterClass {
       @java.lang.Override
       public Builder clear() {
         super.clear();
-        if (topologyIdBuilder_ == null) {
-          topologyId_ = null;
-        } else {
-          topologyId_ = null;
-          topologyIdBuilder_ = null;
-        }
-        if (deviceIdBuilder_ == null) {
-          deviceId_ = null;
-        } else {
-          deviceId_ = null;
-          deviceIdBuilder_ = null;
-        }
-        if (endpointUuidBuilder_ == null) {
-          endpointUuid_ = null;
+        if (connectionUuidBuilder_ == null) {
+          connectionUuid_ = null;
         } else {
-          endpointUuid_ = null;
-          endpointUuidBuilder_ = null;
+          connectionUuid_ = null;
+          connectionUuidBuilder_ = null;
         }
         return this;
       }
@@ -28158,17 +28006,17 @@ public final class ContextOuterClass {
       @java.lang.Override
       public com.google.protobuf.Descriptors.Descriptor
           getDescriptorForType() {
-        return context.ContextOuterClass.internal_static_context_EndPointId_descriptor;
+        return context.ContextOuterClass.internal_static_context_ConnectionId_descriptor;
       }
 
       @java.lang.Override
-      public context.ContextOuterClass.EndPointId getDefaultInstanceForType() {
-        return context.ContextOuterClass.EndPointId.getDefaultInstance();
+      public context.ContextOuterClass.ConnectionId getDefaultInstanceForType() {
+        return context.ContextOuterClass.ConnectionId.getDefaultInstance();
       }
 
       @java.lang.Override
-      public context.ContextOuterClass.EndPointId build() {
-        context.ContextOuterClass.EndPointId result = buildPartial();
+      public context.ContextOuterClass.ConnectionId build() {
+        context.ContextOuterClass.ConnectionId result = buildPartial();
         if (!result.isInitialized()) {
           throw newUninitializedMessageException(result);
         }
@@ -28176,22 +28024,12 @@ public final class ContextOuterClass {
       }
 
       @java.lang.Override
-      public context.ContextOuterClass.EndPointId buildPartial() {
-        context.ContextOuterClass.EndPointId result = new context.ContextOuterClass.EndPointId(this);
-        if (topologyIdBuilder_ == null) {
-          result.topologyId_ = topologyId_;
-        } else {
-          result.topologyId_ = topologyIdBuilder_.build();
-        }
-        if (deviceIdBuilder_ == null) {
-          result.deviceId_ = deviceId_;
-        } else {
-          result.deviceId_ = deviceIdBuilder_.build();
-        }
-        if (endpointUuidBuilder_ == null) {
-          result.endpointUuid_ = endpointUuid_;
+      public context.ContextOuterClass.ConnectionId buildPartial() {
+        context.ContextOuterClass.ConnectionId result = new context.ContextOuterClass.ConnectionId(this);
+        if (connectionUuidBuilder_ == null) {
+          result.connectionUuid_ = connectionUuid_;
         } else {
-          result.endpointUuid_ = endpointUuidBuilder_.build();
+          result.connectionUuid_ = connectionUuidBuilder_.build();
         }
         onBuilt();
         return result;
@@ -28231,24 +28069,18 @@ public final class ContextOuterClass {
       }
       @java.lang.Override
       public Builder mergeFrom(com.google.protobuf.Message other) {
-        if (other instanceof context.ContextOuterClass.EndPointId) {
-          return mergeFrom((context.ContextOuterClass.EndPointId)other);
+        if (other instanceof context.ContextOuterClass.ConnectionId) {
+          return mergeFrom((context.ContextOuterClass.ConnectionId)other);
         } else {
           super.mergeFrom(other);
           return this;
         }
       }
 
-      public Builder mergeFrom(context.ContextOuterClass.EndPointId other) {
-        if (other == context.ContextOuterClass.EndPointId.getDefaultInstance()) return this;
-        if (other.hasTopologyId()) {
-          mergeTopologyId(other.getTopologyId());
-        }
-        if (other.hasDeviceId()) {
-          mergeDeviceId(other.getDeviceId());
-        }
-        if (other.hasEndpointUuid()) {
-          mergeEndpointUuid(other.getEndpointUuid());
+      public Builder mergeFrom(context.ContextOuterClass.ConnectionId other) {
+        if (other == context.ContextOuterClass.ConnectionId.getDefaultInstance()) return this;
+        if (other.hasConnectionUuid()) {
+          mergeConnectionUuid(other.getConnectionUuid());
         }
         this.mergeUnknownFields(other.unknownFields);
         onChanged();
@@ -28265,11 +28097,11 @@ public final class ContextOuterClass {
           com.google.protobuf.CodedInputStream input,
           com.google.protobuf.ExtensionRegistryLite extensionRegistry)
           throws java.io.IOException {
-        context.ContextOuterClass.EndPointId parsedMessage = null;
+        context.ContextOuterClass.ConnectionId parsedMessage = null;
         try {
           parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
         } catch (com.google.protobuf.InvalidProtocolBufferException e) {
-          parsedMessage = (context.ContextOuterClass.EndPointId) e.getUnfinishedMessage();
+          parsedMessage = (context.ContextOuterClass.ConnectionId) e.getUnfinishedMessage();
           throw e.unwrapIOException();
         } finally {
           if (parsedMessage != null) {
@@ -28279,361 +28111,1694 @@ public final class ContextOuterClass {
         return this;
       }
 
-      private context.ContextOuterClass.TopologyId topologyId_;
+      private context.ContextOuterClass.Uuid connectionUuid_;
       private com.google.protobuf.SingleFieldBuilderV3<
-          context.ContextOuterClass.TopologyId, context.ContextOuterClass.TopologyId.Builder, context.ContextOuterClass.TopologyIdOrBuilder> topologyIdBuilder_;
+          context.ContextOuterClass.Uuid, context.ContextOuterClass.Uuid.Builder, context.ContextOuterClass.UuidOrBuilder> connectionUuidBuilder_;
       /**
-       * <code>.context.TopologyId topology_id = 1;</code>
-       * @return Whether the topologyId field is set.
+       * <code>.context.Uuid connection_uuid = 1;</code>
+       * @return Whether the connectionUuid field is set.
        */
-      public boolean hasTopologyId() {
-        return topologyIdBuilder_ != null || topologyId_ != null;
+      public boolean hasConnectionUuid() {
+        return connectionUuidBuilder_ != null || connectionUuid_ != null;
       }
       /**
-       * <code>.context.TopologyId topology_id = 1;</code>
-       * @return The topologyId.
+       * <code>.context.Uuid connection_uuid = 1;</code>
+       * @return The connectionUuid.
        */
-      public context.ContextOuterClass.TopologyId getTopologyId() {
-        if (topologyIdBuilder_ == null) {
-          return topologyId_ == null ? context.ContextOuterClass.TopologyId.getDefaultInstance() : topologyId_;
+      public context.ContextOuterClass.Uuid getConnectionUuid() {
+        if (connectionUuidBuilder_ == null) {
+          return connectionUuid_ == null ? context.ContextOuterClass.Uuid.getDefaultInstance() : connectionUuid_;
         } else {
-          return topologyIdBuilder_.getMessage();
+          return connectionUuidBuilder_.getMessage();
         }
       }
       /**
-       * <code>.context.TopologyId topology_id = 1;</code>
+       * <code>.context.Uuid connection_uuid = 1;</code>
        */
-      public Builder setTopologyId(context.ContextOuterClass.TopologyId value) {
-        if (topologyIdBuilder_ == null) {
+      public Builder setConnectionUuid(context.ContextOuterClass.Uuid value) {
+        if (connectionUuidBuilder_ == null) {
           if (value == null) {
             throw new NullPointerException();
           }
-          topologyId_ = value;
+          connectionUuid_ = value;
           onChanged();
         } else {
-          topologyIdBuilder_.setMessage(value);
+          connectionUuidBuilder_.setMessage(value);
         }
 
         return this;
       }
       /**
-       * <code>.context.TopologyId topology_id = 1;</code>
+       * <code>.context.Uuid connection_uuid = 1;</code>
        */
-      public Builder setTopologyId(
-          context.ContextOuterClass.TopologyId.Builder builderForValue) {
-        if (topologyIdBuilder_ == null) {
-          topologyId_ = builderForValue.build();
+      public Builder setConnectionUuid(
+          context.ContextOuterClass.Uuid.Builder builderForValue) {
+        if (connectionUuidBuilder_ == null) {
+          connectionUuid_ = builderForValue.build();
           onChanged();
         } else {
-          topologyIdBuilder_.setMessage(builderForValue.build());
+          connectionUuidBuilder_.setMessage(builderForValue.build());
         }
 
         return this;
       }
       /**
-       * <code>.context.TopologyId topology_id = 1;</code>
+       * <code>.context.Uuid connection_uuid = 1;</code>
        */
-      public Builder mergeTopologyId(context.ContextOuterClass.TopologyId value) {
-        if (topologyIdBuilder_ == null) {
-          if (topologyId_ != null) {
-            topologyId_ =
-              context.ContextOuterClass.TopologyId.newBuilder(topologyId_).mergeFrom(value).buildPartial();
+      public Builder mergeConnectionUuid(context.ContextOuterClass.Uuid value) {
+        if (connectionUuidBuilder_ == null) {
+          if (connectionUuid_ != null) {
+            connectionUuid_ =
+              context.ContextOuterClass.Uuid.newBuilder(connectionUuid_).mergeFrom(value).buildPartial();
           } else {
-            topologyId_ = value;
+            connectionUuid_ = value;
           }
           onChanged();
         } else {
-          topologyIdBuilder_.mergeFrom(value);
+          connectionUuidBuilder_.mergeFrom(value);
         }
 
         return this;
       }
       /**
-       * <code>.context.TopologyId topology_id = 1;</code>
+       * <code>.context.Uuid connection_uuid = 1;</code>
        */
-      public Builder clearTopologyId() {
-        if (topologyIdBuilder_ == null) {
-          topologyId_ = null;
+      public Builder clearConnectionUuid() {
+        if (connectionUuidBuilder_ == null) {
+          connectionUuid_ = null;
           onChanged();
         } else {
-          topologyId_ = null;
-          topologyIdBuilder_ = null;
+          connectionUuid_ = null;
+          connectionUuidBuilder_ = null;
         }
 
         return this;
       }
       /**
-       * <code>.context.TopologyId topology_id = 1;</code>
+       * <code>.context.Uuid connection_uuid = 1;</code>
        */
-      public context.ContextOuterClass.TopologyId.Builder getTopologyIdBuilder() {
+      public context.ContextOuterClass.Uuid.Builder getConnectionUuidBuilder() {
         
         onChanged();
-        return getTopologyIdFieldBuilder().getBuilder();
+        return getConnectionUuidFieldBuilder().getBuilder();
       }
       /**
-       * <code>.context.TopologyId topology_id = 1;</code>
+       * <code>.context.Uuid connection_uuid = 1;</code>
        */
-      public context.ContextOuterClass.TopologyIdOrBuilder getTopologyIdOrBuilder() {
-        if (topologyIdBuilder_ != null) {
-          return topologyIdBuilder_.getMessageOrBuilder();
+      public context.ContextOuterClass.UuidOrBuilder getConnectionUuidOrBuilder() {
+        if (connectionUuidBuilder_ != null) {
+          return connectionUuidBuilder_.getMessageOrBuilder();
         } else {
-          return topologyId_ == null ?
-              context.ContextOuterClass.TopologyId.getDefaultInstance() : topologyId_;
+          return connectionUuid_ == null ?
+              context.ContextOuterClass.Uuid.getDefaultInstance() : connectionUuid_;
         }
       }
       /**
-       * <code>.context.TopologyId topology_id = 1;</code>
+       * <code>.context.Uuid connection_uuid = 1;</code>
        */
       private com.google.protobuf.SingleFieldBuilderV3<
-          context.ContextOuterClass.TopologyId, context.ContextOuterClass.TopologyId.Builder, context.ContextOuterClass.TopologyIdOrBuilder> 
-          getTopologyIdFieldBuilder() {
-        if (topologyIdBuilder_ == null) {
-          topologyIdBuilder_ = new com.google.protobuf.SingleFieldBuilderV3<
-              context.ContextOuterClass.TopologyId, context.ContextOuterClass.TopologyId.Builder, context.ContextOuterClass.TopologyIdOrBuilder>(
-                  getTopologyId(),
+          context.ContextOuterClass.Uuid, context.ContextOuterClass.Uuid.Builder, context.ContextOuterClass.UuidOrBuilder> 
+          getConnectionUuidFieldBuilder() {
+        if (connectionUuidBuilder_ == null) {
+          connectionUuidBuilder_ = new com.google.protobuf.SingleFieldBuilderV3<
+              context.ContextOuterClass.Uuid, context.ContextOuterClass.Uuid.Builder, context.ContextOuterClass.UuidOrBuilder>(
+                  getConnectionUuid(),
                   getParentForChildren(),
                   isClean());
-          topologyId_ = null;
+          connectionUuid_ = null;
         }
-        return topologyIdBuilder_;
-      }
-
-      private context.ContextOuterClass.DeviceId deviceId_;
-      private com.google.protobuf.SingleFieldBuilderV3<
-          context.ContextOuterClass.DeviceId, context.ContextOuterClass.DeviceId.Builder, context.ContextOuterClass.DeviceIdOrBuilder> deviceIdBuilder_;
-      /**
-       * <code>.context.DeviceId device_id = 2;</code>
-       * @return Whether the deviceId field is set.
-       */
-      public boolean hasDeviceId() {
-        return deviceIdBuilder_ != null || deviceId_ != null;
+        return connectionUuidBuilder_;
       }
-      /**
-       * <code>.context.DeviceId device_id = 2;</code>
-       * @return The deviceId.
-       */
-      public context.ContextOuterClass.DeviceId getDeviceId() {
-        if (deviceIdBuilder_ == null) {
-          return deviceId_ == null ? context.ContextOuterClass.DeviceId.getDefaultInstance() : deviceId_;
-        } else {
-          return deviceIdBuilder_.getMessage();
-        }
+      @java.lang.Override
+      public final Builder setUnknownFields(
+          final com.google.protobuf.UnknownFieldSet unknownFields) {
+        return super.setUnknownFields(unknownFields);
       }
-      /**
-       * <code>.context.DeviceId device_id = 2;</code>
-       */
-      public Builder setDeviceId(context.ContextOuterClass.DeviceId value) {
-        if (deviceIdBuilder_ == null) {
-          if (value == null) {
-            throw new NullPointerException();
-          }
-          deviceId_ = value;
-          onChanged();
-        } else {
-          deviceIdBuilder_.setMessage(value);
-        }
 
-        return this;
-      }
+      @java.lang.Override
+      public final Builder mergeUnknownFields(
+          final com.google.protobuf.UnknownFieldSet unknownFields) {
+        return super.mergeUnknownFields(unknownFields);
+      }
+
+
+      // @@protoc_insertion_point(builder_scope:context.ConnectionId)
+    }
+
+    // @@protoc_insertion_point(class_scope:context.ConnectionId)
+    private static final context.ContextOuterClass.ConnectionId DEFAULT_INSTANCE;
+    static {
+      DEFAULT_INSTANCE = new context.ContextOuterClass.ConnectionId();
+    }
+
+    public static context.ContextOuterClass.ConnectionId getDefaultInstance() {
+      return DEFAULT_INSTANCE;
+    }
+
+    private static final com.google.protobuf.Parser<ConnectionId>
+        PARSER = new com.google.protobuf.AbstractParser<ConnectionId>() {
+      @java.lang.Override
+      public ConnectionId parsePartialFrom(
+          com.google.protobuf.CodedInputStream input,
+          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+          throws com.google.protobuf.InvalidProtocolBufferException {
+        return new ConnectionId(input, extensionRegistry);
+      }
+    };
+
+    public static com.google.protobuf.Parser<ConnectionId> parser() {
+      return PARSER;
+    }
+
+    @java.lang.Override
+    public com.google.protobuf.Parser<ConnectionId> getParserForType() {
+      return PARSER;
+    }
+
+    @java.lang.Override
+    public context.ContextOuterClass.ConnectionId getDefaultInstanceForType() {
+      return DEFAULT_INSTANCE;
+    }
+
+  }
+
+  public interface ConnectionOrBuilder extends
+      // @@protoc_insertion_point(interface_extends:context.Connection)
+      com.google.protobuf.MessageOrBuilder {
+
+    /**
+     * <code>.context.ConnectionId connection_id = 1;</code>
+     * @return Whether the connectionId field is set.
+     */
+    boolean hasConnectionId();
+    /**
+     * <code>.context.ConnectionId connection_id = 1;</code>
+     * @return The connectionId.
+     */
+    context.ContextOuterClass.ConnectionId getConnectionId();
+    /**
+     * <code>.context.ConnectionId connection_id = 1;</code>
+     */
+    context.ContextOuterClass.ConnectionIdOrBuilder getConnectionIdOrBuilder();
+
+    /**
+     * <code>.context.ServiceId service_id = 2;</code>
+     * @return Whether the serviceId field is set.
+     */
+    boolean hasServiceId();
+    /**
+     * <code>.context.ServiceId service_id = 2;</code>
+     * @return The serviceId.
+     */
+    context.ContextOuterClass.ServiceId getServiceId();
+    /**
+     * <code>.context.ServiceId service_id = 2;</code>
+     */
+    context.ContextOuterClass.ServiceIdOrBuilder getServiceIdOrBuilder();
+
+    /**
+     * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
+     */
+    java.util.List<context.ContextOuterClass.EndPointId> 
+        getPathHopsEndpointIdsList();
+    /**
+     * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
+     */
+    context.ContextOuterClass.EndPointId getPathHopsEndpointIds(int index);
+    /**
+     * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
+     */
+    int getPathHopsEndpointIdsCount();
+    /**
+     * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
+     */
+    java.util.List<? extends context.ContextOuterClass.EndPointIdOrBuilder> 
+        getPathHopsEndpointIdsOrBuilderList();
+    /**
+     * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
+     */
+    context.ContextOuterClass.EndPointIdOrBuilder getPathHopsEndpointIdsOrBuilder(
+        int index);
+
+    /**
+     * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
+     */
+    java.util.List<context.ContextOuterClass.ServiceId> 
+        getSubServiceIdsList();
+    /**
+     * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
+     */
+    context.ContextOuterClass.ServiceId getSubServiceIds(int index);
+    /**
+     * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
+     */
+    int getSubServiceIdsCount();
+    /**
+     * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
+     */
+    java.util.List<? extends context.ContextOuterClass.ServiceIdOrBuilder> 
+        getSubServiceIdsOrBuilderList();
+    /**
+     * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
+     */
+    context.ContextOuterClass.ServiceIdOrBuilder getSubServiceIdsOrBuilder(
+        int index);
+  }
+  /**
+   * Protobuf type {@code context.Connection}
+   */
+  public static final class Connection extends
+      com.google.protobuf.GeneratedMessageV3 implements
+      // @@protoc_insertion_point(message_implements:context.Connection)
+      ConnectionOrBuilder {
+  private static final long serialVersionUID = 0L;
+    // Use Connection.newBuilder() to construct.
+    private Connection(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
+      super(builder);
+    }
+    private Connection() {
+      pathHopsEndpointIds_ = java.util.Collections.emptyList();
+      subServiceIds_ = java.util.Collections.emptyList();
+    }
+
+    @java.lang.Override
+    @SuppressWarnings({"unused"})
+    protected java.lang.Object newInstance(
+        UnusedPrivateParameter unused) {
+      return new Connection();
+    }
+
+    @java.lang.Override
+    public final com.google.protobuf.UnknownFieldSet
+    getUnknownFields() {
+      return this.unknownFields;
+    }
+    private Connection(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      this();
+      if (extensionRegistry == null) {
+        throw new java.lang.NullPointerException();
+      }
+      int mutable_bitField0_ = 0;
+      com.google.protobuf.UnknownFieldSet.Builder unknownFields =
+          com.google.protobuf.UnknownFieldSet.newBuilder();
+      try {
+        boolean done = false;
+        while (!done) {
+          int tag = input.readTag();
+          switch (tag) {
+            case 0:
+              done = true;
+              break;
+            case 10: {
+              context.ContextOuterClass.ConnectionId.Builder subBuilder = null;
+              if (connectionId_ != null) {
+                subBuilder = connectionId_.toBuilder();
+              }
+              connectionId_ = input.readMessage(context.ContextOuterClass.ConnectionId.parser(), extensionRegistry);
+              if (subBuilder != null) {
+                subBuilder.mergeFrom(connectionId_);
+                connectionId_ = subBuilder.buildPartial();
+              }
+
+              break;
+            }
+            case 18: {
+              context.ContextOuterClass.ServiceId.Builder subBuilder = null;
+              if (serviceId_ != null) {
+                subBuilder = serviceId_.toBuilder();
+              }
+              serviceId_ = input.readMessage(context.ContextOuterClass.ServiceId.parser(), extensionRegistry);
+              if (subBuilder != null) {
+                subBuilder.mergeFrom(serviceId_);
+                serviceId_ = subBuilder.buildPartial();
+              }
+
+              break;
+            }
+            case 26: {
+              if (!((mutable_bitField0_ & 0x00000001) != 0)) {
+                pathHopsEndpointIds_ = new java.util.ArrayList<context.ContextOuterClass.EndPointId>();
+                mutable_bitField0_ |= 0x00000001;
+              }
+              pathHopsEndpointIds_.add(
+                  input.readMessage(context.ContextOuterClass.EndPointId.parser(), extensionRegistry));
+              break;
+            }
+            case 34: {
+              if (!((mutable_bitField0_ & 0x00000002) != 0)) {
+                subServiceIds_ = new java.util.ArrayList<context.ContextOuterClass.ServiceId>();
+                mutable_bitField0_ |= 0x00000002;
+              }
+              subServiceIds_.add(
+                  input.readMessage(context.ContextOuterClass.ServiceId.parser(), extensionRegistry));
+              break;
+            }
+            default: {
+              if (!parseUnknownField(
+                  input, unknownFields, extensionRegistry, tag)) {
+                done = true;
+              }
+              break;
+            }
+          }
+        }
+      } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+        throw e.setUnfinishedMessage(this);
+      } catch (java.io.IOException e) {
+        throw new com.google.protobuf.InvalidProtocolBufferException(
+            e).setUnfinishedMessage(this);
+      } finally {
+        if (((mutable_bitField0_ & 0x00000001) != 0)) {
+          pathHopsEndpointIds_ = java.util.Collections.unmodifiableList(pathHopsEndpointIds_);
+        }
+        if (((mutable_bitField0_ & 0x00000002) != 0)) {
+          subServiceIds_ = java.util.Collections.unmodifiableList(subServiceIds_);
+        }
+        this.unknownFields = unknownFields.build();
+        makeExtensionsImmutable();
+      }
+    }
+    public static final com.google.protobuf.Descriptors.Descriptor
+        getDescriptor() {
+      return context.ContextOuterClass.internal_static_context_Connection_descriptor;
+    }
+
+    @java.lang.Override
+    protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+        internalGetFieldAccessorTable() {
+      return context.ContextOuterClass.internal_static_context_Connection_fieldAccessorTable
+          .ensureFieldAccessorsInitialized(
+              context.ContextOuterClass.Connection.class, context.ContextOuterClass.Connection.Builder.class);
+    }
+
+    public static final int CONNECTION_ID_FIELD_NUMBER = 1;
+    private context.ContextOuterClass.ConnectionId connectionId_;
+    /**
+     * <code>.context.ConnectionId connection_id = 1;</code>
+     * @return Whether the connectionId field is set.
+     */
+    @java.lang.Override
+    public boolean hasConnectionId() {
+      return connectionId_ != null;
+    }
+    /**
+     * <code>.context.ConnectionId connection_id = 1;</code>
+     * @return The connectionId.
+     */
+    @java.lang.Override
+    public context.ContextOuterClass.ConnectionId getConnectionId() {
+      return connectionId_ == null ? context.ContextOuterClass.ConnectionId.getDefaultInstance() : connectionId_;
+    }
+    /**
+     * <code>.context.ConnectionId connection_id = 1;</code>
+     */
+    @java.lang.Override
+    public context.ContextOuterClass.ConnectionIdOrBuilder getConnectionIdOrBuilder() {
+      return getConnectionId();
+    }
+
+    public static final int SERVICE_ID_FIELD_NUMBER = 2;
+    private context.ContextOuterClass.ServiceId serviceId_;
+    /**
+     * <code>.context.ServiceId service_id = 2;</code>
+     * @return Whether the serviceId field is set.
+     */
+    @java.lang.Override
+    public boolean hasServiceId() {
+      return serviceId_ != null;
+    }
+    /**
+     * <code>.context.ServiceId service_id = 2;</code>
+     * @return The serviceId.
+     */
+    @java.lang.Override
+    public context.ContextOuterClass.ServiceId getServiceId() {
+      return serviceId_ == null ? context.ContextOuterClass.ServiceId.getDefaultInstance() : serviceId_;
+    }
+    /**
+     * <code>.context.ServiceId service_id = 2;</code>
+     */
+    @java.lang.Override
+    public context.ContextOuterClass.ServiceIdOrBuilder getServiceIdOrBuilder() {
+      return getServiceId();
+    }
+
+    public static final int PATH_HOPS_ENDPOINT_IDS_FIELD_NUMBER = 3;
+    private java.util.List<context.ContextOuterClass.EndPointId> pathHopsEndpointIds_;
+    /**
+     * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
+     */
+    @java.lang.Override
+    public java.util.List<context.ContextOuterClass.EndPointId> getPathHopsEndpointIdsList() {
+      return pathHopsEndpointIds_;
+    }
+    /**
+     * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
+     */
+    @java.lang.Override
+    public java.util.List<? extends context.ContextOuterClass.EndPointIdOrBuilder> 
+        getPathHopsEndpointIdsOrBuilderList() {
+      return pathHopsEndpointIds_;
+    }
+    /**
+     * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
+     */
+    @java.lang.Override
+    public int getPathHopsEndpointIdsCount() {
+      return pathHopsEndpointIds_.size();
+    }
+    /**
+     * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
+     */
+    @java.lang.Override
+    public context.ContextOuterClass.EndPointId getPathHopsEndpointIds(int index) {
+      return pathHopsEndpointIds_.get(index);
+    }
+    /**
+     * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
+     */
+    @java.lang.Override
+    public context.ContextOuterClass.EndPointIdOrBuilder getPathHopsEndpointIdsOrBuilder(
+        int index) {
+      return pathHopsEndpointIds_.get(index);
+    }
+
+    public static final int SUB_SERVICE_IDS_FIELD_NUMBER = 4;
+    private java.util.List<context.ContextOuterClass.ServiceId> subServiceIds_;
+    /**
+     * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
+     */
+    @java.lang.Override
+    public java.util.List<context.ContextOuterClass.ServiceId> getSubServiceIdsList() {
+      return subServiceIds_;
+    }
+    /**
+     * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
+     */
+    @java.lang.Override
+    public java.util.List<? extends context.ContextOuterClass.ServiceIdOrBuilder> 
+        getSubServiceIdsOrBuilderList() {
+      return subServiceIds_;
+    }
+    /**
+     * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
+     */
+    @java.lang.Override
+    public int getSubServiceIdsCount() {
+      return subServiceIds_.size();
+    }
+    /**
+     * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
+     */
+    @java.lang.Override
+    public context.ContextOuterClass.ServiceId getSubServiceIds(int index) {
+      return subServiceIds_.get(index);
+    }
+    /**
+     * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
+     */
+    @java.lang.Override
+    public context.ContextOuterClass.ServiceIdOrBuilder getSubServiceIdsOrBuilder(
+        int index) {
+      return subServiceIds_.get(index);
+    }
+
+    private byte memoizedIsInitialized = -1;
+    @java.lang.Override
+    public final boolean isInitialized() {
+      byte isInitialized = memoizedIsInitialized;
+      if (isInitialized == 1) return true;
+      if (isInitialized == 0) return false;
+
+      memoizedIsInitialized = 1;
+      return true;
+    }
+
+    @java.lang.Override
+    public void writeTo(com.google.protobuf.CodedOutputStream output)
+                        throws java.io.IOException {
+      if (connectionId_ != null) {
+        output.writeMessage(1, getConnectionId());
+      }
+      if (serviceId_ != null) {
+        output.writeMessage(2, getServiceId());
+      }
+      for (int i = 0; i < pathHopsEndpointIds_.size(); i++) {
+        output.writeMessage(3, pathHopsEndpointIds_.get(i));
+      }
+      for (int i = 0; i < subServiceIds_.size(); i++) {
+        output.writeMessage(4, subServiceIds_.get(i));
+      }
+      unknownFields.writeTo(output);
+    }
+
+    @java.lang.Override
+    public int getSerializedSize() {
+      int size = memoizedSize;
+      if (size != -1) return size;
+
+      size = 0;
+      if (connectionId_ != null) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeMessageSize(1, getConnectionId());
+      }
+      if (serviceId_ != null) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeMessageSize(2, getServiceId());
+      }
+      for (int i = 0; i < pathHopsEndpointIds_.size(); i++) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeMessageSize(3, pathHopsEndpointIds_.get(i));
+      }
+      for (int i = 0; i < subServiceIds_.size(); i++) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeMessageSize(4, subServiceIds_.get(i));
+      }
+      size += unknownFields.getSerializedSize();
+      memoizedSize = size;
+      return size;
+    }
+
+    @java.lang.Override
+    public boolean equals(final java.lang.Object obj) {
+      if (obj == this) {
+       return true;
+      }
+      if (!(obj instanceof context.ContextOuterClass.Connection)) {
+        return super.equals(obj);
+      }
+      context.ContextOuterClass.Connection other = (context.ContextOuterClass.Connection) obj;
+
+      if (hasConnectionId() != other.hasConnectionId()) return false;
+      if (hasConnectionId()) {
+        if (!getConnectionId()
+            .equals(other.getConnectionId())) return false;
+      }
+      if (hasServiceId() != other.hasServiceId()) return false;
+      if (hasServiceId()) {
+        if (!getServiceId()
+            .equals(other.getServiceId())) return false;
+      }
+      if (!getPathHopsEndpointIdsList()
+          .equals(other.getPathHopsEndpointIdsList())) return false;
+      if (!getSubServiceIdsList()
+          .equals(other.getSubServiceIdsList())) return false;
+      if (!unknownFields.equals(other.unknownFields)) return false;
+      return true;
+    }
+
+    @java.lang.Override
+    public int hashCode() {
+      if (memoizedHashCode != 0) {
+        return memoizedHashCode;
+      }
+      int hash = 41;
+      hash = (19 * hash) + getDescriptor().hashCode();
+      if (hasConnectionId()) {
+        hash = (37 * hash) + CONNECTION_ID_FIELD_NUMBER;
+        hash = (53 * hash) + getConnectionId().hashCode();
+      }
+      if (hasServiceId()) {
+        hash = (37 * hash) + SERVICE_ID_FIELD_NUMBER;
+        hash = (53 * hash) + getServiceId().hashCode();
+      }
+      if (getPathHopsEndpointIdsCount() > 0) {
+        hash = (37 * hash) + PATH_HOPS_ENDPOINT_IDS_FIELD_NUMBER;
+        hash = (53 * hash) + getPathHopsEndpointIdsList().hashCode();
+      }
+      if (getSubServiceIdsCount() > 0) {
+        hash = (37 * hash) + SUB_SERVICE_IDS_FIELD_NUMBER;
+        hash = (53 * hash) + getSubServiceIdsList().hashCode();
+      }
+      hash = (29 * hash) + unknownFields.hashCode();
+      memoizedHashCode = hash;
+      return hash;
+    }
+
+    public static context.ContextOuterClass.Connection parseFrom(
+        java.nio.ByteBuffer data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data);
+    }
+    public static context.ContextOuterClass.Connection parseFrom(
+        java.nio.ByteBuffer data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data, extensionRegistry);
+    }
+    public static context.ContextOuterClass.Connection parseFrom(
+        com.google.protobuf.ByteString data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data);
+    }
+    public static context.ContextOuterClass.Connection parseFrom(
+        com.google.protobuf.ByteString data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data, extensionRegistry);
+    }
+    public static context.ContextOuterClass.Connection parseFrom(byte[] data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data);
+    }
+    public static context.ContextOuterClass.Connection parseFrom(
+        byte[] data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data, extensionRegistry);
+    }
+    public static context.ContextOuterClass.Connection parseFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      return com.google.protobuf.GeneratedMessageV3
+          .parseWithIOException(PARSER, input);
+    }
+    public static context.ContextOuterClass.Connection parseFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return com.google.protobuf.GeneratedMessageV3
+          .parseWithIOException(PARSER, input, extensionRegistry);
+    }
+    public static context.ContextOuterClass.Connection parseDelimitedFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      return com.google.protobuf.GeneratedMessageV3
+          .parseDelimitedWithIOException(PARSER, input);
+    }
+    public static context.ContextOuterClass.Connection parseDelimitedFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return com.google.protobuf.GeneratedMessageV3
+          .parseDelimitedWithIOException(PARSER, input, extensionRegistry);
+    }
+    public static context.ContextOuterClass.Connection parseFrom(
+        com.google.protobuf.CodedInputStream input)
+        throws java.io.IOException {
+      return com.google.protobuf.GeneratedMessageV3
+          .parseWithIOException(PARSER, input);
+    }
+    public static context.ContextOuterClass.Connection parseFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return com.google.protobuf.GeneratedMessageV3
+          .parseWithIOException(PARSER, input, extensionRegistry);
+    }
+
+    @java.lang.Override
+    public Builder newBuilderForType() { return newBuilder(); }
+    public static Builder newBuilder() {
+      return DEFAULT_INSTANCE.toBuilder();
+    }
+    public static Builder newBuilder(context.ContextOuterClass.Connection prototype) {
+      return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
+    }
+    @java.lang.Override
+    public Builder toBuilder() {
+      return this == DEFAULT_INSTANCE
+          ? new Builder() : new Builder().mergeFrom(this);
+    }
+
+    @java.lang.Override
+    protected Builder newBuilderForType(
+        com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+      Builder builder = new Builder(parent);
+      return builder;
+    }
+    /**
+     * Protobuf type {@code context.Connection}
+     */
+    public static final class Builder extends
+        com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
+        // @@protoc_insertion_point(builder_implements:context.Connection)
+        context.ContextOuterClass.ConnectionOrBuilder {
+      public static final com.google.protobuf.Descriptors.Descriptor
+          getDescriptor() {
+        return context.ContextOuterClass.internal_static_context_Connection_descriptor;
+      }
+
+      @java.lang.Override
+      protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+          internalGetFieldAccessorTable() {
+        return context.ContextOuterClass.internal_static_context_Connection_fieldAccessorTable
+            .ensureFieldAccessorsInitialized(
+                context.ContextOuterClass.Connection.class, context.ContextOuterClass.Connection.Builder.class);
+      }
+
+      // Construct using context.ContextOuterClass.Connection.newBuilder()
+      private Builder() {
+        maybeForceBuilderInitialization();
+      }
+
+      private Builder(
+          com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+        super(parent);
+        maybeForceBuilderInitialization();
+      }
+      private void maybeForceBuilderInitialization() {
+        if (com.google.protobuf.GeneratedMessageV3
+                .alwaysUseFieldBuilders) {
+          getPathHopsEndpointIdsFieldBuilder();
+          getSubServiceIdsFieldBuilder();
+        }
+      }
+      @java.lang.Override
+      public Builder clear() {
+        super.clear();
+        if (connectionIdBuilder_ == null) {
+          connectionId_ = null;
+        } else {
+          connectionId_ = null;
+          connectionIdBuilder_ = null;
+        }
+        if (serviceIdBuilder_ == null) {
+          serviceId_ = null;
+        } else {
+          serviceId_ = null;
+          serviceIdBuilder_ = null;
+        }
+        if (pathHopsEndpointIdsBuilder_ == null) {
+          pathHopsEndpointIds_ = java.util.Collections.emptyList();
+          bitField0_ = (bitField0_ & ~0x00000001);
+        } else {
+          pathHopsEndpointIdsBuilder_.clear();
+        }
+        if (subServiceIdsBuilder_ == null) {
+          subServiceIds_ = java.util.Collections.emptyList();
+          bitField0_ = (bitField0_ & ~0x00000002);
+        } else {
+          subServiceIdsBuilder_.clear();
+        }
+        return this;
+      }
+
+      @java.lang.Override
+      public com.google.protobuf.Descriptors.Descriptor
+          getDescriptorForType() {
+        return context.ContextOuterClass.internal_static_context_Connection_descriptor;
+      }
+
+      @java.lang.Override
+      public context.ContextOuterClass.Connection getDefaultInstanceForType() {
+        return context.ContextOuterClass.Connection.getDefaultInstance();
+      }
+
+      @java.lang.Override
+      public context.ContextOuterClass.Connection build() {
+        context.ContextOuterClass.Connection result = buildPartial();
+        if (!result.isInitialized()) {
+          throw newUninitializedMessageException(result);
+        }
+        return result;
+      }
+
+      @java.lang.Override
+      public context.ContextOuterClass.Connection buildPartial() {
+        context.ContextOuterClass.Connection result = new context.ContextOuterClass.Connection(this);
+        int from_bitField0_ = bitField0_;
+        if (connectionIdBuilder_ == null) {
+          result.connectionId_ = connectionId_;
+        } else {
+          result.connectionId_ = connectionIdBuilder_.build();
+        }
+        if (serviceIdBuilder_ == null) {
+          result.serviceId_ = serviceId_;
+        } else {
+          result.serviceId_ = serviceIdBuilder_.build();
+        }
+        if (pathHopsEndpointIdsBuilder_ == null) {
+          if (((bitField0_ & 0x00000001) != 0)) {
+            pathHopsEndpointIds_ = java.util.Collections.unmodifiableList(pathHopsEndpointIds_);
+            bitField0_ = (bitField0_ & ~0x00000001);
+          }
+          result.pathHopsEndpointIds_ = pathHopsEndpointIds_;
+        } else {
+          result.pathHopsEndpointIds_ = pathHopsEndpointIdsBuilder_.build();
+        }
+        if (subServiceIdsBuilder_ == null) {
+          if (((bitField0_ & 0x00000002) != 0)) {
+            subServiceIds_ = java.util.Collections.unmodifiableList(subServiceIds_);
+            bitField0_ = (bitField0_ & ~0x00000002);
+          }
+          result.subServiceIds_ = subServiceIds_;
+        } else {
+          result.subServiceIds_ = subServiceIdsBuilder_.build();
+        }
+        onBuilt();
+        return result;
+      }
+
+      @java.lang.Override
+      public Builder clone() {
+        return super.clone();
+      }
+      @java.lang.Override
+      public Builder setField(
+          com.google.protobuf.Descriptors.FieldDescriptor field,
+          java.lang.Object value) {
+        return super.setField(field, value);
+      }
+      @java.lang.Override
+      public Builder clearField(
+          com.google.protobuf.Descriptors.FieldDescriptor field) {
+        return super.clearField(field);
+      }
+      @java.lang.Override
+      public Builder clearOneof(
+          com.google.protobuf.Descriptors.OneofDescriptor oneof) {
+        return super.clearOneof(oneof);
+      }
+      @java.lang.Override
+      public Builder setRepeatedField(
+          com.google.protobuf.Descriptors.FieldDescriptor field,
+          int index, java.lang.Object value) {
+        return super.setRepeatedField(field, index, value);
+      }
+      @java.lang.Override
+      public Builder addRepeatedField(
+          com.google.protobuf.Descriptors.FieldDescriptor field,
+          java.lang.Object value) {
+        return super.addRepeatedField(field, value);
+      }
+      @java.lang.Override
+      public Builder mergeFrom(com.google.protobuf.Message other) {
+        if (other instanceof context.ContextOuterClass.Connection) {
+          return mergeFrom((context.ContextOuterClass.Connection)other);
+        } else {
+          super.mergeFrom(other);
+          return this;
+        }
+      }
+
+      public Builder mergeFrom(context.ContextOuterClass.Connection other) {
+        if (other == context.ContextOuterClass.Connection.getDefaultInstance()) return this;
+        if (other.hasConnectionId()) {
+          mergeConnectionId(other.getConnectionId());
+        }
+        if (other.hasServiceId()) {
+          mergeServiceId(other.getServiceId());
+        }
+        if (pathHopsEndpointIdsBuilder_ == null) {
+          if (!other.pathHopsEndpointIds_.isEmpty()) {
+            if (pathHopsEndpointIds_.isEmpty()) {
+              pathHopsEndpointIds_ = other.pathHopsEndpointIds_;
+              bitField0_ = (bitField0_ & ~0x00000001);
+            } else {
+              ensurePathHopsEndpointIdsIsMutable();
+              pathHopsEndpointIds_.addAll(other.pathHopsEndpointIds_);
+            }
+            onChanged();
+          }
+        } else {
+          if (!other.pathHopsEndpointIds_.isEmpty()) {
+            if (pathHopsEndpointIdsBuilder_.isEmpty()) {
+              pathHopsEndpointIdsBuilder_.dispose();
+              pathHopsEndpointIdsBuilder_ = null;
+              pathHopsEndpointIds_ = other.pathHopsEndpointIds_;
+              bitField0_ = (bitField0_ & ~0x00000001);
+              pathHopsEndpointIdsBuilder_ = 
+                com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders ?
+                   getPathHopsEndpointIdsFieldBuilder() : null;
+            } else {
+              pathHopsEndpointIdsBuilder_.addAllMessages(other.pathHopsEndpointIds_);
+            }
+          }
+        }
+        if (subServiceIdsBuilder_ == null) {
+          if (!other.subServiceIds_.isEmpty()) {
+            if (subServiceIds_.isEmpty()) {
+              subServiceIds_ = other.subServiceIds_;
+              bitField0_ = (bitField0_ & ~0x00000002);
+            } else {
+              ensureSubServiceIdsIsMutable();
+              subServiceIds_.addAll(other.subServiceIds_);
+            }
+            onChanged();
+          }
+        } else {
+          if (!other.subServiceIds_.isEmpty()) {
+            if (subServiceIdsBuilder_.isEmpty()) {
+              subServiceIdsBuilder_.dispose();
+              subServiceIdsBuilder_ = null;
+              subServiceIds_ = other.subServiceIds_;
+              bitField0_ = (bitField0_ & ~0x00000002);
+              subServiceIdsBuilder_ = 
+                com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders ?
+                   getSubServiceIdsFieldBuilder() : null;
+            } else {
+              subServiceIdsBuilder_.addAllMessages(other.subServiceIds_);
+            }
+          }
+        }
+        this.mergeUnknownFields(other.unknownFields);
+        onChanged();
+        return this;
+      }
+
+      @java.lang.Override
+      public final boolean isInitialized() {
+        return true;
+      }
+
+      @java.lang.Override
+      public Builder mergeFrom(
+          com.google.protobuf.CodedInputStream input,
+          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+          throws java.io.IOException {
+        context.ContextOuterClass.Connection parsedMessage = null;
+        try {
+          parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
+        } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+          parsedMessage = (context.ContextOuterClass.Connection) e.getUnfinishedMessage();
+          throw e.unwrapIOException();
+        } finally {
+          if (parsedMessage != null) {
+            mergeFrom(parsedMessage);
+          }
+        }
+        return this;
+      }
+      private int bitField0_;
+
+      private context.ContextOuterClass.ConnectionId connectionId_;
+      private com.google.protobuf.SingleFieldBuilderV3<
+          context.ContextOuterClass.ConnectionId, context.ContextOuterClass.ConnectionId.Builder, context.ContextOuterClass.ConnectionIdOrBuilder> connectionIdBuilder_;
+      /**
+       * <code>.context.ConnectionId connection_id = 1;</code>
+       * @return Whether the connectionId field is set.
+       */
+      public boolean hasConnectionId() {
+        return connectionIdBuilder_ != null || connectionId_ != null;
+      }
+      /**
+       * <code>.context.ConnectionId connection_id = 1;</code>
+       * @return The connectionId.
+       */
+      public context.ContextOuterClass.ConnectionId getConnectionId() {
+        if (connectionIdBuilder_ == null) {
+          return connectionId_ == null ? context.ContextOuterClass.ConnectionId.getDefaultInstance() : connectionId_;
+        } else {
+          return connectionIdBuilder_.getMessage();
+        }
+      }
+      /**
+       * <code>.context.ConnectionId connection_id = 1;</code>
+       */
+      public Builder setConnectionId(context.ContextOuterClass.ConnectionId value) {
+        if (connectionIdBuilder_ == null) {
+          if (value == null) {
+            throw new NullPointerException();
+          }
+          connectionId_ = value;
+          onChanged();
+        } else {
+          connectionIdBuilder_.setMessage(value);
+        }
+
+        return this;
+      }
+      /**
+       * <code>.context.ConnectionId connection_id = 1;</code>
+       */
+      public Builder setConnectionId(
+          context.ContextOuterClass.ConnectionId.Builder builderForValue) {
+        if (connectionIdBuilder_ == null) {
+          connectionId_ = builderForValue.build();
+          onChanged();
+        } else {
+          connectionIdBuilder_.setMessage(builderForValue.build());
+        }
+
+        return this;
+      }
+      /**
+       * <code>.context.ConnectionId connection_id = 1;</code>
+       */
+      public Builder mergeConnectionId(context.ContextOuterClass.ConnectionId value) {
+        if (connectionIdBuilder_ == null) {
+          if (connectionId_ != null) {
+            connectionId_ =
+              context.ContextOuterClass.ConnectionId.newBuilder(connectionId_).mergeFrom(value).buildPartial();
+          } else {
+            connectionId_ = value;
+          }
+          onChanged();
+        } else {
+          connectionIdBuilder_.mergeFrom(value);
+        }
+
+        return this;
+      }
+      /**
+       * <code>.context.ConnectionId connection_id = 1;</code>
+       */
+      public Builder clearConnectionId() {
+        if (connectionIdBuilder_ == null) {
+          connectionId_ = null;
+          onChanged();
+        } else {
+          connectionId_ = null;
+          connectionIdBuilder_ = null;
+        }
+
+        return this;
+      }
+      /**
+       * <code>.context.ConnectionId connection_id = 1;</code>
+       */
+      public context.ContextOuterClass.ConnectionId.Builder getConnectionIdBuilder() {
+        
+        onChanged();
+        return getConnectionIdFieldBuilder().getBuilder();
+      }
+      /**
+       * <code>.context.ConnectionId connection_id = 1;</code>
+       */
+      public context.ContextOuterClass.ConnectionIdOrBuilder getConnectionIdOrBuilder() {
+        if (connectionIdBuilder_ != null) {
+          return connectionIdBuilder_.getMessageOrBuilder();
+        } else {
+          return connectionId_ == null ?
+              context.ContextOuterClass.ConnectionId.getDefaultInstance() : connectionId_;
+        }
+      }
+      /**
+       * <code>.context.ConnectionId connection_id = 1;</code>
+       */
+      private com.google.protobuf.SingleFieldBuilderV3<
+          context.ContextOuterClass.ConnectionId, context.ContextOuterClass.ConnectionId.Builder, context.ContextOuterClass.ConnectionIdOrBuilder> 
+          getConnectionIdFieldBuilder() {
+        if (connectionIdBuilder_ == null) {
+          connectionIdBuilder_ = new com.google.protobuf.SingleFieldBuilderV3<
+              context.ContextOuterClass.ConnectionId, context.ContextOuterClass.ConnectionId.Builder, context.ContextOuterClass.ConnectionIdOrBuilder>(
+                  getConnectionId(),
+                  getParentForChildren(),
+                  isClean());
+          connectionId_ = null;
+        }
+        return connectionIdBuilder_;
+      }
+
+      private context.ContextOuterClass.ServiceId serviceId_;
+      private com.google.protobuf.SingleFieldBuilderV3<
+          context.ContextOuterClass.ServiceId, context.ContextOuterClass.ServiceId.Builder, context.ContextOuterClass.ServiceIdOrBuilder> serviceIdBuilder_;
+      /**
+       * <code>.context.ServiceId service_id = 2;</code>
+       * @return Whether the serviceId field is set.
+       */
+      public boolean hasServiceId() {
+        return serviceIdBuilder_ != null || serviceId_ != null;
+      }
+      /**
+       * <code>.context.ServiceId service_id = 2;</code>
+       * @return The serviceId.
+       */
+      public context.ContextOuterClass.ServiceId getServiceId() {
+        if (serviceIdBuilder_ == null) {
+          return serviceId_ == null ? context.ContextOuterClass.ServiceId.getDefaultInstance() : serviceId_;
+        } else {
+          return serviceIdBuilder_.getMessage();
+        }
+      }
+      /**
+       * <code>.context.ServiceId service_id = 2;</code>
+       */
+      public Builder setServiceId(context.ContextOuterClass.ServiceId value) {
+        if (serviceIdBuilder_ == null) {
+          if (value == null) {
+            throw new NullPointerException();
+          }
+          serviceId_ = value;
+          onChanged();
+        } else {
+          serviceIdBuilder_.setMessage(value);
+        }
+
+        return this;
+      }
+      /**
+       * <code>.context.ServiceId service_id = 2;</code>
+       */
+      public Builder setServiceId(
+          context.ContextOuterClass.ServiceId.Builder builderForValue) {
+        if (serviceIdBuilder_ == null) {
+          serviceId_ = builderForValue.build();
+          onChanged();
+        } else {
+          serviceIdBuilder_.setMessage(builderForValue.build());
+        }
+
+        return this;
+      }
+      /**
+       * <code>.context.ServiceId service_id = 2;</code>
+       */
+      public Builder mergeServiceId(context.ContextOuterClass.ServiceId value) {
+        if (serviceIdBuilder_ == null) {
+          if (serviceId_ != null) {
+            serviceId_ =
+              context.ContextOuterClass.ServiceId.newBuilder(serviceId_).mergeFrom(value).buildPartial();
+          } else {
+            serviceId_ = value;
+          }
+          onChanged();
+        } else {
+          serviceIdBuilder_.mergeFrom(value);
+        }
+
+        return this;
+      }
+      /**
+       * <code>.context.ServiceId service_id = 2;</code>
+       */
+      public Builder clearServiceId() {
+        if (serviceIdBuilder_ == null) {
+          serviceId_ = null;
+          onChanged();
+        } else {
+          serviceId_ = null;
+          serviceIdBuilder_ = null;
+        }
+
+        return this;
+      }
+      /**
+       * <code>.context.ServiceId service_id = 2;</code>
+       */
+      public context.ContextOuterClass.ServiceId.Builder getServiceIdBuilder() {
+        
+        onChanged();
+        return getServiceIdFieldBuilder().getBuilder();
+      }
+      /**
+       * <code>.context.ServiceId service_id = 2;</code>
+       */
+      public context.ContextOuterClass.ServiceIdOrBuilder getServiceIdOrBuilder() {
+        if (serviceIdBuilder_ != null) {
+          return serviceIdBuilder_.getMessageOrBuilder();
+        } else {
+          return serviceId_ == null ?
+              context.ContextOuterClass.ServiceId.getDefaultInstance() : serviceId_;
+        }
+      }
+      /**
+       * <code>.context.ServiceId service_id = 2;</code>
+       */
+      private com.google.protobuf.SingleFieldBuilderV3<
+          context.ContextOuterClass.ServiceId, context.ContextOuterClass.ServiceId.Builder, context.ContextOuterClass.ServiceIdOrBuilder> 
+          getServiceIdFieldBuilder() {
+        if (serviceIdBuilder_ == null) {
+          serviceIdBuilder_ = new com.google.protobuf.SingleFieldBuilderV3<
+              context.ContextOuterClass.ServiceId, context.ContextOuterClass.ServiceId.Builder, context.ContextOuterClass.ServiceIdOrBuilder>(
+                  getServiceId(),
+                  getParentForChildren(),
+                  isClean());
+          serviceId_ = null;
+        }
+        return serviceIdBuilder_;
+      }
+
+      private java.util.List<context.ContextOuterClass.EndPointId> pathHopsEndpointIds_ =
+        java.util.Collections.emptyList();
+      private void ensurePathHopsEndpointIdsIsMutable() {
+        if (!((bitField0_ & 0x00000001) != 0)) {
+          pathHopsEndpointIds_ = new java.util.ArrayList<context.ContextOuterClass.EndPointId>(pathHopsEndpointIds_);
+          bitField0_ |= 0x00000001;
+         }
+      }
+
+      private com.google.protobuf.RepeatedFieldBuilderV3<
+          context.ContextOuterClass.EndPointId, context.ContextOuterClass.EndPointId.Builder, context.ContextOuterClass.EndPointIdOrBuilder> pathHopsEndpointIdsBuilder_;
+
       /**
-       * <code>.context.DeviceId device_id = 2;</code>
+       * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
        */
-      public Builder setDeviceId(
-          context.ContextOuterClass.DeviceId.Builder builderForValue) {
-        if (deviceIdBuilder_ == null) {
-          deviceId_ = builderForValue.build();
+      public java.util.List<context.ContextOuterClass.EndPointId> getPathHopsEndpointIdsList() {
+        if (pathHopsEndpointIdsBuilder_ == null) {
+          return java.util.Collections.unmodifiableList(pathHopsEndpointIds_);
+        } else {
+          return pathHopsEndpointIdsBuilder_.getMessageList();
+        }
+      }
+      /**
+       * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
+       */
+      public int getPathHopsEndpointIdsCount() {
+        if (pathHopsEndpointIdsBuilder_ == null) {
+          return pathHopsEndpointIds_.size();
+        } else {
+          return pathHopsEndpointIdsBuilder_.getCount();
+        }
+      }
+      /**
+       * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
+       */
+      public context.ContextOuterClass.EndPointId getPathHopsEndpointIds(int index) {
+        if (pathHopsEndpointIdsBuilder_ == null) {
+          return pathHopsEndpointIds_.get(index);
+        } else {
+          return pathHopsEndpointIdsBuilder_.getMessage(index);
+        }
+      }
+      /**
+       * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
+       */
+      public Builder setPathHopsEndpointIds(
+          int index, context.ContextOuterClass.EndPointId value) {
+        if (pathHopsEndpointIdsBuilder_ == null) {
+          if (value == null) {
+            throw new NullPointerException();
+          }
+          ensurePathHopsEndpointIdsIsMutable();
+          pathHopsEndpointIds_.set(index, value);
           onChanged();
         } else {
-          deviceIdBuilder_.setMessage(builderForValue.build());
+          pathHopsEndpointIdsBuilder_.setMessage(index, value);
         }
-
         return this;
       }
       /**
-       * <code>.context.DeviceId device_id = 2;</code>
+       * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
        */
-      public Builder mergeDeviceId(context.ContextOuterClass.DeviceId value) {
-        if (deviceIdBuilder_ == null) {
-          if (deviceId_ != null) {
-            deviceId_ =
-              context.ContextOuterClass.DeviceId.newBuilder(deviceId_).mergeFrom(value).buildPartial();
-          } else {
-            deviceId_ = value;
+      public Builder setPathHopsEndpointIds(
+          int index, context.ContextOuterClass.EndPointId.Builder builderForValue) {
+        if (pathHopsEndpointIdsBuilder_ == null) {
+          ensurePathHopsEndpointIdsIsMutable();
+          pathHopsEndpointIds_.set(index, builderForValue.build());
+          onChanged();
+        } else {
+          pathHopsEndpointIdsBuilder_.setMessage(index, builderForValue.build());
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
+       */
+      public Builder addPathHopsEndpointIds(context.ContextOuterClass.EndPointId value) {
+        if (pathHopsEndpointIdsBuilder_ == null) {
+          if (value == null) {
+            throw new NullPointerException();
           }
+          ensurePathHopsEndpointIdsIsMutable();
+          pathHopsEndpointIds_.add(value);
           onChanged();
         } else {
-          deviceIdBuilder_.mergeFrom(value);
+          pathHopsEndpointIdsBuilder_.addMessage(value);
         }
-
         return this;
       }
       /**
-       * <code>.context.DeviceId device_id = 2;</code>
+       * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
        */
-      public Builder clearDeviceId() {
-        if (deviceIdBuilder_ == null) {
-          deviceId_ = null;
+      public Builder addPathHopsEndpointIds(
+          int index, context.ContextOuterClass.EndPointId value) {
+        if (pathHopsEndpointIdsBuilder_ == null) {
+          if (value == null) {
+            throw new NullPointerException();
+          }
+          ensurePathHopsEndpointIdsIsMutable();
+          pathHopsEndpointIds_.add(index, value);
           onChanged();
         } else {
-          deviceId_ = null;
-          deviceIdBuilder_ = null;
+          pathHopsEndpointIdsBuilder_.addMessage(index, value);
         }
-
         return this;
       }
       /**
-       * <code>.context.DeviceId device_id = 2;</code>
+       * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
        */
-      public context.ContextOuterClass.DeviceId.Builder getDeviceIdBuilder() {
-        
-        onChanged();
-        return getDeviceIdFieldBuilder().getBuilder();
+      public Builder addPathHopsEndpointIds(
+          context.ContextOuterClass.EndPointId.Builder builderForValue) {
+        if (pathHopsEndpointIdsBuilder_ == null) {
+          ensurePathHopsEndpointIdsIsMutable();
+          pathHopsEndpointIds_.add(builderForValue.build());
+          onChanged();
+        } else {
+          pathHopsEndpointIdsBuilder_.addMessage(builderForValue.build());
+        }
+        return this;
       }
       /**
-       * <code>.context.DeviceId device_id = 2;</code>
+       * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
        */
-      public context.ContextOuterClass.DeviceIdOrBuilder getDeviceIdOrBuilder() {
-        if (deviceIdBuilder_ != null) {
-          return deviceIdBuilder_.getMessageOrBuilder();
+      public Builder addPathHopsEndpointIds(
+          int index, context.ContextOuterClass.EndPointId.Builder builderForValue) {
+        if (pathHopsEndpointIdsBuilder_ == null) {
+          ensurePathHopsEndpointIdsIsMutable();
+          pathHopsEndpointIds_.add(index, builderForValue.build());
+          onChanged();
         } else {
-          return deviceId_ == null ?
-              context.ContextOuterClass.DeviceId.getDefaultInstance() : deviceId_;
+          pathHopsEndpointIdsBuilder_.addMessage(index, builderForValue.build());
         }
+        return this;
       }
       /**
-       * <code>.context.DeviceId device_id = 2;</code>
+       * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
        */
-      private com.google.protobuf.SingleFieldBuilderV3<
-          context.ContextOuterClass.DeviceId, context.ContextOuterClass.DeviceId.Builder, context.ContextOuterClass.DeviceIdOrBuilder> 
-          getDeviceIdFieldBuilder() {
-        if (deviceIdBuilder_ == null) {
-          deviceIdBuilder_ = new com.google.protobuf.SingleFieldBuilderV3<
-              context.ContextOuterClass.DeviceId, context.ContextOuterClass.DeviceId.Builder, context.ContextOuterClass.DeviceIdOrBuilder>(
-                  getDeviceId(),
+      public Builder addAllPathHopsEndpointIds(
+          java.lang.Iterable<? extends context.ContextOuterClass.EndPointId> values) {
+        if (pathHopsEndpointIdsBuilder_ == null) {
+          ensurePathHopsEndpointIdsIsMutable();
+          com.google.protobuf.AbstractMessageLite.Builder.addAll(
+              values, pathHopsEndpointIds_);
+          onChanged();
+        } else {
+          pathHopsEndpointIdsBuilder_.addAllMessages(values);
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
+       */
+      public Builder clearPathHopsEndpointIds() {
+        if (pathHopsEndpointIdsBuilder_ == null) {
+          pathHopsEndpointIds_ = java.util.Collections.emptyList();
+          bitField0_ = (bitField0_ & ~0x00000001);
+          onChanged();
+        } else {
+          pathHopsEndpointIdsBuilder_.clear();
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
+       */
+      public Builder removePathHopsEndpointIds(int index) {
+        if (pathHopsEndpointIdsBuilder_ == null) {
+          ensurePathHopsEndpointIdsIsMutable();
+          pathHopsEndpointIds_.remove(index);
+          onChanged();
+        } else {
+          pathHopsEndpointIdsBuilder_.remove(index);
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
+       */
+      public context.ContextOuterClass.EndPointId.Builder getPathHopsEndpointIdsBuilder(
+          int index) {
+        return getPathHopsEndpointIdsFieldBuilder().getBuilder(index);
+      }
+      /**
+       * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
+       */
+      public context.ContextOuterClass.EndPointIdOrBuilder getPathHopsEndpointIdsOrBuilder(
+          int index) {
+        if (pathHopsEndpointIdsBuilder_ == null) {
+          return pathHopsEndpointIds_.get(index);  } else {
+          return pathHopsEndpointIdsBuilder_.getMessageOrBuilder(index);
+        }
+      }
+      /**
+       * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
+       */
+      public java.util.List<? extends context.ContextOuterClass.EndPointIdOrBuilder> 
+           getPathHopsEndpointIdsOrBuilderList() {
+        if (pathHopsEndpointIdsBuilder_ != null) {
+          return pathHopsEndpointIdsBuilder_.getMessageOrBuilderList();
+        } else {
+          return java.util.Collections.unmodifiableList(pathHopsEndpointIds_);
+        }
+      }
+      /**
+       * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
+       */
+      public context.ContextOuterClass.EndPointId.Builder addPathHopsEndpointIdsBuilder() {
+        return getPathHopsEndpointIdsFieldBuilder().addBuilder(
+            context.ContextOuterClass.EndPointId.getDefaultInstance());
+      }
+      /**
+       * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
+       */
+      public context.ContextOuterClass.EndPointId.Builder addPathHopsEndpointIdsBuilder(
+          int index) {
+        return getPathHopsEndpointIdsFieldBuilder().addBuilder(
+            index, context.ContextOuterClass.EndPointId.getDefaultInstance());
+      }
+      /**
+       * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
+       */
+      public java.util.List<context.ContextOuterClass.EndPointId.Builder> 
+           getPathHopsEndpointIdsBuilderList() {
+        return getPathHopsEndpointIdsFieldBuilder().getBuilderList();
+      }
+      private com.google.protobuf.RepeatedFieldBuilderV3<
+          context.ContextOuterClass.EndPointId, context.ContextOuterClass.EndPointId.Builder, context.ContextOuterClass.EndPointIdOrBuilder> 
+          getPathHopsEndpointIdsFieldBuilder() {
+        if (pathHopsEndpointIdsBuilder_ == null) {
+          pathHopsEndpointIdsBuilder_ = new com.google.protobuf.RepeatedFieldBuilderV3<
+              context.ContextOuterClass.EndPointId, context.ContextOuterClass.EndPointId.Builder, context.ContextOuterClass.EndPointIdOrBuilder>(
+                  pathHopsEndpointIds_,
+                  ((bitField0_ & 0x00000001) != 0),
                   getParentForChildren(),
                   isClean());
-          deviceId_ = null;
+          pathHopsEndpointIds_ = null;
         }
-        return deviceIdBuilder_;
+        return pathHopsEndpointIdsBuilder_;
       }
 
-      private context.ContextOuterClass.Uuid endpointUuid_;
-      private com.google.protobuf.SingleFieldBuilderV3<
-          context.ContextOuterClass.Uuid, context.ContextOuterClass.Uuid.Builder, context.ContextOuterClass.UuidOrBuilder> endpointUuidBuilder_;
+      private java.util.List<context.ContextOuterClass.ServiceId> subServiceIds_ =
+        java.util.Collections.emptyList();
+      private void ensureSubServiceIdsIsMutable() {
+        if (!((bitField0_ & 0x00000002) != 0)) {
+          subServiceIds_ = new java.util.ArrayList<context.ContextOuterClass.ServiceId>(subServiceIds_);
+          bitField0_ |= 0x00000002;
+         }
+      }
+
+      private com.google.protobuf.RepeatedFieldBuilderV3<
+          context.ContextOuterClass.ServiceId, context.ContextOuterClass.ServiceId.Builder, context.ContextOuterClass.ServiceIdOrBuilder> subServiceIdsBuilder_;
+
       /**
-       * <code>.context.Uuid endpoint_uuid = 3;</code>
-       * @return Whether the endpointUuid field is set.
+       * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
        */
-      public boolean hasEndpointUuid() {
-        return endpointUuidBuilder_ != null || endpointUuid_ != null;
+      public java.util.List<context.ContextOuterClass.ServiceId> getSubServiceIdsList() {
+        if (subServiceIdsBuilder_ == null) {
+          return java.util.Collections.unmodifiableList(subServiceIds_);
+        } else {
+          return subServiceIdsBuilder_.getMessageList();
+        }
       }
       /**
-       * <code>.context.Uuid endpoint_uuid = 3;</code>
-       * @return The endpointUuid.
+       * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
        */
-      public context.ContextOuterClass.Uuid getEndpointUuid() {
-        if (endpointUuidBuilder_ == null) {
-          return endpointUuid_ == null ? context.ContextOuterClass.Uuid.getDefaultInstance() : endpointUuid_;
+      public int getSubServiceIdsCount() {
+        if (subServiceIdsBuilder_ == null) {
+          return subServiceIds_.size();
         } else {
-          return endpointUuidBuilder_.getMessage();
+          return subServiceIdsBuilder_.getCount();
         }
       }
       /**
-       * <code>.context.Uuid endpoint_uuid = 3;</code>
+       * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
        */
-      public Builder setEndpointUuid(context.ContextOuterClass.Uuid value) {
-        if (endpointUuidBuilder_ == null) {
+      public context.ContextOuterClass.ServiceId getSubServiceIds(int index) {
+        if (subServiceIdsBuilder_ == null) {
+          return subServiceIds_.get(index);
+        } else {
+          return subServiceIdsBuilder_.getMessage(index);
+        }
+      }
+      /**
+       * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
+       */
+      public Builder setSubServiceIds(
+          int index, context.ContextOuterClass.ServiceId value) {
+        if (subServiceIdsBuilder_ == null) {
           if (value == null) {
             throw new NullPointerException();
           }
-          endpointUuid_ = value;
+          ensureSubServiceIdsIsMutable();
+          subServiceIds_.set(index, value);
           onChanged();
         } else {
-          endpointUuidBuilder_.setMessage(value);
+          subServiceIdsBuilder_.setMessage(index, value);
         }
-
         return this;
       }
       /**
-       * <code>.context.Uuid endpoint_uuid = 3;</code>
+       * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
        */
-      public Builder setEndpointUuid(
-          context.ContextOuterClass.Uuid.Builder builderForValue) {
-        if (endpointUuidBuilder_ == null) {
-          endpointUuid_ = builderForValue.build();
+      public Builder setSubServiceIds(
+          int index, context.ContextOuterClass.ServiceId.Builder builderForValue) {
+        if (subServiceIdsBuilder_ == null) {
+          ensureSubServiceIdsIsMutable();
+          subServiceIds_.set(index, builderForValue.build());
           onChanged();
         } else {
-          endpointUuidBuilder_.setMessage(builderForValue.build());
+          subServiceIdsBuilder_.setMessage(index, builderForValue.build());
         }
-
         return this;
       }
       /**
-       * <code>.context.Uuid endpoint_uuid = 3;</code>
+       * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
        */
-      public Builder mergeEndpointUuid(context.ContextOuterClass.Uuid value) {
-        if (endpointUuidBuilder_ == null) {
-          if (endpointUuid_ != null) {
-            endpointUuid_ =
-              context.ContextOuterClass.Uuid.newBuilder(endpointUuid_).mergeFrom(value).buildPartial();
-          } else {
-            endpointUuid_ = value;
+      public Builder addSubServiceIds(context.ContextOuterClass.ServiceId value) {
+        if (subServiceIdsBuilder_ == null) {
+          if (value == null) {
+            throw new NullPointerException();
           }
+          ensureSubServiceIdsIsMutable();
+          subServiceIds_.add(value);
           onChanged();
         } else {
-          endpointUuidBuilder_.mergeFrom(value);
+          subServiceIdsBuilder_.addMessage(value);
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
+       */
+      public Builder addSubServiceIds(
+          int index, context.ContextOuterClass.ServiceId value) {
+        if (subServiceIdsBuilder_ == null) {
+          if (value == null) {
+            throw new NullPointerException();
+          }
+          ensureSubServiceIdsIsMutable();
+          subServiceIds_.add(index, value);
+          onChanged();
+        } else {
+          subServiceIdsBuilder_.addMessage(index, value);
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
+       */
+      public Builder addSubServiceIds(
+          context.ContextOuterClass.ServiceId.Builder builderForValue) {
+        if (subServiceIdsBuilder_ == null) {
+          ensureSubServiceIdsIsMutable();
+          subServiceIds_.add(builderForValue.build());
+          onChanged();
+        } else {
+          subServiceIdsBuilder_.addMessage(builderForValue.build());
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
+       */
+      public Builder addSubServiceIds(
+          int index, context.ContextOuterClass.ServiceId.Builder builderForValue) {
+        if (subServiceIdsBuilder_ == null) {
+          ensureSubServiceIdsIsMutable();
+          subServiceIds_.add(index, builderForValue.build());
+          onChanged();
+        } else {
+          subServiceIdsBuilder_.addMessage(index, builderForValue.build());
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
+       */
+      public Builder addAllSubServiceIds(
+          java.lang.Iterable<? extends context.ContextOuterClass.ServiceId> values) {
+        if (subServiceIdsBuilder_ == null) {
+          ensureSubServiceIdsIsMutable();
+          com.google.protobuf.AbstractMessageLite.Builder.addAll(
+              values, subServiceIds_);
+          onChanged();
+        } else {
+          subServiceIdsBuilder_.addAllMessages(values);
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
+       */
+      public Builder clearSubServiceIds() {
+        if (subServiceIdsBuilder_ == null) {
+          subServiceIds_ = java.util.Collections.emptyList();
+          bitField0_ = (bitField0_ & ~0x00000002);
+          onChanged();
+        } else {
+          subServiceIdsBuilder_.clear();
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
+       */
+      public Builder removeSubServiceIds(int index) {
+        if (subServiceIdsBuilder_ == null) {
+          ensureSubServiceIdsIsMutable();
+          subServiceIds_.remove(index);
+          onChanged();
+        } else {
+          subServiceIdsBuilder_.remove(index);
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
+       */
+      public context.ContextOuterClass.ServiceId.Builder getSubServiceIdsBuilder(
+          int index) {
+        return getSubServiceIdsFieldBuilder().getBuilder(index);
+      }
+      /**
+       * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
+       */
+      public context.ContextOuterClass.ServiceIdOrBuilder getSubServiceIdsOrBuilder(
+          int index) {
+        if (subServiceIdsBuilder_ == null) {
+          return subServiceIds_.get(index);  } else {
+          return subServiceIdsBuilder_.getMessageOrBuilder(index);
         }
-
-        return this;
       }
       /**
-       * <code>.context.Uuid endpoint_uuid = 3;</code>
+       * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
        */
-      public Builder clearEndpointUuid() {
-        if (endpointUuidBuilder_ == null) {
-          endpointUuid_ = null;
-          onChanged();
+      public java.util.List<? extends context.ContextOuterClass.ServiceIdOrBuilder> 
+           getSubServiceIdsOrBuilderList() {
+        if (subServiceIdsBuilder_ != null) {
+          return subServiceIdsBuilder_.getMessageOrBuilderList();
         } else {
-          endpointUuid_ = null;
-          endpointUuidBuilder_ = null;
+          return java.util.Collections.unmodifiableList(subServiceIds_);
         }
-
-        return this;
       }
       /**
-       * <code>.context.Uuid endpoint_uuid = 3;</code>
+       * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
        */
-      public context.ContextOuterClass.Uuid.Builder getEndpointUuidBuilder() {
-        
-        onChanged();
-        return getEndpointUuidFieldBuilder().getBuilder();
+      public context.ContextOuterClass.ServiceId.Builder addSubServiceIdsBuilder() {
+        return getSubServiceIdsFieldBuilder().addBuilder(
+            context.ContextOuterClass.ServiceId.getDefaultInstance());
       }
       /**
-       * <code>.context.Uuid endpoint_uuid = 3;</code>
+       * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
        */
-      public context.ContextOuterClass.UuidOrBuilder getEndpointUuidOrBuilder() {
-        if (endpointUuidBuilder_ != null) {
-          return endpointUuidBuilder_.getMessageOrBuilder();
-        } else {
-          return endpointUuid_ == null ?
-              context.ContextOuterClass.Uuid.getDefaultInstance() : endpointUuid_;
-        }
+      public context.ContextOuterClass.ServiceId.Builder addSubServiceIdsBuilder(
+          int index) {
+        return getSubServiceIdsFieldBuilder().addBuilder(
+            index, context.ContextOuterClass.ServiceId.getDefaultInstance());
       }
       /**
-       * <code>.context.Uuid endpoint_uuid = 3;</code>
+       * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
        */
-      private com.google.protobuf.SingleFieldBuilderV3<
-          context.ContextOuterClass.Uuid, context.ContextOuterClass.Uuid.Builder, context.ContextOuterClass.UuidOrBuilder> 
-          getEndpointUuidFieldBuilder() {
-        if (endpointUuidBuilder_ == null) {
-          endpointUuidBuilder_ = new com.google.protobuf.SingleFieldBuilderV3<
-              context.ContextOuterClass.Uuid, context.ContextOuterClass.Uuid.Builder, context.ContextOuterClass.UuidOrBuilder>(
-                  getEndpointUuid(),
+      public java.util.List<context.ContextOuterClass.ServiceId.Builder> 
+           getSubServiceIdsBuilderList() {
+        return getSubServiceIdsFieldBuilder().getBuilderList();
+      }
+      private com.google.protobuf.RepeatedFieldBuilderV3<
+          context.ContextOuterClass.ServiceId, context.ContextOuterClass.ServiceId.Builder, context.ContextOuterClass.ServiceIdOrBuilder> 
+          getSubServiceIdsFieldBuilder() {
+        if (subServiceIdsBuilder_ == null) {
+          subServiceIdsBuilder_ = new com.google.protobuf.RepeatedFieldBuilderV3<
+              context.ContextOuterClass.ServiceId, context.ContextOuterClass.ServiceId.Builder, context.ContextOuterClass.ServiceIdOrBuilder>(
+                  subServiceIds_,
+                  ((bitField0_ & 0x00000002) != 0),
                   getParentForChildren(),
                   isClean());
-          endpointUuid_ = null;
+          subServiceIds_ = null;
         }
-        return endpointUuidBuilder_;
+        return subServiceIdsBuilder_;
       }
       @java.lang.Override
       public final Builder setUnknownFields(
@@ -28648,128 +29813,95 @@ public final class ContextOuterClass {
       }
 
 
-      // @@protoc_insertion_point(builder_scope:context.EndPointId)
+      // @@protoc_insertion_point(builder_scope:context.Connection)
     }
 
-    // @@protoc_insertion_point(class_scope:context.EndPointId)
-    private static final context.ContextOuterClass.EndPointId DEFAULT_INSTANCE;
+    // @@protoc_insertion_point(class_scope:context.Connection)
+    private static final context.ContextOuterClass.Connection DEFAULT_INSTANCE;
     static {
-      DEFAULT_INSTANCE = new context.ContextOuterClass.EndPointId();
+      DEFAULT_INSTANCE = new context.ContextOuterClass.Connection();
     }
 
-    public static context.ContextOuterClass.EndPointId getDefaultInstance() {
+    public static context.ContextOuterClass.Connection getDefaultInstance() {
       return DEFAULT_INSTANCE;
     }
 
-    private static final com.google.protobuf.Parser<EndPointId>
-        PARSER = new com.google.protobuf.AbstractParser<EndPointId>() {
+    private static final com.google.protobuf.Parser<Connection>
+        PARSER = new com.google.protobuf.AbstractParser<Connection>() {
       @java.lang.Override
-      public EndPointId parsePartialFrom(
+      public Connection parsePartialFrom(
           com.google.protobuf.CodedInputStream input,
           com.google.protobuf.ExtensionRegistryLite extensionRegistry)
           throws com.google.protobuf.InvalidProtocolBufferException {
-        return new EndPointId(input, extensionRegistry);
+        return new Connection(input, extensionRegistry);
       }
     };
 
-    public static com.google.protobuf.Parser<EndPointId> parser() {
+    public static com.google.protobuf.Parser<Connection> parser() {
       return PARSER;
     }
 
     @java.lang.Override
-    public com.google.protobuf.Parser<EndPointId> getParserForType() {
+    public com.google.protobuf.Parser<Connection> getParserForType() {
       return PARSER;
     }
 
     @java.lang.Override
-    public context.ContextOuterClass.EndPointId getDefaultInstanceForType() {
+    public context.ContextOuterClass.Connection getDefaultInstanceForType() {
       return DEFAULT_INSTANCE;
     }
 
   }
 
-  public interface EndPointOrBuilder extends
-      // @@protoc_insertion_point(interface_extends:context.EndPoint)
+  public interface ConnectionIdListOrBuilder extends
+      // @@protoc_insertion_point(interface_extends:context.ConnectionIdList)
       com.google.protobuf.MessageOrBuilder {
 
     /**
-     * <code>.context.EndPointId endpoint_id = 1;</code>
-     * @return Whether the endpointId field is set.
-     */
-    boolean hasEndpointId();
-    /**
-     * <code>.context.EndPointId endpoint_id = 1;</code>
-     * @return The endpointId.
-     */
-    context.ContextOuterClass.EndPointId getEndpointId();
-    /**
-     * <code>.context.EndPointId endpoint_id = 1;</code>
-     */
-    context.ContextOuterClass.EndPointIdOrBuilder getEndpointIdOrBuilder();
-
-    /**
-     * <code>string endpoint_type = 2;</code>
-     * @return The endpointType.
-     */
-    java.lang.String getEndpointType();
-    /**
-     * <code>string endpoint_type = 2;</code>
-     * @return The bytes for endpointType.
-     */
-    com.google.protobuf.ByteString
-        getEndpointTypeBytes();
-
-    /**
-     * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
-     * @return A list containing the kpiSampleTypes.
+     * <code>repeated .context.ConnectionId connection_ids = 1;</code>
      */
-    java.util.List<kpi_sample_types.KpiSampleTypes.KpiSampleType> getKpiSampleTypesList();
+    java.util.List<context.ContextOuterClass.ConnectionId> 
+        getConnectionIdsList();
     /**
-     * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
-     * @return The count of kpiSampleTypes.
+     * <code>repeated .context.ConnectionId connection_ids = 1;</code>
      */
-    int getKpiSampleTypesCount();
+    context.ContextOuterClass.ConnectionId getConnectionIds(int index);
     /**
-     * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
-     * @param index The index of the element to return.
-     * @return The kpiSampleTypes at the given index.
+     * <code>repeated .context.ConnectionId connection_ids = 1;</code>
      */
-    kpi_sample_types.KpiSampleTypes.KpiSampleType getKpiSampleTypes(int index);
+    int getConnectionIdsCount();
     /**
-     * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
-     * @return A list containing the enum numeric values on the wire for kpiSampleTypes.
+     * <code>repeated .context.ConnectionId connection_ids = 1;</code>
      */
-    java.util.List<java.lang.Integer>
-    getKpiSampleTypesValueList();
+    java.util.List<? extends context.ContextOuterClass.ConnectionIdOrBuilder> 
+        getConnectionIdsOrBuilderList();
     /**
-     * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
-     * @param index The index of the value to return.
-     * @return The enum numeric value on the wire of kpiSampleTypes at the given index.
+     * <code>repeated .context.ConnectionId connection_ids = 1;</code>
      */
-    int getKpiSampleTypesValue(int index);
+    context.ContextOuterClass.ConnectionIdOrBuilder getConnectionIdsOrBuilder(
+        int index);
   }
   /**
-   * Protobuf type {@code context.EndPoint}
+   * Protobuf type {@code context.ConnectionIdList}
    */
-  public static final class EndPoint extends
+  public static final class ConnectionIdList extends
       com.google.protobuf.GeneratedMessageV3 implements
-      // @@protoc_insertion_point(message_implements:context.EndPoint)
-      EndPointOrBuilder {
+      // @@protoc_insertion_point(message_implements:context.ConnectionIdList)
+      ConnectionIdListOrBuilder {
   private static final long serialVersionUID = 0L;
-    // Use EndPoint.newBuilder() to construct.
-    private EndPoint(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
+    // Use ConnectionIdList.newBuilder() to construct.
+    private ConnectionIdList(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
       super(builder);
     }
-    private EndPoint() {
-      endpointType_ = "";
-      kpiSampleTypes_ = java.util.Collections.emptyList();
+    private ConnectionIdList() {
+      connectionIds_ = java.util.Collections.emptyList();
     }
 
     @java.lang.Override
     @SuppressWarnings({"unused"})
     protected java.lang.Object newInstance(
         UnusedPrivateParameter unused) {
-      return new EndPoint();
+      return new ConnectionIdList();
     }
 
     @java.lang.Override
@@ -28777,7 +29909,7 @@ public final class ContextOuterClass {
     getUnknownFields() {
       return this.unknownFields;
     }
-    private EndPoint(
+    private ConnectionIdList(
         com.google.protobuf.CodedInputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
@@ -28797,45 +29929,12 @@ public final class ContextOuterClass {
               done = true;
               break;
             case 10: {
-              context.ContextOuterClass.EndPointId.Builder subBuilder = null;
-              if (endpointId_ != null) {
-                subBuilder = endpointId_.toBuilder();
-              }
-              endpointId_ = input.readMessage(context.ContextOuterClass.EndPointId.parser(), extensionRegistry);
-              if (subBuilder != null) {
-                subBuilder.mergeFrom(endpointId_);
-                endpointId_ = subBuilder.buildPartial();
-              }
-
-              break;
-            }
-            case 18: {
-              java.lang.String s = input.readStringRequireUtf8();
-
-              endpointType_ = s;
-              break;
-            }
-            case 24: {
-              int rawValue = input.readEnum();
               if (!((mutable_bitField0_ & 0x00000001) != 0)) {
-                kpiSampleTypes_ = new java.util.ArrayList<java.lang.Integer>();
+                connectionIds_ = new java.util.ArrayList<context.ContextOuterClass.ConnectionId>();
                 mutable_bitField0_ |= 0x00000001;
               }
-              kpiSampleTypes_.add(rawValue);
-              break;
-            }
-            case 26: {
-              int length = input.readRawVarint32();
-              int oldLimit = input.pushLimit(length);
-              while(input.getBytesUntilLimit() > 0) {
-                int rawValue = input.readEnum();
-                if (!((mutable_bitField0_ & 0x00000001) != 0)) {
-                  kpiSampleTypes_ = new java.util.ArrayList<java.lang.Integer>();
-                  mutable_bitField0_ |= 0x00000001;
-                }
-                kpiSampleTypes_.add(rawValue);
-              }
-              input.popLimit(oldLimit);
+              connectionIds_.add(
+                  input.readMessage(context.ContextOuterClass.ConnectionId.parser(), extensionRegistry));
               break;
             }
             default: {
@@ -28850,150 +29949,68 @@ public final class ContextOuterClass {
       } catch (com.google.protobuf.InvalidProtocolBufferException e) {
         throw e.setUnfinishedMessage(this);
       } catch (java.io.IOException e) {
-        throw new com.google.protobuf.InvalidProtocolBufferException(
-            e).setUnfinishedMessage(this);
-      } finally {
-        if (((mutable_bitField0_ & 0x00000001) != 0)) {
-          kpiSampleTypes_ = java.util.Collections.unmodifiableList(kpiSampleTypes_);
-        }
-        this.unknownFields = unknownFields.build();
-        makeExtensionsImmutable();
-      }
-    }
-    public static final com.google.protobuf.Descriptors.Descriptor
-        getDescriptor() {
-      return context.ContextOuterClass.internal_static_context_EndPoint_descriptor;
-    }
-
-    @java.lang.Override
-    protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
-        internalGetFieldAccessorTable() {
-      return context.ContextOuterClass.internal_static_context_EndPoint_fieldAccessorTable
-          .ensureFieldAccessorsInitialized(
-              context.ContextOuterClass.EndPoint.class, context.ContextOuterClass.EndPoint.Builder.class);
-    }
-
-    public static final int ENDPOINT_ID_FIELD_NUMBER = 1;
-    private context.ContextOuterClass.EndPointId endpointId_;
-    /**
-     * <code>.context.EndPointId endpoint_id = 1;</code>
-     * @return Whether the endpointId field is set.
-     */
-    @java.lang.Override
-    public boolean hasEndpointId() {
-      return endpointId_ != null;
-    }
-    /**
-     * <code>.context.EndPointId endpoint_id = 1;</code>
-     * @return The endpointId.
-     */
-    @java.lang.Override
-    public context.ContextOuterClass.EndPointId getEndpointId() {
-      return endpointId_ == null ? context.ContextOuterClass.EndPointId.getDefaultInstance() : endpointId_;
-    }
-    /**
-     * <code>.context.EndPointId endpoint_id = 1;</code>
-     */
-    @java.lang.Override
-    public context.ContextOuterClass.EndPointIdOrBuilder getEndpointIdOrBuilder() {
-      return getEndpointId();
-    }
-
-    public static final int ENDPOINT_TYPE_FIELD_NUMBER = 2;
-    private volatile java.lang.Object endpointType_;
-    /**
-     * <code>string endpoint_type = 2;</code>
-     * @return The endpointType.
-     */
-    @java.lang.Override
-    public java.lang.String getEndpointType() {
-      java.lang.Object ref = endpointType_;
-      if (ref instanceof java.lang.String) {
-        return (java.lang.String) ref;
-      } else {
-        com.google.protobuf.ByteString bs = 
-            (com.google.protobuf.ByteString) ref;
-        java.lang.String s = bs.toStringUtf8();
-        endpointType_ = s;
-        return s;
+        throw new com.google.protobuf.InvalidProtocolBufferException(
+            e).setUnfinishedMessage(this);
+      } finally {
+        if (((mutable_bitField0_ & 0x00000001) != 0)) {
+          connectionIds_ = java.util.Collections.unmodifiableList(connectionIds_);
+        }
+        this.unknownFields = unknownFields.build();
+        makeExtensionsImmutable();
       }
     }
-    /**
-     * <code>string endpoint_type = 2;</code>
-     * @return The bytes for endpointType.
-     */
+    public static final com.google.protobuf.Descriptors.Descriptor
+        getDescriptor() {
+      return context.ContextOuterClass.internal_static_context_ConnectionIdList_descriptor;
+    }
+
     @java.lang.Override
-    public com.google.protobuf.ByteString
-        getEndpointTypeBytes() {
-      java.lang.Object ref = endpointType_;
-      if (ref instanceof java.lang.String) {
-        com.google.protobuf.ByteString b = 
-            com.google.protobuf.ByteString.copyFromUtf8(
-                (java.lang.String) ref);
-        endpointType_ = b;
-        return b;
-      } else {
-        return (com.google.protobuf.ByteString) ref;
-      }
+    protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+        internalGetFieldAccessorTable() {
+      return context.ContextOuterClass.internal_static_context_ConnectionIdList_fieldAccessorTable
+          .ensureFieldAccessorsInitialized(
+              context.ContextOuterClass.ConnectionIdList.class, context.ContextOuterClass.ConnectionIdList.Builder.class);
     }
 
-    public static final int KPI_SAMPLE_TYPES_FIELD_NUMBER = 3;
-    private java.util.List<java.lang.Integer> kpiSampleTypes_;
-    private static final com.google.protobuf.Internal.ListAdapter.Converter<
-        java.lang.Integer, kpi_sample_types.KpiSampleTypes.KpiSampleType> kpiSampleTypes_converter_ =
-            new com.google.protobuf.Internal.ListAdapter.Converter<
-                java.lang.Integer, kpi_sample_types.KpiSampleTypes.KpiSampleType>() {
-              public kpi_sample_types.KpiSampleTypes.KpiSampleType convert(java.lang.Integer from) {
-                @SuppressWarnings("deprecation")
-                kpi_sample_types.KpiSampleTypes.KpiSampleType result = kpi_sample_types.KpiSampleTypes.KpiSampleType.valueOf(from);
-                return result == null ? kpi_sample_types.KpiSampleTypes.KpiSampleType.UNRECOGNIZED : result;
-              }
-            };
+    public static final int CONNECTION_IDS_FIELD_NUMBER = 1;
+    private java.util.List<context.ContextOuterClass.ConnectionId> connectionIds_;
     /**
-     * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
-     * @return A list containing the kpiSampleTypes.
+     * <code>repeated .context.ConnectionId connection_ids = 1;</code>
      */
     @java.lang.Override
-    public java.util.List<kpi_sample_types.KpiSampleTypes.KpiSampleType> getKpiSampleTypesList() {
-      return new com.google.protobuf.Internal.ListAdapter<
-          java.lang.Integer, kpi_sample_types.KpiSampleTypes.KpiSampleType>(kpiSampleTypes_, kpiSampleTypes_converter_);
+    public java.util.List<context.ContextOuterClass.ConnectionId> getConnectionIdsList() {
+      return connectionIds_;
     }
     /**
-     * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
-     * @return The count of kpiSampleTypes.
+     * <code>repeated .context.ConnectionId connection_ids = 1;</code>
      */
     @java.lang.Override
-    public int getKpiSampleTypesCount() {
-      return kpiSampleTypes_.size();
+    public java.util.List<? extends context.ContextOuterClass.ConnectionIdOrBuilder> 
+        getConnectionIdsOrBuilderList() {
+      return connectionIds_;
     }
     /**
-     * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
-     * @param index The index of the element to return.
-     * @return The kpiSampleTypes at the given index.
+     * <code>repeated .context.ConnectionId connection_ids = 1;</code>
      */
     @java.lang.Override
-    public kpi_sample_types.KpiSampleTypes.KpiSampleType getKpiSampleTypes(int index) {
-      return kpiSampleTypes_converter_.convert(kpiSampleTypes_.get(index));
+    public int getConnectionIdsCount() {
+      return connectionIds_.size();
     }
     /**
-     * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
-     * @return A list containing the enum numeric values on the wire for kpiSampleTypes.
+     * <code>repeated .context.ConnectionId connection_ids = 1;</code>
      */
     @java.lang.Override
-    public java.util.List<java.lang.Integer>
-    getKpiSampleTypesValueList() {
-      return kpiSampleTypes_;
+    public context.ContextOuterClass.ConnectionId getConnectionIds(int index) {
+      return connectionIds_.get(index);
     }
     /**
-     * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
-     * @param index The index of the value to return.
-     * @return The enum numeric value on the wire of kpiSampleTypes at the given index.
+     * <code>repeated .context.ConnectionId connection_ids = 1;</code>
      */
     @java.lang.Override
-    public int getKpiSampleTypesValue(int index) {
-      return kpiSampleTypes_.get(index);
+    public context.ContextOuterClass.ConnectionIdOrBuilder getConnectionIdsOrBuilder(
+        int index) {
+      return connectionIds_.get(index);
     }
-    private int kpiSampleTypesMemoizedSerializedSize;
 
     private byte memoizedIsInitialized = -1;
     @java.lang.Override
@@ -29009,19 +30026,8 @@ public final class ContextOuterClass {
     @java.lang.Override
     public void writeTo(com.google.protobuf.CodedOutputStream output)
                         throws java.io.IOException {
-      getSerializedSize();
-      if (endpointId_ != null) {
-        output.writeMessage(1, getEndpointId());
-      }
-      if (!getEndpointTypeBytes().isEmpty()) {
-        com.google.protobuf.GeneratedMessageV3.writeString(output, 2, endpointType_);
-      }
-      if (getKpiSampleTypesList().size() > 0) {
-        output.writeUInt32NoTag(26);
-        output.writeUInt32NoTag(kpiSampleTypesMemoizedSerializedSize);
-      }
-      for (int i = 0; i < kpiSampleTypes_.size(); i++) {
-        output.writeEnumNoTag(kpiSampleTypes_.get(i));
+      for (int i = 0; i < connectionIds_.size(); i++) {
+        output.writeMessage(1, connectionIds_.get(i));
       }
       unknownFields.writeTo(output);
     }
@@ -29032,24 +30038,9 @@ public final class ContextOuterClass {
       if (size != -1) return size;
 
       size = 0;
-      if (endpointId_ != null) {
+      for (int i = 0; i < connectionIds_.size(); i++) {
         size += com.google.protobuf.CodedOutputStream
-          .computeMessageSize(1, getEndpointId());
-      }
-      if (!getEndpointTypeBytes().isEmpty()) {
-        size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, endpointType_);
-      }
-      {
-        int dataSize = 0;
-        for (int i = 0; i < kpiSampleTypes_.size(); i++) {
-          dataSize += com.google.protobuf.CodedOutputStream
-            .computeEnumSizeNoTag(kpiSampleTypes_.get(i));
-        }
-        size += dataSize;
-        if (!getKpiSampleTypesList().isEmpty()) {  size += 1;
-          size += com.google.protobuf.CodedOutputStream
-            .computeUInt32SizeNoTag(dataSize);
-        }kpiSampleTypesMemoizedSerializedSize = dataSize;
+          .computeMessageSize(1, connectionIds_.get(i));
       }
       size += unknownFields.getSerializedSize();
       memoizedSize = size;
@@ -29061,19 +30052,13 @@ public final class ContextOuterClass {
       if (obj == this) {
        return true;
       }
-      if (!(obj instanceof context.ContextOuterClass.EndPoint)) {
+      if (!(obj instanceof context.ContextOuterClass.ConnectionIdList)) {
         return super.equals(obj);
       }
-      context.ContextOuterClass.EndPoint other = (context.ContextOuterClass.EndPoint) obj;
+      context.ContextOuterClass.ConnectionIdList other = (context.ContextOuterClass.ConnectionIdList) obj;
 
-      if (hasEndpointId() != other.hasEndpointId()) return false;
-      if (hasEndpointId()) {
-        if (!getEndpointId()
-            .equals(other.getEndpointId())) return false;
-      }
-      if (!getEndpointType()
-          .equals(other.getEndpointType())) return false;
-      if (!kpiSampleTypes_.equals(other.kpiSampleTypes_)) return false;
+      if (!getConnectionIdsList()
+          .equals(other.getConnectionIdsList())) return false;
       if (!unknownFields.equals(other.unknownFields)) return false;
       return true;
     }
@@ -29085,84 +30070,78 @@ public final class ContextOuterClass {
       }
       int hash = 41;
       hash = (19 * hash) + getDescriptor().hashCode();
-      if (hasEndpointId()) {
-        hash = (37 * hash) + ENDPOINT_ID_FIELD_NUMBER;
-        hash = (53 * hash) + getEndpointId().hashCode();
-      }
-      hash = (37 * hash) + ENDPOINT_TYPE_FIELD_NUMBER;
-      hash = (53 * hash) + getEndpointType().hashCode();
-      if (getKpiSampleTypesCount() > 0) {
-        hash = (37 * hash) + KPI_SAMPLE_TYPES_FIELD_NUMBER;
-        hash = (53 * hash) + kpiSampleTypes_.hashCode();
+      if (getConnectionIdsCount() > 0) {
+        hash = (37 * hash) + CONNECTION_IDS_FIELD_NUMBER;
+        hash = (53 * hash) + getConnectionIdsList().hashCode();
       }
       hash = (29 * hash) + unknownFields.hashCode();
       memoizedHashCode = hash;
       return hash;
     }
 
-    public static context.ContextOuterClass.EndPoint parseFrom(
+    public static context.ContextOuterClass.ConnectionIdList parseFrom(
         java.nio.ByteBuffer data)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data);
     }
-    public static context.ContextOuterClass.EndPoint parseFrom(
+    public static context.ContextOuterClass.ConnectionIdList parseFrom(
         java.nio.ByteBuffer data,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data, extensionRegistry);
     }
-    public static context.ContextOuterClass.EndPoint parseFrom(
+    public static context.ContextOuterClass.ConnectionIdList parseFrom(
         com.google.protobuf.ByteString data)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data);
     }
-    public static context.ContextOuterClass.EndPoint parseFrom(
+    public static context.ContextOuterClass.ConnectionIdList parseFrom(
         com.google.protobuf.ByteString data,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data, extensionRegistry);
     }
-    public static context.ContextOuterClass.EndPoint parseFrom(byte[] data)
+    public static context.ContextOuterClass.ConnectionIdList parseFrom(byte[] data)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data);
     }
-    public static context.ContextOuterClass.EndPoint parseFrom(
+    public static context.ContextOuterClass.ConnectionIdList parseFrom(
         byte[] data,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data, extensionRegistry);
     }
-    public static context.ContextOuterClass.EndPoint parseFrom(java.io.InputStream input)
+    public static context.ContextOuterClass.ConnectionIdList parseFrom(java.io.InputStream input)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseWithIOException(PARSER, input);
     }
-    public static context.ContextOuterClass.EndPoint parseFrom(
+    public static context.ContextOuterClass.ConnectionIdList parseFrom(
         java.io.InputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseWithIOException(PARSER, input, extensionRegistry);
     }
-    public static context.ContextOuterClass.EndPoint parseDelimitedFrom(java.io.InputStream input)
+    public static context.ContextOuterClass.ConnectionIdList parseDelimitedFrom(java.io.InputStream input)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseDelimitedWithIOException(PARSER, input);
     }
-    public static context.ContextOuterClass.EndPoint parseDelimitedFrom(
+    public static context.ContextOuterClass.ConnectionIdList parseDelimitedFrom(
         java.io.InputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseDelimitedWithIOException(PARSER, input, extensionRegistry);
     }
-    public static context.ContextOuterClass.EndPoint parseFrom(
+    public static context.ContextOuterClass.ConnectionIdList parseFrom(
         com.google.protobuf.CodedInputStream input)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseWithIOException(PARSER, input);
     }
-    public static context.ContextOuterClass.EndPoint parseFrom(
+    public static context.ContextOuterClass.ConnectionIdList parseFrom(
         com.google.protobuf.CodedInputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws java.io.IOException {
@@ -29175,7 +30154,7 @@ public final class ContextOuterClass {
     public static Builder newBuilder() {
       return DEFAULT_INSTANCE.toBuilder();
     }
-    public static Builder newBuilder(context.ContextOuterClass.EndPoint prototype) {
+    public static Builder newBuilder(context.ContextOuterClass.ConnectionIdList prototype) {
       return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
     }
     @java.lang.Override
@@ -29191,26 +30170,26 @@ public final class ContextOuterClass {
       return builder;
     }
     /**
-     * Protobuf type {@code context.EndPoint}
+     * Protobuf type {@code context.ConnectionIdList}
      */
     public static final class Builder extends
         com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
-        // @@protoc_insertion_point(builder_implements:context.EndPoint)
-        context.ContextOuterClass.EndPointOrBuilder {
+        // @@protoc_insertion_point(builder_implements:context.ConnectionIdList)
+        context.ContextOuterClass.ConnectionIdListOrBuilder {
       public static final com.google.protobuf.Descriptors.Descriptor
           getDescriptor() {
-        return context.ContextOuterClass.internal_static_context_EndPoint_descriptor;
+        return context.ContextOuterClass.internal_static_context_ConnectionIdList_descriptor;
       }
 
       @java.lang.Override
       protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
           internalGetFieldAccessorTable() {
-        return context.ContextOuterClass.internal_static_context_EndPoint_fieldAccessorTable
+        return context.ContextOuterClass.internal_static_context_ConnectionIdList_fieldAccessorTable
             .ensureFieldAccessorsInitialized(
-                context.ContextOuterClass.EndPoint.class, context.ContextOuterClass.EndPoint.Builder.class);
+                context.ContextOuterClass.ConnectionIdList.class, context.ContextOuterClass.ConnectionIdList.Builder.class);
       }
 
-      // Construct using context.ContextOuterClass.EndPoint.newBuilder()
+      // Construct using context.ContextOuterClass.ConnectionIdList.newBuilder()
       private Builder() {
         maybeForceBuilderInitialization();
       }
@@ -29223,38 +30202,35 @@ public final class ContextOuterClass {
       private void maybeForceBuilderInitialization() {
         if (com.google.protobuf.GeneratedMessageV3
                 .alwaysUseFieldBuilders) {
+          getConnectionIdsFieldBuilder();
         }
       }
       @java.lang.Override
       public Builder clear() {
         super.clear();
-        if (endpointIdBuilder_ == null) {
-          endpointId_ = null;
+        if (connectionIdsBuilder_ == null) {
+          connectionIds_ = java.util.Collections.emptyList();
+          bitField0_ = (bitField0_ & ~0x00000001);
         } else {
-          endpointId_ = null;
-          endpointIdBuilder_ = null;
+          connectionIdsBuilder_.clear();
         }
-        endpointType_ = "";
-
-        kpiSampleTypes_ = java.util.Collections.emptyList();
-        bitField0_ = (bitField0_ & ~0x00000001);
         return this;
       }
 
       @java.lang.Override
       public com.google.protobuf.Descriptors.Descriptor
           getDescriptorForType() {
-        return context.ContextOuterClass.internal_static_context_EndPoint_descriptor;
+        return context.ContextOuterClass.internal_static_context_ConnectionIdList_descriptor;
       }
 
       @java.lang.Override
-      public context.ContextOuterClass.EndPoint getDefaultInstanceForType() {
-        return context.ContextOuterClass.EndPoint.getDefaultInstance();
+      public context.ContextOuterClass.ConnectionIdList getDefaultInstanceForType() {
+        return context.ContextOuterClass.ConnectionIdList.getDefaultInstance();
       }
 
       @java.lang.Override
-      public context.ContextOuterClass.EndPoint build() {
-        context.ContextOuterClass.EndPoint result = buildPartial();
+      public context.ContextOuterClass.ConnectionIdList build() {
+        context.ContextOuterClass.ConnectionIdList result = buildPartial();
         if (!result.isInitialized()) {
           throw newUninitializedMessageException(result);
         }
@@ -29262,20 +30238,18 @@ public final class ContextOuterClass {
       }
 
       @java.lang.Override
-      public context.ContextOuterClass.EndPoint buildPartial() {
-        context.ContextOuterClass.EndPoint result = new context.ContextOuterClass.EndPoint(this);
+      public context.ContextOuterClass.ConnectionIdList buildPartial() {
+        context.ContextOuterClass.ConnectionIdList result = new context.ContextOuterClass.ConnectionIdList(this);
         int from_bitField0_ = bitField0_;
-        if (endpointIdBuilder_ == null) {
-          result.endpointId_ = endpointId_;
+        if (connectionIdsBuilder_ == null) {
+          if (((bitField0_ & 0x00000001) != 0)) {
+            connectionIds_ = java.util.Collections.unmodifiableList(connectionIds_);
+            bitField0_ = (bitField0_ & ~0x00000001);
+          }
+          result.connectionIds_ = connectionIds_;
         } else {
-          result.endpointId_ = endpointIdBuilder_.build();
-        }
-        result.endpointType_ = endpointType_;
-        if (((bitField0_ & 0x00000001) != 0)) {
-          kpiSampleTypes_ = java.util.Collections.unmodifiableList(kpiSampleTypes_);
-          bitField0_ = (bitField0_ & ~0x00000001);
+          result.connectionIds_ = connectionIdsBuilder_.build();
         }
-        result.kpiSampleTypes_ = kpiSampleTypes_;
         onBuilt();
         return result;
       }
@@ -29314,32 +30288,41 @@ public final class ContextOuterClass {
       }
       @java.lang.Override
       public Builder mergeFrom(com.google.protobuf.Message other) {
-        if (other instanceof context.ContextOuterClass.EndPoint) {
-          return mergeFrom((context.ContextOuterClass.EndPoint)other);
+        if (other instanceof context.ContextOuterClass.ConnectionIdList) {
+          return mergeFrom((context.ContextOuterClass.ConnectionIdList)other);
         } else {
           super.mergeFrom(other);
           return this;
         }
       }
 
-      public Builder mergeFrom(context.ContextOuterClass.EndPoint other) {
-        if (other == context.ContextOuterClass.EndPoint.getDefaultInstance()) return this;
-        if (other.hasEndpointId()) {
-          mergeEndpointId(other.getEndpointId());
-        }
-        if (!other.getEndpointType().isEmpty()) {
-          endpointType_ = other.endpointType_;
-          onChanged();
-        }
-        if (!other.kpiSampleTypes_.isEmpty()) {
-          if (kpiSampleTypes_.isEmpty()) {
-            kpiSampleTypes_ = other.kpiSampleTypes_;
-            bitField0_ = (bitField0_ & ~0x00000001);
-          } else {
-            ensureKpiSampleTypesIsMutable();
-            kpiSampleTypes_.addAll(other.kpiSampleTypes_);
+      public Builder mergeFrom(context.ContextOuterClass.ConnectionIdList other) {
+        if (other == context.ContextOuterClass.ConnectionIdList.getDefaultInstance()) return this;
+        if (connectionIdsBuilder_ == null) {
+          if (!other.connectionIds_.isEmpty()) {
+            if (connectionIds_.isEmpty()) {
+              connectionIds_ = other.connectionIds_;
+              bitField0_ = (bitField0_ & ~0x00000001);
+            } else {
+              ensureConnectionIdsIsMutable();
+              connectionIds_.addAll(other.connectionIds_);
+            }
+            onChanged();
+          }
+        } else {
+          if (!other.connectionIds_.isEmpty()) {
+            if (connectionIdsBuilder_.isEmpty()) {
+              connectionIdsBuilder_.dispose();
+              connectionIdsBuilder_ = null;
+              connectionIds_ = other.connectionIds_;
+              bitField0_ = (bitField0_ & ~0x00000001);
+              connectionIdsBuilder_ = 
+                com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders ?
+                   getConnectionIdsFieldBuilder() : null;
+            } else {
+              connectionIdsBuilder_.addAllMessages(other.connectionIds_);
+            }
           }
-          onChanged();
         }
         this.mergeUnknownFields(other.unknownFields);
         onChanged();
@@ -29356,11 +30339,11 @@ public final class ContextOuterClass {
           com.google.protobuf.CodedInputStream input,
           com.google.protobuf.ExtensionRegistryLite extensionRegistry)
           throws java.io.IOException {
-        context.ContextOuterClass.EndPoint parsedMessage = null;
+        context.ContextOuterClass.ConnectionIdList parsedMessage = null;
         try {
           parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
         } catch (com.google.protobuf.InvalidProtocolBufferException e) {
-          parsedMessage = (context.ContextOuterClass.EndPoint) e.getUnfinishedMessage();
+          parsedMessage = (context.ContextOuterClass.ConnectionIdList) e.getUnfinishedMessage();
           throw e.unwrapIOException();
         } finally {
           if (parsedMessage != null) {
@@ -29371,339 +30354,244 @@ public final class ContextOuterClass {
       }
       private int bitField0_;
 
-      private context.ContextOuterClass.EndPointId endpointId_;
-      private com.google.protobuf.SingleFieldBuilderV3<
-          context.ContextOuterClass.EndPointId, context.ContextOuterClass.EndPointId.Builder, context.ContextOuterClass.EndPointIdOrBuilder> endpointIdBuilder_;
-      /**
-       * <code>.context.EndPointId endpoint_id = 1;</code>
-       * @return Whether the endpointId field is set.
-       */
-      public boolean hasEndpointId() {
-        return endpointIdBuilder_ != null || endpointId_ != null;
+      private java.util.List<context.ContextOuterClass.ConnectionId> connectionIds_ =
+        java.util.Collections.emptyList();
+      private void ensureConnectionIdsIsMutable() {
+        if (!((bitField0_ & 0x00000001) != 0)) {
+          connectionIds_ = new java.util.ArrayList<context.ContextOuterClass.ConnectionId>(connectionIds_);
+          bitField0_ |= 0x00000001;
+         }
       }
+
+      private com.google.protobuf.RepeatedFieldBuilderV3<
+          context.ContextOuterClass.ConnectionId, context.ContextOuterClass.ConnectionId.Builder, context.ContextOuterClass.ConnectionIdOrBuilder> connectionIdsBuilder_;
+
       /**
-       * <code>.context.EndPointId endpoint_id = 1;</code>
-       * @return The endpointId.
+       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
        */
-      public context.ContextOuterClass.EndPointId getEndpointId() {
-        if (endpointIdBuilder_ == null) {
-          return endpointId_ == null ? context.ContextOuterClass.EndPointId.getDefaultInstance() : endpointId_;
+      public java.util.List<context.ContextOuterClass.ConnectionId> getConnectionIdsList() {
+        if (connectionIdsBuilder_ == null) {
+          return java.util.Collections.unmodifiableList(connectionIds_);
         } else {
-          return endpointIdBuilder_.getMessage();
+          return connectionIdsBuilder_.getMessageList();
         }
       }
       /**
-       * <code>.context.EndPointId endpoint_id = 1;</code>
+       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
        */
-      public Builder setEndpointId(context.ContextOuterClass.EndPointId value) {
-        if (endpointIdBuilder_ == null) {
-          if (value == null) {
-            throw new NullPointerException();
-          }
-          endpointId_ = value;
-          onChanged();
+      public int getConnectionIdsCount() {
+        if (connectionIdsBuilder_ == null) {
+          return connectionIds_.size();
         } else {
-          endpointIdBuilder_.setMessage(value);
+          return connectionIdsBuilder_.getCount();
         }
-
-        return this;
       }
       /**
-       * <code>.context.EndPointId endpoint_id = 1;</code>
+       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
        */
-      public Builder setEndpointId(
-          context.ContextOuterClass.EndPointId.Builder builderForValue) {
-        if (endpointIdBuilder_ == null) {
-          endpointId_ = builderForValue.build();
-          onChanged();
+      public context.ContextOuterClass.ConnectionId getConnectionIds(int index) {
+        if (connectionIdsBuilder_ == null) {
+          return connectionIds_.get(index);
         } else {
-          endpointIdBuilder_.setMessage(builderForValue.build());
+          return connectionIdsBuilder_.getMessage(index);
         }
-
-        return this;
       }
       /**
-       * <code>.context.EndPointId endpoint_id = 1;</code>
+       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
        */
-      public Builder mergeEndpointId(context.ContextOuterClass.EndPointId value) {
-        if (endpointIdBuilder_ == null) {
-          if (endpointId_ != null) {
-            endpointId_ =
-              context.ContextOuterClass.EndPointId.newBuilder(endpointId_).mergeFrom(value).buildPartial();
-          } else {
-            endpointId_ = value;
+      public Builder setConnectionIds(
+          int index, context.ContextOuterClass.ConnectionId value) {
+        if (connectionIdsBuilder_ == null) {
+          if (value == null) {
+            throw new NullPointerException();
           }
+          ensureConnectionIdsIsMutable();
+          connectionIds_.set(index, value);
           onChanged();
         } else {
-          endpointIdBuilder_.mergeFrom(value);
+          connectionIdsBuilder_.setMessage(index, value);
         }
-
         return this;
       }
       /**
-       * <code>.context.EndPointId endpoint_id = 1;</code>
+       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
        */
-      public Builder clearEndpointId() {
-        if (endpointIdBuilder_ == null) {
-          endpointId_ = null;
+      public Builder setConnectionIds(
+          int index, context.ContextOuterClass.ConnectionId.Builder builderForValue) {
+        if (connectionIdsBuilder_ == null) {
+          ensureConnectionIdsIsMutable();
+          connectionIds_.set(index, builderForValue.build());
           onChanged();
         } else {
-          endpointId_ = null;
-          endpointIdBuilder_ = null;
+          connectionIdsBuilder_.setMessage(index, builderForValue.build());
         }
-
         return this;
       }
       /**
-       * <code>.context.EndPointId endpoint_id = 1;</code>
-       */
-      public context.ContextOuterClass.EndPointId.Builder getEndpointIdBuilder() {
-        
-        onChanged();
-        return getEndpointIdFieldBuilder().getBuilder();
-      }
-      /**
-       * <code>.context.EndPointId endpoint_id = 1;</code>
-       */
-      public context.ContextOuterClass.EndPointIdOrBuilder getEndpointIdOrBuilder() {
-        if (endpointIdBuilder_ != null) {
-          return endpointIdBuilder_.getMessageOrBuilder();
-        } else {
-          return endpointId_ == null ?
-              context.ContextOuterClass.EndPointId.getDefaultInstance() : endpointId_;
-        }
-      }
-      /**
-       * <code>.context.EndPointId endpoint_id = 1;</code>
-       */
-      private com.google.protobuf.SingleFieldBuilderV3<
-          context.ContextOuterClass.EndPointId, context.ContextOuterClass.EndPointId.Builder, context.ContextOuterClass.EndPointIdOrBuilder> 
-          getEndpointIdFieldBuilder() {
-        if (endpointIdBuilder_ == null) {
-          endpointIdBuilder_ = new com.google.protobuf.SingleFieldBuilderV3<
-              context.ContextOuterClass.EndPointId, context.ContextOuterClass.EndPointId.Builder, context.ContextOuterClass.EndPointIdOrBuilder>(
-                  getEndpointId(),
-                  getParentForChildren(),
-                  isClean());
-          endpointId_ = null;
-        }
-        return endpointIdBuilder_;
-      }
-
-      private java.lang.Object endpointType_ = "";
-      /**
-       * <code>string endpoint_type = 2;</code>
-       * @return The endpointType.
-       */
-      public java.lang.String getEndpointType() {
-        java.lang.Object ref = endpointType_;
-        if (!(ref instanceof java.lang.String)) {
-          com.google.protobuf.ByteString bs =
-              (com.google.protobuf.ByteString) ref;
-          java.lang.String s = bs.toStringUtf8();
-          endpointType_ = s;
-          return s;
-        } else {
-          return (java.lang.String) ref;
-        }
-      }
-      /**
-       * <code>string endpoint_type = 2;</code>
-       * @return The bytes for endpointType.
+       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
        */
-      public com.google.protobuf.ByteString
-          getEndpointTypeBytes() {
-        java.lang.Object ref = endpointType_;
-        if (ref instanceof String) {
-          com.google.protobuf.ByteString b = 
-              com.google.protobuf.ByteString.copyFromUtf8(
-                  (java.lang.String) ref);
-          endpointType_ = b;
-          return b;
+      public Builder addConnectionIds(context.ContextOuterClass.ConnectionId value) {
+        if (connectionIdsBuilder_ == null) {
+          if (value == null) {
+            throw new NullPointerException();
+          }
+          ensureConnectionIdsIsMutable();
+          connectionIds_.add(value);
+          onChanged();
         } else {
-          return (com.google.protobuf.ByteString) ref;
-        }
-      }
-      /**
-       * <code>string endpoint_type = 2;</code>
-       * @param value The endpointType to set.
-       * @return This builder for chaining.
-       */
-      public Builder setEndpointType(
-          java.lang.String value) {
-        if (value == null) {
-    throw new NullPointerException();
-  }
-  
-        endpointType_ = value;
-        onChanged();
-        return this;
-      }
-      /**
-       * <code>string endpoint_type = 2;</code>
-       * @return This builder for chaining.
-       */
-      public Builder clearEndpointType() {
-        
-        endpointType_ = getDefaultInstance().getEndpointType();
-        onChanged();
-        return this;
-      }
-      /**
-       * <code>string endpoint_type = 2;</code>
-       * @param value The bytes for endpointType to set.
-       * @return This builder for chaining.
-       */
-      public Builder setEndpointTypeBytes(
-          com.google.protobuf.ByteString value) {
-        if (value == null) {
-    throw new NullPointerException();
-  }
-  checkByteStringIsUtf8(value);
-        
-        endpointType_ = value;
-        onChanged();
-        return this;
-      }
-
-      private java.util.List<java.lang.Integer> kpiSampleTypes_ =
-        java.util.Collections.emptyList();
-      private void ensureKpiSampleTypesIsMutable() {
-        if (!((bitField0_ & 0x00000001) != 0)) {
-          kpiSampleTypes_ = new java.util.ArrayList<java.lang.Integer>(kpiSampleTypes_);
-          bitField0_ |= 0x00000001;
-        }
-      }
-      /**
-       * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
-       * @return A list containing the kpiSampleTypes.
-       */
-      public java.util.List<kpi_sample_types.KpiSampleTypes.KpiSampleType> getKpiSampleTypesList() {
-        return new com.google.protobuf.Internal.ListAdapter<
-            java.lang.Integer, kpi_sample_types.KpiSampleTypes.KpiSampleType>(kpiSampleTypes_, kpiSampleTypes_converter_);
+          connectionIdsBuilder_.addMessage(value);
+        }
+        return this;
       }
       /**
-       * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
-       * @return The count of kpiSampleTypes.
+       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
        */
-      public int getKpiSampleTypesCount() {
-        return kpiSampleTypes_.size();
+      public Builder addConnectionIds(
+          int index, context.ContextOuterClass.ConnectionId value) {
+        if (connectionIdsBuilder_ == null) {
+          if (value == null) {
+            throw new NullPointerException();
+          }
+          ensureConnectionIdsIsMutable();
+          connectionIds_.add(index, value);
+          onChanged();
+        } else {
+          connectionIdsBuilder_.addMessage(index, value);
+        }
+        return this;
       }
       /**
-       * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
-       * @param index The index of the element to return.
-       * @return The kpiSampleTypes at the given index.
+       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
        */
-      public kpi_sample_types.KpiSampleTypes.KpiSampleType getKpiSampleTypes(int index) {
-        return kpiSampleTypes_converter_.convert(kpiSampleTypes_.get(index));
+      public Builder addConnectionIds(
+          context.ContextOuterClass.ConnectionId.Builder builderForValue) {
+        if (connectionIdsBuilder_ == null) {
+          ensureConnectionIdsIsMutable();
+          connectionIds_.add(builderForValue.build());
+          onChanged();
+        } else {
+          connectionIdsBuilder_.addMessage(builderForValue.build());
+        }
+        return this;
       }
       /**
-       * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
-       * @param index The index to set the value at.
-       * @param value The kpiSampleTypes to set.
-       * @return This builder for chaining.
+       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
        */
-      public Builder setKpiSampleTypes(
-          int index, kpi_sample_types.KpiSampleTypes.KpiSampleType value) {
-        if (value == null) {
-          throw new NullPointerException();
+      public Builder addConnectionIds(
+          int index, context.ContextOuterClass.ConnectionId.Builder builderForValue) {
+        if (connectionIdsBuilder_ == null) {
+          ensureConnectionIdsIsMutable();
+          connectionIds_.add(index, builderForValue.build());
+          onChanged();
+        } else {
+          connectionIdsBuilder_.addMessage(index, builderForValue.build());
         }
-        ensureKpiSampleTypesIsMutable();
-        kpiSampleTypes_.set(index, value.getNumber());
-        onChanged();
         return this;
       }
       /**
-       * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
-       * @param value The kpiSampleTypes to add.
-       * @return This builder for chaining.
+       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
        */
-      public Builder addKpiSampleTypes(kpi_sample_types.KpiSampleTypes.KpiSampleType value) {
-        if (value == null) {
-          throw new NullPointerException();
+      public Builder addAllConnectionIds(
+          java.lang.Iterable<? extends context.ContextOuterClass.ConnectionId> values) {
+        if (connectionIdsBuilder_ == null) {
+          ensureConnectionIdsIsMutable();
+          com.google.protobuf.AbstractMessageLite.Builder.addAll(
+              values, connectionIds_);
+          onChanged();
+        } else {
+          connectionIdsBuilder_.addAllMessages(values);
         }
-        ensureKpiSampleTypesIsMutable();
-        kpiSampleTypes_.add(value.getNumber());
-        onChanged();
         return this;
       }
       /**
-       * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
-       * @param values The kpiSampleTypes to add.
-       * @return This builder for chaining.
+       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
        */
-      public Builder addAllKpiSampleTypes(
-          java.lang.Iterable<? extends kpi_sample_types.KpiSampleTypes.KpiSampleType> values) {
-        ensureKpiSampleTypesIsMutable();
-        for (kpi_sample_types.KpiSampleTypes.KpiSampleType value : values) {
-          kpiSampleTypes_.add(value.getNumber());
+      public Builder clearConnectionIds() {
+        if (connectionIdsBuilder_ == null) {
+          connectionIds_ = java.util.Collections.emptyList();
+          bitField0_ = (bitField0_ & ~0x00000001);
+          onChanged();
+        } else {
+          connectionIdsBuilder_.clear();
         }
-        onChanged();
         return this;
       }
       /**
-       * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
-       * @return This builder for chaining.
+       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
        */
-      public Builder clearKpiSampleTypes() {
-        kpiSampleTypes_ = java.util.Collections.emptyList();
-        bitField0_ = (bitField0_ & ~0x00000001);
-        onChanged();
+      public Builder removeConnectionIds(int index) {
+        if (connectionIdsBuilder_ == null) {
+          ensureConnectionIdsIsMutable();
+          connectionIds_.remove(index);
+          onChanged();
+        } else {
+          connectionIdsBuilder_.remove(index);
+        }
         return this;
       }
       /**
-       * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
-       * @return A list containing the enum numeric values on the wire for kpiSampleTypes.
+       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
        */
-      public java.util.List<java.lang.Integer>
-      getKpiSampleTypesValueList() {
-        return java.util.Collections.unmodifiableList(kpiSampleTypes_);
+      public context.ContextOuterClass.ConnectionId.Builder getConnectionIdsBuilder(
+          int index) {
+        return getConnectionIdsFieldBuilder().getBuilder(index);
       }
       /**
-       * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
-       * @param index The index of the value to return.
-       * @return The enum numeric value on the wire of kpiSampleTypes at the given index.
+       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
        */
-      public int getKpiSampleTypesValue(int index) {
-        return kpiSampleTypes_.get(index);
+      public context.ContextOuterClass.ConnectionIdOrBuilder getConnectionIdsOrBuilder(
+          int index) {
+        if (connectionIdsBuilder_ == null) {
+          return connectionIds_.get(index);  } else {
+          return connectionIdsBuilder_.getMessageOrBuilder(index);
+        }
       }
       /**
-       * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
-       * @param index The index of the value to return.
-       * @return The enum numeric value on the wire of kpiSampleTypes at the given index.
-       * @return This builder for chaining.
+       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
        */
-      public Builder setKpiSampleTypesValue(
-          int index, int value) {
-        ensureKpiSampleTypesIsMutable();
-        kpiSampleTypes_.set(index, value);
-        onChanged();
-        return this;
+      public java.util.List<? extends context.ContextOuterClass.ConnectionIdOrBuilder> 
+           getConnectionIdsOrBuilderList() {
+        if (connectionIdsBuilder_ != null) {
+          return connectionIdsBuilder_.getMessageOrBuilderList();
+        } else {
+          return java.util.Collections.unmodifiableList(connectionIds_);
+        }
       }
       /**
-       * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
-       * @param value The enum numeric value on the wire for kpiSampleTypes to add.
-       * @return This builder for chaining.
+       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
        */
-      public Builder addKpiSampleTypesValue(int value) {
-        ensureKpiSampleTypesIsMutable();
-        kpiSampleTypes_.add(value);
-        onChanged();
-        return this;
+      public context.ContextOuterClass.ConnectionId.Builder addConnectionIdsBuilder() {
+        return getConnectionIdsFieldBuilder().addBuilder(
+            context.ContextOuterClass.ConnectionId.getDefaultInstance());
       }
       /**
-       * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
-       * @param values The enum numeric values on the wire for kpiSampleTypes to add.
-       * @return This builder for chaining.
+       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
        */
-      public Builder addAllKpiSampleTypesValue(
-          java.lang.Iterable<java.lang.Integer> values) {
-        ensureKpiSampleTypesIsMutable();
-        for (int value : values) {
-          kpiSampleTypes_.add(value);
+      public context.ContextOuterClass.ConnectionId.Builder addConnectionIdsBuilder(
+          int index) {
+        return getConnectionIdsFieldBuilder().addBuilder(
+            index, context.ContextOuterClass.ConnectionId.getDefaultInstance());
+      }
+      /**
+       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+       */
+      public java.util.List<context.ContextOuterClass.ConnectionId.Builder> 
+           getConnectionIdsBuilderList() {
+        return getConnectionIdsFieldBuilder().getBuilderList();
+      }
+      private com.google.protobuf.RepeatedFieldBuilderV3<
+          context.ContextOuterClass.ConnectionId, context.ContextOuterClass.ConnectionId.Builder, context.ContextOuterClass.ConnectionIdOrBuilder> 
+          getConnectionIdsFieldBuilder() {
+        if (connectionIdsBuilder_ == null) {
+          connectionIdsBuilder_ = new com.google.protobuf.RepeatedFieldBuilderV3<
+              context.ContextOuterClass.ConnectionId, context.ContextOuterClass.ConnectionId.Builder, context.ContextOuterClass.ConnectionIdOrBuilder>(
+                  connectionIds_,
+                  ((bitField0_ & 0x00000001) != 0),
+                  getParentForChildren(),
+                  isClean());
+          connectionIds_ = null;
         }
-        onChanged();
-        return this;
+        return connectionIdsBuilder_;
       }
       @java.lang.Override
       public final Builder setUnknownFields(
@@ -29718,108 +30606,95 @@ public final class ContextOuterClass {
       }
 
 
-      // @@protoc_insertion_point(builder_scope:context.EndPoint)
+      // @@protoc_insertion_point(builder_scope:context.ConnectionIdList)
     }
 
-    // @@protoc_insertion_point(class_scope:context.EndPoint)
-    private static final context.ContextOuterClass.EndPoint DEFAULT_INSTANCE;
+    // @@protoc_insertion_point(class_scope:context.ConnectionIdList)
+    private static final context.ContextOuterClass.ConnectionIdList DEFAULT_INSTANCE;
     static {
-      DEFAULT_INSTANCE = new context.ContextOuterClass.EndPoint();
+      DEFAULT_INSTANCE = new context.ContextOuterClass.ConnectionIdList();
     }
 
-    public static context.ContextOuterClass.EndPoint getDefaultInstance() {
+    public static context.ContextOuterClass.ConnectionIdList getDefaultInstance() {
       return DEFAULT_INSTANCE;
     }
 
-    private static final com.google.protobuf.Parser<EndPoint>
-        PARSER = new com.google.protobuf.AbstractParser<EndPoint>() {
+    private static final com.google.protobuf.Parser<ConnectionIdList>
+        PARSER = new com.google.protobuf.AbstractParser<ConnectionIdList>() {
       @java.lang.Override
-      public EndPoint parsePartialFrom(
+      public ConnectionIdList parsePartialFrom(
           com.google.protobuf.CodedInputStream input,
           com.google.protobuf.ExtensionRegistryLite extensionRegistry)
           throws com.google.protobuf.InvalidProtocolBufferException {
-        return new EndPoint(input, extensionRegistry);
+        return new ConnectionIdList(input, extensionRegistry);
       }
     };
 
-    public static com.google.protobuf.Parser<EndPoint> parser() {
+    public static com.google.protobuf.Parser<ConnectionIdList> parser() {
       return PARSER;
     }
 
     @java.lang.Override
-    public com.google.protobuf.Parser<EndPoint> getParserForType() {
+    public com.google.protobuf.Parser<ConnectionIdList> getParserForType() {
       return PARSER;
     }
 
     @java.lang.Override
-    public context.ContextOuterClass.EndPoint getDefaultInstanceForType() {
+    public context.ContextOuterClass.ConnectionIdList getDefaultInstanceForType() {
       return DEFAULT_INSTANCE;
     }
 
   }
 
-  public interface ConfigRuleOrBuilder extends
-      // @@protoc_insertion_point(interface_extends:context.ConfigRule)
+  public interface ConnectionListOrBuilder extends
+      // @@protoc_insertion_point(interface_extends:context.ConnectionList)
       com.google.protobuf.MessageOrBuilder {
 
     /**
-     * <code>.context.ConfigActionEnum action = 1;</code>
-     * @return The enum numeric value on the wire for action.
-     */
-    int getActionValue();
-    /**
-     * <code>.context.ConfigActionEnum action = 1;</code>
-     * @return The action.
+     * <code>repeated .context.Connection connections = 1;</code>
      */
-    context.ContextOuterClass.ConfigActionEnum getAction();
-
+    java.util.List<context.ContextOuterClass.Connection> 
+        getConnectionsList();
     /**
-     * <code>string resource_key = 2;</code>
-     * @return The resourceKey.
+     * <code>repeated .context.Connection connections = 1;</code>
      */
-    java.lang.String getResourceKey();
+    context.ContextOuterClass.Connection getConnections(int index);
     /**
-     * <code>string resource_key = 2;</code>
-     * @return The bytes for resourceKey.
+     * <code>repeated .context.Connection connections = 1;</code>
      */
-    com.google.protobuf.ByteString
-        getResourceKeyBytes();
-
+    int getConnectionsCount();
     /**
-     * <code>string resource_value = 3;</code>
-     * @return The resourceValue.
+     * <code>repeated .context.Connection connections = 1;</code>
      */
-    java.lang.String getResourceValue();
+    java.util.List<? extends context.ContextOuterClass.ConnectionOrBuilder> 
+        getConnectionsOrBuilderList();
     /**
-     * <code>string resource_value = 3;</code>
-     * @return The bytes for resourceValue.
+     * <code>repeated .context.Connection connections = 1;</code>
      */
-    com.google.protobuf.ByteString
-        getResourceValueBytes();
+    context.ContextOuterClass.ConnectionOrBuilder getConnectionsOrBuilder(
+        int index);
   }
   /**
-   * Protobuf type {@code context.ConfigRule}
+   * Protobuf type {@code context.ConnectionList}
    */
-  public static final class ConfigRule extends
+  public static final class ConnectionList extends
       com.google.protobuf.GeneratedMessageV3 implements
-      // @@protoc_insertion_point(message_implements:context.ConfigRule)
-      ConfigRuleOrBuilder {
+      // @@protoc_insertion_point(message_implements:context.ConnectionList)
+      ConnectionListOrBuilder {
   private static final long serialVersionUID = 0L;
-    // Use ConfigRule.newBuilder() to construct.
-    private ConfigRule(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
+    // Use ConnectionList.newBuilder() to construct.
+    private ConnectionList(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
       super(builder);
     }
-    private ConfigRule() {
-      action_ = 0;
-      resourceKey_ = "";
-      resourceValue_ = "";
+    private ConnectionList() {
+      connections_ = java.util.Collections.emptyList();
     }
 
     @java.lang.Override
     @SuppressWarnings({"unused"})
     protected java.lang.Object newInstance(
         UnusedPrivateParameter unused) {
-      return new ConfigRule();
+      return new ConnectionList();
     }
 
     @java.lang.Override
@@ -29827,7 +30702,7 @@ public final class ContextOuterClass {
     getUnknownFields() {
       return this.unknownFields;
     }
-    private ConfigRule(
+    private ConnectionList(
         com.google.protobuf.CodedInputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
@@ -29835,6 +30710,7 @@ public final class ContextOuterClass {
       if (extensionRegistry == null) {
         throw new java.lang.NullPointerException();
       }
+      int mutable_bitField0_ = 0;
       com.google.protobuf.UnknownFieldSet.Builder unknownFields =
           com.google.protobuf.UnknownFieldSet.newBuilder();
       try {
@@ -29845,22 +30721,13 @@ public final class ContextOuterClass {
             case 0:
               done = true;
               break;
-            case 8: {
-              int rawValue = input.readEnum();
-
-              action_ = rawValue;
-              break;
-            }
-            case 18: {
-              java.lang.String s = input.readStringRequireUtf8();
-
-              resourceKey_ = s;
-              break;
-            }
-            case 26: {
-              java.lang.String s = input.readStringRequireUtf8();
-
-              resourceValue_ = s;
+            case 10: {
+              if (!((mutable_bitField0_ & 0x00000001) != 0)) {
+                connections_ = new java.util.ArrayList<context.ContextOuterClass.Connection>();
+                mutable_bitField0_ |= 0x00000001;
+              }
+              connections_.add(
+                  input.readMessage(context.ContextOuterClass.Connection.parser(), extensionRegistry));
               break;
             }
             default: {
@@ -29878,116 +30745,64 @@ public final class ContextOuterClass {
         throw new com.google.protobuf.InvalidProtocolBufferException(
             e).setUnfinishedMessage(this);
       } finally {
+        if (((mutable_bitField0_ & 0x00000001) != 0)) {
+          connections_ = java.util.Collections.unmodifiableList(connections_);
+        }
         this.unknownFields = unknownFields.build();
         makeExtensionsImmutable();
       }
     }
     public static final com.google.protobuf.Descriptors.Descriptor
         getDescriptor() {
-      return context.ContextOuterClass.internal_static_context_ConfigRule_descriptor;
+      return context.ContextOuterClass.internal_static_context_ConnectionList_descriptor;
     }
 
     @java.lang.Override
     protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
         internalGetFieldAccessorTable() {
-      return context.ContextOuterClass.internal_static_context_ConfigRule_fieldAccessorTable
+      return context.ContextOuterClass.internal_static_context_ConnectionList_fieldAccessorTable
           .ensureFieldAccessorsInitialized(
-              context.ContextOuterClass.ConfigRule.class, context.ContextOuterClass.ConfigRule.Builder.class);
+              context.ContextOuterClass.ConnectionList.class, context.ContextOuterClass.ConnectionList.Builder.class);
     }
 
-    public static final int ACTION_FIELD_NUMBER = 1;
-    private int action_;
-    /**
-     * <code>.context.ConfigActionEnum action = 1;</code>
-     * @return The enum numeric value on the wire for action.
-     */
-    @java.lang.Override public int getActionValue() {
-      return action_;
-    }
+    public static final int CONNECTIONS_FIELD_NUMBER = 1;
+    private java.util.List<context.ContextOuterClass.Connection> connections_;
     /**
-     * <code>.context.ConfigActionEnum action = 1;</code>
-     * @return The action.
+     * <code>repeated .context.Connection connections = 1;</code>
      */
-    @java.lang.Override public context.ContextOuterClass.ConfigActionEnum getAction() {
-      @SuppressWarnings("deprecation")
-      context.ContextOuterClass.ConfigActionEnum result = context.ContextOuterClass.ConfigActionEnum.valueOf(action_);
-      return result == null ? context.ContextOuterClass.ConfigActionEnum.UNRECOGNIZED : result;
+    @java.lang.Override
+    public java.util.List<context.ContextOuterClass.Connection> getConnectionsList() {
+      return connections_;
     }
-
-    public static final int RESOURCE_KEY_FIELD_NUMBER = 2;
-    private volatile java.lang.Object resourceKey_;
     /**
-     * <code>string resource_key = 2;</code>
-     * @return The resourceKey.
+     * <code>repeated .context.Connection connections = 1;</code>
      */
     @java.lang.Override
-    public java.lang.String getResourceKey() {
-      java.lang.Object ref = resourceKey_;
-      if (ref instanceof java.lang.String) {
-        return (java.lang.String) ref;
-      } else {
-        com.google.protobuf.ByteString bs = 
-            (com.google.protobuf.ByteString) ref;
-        java.lang.String s = bs.toStringUtf8();
-        resourceKey_ = s;
-        return s;
-      }
+    public java.util.List<? extends context.ContextOuterClass.ConnectionOrBuilder> 
+        getConnectionsOrBuilderList() {
+      return connections_;
     }
     /**
-     * <code>string resource_key = 2;</code>
-     * @return The bytes for resourceKey.
+     * <code>repeated .context.Connection connections = 1;</code>
      */
     @java.lang.Override
-    public com.google.protobuf.ByteString
-        getResourceKeyBytes() {
-      java.lang.Object ref = resourceKey_;
-      if (ref instanceof java.lang.String) {
-        com.google.protobuf.ByteString b = 
-            com.google.protobuf.ByteString.copyFromUtf8(
-                (java.lang.String) ref);
-        resourceKey_ = b;
-        return b;
-      } else {
-        return (com.google.protobuf.ByteString) ref;
-      }
+    public int getConnectionsCount() {
+      return connections_.size();
     }
-
-    public static final int RESOURCE_VALUE_FIELD_NUMBER = 3;
-    private volatile java.lang.Object resourceValue_;
     /**
-     * <code>string resource_value = 3;</code>
-     * @return The resourceValue.
+     * <code>repeated .context.Connection connections = 1;</code>
      */
     @java.lang.Override
-    public java.lang.String getResourceValue() {
-      java.lang.Object ref = resourceValue_;
-      if (ref instanceof java.lang.String) {
-        return (java.lang.String) ref;
-      } else {
-        com.google.protobuf.ByteString bs = 
-            (com.google.protobuf.ByteString) ref;
-        java.lang.String s = bs.toStringUtf8();
-        resourceValue_ = s;
-        return s;
-      }
+    public context.ContextOuterClass.Connection getConnections(int index) {
+      return connections_.get(index);
     }
     /**
-     * <code>string resource_value = 3;</code>
-     * @return The bytes for resourceValue.
+     * <code>repeated .context.Connection connections = 1;</code>
      */
     @java.lang.Override
-    public com.google.protobuf.ByteString
-        getResourceValueBytes() {
-      java.lang.Object ref = resourceValue_;
-      if (ref instanceof java.lang.String) {
-        com.google.protobuf.ByteString b = 
-            com.google.protobuf.ByteString.copyFromUtf8(
-                (java.lang.String) ref);
-        resourceValue_ = b;
-        return b;
-      } else {
-        return (com.google.protobuf.ByteString) ref;
-      }
+    public context.ContextOuterClass.ConnectionOrBuilder getConnectionsOrBuilder(
+        int index) {
+      return connections_.get(index);
     }
 
     private byte memoizedIsInitialized = -1;
@@ -30004,14 +30819,8 @@ public final class ContextOuterClass {
     @java.lang.Override
     public void writeTo(com.google.protobuf.CodedOutputStream output)
                         throws java.io.IOException {
-      if (action_ != context.ContextOuterClass.ConfigActionEnum.CONFIGACTION_UNDEFINED.getNumber()) {
-        output.writeEnum(1, action_);
-      }
-      if (!getResourceKeyBytes().isEmpty()) {
-        com.google.protobuf.GeneratedMessageV3.writeString(output, 2, resourceKey_);
-      }
-      if (!getResourceValueBytes().isEmpty()) {
-        com.google.protobuf.GeneratedMessageV3.writeString(output, 3, resourceValue_);
+      for (int i = 0; i < connections_.size(); i++) {
+        output.writeMessage(1, connections_.get(i));
       }
       unknownFields.writeTo(output);
     }
@@ -30022,15 +30831,9 @@ public final class ContextOuterClass {
       if (size != -1) return size;
 
       size = 0;
-      if (action_ != context.ContextOuterClass.ConfigActionEnum.CONFIGACTION_UNDEFINED.getNumber()) {
+      for (int i = 0; i < connections_.size(); i++) {
         size += com.google.protobuf.CodedOutputStream
-          .computeEnumSize(1, action_);
-      }
-      if (!getResourceKeyBytes().isEmpty()) {
-        size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, resourceKey_);
-      }
-      if (!getResourceValueBytes().isEmpty()) {
-        size += com.google.protobuf.GeneratedMessageV3.computeStringSize(3, resourceValue_);
+          .computeMessageSize(1, connections_.get(i));
       }
       size += unknownFields.getSerializedSize();
       memoizedSize = size;
@@ -30042,16 +30845,13 @@ public final class ContextOuterClass {
       if (obj == this) {
        return true;
       }
-      if (!(obj instanceof context.ContextOuterClass.ConfigRule)) {
+      if (!(obj instanceof context.ContextOuterClass.ConnectionList)) {
         return super.equals(obj);
       }
-      context.ContextOuterClass.ConfigRule other = (context.ContextOuterClass.ConfigRule) obj;
+      context.ContextOuterClass.ConnectionList other = (context.ContextOuterClass.ConnectionList) obj;
 
-      if (action_ != other.action_) return false;
-      if (!getResourceKey()
-          .equals(other.getResourceKey())) return false;
-      if (!getResourceValue()
-          .equals(other.getResourceValue())) return false;
+      if (!getConnectionsList()
+          .equals(other.getConnectionsList())) return false;
       if (!unknownFields.equals(other.unknownFields)) return false;
       return true;
     }
@@ -30063,80 +30863,78 @@ public final class ContextOuterClass {
       }
       int hash = 41;
       hash = (19 * hash) + getDescriptor().hashCode();
-      hash = (37 * hash) + ACTION_FIELD_NUMBER;
-      hash = (53 * hash) + action_;
-      hash = (37 * hash) + RESOURCE_KEY_FIELD_NUMBER;
-      hash = (53 * hash) + getResourceKey().hashCode();
-      hash = (37 * hash) + RESOURCE_VALUE_FIELD_NUMBER;
-      hash = (53 * hash) + getResourceValue().hashCode();
+      if (getConnectionsCount() > 0) {
+        hash = (37 * hash) + CONNECTIONS_FIELD_NUMBER;
+        hash = (53 * hash) + getConnectionsList().hashCode();
+      }
       hash = (29 * hash) + unknownFields.hashCode();
       memoizedHashCode = hash;
       return hash;
     }
 
-    public static context.ContextOuterClass.ConfigRule parseFrom(
+    public static context.ContextOuterClass.ConnectionList parseFrom(
         java.nio.ByteBuffer data)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data);
     }
-    public static context.ContextOuterClass.ConfigRule parseFrom(
+    public static context.ContextOuterClass.ConnectionList parseFrom(
         java.nio.ByteBuffer data,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data, extensionRegistry);
     }
-    public static context.ContextOuterClass.ConfigRule parseFrom(
+    public static context.ContextOuterClass.ConnectionList parseFrom(
         com.google.protobuf.ByteString data)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data);
     }
-    public static context.ContextOuterClass.ConfigRule parseFrom(
+    public static context.ContextOuterClass.ConnectionList parseFrom(
         com.google.protobuf.ByteString data,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data, extensionRegistry);
     }
-    public static context.ContextOuterClass.ConfigRule parseFrom(byte[] data)
+    public static context.ContextOuterClass.ConnectionList parseFrom(byte[] data)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data);
     }
-    public static context.ContextOuterClass.ConfigRule parseFrom(
+    public static context.ContextOuterClass.ConnectionList parseFrom(
         byte[] data,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data, extensionRegistry);
     }
-    public static context.ContextOuterClass.ConfigRule parseFrom(java.io.InputStream input)
+    public static context.ContextOuterClass.ConnectionList parseFrom(java.io.InputStream input)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseWithIOException(PARSER, input);
     }
-    public static context.ContextOuterClass.ConfigRule parseFrom(
+    public static context.ContextOuterClass.ConnectionList parseFrom(
         java.io.InputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseWithIOException(PARSER, input, extensionRegistry);
     }
-    public static context.ContextOuterClass.ConfigRule parseDelimitedFrom(java.io.InputStream input)
+    public static context.ContextOuterClass.ConnectionList parseDelimitedFrom(java.io.InputStream input)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseDelimitedWithIOException(PARSER, input);
     }
-    public static context.ContextOuterClass.ConfigRule parseDelimitedFrom(
+    public static context.ContextOuterClass.ConnectionList parseDelimitedFrom(
         java.io.InputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseDelimitedWithIOException(PARSER, input, extensionRegistry);
     }
-    public static context.ContextOuterClass.ConfigRule parseFrom(
+    public static context.ContextOuterClass.ConnectionList parseFrom(
         com.google.protobuf.CodedInputStream input)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseWithIOException(PARSER, input);
     }
-    public static context.ContextOuterClass.ConfigRule parseFrom(
+    public static context.ContextOuterClass.ConnectionList parseFrom(
         com.google.protobuf.CodedInputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws java.io.IOException {
@@ -30149,7 +30947,7 @@ public final class ContextOuterClass {
     public static Builder newBuilder() {
       return DEFAULT_INSTANCE.toBuilder();
     }
-    public static Builder newBuilder(context.ContextOuterClass.ConfigRule prototype) {
+    public static Builder newBuilder(context.ContextOuterClass.ConnectionList prototype) {
       return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
     }
     @java.lang.Override
@@ -30165,26 +30963,26 @@ public final class ContextOuterClass {
       return builder;
     }
     /**
-     * Protobuf type {@code context.ConfigRule}
+     * Protobuf type {@code context.ConnectionList}
      */
     public static final class Builder extends
         com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
-        // @@protoc_insertion_point(builder_implements:context.ConfigRule)
-        context.ContextOuterClass.ConfigRuleOrBuilder {
+        // @@protoc_insertion_point(builder_implements:context.ConnectionList)
+        context.ContextOuterClass.ConnectionListOrBuilder {
       public static final com.google.protobuf.Descriptors.Descriptor
           getDescriptor() {
-        return context.ContextOuterClass.internal_static_context_ConfigRule_descriptor;
+        return context.ContextOuterClass.internal_static_context_ConnectionList_descriptor;
       }
 
       @java.lang.Override
       protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
           internalGetFieldAccessorTable() {
-        return context.ContextOuterClass.internal_static_context_ConfigRule_fieldAccessorTable
+        return context.ContextOuterClass.internal_static_context_ConnectionList_fieldAccessorTable
             .ensureFieldAccessorsInitialized(
-                context.ContextOuterClass.ConfigRule.class, context.ContextOuterClass.ConfigRule.Builder.class);
+                context.ContextOuterClass.ConnectionList.class, context.ContextOuterClass.ConnectionList.Builder.class);
       }
 
-      // Construct using context.ContextOuterClass.ConfigRule.newBuilder()
+      // Construct using context.ContextOuterClass.ConnectionList.newBuilder()
       private Builder() {
         maybeForceBuilderInitialization();
       }
@@ -30197,34 +30995,35 @@ public final class ContextOuterClass {
       private void maybeForceBuilderInitialization() {
         if (com.google.protobuf.GeneratedMessageV3
                 .alwaysUseFieldBuilders) {
+          getConnectionsFieldBuilder();
         }
       }
       @java.lang.Override
       public Builder clear() {
         super.clear();
-        action_ = 0;
-
-        resourceKey_ = "";
-
-        resourceValue_ = "";
-
+        if (connectionsBuilder_ == null) {
+          connections_ = java.util.Collections.emptyList();
+          bitField0_ = (bitField0_ & ~0x00000001);
+        } else {
+          connectionsBuilder_.clear();
+        }
         return this;
       }
 
       @java.lang.Override
       public com.google.protobuf.Descriptors.Descriptor
           getDescriptorForType() {
-        return context.ContextOuterClass.internal_static_context_ConfigRule_descriptor;
+        return context.ContextOuterClass.internal_static_context_ConnectionList_descriptor;
       }
 
       @java.lang.Override
-      public context.ContextOuterClass.ConfigRule getDefaultInstanceForType() {
-        return context.ContextOuterClass.ConfigRule.getDefaultInstance();
+      public context.ContextOuterClass.ConnectionList getDefaultInstanceForType() {
+        return context.ContextOuterClass.ConnectionList.getDefaultInstance();
       }
 
       @java.lang.Override
-      public context.ContextOuterClass.ConfigRule build() {
-        context.ContextOuterClass.ConfigRule result = buildPartial();
+      public context.ContextOuterClass.ConnectionList build() {
+        context.ContextOuterClass.ConnectionList result = buildPartial();
         if (!result.isInitialized()) {
           throw newUninitializedMessageException(result);
         }
@@ -30232,11 +31031,18 @@ public final class ContextOuterClass {
       }
 
       @java.lang.Override
-      public context.ContextOuterClass.ConfigRule buildPartial() {
-        context.ContextOuterClass.ConfigRule result = new context.ContextOuterClass.ConfigRule(this);
-        result.action_ = action_;
-        result.resourceKey_ = resourceKey_;
-        result.resourceValue_ = resourceValue_;
+      public context.ContextOuterClass.ConnectionList buildPartial() {
+        context.ContextOuterClass.ConnectionList result = new context.ContextOuterClass.ConnectionList(this);
+        int from_bitField0_ = bitField0_;
+        if (connectionsBuilder_ == null) {
+          if (((bitField0_ & 0x00000001) != 0)) {
+            connections_ = java.util.Collections.unmodifiableList(connections_);
+            bitField0_ = (bitField0_ & ~0x00000001);
+          }
+          result.connections_ = connections_;
+        } else {
+          result.connections_ = connectionsBuilder_.build();
+        }
         onBuilt();
         return result;
       }
@@ -30275,26 +31081,41 @@ public final class ContextOuterClass {
       }
       @java.lang.Override
       public Builder mergeFrom(com.google.protobuf.Message other) {
-        if (other instanceof context.ContextOuterClass.ConfigRule) {
-          return mergeFrom((context.ContextOuterClass.ConfigRule)other);
+        if (other instanceof context.ContextOuterClass.ConnectionList) {
+          return mergeFrom((context.ContextOuterClass.ConnectionList)other);
         } else {
           super.mergeFrom(other);
           return this;
         }
       }
 
-      public Builder mergeFrom(context.ContextOuterClass.ConfigRule other) {
-        if (other == context.ContextOuterClass.ConfigRule.getDefaultInstance()) return this;
-        if (other.action_ != 0) {
-          setActionValue(other.getActionValue());
-        }
-        if (!other.getResourceKey().isEmpty()) {
-          resourceKey_ = other.resourceKey_;
-          onChanged();
-        }
-        if (!other.getResourceValue().isEmpty()) {
-          resourceValue_ = other.resourceValue_;
-          onChanged();
+      public Builder mergeFrom(context.ContextOuterClass.ConnectionList other) {
+        if (other == context.ContextOuterClass.ConnectionList.getDefaultInstance()) return this;
+        if (connectionsBuilder_ == null) {
+          if (!other.connections_.isEmpty()) {
+            if (connections_.isEmpty()) {
+              connections_ = other.connections_;
+              bitField0_ = (bitField0_ & ~0x00000001);
+            } else {
+              ensureConnectionsIsMutable();
+              connections_.addAll(other.connections_);
+            }
+            onChanged();
+          }
+        } else {
+          if (!other.connections_.isEmpty()) {
+            if (connectionsBuilder_.isEmpty()) {
+              connectionsBuilder_.dispose();
+              connectionsBuilder_ = null;
+              connections_ = other.connections_;
+              bitField0_ = (bitField0_ & ~0x00000001);
+              connectionsBuilder_ = 
+                com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders ?
+                   getConnectionsFieldBuilder() : null;
+            } else {
+              connectionsBuilder_.addAllMessages(other.connections_);
+            }
+          }
         }
         this.mergeUnknownFields(other.unknownFields);
         onChanged();
@@ -30311,11 +31132,11 @@ public final class ContextOuterClass {
           com.google.protobuf.CodedInputStream input,
           com.google.protobuf.ExtensionRegistryLite extensionRegistry)
           throws java.io.IOException {
-        context.ContextOuterClass.ConfigRule parsedMessage = null;
+        context.ContextOuterClass.ConnectionList parsedMessage = null;
         try {
           parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
         } catch (com.google.protobuf.InvalidProtocolBufferException e) {
-          parsedMessage = (context.ContextOuterClass.ConfigRule) e.getUnfinishedMessage();
+          parsedMessage = (context.ContextOuterClass.ConnectionList) e.getUnfinishedMessage();
           throw e.unwrapIOException();
         } finally {
           if (parsedMessage != null) {
@@ -30324,211 +31145,246 @@ public final class ContextOuterClass {
         }
         return this;
       }
+      private int bitField0_;
+
+      private java.util.List<context.ContextOuterClass.Connection> connections_ =
+        java.util.Collections.emptyList();
+      private void ensureConnectionsIsMutable() {
+        if (!((bitField0_ & 0x00000001) != 0)) {
+          connections_ = new java.util.ArrayList<context.ContextOuterClass.Connection>(connections_);
+          bitField0_ |= 0x00000001;
+         }
+      }
+
+      private com.google.protobuf.RepeatedFieldBuilderV3<
+          context.ContextOuterClass.Connection, context.ContextOuterClass.Connection.Builder, context.ContextOuterClass.ConnectionOrBuilder> connectionsBuilder_;
 
-      private int action_ = 0;
       /**
-       * <code>.context.ConfigActionEnum action = 1;</code>
-       * @return The enum numeric value on the wire for action.
+       * <code>repeated .context.Connection connections = 1;</code>
        */
-      @java.lang.Override public int getActionValue() {
-        return action_;
+      public java.util.List<context.ContextOuterClass.Connection> getConnectionsList() {
+        if (connectionsBuilder_ == null) {
+          return java.util.Collections.unmodifiableList(connections_);
+        } else {
+          return connectionsBuilder_.getMessageList();
+        }
       }
       /**
-       * <code>.context.ConfigActionEnum action = 1;</code>
-       * @param value The enum numeric value on the wire for action to set.
-       * @return This builder for chaining.
+       * <code>repeated .context.Connection connections = 1;</code>
        */
-      public Builder setActionValue(int value) {
-        
-        action_ = value;
-        onChanged();
+      public int getConnectionsCount() {
+        if (connectionsBuilder_ == null) {
+          return connections_.size();
+        } else {
+          return connectionsBuilder_.getCount();
+        }
+      }
+      /**
+       * <code>repeated .context.Connection connections = 1;</code>
+       */
+      public context.ContextOuterClass.Connection getConnections(int index) {
+        if (connectionsBuilder_ == null) {
+          return connections_.get(index);
+        } else {
+          return connectionsBuilder_.getMessage(index);
+        }
+      }
+      /**
+       * <code>repeated .context.Connection connections = 1;</code>
+       */
+      public Builder setConnections(
+          int index, context.ContextOuterClass.Connection value) {
+        if (connectionsBuilder_ == null) {
+          if (value == null) {
+            throw new NullPointerException();
+          }
+          ensureConnectionsIsMutable();
+          connections_.set(index, value);
+          onChanged();
+        } else {
+          connectionsBuilder_.setMessage(index, value);
+        }
         return this;
       }
       /**
-       * <code>.context.ConfigActionEnum action = 1;</code>
-       * @return The action.
+       * <code>repeated .context.Connection connections = 1;</code>
        */
-      @java.lang.Override
-      public context.ContextOuterClass.ConfigActionEnum getAction() {
-        @SuppressWarnings("deprecation")
-        context.ContextOuterClass.ConfigActionEnum result = context.ContextOuterClass.ConfigActionEnum.valueOf(action_);
-        return result == null ? context.ContextOuterClass.ConfigActionEnum.UNRECOGNIZED : result;
+      public Builder setConnections(
+          int index, context.ContextOuterClass.Connection.Builder builderForValue) {
+        if (connectionsBuilder_ == null) {
+          ensureConnectionsIsMutable();
+          connections_.set(index, builderForValue.build());
+          onChanged();
+        } else {
+          connectionsBuilder_.setMessage(index, builderForValue.build());
+        }
+        return this;
       }
       /**
-       * <code>.context.ConfigActionEnum action = 1;</code>
-       * @param value The action to set.
-       * @return This builder for chaining.
+       * <code>repeated .context.Connection connections = 1;</code>
        */
-      public Builder setAction(context.ContextOuterClass.ConfigActionEnum value) {
-        if (value == null) {
-          throw new NullPointerException();
+      public Builder addConnections(context.ContextOuterClass.Connection value) {
+        if (connectionsBuilder_ == null) {
+          if (value == null) {
+            throw new NullPointerException();
+          }
+          ensureConnectionsIsMutable();
+          connections_.add(value);
+          onChanged();
+        } else {
+          connectionsBuilder_.addMessage(value);
         }
-        
-        action_ = value.getNumber();
-        onChanged();
         return this;
       }
       /**
-       * <code>.context.ConfigActionEnum action = 1;</code>
-       * @return This builder for chaining.
+       * <code>repeated .context.Connection connections = 1;</code>
        */
-      public Builder clearAction() {
-        
-        action_ = 0;
-        onChanged();
+      public Builder addConnections(
+          int index, context.ContextOuterClass.Connection value) {
+        if (connectionsBuilder_ == null) {
+          if (value == null) {
+            throw new NullPointerException();
+          }
+          ensureConnectionsIsMutable();
+          connections_.add(index, value);
+          onChanged();
+        } else {
+          connectionsBuilder_.addMessage(index, value);
+        }
         return this;
       }
-
-      private java.lang.Object resourceKey_ = "";
       /**
-       * <code>string resource_key = 2;</code>
-       * @return The resourceKey.
+       * <code>repeated .context.Connection connections = 1;</code>
        */
-      public java.lang.String getResourceKey() {
-        java.lang.Object ref = resourceKey_;
-        if (!(ref instanceof java.lang.String)) {
-          com.google.protobuf.ByteString bs =
-              (com.google.protobuf.ByteString) ref;
-          java.lang.String s = bs.toStringUtf8();
-          resourceKey_ = s;
-          return s;
+      public Builder addConnections(
+          context.ContextOuterClass.Connection.Builder builderForValue) {
+        if (connectionsBuilder_ == null) {
+          ensureConnectionsIsMutable();
+          connections_.add(builderForValue.build());
+          onChanged();
         } else {
-          return (java.lang.String) ref;
+          connectionsBuilder_.addMessage(builderForValue.build());
         }
+        return this;
       }
       /**
-       * <code>string resource_key = 2;</code>
-       * @return The bytes for resourceKey.
+       * <code>repeated .context.Connection connections = 1;</code>
        */
-      public com.google.protobuf.ByteString
-          getResourceKeyBytes() {
-        java.lang.Object ref = resourceKey_;
-        if (ref instanceof String) {
-          com.google.protobuf.ByteString b = 
-              com.google.protobuf.ByteString.copyFromUtf8(
-                  (java.lang.String) ref);
-          resourceKey_ = b;
-          return b;
+      public Builder addConnections(
+          int index, context.ContextOuterClass.Connection.Builder builderForValue) {
+        if (connectionsBuilder_ == null) {
+          ensureConnectionsIsMutable();
+          connections_.add(index, builderForValue.build());
+          onChanged();
         } else {
-          return (com.google.protobuf.ByteString) ref;
+          connectionsBuilder_.addMessage(index, builderForValue.build());
         }
+        return this;
       }
       /**
-       * <code>string resource_key = 2;</code>
-       * @param value The resourceKey to set.
-       * @return This builder for chaining.
+       * <code>repeated .context.Connection connections = 1;</code>
        */
-      public Builder setResourceKey(
-          java.lang.String value) {
-        if (value == null) {
-    throw new NullPointerException();
-  }
-  
-        resourceKey_ = value;
-        onChanged();
+      public Builder addAllConnections(
+          java.lang.Iterable<? extends context.ContextOuterClass.Connection> values) {
+        if (connectionsBuilder_ == null) {
+          ensureConnectionsIsMutable();
+          com.google.protobuf.AbstractMessageLite.Builder.addAll(
+              values, connections_);
+          onChanged();
+        } else {
+          connectionsBuilder_.addAllMessages(values);
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .context.Connection connections = 1;</code>
+       */
+      public Builder clearConnections() {
+        if (connectionsBuilder_ == null) {
+          connections_ = java.util.Collections.emptyList();
+          bitField0_ = (bitField0_ & ~0x00000001);
+          onChanged();
+        } else {
+          connectionsBuilder_.clear();
+        }
         return this;
       }
       /**
-       * <code>string resource_key = 2;</code>
-       * @return This builder for chaining.
+       * <code>repeated .context.Connection connections = 1;</code>
        */
-      public Builder clearResourceKey() {
-        
-        resourceKey_ = getDefaultInstance().getResourceKey();
-        onChanged();
+      public Builder removeConnections(int index) {
+        if (connectionsBuilder_ == null) {
+          ensureConnectionsIsMutable();
+          connections_.remove(index);
+          onChanged();
+        } else {
+          connectionsBuilder_.remove(index);
+        }
         return this;
       }
       /**
-       * <code>string resource_key = 2;</code>
-       * @param value The bytes for resourceKey to set.
-       * @return This builder for chaining.
+       * <code>repeated .context.Connection connections = 1;</code>
        */
-      public Builder setResourceKeyBytes(
-          com.google.protobuf.ByteString value) {
-        if (value == null) {
-    throw new NullPointerException();
-  }
-  checkByteStringIsUtf8(value);
-        
-        resourceKey_ = value;
-        onChanged();
-        return this;
+      public context.ContextOuterClass.Connection.Builder getConnectionsBuilder(
+          int index) {
+        return getConnectionsFieldBuilder().getBuilder(index);
       }
-
-      private java.lang.Object resourceValue_ = "";
       /**
-       * <code>string resource_value = 3;</code>
-       * @return The resourceValue.
+       * <code>repeated .context.Connection connections = 1;</code>
        */
-      public java.lang.String getResourceValue() {
-        java.lang.Object ref = resourceValue_;
-        if (!(ref instanceof java.lang.String)) {
-          com.google.protobuf.ByteString bs =
-              (com.google.protobuf.ByteString) ref;
-          java.lang.String s = bs.toStringUtf8();
-          resourceValue_ = s;
-          return s;
-        } else {
-          return (java.lang.String) ref;
+      public context.ContextOuterClass.ConnectionOrBuilder getConnectionsOrBuilder(
+          int index) {
+        if (connectionsBuilder_ == null) {
+          return connections_.get(index);  } else {
+          return connectionsBuilder_.getMessageOrBuilder(index);
         }
       }
       /**
-       * <code>string resource_value = 3;</code>
-       * @return The bytes for resourceValue.
+       * <code>repeated .context.Connection connections = 1;</code>
        */
-      public com.google.protobuf.ByteString
-          getResourceValueBytes() {
-        java.lang.Object ref = resourceValue_;
-        if (ref instanceof String) {
-          com.google.protobuf.ByteString b = 
-              com.google.protobuf.ByteString.copyFromUtf8(
-                  (java.lang.String) ref);
-          resourceValue_ = b;
-          return b;
+      public java.util.List<? extends context.ContextOuterClass.ConnectionOrBuilder> 
+           getConnectionsOrBuilderList() {
+        if (connectionsBuilder_ != null) {
+          return connectionsBuilder_.getMessageOrBuilderList();
         } else {
-          return (com.google.protobuf.ByteString) ref;
+          return java.util.Collections.unmodifiableList(connections_);
         }
       }
       /**
-       * <code>string resource_value = 3;</code>
-       * @param value The resourceValue to set.
-       * @return This builder for chaining.
+       * <code>repeated .context.Connection connections = 1;</code>
        */
-      public Builder setResourceValue(
-          java.lang.String value) {
-        if (value == null) {
-    throw new NullPointerException();
-  }
-  
-        resourceValue_ = value;
-        onChanged();
-        return this;
+      public context.ContextOuterClass.Connection.Builder addConnectionsBuilder() {
+        return getConnectionsFieldBuilder().addBuilder(
+            context.ContextOuterClass.Connection.getDefaultInstance());
       }
       /**
-       * <code>string resource_value = 3;</code>
-       * @return This builder for chaining.
+       * <code>repeated .context.Connection connections = 1;</code>
        */
-      public Builder clearResourceValue() {
-        
-        resourceValue_ = getDefaultInstance().getResourceValue();
-        onChanged();
-        return this;
+      public context.ContextOuterClass.Connection.Builder addConnectionsBuilder(
+          int index) {
+        return getConnectionsFieldBuilder().addBuilder(
+            index, context.ContextOuterClass.Connection.getDefaultInstance());
       }
       /**
-       * <code>string resource_value = 3;</code>
-       * @param value The bytes for resourceValue to set.
-       * @return This builder for chaining.
+       * <code>repeated .context.Connection connections = 1;</code>
        */
-      public Builder setResourceValueBytes(
-          com.google.protobuf.ByteString value) {
-        if (value == null) {
-    throw new NullPointerException();
-  }
-  checkByteStringIsUtf8(value);
-        
-        resourceValue_ = value;
-        onChanged();
-        return this;
+      public java.util.List<context.ContextOuterClass.Connection.Builder> 
+           getConnectionsBuilderList() {
+        return getConnectionsFieldBuilder().getBuilderList();
+      }
+      private com.google.protobuf.RepeatedFieldBuilderV3<
+          context.ContextOuterClass.Connection, context.ContextOuterClass.Connection.Builder, context.ContextOuterClass.ConnectionOrBuilder> 
+          getConnectionsFieldBuilder() {
+        if (connectionsBuilder_ == null) {
+          connectionsBuilder_ = new com.google.protobuf.RepeatedFieldBuilderV3<
+              context.ContextOuterClass.Connection, context.ContextOuterClass.Connection.Builder, context.ContextOuterClass.ConnectionOrBuilder>(
+                  connections_,
+                  ((bitField0_ & 0x00000001) != 0),
+                  getParentForChildren(),
+                  isClean());
+          connections_ = null;
+        }
+        return connectionsBuilder_;
       }
       @java.lang.Override
       public final Builder setUnknownFields(
@@ -30543,100 +31399,100 @@ public final class ContextOuterClass {
       }
 
 
-      // @@protoc_insertion_point(builder_scope:context.ConfigRule)
+      // @@protoc_insertion_point(builder_scope:context.ConnectionList)
     }
 
-    // @@protoc_insertion_point(class_scope:context.ConfigRule)
-    private static final context.ContextOuterClass.ConfigRule DEFAULT_INSTANCE;
+    // @@protoc_insertion_point(class_scope:context.ConnectionList)
+    private static final context.ContextOuterClass.ConnectionList DEFAULT_INSTANCE;
     static {
-      DEFAULT_INSTANCE = new context.ContextOuterClass.ConfigRule();
+      DEFAULT_INSTANCE = new context.ContextOuterClass.ConnectionList();
     }
 
-    public static context.ContextOuterClass.ConfigRule getDefaultInstance() {
+    public static context.ContextOuterClass.ConnectionList getDefaultInstance() {
       return DEFAULT_INSTANCE;
     }
 
-    private static final com.google.protobuf.Parser<ConfigRule>
-        PARSER = new com.google.protobuf.AbstractParser<ConfigRule>() {
+    private static final com.google.protobuf.Parser<ConnectionList>
+        PARSER = new com.google.protobuf.AbstractParser<ConnectionList>() {
       @java.lang.Override
-      public ConfigRule parsePartialFrom(
+      public ConnectionList parsePartialFrom(
           com.google.protobuf.CodedInputStream input,
           com.google.protobuf.ExtensionRegistryLite extensionRegistry)
           throws com.google.protobuf.InvalidProtocolBufferException {
-        return new ConfigRule(input, extensionRegistry);
+        return new ConnectionList(input, extensionRegistry);
       }
     };
 
-    public static com.google.protobuf.Parser<ConfigRule> parser() {
+    public static com.google.protobuf.Parser<ConnectionList> parser() {
       return PARSER;
     }
 
     @java.lang.Override
-    public com.google.protobuf.Parser<ConfigRule> getParserForType() {
+    public com.google.protobuf.Parser<ConnectionList> getParserForType() {
       return PARSER;
     }
 
     @java.lang.Override
-    public context.ContextOuterClass.ConfigRule getDefaultInstanceForType() {
+    public context.ContextOuterClass.ConnectionList getDefaultInstanceForType() {
       return DEFAULT_INSTANCE;
     }
 
   }
 
-  public interface ConstraintOrBuilder extends
-      // @@protoc_insertion_point(interface_extends:context.Constraint)
+  public interface ConnectionEventOrBuilder extends
+      // @@protoc_insertion_point(interface_extends:context.ConnectionEvent)
       com.google.protobuf.MessageOrBuilder {
 
     /**
-     * <code>string constraint_type = 1;</code>
-     * @return The constraintType.
+     * <code>.context.Event event = 1;</code>
+     * @return Whether the event field is set.
      */
-    java.lang.String getConstraintType();
+    boolean hasEvent();
     /**
-     * <code>string constraint_type = 1;</code>
-     * @return The bytes for constraintType.
+     * <code>.context.Event event = 1;</code>
+     * @return The event.
      */
-    com.google.protobuf.ByteString
-        getConstraintTypeBytes();
+    context.ContextOuterClass.Event getEvent();
+    /**
+     * <code>.context.Event event = 1;</code>
+     */
+    context.ContextOuterClass.EventOrBuilder getEventOrBuilder();
 
     /**
-     * <code>string constraint_value = 2;</code>
-     * @return The constraintValue.
+     * <code>.context.ConnectionId connection_id = 2;</code>
+     * @return Whether the connectionId field is set.
      */
-    java.lang.String getConstraintValue();
+    boolean hasConnectionId();
     /**
-     * <code>string constraint_value = 2;</code>
-     * @return The bytes for constraintValue.
+     * <code>.context.ConnectionId connection_id = 2;</code>
+     * @return The connectionId.
      */
-    com.google.protobuf.ByteString
-        getConstraintValueBytes();
+    context.ContextOuterClass.ConnectionId getConnectionId();
+    /**
+     * <code>.context.ConnectionId connection_id = 2;</code>
+     */
+    context.ContextOuterClass.ConnectionIdOrBuilder getConnectionIdOrBuilder();
   }
   /**
-   * <pre>
-   * ----- Constraint ----------------------------------------------------------------------------------------------------
-   * </pre>
-   *
-   * Protobuf type {@code context.Constraint}
+   * Protobuf type {@code context.ConnectionEvent}
    */
-  public static final class Constraint extends
+  public static final class ConnectionEvent extends
       com.google.protobuf.GeneratedMessageV3 implements
-      // @@protoc_insertion_point(message_implements:context.Constraint)
-      ConstraintOrBuilder {
+      // @@protoc_insertion_point(message_implements:context.ConnectionEvent)
+      ConnectionEventOrBuilder {
   private static final long serialVersionUID = 0L;
-    // Use Constraint.newBuilder() to construct.
-    private Constraint(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
+    // Use ConnectionEvent.newBuilder() to construct.
+    private ConnectionEvent(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
       super(builder);
     }
-    private Constraint() {
-      constraintType_ = "";
-      constraintValue_ = "";
+    private ConnectionEvent() {
     }
 
     @java.lang.Override
     @SuppressWarnings({"unused"})
     protected java.lang.Object newInstance(
         UnusedPrivateParameter unused) {
-      return new Constraint();
+      return new ConnectionEvent();
     }
 
     @java.lang.Override
@@ -30644,7 +31500,7 @@ public final class ContextOuterClass {
     getUnknownFields() {
       return this.unknownFields;
     }
-    private Constraint(
+    private ConnectionEvent(
         com.google.protobuf.CodedInputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
@@ -30663,15 +31519,29 @@ public final class ContextOuterClass {
               done = true;
               break;
             case 10: {
-              java.lang.String s = input.readStringRequireUtf8();
+              context.ContextOuterClass.Event.Builder subBuilder = null;
+              if (event_ != null) {
+                subBuilder = event_.toBuilder();
+              }
+              event_ = input.readMessage(context.ContextOuterClass.Event.parser(), extensionRegistry);
+              if (subBuilder != null) {
+                subBuilder.mergeFrom(event_);
+                event_ = subBuilder.buildPartial();
+              }
 
-              constraintType_ = s;
               break;
             }
             case 18: {
-              java.lang.String s = input.readStringRequireUtf8();
+              context.ContextOuterClass.ConnectionId.Builder subBuilder = null;
+              if (connectionId_ != null) {
+                subBuilder = connectionId_.toBuilder();
+              }
+              connectionId_ = input.readMessage(context.ContextOuterClass.ConnectionId.parser(), extensionRegistry);
+              if (subBuilder != null) {
+                subBuilder.mergeFrom(connectionId_);
+                connectionId_ = subBuilder.buildPartial();
+              }
 
-              constraintValue_ = s;
               break;
             }
             default: {
@@ -30695,91 +31565,67 @@ public final class ContextOuterClass {
     }
     public static final com.google.protobuf.Descriptors.Descriptor
         getDescriptor() {
-      return context.ContextOuterClass.internal_static_context_Constraint_descriptor;
+      return context.ContextOuterClass.internal_static_context_ConnectionEvent_descriptor;
     }
 
     @java.lang.Override
     protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
         internalGetFieldAccessorTable() {
-      return context.ContextOuterClass.internal_static_context_Constraint_fieldAccessorTable
+      return context.ContextOuterClass.internal_static_context_ConnectionEvent_fieldAccessorTable
           .ensureFieldAccessorsInitialized(
-              context.ContextOuterClass.Constraint.class, context.ContextOuterClass.Constraint.Builder.class);
+              context.ContextOuterClass.ConnectionEvent.class, context.ContextOuterClass.ConnectionEvent.Builder.class);
     }
 
-    public static final int CONSTRAINT_TYPE_FIELD_NUMBER = 1;
-    private volatile java.lang.Object constraintType_;
+    public static final int EVENT_FIELD_NUMBER = 1;
+    private context.ContextOuterClass.Event event_;
     /**
-     * <code>string constraint_type = 1;</code>
-     * @return The constraintType.
+     * <code>.context.Event event = 1;</code>
+     * @return Whether the event field is set.
+     */
+    @java.lang.Override
+    public boolean hasEvent() {
+      return event_ != null;
+    }
+    /**
+     * <code>.context.Event event = 1;</code>
+     * @return The event.
+     */
+    @java.lang.Override
+    public context.ContextOuterClass.Event getEvent() {
+      return event_ == null ? context.ContextOuterClass.Event.getDefaultInstance() : event_;
+    }
+    /**
+     * <code>.context.Event event = 1;</code>
      */
     @java.lang.Override
-    public java.lang.String getConstraintType() {
-      java.lang.Object ref = constraintType_;
-      if (ref instanceof java.lang.String) {
-        return (java.lang.String) ref;
-      } else {
-        com.google.protobuf.ByteString bs = 
-            (com.google.protobuf.ByteString) ref;
-        java.lang.String s = bs.toStringUtf8();
-        constraintType_ = s;
-        return s;
-      }
+    public context.ContextOuterClass.EventOrBuilder getEventOrBuilder() {
+      return getEvent();
     }
+
+    public static final int CONNECTION_ID_FIELD_NUMBER = 2;
+    private context.ContextOuterClass.ConnectionId connectionId_;
     /**
-     * <code>string constraint_type = 1;</code>
-     * @return The bytes for constraintType.
+     * <code>.context.ConnectionId connection_id = 2;</code>
+     * @return Whether the connectionId field is set.
      */
     @java.lang.Override
-    public com.google.protobuf.ByteString
-        getConstraintTypeBytes() {
-      java.lang.Object ref = constraintType_;
-      if (ref instanceof java.lang.String) {
-        com.google.protobuf.ByteString b = 
-            com.google.protobuf.ByteString.copyFromUtf8(
-                (java.lang.String) ref);
-        constraintType_ = b;
-        return b;
-      } else {
-        return (com.google.protobuf.ByteString) ref;
-      }
+    public boolean hasConnectionId() {
+      return connectionId_ != null;
     }
-
-    public static final int CONSTRAINT_VALUE_FIELD_NUMBER = 2;
-    private volatile java.lang.Object constraintValue_;
     /**
-     * <code>string constraint_value = 2;</code>
-     * @return The constraintValue.
+     * <code>.context.ConnectionId connection_id = 2;</code>
+     * @return The connectionId.
      */
     @java.lang.Override
-    public java.lang.String getConstraintValue() {
-      java.lang.Object ref = constraintValue_;
-      if (ref instanceof java.lang.String) {
-        return (java.lang.String) ref;
-      } else {
-        com.google.protobuf.ByteString bs = 
-            (com.google.protobuf.ByteString) ref;
-        java.lang.String s = bs.toStringUtf8();
-        constraintValue_ = s;
-        return s;
-      }
+    public context.ContextOuterClass.ConnectionId getConnectionId() {
+      return connectionId_ == null ? context.ContextOuterClass.ConnectionId.getDefaultInstance() : connectionId_;
     }
     /**
-     * <code>string constraint_value = 2;</code>
-     * @return The bytes for constraintValue.
+     * <code>.context.ConnectionId connection_id = 2;</code>
      */
     @java.lang.Override
-    public com.google.protobuf.ByteString
-        getConstraintValueBytes() {
-      java.lang.Object ref = constraintValue_;
-      if (ref instanceof java.lang.String) {
-        com.google.protobuf.ByteString b = 
-            com.google.protobuf.ByteString.copyFromUtf8(
-                (java.lang.String) ref);
-        constraintValue_ = b;
-        return b;
-      } else {
-        return (com.google.protobuf.ByteString) ref;
-      }
+    public context.ContextOuterClass.ConnectionIdOrBuilder getConnectionIdOrBuilder() {
+      return getConnectionId();
     }
 
     private byte memoizedIsInitialized = -1;
@@ -30796,11 +31642,11 @@ public final class ContextOuterClass {
     @java.lang.Override
     public void writeTo(com.google.protobuf.CodedOutputStream output)
                         throws java.io.IOException {
-      if (!getConstraintTypeBytes().isEmpty()) {
-        com.google.protobuf.GeneratedMessageV3.writeString(output, 1, constraintType_);
+      if (event_ != null) {
+        output.writeMessage(1, getEvent());
       }
-      if (!getConstraintValueBytes().isEmpty()) {
-        com.google.protobuf.GeneratedMessageV3.writeString(output, 2, constraintValue_);
+      if (connectionId_ != null) {
+        output.writeMessage(2, getConnectionId());
       }
       unknownFields.writeTo(output);
     }
@@ -30811,11 +31657,13 @@ public final class ContextOuterClass {
       if (size != -1) return size;
 
       size = 0;
-      if (!getConstraintTypeBytes().isEmpty()) {
-        size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, constraintType_);
+      if (event_ != null) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeMessageSize(1, getEvent());
       }
-      if (!getConstraintValueBytes().isEmpty()) {
-        size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, constraintValue_);
+      if (connectionId_ != null) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeMessageSize(2, getConnectionId());
       }
       size += unknownFields.getSerializedSize();
       memoizedSize = size;
@@ -30827,15 +31675,21 @@ public final class ContextOuterClass {
       if (obj == this) {
        return true;
       }
-      if (!(obj instanceof context.ContextOuterClass.Constraint)) {
+      if (!(obj instanceof context.ContextOuterClass.ConnectionEvent)) {
         return super.equals(obj);
       }
-      context.ContextOuterClass.Constraint other = (context.ContextOuterClass.Constraint) obj;
+      context.ContextOuterClass.ConnectionEvent other = (context.ContextOuterClass.ConnectionEvent) obj;
 
-      if (!getConstraintType()
-          .equals(other.getConstraintType())) return false;
-      if (!getConstraintValue()
-          .equals(other.getConstraintValue())) return false;
+      if (hasEvent() != other.hasEvent()) return false;
+      if (hasEvent()) {
+        if (!getEvent()
+            .equals(other.getEvent())) return false;
+      }
+      if (hasConnectionId() != other.hasConnectionId()) return false;
+      if (hasConnectionId()) {
+        if (!getConnectionId()
+            .equals(other.getConnectionId())) return false;
+      }
       if (!unknownFields.equals(other.unknownFields)) return false;
       return true;
     }
@@ -30847,78 +31701,82 @@ public final class ContextOuterClass {
       }
       int hash = 41;
       hash = (19 * hash) + getDescriptor().hashCode();
-      hash = (37 * hash) + CONSTRAINT_TYPE_FIELD_NUMBER;
-      hash = (53 * hash) + getConstraintType().hashCode();
-      hash = (37 * hash) + CONSTRAINT_VALUE_FIELD_NUMBER;
-      hash = (53 * hash) + getConstraintValue().hashCode();
+      if (hasEvent()) {
+        hash = (37 * hash) + EVENT_FIELD_NUMBER;
+        hash = (53 * hash) + getEvent().hashCode();
+      }
+      if (hasConnectionId()) {
+        hash = (37 * hash) + CONNECTION_ID_FIELD_NUMBER;
+        hash = (53 * hash) + getConnectionId().hashCode();
+      }
       hash = (29 * hash) + unknownFields.hashCode();
       memoizedHashCode = hash;
       return hash;
     }
 
-    public static context.ContextOuterClass.Constraint parseFrom(
+    public static context.ContextOuterClass.ConnectionEvent parseFrom(
         java.nio.ByteBuffer data)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data);
     }
-    public static context.ContextOuterClass.Constraint parseFrom(
+    public static context.ContextOuterClass.ConnectionEvent parseFrom(
         java.nio.ByteBuffer data,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data, extensionRegistry);
     }
-    public static context.ContextOuterClass.Constraint parseFrom(
+    public static context.ContextOuterClass.ConnectionEvent parseFrom(
         com.google.protobuf.ByteString data)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data);
     }
-    public static context.ContextOuterClass.Constraint parseFrom(
+    public static context.ContextOuterClass.ConnectionEvent parseFrom(
         com.google.protobuf.ByteString data,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data, extensionRegistry);
     }
-    public static context.ContextOuterClass.Constraint parseFrom(byte[] data)
+    public static context.ContextOuterClass.ConnectionEvent parseFrom(byte[] data)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data);
     }
-    public static context.ContextOuterClass.Constraint parseFrom(
+    public static context.ContextOuterClass.ConnectionEvent parseFrom(
         byte[] data,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data, extensionRegistry);
     }
-    public static context.ContextOuterClass.Constraint parseFrom(java.io.InputStream input)
+    public static context.ContextOuterClass.ConnectionEvent parseFrom(java.io.InputStream input)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseWithIOException(PARSER, input);
     }
-    public static context.ContextOuterClass.Constraint parseFrom(
+    public static context.ContextOuterClass.ConnectionEvent parseFrom(
         java.io.InputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseWithIOException(PARSER, input, extensionRegistry);
     }
-    public static context.ContextOuterClass.Constraint parseDelimitedFrom(java.io.InputStream input)
+    public static context.ContextOuterClass.ConnectionEvent parseDelimitedFrom(java.io.InputStream input)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseDelimitedWithIOException(PARSER, input);
     }
-    public static context.ContextOuterClass.Constraint parseDelimitedFrom(
+    public static context.ContextOuterClass.ConnectionEvent parseDelimitedFrom(
         java.io.InputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseDelimitedWithIOException(PARSER, input, extensionRegistry);
     }
-    public static context.ContextOuterClass.Constraint parseFrom(
+    public static context.ContextOuterClass.ConnectionEvent parseFrom(
         com.google.protobuf.CodedInputStream input)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseWithIOException(PARSER, input);
     }
-    public static context.ContextOuterClass.Constraint parseFrom(
+    public static context.ContextOuterClass.ConnectionEvent parseFrom(
         com.google.protobuf.CodedInputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws java.io.IOException {
@@ -30931,7 +31789,7 @@ public final class ContextOuterClass {
     public static Builder newBuilder() {
       return DEFAULT_INSTANCE.toBuilder();
     }
-    public static Builder newBuilder(context.ContextOuterClass.Constraint prototype) {
+    public static Builder newBuilder(context.ContextOuterClass.ConnectionEvent prototype) {
       return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
     }
     @java.lang.Override
@@ -30947,30 +31805,26 @@ public final class ContextOuterClass {
       return builder;
     }
     /**
-     * <pre>
-     * ----- Constraint ----------------------------------------------------------------------------------------------------
-     * </pre>
-     *
-     * Protobuf type {@code context.Constraint}
+     * Protobuf type {@code context.ConnectionEvent}
      */
     public static final class Builder extends
         com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
-        // @@protoc_insertion_point(builder_implements:context.Constraint)
-        context.ContextOuterClass.ConstraintOrBuilder {
+        // @@protoc_insertion_point(builder_implements:context.ConnectionEvent)
+        context.ContextOuterClass.ConnectionEventOrBuilder {
       public static final com.google.protobuf.Descriptors.Descriptor
           getDescriptor() {
-        return context.ContextOuterClass.internal_static_context_Constraint_descriptor;
+        return context.ContextOuterClass.internal_static_context_ConnectionEvent_descriptor;
       }
 
       @java.lang.Override
       protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
           internalGetFieldAccessorTable() {
-        return context.ContextOuterClass.internal_static_context_Constraint_fieldAccessorTable
+        return context.ContextOuterClass.internal_static_context_ConnectionEvent_fieldAccessorTable
             .ensureFieldAccessorsInitialized(
-                context.ContextOuterClass.Constraint.class, context.ContextOuterClass.Constraint.Builder.class);
+                context.ContextOuterClass.ConnectionEvent.class, context.ContextOuterClass.ConnectionEvent.Builder.class);
       }
 
-      // Construct using context.ContextOuterClass.Constraint.newBuilder()
+      // Construct using context.ContextOuterClass.ConnectionEvent.newBuilder()
       private Builder() {
         maybeForceBuilderInitialization();
       }
@@ -30988,27 +31842,35 @@ public final class ContextOuterClass {
       @java.lang.Override
       public Builder clear() {
         super.clear();
-        constraintType_ = "";
-
-        constraintValue_ = "";
-
+        if (eventBuilder_ == null) {
+          event_ = null;
+        } else {
+          event_ = null;
+          eventBuilder_ = null;
+        }
+        if (connectionIdBuilder_ == null) {
+          connectionId_ = null;
+        } else {
+          connectionId_ = null;
+          connectionIdBuilder_ = null;
+        }
         return this;
       }
 
       @java.lang.Override
       public com.google.protobuf.Descriptors.Descriptor
           getDescriptorForType() {
-        return context.ContextOuterClass.internal_static_context_Constraint_descriptor;
+        return context.ContextOuterClass.internal_static_context_ConnectionEvent_descriptor;
       }
 
       @java.lang.Override
-      public context.ContextOuterClass.Constraint getDefaultInstanceForType() {
-        return context.ContextOuterClass.Constraint.getDefaultInstance();
+      public context.ContextOuterClass.ConnectionEvent getDefaultInstanceForType() {
+        return context.ContextOuterClass.ConnectionEvent.getDefaultInstance();
       }
 
       @java.lang.Override
-      public context.ContextOuterClass.Constraint build() {
-        context.ContextOuterClass.Constraint result = buildPartial();
+      public context.ContextOuterClass.ConnectionEvent build() {
+        context.ContextOuterClass.ConnectionEvent result = buildPartial();
         if (!result.isInitialized()) {
           throw newUninitializedMessageException(result);
         }
@@ -31016,10 +31878,18 @@ public final class ContextOuterClass {
       }
 
       @java.lang.Override
-      public context.ContextOuterClass.Constraint buildPartial() {
-        context.ContextOuterClass.Constraint result = new context.ContextOuterClass.Constraint(this);
-        result.constraintType_ = constraintType_;
-        result.constraintValue_ = constraintValue_;
+      public context.ContextOuterClass.ConnectionEvent buildPartial() {
+        context.ContextOuterClass.ConnectionEvent result = new context.ContextOuterClass.ConnectionEvent(this);
+        if (eventBuilder_ == null) {
+          result.event_ = event_;
+        } else {
+          result.event_ = eventBuilder_.build();
+        }
+        if (connectionIdBuilder_ == null) {
+          result.connectionId_ = connectionId_;
+        } else {
+          result.connectionId_ = connectionIdBuilder_.build();
+        }
         onBuilt();
         return result;
       }
@@ -31058,23 +31928,21 @@ public final class ContextOuterClass {
       }
       @java.lang.Override
       public Builder mergeFrom(com.google.protobuf.Message other) {
-        if (other instanceof context.ContextOuterClass.Constraint) {
-          return mergeFrom((context.ContextOuterClass.Constraint)other);
+        if (other instanceof context.ContextOuterClass.ConnectionEvent) {
+          return mergeFrom((context.ContextOuterClass.ConnectionEvent)other);
         } else {
           super.mergeFrom(other);
           return this;
         }
       }
 
-      public Builder mergeFrom(context.ContextOuterClass.Constraint other) {
-        if (other == context.ContextOuterClass.Constraint.getDefaultInstance()) return this;
-        if (!other.getConstraintType().isEmpty()) {
-          constraintType_ = other.constraintType_;
-          onChanged();
+      public Builder mergeFrom(context.ContextOuterClass.ConnectionEvent other) {
+        if (other == context.ContextOuterClass.ConnectionEvent.getDefaultInstance()) return this;
+        if (other.hasEvent()) {
+          mergeEvent(other.getEvent());
         }
-        if (!other.getConstraintValue().isEmpty()) {
-          constraintValue_ = other.constraintValue_;
-          onChanged();
+        if (other.hasConnectionId()) {
+          mergeConnectionId(other.getConnectionId());
         }
         this.mergeUnknownFields(other.unknownFields);
         onChanged();
@@ -31091,11 +31959,11 @@ public final class ContextOuterClass {
           com.google.protobuf.CodedInputStream input,
           com.google.protobuf.ExtensionRegistryLite extensionRegistry)
           throws java.io.IOException {
-        context.ContextOuterClass.Constraint parsedMessage = null;
+        context.ContextOuterClass.ConnectionEvent parsedMessage = null;
         try {
           parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
         } catch (com.google.protobuf.InvalidProtocolBufferException e) {
-          parsedMessage = (context.ContextOuterClass.Constraint) e.getUnfinishedMessage();
+          parsedMessage = (context.ContextOuterClass.ConnectionEvent) e.getUnfinishedMessage();
           throw e.unwrapIOException();
         } finally {
           if (parsedMessage != null) {
@@ -31104,157 +31972,243 @@ public final class ContextOuterClass {
         }
         return this;
       }
-
-      private java.lang.Object constraintType_ = "";
+
+      private context.ContextOuterClass.Event event_;
+      private com.google.protobuf.SingleFieldBuilderV3<
+          context.ContextOuterClass.Event, context.ContextOuterClass.Event.Builder, context.ContextOuterClass.EventOrBuilder> eventBuilder_;
+      /**
+       * <code>.context.Event event = 1;</code>
+       * @return Whether the event field is set.
+       */
+      public boolean hasEvent() {
+        return eventBuilder_ != null || event_ != null;
+      }
+      /**
+       * <code>.context.Event event = 1;</code>
+       * @return The event.
+       */
+      public context.ContextOuterClass.Event getEvent() {
+        if (eventBuilder_ == null) {
+          return event_ == null ? context.ContextOuterClass.Event.getDefaultInstance() : event_;
+        } else {
+          return eventBuilder_.getMessage();
+        }
+      }
+      /**
+       * <code>.context.Event event = 1;</code>
+       */
+      public Builder setEvent(context.ContextOuterClass.Event value) {
+        if (eventBuilder_ == null) {
+          if (value == null) {
+            throw new NullPointerException();
+          }
+          event_ = value;
+          onChanged();
+        } else {
+          eventBuilder_.setMessage(value);
+        }
+
+        return this;
+      }
+      /**
+       * <code>.context.Event event = 1;</code>
+       */
+      public Builder setEvent(
+          context.ContextOuterClass.Event.Builder builderForValue) {
+        if (eventBuilder_ == null) {
+          event_ = builderForValue.build();
+          onChanged();
+        } else {
+          eventBuilder_.setMessage(builderForValue.build());
+        }
+
+        return this;
+      }
+      /**
+       * <code>.context.Event event = 1;</code>
+       */
+      public Builder mergeEvent(context.ContextOuterClass.Event value) {
+        if (eventBuilder_ == null) {
+          if (event_ != null) {
+            event_ =
+              context.ContextOuterClass.Event.newBuilder(event_).mergeFrom(value).buildPartial();
+          } else {
+            event_ = value;
+          }
+          onChanged();
+        } else {
+          eventBuilder_.mergeFrom(value);
+        }
+
+        return this;
+      }
       /**
-       * <code>string constraint_type = 1;</code>
-       * @return The constraintType.
+       * <code>.context.Event event = 1;</code>
        */
-      public java.lang.String getConstraintType() {
-        java.lang.Object ref = constraintType_;
-        if (!(ref instanceof java.lang.String)) {
-          com.google.protobuf.ByteString bs =
-              (com.google.protobuf.ByteString) ref;
-          java.lang.String s = bs.toStringUtf8();
-          constraintType_ = s;
-          return s;
+      public Builder clearEvent() {
+        if (eventBuilder_ == null) {
+          event_ = null;
+          onChanged();
         } else {
-          return (java.lang.String) ref;
+          event_ = null;
+          eventBuilder_ = null;
         }
+
+        return this;
       }
       /**
-       * <code>string constraint_type = 1;</code>
-       * @return The bytes for constraintType.
+       * <code>.context.Event event = 1;</code>
        */
-      public com.google.protobuf.ByteString
-          getConstraintTypeBytes() {
-        java.lang.Object ref = constraintType_;
-        if (ref instanceof String) {
-          com.google.protobuf.ByteString b = 
-              com.google.protobuf.ByteString.copyFromUtf8(
-                  (java.lang.String) ref);
-          constraintType_ = b;
-          return b;
+      public context.ContextOuterClass.Event.Builder getEventBuilder() {
+        
+        onChanged();
+        return getEventFieldBuilder().getBuilder();
+      }
+      /**
+       * <code>.context.Event event = 1;</code>
+       */
+      public context.ContextOuterClass.EventOrBuilder getEventOrBuilder() {
+        if (eventBuilder_ != null) {
+          return eventBuilder_.getMessageOrBuilder();
         } else {
-          return (com.google.protobuf.ByteString) ref;
+          return event_ == null ?
+              context.ContextOuterClass.Event.getDefaultInstance() : event_;
         }
       }
       /**
-       * <code>string constraint_type = 1;</code>
-       * @param value The constraintType to set.
-       * @return This builder for chaining.
+       * <code>.context.Event event = 1;</code>
        */
-      public Builder setConstraintType(
-          java.lang.String value) {
-        if (value == null) {
-    throw new NullPointerException();
-  }
-  
-        constraintType_ = value;
-        onChanged();
-        return this;
+      private com.google.protobuf.SingleFieldBuilderV3<
+          context.ContextOuterClass.Event, context.ContextOuterClass.Event.Builder, context.ContextOuterClass.EventOrBuilder> 
+          getEventFieldBuilder() {
+        if (eventBuilder_ == null) {
+          eventBuilder_ = new com.google.protobuf.SingleFieldBuilderV3<
+              context.ContextOuterClass.Event, context.ContextOuterClass.Event.Builder, context.ContextOuterClass.EventOrBuilder>(
+                  getEvent(),
+                  getParentForChildren(),
+                  isClean());
+          event_ = null;
+        }
+        return eventBuilder_;
       }
+
+      private context.ContextOuterClass.ConnectionId connectionId_;
+      private com.google.protobuf.SingleFieldBuilderV3<
+          context.ContextOuterClass.ConnectionId, context.ContextOuterClass.ConnectionId.Builder, context.ContextOuterClass.ConnectionIdOrBuilder> connectionIdBuilder_;
       /**
-       * <code>string constraint_type = 1;</code>
-       * @return This builder for chaining.
+       * <code>.context.ConnectionId connection_id = 2;</code>
+       * @return Whether the connectionId field is set.
        */
-      public Builder clearConstraintType() {
-        
-        constraintType_ = getDefaultInstance().getConstraintType();
-        onChanged();
-        return this;
+      public boolean hasConnectionId() {
+        return connectionIdBuilder_ != null || connectionId_ != null;
       }
       /**
-       * <code>string constraint_type = 1;</code>
-       * @param value The bytes for constraintType to set.
-       * @return This builder for chaining.
+       * <code>.context.ConnectionId connection_id = 2;</code>
+       * @return The connectionId.
        */
-      public Builder setConstraintTypeBytes(
-          com.google.protobuf.ByteString value) {
-        if (value == null) {
-    throw new NullPointerException();
-  }
-  checkByteStringIsUtf8(value);
-        
-        constraintType_ = value;
-        onChanged();
-        return this;
+      public context.ContextOuterClass.ConnectionId getConnectionId() {
+        if (connectionIdBuilder_ == null) {
+          return connectionId_ == null ? context.ContextOuterClass.ConnectionId.getDefaultInstance() : connectionId_;
+        } else {
+          return connectionIdBuilder_.getMessage();
+        }
       }
-
-      private java.lang.Object constraintValue_ = "";
       /**
-       * <code>string constraint_value = 2;</code>
-       * @return The constraintValue.
+       * <code>.context.ConnectionId connection_id = 2;</code>
        */
-      public java.lang.String getConstraintValue() {
-        java.lang.Object ref = constraintValue_;
-        if (!(ref instanceof java.lang.String)) {
-          com.google.protobuf.ByteString bs =
-              (com.google.protobuf.ByteString) ref;
-          java.lang.String s = bs.toStringUtf8();
-          constraintValue_ = s;
-          return s;
+      public Builder setConnectionId(context.ContextOuterClass.ConnectionId value) {
+        if (connectionIdBuilder_ == null) {
+          if (value == null) {
+            throw new NullPointerException();
+          }
+          connectionId_ = value;
+          onChanged();
         } else {
-          return (java.lang.String) ref;
+          connectionIdBuilder_.setMessage(value);
         }
+
+        return this;
       }
       /**
-       * <code>string constraint_value = 2;</code>
-       * @return The bytes for constraintValue.
+       * <code>.context.ConnectionId connection_id = 2;</code>
        */
-      public com.google.protobuf.ByteString
-          getConstraintValueBytes() {
-        java.lang.Object ref = constraintValue_;
-        if (ref instanceof String) {
-          com.google.protobuf.ByteString b = 
-              com.google.protobuf.ByteString.copyFromUtf8(
-                  (java.lang.String) ref);
-          constraintValue_ = b;
-          return b;
+      public Builder setConnectionId(
+          context.ContextOuterClass.ConnectionId.Builder builderForValue) {
+        if (connectionIdBuilder_ == null) {
+          connectionId_ = builderForValue.build();
+          onChanged();
         } else {
-          return (com.google.protobuf.ByteString) ref;
+          connectionIdBuilder_.setMessage(builderForValue.build());
         }
+
+        return this;
       }
       /**
-       * <code>string constraint_value = 2;</code>
-       * @param value The constraintValue to set.
-       * @return This builder for chaining.
+       * <code>.context.ConnectionId connection_id = 2;</code>
        */
-      public Builder setConstraintValue(
-          java.lang.String value) {
-        if (value == null) {
-    throw new NullPointerException();
-  }
-  
-        constraintValue_ = value;
-        onChanged();
+      public Builder mergeConnectionId(context.ContextOuterClass.ConnectionId value) {
+        if (connectionIdBuilder_ == null) {
+          if (connectionId_ != null) {
+            connectionId_ =
+              context.ContextOuterClass.ConnectionId.newBuilder(connectionId_).mergeFrom(value).buildPartial();
+          } else {
+            connectionId_ = value;
+          }
+          onChanged();
+        } else {
+          connectionIdBuilder_.mergeFrom(value);
+        }
+
         return this;
       }
       /**
-       * <code>string constraint_value = 2;</code>
-       * @return This builder for chaining.
+       * <code>.context.ConnectionId connection_id = 2;</code>
        */
-      public Builder clearConstraintValue() {
-        
-        constraintValue_ = getDefaultInstance().getConstraintValue();
-        onChanged();
+      public Builder clearConnectionId() {
+        if (connectionIdBuilder_ == null) {
+          connectionId_ = null;
+          onChanged();
+        } else {
+          connectionId_ = null;
+          connectionIdBuilder_ = null;
+        }
+
         return this;
       }
       /**
-       * <code>string constraint_value = 2;</code>
-       * @param value The bytes for constraintValue to set.
-       * @return This builder for chaining.
+       * <code>.context.ConnectionId connection_id = 2;</code>
        */
-      public Builder setConstraintValueBytes(
-          com.google.protobuf.ByteString value) {
-        if (value == null) {
-    throw new NullPointerException();
-  }
-  checkByteStringIsUtf8(value);
+      public context.ContextOuterClass.ConnectionId.Builder getConnectionIdBuilder() {
         
-        constraintValue_ = value;
         onChanged();
-        return this;
+        return getConnectionIdFieldBuilder().getBuilder();
+      }
+      /**
+       * <code>.context.ConnectionId connection_id = 2;</code>
+       */
+      public context.ContextOuterClass.ConnectionIdOrBuilder getConnectionIdOrBuilder() {
+        if (connectionIdBuilder_ != null) {
+          return connectionIdBuilder_.getMessageOrBuilder();
+        } else {
+          return connectionId_ == null ?
+              context.ContextOuterClass.ConnectionId.getDefaultInstance() : connectionId_;
+        }
+      }
+      /**
+       * <code>.context.ConnectionId connection_id = 2;</code>
+       */
+      private com.google.protobuf.SingleFieldBuilderV3<
+          context.ContextOuterClass.ConnectionId, context.ContextOuterClass.ConnectionId.Builder, context.ContextOuterClass.ConnectionIdOrBuilder> 
+          getConnectionIdFieldBuilder() {
+        if (connectionIdBuilder_ == null) {
+          connectionIdBuilder_ = new com.google.protobuf.SingleFieldBuilderV3<
+              context.ContextOuterClass.ConnectionId, context.ContextOuterClass.ConnectionId.Builder, context.ContextOuterClass.ConnectionIdOrBuilder>(
+                  getConnectionId(),
+                  getParentForChildren(),
+                  isClean());
+          connectionId_ = null;
+        }
+        return connectionIdBuilder_;
       }
       @java.lang.Override
       public final Builder setUnknownFields(
@@ -31269,89 +32223,119 @@ public final class ContextOuterClass {
       }
 
 
-      // @@protoc_insertion_point(builder_scope:context.Constraint)
+      // @@protoc_insertion_point(builder_scope:context.ConnectionEvent)
     }
 
-    // @@protoc_insertion_point(class_scope:context.Constraint)
-    private static final context.ContextOuterClass.Constraint DEFAULT_INSTANCE;
+    // @@protoc_insertion_point(class_scope:context.ConnectionEvent)
+    private static final context.ContextOuterClass.ConnectionEvent DEFAULT_INSTANCE;
     static {
-      DEFAULT_INSTANCE = new context.ContextOuterClass.Constraint();
+      DEFAULT_INSTANCE = new context.ContextOuterClass.ConnectionEvent();
     }
 
-    public static context.ContextOuterClass.Constraint getDefaultInstance() {
+    public static context.ContextOuterClass.ConnectionEvent getDefaultInstance() {
       return DEFAULT_INSTANCE;
     }
 
-    private static final com.google.protobuf.Parser<Constraint>
-        PARSER = new com.google.protobuf.AbstractParser<Constraint>() {
+    private static final com.google.protobuf.Parser<ConnectionEvent>
+        PARSER = new com.google.protobuf.AbstractParser<ConnectionEvent>() {
       @java.lang.Override
-      public Constraint parsePartialFrom(
+      public ConnectionEvent parsePartialFrom(
           com.google.protobuf.CodedInputStream input,
           com.google.protobuf.ExtensionRegistryLite extensionRegistry)
           throws com.google.protobuf.InvalidProtocolBufferException {
-        return new Constraint(input, extensionRegistry);
+        return new ConnectionEvent(input, extensionRegistry);
       }
     };
 
-    public static com.google.protobuf.Parser<Constraint> parser() {
+    public static com.google.protobuf.Parser<ConnectionEvent> parser() {
       return PARSER;
     }
 
     @java.lang.Override
-    public com.google.protobuf.Parser<Constraint> getParserForType() {
+    public com.google.protobuf.Parser<ConnectionEvent> getParserForType() {
       return PARSER;
     }
 
     @java.lang.Override
-    public context.ContextOuterClass.Constraint getDefaultInstanceForType() {
+    public context.ContextOuterClass.ConnectionEvent getDefaultInstanceForType() {
       return DEFAULT_INSTANCE;
     }
 
   }
 
-  public interface ConnectionIdOrBuilder extends
-      // @@protoc_insertion_point(interface_extends:context.ConnectionId)
-      com.google.protobuf.MessageOrBuilder {
+  public interface EndPointIdOrBuilder extends
+      // @@protoc_insertion_point(interface_extends:context.EndPointId)
+      com.google.protobuf.MessageOrBuilder {
+
+    /**
+     * <code>.context.TopologyId topology_id = 1;</code>
+     * @return Whether the topologyId field is set.
+     */
+    boolean hasTopologyId();
+    /**
+     * <code>.context.TopologyId topology_id = 1;</code>
+     * @return The topologyId.
+     */
+    context.ContextOuterClass.TopologyId getTopologyId();
+    /**
+     * <code>.context.TopologyId topology_id = 1;</code>
+     */
+    context.ContextOuterClass.TopologyIdOrBuilder getTopologyIdOrBuilder();
+
+    /**
+     * <code>.context.DeviceId device_id = 2;</code>
+     * @return Whether the deviceId field is set.
+     */
+    boolean hasDeviceId();
+    /**
+     * <code>.context.DeviceId device_id = 2;</code>
+     * @return The deviceId.
+     */
+    context.ContextOuterClass.DeviceId getDeviceId();
+    /**
+     * <code>.context.DeviceId device_id = 2;</code>
+     */
+    context.ContextOuterClass.DeviceIdOrBuilder getDeviceIdOrBuilder();
 
     /**
-     * <code>.context.Uuid connection_uuid = 1;</code>
-     * @return Whether the connectionUuid field is set.
+     * <code>.context.Uuid endpoint_uuid = 3;</code>
+     * @return Whether the endpointUuid field is set.
      */
-    boolean hasConnectionUuid();
+    boolean hasEndpointUuid();
     /**
-     * <code>.context.Uuid connection_uuid = 1;</code>
-     * @return The connectionUuid.
+     * <code>.context.Uuid endpoint_uuid = 3;</code>
+     * @return The endpointUuid.
      */
-    context.ContextOuterClass.Uuid getConnectionUuid();
+    context.ContextOuterClass.Uuid getEndpointUuid();
     /**
-     * <code>.context.Uuid connection_uuid = 1;</code>
+     * <code>.context.Uuid endpoint_uuid = 3;</code>
      */
-    context.ContextOuterClass.UuidOrBuilder getConnectionUuidOrBuilder();
+    context.ContextOuterClass.UuidOrBuilder getEndpointUuidOrBuilder();
   }
   /**
    * <pre>
-   * ----- Connection ----------------------------------------------------------------------------------------------------
+   * ----- Endpoint ------------------------------------------------------------------------------------------------------
    * </pre>
    *
-   * Protobuf type {@code context.ConnectionId}
+   * Protobuf type {@code context.EndPointId}
    */
-  public static final class ConnectionId extends
+  public static final class EndPointId extends
       com.google.protobuf.GeneratedMessageV3 implements
-      // @@protoc_insertion_point(message_implements:context.ConnectionId)
-      ConnectionIdOrBuilder {
+      // @@protoc_insertion_point(message_implements:context.EndPointId)
+      EndPointIdOrBuilder {
   private static final long serialVersionUID = 0L;
-    // Use ConnectionId.newBuilder() to construct.
-    private ConnectionId(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
+    // Use EndPointId.newBuilder() to construct.
+    private EndPointId(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
       super(builder);
     }
-    private ConnectionId() {
+    private EndPointId() {
     }
 
     @java.lang.Override
     @SuppressWarnings({"unused"})
     protected java.lang.Object newInstance(
         UnusedPrivateParameter unused) {
-      return new ConnectionId();
+      return new EndPointId();
     }
 
     @java.lang.Override
@@ -31359,7 +32343,7 @@ public final class ContextOuterClass {
     getUnknownFields() {
       return this.unknownFields;
     }
-    private ConnectionId(
+    private EndPointId(
         com.google.protobuf.CodedInputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
@@ -31378,14 +32362,40 @@ public final class ContextOuterClass {
               done = true;
               break;
             case 10: {
+              context.ContextOuterClass.TopologyId.Builder subBuilder = null;
+              if (topologyId_ != null) {
+                subBuilder = topologyId_.toBuilder();
+              }
+              topologyId_ = input.readMessage(context.ContextOuterClass.TopologyId.parser(), extensionRegistry);
+              if (subBuilder != null) {
+                subBuilder.mergeFrom(topologyId_);
+                topologyId_ = subBuilder.buildPartial();
+              }
+
+              break;
+            }
+            case 18: {
+              context.ContextOuterClass.DeviceId.Builder subBuilder = null;
+              if (deviceId_ != null) {
+                subBuilder = deviceId_.toBuilder();
+              }
+              deviceId_ = input.readMessage(context.ContextOuterClass.DeviceId.parser(), extensionRegistry);
+              if (subBuilder != null) {
+                subBuilder.mergeFrom(deviceId_);
+                deviceId_ = subBuilder.buildPartial();
+              }
+
+              break;
+            }
+            case 26: {
               context.ContextOuterClass.Uuid.Builder subBuilder = null;
-              if (connectionUuid_ != null) {
-                subBuilder = connectionUuid_.toBuilder();
+              if (endpointUuid_ != null) {
+                subBuilder = endpointUuid_.toBuilder();
               }
-              connectionUuid_ = input.readMessage(context.ContextOuterClass.Uuid.parser(), extensionRegistry);
+              endpointUuid_ = input.readMessage(context.ContextOuterClass.Uuid.parser(), extensionRegistry);
               if (subBuilder != null) {
-                subBuilder.mergeFrom(connectionUuid_);
-                connectionUuid_ = subBuilder.buildPartial();
+                subBuilder.mergeFrom(endpointUuid_);
+                endpointUuid_ = subBuilder.buildPartial();
               }
 
               break;
@@ -31411,41 +32421,93 @@ public final class ContextOuterClass {
     }
     public static final com.google.protobuf.Descriptors.Descriptor
         getDescriptor() {
-      return context.ContextOuterClass.internal_static_context_ConnectionId_descriptor;
+      return context.ContextOuterClass.internal_static_context_EndPointId_descriptor;
     }
 
     @java.lang.Override
     protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
         internalGetFieldAccessorTable() {
-      return context.ContextOuterClass.internal_static_context_ConnectionId_fieldAccessorTable
+      return context.ContextOuterClass.internal_static_context_EndPointId_fieldAccessorTable
           .ensureFieldAccessorsInitialized(
-              context.ContextOuterClass.ConnectionId.class, context.ContextOuterClass.ConnectionId.Builder.class);
+              context.ContextOuterClass.EndPointId.class, context.ContextOuterClass.EndPointId.Builder.class);
     }
 
-    public static final int CONNECTION_UUID_FIELD_NUMBER = 1;
-    private context.ContextOuterClass.Uuid connectionUuid_;
+    public static final int TOPOLOGY_ID_FIELD_NUMBER = 1;
+    private context.ContextOuterClass.TopologyId topologyId_;
     /**
-     * <code>.context.Uuid connection_uuid = 1;</code>
-     * @return Whether the connectionUuid field is set.
+     * <code>.context.TopologyId topology_id = 1;</code>
+     * @return Whether the topologyId field is set.
      */
     @java.lang.Override
-    public boolean hasConnectionUuid() {
-      return connectionUuid_ != null;
+    public boolean hasTopologyId() {
+      return topologyId_ != null;
     }
     /**
-     * <code>.context.Uuid connection_uuid = 1;</code>
-     * @return The connectionUuid.
+     * <code>.context.TopologyId topology_id = 1;</code>
+     * @return The topologyId.
      */
     @java.lang.Override
-    public context.ContextOuterClass.Uuid getConnectionUuid() {
-      return connectionUuid_ == null ? context.ContextOuterClass.Uuid.getDefaultInstance() : connectionUuid_;
+    public context.ContextOuterClass.TopologyId getTopologyId() {
+      return topologyId_ == null ? context.ContextOuterClass.TopologyId.getDefaultInstance() : topologyId_;
     }
     /**
-     * <code>.context.Uuid connection_uuid = 1;</code>
+     * <code>.context.TopologyId topology_id = 1;</code>
      */
     @java.lang.Override
-    public context.ContextOuterClass.UuidOrBuilder getConnectionUuidOrBuilder() {
-      return getConnectionUuid();
+    public context.ContextOuterClass.TopologyIdOrBuilder getTopologyIdOrBuilder() {
+      return getTopologyId();
+    }
+
+    public static final int DEVICE_ID_FIELD_NUMBER = 2;
+    private context.ContextOuterClass.DeviceId deviceId_;
+    /**
+     * <code>.context.DeviceId device_id = 2;</code>
+     * @return Whether the deviceId field is set.
+     */
+    @java.lang.Override
+    public boolean hasDeviceId() {
+      return deviceId_ != null;
+    }
+    /**
+     * <code>.context.DeviceId device_id = 2;</code>
+     * @return The deviceId.
+     */
+    @java.lang.Override
+    public context.ContextOuterClass.DeviceId getDeviceId() {
+      return deviceId_ == null ? context.ContextOuterClass.DeviceId.getDefaultInstance() : deviceId_;
+    }
+    /**
+     * <code>.context.DeviceId device_id = 2;</code>
+     */
+    @java.lang.Override
+    public context.ContextOuterClass.DeviceIdOrBuilder getDeviceIdOrBuilder() {
+      return getDeviceId();
+    }
+
+    public static final int ENDPOINT_UUID_FIELD_NUMBER = 3;
+    private context.ContextOuterClass.Uuid endpointUuid_;
+    /**
+     * <code>.context.Uuid endpoint_uuid = 3;</code>
+     * @return Whether the endpointUuid field is set.
+     */
+    @java.lang.Override
+    public boolean hasEndpointUuid() {
+      return endpointUuid_ != null;
+    }
+    /**
+     * <code>.context.Uuid endpoint_uuid = 3;</code>
+     * @return The endpointUuid.
+     */
+    @java.lang.Override
+    public context.ContextOuterClass.Uuid getEndpointUuid() {
+      return endpointUuid_ == null ? context.ContextOuterClass.Uuid.getDefaultInstance() : endpointUuid_;
+    }
+    /**
+     * <code>.context.Uuid endpoint_uuid = 3;</code>
+     */
+    @java.lang.Override
+    public context.ContextOuterClass.UuidOrBuilder getEndpointUuidOrBuilder() {
+      return getEndpointUuid();
     }
 
     private byte memoizedIsInitialized = -1;
@@ -31462,8 +32524,14 @@ public final class ContextOuterClass {
     @java.lang.Override
     public void writeTo(com.google.protobuf.CodedOutputStream output)
                         throws java.io.IOException {
-      if (connectionUuid_ != null) {
-        output.writeMessage(1, getConnectionUuid());
+      if (topologyId_ != null) {
+        output.writeMessage(1, getTopologyId());
+      }
+      if (deviceId_ != null) {
+        output.writeMessage(2, getDeviceId());
+      }
+      if (endpointUuid_ != null) {
+        output.writeMessage(3, getEndpointUuid());
       }
       unknownFields.writeTo(output);
     }
@@ -31474,9 +32542,17 @@ public final class ContextOuterClass {
       if (size != -1) return size;
 
       size = 0;
-      if (connectionUuid_ != null) {
+      if (topologyId_ != null) {
         size += com.google.protobuf.CodedOutputStream
-          .computeMessageSize(1, getConnectionUuid());
+          .computeMessageSize(1, getTopologyId());
+      }
+      if (deviceId_ != null) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeMessageSize(2, getDeviceId());
+      }
+      if (endpointUuid_ != null) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeMessageSize(3, getEndpointUuid());
       }
       size += unknownFields.getSerializedSize();
       memoizedSize = size;
@@ -31488,15 +32564,25 @@ public final class ContextOuterClass {
       if (obj == this) {
        return true;
       }
-      if (!(obj instanceof context.ContextOuterClass.ConnectionId)) {
+      if (!(obj instanceof context.ContextOuterClass.EndPointId)) {
         return super.equals(obj);
       }
-      context.ContextOuterClass.ConnectionId other = (context.ContextOuterClass.ConnectionId) obj;
+      context.ContextOuterClass.EndPointId other = (context.ContextOuterClass.EndPointId) obj;
 
-      if (hasConnectionUuid() != other.hasConnectionUuid()) return false;
-      if (hasConnectionUuid()) {
-        if (!getConnectionUuid()
-            .equals(other.getConnectionUuid())) return false;
+      if (hasTopologyId() != other.hasTopologyId()) return false;
+      if (hasTopologyId()) {
+        if (!getTopologyId()
+            .equals(other.getTopologyId())) return false;
+      }
+      if (hasDeviceId() != other.hasDeviceId()) return false;
+      if (hasDeviceId()) {
+        if (!getDeviceId()
+            .equals(other.getDeviceId())) return false;
+      }
+      if (hasEndpointUuid() != other.hasEndpointUuid()) return false;
+      if (hasEndpointUuid()) {
+        if (!getEndpointUuid()
+            .equals(other.getEndpointUuid())) return false;
       }
       if (!unknownFields.equals(other.unknownFields)) return false;
       return true;
@@ -31509,78 +32595,86 @@ public final class ContextOuterClass {
       }
       int hash = 41;
       hash = (19 * hash) + getDescriptor().hashCode();
-      if (hasConnectionUuid()) {
-        hash = (37 * hash) + CONNECTION_UUID_FIELD_NUMBER;
-        hash = (53 * hash) + getConnectionUuid().hashCode();
+      if (hasTopologyId()) {
+        hash = (37 * hash) + TOPOLOGY_ID_FIELD_NUMBER;
+        hash = (53 * hash) + getTopologyId().hashCode();
+      }
+      if (hasDeviceId()) {
+        hash = (37 * hash) + DEVICE_ID_FIELD_NUMBER;
+        hash = (53 * hash) + getDeviceId().hashCode();
+      }
+      if (hasEndpointUuid()) {
+        hash = (37 * hash) + ENDPOINT_UUID_FIELD_NUMBER;
+        hash = (53 * hash) + getEndpointUuid().hashCode();
       }
       hash = (29 * hash) + unknownFields.hashCode();
       memoizedHashCode = hash;
       return hash;
     }
 
-    public static context.ContextOuterClass.ConnectionId parseFrom(
+    public static context.ContextOuterClass.EndPointId parseFrom(
         java.nio.ByteBuffer data)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data);
     }
-    public static context.ContextOuterClass.ConnectionId parseFrom(
+    public static context.ContextOuterClass.EndPointId parseFrom(
         java.nio.ByteBuffer data,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data, extensionRegistry);
     }
-    public static context.ContextOuterClass.ConnectionId parseFrom(
+    public static context.ContextOuterClass.EndPointId parseFrom(
         com.google.protobuf.ByteString data)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data);
     }
-    public static context.ContextOuterClass.ConnectionId parseFrom(
+    public static context.ContextOuterClass.EndPointId parseFrom(
         com.google.protobuf.ByteString data,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data, extensionRegistry);
     }
-    public static context.ContextOuterClass.ConnectionId parseFrom(byte[] data)
+    public static context.ContextOuterClass.EndPointId parseFrom(byte[] data)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data);
     }
-    public static context.ContextOuterClass.ConnectionId parseFrom(
+    public static context.ContextOuterClass.EndPointId parseFrom(
         byte[] data,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data, extensionRegistry);
     }
-    public static context.ContextOuterClass.ConnectionId parseFrom(java.io.InputStream input)
+    public static context.ContextOuterClass.EndPointId parseFrom(java.io.InputStream input)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseWithIOException(PARSER, input);
     }
-    public static context.ContextOuterClass.ConnectionId parseFrom(
+    public static context.ContextOuterClass.EndPointId parseFrom(
         java.io.InputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseWithIOException(PARSER, input, extensionRegistry);
     }
-    public static context.ContextOuterClass.ConnectionId parseDelimitedFrom(java.io.InputStream input)
+    public static context.ContextOuterClass.EndPointId parseDelimitedFrom(java.io.InputStream input)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseDelimitedWithIOException(PARSER, input);
     }
-    public static context.ContextOuterClass.ConnectionId parseDelimitedFrom(
+    public static context.ContextOuterClass.EndPointId parseDelimitedFrom(
         java.io.InputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseDelimitedWithIOException(PARSER, input, extensionRegistry);
     }
-    public static context.ContextOuterClass.ConnectionId parseFrom(
+    public static context.ContextOuterClass.EndPointId parseFrom(
         com.google.protobuf.CodedInputStream input)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseWithIOException(PARSER, input);
     }
-    public static context.ContextOuterClass.ConnectionId parseFrom(
+    public static context.ContextOuterClass.EndPointId parseFrom(
         com.google.protobuf.CodedInputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws java.io.IOException {
@@ -31593,7 +32687,7 @@ public final class ContextOuterClass {
     public static Builder newBuilder() {
       return DEFAULT_INSTANCE.toBuilder();
     }
-    public static Builder newBuilder(context.ContextOuterClass.ConnectionId prototype) {
+    public static Builder newBuilder(context.ContextOuterClass.EndPointId prototype) {
       return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
     }
     @java.lang.Override
@@ -31610,29 +32704,29 @@ public final class ContextOuterClass {
     }
     /**
      * <pre>
-     * ----- Connection ----------------------------------------------------------------------------------------------------
+     * ----- Endpoint ------------------------------------------------------------------------------------------------------
      * </pre>
      *
-     * Protobuf type {@code context.ConnectionId}
+     * Protobuf type {@code context.EndPointId}
      */
     public static final class Builder extends
         com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
-        // @@protoc_insertion_point(builder_implements:context.ConnectionId)
-        context.ContextOuterClass.ConnectionIdOrBuilder {
+        // @@protoc_insertion_point(builder_implements:context.EndPointId)
+        context.ContextOuterClass.EndPointIdOrBuilder {
       public static final com.google.protobuf.Descriptors.Descriptor
           getDescriptor() {
-        return context.ContextOuterClass.internal_static_context_ConnectionId_descriptor;
+        return context.ContextOuterClass.internal_static_context_EndPointId_descriptor;
       }
 
       @java.lang.Override
       protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
           internalGetFieldAccessorTable() {
-        return context.ContextOuterClass.internal_static_context_ConnectionId_fieldAccessorTable
+        return context.ContextOuterClass.internal_static_context_EndPointId_fieldAccessorTable
             .ensureFieldAccessorsInitialized(
-                context.ContextOuterClass.ConnectionId.class, context.ContextOuterClass.ConnectionId.Builder.class);
+                context.ContextOuterClass.EndPointId.class, context.ContextOuterClass.EndPointId.Builder.class);
       }
 
-      // Construct using context.ContextOuterClass.ConnectionId.newBuilder()
+      // Construct using context.ContextOuterClass.EndPointId.newBuilder()
       private Builder() {
         maybeForceBuilderInitialization();
       }
@@ -31650,11 +32744,23 @@ public final class ContextOuterClass {
       @java.lang.Override
       public Builder clear() {
         super.clear();
-        if (connectionUuidBuilder_ == null) {
-          connectionUuid_ = null;
+        if (topologyIdBuilder_ == null) {
+          topologyId_ = null;
         } else {
-          connectionUuid_ = null;
-          connectionUuidBuilder_ = null;
+          topologyId_ = null;
+          topologyIdBuilder_ = null;
+        }
+        if (deviceIdBuilder_ == null) {
+          deviceId_ = null;
+        } else {
+          deviceId_ = null;
+          deviceIdBuilder_ = null;
+        }
+        if (endpointUuidBuilder_ == null) {
+          endpointUuid_ = null;
+        } else {
+          endpointUuid_ = null;
+          endpointUuidBuilder_ = null;
         }
         return this;
       }
@@ -31662,17 +32768,17 @@ public final class ContextOuterClass {
       @java.lang.Override
       public com.google.protobuf.Descriptors.Descriptor
           getDescriptorForType() {
-        return context.ContextOuterClass.internal_static_context_ConnectionId_descriptor;
+        return context.ContextOuterClass.internal_static_context_EndPointId_descriptor;
       }
 
       @java.lang.Override
-      public context.ContextOuterClass.ConnectionId getDefaultInstanceForType() {
-        return context.ContextOuterClass.ConnectionId.getDefaultInstance();
+      public context.ContextOuterClass.EndPointId getDefaultInstanceForType() {
+        return context.ContextOuterClass.EndPointId.getDefaultInstance();
       }
 
       @java.lang.Override
-      public context.ContextOuterClass.ConnectionId build() {
-        context.ContextOuterClass.ConnectionId result = buildPartial();
+      public context.ContextOuterClass.EndPointId build() {
+        context.ContextOuterClass.EndPointId result = buildPartial();
         if (!result.isInitialized()) {
           throw newUninitializedMessageException(result);
         }
@@ -31680,12 +32786,22 @@ public final class ContextOuterClass {
       }
 
       @java.lang.Override
-      public context.ContextOuterClass.ConnectionId buildPartial() {
-        context.ContextOuterClass.ConnectionId result = new context.ContextOuterClass.ConnectionId(this);
-        if (connectionUuidBuilder_ == null) {
-          result.connectionUuid_ = connectionUuid_;
+      public context.ContextOuterClass.EndPointId buildPartial() {
+        context.ContextOuterClass.EndPointId result = new context.ContextOuterClass.EndPointId(this);
+        if (topologyIdBuilder_ == null) {
+          result.topologyId_ = topologyId_;
         } else {
-          result.connectionUuid_ = connectionUuidBuilder_.build();
+          result.topologyId_ = topologyIdBuilder_.build();
+        }
+        if (deviceIdBuilder_ == null) {
+          result.deviceId_ = deviceId_;
+        } else {
+          result.deviceId_ = deviceIdBuilder_.build();
+        }
+        if (endpointUuidBuilder_ == null) {
+          result.endpointUuid_ = endpointUuid_;
+        } else {
+          result.endpointUuid_ = endpointUuidBuilder_.build();
         }
         onBuilt();
         return result;
@@ -31725,165 +32841,409 @@ public final class ContextOuterClass {
       }
       @java.lang.Override
       public Builder mergeFrom(com.google.protobuf.Message other) {
-        if (other instanceof context.ContextOuterClass.ConnectionId) {
-          return mergeFrom((context.ContextOuterClass.ConnectionId)other);
+        if (other instanceof context.ContextOuterClass.EndPointId) {
+          return mergeFrom((context.ContextOuterClass.EndPointId)other);
+        } else {
+          super.mergeFrom(other);
+          return this;
+        }
+      }
+
+      public Builder mergeFrom(context.ContextOuterClass.EndPointId other) {
+        if (other == context.ContextOuterClass.EndPointId.getDefaultInstance()) return this;
+        if (other.hasTopologyId()) {
+          mergeTopologyId(other.getTopologyId());
+        }
+        if (other.hasDeviceId()) {
+          mergeDeviceId(other.getDeviceId());
+        }
+        if (other.hasEndpointUuid()) {
+          mergeEndpointUuid(other.getEndpointUuid());
+        }
+        this.mergeUnknownFields(other.unknownFields);
+        onChanged();
+        return this;
+      }
+
+      @java.lang.Override
+      public final boolean isInitialized() {
+        return true;
+      }
+
+      @java.lang.Override
+      public Builder mergeFrom(
+          com.google.protobuf.CodedInputStream input,
+          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+          throws java.io.IOException {
+        context.ContextOuterClass.EndPointId parsedMessage = null;
+        try {
+          parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
+        } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+          parsedMessage = (context.ContextOuterClass.EndPointId) e.getUnfinishedMessage();
+          throw e.unwrapIOException();
+        } finally {
+          if (parsedMessage != null) {
+            mergeFrom(parsedMessage);
+          }
+        }
+        return this;
+      }
+
+      private context.ContextOuterClass.TopologyId topologyId_;
+      private com.google.protobuf.SingleFieldBuilderV3<
+          context.ContextOuterClass.TopologyId, context.ContextOuterClass.TopologyId.Builder, context.ContextOuterClass.TopologyIdOrBuilder> topologyIdBuilder_;
+      /**
+       * <code>.context.TopologyId topology_id = 1;</code>
+       * @return Whether the topologyId field is set.
+       */
+      public boolean hasTopologyId() {
+        return topologyIdBuilder_ != null || topologyId_ != null;
+      }
+      /**
+       * <code>.context.TopologyId topology_id = 1;</code>
+       * @return The topologyId.
+       */
+      public context.ContextOuterClass.TopologyId getTopologyId() {
+        if (topologyIdBuilder_ == null) {
+          return topologyId_ == null ? context.ContextOuterClass.TopologyId.getDefaultInstance() : topologyId_;
+        } else {
+          return topologyIdBuilder_.getMessage();
+        }
+      }
+      /**
+       * <code>.context.TopologyId topology_id = 1;</code>
+       */
+      public Builder setTopologyId(context.ContextOuterClass.TopologyId value) {
+        if (topologyIdBuilder_ == null) {
+          if (value == null) {
+            throw new NullPointerException();
+          }
+          topologyId_ = value;
+          onChanged();
+        } else {
+          topologyIdBuilder_.setMessage(value);
+        }
+
+        return this;
+      }
+      /**
+       * <code>.context.TopologyId topology_id = 1;</code>
+       */
+      public Builder setTopologyId(
+          context.ContextOuterClass.TopologyId.Builder builderForValue) {
+        if (topologyIdBuilder_ == null) {
+          topologyId_ = builderForValue.build();
+          onChanged();
+        } else {
+          topologyIdBuilder_.setMessage(builderForValue.build());
+        }
+
+        return this;
+      }
+      /**
+       * <code>.context.TopologyId topology_id = 1;</code>
+       */
+      public Builder mergeTopologyId(context.ContextOuterClass.TopologyId value) {
+        if (topologyIdBuilder_ == null) {
+          if (topologyId_ != null) {
+            topologyId_ =
+              context.ContextOuterClass.TopologyId.newBuilder(topologyId_).mergeFrom(value).buildPartial();
+          } else {
+            topologyId_ = value;
+          }
+          onChanged();
+        } else {
+          topologyIdBuilder_.mergeFrom(value);
+        }
+
+        return this;
+      }
+      /**
+       * <code>.context.TopologyId topology_id = 1;</code>
+       */
+      public Builder clearTopologyId() {
+        if (topologyIdBuilder_ == null) {
+          topologyId_ = null;
+          onChanged();
+        } else {
+          topologyId_ = null;
+          topologyIdBuilder_ = null;
+        }
+
+        return this;
+      }
+      /**
+       * <code>.context.TopologyId topology_id = 1;</code>
+       */
+      public context.ContextOuterClass.TopologyId.Builder getTopologyIdBuilder() {
+        
+        onChanged();
+        return getTopologyIdFieldBuilder().getBuilder();
+      }
+      /**
+       * <code>.context.TopologyId topology_id = 1;</code>
+       */
+      public context.ContextOuterClass.TopologyIdOrBuilder getTopologyIdOrBuilder() {
+        if (topologyIdBuilder_ != null) {
+          return topologyIdBuilder_.getMessageOrBuilder();
+        } else {
+          return topologyId_ == null ?
+              context.ContextOuterClass.TopologyId.getDefaultInstance() : topologyId_;
+        }
+      }
+      /**
+       * <code>.context.TopologyId topology_id = 1;</code>
+       */
+      private com.google.protobuf.SingleFieldBuilderV3<
+          context.ContextOuterClass.TopologyId, context.ContextOuterClass.TopologyId.Builder, context.ContextOuterClass.TopologyIdOrBuilder> 
+          getTopologyIdFieldBuilder() {
+        if (topologyIdBuilder_ == null) {
+          topologyIdBuilder_ = new com.google.protobuf.SingleFieldBuilderV3<
+              context.ContextOuterClass.TopologyId, context.ContextOuterClass.TopologyId.Builder, context.ContextOuterClass.TopologyIdOrBuilder>(
+                  getTopologyId(),
+                  getParentForChildren(),
+                  isClean());
+          topologyId_ = null;
+        }
+        return topologyIdBuilder_;
+      }
+
+      private context.ContextOuterClass.DeviceId deviceId_;
+      private com.google.protobuf.SingleFieldBuilderV3<
+          context.ContextOuterClass.DeviceId, context.ContextOuterClass.DeviceId.Builder, context.ContextOuterClass.DeviceIdOrBuilder> deviceIdBuilder_;
+      /**
+       * <code>.context.DeviceId device_id = 2;</code>
+       * @return Whether the deviceId field is set.
+       */
+      public boolean hasDeviceId() {
+        return deviceIdBuilder_ != null || deviceId_ != null;
+      }
+      /**
+       * <code>.context.DeviceId device_id = 2;</code>
+       * @return The deviceId.
+       */
+      public context.ContextOuterClass.DeviceId getDeviceId() {
+        if (deviceIdBuilder_ == null) {
+          return deviceId_ == null ? context.ContextOuterClass.DeviceId.getDefaultInstance() : deviceId_;
         } else {
-          super.mergeFrom(other);
-          return this;
+          return deviceIdBuilder_.getMessage();
         }
       }
-
-      public Builder mergeFrom(context.ContextOuterClass.ConnectionId other) {
-        if (other == context.ContextOuterClass.ConnectionId.getDefaultInstance()) return this;
-        if (other.hasConnectionUuid()) {
-          mergeConnectionUuid(other.getConnectionUuid());
+      /**
+       * <code>.context.DeviceId device_id = 2;</code>
+       */
+      public Builder setDeviceId(context.ContextOuterClass.DeviceId value) {
+        if (deviceIdBuilder_ == null) {
+          if (value == null) {
+            throw new NullPointerException();
+          }
+          deviceId_ = value;
+          onChanged();
+        } else {
+          deviceIdBuilder_.setMessage(value);
         }
-        this.mergeUnknownFields(other.unknownFields);
-        onChanged();
+
         return this;
       }
+      /**
+       * <code>.context.DeviceId device_id = 2;</code>
+       */
+      public Builder setDeviceId(
+          context.ContextOuterClass.DeviceId.Builder builderForValue) {
+        if (deviceIdBuilder_ == null) {
+          deviceId_ = builderForValue.build();
+          onChanged();
+        } else {
+          deviceIdBuilder_.setMessage(builderForValue.build());
+        }
 
-      @java.lang.Override
-      public final boolean isInitialized() {
-        return true;
+        return this;
       }
-
-      @java.lang.Override
-      public Builder mergeFrom(
-          com.google.protobuf.CodedInputStream input,
-          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
-          throws java.io.IOException {
-        context.ContextOuterClass.ConnectionId parsedMessage = null;
-        try {
-          parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
-        } catch (com.google.protobuf.InvalidProtocolBufferException e) {
-          parsedMessage = (context.ContextOuterClass.ConnectionId) e.getUnfinishedMessage();
-          throw e.unwrapIOException();
-        } finally {
-          if (parsedMessage != null) {
-            mergeFrom(parsedMessage);
+      /**
+       * <code>.context.DeviceId device_id = 2;</code>
+       */
+      public Builder mergeDeviceId(context.ContextOuterClass.DeviceId value) {
+        if (deviceIdBuilder_ == null) {
+          if (deviceId_ != null) {
+            deviceId_ =
+              context.ContextOuterClass.DeviceId.newBuilder(deviceId_).mergeFrom(value).buildPartial();
+          } else {
+            deviceId_ = value;
           }
+          onChanged();
+        } else {
+          deviceIdBuilder_.mergeFrom(value);
         }
+
         return this;
       }
+      /**
+       * <code>.context.DeviceId device_id = 2;</code>
+       */
+      public Builder clearDeviceId() {
+        if (deviceIdBuilder_ == null) {
+          deviceId_ = null;
+          onChanged();
+        } else {
+          deviceId_ = null;
+          deviceIdBuilder_ = null;
+        }
 
-      private context.ContextOuterClass.Uuid connectionUuid_;
+        return this;
+      }
+      /**
+       * <code>.context.DeviceId device_id = 2;</code>
+       */
+      public context.ContextOuterClass.DeviceId.Builder getDeviceIdBuilder() {
+        
+        onChanged();
+        return getDeviceIdFieldBuilder().getBuilder();
+      }
+      /**
+       * <code>.context.DeviceId device_id = 2;</code>
+       */
+      public context.ContextOuterClass.DeviceIdOrBuilder getDeviceIdOrBuilder() {
+        if (deviceIdBuilder_ != null) {
+          return deviceIdBuilder_.getMessageOrBuilder();
+        } else {
+          return deviceId_ == null ?
+              context.ContextOuterClass.DeviceId.getDefaultInstance() : deviceId_;
+        }
+      }
+      /**
+       * <code>.context.DeviceId device_id = 2;</code>
+       */
       private com.google.protobuf.SingleFieldBuilderV3<
-          context.ContextOuterClass.Uuid, context.ContextOuterClass.Uuid.Builder, context.ContextOuterClass.UuidOrBuilder> connectionUuidBuilder_;
+          context.ContextOuterClass.DeviceId, context.ContextOuterClass.DeviceId.Builder, context.ContextOuterClass.DeviceIdOrBuilder> 
+          getDeviceIdFieldBuilder() {
+        if (deviceIdBuilder_ == null) {
+          deviceIdBuilder_ = new com.google.protobuf.SingleFieldBuilderV3<
+              context.ContextOuterClass.DeviceId, context.ContextOuterClass.DeviceId.Builder, context.ContextOuterClass.DeviceIdOrBuilder>(
+                  getDeviceId(),
+                  getParentForChildren(),
+                  isClean());
+          deviceId_ = null;
+        }
+        return deviceIdBuilder_;
+      }
+
+      private context.ContextOuterClass.Uuid endpointUuid_;
+      private com.google.protobuf.SingleFieldBuilderV3<
+          context.ContextOuterClass.Uuid, context.ContextOuterClass.Uuid.Builder, context.ContextOuterClass.UuidOrBuilder> endpointUuidBuilder_;
       /**
-       * <code>.context.Uuid connection_uuid = 1;</code>
-       * @return Whether the connectionUuid field is set.
+       * <code>.context.Uuid endpoint_uuid = 3;</code>
+       * @return Whether the endpointUuid field is set.
        */
-      public boolean hasConnectionUuid() {
-        return connectionUuidBuilder_ != null || connectionUuid_ != null;
+      public boolean hasEndpointUuid() {
+        return endpointUuidBuilder_ != null || endpointUuid_ != null;
       }
       /**
-       * <code>.context.Uuid connection_uuid = 1;</code>
-       * @return The connectionUuid.
+       * <code>.context.Uuid endpoint_uuid = 3;</code>
+       * @return The endpointUuid.
        */
-      public context.ContextOuterClass.Uuid getConnectionUuid() {
-        if (connectionUuidBuilder_ == null) {
-          return connectionUuid_ == null ? context.ContextOuterClass.Uuid.getDefaultInstance() : connectionUuid_;
+      public context.ContextOuterClass.Uuid getEndpointUuid() {
+        if (endpointUuidBuilder_ == null) {
+          return endpointUuid_ == null ? context.ContextOuterClass.Uuid.getDefaultInstance() : endpointUuid_;
         } else {
-          return connectionUuidBuilder_.getMessage();
+          return endpointUuidBuilder_.getMessage();
         }
       }
       /**
-       * <code>.context.Uuid connection_uuid = 1;</code>
+       * <code>.context.Uuid endpoint_uuid = 3;</code>
        */
-      public Builder setConnectionUuid(context.ContextOuterClass.Uuid value) {
-        if (connectionUuidBuilder_ == null) {
+      public Builder setEndpointUuid(context.ContextOuterClass.Uuid value) {
+        if (endpointUuidBuilder_ == null) {
           if (value == null) {
             throw new NullPointerException();
           }
-          connectionUuid_ = value;
+          endpointUuid_ = value;
           onChanged();
         } else {
-          connectionUuidBuilder_.setMessage(value);
+          endpointUuidBuilder_.setMessage(value);
         }
 
         return this;
       }
       /**
-       * <code>.context.Uuid connection_uuid = 1;</code>
+       * <code>.context.Uuid endpoint_uuid = 3;</code>
        */
-      public Builder setConnectionUuid(
+      public Builder setEndpointUuid(
           context.ContextOuterClass.Uuid.Builder builderForValue) {
-        if (connectionUuidBuilder_ == null) {
-          connectionUuid_ = builderForValue.build();
+        if (endpointUuidBuilder_ == null) {
+          endpointUuid_ = builderForValue.build();
           onChanged();
         } else {
-          connectionUuidBuilder_.setMessage(builderForValue.build());
+          endpointUuidBuilder_.setMessage(builderForValue.build());
         }
 
         return this;
       }
       /**
-       * <code>.context.Uuid connection_uuid = 1;</code>
+       * <code>.context.Uuid endpoint_uuid = 3;</code>
        */
-      public Builder mergeConnectionUuid(context.ContextOuterClass.Uuid value) {
-        if (connectionUuidBuilder_ == null) {
-          if (connectionUuid_ != null) {
-            connectionUuid_ =
-              context.ContextOuterClass.Uuid.newBuilder(connectionUuid_).mergeFrom(value).buildPartial();
+      public Builder mergeEndpointUuid(context.ContextOuterClass.Uuid value) {
+        if (endpointUuidBuilder_ == null) {
+          if (endpointUuid_ != null) {
+            endpointUuid_ =
+              context.ContextOuterClass.Uuid.newBuilder(endpointUuid_).mergeFrom(value).buildPartial();
           } else {
-            connectionUuid_ = value;
+            endpointUuid_ = value;
           }
           onChanged();
         } else {
-          connectionUuidBuilder_.mergeFrom(value);
+          endpointUuidBuilder_.mergeFrom(value);
         }
 
         return this;
       }
       /**
-       * <code>.context.Uuid connection_uuid = 1;</code>
+       * <code>.context.Uuid endpoint_uuid = 3;</code>
        */
-      public Builder clearConnectionUuid() {
-        if (connectionUuidBuilder_ == null) {
-          connectionUuid_ = null;
+      public Builder clearEndpointUuid() {
+        if (endpointUuidBuilder_ == null) {
+          endpointUuid_ = null;
           onChanged();
         } else {
-          connectionUuid_ = null;
-          connectionUuidBuilder_ = null;
+          endpointUuid_ = null;
+          endpointUuidBuilder_ = null;
         }
 
         return this;
       }
       /**
-       * <code>.context.Uuid connection_uuid = 1;</code>
+       * <code>.context.Uuid endpoint_uuid = 3;</code>
        */
-      public context.ContextOuterClass.Uuid.Builder getConnectionUuidBuilder() {
+      public context.ContextOuterClass.Uuid.Builder getEndpointUuidBuilder() {
         
         onChanged();
-        return getConnectionUuidFieldBuilder().getBuilder();
+        return getEndpointUuidFieldBuilder().getBuilder();
       }
       /**
-       * <code>.context.Uuid connection_uuid = 1;</code>
+       * <code>.context.Uuid endpoint_uuid = 3;</code>
        */
-      public context.ContextOuterClass.UuidOrBuilder getConnectionUuidOrBuilder() {
-        if (connectionUuidBuilder_ != null) {
-          return connectionUuidBuilder_.getMessageOrBuilder();
+      public context.ContextOuterClass.UuidOrBuilder getEndpointUuidOrBuilder() {
+        if (endpointUuidBuilder_ != null) {
+          return endpointUuidBuilder_.getMessageOrBuilder();
         } else {
-          return connectionUuid_ == null ?
-              context.ContextOuterClass.Uuid.getDefaultInstance() : connectionUuid_;
+          return endpointUuid_ == null ?
+              context.ContextOuterClass.Uuid.getDefaultInstance() : endpointUuid_;
         }
       }
       /**
-       * <code>.context.Uuid connection_uuid = 1;</code>
+       * <code>.context.Uuid endpoint_uuid = 3;</code>
        */
       private com.google.protobuf.SingleFieldBuilderV3<
           context.ContextOuterClass.Uuid, context.ContextOuterClass.Uuid.Builder, context.ContextOuterClass.UuidOrBuilder> 
-          getConnectionUuidFieldBuilder() {
-        if (connectionUuidBuilder_ == null) {
-          connectionUuidBuilder_ = new com.google.protobuf.SingleFieldBuilderV3<
+          getEndpointUuidFieldBuilder() {
+        if (endpointUuidBuilder_ == null) {
+          endpointUuidBuilder_ = new com.google.protobuf.SingleFieldBuilderV3<
               context.ContextOuterClass.Uuid, context.ContextOuterClass.Uuid.Builder, context.ContextOuterClass.UuidOrBuilder>(
-                  getConnectionUuid(),
+                  getEndpointUuid(),
                   getParentForChildren(),
                   isClean());
-          connectionUuid_ = null;
+          endpointUuid_ = null;
         }
-        return connectionUuidBuilder_;
+        return endpointUuidBuilder_;
       }
       @java.lang.Override
       public final Builder setUnknownFields(
@@ -31898,125 +33258,128 @@ public final class ContextOuterClass {
       }
 
 
-      // @@protoc_insertion_point(builder_scope:context.ConnectionId)
+      // @@protoc_insertion_point(builder_scope:context.EndPointId)
     }
 
-    // @@protoc_insertion_point(class_scope:context.ConnectionId)
-    private static final context.ContextOuterClass.ConnectionId DEFAULT_INSTANCE;
+    // @@protoc_insertion_point(class_scope:context.EndPointId)
+    private static final context.ContextOuterClass.EndPointId DEFAULT_INSTANCE;
     static {
-      DEFAULT_INSTANCE = new context.ContextOuterClass.ConnectionId();
+      DEFAULT_INSTANCE = new context.ContextOuterClass.EndPointId();
     }
 
-    public static context.ContextOuterClass.ConnectionId getDefaultInstance() {
+    public static context.ContextOuterClass.EndPointId getDefaultInstance() {
       return DEFAULT_INSTANCE;
     }
 
-    private static final com.google.protobuf.Parser<ConnectionId>
-        PARSER = new com.google.protobuf.AbstractParser<ConnectionId>() {
+    private static final com.google.protobuf.Parser<EndPointId>
+        PARSER = new com.google.protobuf.AbstractParser<EndPointId>() {
       @java.lang.Override
-      public ConnectionId parsePartialFrom(
+      public EndPointId parsePartialFrom(
           com.google.protobuf.CodedInputStream input,
           com.google.protobuf.ExtensionRegistryLite extensionRegistry)
           throws com.google.protobuf.InvalidProtocolBufferException {
-        return new ConnectionId(input, extensionRegistry);
+        return new EndPointId(input, extensionRegistry);
       }
     };
 
-    public static com.google.protobuf.Parser<ConnectionId> parser() {
+    public static com.google.protobuf.Parser<EndPointId> parser() {
       return PARSER;
     }
 
     @java.lang.Override
-    public com.google.protobuf.Parser<ConnectionId> getParserForType() {
+    public com.google.protobuf.Parser<EndPointId> getParserForType() {
       return PARSER;
     }
 
     @java.lang.Override
-    public context.ContextOuterClass.ConnectionId getDefaultInstanceForType() {
+    public context.ContextOuterClass.EndPointId getDefaultInstanceForType() {
       return DEFAULT_INSTANCE;
     }
 
   }
 
-  public interface ConnectionOrBuilder extends
-      // @@protoc_insertion_point(interface_extends:context.Connection)
+  public interface EndPointOrBuilder extends
+      // @@protoc_insertion_point(interface_extends:context.EndPoint)
       com.google.protobuf.MessageOrBuilder {
 
     /**
-     * <code>.context.ConnectionId connection_id = 1;</code>
-     * @return Whether the connectionId field is set.
-     */
-    boolean hasConnectionId();
-    /**
-     * <code>.context.ConnectionId connection_id = 1;</code>
-     * @return The connectionId.
+     * <code>.context.EndPointId endpoint_id = 1;</code>
+     * @return Whether the endpointId field is set.
      */
-    context.ContextOuterClass.ConnectionId getConnectionId();
+    boolean hasEndpointId();
     /**
-     * <code>.context.ConnectionId connection_id = 1;</code>
+     * <code>.context.EndPointId endpoint_id = 1;</code>
+     * @return The endpointId.
      */
-    context.ContextOuterClass.ConnectionIdOrBuilder getConnectionIdOrBuilder();
-
+    context.ContextOuterClass.EndPointId getEndpointId();
     /**
-     * <code>.context.ServiceId related_service_id = 2;</code>
-     * @return Whether the relatedServiceId field is set.
+     * <code>.context.EndPointId endpoint_id = 1;</code>
      */
-    boolean hasRelatedServiceId();
+    context.ContextOuterClass.EndPointIdOrBuilder getEndpointIdOrBuilder();
+
     /**
-     * <code>.context.ServiceId related_service_id = 2;</code>
-     * @return The relatedServiceId.
+     * <code>string endpoint_type = 2;</code>
+     * @return The endpointType.
      */
-    context.ContextOuterClass.ServiceId getRelatedServiceId();
+    java.lang.String getEndpointType();
     /**
-     * <code>.context.ServiceId related_service_id = 2;</code>
+     * <code>string endpoint_type = 2;</code>
+     * @return The bytes for endpointType.
      */
-    context.ContextOuterClass.ServiceIdOrBuilder getRelatedServiceIdOrBuilder();
+    com.google.protobuf.ByteString
+        getEndpointTypeBytes();
 
     /**
-     * <code>repeated .context.EndPointId path = 3;</code>
+     * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
+     * @return A list containing the kpiSampleTypes.
      */
-    java.util.List<context.ContextOuterClass.EndPointId> 
-        getPathList();
+    java.util.List<kpi_sample_types.KpiSampleTypes.KpiSampleType> getKpiSampleTypesList();
     /**
-     * <code>repeated .context.EndPointId path = 3;</code>
+     * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
+     * @return The count of kpiSampleTypes.
      */
-    context.ContextOuterClass.EndPointId getPath(int index);
+    int getKpiSampleTypesCount();
     /**
-     * <code>repeated .context.EndPointId path = 3;</code>
+     * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
+     * @param index The index of the element to return.
+     * @return The kpiSampleTypes at the given index.
      */
-    int getPathCount();
+    kpi_sample_types.KpiSampleTypes.KpiSampleType getKpiSampleTypes(int index);
     /**
-     * <code>repeated .context.EndPointId path = 3;</code>
+     * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
+     * @return A list containing the enum numeric values on the wire for kpiSampleTypes.
      */
-    java.util.List<? extends context.ContextOuterClass.EndPointIdOrBuilder> 
-        getPathOrBuilderList();
+    java.util.List<java.lang.Integer>
+    getKpiSampleTypesValueList();
     /**
-     * <code>repeated .context.EndPointId path = 3;</code>
+     * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
+     * @param index The index of the value to return.
+     * @return The enum numeric value on the wire of kpiSampleTypes at the given index.
      */
-    context.ContextOuterClass.EndPointIdOrBuilder getPathOrBuilder(
-        int index);
+    int getKpiSampleTypesValue(int index);
   }
   /**
-   * Protobuf type {@code context.Connection}
+   * Protobuf type {@code context.EndPoint}
    */
-  public static final class Connection extends
+  public static final class EndPoint extends
       com.google.protobuf.GeneratedMessageV3 implements
-      // @@protoc_insertion_point(message_implements:context.Connection)
-      ConnectionOrBuilder {
+      // @@protoc_insertion_point(message_implements:context.EndPoint)
+      EndPointOrBuilder {
   private static final long serialVersionUID = 0L;
-    // Use Connection.newBuilder() to construct.
-    private Connection(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
+    // Use EndPoint.newBuilder() to construct.
+    private EndPoint(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
       super(builder);
     }
-    private Connection() {
-      path_ = java.util.Collections.emptyList();
+    private EndPoint() {
+      endpointType_ = "";
+      kpiSampleTypes_ = java.util.Collections.emptyList();
     }
 
     @java.lang.Override
     @SuppressWarnings({"unused"})
     protected java.lang.Object newInstance(
         UnusedPrivateParameter unused) {
-      return new Connection();
+      return new EndPoint();
     }
 
     @java.lang.Override
@@ -32024,7 +33387,7 @@ public final class ContextOuterClass {
     getUnknownFields() {
       return this.unknownFields;
     }
-    private Connection(
+    private EndPoint(
         com.google.protobuf.CodedInputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
@@ -32044,38 +33407,45 @@ public final class ContextOuterClass {
               done = true;
               break;
             case 10: {
-              context.ContextOuterClass.ConnectionId.Builder subBuilder = null;
-              if (connectionId_ != null) {
-                subBuilder = connectionId_.toBuilder();
+              context.ContextOuterClass.EndPointId.Builder subBuilder = null;
+              if (endpointId_ != null) {
+                subBuilder = endpointId_.toBuilder();
               }
-              connectionId_ = input.readMessage(context.ContextOuterClass.ConnectionId.parser(), extensionRegistry);
+              endpointId_ = input.readMessage(context.ContextOuterClass.EndPointId.parser(), extensionRegistry);
               if (subBuilder != null) {
-                subBuilder.mergeFrom(connectionId_);
-                connectionId_ = subBuilder.buildPartial();
+                subBuilder.mergeFrom(endpointId_);
+                endpointId_ = subBuilder.buildPartial();
               }
 
               break;
             }
             case 18: {
-              context.ContextOuterClass.ServiceId.Builder subBuilder = null;
-              if (relatedServiceId_ != null) {
-                subBuilder = relatedServiceId_.toBuilder();
-              }
-              relatedServiceId_ = input.readMessage(context.ContextOuterClass.ServiceId.parser(), extensionRegistry);
-              if (subBuilder != null) {
-                subBuilder.mergeFrom(relatedServiceId_);
-                relatedServiceId_ = subBuilder.buildPartial();
-              }
+              java.lang.String s = input.readStringRequireUtf8();
 
+              endpointType_ = s;
               break;
             }
-            case 26: {
+            case 24: {
+              int rawValue = input.readEnum();
               if (!((mutable_bitField0_ & 0x00000001) != 0)) {
-                path_ = new java.util.ArrayList<context.ContextOuterClass.EndPointId>();
+                kpiSampleTypes_ = new java.util.ArrayList<java.lang.Integer>();
                 mutable_bitField0_ |= 0x00000001;
               }
-              path_.add(
-                  input.readMessage(context.ContextOuterClass.EndPointId.parser(), extensionRegistry));
+              kpiSampleTypes_.add(rawValue);
+              break;
+            }
+            case 26: {
+              int length = input.readRawVarint32();
+              int oldLimit = input.pushLimit(length);
+              while(input.getBytesUntilLimit() > 0) {
+                int rawValue = input.readEnum();
+                if (!((mutable_bitField0_ & 0x00000001) != 0)) {
+                  kpiSampleTypes_ = new java.util.ArrayList<java.lang.Integer>();
+                  mutable_bitField0_ |= 0x00000001;
+                }
+                kpiSampleTypes_.add(rawValue);
+              }
+              input.popLimit(oldLimit);
               break;
             }
             default: {
@@ -32094,7 +33464,7 @@ public final class ContextOuterClass {
             e).setUnfinishedMessage(this);
       } finally {
         if (((mutable_bitField0_ & 0x00000001) != 0)) {
-          path_ = java.util.Collections.unmodifiableList(path_);
+          kpiSampleTypes_ = java.util.Collections.unmodifiableList(kpiSampleTypes_);
         }
         this.unknownFields = unknownFields.build();
         makeExtensionsImmutable();
@@ -32102,108 +33472,138 @@ public final class ContextOuterClass {
     }
     public static final com.google.protobuf.Descriptors.Descriptor
         getDescriptor() {
-      return context.ContextOuterClass.internal_static_context_Connection_descriptor;
+      return context.ContextOuterClass.internal_static_context_EndPoint_descriptor;
     }
 
     @java.lang.Override
     protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
         internalGetFieldAccessorTable() {
-      return context.ContextOuterClass.internal_static_context_Connection_fieldAccessorTable
+      return context.ContextOuterClass.internal_static_context_EndPoint_fieldAccessorTable
           .ensureFieldAccessorsInitialized(
-              context.ContextOuterClass.Connection.class, context.ContextOuterClass.Connection.Builder.class);
+              context.ContextOuterClass.EndPoint.class, context.ContextOuterClass.EndPoint.Builder.class);
     }
 
-    public static final int CONNECTION_ID_FIELD_NUMBER = 1;
-    private context.ContextOuterClass.ConnectionId connectionId_;
+    public static final int ENDPOINT_ID_FIELD_NUMBER = 1;
+    private context.ContextOuterClass.EndPointId endpointId_;
     /**
-     * <code>.context.ConnectionId connection_id = 1;</code>
-     * @return Whether the connectionId field is set.
+     * <code>.context.EndPointId endpoint_id = 1;</code>
+     * @return Whether the endpointId field is set.
      */
     @java.lang.Override
-    public boolean hasConnectionId() {
-      return connectionId_ != null;
+    public boolean hasEndpointId() {
+      return endpointId_ != null;
     }
     /**
-     * <code>.context.ConnectionId connection_id = 1;</code>
-     * @return The connectionId.
+     * <code>.context.EndPointId endpoint_id = 1;</code>
+     * @return The endpointId.
      */
     @java.lang.Override
-    public context.ContextOuterClass.ConnectionId getConnectionId() {
-      return connectionId_ == null ? context.ContextOuterClass.ConnectionId.getDefaultInstance() : connectionId_;
+    public context.ContextOuterClass.EndPointId getEndpointId() {
+      return endpointId_ == null ? context.ContextOuterClass.EndPointId.getDefaultInstance() : endpointId_;
     }
     /**
-     * <code>.context.ConnectionId connection_id = 1;</code>
+     * <code>.context.EndPointId endpoint_id = 1;</code>
      */
     @java.lang.Override
-    public context.ContextOuterClass.ConnectionIdOrBuilder getConnectionIdOrBuilder() {
-      return getConnectionId();
+    public context.ContextOuterClass.EndPointIdOrBuilder getEndpointIdOrBuilder() {
+      return getEndpointId();
     }
 
-    public static final int RELATED_SERVICE_ID_FIELD_NUMBER = 2;
-    private context.ContextOuterClass.ServiceId relatedServiceId_;
-    /**
-     * <code>.context.ServiceId related_service_id = 2;</code>
-     * @return Whether the relatedServiceId field is set.
-     */
-    @java.lang.Override
-    public boolean hasRelatedServiceId() {
-      return relatedServiceId_ != null;
-    }
+    public static final int ENDPOINT_TYPE_FIELD_NUMBER = 2;
+    private volatile java.lang.Object endpointType_;
     /**
-     * <code>.context.ServiceId related_service_id = 2;</code>
-     * @return The relatedServiceId.
+     * <code>string endpoint_type = 2;</code>
+     * @return The endpointType.
      */
     @java.lang.Override
-    public context.ContextOuterClass.ServiceId getRelatedServiceId() {
-      return relatedServiceId_ == null ? context.ContextOuterClass.ServiceId.getDefaultInstance() : relatedServiceId_;
+    public java.lang.String getEndpointType() {
+      java.lang.Object ref = endpointType_;
+      if (ref instanceof java.lang.String) {
+        return (java.lang.String) ref;
+      } else {
+        com.google.protobuf.ByteString bs = 
+            (com.google.protobuf.ByteString) ref;
+        java.lang.String s = bs.toStringUtf8();
+        endpointType_ = s;
+        return s;
+      }
     }
     /**
-     * <code>.context.ServiceId related_service_id = 2;</code>
+     * <code>string endpoint_type = 2;</code>
+     * @return The bytes for endpointType.
      */
     @java.lang.Override
-    public context.ContextOuterClass.ServiceIdOrBuilder getRelatedServiceIdOrBuilder() {
-      return getRelatedServiceId();
+    public com.google.protobuf.ByteString
+        getEndpointTypeBytes() {
+      java.lang.Object ref = endpointType_;
+      if (ref instanceof java.lang.String) {
+        com.google.protobuf.ByteString b = 
+            com.google.protobuf.ByteString.copyFromUtf8(
+                (java.lang.String) ref);
+        endpointType_ = b;
+        return b;
+      } else {
+        return (com.google.protobuf.ByteString) ref;
+      }
     }
 
-    public static final int PATH_FIELD_NUMBER = 3;
-    private java.util.List<context.ContextOuterClass.EndPointId> path_;
+    public static final int KPI_SAMPLE_TYPES_FIELD_NUMBER = 3;
+    private java.util.List<java.lang.Integer> kpiSampleTypes_;
+    private static final com.google.protobuf.Internal.ListAdapter.Converter<
+        java.lang.Integer, kpi_sample_types.KpiSampleTypes.KpiSampleType> kpiSampleTypes_converter_ =
+            new com.google.protobuf.Internal.ListAdapter.Converter<
+                java.lang.Integer, kpi_sample_types.KpiSampleTypes.KpiSampleType>() {
+              public kpi_sample_types.KpiSampleTypes.KpiSampleType convert(java.lang.Integer from) {
+                @SuppressWarnings("deprecation")
+                kpi_sample_types.KpiSampleTypes.KpiSampleType result = kpi_sample_types.KpiSampleTypes.KpiSampleType.valueOf(from);
+                return result == null ? kpi_sample_types.KpiSampleTypes.KpiSampleType.UNRECOGNIZED : result;
+              }
+            };
     /**
-     * <code>repeated .context.EndPointId path = 3;</code>
+     * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
+     * @return A list containing the kpiSampleTypes.
      */
     @java.lang.Override
-    public java.util.List<context.ContextOuterClass.EndPointId> getPathList() {
-      return path_;
+    public java.util.List<kpi_sample_types.KpiSampleTypes.KpiSampleType> getKpiSampleTypesList() {
+      return new com.google.protobuf.Internal.ListAdapter<
+          java.lang.Integer, kpi_sample_types.KpiSampleTypes.KpiSampleType>(kpiSampleTypes_, kpiSampleTypes_converter_);
     }
     /**
-     * <code>repeated .context.EndPointId path = 3;</code>
+     * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
+     * @return The count of kpiSampleTypes.
      */
     @java.lang.Override
-    public java.util.List<? extends context.ContextOuterClass.EndPointIdOrBuilder> 
-        getPathOrBuilderList() {
-      return path_;
+    public int getKpiSampleTypesCount() {
+      return kpiSampleTypes_.size();
     }
     /**
-     * <code>repeated .context.EndPointId path = 3;</code>
+     * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
+     * @param index The index of the element to return.
+     * @return The kpiSampleTypes at the given index.
      */
     @java.lang.Override
-    public int getPathCount() {
-      return path_.size();
+    public kpi_sample_types.KpiSampleTypes.KpiSampleType getKpiSampleTypes(int index) {
+      return kpiSampleTypes_converter_.convert(kpiSampleTypes_.get(index));
     }
     /**
-     * <code>repeated .context.EndPointId path = 3;</code>
+     * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
+     * @return A list containing the enum numeric values on the wire for kpiSampleTypes.
      */
     @java.lang.Override
-    public context.ContextOuterClass.EndPointId getPath(int index) {
-      return path_.get(index);
+    public java.util.List<java.lang.Integer>
+    getKpiSampleTypesValueList() {
+      return kpiSampleTypes_;
     }
     /**
-     * <code>repeated .context.EndPointId path = 3;</code>
+     * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
+     * @param index The index of the value to return.
+     * @return The enum numeric value on the wire of kpiSampleTypes at the given index.
      */
     @java.lang.Override
-    public context.ContextOuterClass.EndPointIdOrBuilder getPathOrBuilder(
-        int index) {
-      return path_.get(index);
+    public int getKpiSampleTypesValue(int index) {
+      return kpiSampleTypes_.get(index);
     }
+    private int kpiSampleTypesMemoizedSerializedSize;
 
     private byte memoizedIsInitialized = -1;
     @java.lang.Override
@@ -32219,14 +33619,19 @@ public final class ContextOuterClass {
     @java.lang.Override
     public void writeTo(com.google.protobuf.CodedOutputStream output)
                         throws java.io.IOException {
-      if (connectionId_ != null) {
-        output.writeMessage(1, getConnectionId());
+      getSerializedSize();
+      if (endpointId_ != null) {
+        output.writeMessage(1, getEndpointId());
       }
-      if (relatedServiceId_ != null) {
-        output.writeMessage(2, getRelatedServiceId());
+      if (!getEndpointTypeBytes().isEmpty()) {
+        com.google.protobuf.GeneratedMessageV3.writeString(output, 2, endpointType_);
+      }
+      if (getKpiSampleTypesList().size() > 0) {
+        output.writeUInt32NoTag(26);
+        output.writeUInt32NoTag(kpiSampleTypesMemoizedSerializedSize);
       }
-      for (int i = 0; i < path_.size(); i++) {
-        output.writeMessage(3, path_.get(i));
+      for (int i = 0; i < kpiSampleTypes_.size(); i++) {
+        output.writeEnumNoTag(kpiSampleTypes_.get(i));
       }
       unknownFields.writeTo(output);
     }
@@ -32237,17 +33642,24 @@ public final class ContextOuterClass {
       if (size != -1) return size;
 
       size = 0;
-      if (connectionId_ != null) {
+      if (endpointId_ != null) {
         size += com.google.protobuf.CodedOutputStream
-          .computeMessageSize(1, getConnectionId());
+          .computeMessageSize(1, getEndpointId());
       }
-      if (relatedServiceId_ != null) {
-        size += com.google.protobuf.CodedOutputStream
-          .computeMessageSize(2, getRelatedServiceId());
+      if (!getEndpointTypeBytes().isEmpty()) {
+        size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, endpointType_);
       }
-      for (int i = 0; i < path_.size(); i++) {
-        size += com.google.protobuf.CodedOutputStream
-          .computeMessageSize(3, path_.get(i));
+      {
+        int dataSize = 0;
+        for (int i = 0; i < kpiSampleTypes_.size(); i++) {
+          dataSize += com.google.protobuf.CodedOutputStream
+            .computeEnumSizeNoTag(kpiSampleTypes_.get(i));
+        }
+        size += dataSize;
+        if (!getKpiSampleTypesList().isEmpty()) {  size += 1;
+          size += com.google.protobuf.CodedOutputStream
+            .computeUInt32SizeNoTag(dataSize);
+        }kpiSampleTypesMemoizedSerializedSize = dataSize;
       }
       size += unknownFields.getSerializedSize();
       memoizedSize = size;
@@ -32259,23 +33671,19 @@ public final class ContextOuterClass {
       if (obj == this) {
        return true;
       }
-      if (!(obj instanceof context.ContextOuterClass.Connection)) {
+      if (!(obj instanceof context.ContextOuterClass.EndPoint)) {
         return super.equals(obj);
       }
-      context.ContextOuterClass.Connection other = (context.ContextOuterClass.Connection) obj;
+      context.ContextOuterClass.EndPoint other = (context.ContextOuterClass.EndPoint) obj;
 
-      if (hasConnectionId() != other.hasConnectionId()) return false;
-      if (hasConnectionId()) {
-        if (!getConnectionId()
-            .equals(other.getConnectionId())) return false;
-      }
-      if (hasRelatedServiceId() != other.hasRelatedServiceId()) return false;
-      if (hasRelatedServiceId()) {
-        if (!getRelatedServiceId()
-            .equals(other.getRelatedServiceId())) return false;
+      if (hasEndpointId() != other.hasEndpointId()) return false;
+      if (hasEndpointId()) {
+        if (!getEndpointId()
+            .equals(other.getEndpointId())) return false;
       }
-      if (!getPathList()
-          .equals(other.getPathList())) return false;
+      if (!getEndpointType()
+          .equals(other.getEndpointType())) return false;
+      if (!kpiSampleTypes_.equals(other.kpiSampleTypes_)) return false;
       if (!unknownFields.equals(other.unknownFields)) return false;
       return true;
     }
@@ -32287,86 +33695,84 @@ public final class ContextOuterClass {
       }
       int hash = 41;
       hash = (19 * hash) + getDescriptor().hashCode();
-      if (hasConnectionId()) {
-        hash = (37 * hash) + CONNECTION_ID_FIELD_NUMBER;
-        hash = (53 * hash) + getConnectionId().hashCode();
-      }
-      if (hasRelatedServiceId()) {
-        hash = (37 * hash) + RELATED_SERVICE_ID_FIELD_NUMBER;
-        hash = (53 * hash) + getRelatedServiceId().hashCode();
+      if (hasEndpointId()) {
+        hash = (37 * hash) + ENDPOINT_ID_FIELD_NUMBER;
+        hash = (53 * hash) + getEndpointId().hashCode();
       }
-      if (getPathCount() > 0) {
-        hash = (37 * hash) + PATH_FIELD_NUMBER;
-        hash = (53 * hash) + getPathList().hashCode();
+      hash = (37 * hash) + ENDPOINT_TYPE_FIELD_NUMBER;
+      hash = (53 * hash) + getEndpointType().hashCode();
+      if (getKpiSampleTypesCount() > 0) {
+        hash = (37 * hash) + KPI_SAMPLE_TYPES_FIELD_NUMBER;
+        hash = (53 * hash) + kpiSampleTypes_.hashCode();
       }
       hash = (29 * hash) + unknownFields.hashCode();
       memoizedHashCode = hash;
       return hash;
     }
 
-    public static context.ContextOuterClass.Connection parseFrom(
+    public static context.ContextOuterClass.EndPoint parseFrom(
         java.nio.ByteBuffer data)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data);
     }
-    public static context.ContextOuterClass.Connection parseFrom(
+    public static context.ContextOuterClass.EndPoint parseFrom(
         java.nio.ByteBuffer data,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data, extensionRegistry);
     }
-    public static context.ContextOuterClass.Connection parseFrom(
+    public static context.ContextOuterClass.EndPoint parseFrom(
         com.google.protobuf.ByteString data)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data);
     }
-    public static context.ContextOuterClass.Connection parseFrom(
+    public static context.ContextOuterClass.EndPoint parseFrom(
         com.google.protobuf.ByteString data,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data, extensionRegistry);
     }
-    public static context.ContextOuterClass.Connection parseFrom(byte[] data)
+    public static context.ContextOuterClass.EndPoint parseFrom(byte[] data)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data);
     }
-    public static context.ContextOuterClass.Connection parseFrom(
+    public static context.ContextOuterClass.EndPoint parseFrom(
         byte[] data,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data, extensionRegistry);
     }
-    public static context.ContextOuterClass.Connection parseFrom(java.io.InputStream input)
+    public static context.ContextOuterClass.EndPoint parseFrom(java.io.InputStream input)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseWithIOException(PARSER, input);
     }
-    public static context.ContextOuterClass.Connection parseFrom(
+    public static context.ContextOuterClass.EndPoint parseFrom(
         java.io.InputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseWithIOException(PARSER, input, extensionRegistry);
     }
-    public static context.ContextOuterClass.Connection parseDelimitedFrom(java.io.InputStream input)
+    public static context.ContextOuterClass.EndPoint parseDelimitedFrom(java.io.InputStream input)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseDelimitedWithIOException(PARSER, input);
     }
-    public static context.ContextOuterClass.Connection parseDelimitedFrom(
+    public static context.ContextOuterClass.EndPoint parseDelimitedFrom(
         java.io.InputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseDelimitedWithIOException(PARSER, input, extensionRegistry);
     }
-    public static context.ContextOuterClass.Connection parseFrom(
+    public static context.ContextOuterClass.EndPoint parseFrom(
         com.google.protobuf.CodedInputStream input)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseWithIOException(PARSER, input);
     }
-    public static context.ContextOuterClass.Connection parseFrom(
+    public static context.ContextOuterClass.EndPoint parseFrom(
         com.google.protobuf.CodedInputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws java.io.IOException {
@@ -32379,7 +33785,7 @@ public final class ContextOuterClass {
     public static Builder newBuilder() {
       return DEFAULT_INSTANCE.toBuilder();
     }
-    public static Builder newBuilder(context.ContextOuterClass.Connection prototype) {
+    public static Builder newBuilder(context.ContextOuterClass.EndPoint prototype) {
       return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
     }
     @java.lang.Override
@@ -32395,26 +33801,26 @@ public final class ContextOuterClass {
       return builder;
     }
     /**
-     * Protobuf type {@code context.Connection}
+     * Protobuf type {@code context.EndPoint}
      */
     public static final class Builder extends
         com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
-        // @@protoc_insertion_point(builder_implements:context.Connection)
-        context.ContextOuterClass.ConnectionOrBuilder {
+        // @@protoc_insertion_point(builder_implements:context.EndPoint)
+        context.ContextOuterClass.EndPointOrBuilder {
       public static final com.google.protobuf.Descriptors.Descriptor
           getDescriptor() {
-        return context.ContextOuterClass.internal_static_context_Connection_descriptor;
+        return context.ContextOuterClass.internal_static_context_EndPoint_descriptor;
       }
 
       @java.lang.Override
       protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
           internalGetFieldAccessorTable() {
-        return context.ContextOuterClass.internal_static_context_Connection_fieldAccessorTable
+        return context.ContextOuterClass.internal_static_context_EndPoint_fieldAccessorTable
             .ensureFieldAccessorsInitialized(
-                context.ContextOuterClass.Connection.class, context.ContextOuterClass.Connection.Builder.class);
+                context.ContextOuterClass.EndPoint.class, context.ContextOuterClass.EndPoint.Builder.class);
       }
 
-      // Construct using context.ContextOuterClass.Connection.newBuilder()
+      // Construct using context.ContextOuterClass.EndPoint.newBuilder()
       private Builder() {
         maybeForceBuilderInitialization();
       }
@@ -32427,47 +33833,38 @@ public final class ContextOuterClass {
       private void maybeForceBuilderInitialization() {
         if (com.google.protobuf.GeneratedMessageV3
                 .alwaysUseFieldBuilders) {
-          getPathFieldBuilder();
         }
       }
       @java.lang.Override
       public Builder clear() {
         super.clear();
-        if (connectionIdBuilder_ == null) {
-          connectionId_ = null;
-        } else {
-          connectionId_ = null;
-          connectionIdBuilder_ = null;
-        }
-        if (relatedServiceIdBuilder_ == null) {
-          relatedServiceId_ = null;
-        } else {
-          relatedServiceId_ = null;
-          relatedServiceIdBuilder_ = null;
-        }
-        if (pathBuilder_ == null) {
-          path_ = java.util.Collections.emptyList();
-          bitField0_ = (bitField0_ & ~0x00000001);
+        if (endpointIdBuilder_ == null) {
+          endpointId_ = null;
         } else {
-          pathBuilder_.clear();
+          endpointId_ = null;
+          endpointIdBuilder_ = null;
         }
+        endpointType_ = "";
+
+        kpiSampleTypes_ = java.util.Collections.emptyList();
+        bitField0_ = (bitField0_ & ~0x00000001);
         return this;
       }
 
       @java.lang.Override
       public com.google.protobuf.Descriptors.Descriptor
           getDescriptorForType() {
-        return context.ContextOuterClass.internal_static_context_Connection_descriptor;
+        return context.ContextOuterClass.internal_static_context_EndPoint_descriptor;
       }
 
       @java.lang.Override
-      public context.ContextOuterClass.Connection getDefaultInstanceForType() {
-        return context.ContextOuterClass.Connection.getDefaultInstance();
+      public context.ContextOuterClass.EndPoint getDefaultInstanceForType() {
+        return context.ContextOuterClass.EndPoint.getDefaultInstance();
       }
 
       @java.lang.Override
-      public context.ContextOuterClass.Connection build() {
-        context.ContextOuterClass.Connection result = buildPartial();
+      public context.ContextOuterClass.EndPoint build() {
+        context.ContextOuterClass.EndPoint result = buildPartial();
         if (!result.isInitialized()) {
           throw newUninitializedMessageException(result);
         }
@@ -32475,28 +33872,20 @@ public final class ContextOuterClass {
       }
 
       @java.lang.Override
-      public context.ContextOuterClass.Connection buildPartial() {
-        context.ContextOuterClass.Connection result = new context.ContextOuterClass.Connection(this);
+      public context.ContextOuterClass.EndPoint buildPartial() {
+        context.ContextOuterClass.EndPoint result = new context.ContextOuterClass.EndPoint(this);
         int from_bitField0_ = bitField0_;
-        if (connectionIdBuilder_ == null) {
-          result.connectionId_ = connectionId_;
-        } else {
-          result.connectionId_ = connectionIdBuilder_.build();
-        }
-        if (relatedServiceIdBuilder_ == null) {
-          result.relatedServiceId_ = relatedServiceId_;
+        if (endpointIdBuilder_ == null) {
+          result.endpointId_ = endpointId_;
         } else {
-          result.relatedServiceId_ = relatedServiceIdBuilder_.build();
+          result.endpointId_ = endpointIdBuilder_.build();
         }
-        if (pathBuilder_ == null) {
-          if (((bitField0_ & 0x00000001) != 0)) {
-            path_ = java.util.Collections.unmodifiableList(path_);
-            bitField0_ = (bitField0_ & ~0x00000001);
-          }
-          result.path_ = path_;
-        } else {
-          result.path_ = pathBuilder_.build();
+        result.endpointType_ = endpointType_;
+        if (((bitField0_ & 0x00000001) != 0)) {
+          kpiSampleTypes_ = java.util.Collections.unmodifiableList(kpiSampleTypes_);
+          bitField0_ = (bitField0_ & ~0x00000001);
         }
+        result.kpiSampleTypes_ = kpiSampleTypes_;
         onBuilt();
         return result;
       }
@@ -32535,47 +33924,32 @@ public final class ContextOuterClass {
       }
       @java.lang.Override
       public Builder mergeFrom(com.google.protobuf.Message other) {
-        if (other instanceof context.ContextOuterClass.Connection) {
-          return mergeFrom((context.ContextOuterClass.Connection)other);
+        if (other instanceof context.ContextOuterClass.EndPoint) {
+          return mergeFrom((context.ContextOuterClass.EndPoint)other);
         } else {
           super.mergeFrom(other);
           return this;
         }
       }
 
-      public Builder mergeFrom(context.ContextOuterClass.Connection other) {
-        if (other == context.ContextOuterClass.Connection.getDefaultInstance()) return this;
-        if (other.hasConnectionId()) {
-          mergeConnectionId(other.getConnectionId());
+      public Builder mergeFrom(context.ContextOuterClass.EndPoint other) {
+        if (other == context.ContextOuterClass.EndPoint.getDefaultInstance()) return this;
+        if (other.hasEndpointId()) {
+          mergeEndpointId(other.getEndpointId());
         }
-        if (other.hasRelatedServiceId()) {
-          mergeRelatedServiceId(other.getRelatedServiceId());
+        if (!other.getEndpointType().isEmpty()) {
+          endpointType_ = other.endpointType_;
+          onChanged();
         }
-        if (pathBuilder_ == null) {
-          if (!other.path_.isEmpty()) {
-            if (path_.isEmpty()) {
-              path_ = other.path_;
-              bitField0_ = (bitField0_ & ~0x00000001);
-            } else {
-              ensurePathIsMutable();
-              path_.addAll(other.path_);
-            }
-            onChanged();
-          }
-        } else {
-          if (!other.path_.isEmpty()) {
-            if (pathBuilder_.isEmpty()) {
-              pathBuilder_.dispose();
-              pathBuilder_ = null;
-              path_ = other.path_;
-              bitField0_ = (bitField0_ & ~0x00000001);
-              pathBuilder_ = 
-                com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders ?
-                   getPathFieldBuilder() : null;
-            } else {
-              pathBuilder_.addAllMessages(other.path_);
-            }
+        if (!other.kpiSampleTypes_.isEmpty()) {
+          if (kpiSampleTypes_.isEmpty()) {
+            kpiSampleTypes_ = other.kpiSampleTypes_;
+            bitField0_ = (bitField0_ & ~0x00000001);
+          } else {
+            ensureKpiSampleTypesIsMutable();
+            kpiSampleTypes_.addAll(other.kpiSampleTypes_);
           }
+          onChanged();
         }
         this.mergeUnknownFields(other.unknownFields);
         onChanged();
@@ -32592,497 +33966,354 @@ public final class ContextOuterClass {
           com.google.protobuf.CodedInputStream input,
           com.google.protobuf.ExtensionRegistryLite extensionRegistry)
           throws java.io.IOException {
-        context.ContextOuterClass.Connection parsedMessage = null;
+        context.ContextOuterClass.EndPoint parsedMessage = null;
         try {
           parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
         } catch (com.google.protobuf.InvalidProtocolBufferException e) {
-          parsedMessage = (context.ContextOuterClass.Connection) e.getUnfinishedMessage();
-          throw e.unwrapIOException();
-        } finally {
-          if (parsedMessage != null) {
-            mergeFrom(parsedMessage);
-          }
-        }
-        return this;
-      }
-      private int bitField0_;
-
-      private context.ContextOuterClass.ConnectionId connectionId_;
-      private com.google.protobuf.SingleFieldBuilderV3<
-          context.ContextOuterClass.ConnectionId, context.ContextOuterClass.ConnectionId.Builder, context.ContextOuterClass.ConnectionIdOrBuilder> connectionIdBuilder_;
-      /**
-       * <code>.context.ConnectionId connection_id = 1;</code>
-       * @return Whether the connectionId field is set.
-       */
-      public boolean hasConnectionId() {
-        return connectionIdBuilder_ != null || connectionId_ != null;
-      }
-      /**
-       * <code>.context.ConnectionId connection_id = 1;</code>
-       * @return The connectionId.
-       */
-      public context.ContextOuterClass.ConnectionId getConnectionId() {
-        if (connectionIdBuilder_ == null) {
-          return connectionId_ == null ? context.ContextOuterClass.ConnectionId.getDefaultInstance() : connectionId_;
-        } else {
-          return connectionIdBuilder_.getMessage();
-        }
-      }
-      /**
-       * <code>.context.ConnectionId connection_id = 1;</code>
-       */
-      public Builder setConnectionId(context.ContextOuterClass.ConnectionId value) {
-        if (connectionIdBuilder_ == null) {
-          if (value == null) {
-            throw new NullPointerException();
-          }
-          connectionId_ = value;
-          onChanged();
-        } else {
-          connectionIdBuilder_.setMessage(value);
-        }
-
-        return this;
-      }
-      /**
-       * <code>.context.ConnectionId connection_id = 1;</code>
-       */
-      public Builder setConnectionId(
-          context.ContextOuterClass.ConnectionId.Builder builderForValue) {
-        if (connectionIdBuilder_ == null) {
-          connectionId_ = builderForValue.build();
-          onChanged();
-        } else {
-          connectionIdBuilder_.setMessage(builderForValue.build());
-        }
-
-        return this;
-      }
-      /**
-       * <code>.context.ConnectionId connection_id = 1;</code>
-       */
-      public Builder mergeConnectionId(context.ContextOuterClass.ConnectionId value) {
-        if (connectionIdBuilder_ == null) {
-          if (connectionId_ != null) {
-            connectionId_ =
-              context.ContextOuterClass.ConnectionId.newBuilder(connectionId_).mergeFrom(value).buildPartial();
-          } else {
-            connectionId_ = value;
-          }
-          onChanged();
-        } else {
-          connectionIdBuilder_.mergeFrom(value);
-        }
-
-        return this;
-      }
-      /**
-       * <code>.context.ConnectionId connection_id = 1;</code>
-       */
-      public Builder clearConnectionId() {
-        if (connectionIdBuilder_ == null) {
-          connectionId_ = null;
-          onChanged();
-        } else {
-          connectionId_ = null;
-          connectionIdBuilder_ = null;
-        }
-
-        return this;
-      }
-      /**
-       * <code>.context.ConnectionId connection_id = 1;</code>
-       */
-      public context.ContextOuterClass.ConnectionId.Builder getConnectionIdBuilder() {
-        
-        onChanged();
-        return getConnectionIdFieldBuilder().getBuilder();
-      }
-      /**
-       * <code>.context.ConnectionId connection_id = 1;</code>
-       */
-      public context.ContextOuterClass.ConnectionIdOrBuilder getConnectionIdOrBuilder() {
-        if (connectionIdBuilder_ != null) {
-          return connectionIdBuilder_.getMessageOrBuilder();
-        } else {
-          return connectionId_ == null ?
-              context.ContextOuterClass.ConnectionId.getDefaultInstance() : connectionId_;
-        }
-      }
-      /**
-       * <code>.context.ConnectionId connection_id = 1;</code>
-       */
-      private com.google.protobuf.SingleFieldBuilderV3<
-          context.ContextOuterClass.ConnectionId, context.ContextOuterClass.ConnectionId.Builder, context.ContextOuterClass.ConnectionIdOrBuilder> 
-          getConnectionIdFieldBuilder() {
-        if (connectionIdBuilder_ == null) {
-          connectionIdBuilder_ = new com.google.protobuf.SingleFieldBuilderV3<
-              context.ContextOuterClass.ConnectionId, context.ContextOuterClass.ConnectionId.Builder, context.ContextOuterClass.ConnectionIdOrBuilder>(
-                  getConnectionId(),
-                  getParentForChildren(),
-                  isClean());
-          connectionId_ = null;
+          parsedMessage = (context.ContextOuterClass.EndPoint) e.getUnfinishedMessage();
+          throw e.unwrapIOException();
+        } finally {
+          if (parsedMessage != null) {
+            mergeFrom(parsedMessage);
+          }
         }
-        return connectionIdBuilder_;
+        return this;
       }
+      private int bitField0_;
 
-      private context.ContextOuterClass.ServiceId relatedServiceId_;
+      private context.ContextOuterClass.EndPointId endpointId_;
       private com.google.protobuf.SingleFieldBuilderV3<
-          context.ContextOuterClass.ServiceId, context.ContextOuterClass.ServiceId.Builder, context.ContextOuterClass.ServiceIdOrBuilder> relatedServiceIdBuilder_;
+          context.ContextOuterClass.EndPointId, context.ContextOuterClass.EndPointId.Builder, context.ContextOuterClass.EndPointIdOrBuilder> endpointIdBuilder_;
       /**
-       * <code>.context.ServiceId related_service_id = 2;</code>
-       * @return Whether the relatedServiceId field is set.
+       * <code>.context.EndPointId endpoint_id = 1;</code>
+       * @return Whether the endpointId field is set.
        */
-      public boolean hasRelatedServiceId() {
-        return relatedServiceIdBuilder_ != null || relatedServiceId_ != null;
+      public boolean hasEndpointId() {
+        return endpointIdBuilder_ != null || endpointId_ != null;
       }
       /**
-       * <code>.context.ServiceId related_service_id = 2;</code>
-       * @return The relatedServiceId.
+       * <code>.context.EndPointId endpoint_id = 1;</code>
+       * @return The endpointId.
        */
-      public context.ContextOuterClass.ServiceId getRelatedServiceId() {
-        if (relatedServiceIdBuilder_ == null) {
-          return relatedServiceId_ == null ? context.ContextOuterClass.ServiceId.getDefaultInstance() : relatedServiceId_;
+      public context.ContextOuterClass.EndPointId getEndpointId() {
+        if (endpointIdBuilder_ == null) {
+          return endpointId_ == null ? context.ContextOuterClass.EndPointId.getDefaultInstance() : endpointId_;
         } else {
-          return relatedServiceIdBuilder_.getMessage();
+          return endpointIdBuilder_.getMessage();
         }
       }
       /**
-       * <code>.context.ServiceId related_service_id = 2;</code>
+       * <code>.context.EndPointId endpoint_id = 1;</code>
        */
-      public Builder setRelatedServiceId(context.ContextOuterClass.ServiceId value) {
-        if (relatedServiceIdBuilder_ == null) {
+      public Builder setEndpointId(context.ContextOuterClass.EndPointId value) {
+        if (endpointIdBuilder_ == null) {
           if (value == null) {
             throw new NullPointerException();
           }
-          relatedServiceId_ = value;
+          endpointId_ = value;
           onChanged();
         } else {
-          relatedServiceIdBuilder_.setMessage(value);
+          endpointIdBuilder_.setMessage(value);
         }
 
         return this;
       }
       /**
-       * <code>.context.ServiceId related_service_id = 2;</code>
+       * <code>.context.EndPointId endpoint_id = 1;</code>
        */
-      public Builder setRelatedServiceId(
-          context.ContextOuterClass.ServiceId.Builder builderForValue) {
-        if (relatedServiceIdBuilder_ == null) {
-          relatedServiceId_ = builderForValue.build();
+      public Builder setEndpointId(
+          context.ContextOuterClass.EndPointId.Builder builderForValue) {
+        if (endpointIdBuilder_ == null) {
+          endpointId_ = builderForValue.build();
           onChanged();
         } else {
-          relatedServiceIdBuilder_.setMessage(builderForValue.build());
+          endpointIdBuilder_.setMessage(builderForValue.build());
         }
 
         return this;
       }
       /**
-       * <code>.context.ServiceId related_service_id = 2;</code>
+       * <code>.context.EndPointId endpoint_id = 1;</code>
        */
-      public Builder mergeRelatedServiceId(context.ContextOuterClass.ServiceId value) {
-        if (relatedServiceIdBuilder_ == null) {
-          if (relatedServiceId_ != null) {
-            relatedServiceId_ =
-              context.ContextOuterClass.ServiceId.newBuilder(relatedServiceId_).mergeFrom(value).buildPartial();
+      public Builder mergeEndpointId(context.ContextOuterClass.EndPointId value) {
+        if (endpointIdBuilder_ == null) {
+          if (endpointId_ != null) {
+            endpointId_ =
+              context.ContextOuterClass.EndPointId.newBuilder(endpointId_).mergeFrom(value).buildPartial();
           } else {
-            relatedServiceId_ = value;
+            endpointId_ = value;
           }
           onChanged();
         } else {
-          relatedServiceIdBuilder_.mergeFrom(value);
+          endpointIdBuilder_.mergeFrom(value);
         }
 
         return this;
       }
       /**
-       * <code>.context.ServiceId related_service_id = 2;</code>
+       * <code>.context.EndPointId endpoint_id = 1;</code>
        */
-      public Builder clearRelatedServiceId() {
-        if (relatedServiceIdBuilder_ == null) {
-          relatedServiceId_ = null;
+      public Builder clearEndpointId() {
+        if (endpointIdBuilder_ == null) {
+          endpointId_ = null;
           onChanged();
         } else {
-          relatedServiceId_ = null;
-          relatedServiceIdBuilder_ = null;
+          endpointId_ = null;
+          endpointIdBuilder_ = null;
         }
 
         return this;
       }
       /**
-       * <code>.context.ServiceId related_service_id = 2;</code>
+       * <code>.context.EndPointId endpoint_id = 1;</code>
        */
-      public context.ContextOuterClass.ServiceId.Builder getRelatedServiceIdBuilder() {
+      public context.ContextOuterClass.EndPointId.Builder getEndpointIdBuilder() {
         
         onChanged();
-        return getRelatedServiceIdFieldBuilder().getBuilder();
+        return getEndpointIdFieldBuilder().getBuilder();
       }
       /**
-       * <code>.context.ServiceId related_service_id = 2;</code>
+       * <code>.context.EndPointId endpoint_id = 1;</code>
        */
-      public context.ContextOuterClass.ServiceIdOrBuilder getRelatedServiceIdOrBuilder() {
-        if (relatedServiceIdBuilder_ != null) {
-          return relatedServiceIdBuilder_.getMessageOrBuilder();
+      public context.ContextOuterClass.EndPointIdOrBuilder getEndpointIdOrBuilder() {
+        if (endpointIdBuilder_ != null) {
+          return endpointIdBuilder_.getMessageOrBuilder();
         } else {
-          return relatedServiceId_ == null ?
-              context.ContextOuterClass.ServiceId.getDefaultInstance() : relatedServiceId_;
+          return endpointId_ == null ?
+              context.ContextOuterClass.EndPointId.getDefaultInstance() : endpointId_;
         }
       }
       /**
-       * <code>.context.ServiceId related_service_id = 2;</code>
+       * <code>.context.EndPointId endpoint_id = 1;</code>
        */
       private com.google.protobuf.SingleFieldBuilderV3<
-          context.ContextOuterClass.ServiceId, context.ContextOuterClass.ServiceId.Builder, context.ContextOuterClass.ServiceIdOrBuilder> 
-          getRelatedServiceIdFieldBuilder() {
-        if (relatedServiceIdBuilder_ == null) {
-          relatedServiceIdBuilder_ = new com.google.protobuf.SingleFieldBuilderV3<
-              context.ContextOuterClass.ServiceId, context.ContextOuterClass.ServiceId.Builder, context.ContextOuterClass.ServiceIdOrBuilder>(
-                  getRelatedServiceId(),
+          context.ContextOuterClass.EndPointId, context.ContextOuterClass.EndPointId.Builder, context.ContextOuterClass.EndPointIdOrBuilder> 
+          getEndpointIdFieldBuilder() {
+        if (endpointIdBuilder_ == null) {
+          endpointIdBuilder_ = new com.google.protobuf.SingleFieldBuilderV3<
+              context.ContextOuterClass.EndPointId, context.ContextOuterClass.EndPointId.Builder, context.ContextOuterClass.EndPointIdOrBuilder>(
+                  getEndpointId(),
                   getParentForChildren(),
                   isClean());
-          relatedServiceId_ = null;
+          endpointId_ = null;
         }
-        return relatedServiceIdBuilder_;
-      }
-
-      private java.util.List<context.ContextOuterClass.EndPointId> path_ =
-        java.util.Collections.emptyList();
-      private void ensurePathIsMutable() {
-        if (!((bitField0_ & 0x00000001) != 0)) {
-          path_ = new java.util.ArrayList<context.ContextOuterClass.EndPointId>(path_);
-          bitField0_ |= 0x00000001;
-         }
+        return endpointIdBuilder_;
       }
 
-      private com.google.protobuf.RepeatedFieldBuilderV3<
-          context.ContextOuterClass.EndPointId, context.ContextOuterClass.EndPointId.Builder, context.ContextOuterClass.EndPointIdOrBuilder> pathBuilder_;
-
+      private java.lang.Object endpointType_ = "";
       /**
-       * <code>repeated .context.EndPointId path = 3;</code>
+       * <code>string endpoint_type = 2;</code>
+       * @return The endpointType.
        */
-      public java.util.List<context.ContextOuterClass.EndPointId> getPathList() {
-        if (pathBuilder_ == null) {
-          return java.util.Collections.unmodifiableList(path_);
+      public java.lang.String getEndpointType() {
+        java.lang.Object ref = endpointType_;
+        if (!(ref instanceof java.lang.String)) {
+          com.google.protobuf.ByteString bs =
+              (com.google.protobuf.ByteString) ref;
+          java.lang.String s = bs.toStringUtf8();
+          endpointType_ = s;
+          return s;
         } else {
-          return pathBuilder_.getMessageList();
+          return (java.lang.String) ref;
         }
       }
       /**
-       * <code>repeated .context.EndPointId path = 3;</code>
+       * <code>string endpoint_type = 2;</code>
+       * @return The bytes for endpointType.
        */
-      public int getPathCount() {
-        if (pathBuilder_ == null) {
-          return path_.size();
+      public com.google.protobuf.ByteString
+          getEndpointTypeBytes() {
+        java.lang.Object ref = endpointType_;
+        if (ref instanceof String) {
+          com.google.protobuf.ByteString b = 
+              com.google.protobuf.ByteString.copyFromUtf8(
+                  (java.lang.String) ref);
+          endpointType_ = b;
+          return b;
         } else {
-          return pathBuilder_.getCount();
+          return (com.google.protobuf.ByteString) ref;
         }
       }
       /**
-       * <code>repeated .context.EndPointId path = 3;</code>
+       * <code>string endpoint_type = 2;</code>
+       * @param value The endpointType to set.
+       * @return This builder for chaining.
        */
-      public context.ContextOuterClass.EndPointId getPath(int index) {
-        if (pathBuilder_ == null) {
-          return path_.get(index);
-        } else {
-          return pathBuilder_.getMessage(index);
-        }
+      public Builder setEndpointType(
+          java.lang.String value) {
+        if (value == null) {
+    throw new NullPointerException();
+  }
+  
+        endpointType_ = value;
+        onChanged();
+        return this;
       }
       /**
-       * <code>repeated .context.EndPointId path = 3;</code>
+       * <code>string endpoint_type = 2;</code>
+       * @return This builder for chaining.
        */
-      public Builder setPath(
-          int index, context.ContextOuterClass.EndPointId value) {
-        if (pathBuilder_ == null) {
-          if (value == null) {
-            throw new NullPointerException();
-          }
-          ensurePathIsMutable();
-          path_.set(index, value);
-          onChanged();
-        } else {
-          pathBuilder_.setMessage(index, value);
-        }
+      public Builder clearEndpointType() {
+        
+        endpointType_ = getDefaultInstance().getEndpointType();
+        onChanged();
         return this;
       }
       /**
-       * <code>repeated .context.EndPointId path = 3;</code>
+       * <code>string endpoint_type = 2;</code>
+       * @param value The bytes for endpointType to set.
+       * @return This builder for chaining.
        */
-      public Builder setPath(
-          int index, context.ContextOuterClass.EndPointId.Builder builderForValue) {
-        if (pathBuilder_ == null) {
-          ensurePathIsMutable();
-          path_.set(index, builderForValue.build());
-          onChanged();
-        } else {
-          pathBuilder_.setMessage(index, builderForValue.build());
-        }
+      public Builder setEndpointTypeBytes(
+          com.google.protobuf.ByteString value) {
+        if (value == null) {
+    throw new NullPointerException();
+  }
+  checkByteStringIsUtf8(value);
+        
+        endpointType_ = value;
+        onChanged();
         return this;
       }
-      /**
-       * <code>repeated .context.EndPointId path = 3;</code>
-       */
-      public Builder addPath(context.ContextOuterClass.EndPointId value) {
-        if (pathBuilder_ == null) {
-          if (value == null) {
-            throw new NullPointerException();
-          }
-          ensurePathIsMutable();
-          path_.add(value);
-          onChanged();
-        } else {
-          pathBuilder_.addMessage(value);
+
+      private java.util.List<java.lang.Integer> kpiSampleTypes_ =
+        java.util.Collections.emptyList();
+      private void ensureKpiSampleTypesIsMutable() {
+        if (!((bitField0_ & 0x00000001) != 0)) {
+          kpiSampleTypes_ = new java.util.ArrayList<java.lang.Integer>(kpiSampleTypes_);
+          bitField0_ |= 0x00000001;
         }
-        return this;
       }
       /**
-       * <code>repeated .context.EndPointId path = 3;</code>
+       * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
+       * @return A list containing the kpiSampleTypes.
        */
-      public Builder addPath(
-          int index, context.ContextOuterClass.EndPointId value) {
-        if (pathBuilder_ == null) {
-          if (value == null) {
-            throw new NullPointerException();
-          }
-          ensurePathIsMutable();
-          path_.add(index, value);
-          onChanged();
-        } else {
-          pathBuilder_.addMessage(index, value);
-        }
-        return this;
+      public java.util.List<kpi_sample_types.KpiSampleTypes.KpiSampleType> getKpiSampleTypesList() {
+        return new com.google.protobuf.Internal.ListAdapter<
+            java.lang.Integer, kpi_sample_types.KpiSampleTypes.KpiSampleType>(kpiSampleTypes_, kpiSampleTypes_converter_);
       }
       /**
-       * <code>repeated .context.EndPointId path = 3;</code>
+       * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
+       * @return The count of kpiSampleTypes.
        */
-      public Builder addPath(
-          context.ContextOuterClass.EndPointId.Builder builderForValue) {
-        if (pathBuilder_ == null) {
-          ensurePathIsMutable();
-          path_.add(builderForValue.build());
-          onChanged();
-        } else {
-          pathBuilder_.addMessage(builderForValue.build());
-        }
-        return this;
+      public int getKpiSampleTypesCount() {
+        return kpiSampleTypes_.size();
       }
       /**
-       * <code>repeated .context.EndPointId path = 3;</code>
+       * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
+       * @param index The index of the element to return.
+       * @return The kpiSampleTypes at the given index.
        */
-      public Builder addPath(
-          int index, context.ContextOuterClass.EndPointId.Builder builderForValue) {
-        if (pathBuilder_ == null) {
-          ensurePathIsMutable();
-          path_.add(index, builderForValue.build());
-          onChanged();
-        } else {
-          pathBuilder_.addMessage(index, builderForValue.build());
-        }
-        return this;
+      public kpi_sample_types.KpiSampleTypes.KpiSampleType getKpiSampleTypes(int index) {
+        return kpiSampleTypes_converter_.convert(kpiSampleTypes_.get(index));
       }
       /**
-       * <code>repeated .context.EndPointId path = 3;</code>
+       * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
+       * @param index The index to set the value at.
+       * @param value The kpiSampleTypes to set.
+       * @return This builder for chaining.
        */
-      public Builder addAllPath(
-          java.lang.Iterable<? extends context.ContextOuterClass.EndPointId> values) {
-        if (pathBuilder_ == null) {
-          ensurePathIsMutable();
-          com.google.protobuf.AbstractMessageLite.Builder.addAll(
-              values, path_);
-          onChanged();
-        } else {
-          pathBuilder_.addAllMessages(values);
+      public Builder setKpiSampleTypes(
+          int index, kpi_sample_types.KpiSampleTypes.KpiSampleType value) {
+        if (value == null) {
+          throw new NullPointerException();
         }
+        ensureKpiSampleTypesIsMutable();
+        kpiSampleTypes_.set(index, value.getNumber());
+        onChanged();
         return this;
       }
       /**
-       * <code>repeated .context.EndPointId path = 3;</code>
+       * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
+       * @param value The kpiSampleTypes to add.
+       * @return This builder for chaining.
        */
-      public Builder clearPath() {
-        if (pathBuilder_ == null) {
-          path_ = java.util.Collections.emptyList();
-          bitField0_ = (bitField0_ & ~0x00000001);
-          onChanged();
-        } else {
-          pathBuilder_.clear();
+      public Builder addKpiSampleTypes(kpi_sample_types.KpiSampleTypes.KpiSampleType value) {
+        if (value == null) {
+          throw new NullPointerException();
         }
+        ensureKpiSampleTypesIsMutable();
+        kpiSampleTypes_.add(value.getNumber());
+        onChanged();
         return this;
       }
       /**
-       * <code>repeated .context.EndPointId path = 3;</code>
+       * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
+       * @param values The kpiSampleTypes to add.
+       * @return This builder for chaining.
        */
-      public Builder removePath(int index) {
-        if (pathBuilder_ == null) {
-          ensurePathIsMutable();
-          path_.remove(index);
-          onChanged();
-        } else {
-          pathBuilder_.remove(index);
+      public Builder addAllKpiSampleTypes(
+          java.lang.Iterable<? extends kpi_sample_types.KpiSampleTypes.KpiSampleType> values) {
+        ensureKpiSampleTypesIsMutable();
+        for (kpi_sample_types.KpiSampleTypes.KpiSampleType value : values) {
+          kpiSampleTypes_.add(value.getNumber());
         }
+        onChanged();
         return this;
       }
       /**
-       * <code>repeated .context.EndPointId path = 3;</code>
+       * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
+       * @return This builder for chaining.
        */
-      public context.ContextOuterClass.EndPointId.Builder getPathBuilder(
-          int index) {
-        return getPathFieldBuilder().getBuilder(index);
+      public Builder clearKpiSampleTypes() {
+        kpiSampleTypes_ = java.util.Collections.emptyList();
+        bitField0_ = (bitField0_ & ~0x00000001);
+        onChanged();
+        return this;
       }
       /**
-       * <code>repeated .context.EndPointId path = 3;</code>
+       * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
+       * @return A list containing the enum numeric values on the wire for kpiSampleTypes.
        */
-      public context.ContextOuterClass.EndPointIdOrBuilder getPathOrBuilder(
-          int index) {
-        if (pathBuilder_ == null) {
-          return path_.get(index);  } else {
-          return pathBuilder_.getMessageOrBuilder(index);
-        }
+      public java.util.List<java.lang.Integer>
+      getKpiSampleTypesValueList() {
+        return java.util.Collections.unmodifiableList(kpiSampleTypes_);
       }
       /**
-       * <code>repeated .context.EndPointId path = 3;</code>
+       * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
+       * @param index The index of the value to return.
+       * @return The enum numeric value on the wire of kpiSampleTypes at the given index.
        */
-      public java.util.List<? extends context.ContextOuterClass.EndPointIdOrBuilder> 
-           getPathOrBuilderList() {
-        if (pathBuilder_ != null) {
-          return pathBuilder_.getMessageOrBuilderList();
-        } else {
-          return java.util.Collections.unmodifiableList(path_);
-        }
+      public int getKpiSampleTypesValue(int index) {
+        return kpiSampleTypes_.get(index);
       }
       /**
-       * <code>repeated .context.EndPointId path = 3;</code>
+       * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
+       * @param index The index of the value to return.
+       * @return The enum numeric value on the wire of kpiSampleTypes at the given index.
+       * @return This builder for chaining.
        */
-      public context.ContextOuterClass.EndPointId.Builder addPathBuilder() {
-        return getPathFieldBuilder().addBuilder(
-            context.ContextOuterClass.EndPointId.getDefaultInstance());
+      public Builder setKpiSampleTypesValue(
+          int index, int value) {
+        ensureKpiSampleTypesIsMutable();
+        kpiSampleTypes_.set(index, value);
+        onChanged();
+        return this;
       }
       /**
-       * <code>repeated .context.EndPointId path = 3;</code>
+       * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
+       * @param value The enum numeric value on the wire for kpiSampleTypes to add.
+       * @return This builder for chaining.
        */
-      public context.ContextOuterClass.EndPointId.Builder addPathBuilder(
-          int index) {
-        return getPathFieldBuilder().addBuilder(
-            index, context.ContextOuterClass.EndPointId.getDefaultInstance());
+      public Builder addKpiSampleTypesValue(int value) {
+        ensureKpiSampleTypesIsMutable();
+        kpiSampleTypes_.add(value);
+        onChanged();
+        return this;
       }
       /**
-       * <code>repeated .context.EndPointId path = 3;</code>
+       * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
+       * @param values The enum numeric values on the wire for kpiSampleTypes to add.
+       * @return This builder for chaining.
        */
-      public java.util.List<context.ContextOuterClass.EndPointId.Builder> 
-           getPathBuilderList() {
-        return getPathFieldBuilder().getBuilderList();
-      }
-      private com.google.protobuf.RepeatedFieldBuilderV3<
-          context.ContextOuterClass.EndPointId, context.ContextOuterClass.EndPointId.Builder, context.ContextOuterClass.EndPointIdOrBuilder> 
-          getPathFieldBuilder() {
-        if (pathBuilder_ == null) {
-          pathBuilder_ = new com.google.protobuf.RepeatedFieldBuilderV3<
-              context.ContextOuterClass.EndPointId, context.ContextOuterClass.EndPointId.Builder, context.ContextOuterClass.EndPointIdOrBuilder>(
-                  path_,
-                  ((bitField0_ & 0x00000001) != 0),
-                  getParentForChildren(),
-                  isClean());
-          path_ = null;
+      public Builder addAllKpiSampleTypesValue(
+          java.lang.Iterable<java.lang.Integer> values) {
+        ensureKpiSampleTypesIsMutable();
+        for (int value : values) {
+          kpiSampleTypes_.add(value);
         }
-        return pathBuilder_;
+        onChanged();
+        return this;
       }
       @java.lang.Override
       public final Builder setUnknownFields(
@@ -33097,95 +34328,108 @@ public final class ContextOuterClass {
       }
 
 
-      // @@protoc_insertion_point(builder_scope:context.Connection)
+      // @@protoc_insertion_point(builder_scope:context.EndPoint)
     }
 
-    // @@protoc_insertion_point(class_scope:context.Connection)
-    private static final context.ContextOuterClass.Connection DEFAULT_INSTANCE;
+    // @@protoc_insertion_point(class_scope:context.EndPoint)
+    private static final context.ContextOuterClass.EndPoint DEFAULT_INSTANCE;
     static {
-      DEFAULT_INSTANCE = new context.ContextOuterClass.Connection();
+      DEFAULT_INSTANCE = new context.ContextOuterClass.EndPoint();
     }
 
-    public static context.ContextOuterClass.Connection getDefaultInstance() {
+    public static context.ContextOuterClass.EndPoint getDefaultInstance() {
       return DEFAULT_INSTANCE;
     }
 
-    private static final com.google.protobuf.Parser<Connection>
-        PARSER = new com.google.protobuf.AbstractParser<Connection>() {
+    private static final com.google.protobuf.Parser<EndPoint>
+        PARSER = new com.google.protobuf.AbstractParser<EndPoint>() {
       @java.lang.Override
-      public Connection parsePartialFrom(
+      public EndPoint parsePartialFrom(
           com.google.protobuf.CodedInputStream input,
           com.google.protobuf.ExtensionRegistryLite extensionRegistry)
           throws com.google.protobuf.InvalidProtocolBufferException {
-        return new Connection(input, extensionRegistry);
+        return new EndPoint(input, extensionRegistry);
       }
     };
 
-    public static com.google.protobuf.Parser<Connection> parser() {
+    public static com.google.protobuf.Parser<EndPoint> parser() {
       return PARSER;
     }
 
     @java.lang.Override
-    public com.google.protobuf.Parser<Connection> getParserForType() {
+    public com.google.protobuf.Parser<EndPoint> getParserForType() {
       return PARSER;
     }
 
     @java.lang.Override
-    public context.ContextOuterClass.Connection getDefaultInstanceForType() {
+    public context.ContextOuterClass.EndPoint getDefaultInstanceForType() {
       return DEFAULT_INSTANCE;
     }
 
   }
 
-  public interface ConnectionIdListOrBuilder extends
-      // @@protoc_insertion_point(interface_extends:context.ConnectionIdList)
+  public interface ConfigRuleOrBuilder extends
+      // @@protoc_insertion_point(interface_extends:context.ConfigRule)
       com.google.protobuf.MessageOrBuilder {
 
     /**
-     * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+     * <code>.context.ConfigActionEnum action = 1;</code>
+     * @return The enum numeric value on the wire for action.
      */
-    java.util.List<context.ContextOuterClass.ConnectionId> 
-        getConnectionIdsList();
+    int getActionValue();
     /**
-     * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+     * <code>.context.ConfigActionEnum action = 1;</code>
+     * @return The action.
      */
-    context.ContextOuterClass.ConnectionId getConnectionIds(int index);
+    context.ContextOuterClass.ConfigActionEnum getAction();
+
     /**
-     * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+     * <code>string resource_key = 2;</code>
+     * @return The resourceKey.
      */
-    int getConnectionIdsCount();
+    java.lang.String getResourceKey();
     /**
-     * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+     * <code>string resource_key = 2;</code>
+     * @return The bytes for resourceKey.
      */
-    java.util.List<? extends context.ContextOuterClass.ConnectionIdOrBuilder> 
-        getConnectionIdsOrBuilderList();
+    com.google.protobuf.ByteString
+        getResourceKeyBytes();
+
     /**
-     * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+     * <code>string resource_value = 3;</code>
+     * @return The resourceValue.
      */
-    context.ContextOuterClass.ConnectionIdOrBuilder getConnectionIdsOrBuilder(
-        int index);
+    java.lang.String getResourceValue();
+    /**
+     * <code>string resource_value = 3;</code>
+     * @return The bytes for resourceValue.
+     */
+    com.google.protobuf.ByteString
+        getResourceValueBytes();
   }
   /**
-   * Protobuf type {@code context.ConnectionIdList}
+   * Protobuf type {@code context.ConfigRule}
    */
-  public static final class ConnectionIdList extends
+  public static final class ConfigRule extends
       com.google.protobuf.GeneratedMessageV3 implements
-      // @@protoc_insertion_point(message_implements:context.ConnectionIdList)
-      ConnectionIdListOrBuilder {
+      // @@protoc_insertion_point(message_implements:context.ConfigRule)
+      ConfigRuleOrBuilder {
   private static final long serialVersionUID = 0L;
-    // Use ConnectionIdList.newBuilder() to construct.
-    private ConnectionIdList(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
+    // Use ConfigRule.newBuilder() to construct.
+    private ConfigRule(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
       super(builder);
     }
-    private ConnectionIdList() {
-      connectionIds_ = java.util.Collections.emptyList();
+    private ConfigRule() {
+      action_ = 0;
+      resourceKey_ = "";
+      resourceValue_ = "";
     }
 
     @java.lang.Override
     @SuppressWarnings({"unused"})
     protected java.lang.Object newInstance(
         UnusedPrivateParameter unused) {
-      return new ConnectionIdList();
+      return new ConfigRule();
     }
 
     @java.lang.Override
@@ -33193,7 +34437,7 @@ public final class ContextOuterClass {
     getUnknownFields() {
       return this.unknownFields;
     }
-    private ConnectionIdList(
+    private ConfigRule(
         com.google.protobuf.CodedInputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
@@ -33201,7 +34445,6 @@ public final class ContextOuterClass {
       if (extensionRegistry == null) {
         throw new java.lang.NullPointerException();
       }
-      int mutable_bitField0_ = 0;
       com.google.protobuf.UnknownFieldSet.Builder unknownFields =
           com.google.protobuf.UnknownFieldSet.newBuilder();
       try {
@@ -33212,13 +34455,22 @@ public final class ContextOuterClass {
             case 0:
               done = true;
               break;
-            case 10: {
-              if (!((mutable_bitField0_ & 0x00000001) != 0)) {
-                connectionIds_ = new java.util.ArrayList<context.ContextOuterClass.ConnectionId>();
-                mutable_bitField0_ |= 0x00000001;
-              }
-              connectionIds_.add(
-                  input.readMessage(context.ContextOuterClass.ConnectionId.parser(), extensionRegistry));
+            case 8: {
+              int rawValue = input.readEnum();
+
+              action_ = rawValue;
+              break;
+            }
+            case 18: {
+              java.lang.String s = input.readStringRequireUtf8();
+
+              resourceKey_ = s;
+              break;
+            }
+            case 26: {
+              java.lang.String s = input.readStringRequireUtf8();
+
+              resourceValue_ = s;
               break;
             }
             default: {
@@ -33236,64 +34488,116 @@ public final class ContextOuterClass {
         throw new com.google.protobuf.InvalidProtocolBufferException(
             e).setUnfinishedMessage(this);
       } finally {
-        if (((mutable_bitField0_ & 0x00000001) != 0)) {
-          connectionIds_ = java.util.Collections.unmodifiableList(connectionIds_);
-        }
         this.unknownFields = unknownFields.build();
         makeExtensionsImmutable();
       }
     }
     public static final com.google.protobuf.Descriptors.Descriptor
         getDescriptor() {
-      return context.ContextOuterClass.internal_static_context_ConnectionIdList_descriptor;
+      return context.ContextOuterClass.internal_static_context_ConfigRule_descriptor;
     }
 
     @java.lang.Override
     protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
         internalGetFieldAccessorTable() {
-      return context.ContextOuterClass.internal_static_context_ConnectionIdList_fieldAccessorTable
+      return context.ContextOuterClass.internal_static_context_ConfigRule_fieldAccessorTable
           .ensureFieldAccessorsInitialized(
-              context.ContextOuterClass.ConnectionIdList.class, context.ContextOuterClass.ConnectionIdList.Builder.class);
+              context.ContextOuterClass.ConfigRule.class, context.ContextOuterClass.ConfigRule.Builder.class);
     }
 
-    public static final int CONNECTION_IDS_FIELD_NUMBER = 1;
-    private java.util.List<context.ContextOuterClass.ConnectionId> connectionIds_;
+    public static final int ACTION_FIELD_NUMBER = 1;
+    private int action_;
     /**
-     * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+     * <code>.context.ConfigActionEnum action = 1;</code>
+     * @return The enum numeric value on the wire for action.
      */
-    @java.lang.Override
-    public java.util.List<context.ContextOuterClass.ConnectionId> getConnectionIdsList() {
-      return connectionIds_;
+    @java.lang.Override public int getActionValue() {
+      return action_;
     }
     /**
-     * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+     * <code>.context.ConfigActionEnum action = 1;</code>
+     * @return The action.
+     */
+    @java.lang.Override public context.ContextOuterClass.ConfigActionEnum getAction() {
+      @SuppressWarnings("deprecation")
+      context.ContextOuterClass.ConfigActionEnum result = context.ContextOuterClass.ConfigActionEnum.valueOf(action_);
+      return result == null ? context.ContextOuterClass.ConfigActionEnum.UNRECOGNIZED : result;
+    }
+
+    public static final int RESOURCE_KEY_FIELD_NUMBER = 2;
+    private volatile java.lang.Object resourceKey_;
+    /**
+     * <code>string resource_key = 2;</code>
+     * @return The resourceKey.
      */
     @java.lang.Override
-    public java.util.List<? extends context.ContextOuterClass.ConnectionIdOrBuilder> 
-        getConnectionIdsOrBuilderList() {
-      return connectionIds_;
+    public java.lang.String getResourceKey() {
+      java.lang.Object ref = resourceKey_;
+      if (ref instanceof java.lang.String) {
+        return (java.lang.String) ref;
+      } else {
+        com.google.protobuf.ByteString bs = 
+            (com.google.protobuf.ByteString) ref;
+        java.lang.String s = bs.toStringUtf8();
+        resourceKey_ = s;
+        return s;
+      }
     }
     /**
-     * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+     * <code>string resource_key = 2;</code>
+     * @return The bytes for resourceKey.
      */
     @java.lang.Override
-    public int getConnectionIdsCount() {
-      return connectionIds_.size();
+    public com.google.protobuf.ByteString
+        getResourceKeyBytes() {
+      java.lang.Object ref = resourceKey_;
+      if (ref instanceof java.lang.String) {
+        com.google.protobuf.ByteString b = 
+            com.google.protobuf.ByteString.copyFromUtf8(
+                (java.lang.String) ref);
+        resourceKey_ = b;
+        return b;
+      } else {
+        return (com.google.protobuf.ByteString) ref;
+      }
     }
+
+    public static final int RESOURCE_VALUE_FIELD_NUMBER = 3;
+    private volatile java.lang.Object resourceValue_;
     /**
-     * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+     * <code>string resource_value = 3;</code>
+     * @return The resourceValue.
      */
     @java.lang.Override
-    public context.ContextOuterClass.ConnectionId getConnectionIds(int index) {
-      return connectionIds_.get(index);
+    public java.lang.String getResourceValue() {
+      java.lang.Object ref = resourceValue_;
+      if (ref instanceof java.lang.String) {
+        return (java.lang.String) ref;
+      } else {
+        com.google.protobuf.ByteString bs = 
+            (com.google.protobuf.ByteString) ref;
+        java.lang.String s = bs.toStringUtf8();
+        resourceValue_ = s;
+        return s;
+      }
     }
     /**
-     * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+     * <code>string resource_value = 3;</code>
+     * @return The bytes for resourceValue.
      */
     @java.lang.Override
-    public context.ContextOuterClass.ConnectionIdOrBuilder getConnectionIdsOrBuilder(
-        int index) {
-      return connectionIds_.get(index);
+    public com.google.protobuf.ByteString
+        getResourceValueBytes() {
+      java.lang.Object ref = resourceValue_;
+      if (ref instanceof java.lang.String) {
+        com.google.protobuf.ByteString b = 
+            com.google.protobuf.ByteString.copyFromUtf8(
+                (java.lang.String) ref);
+        resourceValue_ = b;
+        return b;
+      } else {
+        return (com.google.protobuf.ByteString) ref;
+      }
     }
 
     private byte memoizedIsInitialized = -1;
@@ -33310,8 +34614,14 @@ public final class ContextOuterClass {
     @java.lang.Override
     public void writeTo(com.google.protobuf.CodedOutputStream output)
                         throws java.io.IOException {
-      for (int i = 0; i < connectionIds_.size(); i++) {
-        output.writeMessage(1, connectionIds_.get(i));
+      if (action_ != context.ContextOuterClass.ConfigActionEnum.CONFIGACTION_UNDEFINED.getNumber()) {
+        output.writeEnum(1, action_);
+      }
+      if (!getResourceKeyBytes().isEmpty()) {
+        com.google.protobuf.GeneratedMessageV3.writeString(output, 2, resourceKey_);
+      }
+      if (!getResourceValueBytes().isEmpty()) {
+        com.google.protobuf.GeneratedMessageV3.writeString(output, 3, resourceValue_);
       }
       unknownFields.writeTo(output);
     }
@@ -33322,9 +34632,15 @@ public final class ContextOuterClass {
       if (size != -1) return size;
 
       size = 0;
-      for (int i = 0; i < connectionIds_.size(); i++) {
+      if (action_ != context.ContextOuterClass.ConfigActionEnum.CONFIGACTION_UNDEFINED.getNumber()) {
         size += com.google.protobuf.CodedOutputStream
-          .computeMessageSize(1, connectionIds_.get(i));
+          .computeEnumSize(1, action_);
+      }
+      if (!getResourceKeyBytes().isEmpty()) {
+        size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, resourceKey_);
+      }
+      if (!getResourceValueBytes().isEmpty()) {
+        size += com.google.protobuf.GeneratedMessageV3.computeStringSize(3, resourceValue_);
       }
       size += unknownFields.getSerializedSize();
       memoizedSize = size;
@@ -33336,13 +34652,16 @@ public final class ContextOuterClass {
       if (obj == this) {
        return true;
       }
-      if (!(obj instanceof context.ContextOuterClass.ConnectionIdList)) {
+      if (!(obj instanceof context.ContextOuterClass.ConfigRule)) {
         return super.equals(obj);
       }
-      context.ContextOuterClass.ConnectionIdList other = (context.ContextOuterClass.ConnectionIdList) obj;
+      context.ContextOuterClass.ConfigRule other = (context.ContextOuterClass.ConfigRule) obj;
 
-      if (!getConnectionIdsList()
-          .equals(other.getConnectionIdsList())) return false;
+      if (action_ != other.action_) return false;
+      if (!getResourceKey()
+          .equals(other.getResourceKey())) return false;
+      if (!getResourceValue()
+          .equals(other.getResourceValue())) return false;
       if (!unknownFields.equals(other.unknownFields)) return false;
       return true;
     }
@@ -33354,78 +34673,80 @@ public final class ContextOuterClass {
       }
       int hash = 41;
       hash = (19 * hash) + getDescriptor().hashCode();
-      if (getConnectionIdsCount() > 0) {
-        hash = (37 * hash) + CONNECTION_IDS_FIELD_NUMBER;
-        hash = (53 * hash) + getConnectionIdsList().hashCode();
-      }
+      hash = (37 * hash) + ACTION_FIELD_NUMBER;
+      hash = (53 * hash) + action_;
+      hash = (37 * hash) + RESOURCE_KEY_FIELD_NUMBER;
+      hash = (53 * hash) + getResourceKey().hashCode();
+      hash = (37 * hash) + RESOURCE_VALUE_FIELD_NUMBER;
+      hash = (53 * hash) + getResourceValue().hashCode();
       hash = (29 * hash) + unknownFields.hashCode();
       memoizedHashCode = hash;
       return hash;
     }
 
-    public static context.ContextOuterClass.ConnectionIdList parseFrom(
+    public static context.ContextOuterClass.ConfigRule parseFrom(
         java.nio.ByteBuffer data)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data);
     }
-    public static context.ContextOuterClass.ConnectionIdList parseFrom(
+    public static context.ContextOuterClass.ConfigRule parseFrom(
         java.nio.ByteBuffer data,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data, extensionRegistry);
     }
-    public static context.ContextOuterClass.ConnectionIdList parseFrom(
+    public static context.ContextOuterClass.ConfigRule parseFrom(
         com.google.protobuf.ByteString data)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data);
     }
-    public static context.ContextOuterClass.ConnectionIdList parseFrom(
+    public static context.ContextOuterClass.ConfigRule parseFrom(
         com.google.protobuf.ByteString data,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data, extensionRegistry);
     }
-    public static context.ContextOuterClass.ConnectionIdList parseFrom(byte[] data)
+    public static context.ContextOuterClass.ConfigRule parseFrom(byte[] data)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data);
     }
-    public static context.ContextOuterClass.ConnectionIdList parseFrom(
+    public static context.ContextOuterClass.ConfigRule parseFrom(
         byte[] data,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data, extensionRegistry);
     }
-    public static context.ContextOuterClass.ConnectionIdList parseFrom(java.io.InputStream input)
+    public static context.ContextOuterClass.ConfigRule parseFrom(java.io.InputStream input)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseWithIOException(PARSER, input);
     }
-    public static context.ContextOuterClass.ConnectionIdList parseFrom(
+    public static context.ContextOuterClass.ConfigRule parseFrom(
         java.io.InputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseWithIOException(PARSER, input, extensionRegistry);
     }
-    public static context.ContextOuterClass.ConnectionIdList parseDelimitedFrom(java.io.InputStream input)
+    public static context.ContextOuterClass.ConfigRule parseDelimitedFrom(java.io.InputStream input)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseDelimitedWithIOException(PARSER, input);
     }
-    public static context.ContextOuterClass.ConnectionIdList parseDelimitedFrom(
+    public static context.ContextOuterClass.ConfigRule parseDelimitedFrom(
         java.io.InputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseDelimitedWithIOException(PARSER, input, extensionRegistry);
     }
-    public static context.ContextOuterClass.ConnectionIdList parseFrom(
+    public static context.ContextOuterClass.ConfigRule parseFrom(
         com.google.protobuf.CodedInputStream input)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseWithIOException(PARSER, input);
     }
-    public static context.ContextOuterClass.ConnectionIdList parseFrom(
+    public static context.ContextOuterClass.ConfigRule parseFrom(
         com.google.protobuf.CodedInputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws java.io.IOException {
@@ -33438,7 +34759,7 @@ public final class ContextOuterClass {
     public static Builder newBuilder() {
       return DEFAULT_INSTANCE.toBuilder();
     }
-    public static Builder newBuilder(context.ContextOuterClass.ConnectionIdList prototype) {
+    public static Builder newBuilder(context.ContextOuterClass.ConfigRule prototype) {
       return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
     }
     @java.lang.Override
@@ -33454,26 +34775,26 @@ public final class ContextOuterClass {
       return builder;
     }
     /**
-     * Protobuf type {@code context.ConnectionIdList}
+     * Protobuf type {@code context.ConfigRule}
      */
     public static final class Builder extends
         com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
-        // @@protoc_insertion_point(builder_implements:context.ConnectionIdList)
-        context.ContextOuterClass.ConnectionIdListOrBuilder {
+        // @@protoc_insertion_point(builder_implements:context.ConfigRule)
+        context.ContextOuterClass.ConfigRuleOrBuilder {
       public static final com.google.protobuf.Descriptors.Descriptor
           getDescriptor() {
-        return context.ContextOuterClass.internal_static_context_ConnectionIdList_descriptor;
+        return context.ContextOuterClass.internal_static_context_ConfigRule_descriptor;
       }
 
       @java.lang.Override
       protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
           internalGetFieldAccessorTable() {
-        return context.ContextOuterClass.internal_static_context_ConnectionIdList_fieldAccessorTable
+        return context.ContextOuterClass.internal_static_context_ConfigRule_fieldAccessorTable
             .ensureFieldAccessorsInitialized(
-                context.ContextOuterClass.ConnectionIdList.class, context.ContextOuterClass.ConnectionIdList.Builder.class);
+                context.ContextOuterClass.ConfigRule.class, context.ContextOuterClass.ConfigRule.Builder.class);
       }
 
-      // Construct using context.ContextOuterClass.ConnectionIdList.newBuilder()
+      // Construct using context.ContextOuterClass.ConfigRule.newBuilder()
       private Builder() {
         maybeForceBuilderInitialization();
       }
@@ -33486,35 +34807,34 @@ public final class ContextOuterClass {
       private void maybeForceBuilderInitialization() {
         if (com.google.protobuf.GeneratedMessageV3
                 .alwaysUseFieldBuilders) {
-          getConnectionIdsFieldBuilder();
         }
       }
       @java.lang.Override
       public Builder clear() {
         super.clear();
-        if (connectionIdsBuilder_ == null) {
-          connectionIds_ = java.util.Collections.emptyList();
-          bitField0_ = (bitField0_ & ~0x00000001);
-        } else {
-          connectionIdsBuilder_.clear();
-        }
+        action_ = 0;
+
+        resourceKey_ = "";
+
+        resourceValue_ = "";
+
         return this;
       }
 
       @java.lang.Override
       public com.google.protobuf.Descriptors.Descriptor
           getDescriptorForType() {
-        return context.ContextOuterClass.internal_static_context_ConnectionIdList_descriptor;
+        return context.ContextOuterClass.internal_static_context_ConfigRule_descriptor;
       }
 
       @java.lang.Override
-      public context.ContextOuterClass.ConnectionIdList getDefaultInstanceForType() {
-        return context.ContextOuterClass.ConnectionIdList.getDefaultInstance();
+      public context.ContextOuterClass.ConfigRule getDefaultInstanceForType() {
+        return context.ContextOuterClass.ConfigRule.getDefaultInstance();
       }
 
       @java.lang.Override
-      public context.ContextOuterClass.ConnectionIdList build() {
-        context.ContextOuterClass.ConnectionIdList result = buildPartial();
+      public context.ContextOuterClass.ConfigRule build() {
+        context.ContextOuterClass.ConfigRule result = buildPartial();
         if (!result.isInitialized()) {
           throw newUninitializedMessageException(result);
         }
@@ -33522,18 +34842,11 @@ public final class ContextOuterClass {
       }
 
       @java.lang.Override
-      public context.ContextOuterClass.ConnectionIdList buildPartial() {
-        context.ContextOuterClass.ConnectionIdList result = new context.ContextOuterClass.ConnectionIdList(this);
-        int from_bitField0_ = bitField0_;
-        if (connectionIdsBuilder_ == null) {
-          if (((bitField0_ & 0x00000001) != 0)) {
-            connectionIds_ = java.util.Collections.unmodifiableList(connectionIds_);
-            bitField0_ = (bitField0_ & ~0x00000001);
-          }
-          result.connectionIds_ = connectionIds_;
-        } else {
-          result.connectionIds_ = connectionIdsBuilder_.build();
-        }
+      public context.ContextOuterClass.ConfigRule buildPartial() {
+        context.ContextOuterClass.ConfigRule result = new context.ContextOuterClass.ConfigRule(this);
+        result.action_ = action_;
+        result.resourceKey_ = resourceKey_;
+        result.resourceValue_ = resourceValue_;
         onBuilt();
         return result;
       }
@@ -33572,41 +34885,26 @@ public final class ContextOuterClass {
       }
       @java.lang.Override
       public Builder mergeFrom(com.google.protobuf.Message other) {
-        if (other instanceof context.ContextOuterClass.ConnectionIdList) {
-          return mergeFrom((context.ContextOuterClass.ConnectionIdList)other);
+        if (other instanceof context.ContextOuterClass.ConfigRule) {
+          return mergeFrom((context.ContextOuterClass.ConfigRule)other);
         } else {
           super.mergeFrom(other);
           return this;
         }
       }
 
-      public Builder mergeFrom(context.ContextOuterClass.ConnectionIdList other) {
-        if (other == context.ContextOuterClass.ConnectionIdList.getDefaultInstance()) return this;
-        if (connectionIdsBuilder_ == null) {
-          if (!other.connectionIds_.isEmpty()) {
-            if (connectionIds_.isEmpty()) {
-              connectionIds_ = other.connectionIds_;
-              bitField0_ = (bitField0_ & ~0x00000001);
-            } else {
-              ensureConnectionIdsIsMutable();
-              connectionIds_.addAll(other.connectionIds_);
-            }
-            onChanged();
-          }
-        } else {
-          if (!other.connectionIds_.isEmpty()) {
-            if (connectionIdsBuilder_.isEmpty()) {
-              connectionIdsBuilder_.dispose();
-              connectionIdsBuilder_ = null;
-              connectionIds_ = other.connectionIds_;
-              bitField0_ = (bitField0_ & ~0x00000001);
-              connectionIdsBuilder_ = 
-                com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders ?
-                   getConnectionIdsFieldBuilder() : null;
-            } else {
-              connectionIdsBuilder_.addAllMessages(other.connectionIds_);
-            }
-          }
+      public Builder mergeFrom(context.ContextOuterClass.ConfigRule other) {
+        if (other == context.ContextOuterClass.ConfigRule.getDefaultInstance()) return this;
+        if (other.action_ != 0) {
+          setActionValue(other.getActionValue());
+        }
+        if (!other.getResourceKey().isEmpty()) {
+          resourceKey_ = other.resourceKey_;
+          onChanged();
+        }
+        if (!other.getResourceValue().isEmpty()) {
+          resourceValue_ = other.resourceValue_;
+          onChanged();
         }
         this.mergeUnknownFields(other.unknownFields);
         onChanged();
@@ -33623,11 +34921,11 @@ public final class ContextOuterClass {
           com.google.protobuf.CodedInputStream input,
           com.google.protobuf.ExtensionRegistryLite extensionRegistry)
           throws java.io.IOException {
-        context.ContextOuterClass.ConnectionIdList parsedMessage = null;
+        context.ContextOuterClass.ConfigRule parsedMessage = null;
         try {
           parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
         } catch (com.google.protobuf.InvalidProtocolBufferException e) {
-          parsedMessage = (context.ContextOuterClass.ConnectionIdList) e.getUnfinishedMessage();
+          parsedMessage = (context.ContextOuterClass.ConfigRule) e.getUnfinishedMessage();
           throw e.unwrapIOException();
         } finally {
           if (parsedMessage != null) {
@@ -33636,246 +34934,211 @@ public final class ContextOuterClass {
         }
         return this;
       }
-      private int bitField0_;
-
-      private java.util.List<context.ContextOuterClass.ConnectionId> connectionIds_ =
-        java.util.Collections.emptyList();
-      private void ensureConnectionIdsIsMutable() {
-        if (!((bitField0_ & 0x00000001) != 0)) {
-          connectionIds_ = new java.util.ArrayList<context.ContextOuterClass.ConnectionId>(connectionIds_);
-          bitField0_ |= 0x00000001;
-         }
-      }
-
-      private com.google.protobuf.RepeatedFieldBuilderV3<
-          context.ContextOuterClass.ConnectionId, context.ContextOuterClass.ConnectionId.Builder, context.ContextOuterClass.ConnectionIdOrBuilder> connectionIdsBuilder_;
 
+      private int action_ = 0;
       /**
-       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
-       */
-      public java.util.List<context.ContextOuterClass.ConnectionId> getConnectionIdsList() {
-        if (connectionIdsBuilder_ == null) {
-          return java.util.Collections.unmodifiableList(connectionIds_);
-        } else {
-          return connectionIdsBuilder_.getMessageList();
-        }
-      }
-      /**
-       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
-       */
-      public int getConnectionIdsCount() {
-        if (connectionIdsBuilder_ == null) {
-          return connectionIds_.size();
-        } else {
-          return connectionIdsBuilder_.getCount();
-        }
-      }
-      /**
-       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
-       */
-      public context.ContextOuterClass.ConnectionId getConnectionIds(int index) {
-        if (connectionIdsBuilder_ == null) {
-          return connectionIds_.get(index);
-        } else {
-          return connectionIdsBuilder_.getMessage(index);
-        }
-      }
-      /**
-       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+       * <code>.context.ConfigActionEnum action = 1;</code>
+       * @return The enum numeric value on the wire for action.
        */
-      public Builder setConnectionIds(
-          int index, context.ContextOuterClass.ConnectionId value) {
-        if (connectionIdsBuilder_ == null) {
-          if (value == null) {
-            throw new NullPointerException();
-          }
-          ensureConnectionIdsIsMutable();
-          connectionIds_.set(index, value);
-          onChanged();
-        } else {
-          connectionIdsBuilder_.setMessage(index, value);
-        }
-        return this;
+      @java.lang.Override public int getActionValue() {
+        return action_;
       }
       /**
-       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+       * <code>.context.ConfigActionEnum action = 1;</code>
+       * @param value The enum numeric value on the wire for action to set.
+       * @return This builder for chaining.
        */
-      public Builder setConnectionIds(
-          int index, context.ContextOuterClass.ConnectionId.Builder builderForValue) {
-        if (connectionIdsBuilder_ == null) {
-          ensureConnectionIdsIsMutable();
-          connectionIds_.set(index, builderForValue.build());
-          onChanged();
-        } else {
-          connectionIdsBuilder_.setMessage(index, builderForValue.build());
-        }
+      public Builder setActionValue(int value) {
+        
+        action_ = value;
+        onChanged();
         return this;
       }
       /**
-       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+       * <code>.context.ConfigActionEnum action = 1;</code>
+       * @return The action.
        */
-      public Builder addConnectionIds(context.ContextOuterClass.ConnectionId value) {
-        if (connectionIdsBuilder_ == null) {
-          if (value == null) {
-            throw new NullPointerException();
-          }
-          ensureConnectionIdsIsMutable();
-          connectionIds_.add(value);
-          onChanged();
-        } else {
-          connectionIdsBuilder_.addMessage(value);
-        }
-        return this;
+      @java.lang.Override
+      public context.ContextOuterClass.ConfigActionEnum getAction() {
+        @SuppressWarnings("deprecation")
+        context.ContextOuterClass.ConfigActionEnum result = context.ContextOuterClass.ConfigActionEnum.valueOf(action_);
+        return result == null ? context.ContextOuterClass.ConfigActionEnum.UNRECOGNIZED : result;
       }
       /**
-       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+       * <code>.context.ConfigActionEnum action = 1;</code>
+       * @param value The action to set.
+       * @return This builder for chaining.
        */
-      public Builder addConnectionIds(
-          int index, context.ContextOuterClass.ConnectionId value) {
-        if (connectionIdsBuilder_ == null) {
-          if (value == null) {
-            throw new NullPointerException();
-          }
-          ensureConnectionIdsIsMutable();
-          connectionIds_.add(index, value);
-          onChanged();
-        } else {
-          connectionIdsBuilder_.addMessage(index, value);
+      public Builder setAction(context.ContextOuterClass.ConfigActionEnum value) {
+        if (value == null) {
+          throw new NullPointerException();
         }
+        
+        action_ = value.getNumber();
+        onChanged();
         return this;
       }
       /**
-       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+       * <code>.context.ConfigActionEnum action = 1;</code>
+       * @return This builder for chaining.
        */
-      public Builder addConnectionIds(
-          context.ContextOuterClass.ConnectionId.Builder builderForValue) {
-        if (connectionIdsBuilder_ == null) {
-          ensureConnectionIdsIsMutable();
-          connectionIds_.add(builderForValue.build());
-          onChanged();
-        } else {
-          connectionIdsBuilder_.addMessage(builderForValue.build());
-        }
+      public Builder clearAction() {
+        
+        action_ = 0;
+        onChanged();
         return this;
       }
+
+      private java.lang.Object resourceKey_ = "";
       /**
-       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+       * <code>string resource_key = 2;</code>
+       * @return The resourceKey.
        */
-      public Builder addConnectionIds(
-          int index, context.ContextOuterClass.ConnectionId.Builder builderForValue) {
-        if (connectionIdsBuilder_ == null) {
-          ensureConnectionIdsIsMutable();
-          connectionIds_.add(index, builderForValue.build());
-          onChanged();
+      public java.lang.String getResourceKey() {
+        java.lang.Object ref = resourceKey_;
+        if (!(ref instanceof java.lang.String)) {
+          com.google.protobuf.ByteString bs =
+              (com.google.protobuf.ByteString) ref;
+          java.lang.String s = bs.toStringUtf8();
+          resourceKey_ = s;
+          return s;
         } else {
-          connectionIdsBuilder_.addMessage(index, builderForValue.build());
+          return (java.lang.String) ref;
         }
-        return this;
       }
       /**
-       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+       * <code>string resource_key = 2;</code>
+       * @return The bytes for resourceKey.
        */
-      public Builder addAllConnectionIds(
-          java.lang.Iterable<? extends context.ContextOuterClass.ConnectionId> values) {
-        if (connectionIdsBuilder_ == null) {
-          ensureConnectionIdsIsMutable();
-          com.google.protobuf.AbstractMessageLite.Builder.addAll(
-              values, connectionIds_);
-          onChanged();
+      public com.google.protobuf.ByteString
+          getResourceKeyBytes() {
+        java.lang.Object ref = resourceKey_;
+        if (ref instanceof String) {
+          com.google.protobuf.ByteString b = 
+              com.google.protobuf.ByteString.copyFromUtf8(
+                  (java.lang.String) ref);
+          resourceKey_ = b;
+          return b;
         } else {
-          connectionIdsBuilder_.addAllMessages(values);
+          return (com.google.protobuf.ByteString) ref;
         }
-        return this;
       }
       /**
-       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+       * <code>string resource_key = 2;</code>
+       * @param value The resourceKey to set.
+       * @return This builder for chaining.
        */
-      public Builder clearConnectionIds() {
-        if (connectionIdsBuilder_ == null) {
-          connectionIds_ = java.util.Collections.emptyList();
-          bitField0_ = (bitField0_ & ~0x00000001);
-          onChanged();
-        } else {
-          connectionIdsBuilder_.clear();
-        }
+      public Builder setResourceKey(
+          java.lang.String value) {
+        if (value == null) {
+    throw new NullPointerException();
+  }
+  
+        resourceKey_ = value;
+        onChanged();
         return this;
       }
       /**
-       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+       * <code>string resource_key = 2;</code>
+       * @return This builder for chaining.
        */
-      public Builder removeConnectionIds(int index) {
-        if (connectionIdsBuilder_ == null) {
-          ensureConnectionIdsIsMutable();
-          connectionIds_.remove(index);
-          onChanged();
-        } else {
-          connectionIdsBuilder_.remove(index);
-        }
+      public Builder clearResourceKey() {
+        
+        resourceKey_ = getDefaultInstance().getResourceKey();
+        onChanged();
         return this;
       }
       /**
-       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+       * <code>string resource_key = 2;</code>
+       * @param value The bytes for resourceKey to set.
+       * @return This builder for chaining.
        */
-      public context.ContextOuterClass.ConnectionId.Builder getConnectionIdsBuilder(
-          int index) {
-        return getConnectionIdsFieldBuilder().getBuilder(index);
+      public Builder setResourceKeyBytes(
+          com.google.protobuf.ByteString value) {
+        if (value == null) {
+    throw new NullPointerException();
+  }
+  checkByteStringIsUtf8(value);
+        
+        resourceKey_ = value;
+        onChanged();
+        return this;
       }
+
+      private java.lang.Object resourceValue_ = "";
       /**
-       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+       * <code>string resource_value = 3;</code>
+       * @return The resourceValue.
        */
-      public context.ContextOuterClass.ConnectionIdOrBuilder getConnectionIdsOrBuilder(
-          int index) {
-        if (connectionIdsBuilder_ == null) {
-          return connectionIds_.get(index);  } else {
-          return connectionIdsBuilder_.getMessageOrBuilder(index);
+      public java.lang.String getResourceValue() {
+        java.lang.Object ref = resourceValue_;
+        if (!(ref instanceof java.lang.String)) {
+          com.google.protobuf.ByteString bs =
+              (com.google.protobuf.ByteString) ref;
+          java.lang.String s = bs.toStringUtf8();
+          resourceValue_ = s;
+          return s;
+        } else {
+          return (java.lang.String) ref;
         }
       }
       /**
-       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+       * <code>string resource_value = 3;</code>
+       * @return The bytes for resourceValue.
        */
-      public java.util.List<? extends context.ContextOuterClass.ConnectionIdOrBuilder> 
-           getConnectionIdsOrBuilderList() {
-        if (connectionIdsBuilder_ != null) {
-          return connectionIdsBuilder_.getMessageOrBuilderList();
+      public com.google.protobuf.ByteString
+          getResourceValueBytes() {
+        java.lang.Object ref = resourceValue_;
+        if (ref instanceof String) {
+          com.google.protobuf.ByteString b = 
+              com.google.protobuf.ByteString.copyFromUtf8(
+                  (java.lang.String) ref);
+          resourceValue_ = b;
+          return b;
         } else {
-          return java.util.Collections.unmodifiableList(connectionIds_);
+          return (com.google.protobuf.ByteString) ref;
         }
       }
       /**
-       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+       * <code>string resource_value = 3;</code>
+       * @param value The resourceValue to set.
+       * @return This builder for chaining.
        */
-      public context.ContextOuterClass.ConnectionId.Builder addConnectionIdsBuilder() {
-        return getConnectionIdsFieldBuilder().addBuilder(
-            context.ContextOuterClass.ConnectionId.getDefaultInstance());
+      public Builder setResourceValue(
+          java.lang.String value) {
+        if (value == null) {
+    throw new NullPointerException();
+  }
+  
+        resourceValue_ = value;
+        onChanged();
+        return this;
       }
       /**
-       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+       * <code>string resource_value = 3;</code>
+       * @return This builder for chaining.
        */
-      public context.ContextOuterClass.ConnectionId.Builder addConnectionIdsBuilder(
-          int index) {
-        return getConnectionIdsFieldBuilder().addBuilder(
-            index, context.ContextOuterClass.ConnectionId.getDefaultInstance());
+      public Builder clearResourceValue() {
+        
+        resourceValue_ = getDefaultInstance().getResourceValue();
+        onChanged();
+        return this;
       }
       /**
-       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+       * <code>string resource_value = 3;</code>
+       * @param value The bytes for resourceValue to set.
+       * @return This builder for chaining.
        */
-      public java.util.List<context.ContextOuterClass.ConnectionId.Builder> 
-           getConnectionIdsBuilderList() {
-        return getConnectionIdsFieldBuilder().getBuilderList();
-      }
-      private com.google.protobuf.RepeatedFieldBuilderV3<
-          context.ContextOuterClass.ConnectionId, context.ContextOuterClass.ConnectionId.Builder, context.ContextOuterClass.ConnectionIdOrBuilder> 
-          getConnectionIdsFieldBuilder() {
-        if (connectionIdsBuilder_ == null) {
-          connectionIdsBuilder_ = new com.google.protobuf.RepeatedFieldBuilderV3<
-              context.ContextOuterClass.ConnectionId, context.ContextOuterClass.ConnectionId.Builder, context.ContextOuterClass.ConnectionIdOrBuilder>(
-                  connectionIds_,
-                  ((bitField0_ & 0x00000001) != 0),
-                  getParentForChildren(),
-                  isClean());
-          connectionIds_ = null;
-        }
-        return connectionIdsBuilder_;
+      public Builder setResourceValueBytes(
+          com.google.protobuf.ByteString value) {
+        if (value == null) {
+    throw new NullPointerException();
+  }
+  checkByteStringIsUtf8(value);
+        
+        resourceValue_ = value;
+        onChanged();
+        return this;
       }
       @java.lang.Override
       public final Builder setUnknownFields(
@@ -33890,95 +35153,100 @@ public final class ContextOuterClass {
       }
 
 
-      // @@protoc_insertion_point(builder_scope:context.ConnectionIdList)
+      // @@protoc_insertion_point(builder_scope:context.ConfigRule)
     }
 
-    // @@protoc_insertion_point(class_scope:context.ConnectionIdList)
-    private static final context.ContextOuterClass.ConnectionIdList DEFAULT_INSTANCE;
+    // @@protoc_insertion_point(class_scope:context.ConfigRule)
+    private static final context.ContextOuterClass.ConfigRule DEFAULT_INSTANCE;
     static {
-      DEFAULT_INSTANCE = new context.ContextOuterClass.ConnectionIdList();
+      DEFAULT_INSTANCE = new context.ContextOuterClass.ConfigRule();
     }
 
-    public static context.ContextOuterClass.ConnectionIdList getDefaultInstance() {
+    public static context.ContextOuterClass.ConfigRule getDefaultInstance() {
       return DEFAULT_INSTANCE;
     }
 
-    private static final com.google.protobuf.Parser<ConnectionIdList>
-        PARSER = new com.google.protobuf.AbstractParser<ConnectionIdList>() {
+    private static final com.google.protobuf.Parser<ConfigRule>
+        PARSER = new com.google.protobuf.AbstractParser<ConfigRule>() {
       @java.lang.Override
-      public ConnectionIdList parsePartialFrom(
+      public ConfigRule parsePartialFrom(
           com.google.protobuf.CodedInputStream input,
           com.google.protobuf.ExtensionRegistryLite extensionRegistry)
           throws com.google.protobuf.InvalidProtocolBufferException {
-        return new ConnectionIdList(input, extensionRegistry);
+        return new ConfigRule(input, extensionRegistry);
       }
     };
 
-    public static com.google.protobuf.Parser<ConnectionIdList> parser() {
+    public static com.google.protobuf.Parser<ConfigRule> parser() {
       return PARSER;
     }
 
     @java.lang.Override
-    public com.google.protobuf.Parser<ConnectionIdList> getParserForType() {
+    public com.google.protobuf.Parser<ConfigRule> getParserForType() {
       return PARSER;
     }
 
     @java.lang.Override
-    public context.ContextOuterClass.ConnectionIdList getDefaultInstanceForType() {
+    public context.ContextOuterClass.ConfigRule getDefaultInstanceForType() {
       return DEFAULT_INSTANCE;
     }
 
   }
 
-  public interface ConnectionListOrBuilder extends
-      // @@protoc_insertion_point(interface_extends:context.ConnectionList)
+  public interface ConstraintOrBuilder extends
+      // @@protoc_insertion_point(interface_extends:context.Constraint)
       com.google.protobuf.MessageOrBuilder {
 
     /**
-     * <code>repeated .context.Connection connections = 1;</code>
-     */
-    java.util.List<context.ContextOuterClass.Connection> 
-        getConnectionsList();
-    /**
-     * <code>repeated .context.Connection connections = 1;</code>
+     * <code>string constraint_type = 1;</code>
+     * @return The constraintType.
      */
-    context.ContextOuterClass.Connection getConnections(int index);
+    java.lang.String getConstraintType();
     /**
-     * <code>repeated .context.Connection connections = 1;</code>
+     * <code>string constraint_type = 1;</code>
+     * @return The bytes for constraintType.
      */
-    int getConnectionsCount();
+    com.google.protobuf.ByteString
+        getConstraintTypeBytes();
+
     /**
-     * <code>repeated .context.Connection connections = 1;</code>
+     * <code>string constraint_value = 2;</code>
+     * @return The constraintValue.
      */
-    java.util.List<? extends context.ContextOuterClass.ConnectionOrBuilder> 
-        getConnectionsOrBuilderList();
+    java.lang.String getConstraintValue();
     /**
-     * <code>repeated .context.Connection connections = 1;</code>
+     * <code>string constraint_value = 2;</code>
+     * @return The bytes for constraintValue.
      */
-    context.ContextOuterClass.ConnectionOrBuilder getConnectionsOrBuilder(
-        int index);
+    com.google.protobuf.ByteString
+        getConstraintValueBytes();
   }
   /**
-   * Protobuf type {@code context.ConnectionList}
+   * <pre>
+   * ----- Constraint ----------------------------------------------------------------------------------------------------
+   * </pre>
+   *
+   * Protobuf type {@code context.Constraint}
    */
-  public static final class ConnectionList extends
+  public static final class Constraint extends
       com.google.protobuf.GeneratedMessageV3 implements
-      // @@protoc_insertion_point(message_implements:context.ConnectionList)
-      ConnectionListOrBuilder {
+      // @@protoc_insertion_point(message_implements:context.Constraint)
+      ConstraintOrBuilder {
   private static final long serialVersionUID = 0L;
-    // Use ConnectionList.newBuilder() to construct.
-    private ConnectionList(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
+    // Use Constraint.newBuilder() to construct.
+    private Constraint(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
       super(builder);
     }
-    private ConnectionList() {
-      connections_ = java.util.Collections.emptyList();
+    private Constraint() {
+      constraintType_ = "";
+      constraintValue_ = "";
     }
 
     @java.lang.Override
     @SuppressWarnings({"unused"})
     protected java.lang.Object newInstance(
         UnusedPrivateParameter unused) {
-      return new ConnectionList();
+      return new Constraint();
     }
 
     @java.lang.Override
@@ -33986,7 +35254,7 @@ public final class ContextOuterClass {
     getUnknownFields() {
       return this.unknownFields;
     }
-    private ConnectionList(
+    private Constraint(
         com.google.protobuf.CodedInputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
@@ -33994,7 +35262,6 @@ public final class ContextOuterClass {
       if (extensionRegistry == null) {
         throw new java.lang.NullPointerException();
       }
-      int mutable_bitField0_ = 0;
       com.google.protobuf.UnknownFieldSet.Builder unknownFields =
           com.google.protobuf.UnknownFieldSet.newBuilder();
       try {
@@ -34006,12 +35273,15 @@ public final class ContextOuterClass {
               done = true;
               break;
             case 10: {
-              if (!((mutable_bitField0_ & 0x00000001) != 0)) {
-                connections_ = new java.util.ArrayList<context.ContextOuterClass.Connection>();
-                mutable_bitField0_ |= 0x00000001;
-              }
-              connections_.add(
-                  input.readMessage(context.ContextOuterClass.Connection.parser(), extensionRegistry));
+              java.lang.String s = input.readStringRequireUtf8();
+
+              constraintType_ = s;
+              break;
+            }
+            case 18: {
+              java.lang.String s = input.readStringRequireUtf8();
+
+              constraintValue_ = s;
               break;
             }
             default: {
@@ -34029,64 +35299,97 @@ public final class ContextOuterClass {
         throw new com.google.protobuf.InvalidProtocolBufferException(
             e).setUnfinishedMessage(this);
       } finally {
-        if (((mutable_bitField0_ & 0x00000001) != 0)) {
-          connections_ = java.util.Collections.unmodifiableList(connections_);
-        }
         this.unknownFields = unknownFields.build();
         makeExtensionsImmutable();
       }
     }
     public static final com.google.protobuf.Descriptors.Descriptor
         getDescriptor() {
-      return context.ContextOuterClass.internal_static_context_ConnectionList_descriptor;
+      return context.ContextOuterClass.internal_static_context_Constraint_descriptor;
     }
 
     @java.lang.Override
     protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
         internalGetFieldAccessorTable() {
-      return context.ContextOuterClass.internal_static_context_ConnectionList_fieldAccessorTable
+      return context.ContextOuterClass.internal_static_context_Constraint_fieldAccessorTable
           .ensureFieldAccessorsInitialized(
-              context.ContextOuterClass.ConnectionList.class, context.ContextOuterClass.ConnectionList.Builder.class);
+              context.ContextOuterClass.Constraint.class, context.ContextOuterClass.Constraint.Builder.class);
     }
 
-    public static final int CONNECTIONS_FIELD_NUMBER = 1;
-    private java.util.List<context.ContextOuterClass.Connection> connections_;
-    /**
-     * <code>repeated .context.Connection connections = 1;</code>
-     */
-    @java.lang.Override
-    public java.util.List<context.ContextOuterClass.Connection> getConnectionsList() {
-      return connections_;
-    }
+    public static final int CONSTRAINT_TYPE_FIELD_NUMBER = 1;
+    private volatile java.lang.Object constraintType_;
     /**
-     * <code>repeated .context.Connection connections = 1;</code>
+     * <code>string constraint_type = 1;</code>
+     * @return The constraintType.
      */
     @java.lang.Override
-    public java.util.List<? extends context.ContextOuterClass.ConnectionOrBuilder> 
-        getConnectionsOrBuilderList() {
-      return connections_;
+    public java.lang.String getConstraintType() {
+      java.lang.Object ref = constraintType_;
+      if (ref instanceof java.lang.String) {
+        return (java.lang.String) ref;
+      } else {
+        com.google.protobuf.ByteString bs = 
+            (com.google.protobuf.ByteString) ref;
+        java.lang.String s = bs.toStringUtf8();
+        constraintType_ = s;
+        return s;
+      }
     }
     /**
-     * <code>repeated .context.Connection connections = 1;</code>
+     * <code>string constraint_type = 1;</code>
+     * @return The bytes for constraintType.
      */
     @java.lang.Override
-    public int getConnectionsCount() {
-      return connections_.size();
+    public com.google.protobuf.ByteString
+        getConstraintTypeBytes() {
+      java.lang.Object ref = constraintType_;
+      if (ref instanceof java.lang.String) {
+        com.google.protobuf.ByteString b = 
+            com.google.protobuf.ByteString.copyFromUtf8(
+                (java.lang.String) ref);
+        constraintType_ = b;
+        return b;
+      } else {
+        return (com.google.protobuf.ByteString) ref;
+      }
     }
+
+    public static final int CONSTRAINT_VALUE_FIELD_NUMBER = 2;
+    private volatile java.lang.Object constraintValue_;
     /**
-     * <code>repeated .context.Connection connections = 1;</code>
+     * <code>string constraint_value = 2;</code>
+     * @return The constraintValue.
      */
     @java.lang.Override
-    public context.ContextOuterClass.Connection getConnections(int index) {
-      return connections_.get(index);
+    public java.lang.String getConstraintValue() {
+      java.lang.Object ref = constraintValue_;
+      if (ref instanceof java.lang.String) {
+        return (java.lang.String) ref;
+      } else {
+        com.google.protobuf.ByteString bs = 
+            (com.google.protobuf.ByteString) ref;
+        java.lang.String s = bs.toStringUtf8();
+        constraintValue_ = s;
+        return s;
+      }
     }
     /**
-     * <code>repeated .context.Connection connections = 1;</code>
+     * <code>string constraint_value = 2;</code>
+     * @return The bytes for constraintValue.
      */
     @java.lang.Override
-    public context.ContextOuterClass.ConnectionOrBuilder getConnectionsOrBuilder(
-        int index) {
-      return connections_.get(index);
+    public com.google.protobuf.ByteString
+        getConstraintValueBytes() {
+      java.lang.Object ref = constraintValue_;
+      if (ref instanceof java.lang.String) {
+        com.google.protobuf.ByteString b = 
+            com.google.protobuf.ByteString.copyFromUtf8(
+                (java.lang.String) ref);
+        constraintValue_ = b;
+        return b;
+      } else {
+        return (com.google.protobuf.ByteString) ref;
+      }
     }
 
     private byte memoizedIsInitialized = -1;
@@ -34103,8 +35406,11 @@ public final class ContextOuterClass {
     @java.lang.Override
     public void writeTo(com.google.protobuf.CodedOutputStream output)
                         throws java.io.IOException {
-      for (int i = 0; i < connections_.size(); i++) {
-        output.writeMessage(1, connections_.get(i));
+      if (!getConstraintTypeBytes().isEmpty()) {
+        com.google.protobuf.GeneratedMessageV3.writeString(output, 1, constraintType_);
+      }
+      if (!getConstraintValueBytes().isEmpty()) {
+        com.google.protobuf.GeneratedMessageV3.writeString(output, 2, constraintValue_);
       }
       unknownFields.writeTo(output);
     }
@@ -34115,9 +35421,11 @@ public final class ContextOuterClass {
       if (size != -1) return size;
 
       size = 0;
-      for (int i = 0; i < connections_.size(); i++) {
-        size += com.google.protobuf.CodedOutputStream
-          .computeMessageSize(1, connections_.get(i));
+      if (!getConstraintTypeBytes().isEmpty()) {
+        size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, constraintType_);
+      }
+      if (!getConstraintValueBytes().isEmpty()) {
+        size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, constraintValue_);
       }
       size += unknownFields.getSerializedSize();
       memoizedSize = size;
@@ -34129,13 +35437,15 @@ public final class ContextOuterClass {
       if (obj == this) {
        return true;
       }
-      if (!(obj instanceof context.ContextOuterClass.ConnectionList)) {
+      if (!(obj instanceof context.ContextOuterClass.Constraint)) {
         return super.equals(obj);
       }
-      context.ContextOuterClass.ConnectionList other = (context.ContextOuterClass.ConnectionList) obj;
+      context.ContextOuterClass.Constraint other = (context.ContextOuterClass.Constraint) obj;
 
-      if (!getConnectionsList()
-          .equals(other.getConnectionsList())) return false;
+      if (!getConstraintType()
+          .equals(other.getConstraintType())) return false;
+      if (!getConstraintValue()
+          .equals(other.getConstraintValue())) return false;
       if (!unknownFields.equals(other.unknownFields)) return false;
       return true;
     }
@@ -34147,78 +35457,78 @@ public final class ContextOuterClass {
       }
       int hash = 41;
       hash = (19 * hash) + getDescriptor().hashCode();
-      if (getConnectionsCount() > 0) {
-        hash = (37 * hash) + CONNECTIONS_FIELD_NUMBER;
-        hash = (53 * hash) + getConnectionsList().hashCode();
-      }
+      hash = (37 * hash) + CONSTRAINT_TYPE_FIELD_NUMBER;
+      hash = (53 * hash) + getConstraintType().hashCode();
+      hash = (37 * hash) + CONSTRAINT_VALUE_FIELD_NUMBER;
+      hash = (53 * hash) + getConstraintValue().hashCode();
       hash = (29 * hash) + unknownFields.hashCode();
       memoizedHashCode = hash;
       return hash;
     }
 
-    public static context.ContextOuterClass.ConnectionList parseFrom(
+    public static context.ContextOuterClass.Constraint parseFrom(
         java.nio.ByteBuffer data)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data);
     }
-    public static context.ContextOuterClass.ConnectionList parseFrom(
+    public static context.ContextOuterClass.Constraint parseFrom(
         java.nio.ByteBuffer data,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data, extensionRegistry);
     }
-    public static context.ContextOuterClass.ConnectionList parseFrom(
+    public static context.ContextOuterClass.Constraint parseFrom(
         com.google.protobuf.ByteString data)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data);
     }
-    public static context.ContextOuterClass.ConnectionList parseFrom(
+    public static context.ContextOuterClass.Constraint parseFrom(
         com.google.protobuf.ByteString data,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data, extensionRegistry);
     }
-    public static context.ContextOuterClass.ConnectionList parseFrom(byte[] data)
+    public static context.ContextOuterClass.Constraint parseFrom(byte[] data)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data);
     }
-    public static context.ContextOuterClass.ConnectionList parseFrom(
+    public static context.ContextOuterClass.Constraint parseFrom(
         byte[] data,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data, extensionRegistry);
     }
-    public static context.ContextOuterClass.ConnectionList parseFrom(java.io.InputStream input)
+    public static context.ContextOuterClass.Constraint parseFrom(java.io.InputStream input)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseWithIOException(PARSER, input);
     }
-    public static context.ContextOuterClass.ConnectionList parseFrom(
+    public static context.ContextOuterClass.Constraint parseFrom(
         java.io.InputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseWithIOException(PARSER, input, extensionRegistry);
     }
-    public static context.ContextOuterClass.ConnectionList parseDelimitedFrom(java.io.InputStream input)
+    public static context.ContextOuterClass.Constraint parseDelimitedFrom(java.io.InputStream input)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseDelimitedWithIOException(PARSER, input);
     }
-    public static context.ContextOuterClass.ConnectionList parseDelimitedFrom(
+    public static context.ContextOuterClass.Constraint parseDelimitedFrom(
         java.io.InputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseDelimitedWithIOException(PARSER, input, extensionRegistry);
     }
-    public static context.ContextOuterClass.ConnectionList parseFrom(
+    public static context.ContextOuterClass.Constraint parseFrom(
         com.google.protobuf.CodedInputStream input)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseWithIOException(PARSER, input);
     }
-    public static context.ContextOuterClass.ConnectionList parseFrom(
+    public static context.ContextOuterClass.Constraint parseFrom(
         com.google.protobuf.CodedInputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws java.io.IOException {
@@ -34231,7 +35541,7 @@ public final class ContextOuterClass {
     public static Builder newBuilder() {
       return DEFAULT_INSTANCE.toBuilder();
     }
-    public static Builder newBuilder(context.ContextOuterClass.ConnectionList prototype) {
+    public static Builder newBuilder(context.ContextOuterClass.Constraint prototype) {
       return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
     }
     @java.lang.Override
@@ -34247,26 +35557,30 @@ public final class ContextOuterClass {
       return builder;
     }
     /**
-     * Protobuf type {@code context.ConnectionList}
+     * <pre>
+     * ----- Constraint ----------------------------------------------------------------------------------------------------
+     * </pre>
+     *
+     * Protobuf type {@code context.Constraint}
      */
     public static final class Builder extends
         com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
-        // @@protoc_insertion_point(builder_implements:context.ConnectionList)
-        context.ContextOuterClass.ConnectionListOrBuilder {
+        // @@protoc_insertion_point(builder_implements:context.Constraint)
+        context.ContextOuterClass.ConstraintOrBuilder {
       public static final com.google.protobuf.Descriptors.Descriptor
           getDescriptor() {
-        return context.ContextOuterClass.internal_static_context_ConnectionList_descriptor;
+        return context.ContextOuterClass.internal_static_context_Constraint_descriptor;
       }
 
       @java.lang.Override
       protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
           internalGetFieldAccessorTable() {
-        return context.ContextOuterClass.internal_static_context_ConnectionList_fieldAccessorTable
+        return context.ContextOuterClass.internal_static_context_Constraint_fieldAccessorTable
             .ensureFieldAccessorsInitialized(
-                context.ContextOuterClass.ConnectionList.class, context.ContextOuterClass.ConnectionList.Builder.class);
+                context.ContextOuterClass.Constraint.class, context.ContextOuterClass.Constraint.Builder.class);
       }
 
-      // Construct using context.ContextOuterClass.ConnectionList.newBuilder()
+      // Construct using context.ContextOuterClass.Constraint.newBuilder()
       private Builder() {
         maybeForceBuilderInitialization();
       }
@@ -34278,36 +35592,33 @@ public final class ContextOuterClass {
       }
       private void maybeForceBuilderInitialization() {
         if (com.google.protobuf.GeneratedMessageV3
-                .alwaysUseFieldBuilders) {
-          getConnectionsFieldBuilder();
-        }
-      }
-      @java.lang.Override
-      public Builder clear() {
-        super.clear();
-        if (connectionsBuilder_ == null) {
-          connections_ = java.util.Collections.emptyList();
-          bitField0_ = (bitField0_ & ~0x00000001);
-        } else {
-          connectionsBuilder_.clear();
+                .alwaysUseFieldBuilders) {
         }
+      }
+      @java.lang.Override
+      public Builder clear() {
+        super.clear();
+        constraintType_ = "";
+
+        constraintValue_ = "";
+
         return this;
       }
 
       @java.lang.Override
       public com.google.protobuf.Descriptors.Descriptor
           getDescriptorForType() {
-        return context.ContextOuterClass.internal_static_context_ConnectionList_descriptor;
+        return context.ContextOuterClass.internal_static_context_Constraint_descriptor;
       }
 
       @java.lang.Override
-      public context.ContextOuterClass.ConnectionList getDefaultInstanceForType() {
-        return context.ContextOuterClass.ConnectionList.getDefaultInstance();
+      public context.ContextOuterClass.Constraint getDefaultInstanceForType() {
+        return context.ContextOuterClass.Constraint.getDefaultInstance();
       }
 
       @java.lang.Override
-      public context.ContextOuterClass.ConnectionList build() {
-        context.ContextOuterClass.ConnectionList result = buildPartial();
+      public context.ContextOuterClass.Constraint build() {
+        context.ContextOuterClass.Constraint result = buildPartial();
         if (!result.isInitialized()) {
           throw newUninitializedMessageException(result);
         }
@@ -34315,18 +35626,10 @@ public final class ContextOuterClass {
       }
 
       @java.lang.Override
-      public context.ContextOuterClass.ConnectionList buildPartial() {
-        context.ContextOuterClass.ConnectionList result = new context.ContextOuterClass.ConnectionList(this);
-        int from_bitField0_ = bitField0_;
-        if (connectionsBuilder_ == null) {
-          if (((bitField0_ & 0x00000001) != 0)) {
-            connections_ = java.util.Collections.unmodifiableList(connections_);
-            bitField0_ = (bitField0_ & ~0x00000001);
-          }
-          result.connections_ = connections_;
-        } else {
-          result.connections_ = connectionsBuilder_.build();
-        }
+      public context.ContextOuterClass.Constraint buildPartial() {
+        context.ContextOuterClass.Constraint result = new context.ContextOuterClass.Constraint(this);
+        result.constraintType_ = constraintType_;
+        result.constraintValue_ = constraintValue_;
         onBuilt();
         return result;
       }
@@ -34365,41 +35668,23 @@ public final class ContextOuterClass {
       }
       @java.lang.Override
       public Builder mergeFrom(com.google.protobuf.Message other) {
-        if (other instanceof context.ContextOuterClass.ConnectionList) {
-          return mergeFrom((context.ContextOuterClass.ConnectionList)other);
+        if (other instanceof context.ContextOuterClass.Constraint) {
+          return mergeFrom((context.ContextOuterClass.Constraint)other);
         } else {
           super.mergeFrom(other);
           return this;
         }
       }
 
-      public Builder mergeFrom(context.ContextOuterClass.ConnectionList other) {
-        if (other == context.ContextOuterClass.ConnectionList.getDefaultInstance()) return this;
-        if (connectionsBuilder_ == null) {
-          if (!other.connections_.isEmpty()) {
-            if (connections_.isEmpty()) {
-              connections_ = other.connections_;
-              bitField0_ = (bitField0_ & ~0x00000001);
-            } else {
-              ensureConnectionsIsMutable();
-              connections_.addAll(other.connections_);
-            }
-            onChanged();
-          }
-        } else {
-          if (!other.connections_.isEmpty()) {
-            if (connectionsBuilder_.isEmpty()) {
-              connectionsBuilder_.dispose();
-              connectionsBuilder_ = null;
-              connections_ = other.connections_;
-              bitField0_ = (bitField0_ & ~0x00000001);
-              connectionsBuilder_ = 
-                com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders ?
-                   getConnectionsFieldBuilder() : null;
-            } else {
-              connectionsBuilder_.addAllMessages(other.connections_);
-            }
-          }
+      public Builder mergeFrom(context.ContextOuterClass.Constraint other) {
+        if (other == context.ContextOuterClass.Constraint.getDefaultInstance()) return this;
+        if (!other.getConstraintType().isEmpty()) {
+          constraintType_ = other.constraintType_;
+          onChanged();
+        }
+        if (!other.getConstraintValue().isEmpty()) {
+          constraintValue_ = other.constraintValue_;
+          onChanged();
         }
         this.mergeUnknownFields(other.unknownFields);
         onChanged();
@@ -34416,11 +35701,11 @@ public final class ContextOuterClass {
           com.google.protobuf.CodedInputStream input,
           com.google.protobuf.ExtensionRegistryLite extensionRegistry)
           throws java.io.IOException {
-        context.ContextOuterClass.ConnectionList parsedMessage = null;
+        context.ContextOuterClass.Constraint parsedMessage = null;
         try {
           parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
         } catch (com.google.protobuf.InvalidProtocolBufferException e) {
-          parsedMessage = (context.ContextOuterClass.ConnectionList) e.getUnfinishedMessage();
+          parsedMessage = (context.ContextOuterClass.Constraint) e.getUnfinishedMessage();
           throw e.unwrapIOException();
         } finally {
           if (parsedMessage != null) {
@@ -34429,246 +35714,157 @@ public final class ContextOuterClass {
         }
         return this;
       }
-      private int bitField0_;
-
-      private java.util.List<context.ContextOuterClass.Connection> connections_ =
-        java.util.Collections.emptyList();
-      private void ensureConnectionsIsMutable() {
-        if (!((bitField0_ & 0x00000001) != 0)) {
-          connections_ = new java.util.ArrayList<context.ContextOuterClass.Connection>(connections_);
-          bitField0_ |= 0x00000001;
-         }
-      }
-
-      private com.google.protobuf.RepeatedFieldBuilderV3<
-          context.ContextOuterClass.Connection, context.ContextOuterClass.Connection.Builder, context.ContextOuterClass.ConnectionOrBuilder> connectionsBuilder_;
 
+      private java.lang.Object constraintType_ = "";
       /**
-       * <code>repeated .context.Connection connections = 1;</code>
-       */
-      public java.util.List<context.ContextOuterClass.Connection> getConnectionsList() {
-        if (connectionsBuilder_ == null) {
-          return java.util.Collections.unmodifiableList(connections_);
-        } else {
-          return connectionsBuilder_.getMessageList();
-        }
-      }
-      /**
-       * <code>repeated .context.Connection connections = 1;</code>
-       */
-      public int getConnectionsCount() {
-        if (connectionsBuilder_ == null) {
-          return connections_.size();
-        } else {
-          return connectionsBuilder_.getCount();
-        }
-      }
-      /**
-       * <code>repeated .context.Connection connections = 1;</code>
-       */
-      public context.ContextOuterClass.Connection getConnections(int index) {
-        if (connectionsBuilder_ == null) {
-          return connections_.get(index);
-        } else {
-          return connectionsBuilder_.getMessage(index);
-        }
-      }
-      /**
-       * <code>repeated .context.Connection connections = 1;</code>
-       */
-      public Builder setConnections(
-          int index, context.ContextOuterClass.Connection value) {
-        if (connectionsBuilder_ == null) {
-          if (value == null) {
-            throw new NullPointerException();
-          }
-          ensureConnectionsIsMutable();
-          connections_.set(index, value);
-          onChanged();
-        } else {
-          connectionsBuilder_.setMessage(index, value);
-        }
-        return this;
-      }
-      /**
-       * <code>repeated .context.Connection connections = 1;</code>
-       */
-      public Builder setConnections(
-          int index, context.ContextOuterClass.Connection.Builder builderForValue) {
-        if (connectionsBuilder_ == null) {
-          ensureConnectionsIsMutable();
-          connections_.set(index, builderForValue.build());
-          onChanged();
-        } else {
-          connectionsBuilder_.setMessage(index, builderForValue.build());
-        }
-        return this;
-      }
-      /**
-       * <code>repeated .context.Connection connections = 1;</code>
-       */
-      public Builder addConnections(context.ContextOuterClass.Connection value) {
-        if (connectionsBuilder_ == null) {
-          if (value == null) {
-            throw new NullPointerException();
-          }
-          ensureConnectionsIsMutable();
-          connections_.add(value);
-          onChanged();
-        } else {
-          connectionsBuilder_.addMessage(value);
-        }
-        return this;
-      }
-      /**
-       * <code>repeated .context.Connection connections = 1;</code>
-       */
-      public Builder addConnections(
-          int index, context.ContextOuterClass.Connection value) {
-        if (connectionsBuilder_ == null) {
-          if (value == null) {
-            throw new NullPointerException();
-          }
-          ensureConnectionsIsMutable();
-          connections_.add(index, value);
-          onChanged();
-        } else {
-          connectionsBuilder_.addMessage(index, value);
-        }
-        return this;
-      }
-      /**
-       * <code>repeated .context.Connection connections = 1;</code>
-       */
-      public Builder addConnections(
-          context.ContextOuterClass.Connection.Builder builderForValue) {
-        if (connectionsBuilder_ == null) {
-          ensureConnectionsIsMutable();
-          connections_.add(builderForValue.build());
-          onChanged();
-        } else {
-          connectionsBuilder_.addMessage(builderForValue.build());
-        }
-        return this;
-      }
-      /**
-       * <code>repeated .context.Connection connections = 1;</code>
+       * <code>string constraint_type = 1;</code>
+       * @return The constraintType.
        */
-      public Builder addConnections(
-          int index, context.ContextOuterClass.Connection.Builder builderForValue) {
-        if (connectionsBuilder_ == null) {
-          ensureConnectionsIsMutable();
-          connections_.add(index, builderForValue.build());
-          onChanged();
+      public java.lang.String getConstraintType() {
+        java.lang.Object ref = constraintType_;
+        if (!(ref instanceof java.lang.String)) {
+          com.google.protobuf.ByteString bs =
+              (com.google.protobuf.ByteString) ref;
+          java.lang.String s = bs.toStringUtf8();
+          constraintType_ = s;
+          return s;
         } else {
-          connectionsBuilder_.addMessage(index, builderForValue.build());
+          return (java.lang.String) ref;
         }
-        return this;
       }
       /**
-       * <code>repeated .context.Connection connections = 1;</code>
+       * <code>string constraint_type = 1;</code>
+       * @return The bytes for constraintType.
        */
-      public Builder addAllConnections(
-          java.lang.Iterable<? extends context.ContextOuterClass.Connection> values) {
-        if (connectionsBuilder_ == null) {
-          ensureConnectionsIsMutable();
-          com.google.protobuf.AbstractMessageLite.Builder.addAll(
-              values, connections_);
-          onChanged();
+      public com.google.protobuf.ByteString
+          getConstraintTypeBytes() {
+        java.lang.Object ref = constraintType_;
+        if (ref instanceof String) {
+          com.google.protobuf.ByteString b = 
+              com.google.protobuf.ByteString.copyFromUtf8(
+                  (java.lang.String) ref);
+          constraintType_ = b;
+          return b;
         } else {
-          connectionsBuilder_.addAllMessages(values);
+          return (com.google.protobuf.ByteString) ref;
         }
-        return this;
       }
       /**
-       * <code>repeated .context.Connection connections = 1;</code>
-       */
-      public Builder clearConnections() {
-        if (connectionsBuilder_ == null) {
-          connections_ = java.util.Collections.emptyList();
-          bitField0_ = (bitField0_ & ~0x00000001);
-          onChanged();
-        } else {
-          connectionsBuilder_.clear();
-        }
+       * <code>string constraint_type = 1;</code>
+       * @param value The constraintType to set.
+       * @return This builder for chaining.
+       */
+      public Builder setConstraintType(
+          java.lang.String value) {
+        if (value == null) {
+    throw new NullPointerException();
+  }
+  
+        constraintType_ = value;
+        onChanged();
         return this;
       }
       /**
-       * <code>repeated .context.Connection connections = 1;</code>
+       * <code>string constraint_type = 1;</code>
+       * @return This builder for chaining.
        */
-      public Builder removeConnections(int index) {
-        if (connectionsBuilder_ == null) {
-          ensureConnectionsIsMutable();
-          connections_.remove(index);
-          onChanged();
-        } else {
-          connectionsBuilder_.remove(index);
-        }
+      public Builder clearConstraintType() {
+        
+        constraintType_ = getDefaultInstance().getConstraintType();
+        onChanged();
         return this;
       }
       /**
-       * <code>repeated .context.Connection connections = 1;</code>
+       * <code>string constraint_type = 1;</code>
+       * @param value The bytes for constraintType to set.
+       * @return This builder for chaining.
        */
-      public context.ContextOuterClass.Connection.Builder getConnectionsBuilder(
-          int index) {
-        return getConnectionsFieldBuilder().getBuilder(index);
+      public Builder setConstraintTypeBytes(
+          com.google.protobuf.ByteString value) {
+        if (value == null) {
+    throw new NullPointerException();
+  }
+  checkByteStringIsUtf8(value);
+        
+        constraintType_ = value;
+        onChanged();
+        return this;
       }
+
+      private java.lang.Object constraintValue_ = "";
       /**
-       * <code>repeated .context.Connection connections = 1;</code>
+       * <code>string constraint_value = 2;</code>
+       * @return The constraintValue.
        */
-      public context.ContextOuterClass.ConnectionOrBuilder getConnectionsOrBuilder(
-          int index) {
-        if (connectionsBuilder_ == null) {
-          return connections_.get(index);  } else {
-          return connectionsBuilder_.getMessageOrBuilder(index);
+      public java.lang.String getConstraintValue() {
+        java.lang.Object ref = constraintValue_;
+        if (!(ref instanceof java.lang.String)) {
+          com.google.protobuf.ByteString bs =
+              (com.google.protobuf.ByteString) ref;
+          java.lang.String s = bs.toStringUtf8();
+          constraintValue_ = s;
+          return s;
+        } else {
+          return (java.lang.String) ref;
         }
       }
       /**
-       * <code>repeated .context.Connection connections = 1;</code>
+       * <code>string constraint_value = 2;</code>
+       * @return The bytes for constraintValue.
        */
-      public java.util.List<? extends context.ContextOuterClass.ConnectionOrBuilder> 
-           getConnectionsOrBuilderList() {
-        if (connectionsBuilder_ != null) {
-          return connectionsBuilder_.getMessageOrBuilderList();
+      public com.google.protobuf.ByteString
+          getConstraintValueBytes() {
+        java.lang.Object ref = constraintValue_;
+        if (ref instanceof String) {
+          com.google.protobuf.ByteString b = 
+              com.google.protobuf.ByteString.copyFromUtf8(
+                  (java.lang.String) ref);
+          constraintValue_ = b;
+          return b;
         } else {
-          return java.util.Collections.unmodifiableList(connections_);
+          return (com.google.protobuf.ByteString) ref;
         }
       }
       /**
-       * <code>repeated .context.Connection connections = 1;</code>
+       * <code>string constraint_value = 2;</code>
+       * @param value The constraintValue to set.
+       * @return This builder for chaining.
        */
-      public context.ContextOuterClass.Connection.Builder addConnectionsBuilder() {
-        return getConnectionsFieldBuilder().addBuilder(
-            context.ContextOuterClass.Connection.getDefaultInstance());
+      public Builder setConstraintValue(
+          java.lang.String value) {
+        if (value == null) {
+    throw new NullPointerException();
+  }
+  
+        constraintValue_ = value;
+        onChanged();
+        return this;
       }
       /**
-       * <code>repeated .context.Connection connections = 1;</code>
+       * <code>string constraint_value = 2;</code>
+       * @return This builder for chaining.
        */
-      public context.ContextOuterClass.Connection.Builder addConnectionsBuilder(
-          int index) {
-        return getConnectionsFieldBuilder().addBuilder(
-            index, context.ContextOuterClass.Connection.getDefaultInstance());
+      public Builder clearConstraintValue() {
+        
+        constraintValue_ = getDefaultInstance().getConstraintValue();
+        onChanged();
+        return this;
       }
       /**
-       * <code>repeated .context.Connection connections = 1;</code>
+       * <code>string constraint_value = 2;</code>
+       * @param value The bytes for constraintValue to set.
+       * @return This builder for chaining.
        */
-      public java.util.List<context.ContextOuterClass.Connection.Builder> 
-           getConnectionsBuilderList() {
-        return getConnectionsFieldBuilder().getBuilderList();
-      }
-      private com.google.protobuf.RepeatedFieldBuilderV3<
-          context.ContextOuterClass.Connection, context.ContextOuterClass.Connection.Builder, context.ContextOuterClass.ConnectionOrBuilder> 
-          getConnectionsFieldBuilder() {
-        if (connectionsBuilder_ == null) {
-          connectionsBuilder_ = new com.google.protobuf.RepeatedFieldBuilderV3<
-              context.ContextOuterClass.Connection, context.ContextOuterClass.Connection.Builder, context.ContextOuterClass.ConnectionOrBuilder>(
-                  connections_,
-                  ((bitField0_ & 0x00000001) != 0),
-                  getParentForChildren(),
-                  isClean());
-          connections_ = null;
-        }
-        return connectionsBuilder_;
+      public Builder setConstraintValueBytes(
+          com.google.protobuf.ByteString value) {
+        if (value == null) {
+    throw new NullPointerException();
+  }
+  checkByteStringIsUtf8(value);
+        
+        constraintValue_ = value;
+        onChanged();
+        return this;
       }
       @java.lang.Override
       public final Builder setUnknownFields(
@@ -34683,41 +35879,41 @@ public final class ContextOuterClass {
       }
 
 
-      // @@protoc_insertion_point(builder_scope:context.ConnectionList)
+      // @@protoc_insertion_point(builder_scope:context.Constraint)
     }
 
-    // @@protoc_insertion_point(class_scope:context.ConnectionList)
-    private static final context.ContextOuterClass.ConnectionList DEFAULT_INSTANCE;
+    // @@protoc_insertion_point(class_scope:context.Constraint)
+    private static final context.ContextOuterClass.Constraint DEFAULT_INSTANCE;
     static {
-      DEFAULT_INSTANCE = new context.ContextOuterClass.ConnectionList();
+      DEFAULT_INSTANCE = new context.ContextOuterClass.Constraint();
     }
 
-    public static context.ContextOuterClass.ConnectionList getDefaultInstance() {
+    public static context.ContextOuterClass.Constraint getDefaultInstance() {
       return DEFAULT_INSTANCE;
     }
 
-    private static final com.google.protobuf.Parser<ConnectionList>
-        PARSER = new com.google.protobuf.AbstractParser<ConnectionList>() {
+    private static final com.google.protobuf.Parser<Constraint>
+        PARSER = new com.google.protobuf.AbstractParser<Constraint>() {
       @java.lang.Override
-      public ConnectionList parsePartialFrom(
+      public Constraint parsePartialFrom(
           com.google.protobuf.CodedInputStream input,
           com.google.protobuf.ExtensionRegistryLite extensionRegistry)
           throws com.google.protobuf.InvalidProtocolBufferException {
-        return new ConnectionList(input, extensionRegistry);
+        return new Constraint(input, extensionRegistry);
       }
     };
 
-    public static com.google.protobuf.Parser<ConnectionList> parser() {
+    public static com.google.protobuf.Parser<Constraint> parser() {
       return PARSER;
     }
 
     @java.lang.Override
-    public com.google.protobuf.Parser<ConnectionList> getParserForType() {
+    public com.google.protobuf.Parser<Constraint> getParserForType() {
       return PARSER;
     }
 
     @java.lang.Override
-    public context.ContextOuterClass.ConnectionList getDefaultInstanceForType() {
+    public context.ContextOuterClass.Constraint getDefaultInstanceForType() {
       return DEFAULT_INSTANCE;
     }
 
@@ -36420,45 +37616,50 @@ public final class ContextOuterClass {
     com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
       internal_static_context_ServiceEvent_fieldAccessorTable;
   private static final com.google.protobuf.Descriptors.Descriptor
-    internal_static_context_EndPointId_descriptor;
+    internal_static_context_ConnectionId_descriptor;
   private static final 
     com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
-      internal_static_context_EndPointId_fieldAccessorTable;
+      internal_static_context_ConnectionId_fieldAccessorTable;
   private static final com.google.protobuf.Descriptors.Descriptor
-    internal_static_context_EndPoint_descriptor;
+    internal_static_context_Connection_descriptor;
   private static final 
     com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
-      internal_static_context_EndPoint_fieldAccessorTable;
+      internal_static_context_Connection_fieldAccessorTable;
   private static final com.google.protobuf.Descriptors.Descriptor
-    internal_static_context_ConfigRule_descriptor;
+    internal_static_context_ConnectionIdList_descriptor;
   private static final 
     com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
-      internal_static_context_ConfigRule_fieldAccessorTable;
+      internal_static_context_ConnectionIdList_fieldAccessorTable;
   private static final com.google.protobuf.Descriptors.Descriptor
-    internal_static_context_Constraint_descriptor;
+    internal_static_context_ConnectionList_descriptor;
   private static final 
     com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
-      internal_static_context_Constraint_fieldAccessorTable;
+      internal_static_context_ConnectionList_fieldAccessorTable;
   private static final com.google.protobuf.Descriptors.Descriptor
-    internal_static_context_ConnectionId_descriptor;
+    internal_static_context_ConnectionEvent_descriptor;
   private static final 
     com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
-      internal_static_context_ConnectionId_fieldAccessorTable;
+      internal_static_context_ConnectionEvent_fieldAccessorTable;
   private static final com.google.protobuf.Descriptors.Descriptor
-    internal_static_context_Connection_descriptor;
+    internal_static_context_EndPointId_descriptor;
   private static final 
     com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
-      internal_static_context_Connection_fieldAccessorTable;
+      internal_static_context_EndPointId_fieldAccessorTable;
   private static final com.google.protobuf.Descriptors.Descriptor
-    internal_static_context_ConnectionIdList_descriptor;
+    internal_static_context_EndPoint_descriptor;
   private static final 
     com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
-      internal_static_context_ConnectionIdList_fieldAccessorTable;
+      internal_static_context_EndPoint_fieldAccessorTable;
   private static final com.google.protobuf.Descriptors.Descriptor
-    internal_static_context_ConnectionList_descriptor;
+    internal_static_context_ConfigRule_descriptor;
   private static final 
     com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
-      internal_static_context_ConnectionList_fieldAccessorTable;
+      internal_static_context_ConfigRule_fieldAccessorTable;
+  private static final com.google.protobuf.Descriptors.Descriptor
+    internal_static_context_Constraint_descriptor;
+  private static final 
+    com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+      internal_static_context_Constraint_fieldAccessorTable;
   private static final com.google.protobuf.Descriptors.Descriptor
     internal_static_context_TeraFlowController_descriptor;
   private static final 
@@ -36543,94 +37744,107 @@ public final class ContextOuterClass {
       "\"\n\010services\030\001 \003(\0132\020.context.Service\"U\n\014S" +
       "erviceEvent\022\035\n\005event\030\001 \001(\0132\016.context.Eve" +
       "nt\022&\n\nservice_id\030\002 \001(\0132\022.context.Service" +
-      "Id\"\202\001\n\nEndPointId\022(\n\013topology_id\030\001 \001(\0132\023" +
-      ".context.TopologyId\022$\n\tdevice_id\030\002 \001(\0132\021" +
-      ".context.DeviceId\022$\n\rendpoint_uuid\030\003 \001(\013" +
-      "2\r.context.Uuid\"\206\001\n\010EndPoint\022(\n\013endpoint" +
-      "_id\030\001 \001(\0132\023.context.EndPointId\022\025\n\rendpoi" +
-      "nt_type\030\002 \001(\t\0229\n\020kpi_sample_types\030\003 \003(\0162" +
-      "\037.kpi_sample_types.KpiSampleType\"e\n\nConf" +
-      "igRule\022)\n\006action\030\001 \001(\0162\031.context.ConfigA" +
-      "ctionEnum\022\024\n\014resource_key\030\002 \001(\t\022\026\n\016resou" +
-      "rce_value\030\003 \001(\t\"?\n\nConstraint\022\027\n\017constra" +
-      "int_type\030\001 \001(\t\022\030\n\020constraint_value\030\002 \001(\t" +
-      "\"6\n\014ConnectionId\022&\n\017connection_uuid\030\001 \001(" +
-      "\0132\r.context.Uuid\"\215\001\n\nConnection\022,\n\rconne" +
-      "ction_id\030\001 \001(\0132\025.context.ConnectionId\022.\n" +
-      "\022related_service_id\030\002 \001(\0132\022.context.Serv" +
-      "iceId\022!\n\004path\030\003 \003(\0132\023.context.EndPointId" +
-      "\"A\n\020ConnectionIdList\022-\n\016connection_ids\030\001" +
-      " \003(\0132\025.context.ConnectionId\":\n\016Connectio" +
-      "nList\022(\n\013connections\030\001 \003(\0132\023.context.Con" +
-      "nection\"^\n\022TeraFlowController\022&\n\ncontext" +
-      "_id\030\001 \001(\0132\022.context.ContextId\022\022\n\nip_addr" +
-      "ess\030\002 \001(\t\022\014\n\004port\030\003 \001(\r\"U\n\024Authenticatio" +
-      "nResult\022&\n\ncontext_id\030\001 \001(\0132\022.context.Co" +
-      "ntextId\022\025\n\rauthenticated\030\002 \001(\010*j\n\rEventT" +
-      "ypeEnum\022\027\n\023EVENTTYPE_UNDEFINED\020\000\022\024\n\020EVEN" +
-      "TTYPE_CREATE\020\001\022\024\n\020EVENTTYPE_UPDATE\020\002\022\024\n\020" +
-      "EVENTTYPE_REMOVE\020\003*\305\001\n\020DeviceDriverEnum\022" +
-      "\032\n\026DEVICEDRIVER_UNDEFINED\020\000\022\033\n\027DEVICEDRI" +
-      "VER_OPENCONFIG\020\001\022\036\n\032DEVICEDRIVER_TRANSPO" +
-      "RT_API\020\002\022\023\n\017DEVICEDRIVER_P4\020\003\022&\n\"DEVICED" +
-      "RIVER_IETF_NETWORK_TOPOLOGY\020\004\022\033\n\027DEVICED" +
-      "RIVER_ONF_TR_352\020\005*\217\001\n\033DeviceOperational" +
-      "StatusEnum\022%\n!DEVICEOPERATIONALSTATUS_UN" +
-      "DEFINED\020\000\022$\n DEVICEOPERATIONALSTATUS_DIS" +
-      "ABLED\020\001\022#\n\037DEVICEOPERATIONALSTATUS_ENABL" +
-      "ED\020\002*\201\001\n\017ServiceTypeEnum\022\027\n\023SERVICETYPE_" +
-      "UNKNOWN\020\000\022\024\n\020SERVICETYPE_L3NM\020\001\022\024\n\020SERVI" +
-      "CETYPE_L2NM\020\002\022)\n%SERVICETYPE_TAPI_CONNEC" +
-      "TIVITY_SERVICE\020\003*\210\001\n\021ServiceStatusEnum\022\033" +
-      "\n\027SERVICESTATUS_UNDEFINED\020\000\022\031\n\025SERVICEST" +
-      "ATUS_PLANNED\020\001\022\030\n\024SERVICESTATUS_ACTIVE\020\002" +
-      "\022!\n\035SERVICESTATUS_PENDING_REMOVAL\020\003*]\n\020C" +
-      "onfigActionEnum\022\032\n\026CONFIGACTION_UNDEFINE" +
-      "D\020\000\022\024\n\020CONFIGACTION_SET\020\001\022\027\n\023CONFIGACTIO" +
-      "N_DELETE\020\0022\245\r\n\016ContextService\022:\n\016ListCon" +
-      "textIds\022\016.context.Empty\032\026.context.Contex" +
-      "tIdList\"\000\0226\n\014ListContexts\022\016.context.Empt" +
-      "y\032\024.context.ContextList\"\000\0224\n\nGetContext\022" +
-      "\022.context.ContextId\032\020.context.Context\"\000\022" +
-      "4\n\nSetContext\022\020.context.Context\032\022.contex" +
-      "t.ContextId\"\000\0225\n\rRemoveContext\022\022.context" +
-      ".ContextId\032\016.context.Empty\"\000\022=\n\020GetConte" +
-      "xtEvents\022\016.context.Empty\032\025.context.Conte" +
-      "xtEvent\"\0000\001\022@\n\017ListTopologyIds\022\022.context" +
-      ".ContextId\032\027.context.TopologyIdList\"\000\022=\n" +
-      "\016ListTopologies\022\022.context.ContextId\032\025.co" +
-      "ntext.TopologyList\"\000\0227\n\013GetTopology\022\023.co" +
-      "ntext.TopologyId\032\021.context.Topology\"\000\0227\n" +
-      "\013SetTopology\022\021.context.Topology\032\023.contex" +
-      "t.TopologyId\"\000\0227\n\016RemoveTopology\022\023.conte" +
-      "xt.TopologyId\032\016.context.Empty\"\000\022?\n\021GetTo" +
-      "pologyEvents\022\016.context.Empty\032\026.context.T" +
-      "opologyEvent\"\0000\001\0228\n\rListDeviceIds\022\016.cont" +
-      "ext.Empty\032\025.context.DeviceIdList\"\000\0224\n\013Li" +
-      "stDevices\022\016.context.Empty\032\023.context.Devi" +
-      "ceList\"\000\0221\n\tGetDevice\022\021.context.DeviceId" +
-      "\032\017.context.Device\"\000\0221\n\tSetDevice\022\017.conte" +
-      "xt.Device\032\021.context.DeviceId\"\000\0223\n\014Remove" +
-      "Device\022\021.context.DeviceId\032\016.context.Empt" +
-      "y\"\000\022;\n\017GetDeviceEvents\022\016.context.Empty\032\024" +
-      ".context.DeviceEvent\"\0000\001\0224\n\013ListLinkIds\022" +
-      "\016.context.Empty\032\023.context.LinkIdList\"\000\0220" +
-      "\n\tListLinks\022\016.context.Empty\032\021.context.Li" +
-      "nkList\"\000\022+\n\007GetLink\022\017.context.LinkId\032\r.c" +
-      "ontext.Link\"\000\022+\n\007SetLink\022\r.context.Link\032" +
-      "\017.context.LinkId\"\000\022/\n\nRemoveLink\022\017.conte" +
-      "xt.LinkId\032\016.context.Empty\"\000\0227\n\rGetLinkEv" +
-      "ents\022\016.context.Empty\032\022.context.LinkEvent" +
-      "\"\0000\001\022>\n\016ListServiceIds\022\022.context.Context" +
-      "Id\032\026.context.ServiceIdList\"\000\022:\n\014ListServ" +
-      "ices\022\022.context.ContextId\032\024.context.Servi" +
-      "ceList\"\000\0224\n\nGetService\022\022.context.Service" +
-      "Id\032\020.context.Service\"\000\0224\n\nSetService\022\020.c" +
-      "ontext.Service\032\022.context.ServiceId\"\000\0225\n\r" +
-      "RemoveService\022\022.context.ServiceId\032\016.cont" +
-      "ext.Empty\"\000\022=\n\020GetServiceEvents\022\016.contex" +
-      "t.Empty\032\025.context.ServiceEvent\"\0000\001b\006prot" +
-      "o3"
+      "Id\"6\n\014ConnectionId\022&\n\017connection_uuid\030\001 " +
+      "\001(\0132\r.context.Uuid\"\304\001\n\nConnection\022,\n\rcon" +
+      "nection_id\030\001 \001(\0132\025.context.ConnectionId\022" +
+      "&\n\nservice_id\030\002 \001(\0132\022.context.ServiceId\022" +
+      "3\n\026path_hops_endpoint_ids\030\003 \003(\0132\023.contex" +
+      "t.EndPointId\022+\n\017sub_service_ids\030\004 \003(\0132\022." +
+      "context.ServiceId\"A\n\020ConnectionIdList\022-\n" +
+      "\016connection_ids\030\001 \003(\0132\025.context.Connecti" +
+      "onId\":\n\016ConnectionList\022(\n\013connections\030\001 " +
+      "\003(\0132\023.context.Connection\"^\n\017ConnectionEv" +
+      "ent\022\035\n\005event\030\001 \001(\0132\016.context.Event\022,\n\rco" +
+      "nnection_id\030\002 \001(\0132\025.context.ConnectionId" +
+      "\"\202\001\n\nEndPointId\022(\n\013topology_id\030\001 \001(\0132\023.c" +
+      "ontext.TopologyId\022$\n\tdevice_id\030\002 \001(\0132\021.c" +
+      "ontext.DeviceId\022$\n\rendpoint_uuid\030\003 \001(\0132\r" +
+      ".context.Uuid\"\206\001\n\010EndPoint\022(\n\013endpoint_i" +
+      "d\030\001 \001(\0132\023.context.EndPointId\022\025\n\rendpoint" +
+      "_type\030\002 \001(\t\0229\n\020kpi_sample_types\030\003 \003(\0162\037." +
+      "kpi_sample_types.KpiSampleType\"e\n\nConfig" +
+      "Rule\022)\n\006action\030\001 \001(\0162\031.context.ConfigAct" +
+      "ionEnum\022\024\n\014resource_key\030\002 \001(\t\022\026\n\016resourc" +
+      "e_value\030\003 \001(\t\"?\n\nConstraint\022\027\n\017constrain" +
+      "t_type\030\001 \001(\t\022\030\n\020constraint_value\030\002 \001(\t\"^" +
+      "\n\022TeraFlowController\022&\n\ncontext_id\030\001 \001(\013" +
+      "2\022.context.ContextId\022\022\n\nip_address\030\002 \001(\t" +
+      "\022\014\n\004port\030\003 \001(\r\"U\n\024AuthenticationResult\022&" +
+      "\n\ncontext_id\030\001 \001(\0132\022.context.ContextId\022\025" +
+      "\n\rauthenticated\030\002 \001(\010*j\n\rEventTypeEnum\022\027" +
+      "\n\023EVENTTYPE_UNDEFINED\020\000\022\024\n\020EVENTTYPE_CRE" +
+      "ATE\020\001\022\024\n\020EVENTTYPE_UPDATE\020\002\022\024\n\020EVENTTYPE" +
+      "_REMOVE\020\003*\305\001\n\020DeviceDriverEnum\022\032\n\026DEVICE" +
+      "DRIVER_UNDEFINED\020\000\022\033\n\027DEVICEDRIVER_OPENC" +
+      "ONFIG\020\001\022\036\n\032DEVICEDRIVER_TRANSPORT_API\020\002\022" +
+      "\023\n\017DEVICEDRIVER_P4\020\003\022&\n\"DEVICEDRIVER_IET" +
+      "F_NETWORK_TOPOLOGY\020\004\022\033\n\027DEVICEDRIVER_ONF" +
+      "_TR_352\020\005*\217\001\n\033DeviceOperationalStatusEnu" +
+      "m\022%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\020\000" +
+      "\022$\n DEVICEOPERATIONALSTATUS_DISABLED\020\001\022#" +
+      "\n\037DEVICEOPERATIONALSTATUS_ENABLED\020\002*\201\001\n\017" +
+      "ServiceTypeEnum\022\027\n\023SERVICETYPE_UNKNOWN\020\000" +
+      "\022\024\n\020SERVICETYPE_L3NM\020\001\022\024\n\020SERVICETYPE_L2" +
+      "NM\020\002\022)\n%SERVICETYPE_TAPI_CONNECTIVITY_SE" +
+      "RVICE\020\003*\210\001\n\021ServiceStatusEnum\022\033\n\027SERVICE" +
+      "STATUS_UNDEFINED\020\000\022\031\n\025SERVICESTATUS_PLAN" +
+      "NED\020\001\022\030\n\024SERVICESTATUS_ACTIVE\020\002\022!\n\035SERVI" +
+      "CESTATUS_PENDING_REMOVAL\020\003*]\n\020ConfigActi" +
+      "onEnum\022\032\n\026CONFIGACTION_UNDEFINED\020\000\022\024\n\020CO" +
+      "NFIGACTION_SET\020\001\022\027\n\023CONFIGACTION_DELETE\020" +
+      "\0022\255\020\n\016ContextService\022:\n\016ListContextIds\022\016" +
+      ".context.Empty\032\026.context.ContextIdList\"\000" +
+      "\0226\n\014ListContexts\022\016.context.Empty\032\024.conte" +
+      "xt.ContextList\"\000\0224\n\nGetContext\022\022.context" +
+      ".ContextId\032\020.context.Context\"\000\0224\n\nSetCon" +
+      "text\022\020.context.Context\032\022.context.Context" +
+      "Id\"\000\0225\n\rRemoveContext\022\022.context.ContextI" +
+      "d\032\016.context.Empty\"\000\022=\n\020GetContextEvents\022" +
+      "\016.context.Empty\032\025.context.ContextEvent\"\000" +
+      "0\001\022@\n\017ListTopologyIds\022\022.context.ContextI" +
+      "d\032\027.context.TopologyIdList\"\000\022=\n\016ListTopo" +
+      "logies\022\022.context.ContextId\032\025.context.Top" +
+      "ologyList\"\000\0227\n\013GetTopology\022\023.context.Top" +
+      "ologyId\032\021.context.Topology\"\000\0227\n\013SetTopol" +
+      "ogy\022\021.context.Topology\032\023.context.Topolog" +
+      "yId\"\000\0227\n\016RemoveTopology\022\023.context.Topolo" +
+      "gyId\032\016.context.Empty\"\000\022?\n\021GetTopologyEve" +
+      "nts\022\016.context.Empty\032\026.context.TopologyEv" +
+      "ent\"\0000\001\0228\n\rListDeviceIds\022\016.context.Empty" +
+      "\032\025.context.DeviceIdList\"\000\0224\n\013ListDevices" +
+      "\022\016.context.Empty\032\023.context.DeviceList\"\000\022" +
+      "1\n\tGetDevice\022\021.context.DeviceId\032\017.contex" +
+      "t.Device\"\000\0221\n\tSetDevice\022\017.context.Device" +
+      "\032\021.context.DeviceId\"\000\0223\n\014RemoveDevice\022\021." +
+      "context.DeviceId\032\016.context.Empty\"\000\022;\n\017Ge" +
+      "tDeviceEvents\022\016.context.Empty\032\024.context." +
+      "DeviceEvent\"\0000\001\0224\n\013ListLinkIds\022\016.context" +
+      ".Empty\032\023.context.LinkIdList\"\000\0220\n\tListLin" +
+      "ks\022\016.context.Empty\032\021.context.LinkList\"\000\022" +
+      "+\n\007GetLink\022\017.context.LinkId\032\r.context.Li" +
+      "nk\"\000\022+\n\007SetLink\022\r.context.Link\032\017.context" +
+      ".LinkId\"\000\022/\n\nRemoveLink\022\017.context.LinkId" +
+      "\032\016.context.Empty\"\000\0227\n\rGetLinkEvents\022\016.co" +
+      "ntext.Empty\032\022.context.LinkEvent\"\0000\001\022>\n\016L" +
+      "istServiceIds\022\022.context.ContextId\032\026.cont" +
+      "ext.ServiceIdList\"\000\022:\n\014ListServices\022\022.co" +
+      "ntext.ContextId\032\024.context.ServiceList\"\000\022" +
+      "4\n\nGetService\022\022.context.ServiceId\032\020.cont" +
+      "ext.Service\"\000\0224\n\nSetService\022\020.context.Se" +
+      "rvice\032\022.context.ServiceId\"\000\0225\n\rRemoveSer" +
+      "vice\022\022.context.ServiceId\032\016.context.Empty" +
+      "\"\000\022=\n\020GetServiceEvents\022\016.context.Empty\032\025" +
+      ".context.ServiceEvent\"\0000\001\022D\n\021ListConnect" +
+      "ionIds\022\022.context.ServiceId\032\031.context.Con" +
+      "nectionIdList\"\000\022@\n\017ListConnections\022\022.con" +
+      "text.ServiceId\032\027.context.ConnectionList\"" +
+      "\000\022=\n\rGetConnection\022\025.context.ConnectionI" +
+      "d\032\023.context.Connection\"\000\022=\n\rSetConnectio" +
+      "n\022\023.context.Connection\032\025.context.Connect" +
+      "ionId\"\000\022;\n\020RemoveConnection\022\025.context.Co" +
+      "nnectionId\032\016.context.Empty\"\000\022C\n\023GetConne" +
+      "ctionEvents\022\016.context.Empty\032\030.context.Co" +
+      "nnectionEvent\"\0000\001b\006proto3"
     };
     descriptor = com.google.protobuf.Descriptors.FileDescriptor
       .internalBuildGeneratedFileFrom(descriptorData,
@@ -36823,62 +38037,68 @@ public final class ContextOuterClass {
       com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
         internal_static_context_ServiceEvent_descriptor,
         new java.lang.String[] { "Event", "ServiceId", });
-    internal_static_context_EndPointId_descriptor =
+    internal_static_context_ConnectionId_descriptor =
       getDescriptor().getMessageTypes().get(31);
+    internal_static_context_ConnectionId_fieldAccessorTable = new
+      com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
+        internal_static_context_ConnectionId_descriptor,
+        new java.lang.String[] { "ConnectionUuid", });
+    internal_static_context_Connection_descriptor =
+      getDescriptor().getMessageTypes().get(32);
+    internal_static_context_Connection_fieldAccessorTable = new
+      com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
+        internal_static_context_Connection_descriptor,
+        new java.lang.String[] { "ConnectionId", "ServiceId", "PathHopsEndpointIds", "SubServiceIds", });
+    internal_static_context_ConnectionIdList_descriptor =
+      getDescriptor().getMessageTypes().get(33);
+    internal_static_context_ConnectionIdList_fieldAccessorTable = new
+      com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
+        internal_static_context_ConnectionIdList_descriptor,
+        new java.lang.String[] { "ConnectionIds", });
+    internal_static_context_ConnectionList_descriptor =
+      getDescriptor().getMessageTypes().get(34);
+    internal_static_context_ConnectionList_fieldAccessorTable = new
+      com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
+        internal_static_context_ConnectionList_descriptor,
+        new java.lang.String[] { "Connections", });
+    internal_static_context_ConnectionEvent_descriptor =
+      getDescriptor().getMessageTypes().get(35);
+    internal_static_context_ConnectionEvent_fieldAccessorTable = new
+      com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
+        internal_static_context_ConnectionEvent_descriptor,
+        new java.lang.String[] { "Event", "ConnectionId", });
+    internal_static_context_EndPointId_descriptor =
+      getDescriptor().getMessageTypes().get(36);
     internal_static_context_EndPointId_fieldAccessorTable = new
       com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
         internal_static_context_EndPointId_descriptor,
         new java.lang.String[] { "TopologyId", "DeviceId", "EndpointUuid", });
     internal_static_context_EndPoint_descriptor =
-      getDescriptor().getMessageTypes().get(32);
+      getDescriptor().getMessageTypes().get(37);
     internal_static_context_EndPoint_fieldAccessorTable = new
       com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
         internal_static_context_EndPoint_descriptor,
         new java.lang.String[] { "EndpointId", "EndpointType", "KpiSampleTypes", });
     internal_static_context_ConfigRule_descriptor =
-      getDescriptor().getMessageTypes().get(33);
+      getDescriptor().getMessageTypes().get(38);
     internal_static_context_ConfigRule_fieldAccessorTable = new
       com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
         internal_static_context_ConfigRule_descriptor,
         new java.lang.String[] { "Action", "ResourceKey", "ResourceValue", });
     internal_static_context_Constraint_descriptor =
-      getDescriptor().getMessageTypes().get(34);
+      getDescriptor().getMessageTypes().get(39);
     internal_static_context_Constraint_fieldAccessorTable = new
       com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
         internal_static_context_Constraint_descriptor,
         new java.lang.String[] { "ConstraintType", "ConstraintValue", });
-    internal_static_context_ConnectionId_descriptor =
-      getDescriptor().getMessageTypes().get(35);
-    internal_static_context_ConnectionId_fieldAccessorTable = new
-      com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
-        internal_static_context_ConnectionId_descriptor,
-        new java.lang.String[] { "ConnectionUuid", });
-    internal_static_context_Connection_descriptor =
-      getDescriptor().getMessageTypes().get(36);
-    internal_static_context_Connection_fieldAccessorTable = new
-      com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
-        internal_static_context_Connection_descriptor,
-        new java.lang.String[] { "ConnectionId", "RelatedServiceId", "Path", });
-    internal_static_context_ConnectionIdList_descriptor =
-      getDescriptor().getMessageTypes().get(37);
-    internal_static_context_ConnectionIdList_fieldAccessorTable = new
-      com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
-        internal_static_context_ConnectionIdList_descriptor,
-        new java.lang.String[] { "ConnectionIds", });
-    internal_static_context_ConnectionList_descriptor =
-      getDescriptor().getMessageTypes().get(38);
-    internal_static_context_ConnectionList_fieldAccessorTable = new
-      com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
-        internal_static_context_ConnectionList_descriptor,
-        new java.lang.String[] { "Connections", });
     internal_static_context_TeraFlowController_descriptor =
-      getDescriptor().getMessageTypes().get(39);
+      getDescriptor().getMessageTypes().get(40);
     internal_static_context_TeraFlowController_fieldAccessorTable = new
       com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
         internal_static_context_TeraFlowController_descriptor,
         new java.lang.String[] { "ContextId", "IpAddress", "Port", });
     internal_static_context_AuthenticationResult_descriptor =
-      getDescriptor().getMessageTypes().get(40);
+      getDescriptor().getMessageTypes().get(41);
     internal_static_context_AuthenticationResult_fieldAccessorTable = new
       com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
         internal_static_context_AuthenticationResult_descriptor,
diff --git a/src/automation/target/generated-sources/grpc/context/ContextService.java b/src/automation/target/generated-sources/grpc/context/ContextService.java
index 8e5095d25896abc5ff37b7a5e7d4ac7f97825dc6..cbd51163f340212839e0eeb2ad096032fdba8109 100644
--- a/src/automation/target/generated-sources/grpc/context/ContextService.java
+++ b/src/automation/target/generated-sources/grpc/context/ContextService.java
@@ -58,6 +58,16 @@ public interface ContextService extends MutinyService {
     
     io.smallrye.mutiny.Uni<context.ContextOuterClass.Empty> removeService(context.ContextOuterClass.ServiceId request);
     
+    io.smallrye.mutiny.Uni<context.ContextOuterClass.ConnectionIdList> listConnectionIds(context.ContextOuterClass.ServiceId request);
+    
+    io.smallrye.mutiny.Uni<context.ContextOuterClass.ConnectionList> listConnections(context.ContextOuterClass.ServiceId request);
+    
+    io.smallrye.mutiny.Uni<context.ContextOuterClass.Connection> getConnection(context.ContextOuterClass.ConnectionId request);
+    
+    io.smallrye.mutiny.Uni<context.ContextOuterClass.ConnectionId> setConnection(context.ContextOuterClass.Connection request);
+    
+    io.smallrye.mutiny.Uni<context.ContextOuterClass.Empty> removeConnection(context.ContextOuterClass.ConnectionId request);
+    
     
     io.smallrye.mutiny.Multi<context.ContextOuterClass.ContextEvent> getContextEvents(context.ContextOuterClass.Empty request);
     
@@ -69,6 +79,8 @@ public interface ContextService extends MutinyService {
     
     io.smallrye.mutiny.Multi<context.ContextOuterClass.ServiceEvent> getServiceEvents(context.ContextOuterClass.Empty request);
     
+    io.smallrye.mutiny.Multi<context.ContextOuterClass.ConnectionEvent> getConnectionEvents(context.ContextOuterClass.Empty request);
+    
     
 
 }
\ No newline at end of file
diff --git a/src/automation/target/generated-sources/grpc/context/ContextServiceBean.java b/src/automation/target/generated-sources/grpc/context/ContextServiceBean.java
index fbecd5d2f3cdf04e9f47693a4aeea37753855cdf..6900cf3c8a09ad6e3df1aaf2cc403c32490d3289 100644
--- a/src/automation/target/generated-sources/grpc/context/ContextServiceBean.java
+++ b/src/automation/target/generated-sources/grpc/context/ContextServiceBean.java
@@ -215,6 +215,46 @@ public class ContextServiceBean extends MutinyContextServiceGrpc.ContextServiceI
           throw new io.grpc.StatusRuntimeException(io.grpc.Status.UNIMPLEMENTED);
        }
     }
+    @Override
+    public io.smallrye.mutiny.Uni<context.ContextOuterClass.ConnectionIdList> listConnectionIds(context.ContextOuterClass.ServiceId request) {
+       try {
+         return delegate.listConnectionIds(request);
+       } catch (UnsupportedOperationException e) {
+          throw new io.grpc.StatusRuntimeException(io.grpc.Status.UNIMPLEMENTED);
+       }
+    }
+    @Override
+    public io.smallrye.mutiny.Uni<context.ContextOuterClass.ConnectionList> listConnections(context.ContextOuterClass.ServiceId request) {
+       try {
+         return delegate.listConnections(request);
+       } catch (UnsupportedOperationException e) {
+          throw new io.grpc.StatusRuntimeException(io.grpc.Status.UNIMPLEMENTED);
+       }
+    }
+    @Override
+    public io.smallrye.mutiny.Uni<context.ContextOuterClass.Connection> getConnection(context.ContextOuterClass.ConnectionId request) {
+       try {
+         return delegate.getConnection(request);
+       } catch (UnsupportedOperationException e) {
+          throw new io.grpc.StatusRuntimeException(io.grpc.Status.UNIMPLEMENTED);
+       }
+    }
+    @Override
+    public io.smallrye.mutiny.Uni<context.ContextOuterClass.ConnectionId> setConnection(context.ContextOuterClass.Connection request) {
+       try {
+         return delegate.setConnection(request);
+       } catch (UnsupportedOperationException e) {
+          throw new io.grpc.StatusRuntimeException(io.grpc.Status.UNIMPLEMENTED);
+       }
+    }
+    @Override
+    public io.smallrye.mutiny.Uni<context.ContextOuterClass.Empty> removeConnection(context.ContextOuterClass.ConnectionId request) {
+       try {
+         return delegate.removeConnection(request);
+       } catch (UnsupportedOperationException e) {
+          throw new io.grpc.StatusRuntimeException(io.grpc.Status.UNIMPLEMENTED);
+       }
+    }
 
     @Override
     public io.smallrye.mutiny.Multi<context.ContextOuterClass.ContextEvent> getContextEvents(context.ContextOuterClass.Empty request) {
@@ -261,4 +301,13 @@ public class ContextServiceBean extends MutinyContextServiceGrpc.ContextServiceI
        }
     }
 
+    @Override
+    public io.smallrye.mutiny.Multi<context.ContextOuterClass.ConnectionEvent> getConnectionEvents(context.ContextOuterClass.Empty request) {
+       try {
+         return delegate.getConnectionEvents(request);
+       } catch (UnsupportedOperationException e) {
+          throw new io.grpc.StatusRuntimeException(io.grpc.Status.UNIMPLEMENTED);
+       }
+    }
+
 }
\ No newline at end of file
diff --git a/src/automation/target/generated-sources/grpc/context/ContextServiceClient.java b/src/automation/target/generated-sources/grpc/context/ContextServiceClient.java
index 6e815b940e668e33bf936d7d9de0930d086ff9a3..a3d74cb7dfce2b3ffd259570a6dc65699f85f0cc 100644
--- a/src/automation/target/generated-sources/grpc/context/ContextServiceClient.java
+++ b/src/automation/target/generated-sources/grpc/context/ContextServiceClient.java
@@ -120,6 +120,26 @@ public class ContextServiceClient implements ContextService, MutinyClient<Mutiny
     public io.smallrye.mutiny.Uni<context.ContextOuterClass.Empty> removeService(context.ContextOuterClass.ServiceId request) {
        return stub.removeService(request);
     }
+    @Override
+    public io.smallrye.mutiny.Uni<context.ContextOuterClass.ConnectionIdList> listConnectionIds(context.ContextOuterClass.ServiceId request) {
+       return stub.listConnectionIds(request);
+    }
+    @Override
+    public io.smallrye.mutiny.Uni<context.ContextOuterClass.ConnectionList> listConnections(context.ContextOuterClass.ServiceId request) {
+       return stub.listConnections(request);
+    }
+    @Override
+    public io.smallrye.mutiny.Uni<context.ContextOuterClass.Connection> getConnection(context.ContextOuterClass.ConnectionId request) {
+       return stub.getConnection(request);
+    }
+    @Override
+    public io.smallrye.mutiny.Uni<context.ContextOuterClass.ConnectionId> setConnection(context.ContextOuterClass.Connection request) {
+       return stub.setConnection(request);
+    }
+    @Override
+    public io.smallrye.mutiny.Uni<context.ContextOuterClass.Empty> removeConnection(context.ContextOuterClass.ConnectionId request) {
+       return stub.removeConnection(request);
+    }
 
     @Override
     public io.smallrye.mutiny.Multi<context.ContextOuterClass.ContextEvent> getContextEvents(context.ContextOuterClass.Empty request) {
@@ -146,4 +166,9 @@ public class ContextServiceClient implements ContextService, MutinyClient<Mutiny
        return stub.getServiceEvents(request);
     }
 
+    @Override
+    public io.smallrye.mutiny.Multi<context.ContextOuterClass.ConnectionEvent> getConnectionEvents(context.ContextOuterClass.Empty request) {
+       return stub.getConnectionEvents(request);
+    }
+
 }
\ No newline at end of file
diff --git a/src/automation/target/generated-sources/grpc/context/ContextServiceGrpc.java b/src/automation/target/generated-sources/grpc/context/ContextServiceGrpc.java
index 03f2a66f6f09ed54eb1c359a356ba5240b184166..be9f381ffee96febd202beb540bce862c1347378 100644
--- a/src/automation/target/generated-sources/grpc/context/ContextServiceGrpc.java
+++ b/src/automation/target/generated-sources/grpc/context/ContextServiceGrpc.java
@@ -944,6 +944,192 @@ public final class ContextServiceGrpc {
     return getGetServiceEventsMethod;
   }
 
+  private static volatile io.grpc.MethodDescriptor<context.ContextOuterClass.ServiceId,
+      context.ContextOuterClass.ConnectionIdList> getListConnectionIdsMethod;
+
+  @io.grpc.stub.annotations.RpcMethod(
+      fullMethodName = SERVICE_NAME + '/' + "ListConnectionIds",
+      requestType = context.ContextOuterClass.ServiceId.class,
+      responseType = context.ContextOuterClass.ConnectionIdList.class,
+      methodType = io.grpc.MethodDescriptor.MethodType.UNARY)
+  public static io.grpc.MethodDescriptor<context.ContextOuterClass.ServiceId,
+      context.ContextOuterClass.ConnectionIdList> getListConnectionIdsMethod() {
+    io.grpc.MethodDescriptor<context.ContextOuterClass.ServiceId, context.ContextOuterClass.ConnectionIdList> getListConnectionIdsMethod;
+    if ((getListConnectionIdsMethod = ContextServiceGrpc.getListConnectionIdsMethod) == null) {
+      synchronized (ContextServiceGrpc.class) {
+        if ((getListConnectionIdsMethod = ContextServiceGrpc.getListConnectionIdsMethod) == null) {
+          ContextServiceGrpc.getListConnectionIdsMethod = getListConnectionIdsMethod =
+              io.grpc.MethodDescriptor.<context.ContextOuterClass.ServiceId, context.ContextOuterClass.ConnectionIdList>newBuilder()
+              .setType(io.grpc.MethodDescriptor.MethodType.UNARY)
+              .setFullMethodName(generateFullMethodName(SERVICE_NAME, "ListConnectionIds"))
+              .setSampledToLocalTracing(true)
+              .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
+                  context.ContextOuterClass.ServiceId.getDefaultInstance()))
+              .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
+                  context.ContextOuterClass.ConnectionIdList.getDefaultInstance()))
+              .setSchemaDescriptor(new ContextServiceMethodDescriptorSupplier("ListConnectionIds"))
+              .build();
+        }
+      }
+    }
+    return getListConnectionIdsMethod;
+  }
+
+  private static volatile io.grpc.MethodDescriptor<context.ContextOuterClass.ServiceId,
+      context.ContextOuterClass.ConnectionList> getListConnectionsMethod;
+
+  @io.grpc.stub.annotations.RpcMethod(
+      fullMethodName = SERVICE_NAME + '/' + "ListConnections",
+      requestType = context.ContextOuterClass.ServiceId.class,
+      responseType = context.ContextOuterClass.ConnectionList.class,
+      methodType = io.grpc.MethodDescriptor.MethodType.UNARY)
+  public static io.grpc.MethodDescriptor<context.ContextOuterClass.ServiceId,
+      context.ContextOuterClass.ConnectionList> getListConnectionsMethod() {
+    io.grpc.MethodDescriptor<context.ContextOuterClass.ServiceId, context.ContextOuterClass.ConnectionList> getListConnectionsMethod;
+    if ((getListConnectionsMethod = ContextServiceGrpc.getListConnectionsMethod) == null) {
+      synchronized (ContextServiceGrpc.class) {
+        if ((getListConnectionsMethod = ContextServiceGrpc.getListConnectionsMethod) == null) {
+          ContextServiceGrpc.getListConnectionsMethod = getListConnectionsMethod =
+              io.grpc.MethodDescriptor.<context.ContextOuterClass.ServiceId, context.ContextOuterClass.ConnectionList>newBuilder()
+              .setType(io.grpc.MethodDescriptor.MethodType.UNARY)
+              .setFullMethodName(generateFullMethodName(SERVICE_NAME, "ListConnections"))
+              .setSampledToLocalTracing(true)
+              .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
+                  context.ContextOuterClass.ServiceId.getDefaultInstance()))
+              .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
+                  context.ContextOuterClass.ConnectionList.getDefaultInstance()))
+              .setSchemaDescriptor(new ContextServiceMethodDescriptorSupplier("ListConnections"))
+              .build();
+        }
+      }
+    }
+    return getListConnectionsMethod;
+  }
+
+  private static volatile io.grpc.MethodDescriptor<context.ContextOuterClass.ConnectionId,
+      context.ContextOuterClass.Connection> getGetConnectionMethod;
+
+  @io.grpc.stub.annotations.RpcMethod(
+      fullMethodName = SERVICE_NAME + '/' + "GetConnection",
+      requestType = context.ContextOuterClass.ConnectionId.class,
+      responseType = context.ContextOuterClass.Connection.class,
+      methodType = io.grpc.MethodDescriptor.MethodType.UNARY)
+  public static io.grpc.MethodDescriptor<context.ContextOuterClass.ConnectionId,
+      context.ContextOuterClass.Connection> getGetConnectionMethod() {
+    io.grpc.MethodDescriptor<context.ContextOuterClass.ConnectionId, context.ContextOuterClass.Connection> getGetConnectionMethod;
+    if ((getGetConnectionMethod = ContextServiceGrpc.getGetConnectionMethod) == null) {
+      synchronized (ContextServiceGrpc.class) {
+        if ((getGetConnectionMethod = ContextServiceGrpc.getGetConnectionMethod) == null) {
+          ContextServiceGrpc.getGetConnectionMethod = getGetConnectionMethod =
+              io.grpc.MethodDescriptor.<context.ContextOuterClass.ConnectionId, context.ContextOuterClass.Connection>newBuilder()
+              .setType(io.grpc.MethodDescriptor.MethodType.UNARY)
+              .setFullMethodName(generateFullMethodName(SERVICE_NAME, "GetConnection"))
+              .setSampledToLocalTracing(true)
+              .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
+                  context.ContextOuterClass.ConnectionId.getDefaultInstance()))
+              .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
+                  context.ContextOuterClass.Connection.getDefaultInstance()))
+              .setSchemaDescriptor(new ContextServiceMethodDescriptorSupplier("GetConnection"))
+              .build();
+        }
+      }
+    }
+    return getGetConnectionMethod;
+  }
+
+  private static volatile io.grpc.MethodDescriptor<context.ContextOuterClass.Connection,
+      context.ContextOuterClass.ConnectionId> getSetConnectionMethod;
+
+  @io.grpc.stub.annotations.RpcMethod(
+      fullMethodName = SERVICE_NAME + '/' + "SetConnection",
+      requestType = context.ContextOuterClass.Connection.class,
+      responseType = context.ContextOuterClass.ConnectionId.class,
+      methodType = io.grpc.MethodDescriptor.MethodType.UNARY)
+  public static io.grpc.MethodDescriptor<context.ContextOuterClass.Connection,
+      context.ContextOuterClass.ConnectionId> getSetConnectionMethod() {
+    io.grpc.MethodDescriptor<context.ContextOuterClass.Connection, context.ContextOuterClass.ConnectionId> getSetConnectionMethod;
+    if ((getSetConnectionMethod = ContextServiceGrpc.getSetConnectionMethod) == null) {
+      synchronized (ContextServiceGrpc.class) {
+        if ((getSetConnectionMethod = ContextServiceGrpc.getSetConnectionMethod) == null) {
+          ContextServiceGrpc.getSetConnectionMethod = getSetConnectionMethod =
+              io.grpc.MethodDescriptor.<context.ContextOuterClass.Connection, context.ContextOuterClass.ConnectionId>newBuilder()
+              .setType(io.grpc.MethodDescriptor.MethodType.UNARY)
+              .setFullMethodName(generateFullMethodName(SERVICE_NAME, "SetConnection"))
+              .setSampledToLocalTracing(true)
+              .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
+                  context.ContextOuterClass.Connection.getDefaultInstance()))
+              .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
+                  context.ContextOuterClass.ConnectionId.getDefaultInstance()))
+              .setSchemaDescriptor(new ContextServiceMethodDescriptorSupplier("SetConnection"))
+              .build();
+        }
+      }
+    }
+    return getSetConnectionMethod;
+  }
+
+  private static volatile io.grpc.MethodDescriptor<context.ContextOuterClass.ConnectionId,
+      context.ContextOuterClass.Empty> getRemoveConnectionMethod;
+
+  @io.grpc.stub.annotations.RpcMethod(
+      fullMethodName = SERVICE_NAME + '/' + "RemoveConnection",
+      requestType = context.ContextOuterClass.ConnectionId.class,
+      responseType = context.ContextOuterClass.Empty.class,
+      methodType = io.grpc.MethodDescriptor.MethodType.UNARY)
+  public static io.grpc.MethodDescriptor<context.ContextOuterClass.ConnectionId,
+      context.ContextOuterClass.Empty> getRemoveConnectionMethod() {
+    io.grpc.MethodDescriptor<context.ContextOuterClass.ConnectionId, context.ContextOuterClass.Empty> getRemoveConnectionMethod;
+    if ((getRemoveConnectionMethod = ContextServiceGrpc.getRemoveConnectionMethod) == null) {
+      synchronized (ContextServiceGrpc.class) {
+        if ((getRemoveConnectionMethod = ContextServiceGrpc.getRemoveConnectionMethod) == null) {
+          ContextServiceGrpc.getRemoveConnectionMethod = getRemoveConnectionMethod =
+              io.grpc.MethodDescriptor.<context.ContextOuterClass.ConnectionId, context.ContextOuterClass.Empty>newBuilder()
+              .setType(io.grpc.MethodDescriptor.MethodType.UNARY)
+              .setFullMethodName(generateFullMethodName(SERVICE_NAME, "RemoveConnection"))
+              .setSampledToLocalTracing(true)
+              .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
+                  context.ContextOuterClass.ConnectionId.getDefaultInstance()))
+              .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
+                  context.ContextOuterClass.Empty.getDefaultInstance()))
+              .setSchemaDescriptor(new ContextServiceMethodDescriptorSupplier("RemoveConnection"))
+              .build();
+        }
+      }
+    }
+    return getRemoveConnectionMethod;
+  }
+
+  private static volatile io.grpc.MethodDescriptor<context.ContextOuterClass.Empty,
+      context.ContextOuterClass.ConnectionEvent> getGetConnectionEventsMethod;
+
+  @io.grpc.stub.annotations.RpcMethod(
+      fullMethodName = SERVICE_NAME + '/' + "GetConnectionEvents",
+      requestType = context.ContextOuterClass.Empty.class,
+      responseType = context.ContextOuterClass.ConnectionEvent.class,
+      methodType = io.grpc.MethodDescriptor.MethodType.SERVER_STREAMING)
+  public static io.grpc.MethodDescriptor<context.ContextOuterClass.Empty,
+      context.ContextOuterClass.ConnectionEvent> getGetConnectionEventsMethod() {
+    io.grpc.MethodDescriptor<context.ContextOuterClass.Empty, context.ContextOuterClass.ConnectionEvent> getGetConnectionEventsMethod;
+    if ((getGetConnectionEventsMethod = ContextServiceGrpc.getGetConnectionEventsMethod) == null) {
+      synchronized (ContextServiceGrpc.class) {
+        if ((getGetConnectionEventsMethod = ContextServiceGrpc.getGetConnectionEventsMethod) == null) {
+          ContextServiceGrpc.getGetConnectionEventsMethod = getGetConnectionEventsMethod =
+              io.grpc.MethodDescriptor.<context.ContextOuterClass.Empty, context.ContextOuterClass.ConnectionEvent>newBuilder()
+              .setType(io.grpc.MethodDescriptor.MethodType.SERVER_STREAMING)
+              .setFullMethodName(generateFullMethodName(SERVICE_NAME, "GetConnectionEvents"))
+              .setSampledToLocalTracing(true)
+              .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
+                  context.ContextOuterClass.Empty.getDefaultInstance()))
+              .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
+                  context.ContextOuterClass.ConnectionEvent.getDefaultInstance()))
+              .setSchemaDescriptor(new ContextServiceMethodDescriptorSupplier("GetConnectionEvents"))
+              .build();
+        }
+      }
+    }
+    return getGetConnectionEventsMethod;
+  }
+
   /**
    * Creates a new async stub that supports all call types for the service
    */
@@ -1202,6 +1388,48 @@ public final class ContextServiceGrpc {
       io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getGetServiceEventsMethod(), responseObserver);
     }
 
+    /**
+     */
+    public void listConnectionIds(context.ContextOuterClass.ServiceId request,
+        io.grpc.stub.StreamObserver<context.ContextOuterClass.ConnectionIdList> responseObserver) {
+      io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getListConnectionIdsMethod(), responseObserver);
+    }
+
+    /**
+     */
+    public void listConnections(context.ContextOuterClass.ServiceId request,
+        io.grpc.stub.StreamObserver<context.ContextOuterClass.ConnectionList> responseObserver) {
+      io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getListConnectionsMethod(), responseObserver);
+    }
+
+    /**
+     */
+    public void getConnection(context.ContextOuterClass.ConnectionId request,
+        io.grpc.stub.StreamObserver<context.ContextOuterClass.Connection> responseObserver) {
+      io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getGetConnectionMethod(), responseObserver);
+    }
+
+    /**
+     */
+    public void setConnection(context.ContextOuterClass.Connection request,
+        io.grpc.stub.StreamObserver<context.ContextOuterClass.ConnectionId> responseObserver) {
+      io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getSetConnectionMethod(), responseObserver);
+    }
+
+    /**
+     */
+    public void removeConnection(context.ContextOuterClass.ConnectionId request,
+        io.grpc.stub.StreamObserver<context.ContextOuterClass.Empty> responseObserver) {
+      io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getRemoveConnectionMethod(), responseObserver);
+    }
+
+    /**
+     */
+    public void getConnectionEvents(context.ContextOuterClass.Empty request,
+        io.grpc.stub.StreamObserver<context.ContextOuterClass.ConnectionEvent> responseObserver) {
+      io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getGetConnectionEventsMethod(), responseObserver);
+    }
+
     @java.lang.Override public final io.grpc.ServerServiceDefinition bindService() {
       return io.grpc.ServerServiceDefinition.builder(getServiceDescriptor())
           .addMethod(
@@ -1414,6 +1642,48 @@ public final class ContextServiceGrpc {
                 context.ContextOuterClass.Empty,
                 context.ContextOuterClass.ServiceEvent>(
                   this, METHODID_GET_SERVICE_EVENTS)))
+          .addMethod(
+            getListConnectionIdsMethod(),
+            io.grpc.stub.ServerCalls.asyncUnaryCall(
+              new MethodHandlers<
+                context.ContextOuterClass.ServiceId,
+                context.ContextOuterClass.ConnectionIdList>(
+                  this, METHODID_LIST_CONNECTION_IDS)))
+          .addMethod(
+            getListConnectionsMethod(),
+            io.grpc.stub.ServerCalls.asyncUnaryCall(
+              new MethodHandlers<
+                context.ContextOuterClass.ServiceId,
+                context.ContextOuterClass.ConnectionList>(
+                  this, METHODID_LIST_CONNECTIONS)))
+          .addMethod(
+            getGetConnectionMethod(),
+            io.grpc.stub.ServerCalls.asyncUnaryCall(
+              new MethodHandlers<
+                context.ContextOuterClass.ConnectionId,
+                context.ContextOuterClass.Connection>(
+                  this, METHODID_GET_CONNECTION)))
+          .addMethod(
+            getSetConnectionMethod(),
+            io.grpc.stub.ServerCalls.asyncUnaryCall(
+              new MethodHandlers<
+                context.ContextOuterClass.Connection,
+                context.ContextOuterClass.ConnectionId>(
+                  this, METHODID_SET_CONNECTION)))
+          .addMethod(
+            getRemoveConnectionMethod(),
+            io.grpc.stub.ServerCalls.asyncUnaryCall(
+              new MethodHandlers<
+                context.ContextOuterClass.ConnectionId,
+                context.ContextOuterClass.Empty>(
+                  this, METHODID_REMOVE_CONNECTION)))
+          .addMethod(
+            getGetConnectionEventsMethod(),
+            io.grpc.stub.ServerCalls.asyncServerStreamingCall(
+              new MethodHandlers<
+                context.ContextOuterClass.Empty,
+                context.ContextOuterClass.ConnectionEvent>(
+                  this, METHODID_GET_CONNECTION_EVENTS)))
           .build();
     }
   }
@@ -1671,6 +1941,54 @@ public final class ContextServiceGrpc {
       io.grpc.stub.ClientCalls.asyncServerStreamingCall(
           getChannel().newCall(getGetServiceEventsMethod(), getCallOptions()), request, responseObserver);
     }
+
+    /**
+     */
+    public void listConnectionIds(context.ContextOuterClass.ServiceId request,
+        io.grpc.stub.StreamObserver<context.ContextOuterClass.ConnectionIdList> responseObserver) {
+      io.grpc.stub.ClientCalls.asyncUnaryCall(
+          getChannel().newCall(getListConnectionIdsMethod(), getCallOptions()), request, responseObserver);
+    }
+
+    /**
+     */
+    public void listConnections(context.ContextOuterClass.ServiceId request,
+        io.grpc.stub.StreamObserver<context.ContextOuterClass.ConnectionList> responseObserver) {
+      io.grpc.stub.ClientCalls.asyncUnaryCall(
+          getChannel().newCall(getListConnectionsMethod(), getCallOptions()), request, responseObserver);
+    }
+
+    /**
+     */
+    public void getConnection(context.ContextOuterClass.ConnectionId request,
+        io.grpc.stub.StreamObserver<context.ContextOuterClass.Connection> responseObserver) {
+      io.grpc.stub.ClientCalls.asyncUnaryCall(
+          getChannel().newCall(getGetConnectionMethod(), getCallOptions()), request, responseObserver);
+    }
+
+    /**
+     */
+    public void setConnection(context.ContextOuterClass.Connection request,
+        io.grpc.stub.StreamObserver<context.ContextOuterClass.ConnectionId> responseObserver) {
+      io.grpc.stub.ClientCalls.asyncUnaryCall(
+          getChannel().newCall(getSetConnectionMethod(), getCallOptions()), request, responseObserver);
+    }
+
+    /**
+     */
+    public void removeConnection(context.ContextOuterClass.ConnectionId request,
+        io.grpc.stub.StreamObserver<context.ContextOuterClass.Empty> responseObserver) {
+      io.grpc.stub.ClientCalls.asyncUnaryCall(
+          getChannel().newCall(getRemoveConnectionMethod(), getCallOptions()), request, responseObserver);
+    }
+
+    /**
+     */
+    public void getConnectionEvents(context.ContextOuterClass.Empty request,
+        io.grpc.stub.StreamObserver<context.ContextOuterClass.ConnectionEvent> responseObserver) {
+      io.grpc.stub.ClientCalls.asyncServerStreamingCall(
+          getChannel().newCall(getGetConnectionEventsMethod(), getCallOptions()), request, responseObserver);
+    }
   }
 
   /**
@@ -1901,6 +2219,49 @@ public final class ContextServiceGrpc {
       return io.grpc.stub.ClientCalls.blockingServerStreamingCall(
           getChannel(), getGetServiceEventsMethod(), getCallOptions(), request);
     }
+
+    /**
+     */
+    public context.ContextOuterClass.ConnectionIdList listConnectionIds(context.ContextOuterClass.ServiceId request) {
+      return io.grpc.stub.ClientCalls.blockingUnaryCall(
+          getChannel(), getListConnectionIdsMethod(), getCallOptions(), request);
+    }
+
+    /**
+     */
+    public context.ContextOuterClass.ConnectionList listConnections(context.ContextOuterClass.ServiceId request) {
+      return io.grpc.stub.ClientCalls.blockingUnaryCall(
+          getChannel(), getListConnectionsMethod(), getCallOptions(), request);
+    }
+
+    /**
+     */
+    public context.ContextOuterClass.Connection getConnection(context.ContextOuterClass.ConnectionId request) {
+      return io.grpc.stub.ClientCalls.blockingUnaryCall(
+          getChannel(), getGetConnectionMethod(), getCallOptions(), request);
+    }
+
+    /**
+     */
+    public context.ContextOuterClass.ConnectionId setConnection(context.ContextOuterClass.Connection request) {
+      return io.grpc.stub.ClientCalls.blockingUnaryCall(
+          getChannel(), getSetConnectionMethod(), getCallOptions(), request);
+    }
+
+    /**
+     */
+    public context.ContextOuterClass.Empty removeConnection(context.ContextOuterClass.ConnectionId request) {
+      return io.grpc.stub.ClientCalls.blockingUnaryCall(
+          getChannel(), getRemoveConnectionMethod(), getCallOptions(), request);
+    }
+
+    /**
+     */
+    public java.util.Iterator<context.ContextOuterClass.ConnectionEvent> getConnectionEvents(
+        context.ContextOuterClass.Empty request) {
+      return io.grpc.stub.ClientCalls.blockingServerStreamingCall(
+          getChannel(), getGetConnectionEventsMethod(), getCallOptions(), request);
+    }
   }
 
   /**
@@ -2116,6 +2477,46 @@ public final class ContextServiceGrpc {
       return io.grpc.stub.ClientCalls.futureUnaryCall(
           getChannel().newCall(getRemoveServiceMethod(), getCallOptions()), request);
     }
+
+    /**
+     */
+    public com.google.common.util.concurrent.ListenableFuture<context.ContextOuterClass.ConnectionIdList> listConnectionIds(
+        context.ContextOuterClass.ServiceId request) {
+      return io.grpc.stub.ClientCalls.futureUnaryCall(
+          getChannel().newCall(getListConnectionIdsMethod(), getCallOptions()), request);
+    }
+
+    /**
+     */
+    public com.google.common.util.concurrent.ListenableFuture<context.ContextOuterClass.ConnectionList> listConnections(
+        context.ContextOuterClass.ServiceId request) {
+      return io.grpc.stub.ClientCalls.futureUnaryCall(
+          getChannel().newCall(getListConnectionsMethod(), getCallOptions()), request);
+    }
+
+    /**
+     */
+    public com.google.common.util.concurrent.ListenableFuture<context.ContextOuterClass.Connection> getConnection(
+        context.ContextOuterClass.ConnectionId request) {
+      return io.grpc.stub.ClientCalls.futureUnaryCall(
+          getChannel().newCall(getGetConnectionMethod(), getCallOptions()), request);
+    }
+
+    /**
+     */
+    public com.google.common.util.concurrent.ListenableFuture<context.ContextOuterClass.ConnectionId> setConnection(
+        context.ContextOuterClass.Connection request) {
+      return io.grpc.stub.ClientCalls.futureUnaryCall(
+          getChannel().newCall(getSetConnectionMethod(), getCallOptions()), request);
+    }
+
+    /**
+     */
+    public com.google.common.util.concurrent.ListenableFuture<context.ContextOuterClass.Empty> removeConnection(
+        context.ContextOuterClass.ConnectionId request) {
+      return io.grpc.stub.ClientCalls.futureUnaryCall(
+          getChannel().newCall(getRemoveConnectionMethod(), getCallOptions()), request);
+    }
   }
 
   private static final int METHODID_LIST_CONTEXT_IDS = 0;
@@ -2148,6 +2549,12 @@ public final class ContextServiceGrpc {
   private static final int METHODID_SET_SERVICE = 27;
   private static final int METHODID_REMOVE_SERVICE = 28;
   private static final int METHODID_GET_SERVICE_EVENTS = 29;
+  private static final int METHODID_LIST_CONNECTION_IDS = 30;
+  private static final int METHODID_LIST_CONNECTIONS = 31;
+  private static final int METHODID_GET_CONNECTION = 32;
+  private static final int METHODID_SET_CONNECTION = 33;
+  private static final int METHODID_REMOVE_CONNECTION = 34;
+  private static final int METHODID_GET_CONNECTION_EVENTS = 35;
 
   private static final class MethodHandlers<Req, Resp> implements
       io.grpc.stub.ServerCalls.UnaryMethod<Req, Resp>,
@@ -2286,6 +2693,30 @@ public final class ContextServiceGrpc {
           serviceImpl.getServiceEvents((context.ContextOuterClass.Empty) request,
               (io.grpc.stub.StreamObserver<context.ContextOuterClass.ServiceEvent>) responseObserver);
           break;
+        case METHODID_LIST_CONNECTION_IDS:
+          serviceImpl.listConnectionIds((context.ContextOuterClass.ServiceId) request,
+              (io.grpc.stub.StreamObserver<context.ContextOuterClass.ConnectionIdList>) responseObserver);
+          break;
+        case METHODID_LIST_CONNECTIONS:
+          serviceImpl.listConnections((context.ContextOuterClass.ServiceId) request,
+              (io.grpc.stub.StreamObserver<context.ContextOuterClass.ConnectionList>) responseObserver);
+          break;
+        case METHODID_GET_CONNECTION:
+          serviceImpl.getConnection((context.ContextOuterClass.ConnectionId) request,
+              (io.grpc.stub.StreamObserver<context.ContextOuterClass.Connection>) responseObserver);
+          break;
+        case METHODID_SET_CONNECTION:
+          serviceImpl.setConnection((context.ContextOuterClass.Connection) request,
+              (io.grpc.stub.StreamObserver<context.ContextOuterClass.ConnectionId>) responseObserver);
+          break;
+        case METHODID_REMOVE_CONNECTION:
+          serviceImpl.removeConnection((context.ContextOuterClass.ConnectionId) request,
+              (io.grpc.stub.StreamObserver<context.ContextOuterClass.Empty>) responseObserver);
+          break;
+        case METHODID_GET_CONNECTION_EVENTS:
+          serviceImpl.getConnectionEvents((context.ContextOuterClass.Empty) request,
+              (io.grpc.stub.StreamObserver<context.ContextOuterClass.ConnectionEvent>) responseObserver);
+          break;
         default:
           throw new AssertionError();
       }
@@ -2377,6 +2808,12 @@ public final class ContextServiceGrpc {
               .addMethod(getSetServiceMethod())
               .addMethod(getRemoveServiceMethod())
               .addMethod(getGetServiceEventsMethod())
+              .addMethod(getListConnectionIdsMethod())
+              .addMethod(getListConnectionsMethod())
+              .addMethod(getGetConnectionMethod())
+              .addMethod(getSetConnectionMethod())
+              .addMethod(getRemoveConnectionMethod())
+              .addMethod(getGetConnectionEventsMethod())
               .build();
         }
       }
diff --git a/src/automation/target/generated-sources/grpc/context/MutinyContextServiceGrpc.java b/src/automation/target/generated-sources/grpc/context/MutinyContextServiceGrpc.java
index 7642fdfd5c6d203fe0e0b042ca06448477b05bad..85abba20a9f315f684f964d16db4ad0cd7e91299 100644
--- a/src/automation/target/generated-sources/grpc/context/MutinyContextServiceGrpc.java
+++ b/src/automation/target/generated-sources/grpc/context/MutinyContextServiceGrpc.java
@@ -161,6 +161,31 @@ public final class MutinyContextServiceGrpc implements io.quarkus.grpc.runtime.M
         }
 
         
+        public io.smallrye.mutiny.Uni<context.ContextOuterClass.ConnectionIdList> listConnectionIds(context.ContextOuterClass.ServiceId request) {
+            return io.quarkus.grpc.runtime.ClientCalls.oneToOne(request, delegateStub::listConnectionIds);
+        }
+
+        
+        public io.smallrye.mutiny.Uni<context.ContextOuterClass.ConnectionList> listConnections(context.ContextOuterClass.ServiceId request) {
+            return io.quarkus.grpc.runtime.ClientCalls.oneToOne(request, delegateStub::listConnections);
+        }
+
+        
+        public io.smallrye.mutiny.Uni<context.ContextOuterClass.Connection> getConnection(context.ContextOuterClass.ConnectionId request) {
+            return io.quarkus.grpc.runtime.ClientCalls.oneToOne(request, delegateStub::getConnection);
+        }
+
+        
+        public io.smallrye.mutiny.Uni<context.ContextOuterClass.ConnectionId> setConnection(context.ContextOuterClass.Connection request) {
+            return io.quarkus.grpc.runtime.ClientCalls.oneToOne(request, delegateStub::setConnection);
+        }
+
+        
+        public io.smallrye.mutiny.Uni<context.ContextOuterClass.Empty> removeConnection(context.ContextOuterClass.ConnectionId request) {
+            return io.quarkus.grpc.runtime.ClientCalls.oneToOne(request, delegateStub::removeConnection);
+        }
+
+        
         public io.smallrye.mutiny.Multi<context.ContextOuterClass.ContextEvent> getContextEvents(context.ContextOuterClass.Empty request) {
             return io.quarkus.grpc.runtime.ClientCalls.oneToMany(request, delegateStub::getContextEvents);
         }
@@ -185,6 +210,11 @@ public final class MutinyContextServiceGrpc implements io.quarkus.grpc.runtime.M
             return io.quarkus.grpc.runtime.ClientCalls.oneToMany(request, delegateStub::getServiceEvents);
         }
 
+        
+        public io.smallrye.mutiny.Multi<context.ContextOuterClass.ConnectionEvent> getConnectionEvents(context.ContextOuterClass.Empty request) {
+            return io.quarkus.grpc.runtime.ClientCalls.oneToMany(request, delegateStub::getConnectionEvents);
+        }
+
     }
 
     
@@ -328,6 +358,31 @@ public final class MutinyContextServiceGrpc implements io.quarkus.grpc.runtime.M
         }
 
         
+        public io.smallrye.mutiny.Uni<context.ContextOuterClass.ConnectionIdList> listConnectionIds(context.ContextOuterClass.ServiceId request) {
+            throw new io.grpc.StatusRuntimeException(io.grpc.Status.UNIMPLEMENTED);
+        }
+
+        
+        public io.smallrye.mutiny.Uni<context.ContextOuterClass.ConnectionList> listConnections(context.ContextOuterClass.ServiceId request) {
+            throw new io.grpc.StatusRuntimeException(io.grpc.Status.UNIMPLEMENTED);
+        }
+
+        
+        public io.smallrye.mutiny.Uni<context.ContextOuterClass.Connection> getConnection(context.ContextOuterClass.ConnectionId request) {
+            throw new io.grpc.StatusRuntimeException(io.grpc.Status.UNIMPLEMENTED);
+        }
+
+        
+        public io.smallrye.mutiny.Uni<context.ContextOuterClass.ConnectionId> setConnection(context.ContextOuterClass.Connection request) {
+            throw new io.grpc.StatusRuntimeException(io.grpc.Status.UNIMPLEMENTED);
+        }
+
+        
+        public io.smallrye.mutiny.Uni<context.ContextOuterClass.Empty> removeConnection(context.ContextOuterClass.ConnectionId request) {
+            throw new io.grpc.StatusRuntimeException(io.grpc.Status.UNIMPLEMENTED);
+        }
+
+        
         public io.smallrye.mutiny.Multi<context.ContextOuterClass.ContextEvent> getContextEvents(context.ContextOuterClass.Empty request) {
             throw new io.grpc.StatusRuntimeException(io.grpc.Status.UNIMPLEMENTED);
         }
@@ -352,6 +407,11 @@ public final class MutinyContextServiceGrpc implements io.quarkus.grpc.runtime.M
             throw new io.grpc.StatusRuntimeException(io.grpc.Status.UNIMPLEMENTED);
         }
 
+        
+        public io.smallrye.mutiny.Multi<context.ContextOuterClass.ConnectionEvent> getConnectionEvents(context.ContextOuterClass.Empty request) {
+            throw new io.grpc.StatusRuntimeException(io.grpc.Status.UNIMPLEMENTED);
+        }
+
         @java.lang.Override public final io.grpc.ServerServiceDefinition bindService() {
             return io.grpc.ServerServiceDefinition.builder(getServiceDescriptor())
                     .addMethod(
@@ -564,6 +624,48 @@ public final class MutinyContextServiceGrpc implements io.quarkus.grpc.runtime.M
                                             context.ContextOuterClass.Empty,
                                             context.ContextOuterClass.ServiceEvent>(
                                             this, METHODID_GET_SERVICE_EVENTS, compression)))
+                    .addMethod(
+                            context.ContextServiceGrpc.getListConnectionIdsMethod(),
+                            asyncUnaryCall(
+                                    new MethodHandlers<
+                                            context.ContextOuterClass.ServiceId,
+                                            context.ContextOuterClass.ConnectionIdList>(
+                                            this, METHODID_LIST_CONNECTION_IDS, compression)))
+                    .addMethod(
+                            context.ContextServiceGrpc.getListConnectionsMethod(),
+                            asyncUnaryCall(
+                                    new MethodHandlers<
+                                            context.ContextOuterClass.ServiceId,
+                                            context.ContextOuterClass.ConnectionList>(
+                                            this, METHODID_LIST_CONNECTIONS, compression)))
+                    .addMethod(
+                            context.ContextServiceGrpc.getGetConnectionMethod(),
+                            asyncUnaryCall(
+                                    new MethodHandlers<
+                                            context.ContextOuterClass.ConnectionId,
+                                            context.ContextOuterClass.Connection>(
+                                            this, METHODID_GET_CONNECTION, compression)))
+                    .addMethod(
+                            context.ContextServiceGrpc.getSetConnectionMethod(),
+                            asyncUnaryCall(
+                                    new MethodHandlers<
+                                            context.ContextOuterClass.Connection,
+                                            context.ContextOuterClass.ConnectionId>(
+                                            this, METHODID_SET_CONNECTION, compression)))
+                    .addMethod(
+                            context.ContextServiceGrpc.getRemoveConnectionMethod(),
+                            asyncUnaryCall(
+                                    new MethodHandlers<
+                                            context.ContextOuterClass.ConnectionId,
+                                            context.ContextOuterClass.Empty>(
+                                            this, METHODID_REMOVE_CONNECTION, compression)))
+                    .addMethod(
+                            context.ContextServiceGrpc.getGetConnectionEventsMethod(),
+                            asyncServerStreamingCall(
+                                    new MethodHandlers<
+                                            context.ContextOuterClass.Empty,
+                                            context.ContextOuterClass.ConnectionEvent>(
+                                            this, METHODID_GET_CONNECTION_EVENTS, compression)))
                     .build();
         }
     }
@@ -598,6 +700,12 @@ public final class MutinyContextServiceGrpc implements io.quarkus.grpc.runtime.M
     private static final int METHODID_SET_SERVICE = 27;
     private static final int METHODID_REMOVE_SERVICE = 28;
     private static final int METHODID_GET_SERVICE_EVENTS = 29;
+    private static final int METHODID_LIST_CONNECTION_IDS = 30;
+    private static final int METHODID_LIST_CONNECTIONS = 31;
+    private static final int METHODID_GET_CONNECTION = 32;
+    private static final int METHODID_SET_CONNECTION = 33;
+    private static final int METHODID_REMOVE_CONNECTION = 34;
+    private static final int METHODID_GET_CONNECTION_EVENTS = 35;
 
     private static final class MethodHandlers<Req, Resp> implements
             io.grpc.stub.ServerCalls.UnaryMethod<Req, Resp>,
@@ -798,6 +906,42 @@ public final class MutinyContextServiceGrpc implements io.quarkus.grpc.runtime.M
                             compression,
                             serviceImpl::getServiceEvents);
                     break;
+                case METHODID_LIST_CONNECTION_IDS:
+                    io.quarkus.grpc.runtime.ServerCalls.oneToOne((context.ContextOuterClass.ServiceId) request,
+                            (io.grpc.stub.StreamObserver<context.ContextOuterClass.ConnectionIdList>) responseObserver,
+                            compression,
+                            serviceImpl::listConnectionIds);
+                    break;
+                case METHODID_LIST_CONNECTIONS:
+                    io.quarkus.grpc.runtime.ServerCalls.oneToOne((context.ContextOuterClass.ServiceId) request,
+                            (io.grpc.stub.StreamObserver<context.ContextOuterClass.ConnectionList>) responseObserver,
+                            compression,
+                            serviceImpl::listConnections);
+                    break;
+                case METHODID_GET_CONNECTION:
+                    io.quarkus.grpc.runtime.ServerCalls.oneToOne((context.ContextOuterClass.ConnectionId) request,
+                            (io.grpc.stub.StreamObserver<context.ContextOuterClass.Connection>) responseObserver,
+                            compression,
+                            serviceImpl::getConnection);
+                    break;
+                case METHODID_SET_CONNECTION:
+                    io.quarkus.grpc.runtime.ServerCalls.oneToOne((context.ContextOuterClass.Connection) request,
+                            (io.grpc.stub.StreamObserver<context.ContextOuterClass.ConnectionId>) responseObserver,
+                            compression,
+                            serviceImpl::setConnection);
+                    break;
+                case METHODID_REMOVE_CONNECTION:
+                    io.quarkus.grpc.runtime.ServerCalls.oneToOne((context.ContextOuterClass.ConnectionId) request,
+                            (io.grpc.stub.StreamObserver<context.ContextOuterClass.Empty>) responseObserver,
+                            compression,
+                            serviceImpl::removeConnection);
+                    break;
+                case METHODID_GET_CONNECTION_EVENTS:
+                    io.quarkus.grpc.runtime.ServerCalls.oneToMany((context.ContextOuterClass.Empty) request,
+                            (io.grpc.stub.StreamObserver<context.ContextOuterClass.ConnectionEvent>) responseObserver,
+                            compression,
+                            serviceImpl::getConnectionEvents);
+                    break;
                 default:
                     throw new java.lang.AssertionError();
             }
diff --git a/src/automation/target/kubernetes/kubernetes.yml b/src/automation/target/kubernetes/kubernetes.yml
index 47b852adf6beb80c60488a298e9039cd2011469d..d8d4dc5449107b29174cfd8fecf660a28b5d558e 100644
--- a/src/automation/target/kubernetes/kubernetes.yml
+++ b/src/automation/target/kubernetes/kubernetes.yml
@@ -1,53 +1,62 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 ---
 apiVersion: v1
 kind: Service
 metadata:
   annotations:
-    app.quarkus.io/commit-id: cb66db4bc415be2e94c17f4152e9805301b202b5
-    app.quarkus.io/build-timestamp: 2022-01-05 - 10:42:14 +0000
+    app.quarkus.io/commit-id: 644f8ea4385acb720eab7317de057f9d1d8ebfe6
+    app.quarkus.io/build-timestamp: 2022-02-14 - 10:21:58 +0000
   labels:
     app.kubernetes.io/name: automationservice
-    app.kubernetes.io/version: 0.0.1
     app: automationservice
   name: automationservice
 spec:
   ports:
-    - name: grpc-server
-      port: 9999
-      targetPort: 9999
     - name: http
       port: 8080
       targetPort: 8080
+    - name: grpc-server
+      port: 9999
+      targetPort: 9999
   selector:
     app.kubernetes.io/name: automationservice
-    app.kubernetes.io/version: 0.0.1
-  type: NodePort
+  type: ClusterIP
 ---
 apiVersion: apps/v1
 kind: Deployment
 metadata:
   annotations:
-    app.quarkus.io/commit-id: cb66db4bc415be2e94c17f4152e9805301b202b5
-    app.quarkus.io/build-timestamp: 2022-01-05 - 10:42:14 +0000
+    app.quarkus.io/commit-id: 644f8ea4385acb720eab7317de057f9d1d8ebfe6
+    app.quarkus.io/build-timestamp: 2022-02-14 - 10:21:58 +0000
   labels:
     app: automationservice
-    app.kubernetes.io/version: 0.0.1
     app.kubernetes.io/name: automationservice
   name: automationservice
 spec:
   replicas: 1
   selector:
     matchLabels:
-      app.kubernetes.io/version: 0.0.1
       app.kubernetes.io/name: automationservice
   template:
     metadata:
       annotations:
-        app.quarkus.io/commit-id: cb66db4bc415be2e94c17f4152e9805301b202b5
-        app.quarkus.io/build-timestamp: 2022-01-05 - 10:42:14 +0000
+        app.quarkus.io/commit-id: 644f8ea4385acb720eab7317de057f9d1d8ebfe6
+        app.quarkus.io/build-timestamp: 2022-02-14 - 10:21:58 +0000
       labels:
         app: automationservice
-        app.kubernetes.io/version: 0.0.1
         app.kubernetes.io/name: automationservice
     spec:
       containers:
@@ -57,10 +66,10 @@ spec:
                 fieldRef:
                   fieldPath: metadata.namespace
             - name: DEVICE_SERVICE_HOST
-              value: DeviceService
+              value: deviceservice
             - name: CONTEXT_SERVICE_HOST
-              value: ContextService
-          image: registry.gitlab.com/teraflow-h2020/controller/automation:0.0.1
+              value: contextservice
+          image: registry.gitlab.com/teraflow-h2020/controller/automation:0.2.0
           imagePullPolicy: Always
           livenessProbe:
             failureThreshold: 3
@@ -68,25 +77,25 @@ spec:
               path: /q/health/live
               port: 8080
               scheme: HTTP
-            initialDelaySeconds: 5
-            periodSeconds: 45
+            initialDelaySeconds: 2
+            periodSeconds: 10
             successThreshold: 1
             timeoutSeconds: 10
           name: automationservice
           ports:
-            - containerPort: 9999
-              name: grpc-server
-              protocol: TCP
             - containerPort: 8080
               name: http
               protocol: TCP
+            - containerPort: 9999
+              name: grpc-server
+              protocol: TCP
           readinessProbe:
             failureThreshold: 3
             httpGet:
               path: /q/health/ready
               port: 8080
               scheme: HTTP
-            initialDelaySeconds: 5
-            periodSeconds: 45
+            initialDelaySeconds: 2
+            periodSeconds: 10
             successThreshold: 1
             timeoutSeconds: 10
diff --git a/src/automation/util/set_version.sh b/src/automation/util/set_version.sh
new file mode 100755
index 0000000000000000000000000000000000000000..9022286a9359b908b9fa3764a21df080ec08ffb2
--- /dev/null
+++ b/src/automation/util/set_version.sh
@@ -0,0 +1,31 @@
+#!/usr/bin/env bash
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -eu
+
+if (( $# != 1 )); then
+  echo "Usage: set_version.sh <version>" >&2
+  exit 1
+fi
+
+version="$1"
+
+if [ "$(git status --untracked-files=no --porcelain)" ]; then
+    printf "Uncommitted changes in tracked files.\nPlease commit first and then run the script!\n"
+    exit 0;
+fi
+
+./mvnw versions:set versions:commit -DnewVersion="${version}"
+git commit -am "release(automation): ${version}"
diff --git a/src/build.sh b/src/build.sh
index 19f4e6ed2ef9fe597cb8ba7fd46a7f1b305a6546..297ab196faad327eba279d3acc7065832edfbdd1 100755
--- a/src/build.sh
+++ b/src/build.sh
@@ -1,4 +1,18 @@
 #!/usr/bin/env bash
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 
 # Make folder containing the script the root folder for its execution
 cd $(dirname $0)
diff --git a/src/clean.sh b/src/clean.sh
index 9c96b6e12be0818bdc8a5d21ec7d37c5b1356115..fa0dce2b24a333ef2c10b792d856c590c2d84161 100755
--- a/src/clean.sh
+++ b/src/clean.sh
@@ -1,4 +1,18 @@
 #!/usr/bin/env bash
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 
 docker rm --force monitoring
 docker network remove teraflowbridge
diff --git a/src/common/Constants.py b/src/common/Constants.py
index 2595a1606631fcea4152d43fdf32e7db0dbb560a..9d5edc235f582f7df4a274b15bf646413d14b1bd 100644
--- a/src/common/Constants.py
+++ b/src/common/Constants.py
@@ -1,2 +1,16 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 DEFAULT_CONTEXT_UUID = 'admin'
 DEFAULT_TOPOLOGY_UUID = 'admin'
diff --git a/src/common/DeviceTypes.py b/src/common/DeviceTypes.py
new file mode 100644
index 0000000000000000000000000000000000000000..44f8e3981157d2b77e5450c34efa54ab66a39362
--- /dev/null
+++ b/src/common/DeviceTypes.py
@@ -0,0 +1,25 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from enum import Enum
+
+class DeviceTypeEnum(Enum):
+    EMULATED_OPTICAL_LINE_SYSTEM = 'emu-optical-line-system'
+    EMULATED_PACKET_ROUTER       = 'emu-packet-router'
+    OPTICAL_ROADM                = 'optical-roadm'
+    OPTICAL_TRANDPONDER          = 'optical-trandponder'
+    OPTICAL_LINE_SYSTEM          = 'optical-line-system'
+    PACKET_ROUTER                = 'packet-router'
+    PACKET_SWITCH                = 'packet-switch'
+    P4_SWITCH                    = 'p4-switch'
diff --git a/src/common/Settings.py b/src/common/Settings.py
index 73508da50cf1ccad34e82bbea0b4b42c57c9eda9..f6bc214bf7500227c621b197e2626e09f2dafb93 100644
--- a/src/common/Settings.py
+++ b/src/common/Settings.py
@@ -1,4 +1,34 @@
-import os
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging, os, time
+from typing import List
+
+LOGGER = logging.getLogger(__name__)
+
+DEFAULT_RESTART_DELAY = 5.0 # seconds
+
+def wait_for_environment_variables(
+    required_environment_variables : List[str] = [], wait_delay_seconds : float = DEFAULT_RESTART_DELAY
+):
+    if 'KUBERNETES_PORT' not in os.environ: return # We're not running in Kubernetes, nothing to wait for
+    missing_variables = set(required_environment_variables).difference(set(os.environ.keys()))
+    if len(missing_variables) == 0: return # We have all environment variables defined
+    msg = 'Variables({:s}) are missing in Environment({:s}), restarting in {:f} seconds...'
+    LOGGER.error(msg.format(str(missing_variables), str(os.environ), wait_delay_seconds))
+    time.sleep(wait_delay_seconds)
+    raise Exception('Restarting...')
 
 def get_setting(name, **kwargs):
     value = os.environ.get(name)
diff --git a/src/common/__init__.py b/src/common/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/common/__init__.py
+++ b/src/common/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/common/message_broker/Constants.py b/src/common/message_broker/Constants.py
index 671f11add119bf5103258af0c07df60e0e3afd0d..d3f5190742303b9948b6d973eb2d5030a17a89cc 100644
--- a/src/common/message_broker/Constants.py
+++ b/src/common/message_broker/Constants.py
@@ -1 +1,15 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 CONSUME_TIMEOUT = 0.1 # seconds
diff --git a/src/common/message_broker/Factory.py b/src/common/message_broker/Factory.py
index a2ea36435c717835bf4b7c89c2522878e67074c9..a64913df02805dabc9c1924ea16966e43454b7b5 100644
--- a/src/common/message_broker/Factory.py
+++ b/src/common/message_broker/Factory.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging, os
 from typing import Optional, Union
 from .backend._Backend import _Backend
diff --git a/src/common/message_broker/Message.py b/src/common/message_broker/Message.py
index ee527dc9f46855b3d806f9188a7d5640c34f416b..484c795e77cf9699057a65a85a7df73c53779523 100644
--- a/src/common/message_broker/Message.py
+++ b/src/common/message_broker/Message.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 from typing import NamedTuple
 
 class Message(NamedTuple):
diff --git a/src/common/message_broker/MessageBroker.py b/src/common/message_broker/MessageBroker.py
index 53697db2d0062a8290be44991990ee7f217e2c25..2a561727573247042b0d1755175d0730a97ef1d4 100644
--- a/src/common/message_broker/MessageBroker.py
+++ b/src/common/message_broker/MessageBroker.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging
 from typing import Iterator, Set
 from .backend._Backend import _Backend
diff --git a/src/common/message_broker/__init__.py b/src/common/message_broker/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/common/message_broker/__init__.py
+++ b/src/common/message_broker/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/common/message_broker/backend/BackendEnum.py b/src/common/message_broker/backend/BackendEnum.py
index 9ee482477e489fff25ed0538330e33d378cbd251..bf95f176479fb227503dd04a9dde2b81789ec006 100644
--- a/src/common/message_broker/backend/BackendEnum.py
+++ b/src/common/message_broker/backend/BackendEnum.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 from enum import Enum
 
 class BackendEnum(Enum):
diff --git a/src/common/message_broker/backend/_Backend.py b/src/common/message_broker/backend/_Backend.py
index a6461d2538a1794e6bdec597ac0ad611eec6d484..1e03b2bd27f4949f65d7df839bc5ba6d4da9df0d 100644
--- a/src/common/message_broker/backend/_Backend.py
+++ b/src/common/message_broker/backend/_Backend.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 from typing import Iterator, Set, Tuple
 
 class _Backend:
diff --git a/src/common/message_broker/backend/__init__.py b/src/common/message_broker/backend/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/common/message_broker/backend/__init__.py
+++ b/src/common/message_broker/backend/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/common/message_broker/backend/inmemory/InMemoryBackend.py b/src/common/message_broker/backend/inmemory/InMemoryBackend.py
index 9b0e758724deb280f9cc8a1ba3eb20e6f4c57356..c0a87c667b2bf6d48d8a482c601e48289de7bc90 100644
--- a/src/common/message_broker/backend/inmemory/InMemoryBackend.py
+++ b/src/common/message_broker/backend/inmemory/InMemoryBackend.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 # InMemeory MessageBroker Backend
 # -------------------------------
 # - WARNING: DESIGNED AND BUILT FOR UNIT TESTING AND INTEGRATION TESTING PURPOSES ONLY !!!
diff --git a/src/common/message_broker/backend/inmemory/__init__.py b/src/common/message_broker/backend/inmemory/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/common/message_broker/backend/inmemory/__init__.py
+++ b/src/common/message_broker/backend/inmemory/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/common/message_broker/backend/redis/RedisBackend.py b/src/common/message_broker/backend/redis/RedisBackend.py
index 0e8be0f30d77dcf6db5dbebcacba06591d1dbdff..41490913cbc8363ae86a031e9d7e703713349368 100644
--- a/src/common/message_broker/backend/redis/RedisBackend.py
+++ b/src/common/message_broker/backend/redis/RedisBackend.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import os, threading
 from typing import Any, Dict, Iterator, Set, Tuple
 from redis.client import Redis
diff --git a/src/common/message_broker/backend/redis/__init__.py b/src/common/message_broker/backend/redis/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/common/message_broker/backend/redis/__init__.py
+++ b/src/common/message_broker/backend/redis/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/common/message_broker/tests/__init__.py b/src/common/message_broker/tests/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/common/message_broker/tests/__init__.py
+++ b/src/common/message_broker/tests/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/common/message_broker/tests/test_unitary.py b/src/common/message_broker/tests/test_unitary.py
index c5ff5c77ff55be558858e98b262c912e7db9e3ef..925fcc83e163e9888494dcdc3cddaeaeec1d04fc 100644
--- a/src/common/message_broker/tests/test_unitary.py
+++ b/src/common/message_broker/tests/test_unitary.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging, os, pytest, threading, time
 from typing import List, Set
 from common.message_broker.Factory import get_messagebroker_backend
diff --git a/src/common/orm/Database.py b/src/common/orm/Database.py
index 81c18aec1699a6c356739b4fc7f8a5ae97edac95..e5ec7b0a93164595e2f75f9059c223044b171701 100644
--- a/src/common/orm/Database.py
+++ b/src/common/orm/Database.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging
 from typing import List, Set, Tuple
 from .backend._Backend import _Backend
diff --git a/src/common/orm/Exceptions.py b/src/common/orm/Exceptions.py
index eea0b564e1918cb6a2da0553641c9492a32b1425..cf91bd2f7a628a86fa45f7eb687b3292e00d0f8e 100644
--- a/src/common/orm/Exceptions.py
+++ b/src/common/orm/Exceptions.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 class ConstraintException(Exception):
     pass
 
diff --git a/src/common/orm/Factory.py b/src/common/orm/Factory.py
index 6ef0e11ccdd7b2f0f9e3fde62903fef522cb9f7a..15940ce7cc565c3966e3abfe2604ebd245d2adfc 100644
--- a/src/common/orm/Factory.py
+++ b/src/common/orm/Factory.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging, os
 from typing import Optional, Union
 from .backend._Backend import _Backend
diff --git a/src/common/orm/HighLevel.py b/src/common/orm/HighLevel.py
index 7e2e9e49ea22861d3d96bcac70acc86e2f50db35..a5bdeae3e9607767b5215f6ff87cb0d8624918d0 100644
--- a/src/common/orm/HighLevel.py
+++ b/src/common/orm/HighLevel.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 from typing import Any, Dict, List, Optional, Set, Tuple
 from common.rpc_method_wrapper.ServiceExceptions import NotFoundException
 from common.orm.Database import Database
diff --git a/src/common/orm/__init__.py b/src/common/orm/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/common/orm/__init__.py
+++ b/src/common/orm/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/common/orm/backend/BackendEnum.py b/src/common/orm/backend/BackendEnum.py
index 7699262f468b63044b160b9f0649432668002023..0297ca7be6c3bd13c2ee7d3cabe75efe23ead51f 100644
--- a/src/common/orm/backend/BackendEnum.py
+++ b/src/common/orm/backend/BackendEnum.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 from enum import Enum
 
 class BackendEnum(Enum):
diff --git a/src/common/orm/backend/Tools.py b/src/common/orm/backend/Tools.py
index 8152e1a768e9a824fb6ce8a30ac4a09409fc47da..30223feac8c98c4272e56de841c1abafb792c5a6 100644
--- a/src/common/orm/backend/Tools.py
+++ b/src/common/orm/backend/Tools.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 from typing import List, Union
 
 def key_to_str(key : Union[str, List[str]], separator : str = '/') -> str:
diff --git a/src/common/orm/backend/_Backend.py b/src/common/orm/backend/_Backend.py
index ead206354b12b0a39b0868c9bc853c17ac976325..08b346ea746a4cf1fda393de069c4ab63549973d 100644
--- a/src/common/orm/backend/_Backend.py
+++ b/src/common/orm/backend/_Backend.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 from typing import Any, Dict, List, Optional, Set, Tuple
 
 class _Backend:
diff --git a/src/common/orm/backend/__init__.py b/src/common/orm/backend/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/common/orm/backend/__init__.py
+++ b/src/common/orm/backend/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/common/orm/backend/inmemory/InMemoryBackend.py b/src/common/orm/backend/inmemory/InMemoryBackend.py
index c960a17094c6b8e07ef6823fcef123c092add94a..4930d59571bed319f61918f1af6ea5666e54e140 100644
--- a/src/common/orm/backend/inmemory/InMemoryBackend.py
+++ b/src/common/orm/backend/inmemory/InMemoryBackend.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 # InMemeory Database Backend
 # --------------------------
 # - Concurrency is limited to 1 operation at a time
diff --git a/src/common/orm/backend/inmemory/Tools.py b/src/common/orm/backend/inmemory/Tools.py
index fe10506556de36d6f40665c6f59119cbf540d8a4..9f8e2b5c956f953d45d2ab5a41d7874262ec675c 100644
--- a/src/common/orm/backend/inmemory/Tools.py
+++ b/src/common/orm/backend/inmemory/Tools.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 from typing import Dict, List, Set, Union
 
 def get_dict(keys : Dict[str, Union[Dict, List, Set]], str_key : str) -> Dict:
diff --git a/src/common/orm/backend/inmemory/__init__.py b/src/common/orm/backend/inmemory/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/common/orm/backend/inmemory/__init__.py
+++ b/src/common/orm/backend/inmemory/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/common/orm/backend/redis/Mutex.py b/src/common/orm/backend/redis/Mutex.py
index 55d52d8d5ff558b096958aefd85d926b46716db9..6424b7a79076bdcc42aafe46fd9d7dfe75683a4a 100644
--- a/src/common/orm/backend/redis/Mutex.py
+++ b/src/common/orm/backend/redis/Mutex.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import random, time, uuid
 from typing import Set, Tuple, Union
 from redis.client import Redis
diff --git a/src/common/orm/backend/redis/RedisBackend.py b/src/common/orm/backend/redis/RedisBackend.py
index edd73917315322803a102142c69f59dc305955a5..30225eaa29b4b7bfd69c4c277ee513192d923b7a 100644
--- a/src/common/orm/backend/redis/RedisBackend.py
+++ b/src/common/orm/backend/redis/RedisBackend.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import os, uuid
 from typing import Any, Dict, List, Optional, Set, Tuple
 from redis.client import Redis
diff --git a/src/common/orm/backend/redis/__init__.py b/src/common/orm/backend/redis/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/common/orm/backend/redis/__init__.py
+++ b/src/common/orm/backend/redis/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/common/orm/fields/BooleanField.py b/src/common/orm/fields/BooleanField.py
index 3bfbed17fb8cee7962ae86df2416c64369f9127d..ae9ac8eafa5803217dc3e7554683a737134bc4e5 100644
--- a/src/common/orm/fields/BooleanField.py
+++ b/src/common/orm/fields/BooleanField.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 from __future__ import annotations
 from typing import Union
 from common.type_checkers.Checkers import chk_boolean
diff --git a/src/common/orm/fields/EnumeratedField.py b/src/common/orm/fields/EnumeratedField.py
index 37e95bd338f736c7473055d9952eaa4ce4a334ea..f684649e10bbb985f787ae6f4cb6f4660b5cd943 100644
--- a/src/common/orm/fields/EnumeratedField.py
+++ b/src/common/orm/fields/EnumeratedField.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 from __future__ import annotations
 from enum import Enum
 from typing import Union
diff --git a/src/common/orm/fields/Field.py b/src/common/orm/fields/Field.py
index aa1a9e4352658ce3219abe24abdfb87183508eb5..68d868cf740bc00e22bc951cd8bab843d28db21d 100644
--- a/src/common/orm/fields/Field.py
+++ b/src/common/orm/fields/Field.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 from __future__ import annotations
 import logging
 from typing import TYPE_CHECKING, Any, List, Set, Tuple, Union
diff --git a/src/common/orm/fields/FloatField.py b/src/common/orm/fields/FloatField.py
index e88b10a21c3f556e5ca0501d484aa5ab36311a76..8d006e77347e3c440a31bd13f59e08267daa5e63 100644
--- a/src/common/orm/fields/FloatField.py
+++ b/src/common/orm/fields/FloatField.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 from __future__ import annotations
 from typing import Optional, Union
 from common.type_checkers.Checkers import chk_float
diff --git a/src/common/orm/fields/ForeignKeyField.py b/src/common/orm/fields/ForeignKeyField.py
index 21815d97c930128ef2f75e3105ca6309a13d524e..12e720d17952d6b09d0d4911105b156e8593f33b 100644
--- a/src/common/orm/fields/ForeignKeyField.py
+++ b/src/common/orm/fields/ForeignKeyField.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 from __future__ import annotations
 from typing import TYPE_CHECKING
 from common.type_checkers.Checkers import chk_issubclass, chk_type
diff --git a/src/common/orm/fields/IntegerField.py b/src/common/orm/fields/IntegerField.py
index 591d90b83ea24054712ee2f03ebf09039ce4816f..87ddab4646d5426c462ea96f18368e3e49be06c1 100644
--- a/src/common/orm/fields/IntegerField.py
+++ b/src/common/orm/fields/IntegerField.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 from __future__ import annotations
 from typing import Optional, Union
 from common.type_checkers.Checkers import chk_integer
diff --git a/src/common/orm/fields/PrimaryKeyField.py b/src/common/orm/fields/PrimaryKeyField.py
index 1e567f0361236194819198b726f3d32029072adc..86fdc7e2466f10d3c16cf733fedbfc15fe62f31f 100644
--- a/src/common/orm/fields/PrimaryKeyField.py
+++ b/src/common/orm/fields/PrimaryKeyField.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 from __future__ import annotations
 from typing import TYPE_CHECKING
 from .StringField import StringField
diff --git a/src/common/orm/fields/StringField.py b/src/common/orm/fields/StringField.py
index 0034c69baf2b4bbe0de16ade38244d19262cfe80..ead8487eaf60542079b2ca3745f04aba2c0d6de9 100644
--- a/src/common/orm/fields/StringField.py
+++ b/src/common/orm/fields/StringField.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 from __future__ import annotations
 import re
 from typing import Optional, Pattern, Union
diff --git a/src/common/orm/fields/__init__.py b/src/common/orm/fields/__init__.py
index b97f0f08a646c23d51b98b03bf7fe29f9c7d8bb8..2a4cccb63a28eda573b16e4cef7da3ae58ac9d54 100644
--- a/src/common/orm/fields/__init__.py
+++ b/src/common/orm/fields/__init__.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 from .BooleanField import BooleanField
 from .Field import Field
 from .FloatField import FloatField
diff --git a/src/common/orm/model/Model.py b/src/common/orm/model/Model.py
index cf66a859f16302e9e746b612791fd0dacc07b996..ffb9571142942de0a23982c990db3670a27bc670 100644
--- a/src/common/orm/model/Model.py
+++ b/src/common/orm/model/Model.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 from __future__ import annotations
 import logging, re
 from typing import Any, Dict, List, Mapping, Optional, Set, Tuple, Union
@@ -106,11 +120,13 @@ class Model(metaclass=MetaModel):
     @property
     def instance_key(self) -> str: return self._instance_key
 
-    def lock(self, extra_keys : List[List[str]] = []):
-        lock_keys = Model.get_backend_key_locks(
-            [self._instance_key, self._instances_key, self._references_key] + extra_keys)
-        acquired,self._owner_key = self._backend.lock(lock_keys, owner_key=self._owner_key)
-        if acquired: return
+    def lock(self, extra_keys : List[List[str]] = [], blocking : bool = True):
+        while True:
+            lock_keys = Model.get_backend_key_locks(
+                [self._instance_key, self._instances_key, self._references_key] + extra_keys)
+            acquired,self._owner_key = self._backend.lock(lock_keys, owner_key=self._owner_key)
+            if acquired: return
+            if not blocking: break
         raise MutexException('Unable to lock keys {:s} using owner_key {:s}'.format(
             str(lock_keys), str(self._owner_key)))
 
diff --git a/src/common/orm/model/Tools.py b/src/common/orm/model/Tools.py
index 3ebf63ae35a9e27d083a533ace3d1805197f166c..aed6a14bc2668017e312f0659ccb08c8c185ea90 100644
--- a/src/common/orm/model/Tools.py
+++ b/src/common/orm/model/Tools.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 from collections import OrderedDict
 
 class NoDupOrderedDict(OrderedDict):
diff --git a/src/common/orm/model/__init__.py b/src/common/orm/model/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/common/orm/model/__init__.py
+++ b/src/common/orm/model/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/common/orm/tests/__init__.py b/src/common/orm/tests/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/common/orm/tests/__init__.py
+++ b/src/common/orm/tests/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/common/orm/tests/test_unitary.py b/src/common/orm/tests/test_unitary.py
index b6f58b9a33d474b2574099870c7a8f6f51682b76..586a9623574a104c58b6ae4762fa13fc6ab02871 100644
--- a/src/common/orm/tests/test_unitary.py
+++ b/src/common/orm/tests/test_unitary.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging, pytest
 from enum import Enum
 from common.orm.Exceptions import ConstraintException
diff --git a/src/common/rpc_method_wrapper/Decorator.py b/src/common/rpc_method_wrapper/Decorator.py
index cfbc76ff86810740ed8cc83eff95863ffd823604..212481b7398439d7dbaa5bb76df6901544562ab5 100644
--- a/src/common/rpc_method_wrapper/Decorator.py
+++ b/src/common/rpc_method_wrapper/Decorator.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import grpc, logging
 from enum import Enum
 from typing import Dict, List
@@ -54,8 +68,10 @@ def safe_and_metered_rpc_method(metrics : Dict[str, MetricWrapperBase], logger :
                 COUNTER_COMPLETED.inc()
                 return reply
             except ServiceException as e:   # pragma: no cover (ServiceException not thrown)
-                logger.exception('{:s} exception'.format(function_name))
-                COUNTER_FAILED.inc()
+                if e.code not in [grpc.StatusCode.NOT_FOUND, grpc.StatusCode.ALREADY_EXISTS]:
+                    # Assume not found or already exists is just a condition, not an error
+                    logger.exception('{:s} exception'.format(function_name))
+                    COUNTER_FAILED.inc()
                 grpc_context.abort(e.code, e.details)
             except Exception as e:          # pragma: no cover, pylint: disable=broad-except
                 logger.exception('{:s} exception'.format(function_name))
diff --git a/src/common/rpc_method_wrapper/ServiceExceptions.py b/src/common/rpc_method_wrapper/ServiceExceptions.py
index fb3979c6c216d9361ced706b4fabd5ba5bbcffb2..f4f0a64cad79c96dc069bd37e8d2c2be5f011c53 100644
--- a/src/common/rpc_method_wrapper/ServiceExceptions.py
+++ b/src/common/rpc_method_wrapper/ServiceExceptions.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import grpc
 from typing import Iterable, Union
 
diff --git a/src/common/rpc_method_wrapper/__init__.py b/src/common/rpc_method_wrapper/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/common/rpc_method_wrapper/__init__.py
+++ b/src/common/rpc_method_wrapper/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/common/rpc_method_wrapper/tests/__init__.py b/src/common/rpc_method_wrapper/tests/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/common/rpc_method_wrapper/tests/__init__.py
+++ b/src/common/rpc_method_wrapper/tests/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/common/rpc_method_wrapper/tests/test_unitary.py b/src/common/rpc_method_wrapper/tests/test_unitary.py
index 816b9dae859017a05f27055a14cb97db6d8e034f..c8fc7a2aa187dcb905a8c230b047ffb1171d2ccd 100644
--- a/src/common/rpc_method_wrapper/tests/test_unitary.py
+++ b/src/common/rpc_method_wrapper/tests/test_unitary.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import grpc, logging, time
 from common.rpc_method_wrapper.Decorator import create_metrics, safe_and_metered_rpc_method
 
diff --git a/src/common/tests/EventTools.py b/src/common/tests/EventTools.py
new file mode 100644
index 0000000000000000000000000000000000000000..0b994fc858aafe61b6e99458e84af1e4feb36da2
--- /dev/null
+++ b/src/common/tests/EventTools.py
@@ -0,0 +1,101 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import json, logging
+from typing import Dict, List, Tuple
+from common.tools.grpc.Tools import grpc_message_to_json_string
+from context.client.EventsCollector import EventsCollector
+from context.proto.context_pb2 import (
+    ConnectionEvent, ContextEvent, DeviceEvent, EventTypeEnum, LinkEvent, ServiceEvent, TopologyEvent)
+
+LOGGER = logging.getLogger(__name__)
+
+EVENT_CREATE = EventTypeEnum.EVENTTYPE_CREATE
+EVENT_UPDATE = EventTypeEnum.EVENTTYPE_UPDATE
+EVENT_REMOVE = EventTypeEnum.EVENTTYPE_REMOVE
+
+def class_to_classname(klass): return klass.__name__
+def instance_to_classname(instance): return type(instance).__name__
+
+CLASSNAME_CONTEXT_EVENT    = class_to_classname(ContextEvent)
+CLASSNAME_TOPOLOGY_EVENT   = class_to_classname(TopologyEvent)
+CLASSNAME_DEVICE_EVENT     = class_to_classname(DeviceEvent)
+CLASSNAME_LINK_EVENT       = class_to_classname(LinkEvent)
+CLASSNAME_SERVICE_EVENT    = class_to_classname(ServiceEvent)
+CLASSNAME_CONNECTION_EVENT = class_to_classname(ConnectionEvent)
+
+EVENT_CLASS_NAME__TO__ENTITY_ID_SELECTOR = {
+    CLASSNAME_CONTEXT_EVENT   : lambda event: event.context_id,
+    CLASSNAME_TOPOLOGY_EVENT  : lambda event: event.topology_id,
+    CLASSNAME_DEVICE_EVENT    : lambda event: event.device_id,
+    CLASSNAME_LINK_EVENT      : lambda event: event.link_id,
+    CLASSNAME_SERVICE_EVENT   : lambda event: event.service_id,
+    CLASSNAME_CONNECTION_EVENT: lambda event: event.connection_id,
+}
+
+def event_to_key(event):
+    event_class_name = instance_to_classname(event)
+    entity_id_selector_function = EVENT_CLASS_NAME__TO__ENTITY_ID_SELECTOR.get(event_class_name)
+    entity_id = entity_id_selector_function(event)
+    return (event_class_name, event.event.event_type, grpc_message_to_json_string(entity_id))
+
+def check_events(
+    events_collector : EventsCollector, expected_events : List[Tuple[str, int, Dict]],
+    fail_if_missing_events : bool = True, fail_if_unexpected_events : bool = False,
+    timeout_per_event = 1.0, max_wait_time = 30.0
+) -> None:
+    expected_events_map = {}
+    num_expected_events = 0
+    for event_classname, event_type_id, event_ids in expected_events:
+        event_key = (event_classname, event_type_id, json.dumps(event_ids, sort_keys=True))
+        event_count = expected_events_map.get(event_key, 0)
+        expected_events_map[event_key] = event_count + 1
+        num_expected_events += 1
+
+    i, wait_time = 0, 0
+    while num_expected_events > 0:
+        event_received = events_collector.get_event(block=True, timeout=timeout_per_event)
+        if event_received is None:
+            wait_time += timeout_per_event
+            if wait_time > max_wait_time: break
+            continue
+        LOGGER.info('event_received[{:d}] = {:s}'.format(i, str(event_received)))
+        event_key = event_to_key(event_received)
+        event_count = expected_events_map.pop(event_key, 0)
+        if event_count > 0: num_expected_events -= 1
+        event_count -= 1
+        if event_count != 0: expected_events_map[event_key] = event_count
+
+    if len(expected_events_map) == 0:
+        LOGGER.info('EventsCheck passed')
+    else:
+        missing_events = {}
+        unexpected_events = {}
+        for event_key,event_count in expected_events_map.items():
+            if event_count > 0:
+                missing_events[event_key] = event_count
+            if event_count < 0:
+                unexpected_events[event_key] = -event_count
+        msg_except = ['EventCheck failed:']
+        msg_logger = ['EventCheck:']
+        if len(missing_events) > 0:
+            msg = 'missing_events={:s}'.format(str(missing_events))
+            if fail_if_missing_events: msg_except.append(msg)
+            msg_logger.append(msg)
+        if len(unexpected_events) > 0:
+            msg = 'unexpected_events={:s}'.format(str(unexpected_events))
+            if fail_if_unexpected_events: msg_except.append(msg)
+            msg_logger.append(msg)
+        if len(msg_logger) > 1: LOGGER.warning(' '.join(msg_logger))
+        if len(msg_except) > 1: raise Exception(' '.join(msg_except))
diff --git a/src/compute/tests/MockService.py b/src/common/tests/MockService.py
similarity index 70%
rename from src/compute/tests/MockService.py
rename to src/common/tests/MockService.py
index 54b420f5aa1cf015c90f09b874f9b37225e07328..25f36e009be3a65c1ba9c18c7707742a75253a75 100644
--- a/src/compute/tests/MockService.py
+++ b/src/common/tests/MockService.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import grpc, logging
 from concurrent import futures
 
diff --git a/src/compute/tests/MockServicerImpl_Context.py b/src/common/tests/MockServicerImpl_Context.py
similarity index 71%
rename from src/compute/tests/MockServicerImpl_Context.py
rename to src/common/tests/MockServicerImpl_Context.py
index d79a755d49773dff4b298abdba6dfa38d9e69d57..38f932f4dc9638abf3e526e6867800dd01cca154 100644
--- a/src/compute/tests/MockServicerImpl_Context.py
+++ b/src/common/tests/MockServicerImpl_Context.py
@@ -1,11 +1,26 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import grpc, logging
 from typing import Any, Dict, Iterator, List
+from common.tools.grpc.Tools import grpc_message_to_json_string
 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)
+    Connection, ConnectionEvent, ConnectionId, ConnectionIdList, ConnectionList, 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__)
 
@@ -22,7 +37,7 @@ def get_entry(
     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)))
+        context.abort(grpc.StatusCode.NOT_FOUND, 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:
@@ -37,7 +52,7 @@ def del_entry(
 ) -> 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)))
+        context.abort(grpc.StatusCode.NOT_FOUND, str('{:s}({:s}) not found'.format(container_name, entry_uuid)))
     del container[entry_uuid]
     return Empty()
 
@@ -63,7 +78,7 @@ class MockServicerImpl_Context(ContextServiceServicer):
 
     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
+        return set_entry(self.database, 'context', request.context_id.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)))
@@ -120,7 +135,7 @@ class MockServicerImpl_Context(ContextServiceServicer):
 
     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
+        return set_entry(self.database, 'device', request.device_id.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)))
@@ -146,7 +161,7 @@ class MockServicerImpl_Context(ContextServiceServicer):
 
     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
+        return set_entry(self.database, 'link', request.link_id.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)))
@@ -186,3 +201,43 @@ class MockServicerImpl_Context(ContextServiceServicer):
 
     def GetServiceEvents(self, request: Empty, context : grpc.ServicerContext) -> Iterator[ServiceEvent]:
         LOGGER.info('[GetServiceEvents] request={:s}'.format(grpc_message_to_json_string(request)))
+
+
+    # ----- Connection -------------------------------------------------------------------------------------------------
+
+    def ListConnectionIds(self, request: ServiceId, context : grpc.ServicerContext) -> ConnectionIdList:
+        LOGGER.info('[ListConnectionIds] request={:s}'.format(grpc_message_to_json_string(request)))
+        container_name = 'service_connections[{:s}/{:s}]'.format(
+            str(request.context_id.context_uuid.uuid), str(request.service_uuid.uuid))
+        return ConnectionIdList(connection_ids=[c.connection_id for c in get_entries(self.database, container_name)])
+
+    def ListConnections(self, request: ServiceId, context : grpc.ServicerContext) -> ConnectionList:
+        LOGGER.info('[ListConnections] request={:s}'.format(grpc_message_to_json_string(request)))
+        container_name = 'service_connections[{:s}/{:s}]'.format(
+            str(request.context_id.context_uuid.uuid), str(request.service_uuid.uuid))
+        return ConnectionList(connections=get_entries(self.database, container_name))
+
+    def GetConnection(self, request: ConnectionId, context : grpc.ServicerContext) -> Connection:
+        LOGGER.info('[GetConnection] request={:s}'.format(grpc_message_to_json_string(request)))
+        return get_entry(context, self.database, 'connection', request.connection_uuid.uuid)
+
+    def SetConnection(self, request: Connection, context : grpc.ServicerContext) -> ConnectionId:
+        LOGGER.info('[SetConnection] request={:s}'.format(grpc_message_to_json_string(request)))
+        service_connection__container_name = 'service_connection[{:s}/{:s}]'.format(
+            str(request.service_id.context_id.context_uuid.uuid), str(request.service_id.service_uuid.uuid))
+        set_entry(
+            self.database, service_connection__container_name, request.connection_id.connection_uuid.uuid, request)
+        return set_entry(
+            self.database, 'connection', request.connection_id.connection_uuid.uuid, request).connection_id
+
+    def RemoveConnection(self, request: ConnectionId, context : grpc.ServicerContext) -> Empty:
+        LOGGER.info('[RemoveConnection] request={:s}'.format(grpc_message_to_json_string(request)))
+        connection = get_entry(context, self.database, 'connection', request.connection_uuid.uuid)
+        service_id = connection.service_id
+        service_connection__container_name = 'service_connection[{:s}/{:s}]'.format(
+            str(service_id.context_id.context_uuid.uuid), str(service_id.service_uuid.uuid))
+        del_entry(context, self.database, service_connection__container_name, request.connection_uuid.uuid)
+        return del_entry(context, self.database, 'connection', request.connection_uuid.uuid)
+
+    def GetConnectionEvents(self, request: Empty, context : grpc.ServicerContext) -> Iterator[ConnectionEvent]:
+        LOGGER.info('[GetConnectionEvents] request={:s}'.format(grpc_message_to_json_string(request)))
diff --git a/src/common/tests/MockServicerImpl_Device.py b/src/common/tests/MockServicerImpl_Device.py
new file mode 100644
index 0000000000000000000000000000000000000000..34c9d7fdb528c5ee1ad4d8956b9b3069ac7b37f3
--- /dev/null
+++ b/src/common/tests/MockServicerImpl_Device.py
@@ -0,0 +1,51 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import grpc, logging
+from common.Settings import get_setting
+from common.tools.grpc.Tools import grpc_message_to_json_string
+from context.client.ContextClient import ContextClient
+from context.proto.context_pb2 import Device, DeviceConfig, DeviceId, Empty
+from device.proto.device_pb2 import MonitoringSettings
+from device.proto.device_pb2_grpc import DeviceServiceServicer
+
+LOGGER = logging.getLogger(__name__)
+
+class MockServicerImpl_Device(DeviceServiceServicer):
+    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 AddDevice(self, request : Device, context : grpc.ServicerContext) -> DeviceId:
+        LOGGER.info('[AddDevice] request={:s}'.format(grpc_message_to_json_string(request)))
+        return self.context_client.SetDevice(request)
+
+    def ConfigureDevice(self, request : Device, context : grpc.ServicerContext) -> DeviceId:
+        LOGGER.info('[ConfigureDevice] request={:s}'.format(grpc_message_to_json_string(request)))
+        return self.context_client.SetDevice(request)
+
+    def DeleteDevice(self, request : DeviceId, context : grpc.ServicerContext) -> Empty:
+        LOGGER.info('[DeleteDevice] request={:s}'.format(grpc_message_to_json_string(request)))
+        return self.context_client.RemoveDevice(request)
+
+    def GetInitialConfig(self, request : DeviceId, context : grpc.ServicerContext) -> DeviceConfig:
+        LOGGER.info('[GetInitialConfig] request={:s}'.format(grpc_message_to_json_string(request)))
+        return DeviceConfig()
+
+    def MonitorDeviceKpi(self, request : MonitoringSettings, context : grpc.ServicerContext) -> Empty:
+        LOGGER.info('[MonitorDeviceKpi] request={:s}'.format(grpc_message_to_json_string(request)))
+        return Empty()
diff --git a/src/compute/tests/MockServicerImpl_Service.py b/src/common/tests/MockServicerImpl_Service.py
similarity index 56%
rename from src/compute/tests/MockServicerImpl_Service.py
rename to src/common/tests/MockServicerImpl_Service.py
index 75fdc3073dac1b942c7701f3a0be9feacb60109b..849d153c39edc53afa154cb930c668836f5978a3 100644
--- a/src/compute/tests/MockServicerImpl_Service.py
+++ b/src/common/tests/MockServicerImpl_Service.py
@@ -1,9 +1,23 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import grpc, logging
 from common.Settings import get_setting
+from common.tools.grpc.Tools import grpc_message_to_json_string
 from context.client.ContextClient import ContextClient
-from service.proto.context_pb2 import ConnectionList, Empty, Service, ServiceId
+from service.proto.context_pb2 import Empty, Service, ServiceId, ServiceStatusEnum
 from service.proto.service_pb2_grpc import ServiceServiceServicer
-from .Tools import grpc_message_to_json_string
 
 LOGGER = logging.getLogger(__name__)
 
@@ -21,12 +35,11 @@ class MockServicerImpl_Service(ServiceServiceServicer):
 
     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)
+        service = Service()
+        service.CopyFrom(request)
+        service.service_status.service_status = ServiceStatusEnum.SERVICESTATUS_ACTIVE
+        return self.context_client.SetService(service)
 
     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/common/tests/PytestGenerateTests.py b/src/common/tests/PytestGenerateTests.py
index 1c5abcf45b2f3e2d14fd31a5b3c1ee5d434f10b3..240570565f9f3df2905ee33efb15c3b6a147050c 100644
--- a/src/common/tests/PytestGenerateTests.py
+++ b/src/common/tests/PytestGenerateTests.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 # Create a set of tests enabling to run tests as follows ...
 #   from common.tests.PytestGenerateTests import pytest_generate_tests # pylint: disable=unused-import
 #
diff --git a/src/common/tests/__init__.py b/src/common/tests/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/common/tests/__init__.py
+++ b/src/common/tests/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/common/tools/__init__.py b/src/common/tools/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/common/tools/__init__.py
+++ b/src/common/tools/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/common/tools/client/RetryDecorator.py b/src/common/tools/client/RetryDecorator.py
index d3f7f3e38c3e8b16afd39628bcfa4196f82b077e..d7bcdb42da9d156dd9f80804e33debcb5c278d83 100644
--- a/src/common/tools/client/RetryDecorator.py
+++ b/src/common/tools/client/RetryDecorator.py
@@ -1,6 +1,20 @@
-# This decorator re-executes the decorated function when it raises an exception. It enables to control the exception
-# classes that should trigger the re-execution, the maximum number of retries, the delay between retries, and set the
-# execution of a preparation method before every retry. The delay is specfied by means of user-customizable functions.
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This decorator re-executes the decorated function when it raises an exception. It enables to control the maximum
+# number of retries, the delay between retries, and to set the execution of a preparation method before every retry.
+# The delay is specfied by means of user-customizable functions.
 #
 # Delay functions should return a compute function with a single parameter, the number of retry. For instance:
 #   delay_linear(initial=0, increment=0):
@@ -16,10 +30,8 @@
 #       adds an increasing delay between retries, starting with 1 second, and incresing it exponentially by steps of 2
 #       seconds, up to 10 seconds,  every time an exception is caught within the current execution.
 #       E.g. 1.0, 2.0, 4.0, 8.0, 10.0, 10.0, 10.0, ...
-
+#
 # Arguments:
-# - exceptions: defines the set of exception classes to be catched for reconnection. Others are re-raised.
-#   By default all exceptions are re-raised.
 # - max_retries: defines the maximum number of retries acceptable before giving up. By default, 0 retries are executed.
 # - delay_function: defines the delay computation method to be used. By default, delay_linear with a fixed delay of 0.1
 #   seconds is used.
@@ -31,7 +43,10 @@
 # - prepare_method_kwargs: defines the dictionary of keyword arguments to be provided to the preparation method. If no
 #   preparation method is specified, the argument is silently ignored. By default, an empty dictionary is defined.
 
-import time
+import grpc, logging, time
+from grpc._channel import _InactiveRpcError
+
+LOGGER = logging.getLogger(__name__)
 
 def delay_linear(initial=0, increment=0, maximum=None):
     def compute(num_try):
@@ -42,12 +57,12 @@ def delay_linear(initial=0, increment=0, maximum=None):
 
 def delay_exponential(initial=1, increment=1, maximum=None):
     def compute(num_try):
-        delay = initial * (num_try - 1) ^ increment
+        delay = initial * pow((num_try - 1), increment)
         if maximum is not None: delay = max(delay, maximum)
         return delay
     return compute
 
-def retry(exceptions=set(), max_retries=0, delay_function=delay_linear(initial=0, increment=0),
+def retry(max_retries=0, delay_function=delay_linear(initial=0, increment=0),
           prepare_method_name=None, prepare_method_args=[], prepare_method_kwargs={}):
     def _reconnect(func):
         def wrapper(self, *args, **kwargs):
@@ -58,14 +73,19 @@ def retry(exceptions=set(), max_retries=0, delay_function=delay_linear(initial=0
             while not given_up:
                 try:
                     return func(self, *args, **kwargs)
-                except Exception as e:
-                    if not isinstance(e, tuple(exceptions)): raise
+                except (grpc.RpcError, _InactiveRpcError) as e:
+                    if e.code() not in [grpc.StatusCode.UNAVAILABLE]: raise
 
                     num_try += 1
                     given_up = num_try > max_retries
-                    if given_up: raise Exception('Giving up... {} tries failed'.format(max_retries))
-                    if delay_function is not None: time.sleep(delay_function(num_try))
+                    if given_up: raise Exception('Giving up... {:d} tries failed'.format(max_retries)) from e
+                    if delay_function is not None:
+                        delay = delay_function(num_try)
+                        time.sleep(delay)
+                        LOGGER.info('Retry {:d}/{:d} after {:f} seconds...'.format(num_try, max_retries, delay))
+                    else:
+                        LOGGER.info('Retry {:d}/{:d} immediate...'.format(num_try, max_retries))
 
                     if prepare_method_name is not None: prepare_method(*prepare_method_args, **prepare_method_kwargs)
-        return(wrapper)
-    return(_reconnect)
+        return wrapper
+    return _reconnect
diff --git a/src/common/tools/client/__init__.py b/src/common/tools/client/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/common/tools/client/__init__.py
+++ b/src/common/tools/client/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/common/tools/grpc/Tools.py b/src/common/tools/grpc/Tools.py
new file mode 100644
index 0000000000000000000000000000000000000000..7c6a74348e4e0fa51ac68a40af511f4753c628ae
--- /dev/null
+++ b/src/common/tools/grpc/Tools.py
@@ -0,0 +1,35 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import json
+from google.protobuf.json_format import MessageToDict
+
+def grpc_message_to_json(
+    message, including_default_value_fields=True, preserving_proto_field_name=True, use_integers_for_enums=False):
+
+    return MessageToDict(
+        message, including_default_value_fields=including_default_value_fields,
+        preserving_proto_field_name=preserving_proto_field_name, use_integers_for_enums=use_integers_for_enums)
+
+def grpc_message_list_to_json(message_list):
+    if message_list is None: return None
+    return [grpc_message_to_json(message) for message in message_list]
+
+def grpc_message_to_json_string(message):
+    if message is None: return str(None)
+    return json.dumps(grpc_message_to_json(message), sort_keys=True)
+
+def grpc_message_list_to_json_string(message_list):
+    if message_list is None: return str(None)
+    return json.dumps(grpc_message_list_to_json(message_list), sort_keys=True)
diff --git a/src/common/tools/grpc/__init__.py b/src/common/tools/grpc/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..70a33251242c51f49140e596b8208a19dd5245f7
--- /dev/null
+++ b/src/common/tools/grpc/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/common/tools/object_factory/ConfigRule.py b/src/common/tools/object_factory/ConfigRule.py
new file mode 100644
index 0000000000000000000000000000000000000000..8f65d7123d64f093ec2229de05dbb71bec93ed86
--- /dev/null
+++ b/src/common/tools/object_factory/ConfigRule.py
@@ -0,0 +1,27 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import json
+from typing import Any, Dict, Union
+from context.proto.context_pb2 import ConfigActionEnum
+
+def json_config_rule(action : ConfigActionEnum, resource_key : str, resource_value : Union[str, Dict[str, Any]]):
+    if not isinstance(resource_value, str): resource_value = json.dumps(resource_value, sort_keys=True)
+    return {'action': action, 'resource_key': resource_key, 'resource_value': resource_value}
+
+def json_config_rule_set(resource_key : str, resource_value : Union[str, Dict[str, Any]]):
+    return json_config_rule(ConfigActionEnum.CONFIGACTION_SET, resource_key, resource_value)
+
+def json_config_rule_delete(resource_key : str, resource_value : Union[str, Dict[str, Any]]):
+    return json_config_rule(ConfigActionEnum.CONFIGACTION_DELETE, resource_key, resource_value)
diff --git a/src/common/tools/object_factory/Connection.py b/src/common/tools/object_factory/Connection.py
new file mode 100644
index 0000000000000000000000000000000000000000..3f0207ae57901dc4775552b0a967d280077069bb
--- /dev/null
+++ b/src/common/tools/object_factory/Connection.py
@@ -0,0 +1,32 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import copy
+from typing import Dict, List, Optional
+
+def json_connection_id(connection_uuid : str):
+    return {'connection_uuid': {'uuid': connection_uuid}}
+
+def json_connection(
+        connection_uuid : str, service_id : Optional[Dict] = None, path_hops_endpoint_ids : List[Dict] = [],
+        sub_service_ids : List[Dict] = []
+    ):
+
+    result = {
+        'connection_id'         : json_connection_id(connection_uuid),
+        'path_hops_endpoint_ids': copy.deepcopy(path_hops_endpoint_ids),
+        'sub_service_ids'       : copy.deepcopy(sub_service_ids),
+    }
+    if service_id is not None: result['service_id'] = copy.deepcopy(service_id)
+    return result
diff --git a/src/common/tools/object_factory/Constraint.py b/src/common/tools/object_factory/Constraint.py
new file mode 100644
index 0000000000000000000000000000000000000000..de6e159c3f58af1dd3c53b61b290b2b57aadf672
--- /dev/null
+++ b/src/common/tools/object_factory/Constraint.py
@@ -0,0 +1,20 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import json
+from typing import Any, Dict, Union
+
+def json_constraint(constraint_type : str, constraint_value : Union[str, Dict[str, Any]]):
+    if not isinstance(constraint_value, str): constraint_value = json.dumps(constraint_value, sort_keys=True)
+    return {'constraint_type': constraint_type, 'constraint_value': constraint_value}
diff --git a/src/common/tools/object_factory/Context.py b/src/common/tools/object_factory/Context.py
new file mode 100644
index 0000000000000000000000000000000000000000..d5d1bf9439dd12c67d77bcbe38f37fb29c89d948
--- /dev/null
+++ b/src/common/tools/object_factory/Context.py
@@ -0,0 +1,23 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+def json_context_id(context_uuid : str):
+    return {'context_uuid': {'uuid': context_uuid}}
+
+def json_context(context_uuid : str):
+    return {
+        'context_id'  : json_context_id(context_uuid),
+        'topology_ids': [],
+        'service_ids' : [],
+    }
diff --git a/src/common/tools/object_factory/Device.py b/src/common/tools/object_factory/Device.py
new file mode 100644
index 0000000000000000000000000000000000000000..ae065e9c03127bf26b4e15710431cc1cfad67208
--- /dev/null
+++ b/src/common/tools/object_factory/Device.py
@@ -0,0 +1,107 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import copy
+from typing import Dict, List, Tuple
+from common.DeviceTypes import DeviceTypeEnum
+from common.tools.object_factory.ConfigRule import json_config_rule_set
+from context.proto.context_pb2 import DeviceDriverEnum, DeviceOperationalStatusEnum
+
+DEVICE_DISABLED = DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_DISABLED
+
+DEVICE_EMUPR_TYPE   = DeviceTypeEnum.EMULATED_PACKET_ROUTER.value
+DEVICE_EMUOLS_TYPE   = DeviceTypeEnum.EMULATED_OPTICAL_LINE_SYSTEM.value
+DEVICE_EMU_DRIVERS  = [DeviceDriverEnum.DEVICEDRIVER_UNDEFINED]
+DEVICE_EMU_ADDRESS  = '127.0.0.1'
+DEVICE_EMU_PORT     = '0'
+
+DEVICE_PR_TYPE      = DeviceTypeEnum.PACKET_ROUTER.value
+DEVICE_PR_DRIVERS   = [DeviceDriverEnum.DEVICEDRIVER_OPENCONFIG]
+
+DEVICE_TAPI_TYPE    = DeviceTypeEnum.OPTICAL_LINE_SYSTEM.value
+DEVICE_TAPI_DRIVERS = [DeviceDriverEnum.DEVICEDRIVER_TRANSPORT_API]
+
+DEVICE_P4_TYPE      = DeviceTypeEnum.P4_SWITCH.value
+DEVICE_P4_DRIVERS   = [DeviceDriverEnum.DEVICEDRIVER_P4]
+
+def json_device_id(device_uuid : str):
+    return {'device_uuid': {'uuid': device_uuid}}
+
+def json_device(
+        device_uuid : str, device_type : str, status : DeviceOperationalStatusEnum, endpoints : List[Dict] = [],
+        config_rules : List[Dict] = [], drivers : List[Dict] = []
+    ):
+    return {
+        'device_id'                : json_device_id(device_uuid),
+        'device_type'              : device_type,
+        'device_config'            : {'config_rules': copy.deepcopy(config_rules)},
+        'device_operational_status': status,
+        'device_drivers'           : copy.deepcopy(drivers),
+        'device_endpoints'         : copy.deepcopy(endpoints),
+    }
+
+def json_device_emulated_packet_router_disabled(
+        device_uuid : str, endpoints : List[Dict] = [], config_rules : List[Dict] = [],
+        drivers : List[Dict] = DEVICE_EMU_DRIVERS
+    ):
+    return json_device(
+        device_uuid, DEVICE_EMUPR_TYPE, DEVICE_DISABLED, endpoints=endpoints, config_rules=config_rules,
+        drivers=drivers)
+
+def json_device_emulated_tapi_disabled(
+        device_uuid : str, endpoints : List[Dict] = [], config_rules : List[Dict] = [],
+        drivers : List[Dict] = DEVICE_EMU_DRIVERS
+    ):
+    return json_device(
+        device_uuid, DEVICE_EMUOLS_TYPE, DEVICE_DISABLED, endpoints=endpoints, config_rules=config_rules,
+        drivers=drivers)
+
+def json_device_packetrouter_disabled(
+        device_uuid : str, endpoints : List[Dict] = [], config_rules : List[Dict] = [],
+        drivers : List[Dict] = DEVICE_PR_DRIVERS
+    ):
+    return json_device(
+        device_uuid, DEVICE_PR_TYPE, DEVICE_DISABLED, endpoints=endpoints, config_rules=config_rules, drivers=drivers)
+
+def json_device_tapi_disabled(
+        device_uuid : str, endpoints : List[Dict] = [], config_rules : List[Dict] = [],
+        drivers : List[Dict] = DEVICE_TAPI_DRIVERS
+    ):
+    return json_device(
+        device_uuid, DEVICE_TAPI_TYPE, DEVICE_DISABLED, endpoints=endpoints, config_rules=config_rules, drivers=drivers)
+
+def json_device_p4_disabled(
+        device_uuid : str, endpoints : List[Dict] = [], config_rules : List[Dict] = [],
+        drivers : List[Dict] = DEVICE_P4_DRIVERS
+    ):
+    return json_device(
+        device_uuid, DEVICE_P4_TYPE, DEVICE_DISABLED, endpoints=endpoints, config_rules=config_rules, drivers=drivers)
+
+def json_device_connect_rules(address : str, port : int, settings : Dict = {}):
+    return [
+        json_config_rule_set('_connect/address',  address),
+        json_config_rule_set('_connect/port',     port),
+        json_config_rule_set('_connect/settings', settings),
+    ]
+
+def json_device_emulated_connect_rules(
+        endpoint_descriptors : List[Tuple[str, str, List[int]]], address : str = DEVICE_EMU_ADDRESS,
+        port : int = DEVICE_EMU_PORT
+    ):
+
+    settings = {'endpoints': [
+        {'uuid': endpoint_uuid, 'type': endpoint_type, 'sample_types': sample_types}
+        for endpoint_uuid,endpoint_type,sample_types in endpoint_descriptors
+    ]}
+    return json_device_connect_rules(address, port, settings=settings)
diff --git a/src/common/tools/object_factory/EndPoint.py b/src/common/tools/object_factory/EndPoint.py
new file mode 100644
index 0000000000000000000000000000000000000000..d750604365fc616536374dd3541a17613da4746f
--- /dev/null
+++ b/src/common/tools/object_factory/EndPoint.py
@@ -0,0 +1,33 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import copy
+from typing import Dict, List, Optional
+
+def json_endpoint_id(device_id : Dict, endpoint_uuid : str, topology_id : Optional[Dict] = None):
+    result = {'device_id': copy.deepcopy(device_id), 'endpoint_uuid': {'uuid': endpoint_uuid}}
+    if topology_id is not None: result['topology_id'] = copy.deepcopy(topology_id)
+    return result
+
+def json_endpoint(
+        device_id : Dict, endpoint_uuid : str, endpoint_type : str, topology_id : Optional[Dict] = None,
+        kpi_sample_types : List[int] = []
+    ):
+
+    result = {
+        'endpoint_id': json_endpoint_id(device_id, endpoint_uuid, topology_id=topology_id),
+        'endpoint_type': endpoint_type,
+    }
+    if len(kpi_sample_types) > 0: result['kpi_sample_types'] = copy.deepcopy(kpi_sample_types)
+    return result
diff --git a/src/common/tools/object_factory/Link.py b/src/common/tools/object_factory/Link.py
new file mode 100644
index 0000000000000000000000000000000000000000..fac27945912be6ee24b3808f567ea160e017fb37
--- /dev/null
+++ b/src/common/tools/object_factory/Link.py
@@ -0,0 +1,22 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import copy
+from typing import Dict, List
+
+def json_link_id(link_uuid : str):
+    return {'link_uuid': {'uuid': link_uuid}}
+
+def json_link(link_uuid : str, endpoint_ids : List[Dict]):
+    return {'link_id': json_link_id(link_uuid), 'link_endpoint_ids': copy.deepcopy(endpoint_ids)}
diff --git a/src/common/tools/object_factory/Service.py b/src/common/tools/object_factory/Service.py
new file mode 100644
index 0000000000000000000000000000000000000000..a84953926bed1a479d9c832025e580286353630b
--- /dev/null
+++ b/src/common/tools/object_factory/Service.py
@@ -0,0 +1,48 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import copy
+from typing import Dict, List, Optional
+from common.Constants import DEFAULT_CONTEXT_UUID
+from common.tools.object_factory.Context import json_context_id
+from context.proto.context_pb2 import ServiceStatusEnum, ServiceTypeEnum
+
+def json_service_id(service_uuid : str, context_id : Optional[Dict] = None):
+    result = {'service_uuid': {'uuid': service_uuid}}
+    if context_id is not None: result['context_id'] = copy.deepcopy(context_id)
+    return result
+
+def json_service(
+    service_uuid : str, service_type : ServiceTypeEnum, context_id : Optional[Dict] = None,
+    status : ServiceStatusEnum = ServiceStatusEnum.SERVICESTATUS_PLANNED,
+    endpoint_ids : List[Dict] = [], constraints : List[Dict] = [], config_rules : List[Dict] = []):
+
+    return {
+        'service_id'          : json_service_id(service_uuid, context_id=context_id),
+        'service_type'        : service_type,
+        'service_status'      : {'service_status': status},
+        'service_endpoint_ids': copy.deepcopy(endpoint_ids),
+        'service_constraints' : copy.deepcopy(constraints),
+        'service_config'      : {'config_rules': copy.deepcopy(config_rules)},
+    }
+
+def json_service_l3nm_planned(
+        service_uuid : str, endpoint_ids : List[Dict] = [], constraints : List[Dict] = [],
+        config_rules : List[Dict] = []
+    ):
+
+    return json_service(
+        service_uuid, ServiceTypeEnum.SERVICETYPE_L3NM, context_id=json_context_id(DEFAULT_CONTEXT_UUID),
+        status=ServiceStatusEnum.SERVICESTATUS_PLANNED, endpoint_ids=endpoint_ids, constraints=constraints,
+        config_rules=config_rules)
diff --git a/src/common/tools/object_factory/Topology.py b/src/common/tools/object_factory/Topology.py
new file mode 100644
index 0000000000000000000000000000000000000000..7de4a1d577f1e46cfdf6545dde79b60808cd8afb
--- /dev/null
+++ b/src/common/tools/object_factory/Topology.py
@@ -0,0 +1,28 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import copy
+from typing import Dict, Optional
+
+def json_topology_id(topology_uuid : str, context_id : Optional[Dict] = None):
+    result = {'topology_uuid': {'uuid': topology_uuid}}
+    if context_id is not None: result['context_id'] = copy.deepcopy(context_id)
+    return result
+
+def json_topology(topology_uuid : str, context_id : Optional[Dict] = None):
+    return {
+        'topology_id': json_topology_id(topology_uuid, context_id=context_id),
+        'device_ids' : [],
+        'link_ids'   : [],
+    }
diff --git a/src/common/tools/object_factory/__init__.py b/src/common/tools/object_factory/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..70a33251242c51f49140e596b8208a19dd5245f7
--- /dev/null
+++ b/src/common/tools/object_factory/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/common/type_checkers/Assertions.py b/src/common/type_checkers/Assertions.py
index 31458843c2b5d6e850a7230249b7d499a1a894b9..5e30b75b67355965bd1d4fdd8cc7b7e662beb98d 100644
--- a/src/common/type_checkers/Assertions.py
+++ b/src/common/type_checkers/Assertions.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 # ----- Enumerations ---------------------------------------------------------------------------------------------------
 
 def validate_config_action_enum(message):
diff --git a/src/common/type_checkers/Checkers.py b/src/common/type_checkers/Checkers.py
index f78395c9c5f480bf75b4c9344dfeb3b48f9da062..0bf36cb79fddb827743c2f7529b4ae3a9040d506 100644
--- a/src/common/type_checkers/Checkers.py
+++ b/src/common/type_checkers/Checkers.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import re
 from typing import Any, Container, Dict, List, Optional, Pattern, Set, Sized, Tuple, Union
 
diff --git a/src/common/type_checkers/__init__.py b/src/common/type_checkers/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/common/type_checkers/__init__.py
+++ b/src/common/type_checkers/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/compute/.gitlab-ci.yml b/src/compute/.gitlab-ci.yml
index 96607c896fcfc88d0505b73da007fcc2dcb5c257..7daa1764a9c10a79df3cc1b2aadb270b982c1224 100644
--- a/src/compute/.gitlab-ci.yml
+++ b/src/compute/.gitlab-ci.yml
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 # Build, tag and push the Docker image to the GitLab registry
 build compute:
   variables:
@@ -41,7 +55,9 @@ unit test compute:
     - sleep 5
     - docker ps -a
     - docker logs $IMAGE_NAME
-    - docker exec -i $IMAGE_NAME bash -c "coverage run -m pytest --log-level=INFO --verbose $IMAGE_NAME/tests/test_unitary.py --junitxml=/opt/results/${IMAGE_NAME}_report.xml; coverage xml -o /opt/results/${IMAGE_NAME}_coverage.xml; coverage report --include='${IMAGE_NAME}/*' --show-missing"
+    - docker exec -i $IMAGE_NAME bash -c "coverage run -m pytest --log-level=INFO --verbose $IMAGE_NAME/tests/test_unitary.py --junitxml=/opt/results/${IMAGE_NAME}_report.xml"
+    - docker exec -i $IMAGE_NAME bash -c "coverage xml -o /opt/results/${IMAGE_NAME}_coverage.xml"
+    - docker exec -i $IMAGE_NAME bash -c "coverage report --include='${IMAGE_NAME}/*' --show-missing"
   coverage: '/TOTAL\s+\d+\s+\d+\s+(\d+%)/'
   after_script:
     - docker rm -f $IMAGE_NAME
diff --git a/src/compute/Config.py b/src/compute/Config.py
index b2d3179fab6e55368ba751aac48de507551c4516..c568be4476326be0080aad9f88be0183d82ef833 100644
--- a/src/compute/Config.py
+++ b/src/compute/Config.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging
 from werkzeug.security import generate_password_hash
 
diff --git a/src/compute/Dockerfile b/src/compute/Dockerfile
index 99d4e3ed1adc8a688874f3291f4891112543b3ff..6d3cafda9f02cb5e90947eb8b235182644371be2 100644
--- a/src/compute/Dockerfile
+++ b/src/compute/Dockerfile
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 FROM python:3-slim
 
 # Install dependencies
@@ -25,7 +39,7 @@ RUN mkdir -p /var/teraflow/compute
 # Get Python packages per module
 COPY compute/requirements.in compute/requirements.in
 RUN pip-compile --output-file=compute/requirements.txt compute/requirements.in
-RUN python3 -m pip install -r compute/requirements.in
+RUN python3 -m pip install -r compute/requirements.txt
 
 # Add files into working directory
 COPY common/. common
diff --git a/src/compute/__init__.py b/src/compute/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/compute/__init__.py
+++ b/src/compute/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/compute/client/ComputeClient.py b/src/compute/client/ComputeClient.py
index 19a26b943c3c0d1cb6d6dbcbb1bb33faaa5e3174..ac85500294f0288e7d0e86f77cd7c415f28ef5e5 100644
--- a/src/compute/client/ComputeClient.py
+++ b/src/compute/client/ComputeClient.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import grpc, logging
 from common.tools.client.RetryDecorator import retry, delay_exponential
 from compute.proto.compute_pb2_grpc import ComputeServiceStub
@@ -7,6 +21,7 @@ from compute.proto.context_pb2 import (
 LOGGER = logging.getLogger(__name__)
 MAX_RETRIES = 15
 DELAY_FUNCTION = delay_exponential(initial=0.01, increment=2.0, maximum=5.0)
+RETRY_DECORATOR = retry(max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
 
 class ComputeClient:
     def __init__(self, address, port):
@@ -26,49 +41,49 @@ class ComputeClient:
         self.channel = None
         self.stub = None
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def CheckCredentials(self, request : TeraFlowController) -> AuthenticationResult:
         LOGGER.debug('CheckCredentials request: {:s}'.format(str(request)))
         response = self.stub.CheckCredentials(request)
         LOGGER.debug('CheckCredentials result: {:s}'.format(str(response)))
         return response
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def GetConnectivityServiceStatus(self, request : ServiceId) -> ServiceStatus:
         LOGGER.debug('GetConnectivityServiceStatus request: {:s}'.format(str(request)))
         response = self.stub.GetConnectivityServiceStatus(request)
         LOGGER.debug('GetConnectivityServiceStatus result: {:s}'.format(str(response)))
         return response
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def CreateConnectivityService(self, request : Service) -> ServiceId:
         LOGGER.debug('CreateConnectivityService request: {:s}'.format(str(request)))
         response = self.stub.CreateConnectivityService(request)
         LOGGER.debug('CreateConnectivityService result: {:s}'.format(str(response)))
         return response
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def EditConnectivityService(self, request : Service) -> ServiceId:
         LOGGER.debug('EditConnectivityService request: {:s}'.format(str(request)))
         response = self.stub.EditConnectivityService(request)
         LOGGER.debug('EditConnectivityService result: {:s}'.format(str(response)))
         return response
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def DeleteConnectivityService(self, request : Service) -> Empty:
         LOGGER.debug('DeleteConnectivityService request: {:s}'.format(str(request)))
         response = self.stub.DeleteConnectivityService(request)
         LOGGER.debug('DeleteConnectivityService result: {:s}'.format(str(response)))
         return response
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def GetAllActiveConnectivityServices(self, request : Empty) -> ServiceIdList:
         LOGGER.debug('GetAllActiveConnectivityServices request: {:s}'.format(str(request)))
         response = self.stub.GetAllActiveConnectivityServices(request)
         LOGGER.debug('GetAllActiveConnectivityServices result: {:s}'.format(str(response)))
         return response
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def ClearAllConnectivityServices(self, request : Empty) -> Empty:
         LOGGER.debug('ClearAllConnectivityServices request: {:s}'.format(str(request)))
         response = self.stub.ClearAllConnectivityServices(request)
diff --git a/src/compute/client/__init__.py b/src/compute/client/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/compute/client/__init__.py
+++ b/src/compute/client/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/compute/proto/__init__.py b/src/compute/proto/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/compute/proto/__init__.py
+++ b/src/compute/proto/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/compute/requirements.in b/src/compute/requirements.in
index 42a2f3d40194e61bae5c1e14bb81eb4bc43d5504..fcf7e912cc6902bc8547a2a91fc3797b1b7c3db1 100644
--- a/src/compute/requirements.in
+++ b/src/compute/requirements.in
@@ -1,11 +1,12 @@
-Flask
-Flask-HTTPAuth
-Flask-RESTful
-grpcio-health-checking
-grpcio
-jsonschema
-prometheus-client
-pytest
-pytest-benchmark
-requests
-coverage
+Flask==2.0.2
+Flask-HTTPAuth==4.5.0
+Flask-RESTful==0.3.9
+grpcio==1.43.0
+grpcio-health-checking==1.43.0
+jsonschema==4.4.0
+prometheus-client==0.13.0
+protobuf==3.19.3
+pytest==6.2.5
+pytest-benchmark==3.4.1
+requests==2.27.1
+coverage==6.3
diff --git a/src/compute/service/ComputeService.py b/src/compute/service/ComputeService.py
index 51a15472bc83416ebbfc3c421e5cfed2a9682bdc..1f523793d28526bdce1dd50cba7677aa04481171 100644
--- a/src/compute/service/ComputeService.py
+++ b/src/compute/service/ComputeService.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import grpc, logging
 from concurrent import futures
 from grpc_health.v1.health import HealthServicer, OVERALL_HEALTH
diff --git a/src/compute/service/ComputeServiceServicerImpl.py b/src/compute/service/ComputeServiceServicerImpl.py
index cfde0c3d9798283fe9affd95be36bb3d9d1bcad8..f5850c1ccc5706a58ffbc13ad9ce2fa2b705d09b 100644
--- a/src/compute/service/ComputeServiceServicerImpl.py
+++ b/src/compute/service/ComputeServiceServicerImpl.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import grpc, logging
 from common.rpc_method_wrapper.Decorator import create_metrics, safe_and_metered_rpc_method
 from compute.proto.compute_pb2_grpc import ComputeServiceServicer
diff --git a/src/compute/service/__init__.py b/src/compute/service/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/compute/service/__init__.py
+++ b/src/compute/service/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/compute/service/__main__.py b/src/compute/service/__main__.py
index eacc1f6c464112192194fca5827033aedc57385c..cf6f8241010d26d8aff9718b8bc531c2f7f43d22 100644
--- a/src/compute/service/__main__.py
+++ b/src/compute/service/__main__.py
@@ -1,6 +1,20 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging, signal, sys, threading
 from prometheus_client import start_http_server
-from common.Settings import get_setting
+from common.Settings import get_setting, wait_for_environment_variables
 from compute.Config import (
     GRPC_SERVICE_PORT, GRPC_MAX_WORKERS, GRPC_GRACE_PERIOD, LOG_LEVEL, RESTAPI_SERVICE_PORT, RESTAPI_BASE_URL,
     METRICS_PORT)
@@ -29,6 +43,11 @@ def main():
     logging.basicConfig(level=log_level)
     LOGGER = logging.getLogger(__name__)
 
+    wait_for_environment_variables([
+        'CONTEXTSERVICE_SERVICE_HOST', 'CONTEXTSERVICE_SERVICE_PORT_GRPC',
+        'SERVICESERVICE_SERVICE_HOST', 'SERVICESERVICE_SERVICE_PORT_GRPC'
+    ])
+
     signal.signal(signal.SIGINT,  signal_handler)
     signal.signal(signal.SIGTERM, signal_handler)
 
diff --git a/src/compute/service/rest_server/RestServer.py b/src/compute/service/rest_server/RestServer.py
index 8ed8dbbbf69bc89c9c76fdf31e16b0687d47856e..26055f8dfa319ae715e96241a13860fd8283a5aa 100644
--- a/src/compute/service/rest_server/RestServer.py
+++ b/src/compute/service/rest_server/RestServer.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging, threading, time
 from flask import Flask, request
 from flask_restful import Api, Resource
diff --git a/src/compute/service/rest_server/__init__.py b/src/compute/service/rest_server/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/compute/service/rest_server/__init__.py
+++ b/src/compute/service/rest_server/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/compute/service/rest_server/nbi_plugins/__init__.py b/src/compute/service/rest_server/nbi_plugins/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/compute/service/rest_server/nbi_plugins/__init__.py
+++ b/src/compute/service/rest_server/nbi_plugins/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/Constants.py b/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/Constants.py
index 87c32c444d39acb048ede9105c9a0dc2c7e3899e..9420517e1b253e6f9169ccabafe5f441662c9b04 100644
--- a/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/Constants.py
+++ b/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/Constants.py
@@ -1,3 +1,28 @@
-DEFAULT_MTU = 1512
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+DEFAULT_MTU              = 1512
 DEFAULT_ADDRESS_FAMILIES = ['IPV4']
-DEFAULT_SUB_INTERFACE_INDEX = 0
+DEFAULT_BGP_AS           = 65000
+DEFAULT_BGP_ROUTE_TARGET = '{:d}:{:d}'.format(DEFAULT_BGP_AS, 333)
+
+# Bearer mappings:
+# device_uuid:endpoint_uuid => (
+#       device_uuid, endpoint_uuid, router_id, route_distinguisher, sub_if_index, address_ip, address_prefix)
+BEARER_MAPPINGS = {
+    'R1-INF:13/2/1': ('R1-INF', '13/2/1', '10.10.10.1', '65000:100', 400, '3.3.2.1', 24),
+    'R2-EMU:13/2/1': ('R2-EMU', '13/2/1', '12.12.12.1', '65000:120', 450, '3.4.2.1', 24),
+    'R3-INF:13/2/1': ('R3-INF', '13/2/1', '20.20.20.1', '65000:200', 500, '3.3.1.1', 24),
+    'R4-EMU:13/2/1': ('R4-EMU', '13/2/1', '22.22.22.1', '65000:220', 550, '3.4.1.1', 24),
+}
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
index 752a027ad0d41f67f6a2312ee166a51ebcbc23bd..6a91e6ae1b71bca5c7c26464d0bdb4f187f0381a 100644
--- 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
@@ -1,17 +1,29 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging
-from 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 service.proto.context_pb2 import ServiceStatusEnum
 from .tools.Authentication import HTTP_AUTH
-from .tools.HttpStatusCodes import HTTP_CREATED, HTTP_GATEWAYTIMEOUT, HTTP_NOCONTENT, HTTP_OK, HTTP_SERVERERROR
+from .tools.HttpStatusCodes import HTTP_GATEWAYTIMEOUT, HTTP_NOCONTENT, HTTP_OK, HTTP_SERVERERROR
 
 LOGGER = logging.getLogger(__name__)
 
@@ -37,7 +49,6 @@ class L2VPN_Service(Resource):
             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({})
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
index 2ed0293f0729c6d4617a445034702f706a6daa25..191166a74e53b5c9ae58d3c8cbb3f0e0b1ca8480 100644
--- 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
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging
 from typing import Dict, List
 from flask import request
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
index 1f02e50a4847b78f431fa720b577c6c3cc42cc7f..6811dadac8bbc744bc1630adcfb88750765b11b8 100644
--- 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
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import json, logging
 from typing import Dict
 from flask import request
@@ -7,14 +21,15 @@ from flask_restful import Resource
 from werkzeug.exceptions import UnsupportedMediaType
 from common.Constants import DEFAULT_CONTEXT_UUID
 from common.Settings import get_setting
+from common.tools.grpc.Tools import grpc_message_to_json_string
 from context.client.ContextClient import ContextClient
-from context.proto.context_pb2 import Service, ServiceId, ServiceStatusEnum
+from context.proto.context_pb2 import ConfigActionEnum, Service, ServiceId
 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
+from .Constants import BEARER_MAPPINGS, DEFAULT_ADDRESS_FAMILIES, DEFAULT_BGP_AS, DEFAULT_BGP_ROUTE_TARGET, DEFAULT_MTU
 
 LOGGER = logging.getLogger(__name__)
 
@@ -23,10 +38,11 @@ def process_site_network_access(context_client : ContextClient, site_network_acc
     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)
+    mapping = BEARER_MAPPINGS.get(bearer_reference)
+    if mapping is None:
+        msg = 'Specified Bearer({:s}) is not configured.'
+        raise Exception(msg.format(str(bearer_reference)))
+    device_uuid,endpoint_uuid,router_id,route_distinguisher,sub_if_index,address_ip,address_prefix = mapping
 
     # pylint: disable=no-member
     service_id = ServiceId()
@@ -48,15 +64,9 @@ def process_site_network_access(context_client : ContextClient, site_network_acc
         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
+        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
@@ -69,19 +79,33 @@ def process_site_network_access(context_client : ContextClient, site_network_acc
             msg = 'Specified AddressFamilies({:s}) differs from Service AddressFamilies({:s})'
             raise Exception(msg.format(str(json_settings['address_families']), str(DEFAULT_ADDRESS_FAMILIES)))
 
+        if 'bgp_as' not in json_settings:                                   # missing, add it
+            json_settings['bgp_as'] = DEFAULT_BGP_AS
+        elif json_settings['bgp_as'] != DEFAULT_BGP_AS:                     # differs, raise exception
+            msg = 'Specified BgpAs({:s}) differs from Service BgpAs({:s})'
+            raise Exception(msg.format(str(json_settings['bgp_as']), str(DEFAULT_BGP_AS)))
+
+        if 'bgp_route_target' not in json_settings:                         # missing, add it
+            json_settings['bgp_route_target'] = DEFAULT_BGP_ROUTE_TARGET
+        elif json_settings['bgp_route_target'] != DEFAULT_BGP_ROUTE_TARGET: # differs, raise exception
+            msg = 'Specified BgpRouteTarget({:s}) differs from Service BgpRouteTarget({:s})'
+            raise Exception(msg.format(str(json_settings['bgp_route_target']), str(DEFAULT_BGP_ROUTE_TARGET)))
+
         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.action = ConfigActionEnum.CONFIGACTION_SET
+        config_rule.resource_key = '/settings'
         config_rule.resource_value = json.dumps({
-            'route_distinguisher': route_distinguisher,
-            'mtu': DEFAULT_MTU,
+            'mtu'             : DEFAULT_MTU,
             'address_families': DEFAULT_ADDRESS_FAMILIES,
+            'bgp_as'          : DEFAULT_BGP_AS,
+            'bgp_route_target': DEFAULT_BGP_ROUTE_TARGET,
         }, sort_keys=True)
 
-    endpoint_settings_key = 'device[{:s}]/endpoint[{:s}]/settings'.format(device_uuid, endpoint_uuid)
+    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)
@@ -92,27 +116,56 @@ def process_site_network_access(context_client : ContextClient, site_network_acc
             msg = 'Specified RouterId({:s}) differs from Service RouterId({:s})'
             raise Exception(msg.format(str(json_settings['router_id']), str(router_id)))
 
+        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 '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
+            json_settings['sub_interface_index'] = sub_if_index
+        elif json_settings['sub_interface_index'] != sub_if_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)))
+                str(json_settings['sub_interface_index']), str(sub_if_index)))
+
+        if 'vlan_id' not in json_settings:                                  # missing, add it
+            json_settings['vlan_id'] = cvlan_id
+        elif json_settings['vlan_id'] != cvlan_id:                          # differs, raise exception
+            msg = 'Specified VLANId({:s}) differs from Service VLANId({:s})'
+            raise Exception(msg.format(
+                str(json_settings['vlan_id']), str(cvlan_id)))
+
+        if 'address_ip' not in json_settings:                               # missing, add it
+            json_settings['address_ip'] = address_ip
+        elif json_settings['address_ip'] != address_ip:                     # differs, raise exception
+            msg = 'Specified AddressIP({:s}) differs from Service AddressIP({:s})'
+            raise Exception(msg.format(
+                str(json_settings['address_ip']), str(address_ip)))
+
+        if 'address_prefix' not in json_settings:                           # missing, add it
+            json_settings['address_prefix'] = address_prefix
+        elif json_settings['address_prefix'] != address_prefix:             # differs, raise exception
+            msg = 'Specified AddressPrefix({:s}) differs from Service AddressPrefix({:s})'
+            raise Exception(msg.format(
+                str(json_settings['address_prefix']), str(address_prefix)))
 
         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.action = ConfigActionEnum.CONFIGACTION_SET
         config_rule.resource_key = endpoint_settings_key
         config_rule.resource_value = json.dumps({
             'router_id': router_id,
-            'sub_interface_index': DEFAULT_SUB_INTERFACE_INDEX,
+            'route_distinguisher': route_distinguisher,
+            'sub_interface_index': sub_if_index,
+            'vlan_id': cvlan_id,
+            'address_ip': address_ip,
+            'address_prefix': address_prefix,
         }, 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(
@@ -125,6 +178,7 @@ def process_list_site_network_access(
     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)
+            LOGGER.debug('service_request = {:s}'.format(grpc_message_to_json_string(service_request)))
             service_reply = service_client.UpdateService(service_request)
             if service_reply != service_request.service_id: # pylint: disable=no-member
                 raise Exception('Service update failed. Wrong Service Id was returned')
@@ -144,10 +198,6 @@ class L2VPN_SiteNetworkAccesses(Resource):
         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')
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
index 979c8a3bc1903381516bf0f9683bbe4e4f2c3cb3..79be6b7433a4e448febb37777cd0b310d5bf132d 100644
--- a/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/__init__.py
+++ b/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/__init__.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 # RFC 8466 - L2VPN Service Model (L2SM)
 # Ref: https://datatracker.ietf.org/doc/html/rfc8466
 
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
index f54da792b526cede52b94892ee9946fb63c6b015..30755bbce5910ac05a9572b232f5ae907cf0ae18 100644
--- 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
@@ -1,2 +1,16 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 # 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
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- 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
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/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
index 33ba8cc7fe5be76f82fbd74cd3608703f37e76a0..165973190b46c68e750516796b22ed6d09b470b0 100644
--- 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
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 # Example request:
 # request = {'ietf-l2vpn-svc:site-network-access': [{
 #     'network-access-id': '3fd942ee-2dc3-41d1-aeec-65aa85d117b2',
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
index 54e9c53163b8d764a37b613501f6b427d6e1773d..b224b40737914fbf0c7ae87f08d62dc34836f2aa 100644
--- 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
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 # Example request:
 # request = {'ietf-l2vpn-svc:vpn-service': [{
 #   'vpn-id': 'c6270231-f1de-4687-b2ed-7b58f9105775',
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
index de7c9eafd7b2d5afdc39b82a4d02bea20127fa4a..ebeeda4c81d7ef6b3c3203cb014e85f629d2046b 100644
--- 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
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 from flask_httpauth import HTTPBasicAuth
 from werkzeug.security import check_password_hash
 from compute.Config import RESTAPI_USERS
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
index 5879670102e861bf1598104ace80f1f0cdb931ca..0a98b9e76068a20ce5c78966ede600df84196194 100644
--- 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
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 HTTP_OK             = 200
 HTTP_CREATED        = 201
 HTTP_NOCONTENT      = 204
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
index 9c126d71beba72ebb7b69d9852927cb31ac2a614..b83a0f929769559d7870657cae30ee374962a296 100644
--- 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
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 from typing import List
 from flask.json import jsonify
 from jsonschema import _utils
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
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- 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
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/compute/tests/Constants.py b/src/compute/tests/Constants.py
new file mode 100644
index 0000000000000000000000000000000000000000..8d4e2ba8fe6144e6fc11c61bbe2c8296d74fc910
--- /dev/null
+++ b/src/compute/tests/Constants.py
@@ -0,0 +1,85 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+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': 'R1-INF:13/2/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': 'R2-EMU:13/2/1'},
+            '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': 'R3-INF:13/2/1'},
+            'site-id': '3',
+        },
+        #'switch_dpid'         : ??,                # wan_switch_dpid
+        #'switch_port'         : ??,                # wan_switch_port
+        #'datacenter_id'       : ??,                # vim_account
+    },
+    {
+        'device-id'           : 'dev-4',            # pop_switch_dpid
+        #'device_interface_id' : ??,                # pop_switch_port
+        'service_endpoint_id' : 'ep-4',             # wan_service_endpoint_id
+        'service_mapping_info': {                   # wan_service_mapping_info, other extra info
+            'bearer': {'bearer-reference': 'R4-EMU:13/2/1'},
+            'site-id': '4',
+        },
+        #'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}},
+]
diff --git a/src/compute/tests/Tools.py b/src/compute/tests/Tools.py
deleted file mode 100644
index a96c38ce546d4062df8229f5506f9dd49af6fc81..0000000000000000000000000000000000000000
--- a/src/compute/tests/Tools.py
+++ /dev/null
@@ -1,7 +0,0 @@
-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/__init__.py b/src/compute/tests/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/compute/tests/__init__.py
+++ b/src/compute/tests/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/compute/tests/mock_osm/MockOSM.py b/src/compute/tests/mock_osm/MockOSM.py
index c50ee6c88e75a62a743bba065830ae82827fa7d7..b4e629d5845fd7115fa0fa2c0887eca1c7c816c4 100644
--- a/src/compute/tests/mock_osm/MockOSM.py
+++ b/src/compute/tests/mock_osm/MockOSM.py
@@ -1,94 +1,62 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging
 from .WimconnectorIETFL2VPN import WimconnectorIETFL2VPN
 
 LOGGER = logging.getLogger(__name__)
 
-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}
+    def __init__(self, url, mapping, username, password):
+        wim = {'wim_url': url}
+        wim_account = {'user': username, 'password': password}
+        config = {'mapping_not_needed': False, 'service_endpoint_mapping': mapping}
         self.wim = WimconnectorIETFL2VPN(wim, wim_account, config=config)
-        self.service_uuid = None
-        self.conn_info = None
+        self.conn_info = {} # internal database emulating OSM storage provided to WIM Connectors
 
-    def create_connectivity_service(self):
+    def create_connectivity_service(self, service_type, connection_points):
+        LOGGER.info('[create_connectivity_service] service_type={:s}'.format(str(service_type)))
+        LOGGER.info('[create_connectivity_service] connection_points={:s}'.format(str(connection_points)))
         self.wim.check_credentials()
-        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)
+        result = self.wim.create_connectivity_service(service_type, connection_points)
         LOGGER.info('[create_connectivity_service] result={:s}'.format(str(result)))
-        self.service_uuid, self.conn_info = result
-
-    def get_connectivity_service_status(self):
+        service_uuid, conn_info = result
+        self.conn_info[service_uuid] = conn_info
+        return service_uuid
+
+    def get_connectivity_service_status(self, service_uuid):
+        LOGGER.info('[get_connectivity_service] service_uuid={:s}'.format(str(service_uuid)))
+        conn_info = self.conn_info.get(service_uuid)
+        if conn_info is None: raise Exception('ServiceId({:s}) not found'.format(str(service_uuid)))
+        LOGGER.info('[get_connectivity_service] conn_info={:s}'.format(str(conn_info)))
         self.wim.check_credentials()
-        result = self.wim.get_connectivity_service_status(self.service_uuid, conn_info=self.conn_info)
+        result = self.wim.get_connectivity_service_status(service_uuid, conn_info=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):
+        return result
+
+    def edit_connectivity_service(self, service_uuid, connection_points):
+        LOGGER.info('[edit_connectivity_service] service_uuid={:s}'.format(str(service_uuid)))
+        LOGGER.info('[edit_connectivity_service] connection_points={:s}'.format(str(connection_points)))
+        conn_info = self.conn_info.get(service_uuid)
+        if conn_info is None: raise Exception('ServiceId({:s}) not found'.format(str(service_uuid)))
+        LOGGER.info('[edit_connectivity_service] conn_info={:s}'.format(str(conn_info)))
+        self.wim.edit_connectivity_service(service_uuid, conn_info=conn_info, connection_points=connection_points)
+
+    def delete_connectivity_service(self, service_uuid):
+        LOGGER.info('[delete_connectivity_service] service_uuid={:s}'.format(str(service_uuid)))
+        conn_info = self.conn_info.get(service_uuid)
+        if conn_info is None: raise Exception('ServiceId({:s}) not found'.format(str(service_uuid)))
+        LOGGER.info('[delete_connectivity_service] conn_info={:s}'.format(str(conn_info)))
         self.wim.check_credentials()
-        self.wim.delete_connectivity_service(self.service_uuid, conn_info=self.conn_info)
+        self.wim.delete_connectivity_service(service_uuid, conn_info=conn_info)
diff --git a/src/compute/tests/mock_osm/WimconnectorIETFL2VPN.py b/src/compute/tests/mock_osm/WimconnectorIETFL2VPN.py
index 182115bad67a4fbe1eb04a83ed8d54be964568c8..d5ce65a1e4fcc5c47f90fb35e21023b28cb3c8ee 100644
--- a/src/compute/tests/mock_osm/WimconnectorIETFL2VPN.py
+++ b/src/compute/tests/mock_osm/WimconnectorIETFL2VPN.py
@@ -98,7 +98,7 @@ class WimconnectorIETFL2VPN(SdnConnectorBase):
                 self.wim["wim_url"], service_uuid
             )
             response = requests.get(servicepoint, auth=self.auth)
-
+            self.logger.warning('response.status_code={:s}'.format(str(response.status_code)))
             if response.status_code != requests.codes.ok:
                 raise SdnConnectorError(
                     "Unable to obtain connectivity servcice status",
diff --git a/src/compute/tests/mock_osm/__init__.py b/src/compute/tests/mock_osm/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/compute/tests/mock_osm/__init__.py
+++ b/src/compute/tests/mock_osm/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/compute/tests/test_unitary.py b/src/compute/tests/test_unitary.py
index 001999f1b9607f03cd393f5582cc08a504c0e9d2..2fa5869865213ccc39a759be2fd26ba16413fc3e 100644
--- a/src/compute/tests/test_unitary.py
+++ b/src/compute/tests/test_unitary.py
@@ -1,12 +1,28 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging, os, pytest, time
+from common.tests.MockService import MockService
+from common.tests.MockServicerImpl_Context import MockServicerImpl_Context
+from common.tests.MockServicerImpl_Service import MockServicerImpl_Service
 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
+from .Constants import (
+    SERVICE_CONNECTION_POINTS_1, SERVICE_CONNECTION_POINTS_2, SERVICE_TYPE, WIM_MAPPING, WIM_USERNAME, WIM_PASSWORD)
 
 LOGGER = logging.getLogger(__name__)
 LOGGER.setLevel(logging.DEBUG)
@@ -58,16 +74,19 @@ def compute_service_rest(mockservice):  # pylint: disable=redefined-outer-name
 @pytest.fixture(scope='session')
 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)
+    return MockOSM(wim_url, WIM_MAPPING, WIM_USERNAME, WIM_PASSWORD)
 
 def test_compute_create_connectivity_service_rest_api(osm_wim : MockOSM): # pylint: disable=redefined-outer-name
-    osm_wim.create_connectivity_service()
+    osm_wim.create_connectivity_service(SERVICE_TYPE, SERVICE_CONNECTION_POINTS_1)
 
 def test_compute_get_connectivity_service_status_rest_api(osm_wim : MockOSM): # pylint: disable=redefined-outer-name
-    osm_wim.get_connectivity_service_status()
+    service_uuid = list(osm_wim.conn_info.keys())[0] # this test adds a single service
+    osm_wim.get_connectivity_service_status(service_uuid)
 
 def test_compute_edit_connectivity_service_rest_api(osm_wim : MockOSM): # pylint: disable=redefined-outer-name
-    osm_wim.edit_connectivity_service()
+    service_uuid = list(osm_wim.conn_info.keys())[0] # this test adds a single service
+    osm_wim.edit_connectivity_service(service_uuid, SERVICE_CONNECTION_POINTS_2)
 
 def test_compute_delete_connectivity_service_rest_api(osm_wim : MockOSM): # pylint: disable=redefined-outer-name
-    osm_wim.delete_connectivity_service()
+    service_uuid = list(osm_wim.conn_info.keys())[0] # this test adds a single service
+    osm_wim.delete_connectivity_service(service_uuid)
diff --git a/src/context/.gitlab-ci.yml b/src/context/.gitlab-ci.yml
index a77be1af77766c9dbe6501b28db9a2c206e2864b..c0cbef8d7d8e2165e5d2f64c79fa2e6215ef8b96 100644
--- a/src/context/.gitlab-ci.yml
+++ b/src/context/.gitlab-ci.yml
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 # Build, tag and push the Docker image to the GitLab registry
 build context:
   variables:
@@ -43,7 +57,9 @@ unit test context:
     - sleep 10
     - docker ps -a
     - docker logs $IMAGE_NAME
-    - docker exec -i $IMAGE_NAME bash -c "coverage run -m pytest --log-level=INFO --verbose $IMAGE_NAME/tests/test_unitary.py --junitxml=/opt/results/${IMAGE_NAME}_report.xml; coverage xml -o /opt/results/${IMAGE_NAME}_coverage.xml; coverage report --include='${IMAGE_NAME}/*' --show-missing"
+    - docker exec -i $IMAGE_NAME bash -c "coverage run -m pytest --log-level=INFO --verbose $IMAGE_NAME/tests/test_unitary.py --junitxml=/opt/results/${IMAGE_NAME}_report.xml"
+    - docker exec -i $IMAGE_NAME bash -c "coverage xml -o /opt/results/${IMAGE_NAME}_coverage.xml"
+    - docker exec -i $IMAGE_NAME bash -c "coverage report --include='${IMAGE_NAME}/*' --show-missing"
   coverage: '/TOTAL\s+\d+\s+\d+\s+(\d+%)/'
   after_script:
     - docker rm -f $IMAGE_NAME
diff --git a/src/context/Config.py b/src/context/Config.py
index 1854d83bbe6f8015e7749de2c747e823ce573d95..328610fc81561f60d97b8ef3080930c4affce20e 100644
--- a/src/context/Config.py
+++ b/src/context/Config.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging
 
 # General settings
diff --git a/src/context/Dockerfile b/src/context/Dockerfile
index 57175a3ab66df2ff96dc8964be9b93ab4a159913..1599e588fb6d1dc6acae5f664208b4ce8d2d921b 100644
--- a/src/context/Dockerfile
+++ b/src/context/Dockerfile
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 FROM python:3-slim
 
 # Install dependencies
@@ -25,7 +39,7 @@ RUN mkdir -p /var/teraflow/context
 # Get Python packages per module
 COPY context/requirements.in context/requirements.in
 RUN pip-compile --output-file=context/requirements.txt context/requirements.in
-RUN python3 -m pip install -r context/requirements.in
+RUN python3 -m pip install -r context/requirements.txt
 
 # Add files into working directory
 COPY common/. common
diff --git a/src/context/__init__.py b/src/context/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/context/__init__.py
+++ b/src/context/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/context/client/ContextClient.py b/src/context/client/ContextClient.py
index 84e3031c2e9f26ab64ebd7a92a02c84f2d7cb9f6..bf58ea45db4ced7496657410f0c941584e4611e0 100644
--- a/src/context/client/ContextClient.py
+++ b/src/context/client/ContextClient.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 from typing import Iterator
 import grpc, logging
 from common.tools.client.RetryDecorator import retry, delay_exponential
@@ -11,6 +25,7 @@ from context.proto.context_pb2_grpc import ContextServiceStub
 LOGGER = logging.getLogger(__name__)
 MAX_RETRIES = 15
 DELAY_FUNCTION = delay_exponential(initial=0.01, increment=2.0, maximum=5.0)
+RETRY_DECORATOR = retry(max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
 
 class ContextClient:
     def __init__(self, address, port):
@@ -30,252 +45,252 @@ class ContextClient:
         self.channel = None
         self.stub = None
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def ListContextIds(self, request: Empty) -> ContextIdList:
         LOGGER.debug('ListContextIds request: {:s}'.format(str(request)))
         response = self.stub.ListContextIds(request)
         LOGGER.debug('ListContextIds result: {:s}'.format(str(response)))
         return response
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def ListContexts(self, request: Empty) -> ContextList:
         LOGGER.debug('ListContexts request: {:s}'.format(str(request)))
         response = self.stub.ListContexts(request)
         LOGGER.debug('ListContexts result: {:s}'.format(str(response)))
         return response
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def GetContext(self, request: ContextId) -> Context:
         LOGGER.debug('GetContext request: {:s}'.format(str(request)))
         response = self.stub.GetContext(request)
         LOGGER.debug('GetContext result: {:s}'.format(str(response)))
         return response
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def SetContext(self, request: Context) -> ContextId:
         LOGGER.debug('SetContext request: {:s}'.format(str(request)))
         response = self.stub.SetContext(request)
         LOGGER.debug('SetContext result: {:s}'.format(str(response)))
         return response
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def RemoveContext(self, request: ContextId) -> Empty:
         LOGGER.debug('RemoveContext request: {:s}'.format(str(request)))
         response = self.stub.RemoveContext(request)
         LOGGER.debug('RemoveContext result: {:s}'.format(str(response)))
         return response
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def GetContextEvents(self, request: Empty) -> Iterator[ContextEvent]:
         LOGGER.debug('GetContextEvents request: {:s}'.format(str(request)))
         response = self.stub.GetContextEvents(request)
         LOGGER.debug('GetContextEvents result: {:s}'.format(str(response)))
         return response
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def ListTopologyIds(self, request: ContextId) -> TopologyIdList:
         LOGGER.debug('ListTopologyIds request: {:s}'.format(str(request)))
         response = self.stub.ListTopologyIds(request)
         LOGGER.debug('ListTopologyIds result: {:s}'.format(str(response)))
         return response
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def ListTopologies(self, request: ContextId) -> TopologyList:
         LOGGER.debug('ListTopologies request: {:s}'.format(str(request)))
         response = self.stub.ListTopologies(request)
         LOGGER.debug('ListTopologies result: {:s}'.format(str(response)))
         return response
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def GetTopology(self, request: TopologyId) -> Topology:
         LOGGER.debug('GetTopology request: {:s}'.format(str(request)))
         response = self.stub.GetTopology(request)
         LOGGER.debug('GetTopology result: {:s}'.format(str(response)))
         return response
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def SetTopology(self, request: Topology) -> TopologyId:
         LOGGER.debug('SetTopology request: {:s}'.format(str(request)))
         response = self.stub.SetTopology(request)
         LOGGER.debug('SetTopology result: {:s}'.format(str(response)))
         return response
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def RemoveTopology(self, request: TopologyId) -> Empty:
         LOGGER.debug('RemoveTopology request: {:s}'.format(str(request)))
         response = self.stub.RemoveTopology(request)
         LOGGER.debug('RemoveTopology result: {:s}'.format(str(response)))
         return response
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def GetTopologyEvents(self, request: Empty) -> Iterator[TopologyEvent]:
         LOGGER.debug('GetTopologyEvents request: {:s}'.format(str(request)))
         response = self.stub.GetTopologyEvents(request)
         LOGGER.debug('GetTopologyEvents result: {:s}'.format(str(response)))
         return response
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def ListDeviceIds(self, request: Empty) -> DeviceIdList:
         LOGGER.debug('ListDeviceIds request: {:s}'.format(str(request)))
         response = self.stub.ListDeviceIds(request)
         LOGGER.debug('ListDeviceIds result: {:s}'.format(str(response)))
         return response
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def ListDevices(self, request: Empty) -> DeviceList:
         LOGGER.debug('ListDevices request: {:s}'.format(str(request)))
         response = self.stub.ListDevices(request)
         LOGGER.debug('ListDevices result: {:s}'.format(str(response)))
         return response
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def GetDevice(self, request: DeviceId) -> Device:
         LOGGER.debug('GetDevice request: {:s}'.format(str(request)))
         response = self.stub.GetDevice(request)
         LOGGER.debug('GetDevice result: {:s}'.format(str(response)))
         return response
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def SetDevice(self, request: Device) -> DeviceId:
         LOGGER.debug('SetDevice request: {:s}'.format(str(request)))
         response = self.stub.SetDevice(request)
         LOGGER.debug('SetDevice result: {:s}'.format(str(response)))
         return response
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def RemoveDevice(self, request: DeviceId) -> Empty:
         LOGGER.debug('RemoveDevice request: {:s}'.format(str(request)))
         response = self.stub.RemoveDevice(request)
         LOGGER.debug('RemoveDevice result: {:s}'.format(str(response)))
         return response
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def GetDeviceEvents(self, request: Empty) -> Iterator[DeviceEvent]:
         LOGGER.debug('GetDeviceEvents request: {:s}'.format(str(request)))
         response = self.stub.GetDeviceEvents(request)
         LOGGER.debug('GetDeviceEvents result: {:s}'.format(str(response)))
         return response
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def ListLinkIds(self, request: Empty) -> LinkIdList:
         LOGGER.debug('ListLinkIds request: {:s}'.format(str(request)))
         response = self.stub.ListLinkIds(request)
         LOGGER.debug('ListLinkIds result: {:s}'.format(str(response)))
         return response
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def ListLinks(self, request: Empty) -> LinkList:
         LOGGER.debug('ListLinks request: {:s}'.format(str(request)))
         response = self.stub.ListLinks(request)
         LOGGER.debug('ListLinks result: {:s}'.format(str(response)))
         return response
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def GetLink(self, request: LinkId) -> Link:
         LOGGER.debug('GetLink request: {:s}'.format(str(request)))
         response = self.stub.GetLink(request)
         LOGGER.debug('GetLink result: {:s}'.format(str(response)))
         return response
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def SetLink(self, request: Link) -> LinkId:
         LOGGER.debug('SetLink request: {:s}'.format(str(request)))
         response = self.stub.SetLink(request)
         LOGGER.debug('SetLink result: {:s}'.format(str(response)))
         return response
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def RemoveLink(self, request: LinkId) -> Empty:
         LOGGER.debug('RemoveLink request: {:s}'.format(str(request)))
         response = self.stub.RemoveLink(request)
         LOGGER.debug('RemoveLink result: {:s}'.format(str(response)))
         return response
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def GetLinkEvents(self, request: Empty) -> Iterator[LinkEvent]:
         LOGGER.debug('GetLinkEvents request: {:s}'.format(str(request)))
         response = self.stub.GetLinkEvents(request)
         LOGGER.debug('GetLinkEvents result: {:s}'.format(str(response)))
         return response
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def ListServiceIds(self, request: ContextId) -> ServiceIdList:
         LOGGER.debug('ListServiceIds request: {:s}'.format(str(request)))
         response = self.stub.ListServiceIds(request)
         LOGGER.debug('ListServiceIds result: {:s}'.format(str(response)))
         return response
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def ListServices(self, request: ContextId) -> ServiceList:
         LOGGER.debug('ListServices request: {:s}'.format(str(request)))
         response = self.stub.ListServices(request)
         LOGGER.debug('ListServices result: {:s}'.format(str(response)))
         return response
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def GetService(self, request: ServiceId) -> Service:
         LOGGER.debug('GetService request: {:s}'.format(str(request)))
         response = self.stub.GetService(request)
         LOGGER.debug('GetService result: {:s}'.format(str(response)))
         return response
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def SetService(self, request: Service) -> ServiceId:
         LOGGER.debug('SetService request: {:s}'.format(str(request)))
         response = self.stub.SetService(request)
         LOGGER.debug('SetService result: {:s}'.format(str(response)))
         return response
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def RemoveService(self, request: ServiceId) -> Empty:
         LOGGER.debug('RemoveService request: {:s}'.format(str(request)))
         response = self.stub.RemoveService(request)
         LOGGER.debug('RemoveService result: {:s}'.format(str(response)))
         return response
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def GetServiceEvents(self, request: Empty) -> Iterator[ServiceEvent]:
         LOGGER.debug('GetServiceEvents request: {:s}'.format(str(request)))
         response = self.stub.GetServiceEvents(request)
         LOGGER.debug('GetServiceEvents result: {:s}'.format(str(response)))
         return response
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def ListConnectionIds(self, request: ServiceId) -> ConnectionIdList:
         LOGGER.debug('ListConnectionIds request: {:s}'.format(str(request)))
         response = self.stub.ListConnectionIds(request)
         LOGGER.debug('ListConnectionIds result: {:s}'.format(str(response)))
         return response
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def ListConnections(self, request: ServiceId) -> ConnectionList:
         LOGGER.debug('ListConnections request: {:s}'.format(str(request)))
         response = self.stub.ListConnections(request)
         LOGGER.debug('ListConnections result: {:s}'.format(str(response)))
         return response
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def GetConnection(self, request: ConnectionId) -> Connection:
         LOGGER.debug('GetConnection request: {:s}'.format(str(request)))
         response = self.stub.GetConnection(request)
         LOGGER.debug('GetConnection result: {:s}'.format(str(response)))
         return response
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def SetConnection(self, request: Connection) -> ConnectionId:
         LOGGER.debug('SetConnection request: {:s}'.format(str(request)))
         response = self.stub.SetConnection(request)
         LOGGER.debug('SetConnection result: {:s}'.format(str(response)))
         return response
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def RemoveConnection(self, request: ConnectionId) -> Empty:
         LOGGER.debug('RemoveConnection request: {:s}'.format(str(request)))
         response = self.stub.RemoveConnection(request)
         LOGGER.debug('RemoveConnection result: {:s}'.format(str(response)))
         return response
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def GetConnectionEvents(self, request: Empty) -> Iterator[ConnectionEvent]:
         LOGGER.debug('GetConnectionEvents request: {:s}'.format(str(request)))
         response = self.stub.GetConnectionEvents(request)
diff --git a/src/context/client/EventsCollector.py b/src/context/client/EventsCollector.py
new file mode 100644
index 0000000000000000000000000000000000000000..3022df0a6f4e56cdbf6444b90eeeb41b43277c53
--- /dev/null
+++ b/src/context/client/EventsCollector.py
@@ -0,0 +1,95 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import grpc, logging, queue, threading
+from common.tools.grpc.Tools import grpc_message_to_json_string
+from context.client.ContextClient import ContextClient
+from context.proto.context_pb2 import Empty
+
+LOGGER = logging.getLogger(__name__)
+LOGGER.setLevel(logging.DEBUG)
+
+class EventsCollector:
+    def __init__(
+        self, context_client_grpc : ContextClient, log_events_received=False
+    ) -> None:
+        self._events_queue = queue.Queue()
+        self._log_events_received = log_events_received
+
+        self._context_stream    = context_client_grpc.GetContextEvents(Empty())
+        self._topology_stream   = context_client_grpc.GetTopologyEvents(Empty())
+        self._device_stream     = context_client_grpc.GetDeviceEvents(Empty())
+        self._link_stream       = context_client_grpc.GetLinkEvents(Empty())
+        self._service_stream    = context_client_grpc.GetServiceEvents(Empty())
+        self._connection_stream = context_client_grpc.GetConnectionEvents(Empty())
+
+        self._context_thread    = threading.Thread(target=self._collect, args=(self._context_stream   ,), daemon=False)
+        self._topology_thread   = threading.Thread(target=self._collect, args=(self._topology_stream  ,), daemon=False)
+        self._device_thread     = threading.Thread(target=self._collect, args=(self._device_stream    ,), daemon=False)
+        self._link_thread       = threading.Thread(target=self._collect, args=(self._link_stream      ,), daemon=False)
+        self._service_thread    = threading.Thread(target=self._collect, args=(self._service_stream   ,), daemon=False)
+        self._connection_thread = threading.Thread(target=self._collect, args=(self._connection_stream,), daemon=False)
+
+    def _collect(self, events_stream) -> None:
+        try:
+            for event in events_stream:
+                if self._log_events_received:
+                    LOGGER.info('[_collect] event: {:s}'.format(grpc_message_to_json_string(event)))
+                self._events_queue.put_nowait(event)
+        except grpc.RpcError as e:
+            if e.code() != grpc.StatusCode.CANCELLED: # pylint: disable=no-member
+                raise # pragma: no cover
+
+    def start(self):
+        self._context_thread.start()
+        self._topology_thread.start()
+        self._device_thread.start()
+        self._link_thread.start()
+        self._service_thread.start()
+        self._connection_thread.start()
+
+    def get_event(self, block : bool = True, timeout : float = 0.1):
+        try:
+            return self._events_queue.get(block=block, timeout=timeout)
+        except queue.Empty: # pylint: disable=catching-non-exception
+            return None
+
+    def get_events(self, block : bool = True, timeout : float = 0.1, count : int = None):
+        events = []
+        if count is None:
+            while True:
+                event = self.get_event(block=block, timeout=timeout)
+                if event is None: break
+                events.append(event)
+        else:
+            for _ in range(count):
+                event = self.get_event(block=block, timeout=timeout)
+                if event is None: continue
+                events.append(event)
+        return sorted(events, key=lambda e: e.event.timestamp)
+
+    def stop(self):
+        self._context_stream.cancel()
+        self._topology_stream.cancel()
+        self._device_stream.cancel()
+        self._link_stream.cancel()
+        self._service_stream.cancel()
+        self._connection_stream.cancel()
+
+        self._context_thread.join()
+        self._topology_thread.join()
+        self._device_thread.join()
+        self._link_thread.join()
+        self._service_thread.join()
+        self._connection_thread.join()
diff --git a/src/context/client/__init__.py b/src/context/client/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/context/client/__init__.py
+++ b/src/context/client/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/context/proto/__init__.py b/src/context/proto/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/context/proto/__init__.py
+++ b/src/context/proto/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/context/requirements.in b/src/context/requirements.in
index 86097934b48d8465bc9092881f9320ec9ebe54f4..4c32f5c71c828a57ceeb90f2739e3b551c99abfe 100644
--- a/src/context/requirements.in
+++ b/src/context/requirements.in
@@ -1,9 +1,11 @@
-flask-restful
-grpcio-health-checking
-grpcio
-prometheus-client
-pytest
-pytest-benchmark
-redis
-requests
-coverage
+Flask==2.0.2
+Flask-RESTful==0.3.9
+grpcio==1.43.0
+grpcio-health-checking==1.43.0
+prometheus-client==0.13.0
+protobuf==3.19.3
+pytest==6.2.5
+pytest-benchmark==3.4.1
+redis==4.1.2
+requests==2.27.1
+coverage==6.3
diff --git a/src/context/requirements.txt b/src/context/requirements.txt
new file mode 100644
index 0000000000000000000000000000000000000000..1f0e17131268493dc57951701dba86aba0fffd92
--- /dev/null
+++ b/src/context/requirements.txt
@@ -0,0 +1,72 @@
+#
+# This file is autogenerated by pip-compile with python 3.9
+# To update, run:
+#
+#    pip-compile src/context/requirements.in
+#
+aniso8601==9.0.1
+    # via flask-restful
+attrs==21.2.0
+    # via pytest
+certifi==2021.5.30
+    # via requests
+charset-normalizer==2.0.6
+    # via requests
+click==8.0.1
+    # via flask
+flask==2.0.1
+    # via flask-restful
+flask-restful==0.3.9
+    # via -r src/context/requirements.in
+grpcio==1.41.0
+    # via
+    #   -r src/context/requirements.in
+    #   grpcio-health-checking
+grpcio-health-checking==1.41.0
+    # via -r src/context/requirements.in
+idna==3.2
+    # via requests
+iniconfig==1.1.1
+    # via pytest
+itsdangerous==2.0.1
+    # via flask
+jinja2==3.0.1
+    # via flask
+markupsafe==2.0.1
+    # via jinja2
+packaging==21.0
+    # via pytest
+pluggy==1.0.0
+    # via pytest
+prometheus-client==0.11.0
+    # via -r src/context/requirements.in
+protobuf==3.18.0
+    # via grpcio-health-checking
+py==1.10.0
+    # via pytest
+py-cpuinfo==8.0.0
+    # via pytest-benchmark
+pyparsing==2.4.7
+    # via packaging
+pytest==6.2.5
+    # via
+    #   -r src/context/requirements.in
+    #   pytest-benchmark
+pytest-benchmark==3.4.1
+    # via -r src/context/requirements.in
+pytz==2021.1
+    # via flask-restful
+redis==3.5.3
+    # via -r src/context/requirements.in
+requests==2.26.0
+    # via -r src/context/requirements.in
+six==1.16.0
+    # via
+    #   flask-restful
+    #   grpcio
+toml==0.10.2
+    # via pytest
+urllib3==1.26.7
+    # via requests
+werkzeug==2.0.1
+    # via flask
diff --git a/src/context/service/Populate.py b/src/context/service/Populate.py
index 91460b7ce52ae84e76d2781672f636dc86187f99..f4630182d864a891095caa6689dde9f656ea1c0e 100644
--- a/src/context/service/Populate.py
+++ b/src/context/service/Populate.py
@@ -1,35 +1,49 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import copy
 from context.client.ContextClient import ContextClient
 from context.proto.context_pb2 import Connection, Context, Device, Link, Service, Topology
-from context.tests.example_objects import (
-    CONNECTION_DEV1_DEV3, CONTEXT, TOPOLOGY, DEVICE1, DEVICE1_ID, DEVICE2, DEVICE2_ID, DEVICE3, DEVICE3_ID,
-    LINK_DEV1_DEV2, LINK_DEV1_DEV2_ID, LINK_DEV1_DEV3, LINK_DEV1_DEV3_ID, LINK_DEV2_DEV3, LINK_DEV2_DEV3_ID,
-    SERVICE_DEV1_DEV2, SERVICE_DEV1_DEV3, SERVICE_DEV2_DEV3)
+from context.tests.Objects import (
+    CONNECTION_R1_R3, CONTEXT, TOPOLOGY, DEVICE_R1, DEVICE_R1_ID, DEVICE_R2, DEVICE_R2_ID, DEVICE_R3, DEVICE_R3_ID,
+    LINK_R1_R2, LINK_R1_R2_ID, LINK_R1_R3, LINK_R1_R3_ID, LINK_R2_R3, LINK_R2_R3_ID, SERVICE_R1_R2, SERVICE_R1_R3,
+    SERVICE_R2_R3)
 
 def populate(address, port):
     client = ContextClient(address=address, port=port)
 
     client.SetContext(Context(**CONTEXT))
     client.SetTopology(Topology(**TOPOLOGY))
-    client.SetDevice(Device(**DEVICE1))
-    client.SetDevice(Device(**DEVICE2))
-    client.SetDevice(Device(**DEVICE3))
+    client.SetDevice(Device(**DEVICE_R1))
+    client.SetDevice(Device(**DEVICE_R2))
+    client.SetDevice(Device(**DEVICE_R3))
 
-    client.SetLink(Link(**LINK_DEV1_DEV2))
-    client.SetLink(Link(**LINK_DEV1_DEV3))
-    client.SetLink(Link(**LINK_DEV2_DEV3))
+    client.SetLink(Link(**LINK_R1_R2))
+    client.SetLink(Link(**LINK_R1_R3))
+    client.SetLink(Link(**LINK_R2_R3))
 
     TOPOLOGY_WITH_DEVICES_AND_LINKS = copy.deepcopy(TOPOLOGY)
-    TOPOLOGY_WITH_DEVICES_AND_LINKS['device_ids'].append(DEVICE1_ID)
-    TOPOLOGY_WITH_DEVICES_AND_LINKS['device_ids'].append(DEVICE2_ID)
-    TOPOLOGY_WITH_DEVICES_AND_LINKS['device_ids'].append(DEVICE3_ID)
-    TOPOLOGY_WITH_DEVICES_AND_LINKS['link_ids'].append(LINK_DEV1_DEV2_ID)
-    TOPOLOGY_WITH_DEVICES_AND_LINKS['link_ids'].append(LINK_DEV1_DEV3_ID)
-    TOPOLOGY_WITH_DEVICES_AND_LINKS['link_ids'].append(LINK_DEV2_DEV3_ID)
+    TOPOLOGY_WITH_DEVICES_AND_LINKS['device_ids'].append(DEVICE_R1_ID)
+    TOPOLOGY_WITH_DEVICES_AND_LINKS['device_ids'].append(DEVICE_R2_ID)
+    TOPOLOGY_WITH_DEVICES_AND_LINKS['device_ids'].append(DEVICE_R3_ID)
+    TOPOLOGY_WITH_DEVICES_AND_LINKS['link_ids'].append(LINK_R1_R2_ID)
+    TOPOLOGY_WITH_DEVICES_AND_LINKS['link_ids'].append(LINK_R1_R3_ID)
+    TOPOLOGY_WITH_DEVICES_AND_LINKS['link_ids'].append(LINK_R2_R3_ID)
     client.SetTopology(Topology(**TOPOLOGY_WITH_DEVICES_AND_LINKS))
 
-    client.SetService(Service(**SERVICE_DEV1_DEV2))
-    client.SetService(Service(**SERVICE_DEV2_DEV3))
+    client.SetService(Service(**SERVICE_R1_R2))
+    client.SetService(Service(**SERVICE_R2_R3))
 
-    client.SetService(Service(**SERVICE_DEV1_DEV3))
-    client.SetConnection(Connection(**CONNECTION_DEV1_DEV3))
+    client.SetService(Service(**SERVICE_R1_R3))
+    client.SetConnection(Connection(**CONNECTION_R1_R3))
diff --git a/src/context/service/__init__.py b/src/context/service/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/context/service/__init__.py
+++ b/src/context/service/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/context/service/__main__.py b/src/context/service/__main__.py
index 495c203c93ca7df2032cf54c44ba4cefa58d3324..180a1f44cb6a37b487e6bce0a13706952ff73bc2 100644
--- a/src/context/service/__main__.py
+++ b/src/context/service/__main__.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging, signal, sys, threading
 from prometheus_client import start_http_server
 from common.Settings import get_setting
@@ -8,10 +22,10 @@ from common.message_broker.MessageBroker import MessageBroker
 from context.Config import (
     GRPC_SERVICE_PORT, GRPC_MAX_WORKERS, GRPC_GRACE_PERIOD, LOG_LEVEL, POPULATE_FAKE_DATA, RESTAPI_SERVICE_PORT,
     RESTAPI_BASE_URL, METRICS_PORT)
-from context.service.Populate import populate
-from context.service.grpc_server.ContextService import ContextService
-from context.service.rest_server.Server import Server
-from context.service.rest_server.Resources import RESOURCES
+from .grpc_server.ContextService import ContextService
+from .rest_server.Resources import RESOURCES
+from .rest_server.Server import Server
+from .Populate import populate
 
 terminate = threading.Event()
 LOGGER = None
diff --git a/src/context/service/database/ConfigModel.py b/src/context/service/database/ConfigModel.py
index d97cdb7dfe6594a59be10427ed52341346c19a97..234e155f026164c14924b67a33a4db4812bae620 100644
--- a/src/context/service/database/ConfigModel.py
+++ b/src/context/service/database/ConfigModel.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import functools, logging, operator
 from enum import Enum
 from typing import Dict, List, Optional, Tuple, Union
diff --git a/src/context/service/database/ConnectionModel.py b/src/context/service/database/ConnectionModel.py
index 61033841c02bd5ad4614602605db9fd144564cc8..dfcab3f8a7f9adf5e0c5773d3672922157d1f2dd 100644
--- a/src/context/service/database/ConnectionModel.py
+++ b/src/context/service/database/ConnectionModel.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging, operator
 from typing import Dict, List, Optional, Set, Tuple, Union
 from common.orm.Database import Database
diff --git a/src/context/service/database/ConstraintModel.py b/src/context/service/database/ConstraintModel.py
index 4d11b123193e9cb4ce2c03cbfac6a3604947fbe2..6cd8b31fac64d10f0d11f9b1d5abd3bda1bc049e 100644
--- a/src/context/service/database/ConstraintModel.py
+++ b/src/context/service/database/ConstraintModel.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging, operator
 from typing import Dict, List, Tuple, Union
 from common.orm.Database import Database
diff --git a/src/context/service/database/ContextModel.py b/src/context/service/database/ContextModel.py
index 77243c29f5306b0137c75f71b435a3fc8036353e..a12e6669dbd9c506655fd3e2265dab7b25ca90dd 100644
--- a/src/context/service/database/ContextModel.py
+++ b/src/context/service/database/ContextModel.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging
 from typing import Dict, List
 from common.orm.fields.PrimaryKeyField import PrimaryKeyField
diff --git a/src/context/service/database/DeviceModel.py b/src/context/service/database/DeviceModel.py
index d005292acfd4cf20619548dbfa5b48a08780ec2f..b5faaf77d083e2cb6ef140368a383dd6389dfa18 100644
--- a/src/context/service/database/DeviceModel.py
+++ b/src/context/service/database/DeviceModel.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import functools, logging
 from enum import Enum
 from typing import Dict, List
diff --git a/src/context/service/database/EndPointModel.py b/src/context/service/database/EndPointModel.py
index f1239ac124df42a74575e15d247c98b6dd2a88bb..abeeb1b690b97e47772e3bf38d77016569bf55dc 100644
--- a/src/context/service/database/EndPointModel.py
+++ b/src/context/service/database/EndPointModel.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging
 from typing import Dict, List
 from common.orm.Database import Database
diff --git a/src/context/service/database/Events.py b/src/context/service/database/Events.py
index 2737b9fb7d400d54e2aad9e943ee1c29461e82ff..e87d0a0fb88a8a879f5de3483efa0554840e5683 100644
--- a/src/context/service/database/Events.py
+++ b/src/context/service/database/Events.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import json, time
 from typing import Dict
 from common.message_broker.Message import Message
diff --git a/src/context/service/database/KpiSampleType.py b/src/context/service/database/KpiSampleType.py
index 50cbcd8a6641f1a598b2153cac840d6259462f96..21662779727b5ab6af868e2ad398eb4760c4602e 100644
--- a/src/context/service/database/KpiSampleType.py
+++ b/src/context/service/database/KpiSampleType.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import functools
 from enum import Enum
 from context.proto.kpi_sample_types_pb2 import KpiSampleType
diff --git a/src/context/service/database/LinkModel.py b/src/context/service/database/LinkModel.py
index 48d67bfa6134818d3ba24fd304507b7d14a37e0f..742044b9758df297413ad2d0318520c825e8b738 100644
--- a/src/context/service/database/LinkModel.py
+++ b/src/context/service/database/LinkModel.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging, operator
 from typing import Dict, List
 from common.orm.fields.PrimaryKeyField import PrimaryKeyField
diff --git a/src/context/service/database/RelationModels.py b/src/context/service/database/RelationModels.py
index 8da0862ee744c4015a27a3eb3bdafad2a8e79a26..20e0fc4501f5c81ef4d600dde5067ee5a5f26331 100644
--- a/src/context/service/database/RelationModels.py
+++ b/src/context/service/database/RelationModels.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging
 from common.orm.fields.ForeignKeyField import ForeignKeyField
 from common.orm.fields.PrimaryKeyField import PrimaryKeyField
diff --git a/src/context/service/database/ServiceModel.py b/src/context/service/database/ServiceModel.py
index f6bd2e9c47f77538fac6d65aa0e02ca720126abb..86376f775c54280d06e059aceadc23515d3589e5 100644
--- a/src/context/service/database/ServiceModel.py
+++ b/src/context/service/database/ServiceModel.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import functools, logging, operator
 from enum import Enum
 from typing import Dict, List
diff --git a/src/context/service/database/Tools.py b/src/context/service/database/Tools.py
index 36ffbcd46fcf686371b0799445ce4f9ce5b75838..43bb71bd90582644c67d3ca528611eae937b6460 100644
--- a/src/context/service/database/Tools.py
+++ b/src/context/service/database/Tools.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import hashlib, re
 from enum import Enum
 from typing import Dict, List, Tuple, Union
diff --git a/src/context/service/database/TopologyModel.py b/src/context/service/database/TopologyModel.py
index fc1f2d241e71a4e14511756928ed0b97a0499209..5909c7a2c63d05f2cbde7f0d8555e63587e96682 100644
--- a/src/context/service/database/TopologyModel.py
+++ b/src/context/service/database/TopologyModel.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging, operator
 from typing import Dict, List
 from common.orm.fields.ForeignKeyField import ForeignKeyField
diff --git a/src/context/service/database/__init__.py b/src/context/service/database/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/context/service/database/__init__.py
+++ b/src/context/service/database/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/context/service/grpc_server/Constants.py b/src/context/service/grpc_server/Constants.py
index aff7711e221bdfae70e720d0040f8e5bedcbeec7..b9676397cf2568ad27ba755b6d0a175af0670518 100644
--- a/src/context/service/grpc_server/Constants.py
+++ b/src/context/service/grpc_server/Constants.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 TOPIC_CONTEXT    = 'context'
 TOPIC_TOPOLOGY   = 'topology'
 TOPIC_DEVICE     = 'device'
diff --git a/src/context/service/grpc_server/ContextService.py b/src/context/service/grpc_server/ContextService.py
index 9f1028dc9eed4d172c49f9b9f916572093c850f0..87ca94a70aa2e1733b8ec443c70a4623d2e0c471 100644
--- a/src/context/service/grpc_server/ContextService.py
+++ b/src/context/service/grpc_server/ContextService.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import grpc
 import logging
 from concurrent import futures
diff --git a/src/context/service/grpc_server/ContextServiceServicerImpl.py b/src/context/service/grpc_server/ContextServiceServicerImpl.py
index 73c61f355e01eaa9a2e3abd1dd9a2d1b779b22da..8e40592159a26ac853c9fa4af69f8064a2e55104 100644
--- a/src/context/service/grpc_server/ContextServiceServicerImpl.py
+++ b/src/context/service/grpc_server/ContextServiceServicerImpl.py
@@ -1,4 +1,18 @@
-import grpc, json, logging, operator
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import grpc, json, logging, operator, threading
 from typing import Iterator, List, Set, Tuple
 from common.message_broker.MessageBroker import MessageBroker
 from common.orm.Database import Database
@@ -27,7 +41,7 @@ from context.service.database.RelationModels import (
 from context.service.database.ServiceModel import (
     ServiceModel, grpc_to_enum__service_status, grpc_to_enum__service_type)
 from context.service.database.TopologyModel import TopologyModel
-from context.service.grpc_server.Constants import (
+from .Constants import (
     CONSUME_TIMEOUT, TOPIC_CONNECTION, TOPIC_CONTEXT, TOPIC_DEVICE, TOPIC_LINK, TOPIC_SERVICE, TOPIC_TOPOLOGY)
 
 LOGGER = logging.getLogger(__name__)
@@ -46,6 +60,7 @@ METRICS = create_metrics(SERVICE_NAME, METHOD_NAMES)
 class ContextServiceServicerImpl(ContextServiceServicer):
     def __init__(self, database : Database, messagebroker : MessageBroker):
         LOGGER.debug('Creating Servicer...')
+        self.lock = threading.Lock()
         self.database = database
         self.messagebroker = messagebroker
         LOGGER.debug('Servicer Created')
@@ -55,71 +70,76 @@ class ContextServiceServicerImpl(ContextServiceServicer):
 
     @safe_and_metered_rpc_method(METRICS, LOGGER)
     def ListContextIds(self, request: Empty, context : grpc.ServicerContext) -> ContextIdList:
-        db_contexts : List[ContextModel] = get_all_objects(self.database, ContextModel)
-        db_contexts = sorted(db_contexts, key=operator.attrgetter('pk'))
-        return ContextIdList(context_ids=[db_context.dump_id() for db_context in db_contexts])
+        with self.lock:
+            db_contexts : List[ContextModel] = get_all_objects(self.database, ContextModel)
+            db_contexts = sorted(db_contexts, key=operator.attrgetter('pk'))
+            return ContextIdList(context_ids=[db_context.dump_id() for db_context in db_contexts])
 
     @safe_and_metered_rpc_method(METRICS, LOGGER)
     def ListContexts(self, request: Empty, context : grpc.ServicerContext) -> ContextList:
-        db_contexts : List[ContextModel] = get_all_objects(self.database, ContextModel)
-        db_contexts = sorted(db_contexts, key=operator.attrgetter('pk'))
-        return ContextList(contexts=[db_context.dump() for db_context in db_contexts])
+        with self.lock:
+            db_contexts : List[ContextModel] = get_all_objects(self.database, ContextModel)
+            db_contexts = sorted(db_contexts, key=operator.attrgetter('pk'))
+            return ContextList(contexts=[db_context.dump() for db_context in db_contexts])
 
     @safe_and_metered_rpc_method(METRICS, LOGGER)
     def GetContext(self, request: ContextId, context : grpc.ServicerContext) -> Context:
-        context_uuid = request.context_uuid.uuid
-        db_context : ContextModel = get_object(self.database, ContextModel, context_uuid)
-        return Context(**db_context.dump(include_services=True, include_topologies=True))
+        with self.lock:
+            context_uuid = request.context_uuid.uuid
+            db_context : ContextModel = get_object(self.database, ContextModel, context_uuid)
+            return Context(**db_context.dump(include_services=True, include_topologies=True))
 
     @safe_and_metered_rpc_method(METRICS, LOGGER)
     def SetContext(self, request: Context, context : grpc.ServicerContext) -> ContextId:
-        context_uuid = request.context_id.context_uuid.uuid
-
-        for i,topology_id in enumerate(request.topology_ids):
-            topology_context_uuid = topology_id.context_id.context_uuid.uuid
-            if topology_context_uuid != context_uuid:
-                raise InvalidArgumentException(
-                    'request.topology_ids[{:d}].context_id.context_uuid.uuid'.format(i), topology_context_uuid,
-                    ['should be == {:s}({:s})'.format('request.context_id.context_uuid.uuid', context_uuid)])
-
-        for i,service_id in enumerate(request.service_ids):
-            service_context_uuid = service_id.context_id.context_uuid.uuid
-            if service_context_uuid != context_uuid:
-                raise InvalidArgumentException(
-                    'request.service_ids[{:d}].context_id.context_uuid.uuid'.format(i), service_context_uuid,
-                    ['should be == {:s}({:s})'.format('request.context_id.context_uuid.uuid', context_uuid)])
-
-        result : Tuple[ContextModel, bool] = update_or_create_object(
-            self.database, ContextModel, context_uuid, {'context_uuid': context_uuid})
-        db_context, updated = result
-
-        for i,topology_id in enumerate(request.topology_ids):
-            topology_context_uuid = topology_id.context_id.context_uuid.uuid
-            topology_uuid = topology_id.topology_uuid.uuid
-            get_object(self.database, TopologyModel, [context_uuid, topology_uuid]) # just to confirm it exists
-
-        for i,service_id in enumerate(request.service_ids):
-            service_context_uuid = service_id.context_id.context_uuid.uuid
-            service_uuid = service_id.service_uuid.uuid
-            get_object(self.database, ServiceModel, [context_uuid, service_uuid]) # just to confirm it exists
-
-        event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE
-        dict_context_id = db_context.dump_id()
-        notify_event(self.messagebroker, TOPIC_CONTEXT, event_type, {'context_id': dict_context_id})
-        return ContextId(**dict_context_id)
+        with self.lock:
+            context_uuid = request.context_id.context_uuid.uuid
+
+            for i,topology_id in enumerate(request.topology_ids):
+                topology_context_uuid = topology_id.context_id.context_uuid.uuid
+                if topology_context_uuid != context_uuid:
+                    raise InvalidArgumentException(
+                        'request.topology_ids[{:d}].context_id.context_uuid.uuid'.format(i), topology_context_uuid,
+                        ['should be == {:s}({:s})'.format('request.context_id.context_uuid.uuid', context_uuid)])
+
+            for i,service_id in enumerate(request.service_ids):
+                service_context_uuid = service_id.context_id.context_uuid.uuid
+                if service_context_uuid != context_uuid:
+                    raise InvalidArgumentException(
+                        'request.service_ids[{:d}].context_id.context_uuid.uuid'.format(i), service_context_uuid,
+                        ['should be == {:s}({:s})'.format('request.context_id.context_uuid.uuid', context_uuid)])
+
+            result : Tuple[ContextModel, bool] = update_or_create_object(
+                self.database, ContextModel, context_uuid, {'context_uuid': context_uuid})
+            db_context, updated = result
+
+            for i,topology_id in enumerate(request.topology_ids):
+                topology_context_uuid = topology_id.context_id.context_uuid.uuid
+                topology_uuid = topology_id.topology_uuid.uuid
+                get_object(self.database, TopologyModel, [context_uuid, topology_uuid]) # just to confirm it exists
+
+            for i,service_id in enumerate(request.service_ids):
+                service_context_uuid = service_id.context_id.context_uuid.uuid
+                service_uuid = service_id.service_uuid.uuid
+                get_object(self.database, ServiceModel, [context_uuid, service_uuid]) # just to confirm it exists
+
+            event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE
+            dict_context_id = db_context.dump_id()
+            notify_event(self.messagebroker, TOPIC_CONTEXT, event_type, {'context_id': dict_context_id})
+            return ContextId(**dict_context_id)
 
     @safe_and_metered_rpc_method(METRICS, LOGGER)
     def RemoveContext(self, request: ContextId, context : grpc.ServicerContext) -> Empty:
-        context_uuid = request.context_uuid.uuid
-        db_context = ContextModel(self.database, context_uuid, auto_load=False)
-        found = db_context.load()
-        if not found: return Empty()
+        with self.lock:
+            context_uuid = request.context_uuid.uuid
+            db_context = ContextModel(self.database, context_uuid, auto_load=False)
+            found = db_context.load()
+            if not found: return Empty()
 
-        dict_context_id = db_context.dump_id()
-        db_context.delete()
-        event_type = EventTypeEnum.EVENTTYPE_REMOVE
-        notify_event(self.messagebroker, TOPIC_CONTEXT, event_type, {'context_id': dict_context_id})
-        return Empty()
+            dict_context_id = db_context.dump_id()
+            db_context.delete()
+            event_type = EventTypeEnum.EVENTTYPE_REMOVE
+            notify_event(self.messagebroker, TOPIC_CONTEXT, event_type, {'context_id': dict_context_id})
+            return Empty()
 
     @safe_and_metered_rpc_method(METRICS, LOGGER)
     def GetContextEvents(self, request: Empty, context : grpc.ServicerContext) -> Iterator[ContextEvent]:
@@ -131,74 +151,79 @@ class ContextServiceServicerImpl(ContextServiceServicer):
 
     @safe_and_metered_rpc_method(METRICS, LOGGER)
     def ListTopologyIds(self, request: ContextId, context : grpc.ServicerContext) -> TopologyIdList:
-        context_uuid = request.context_uuid.uuid
-        db_context : ContextModel = get_object(self.database, ContextModel, context_uuid)
-        db_topologies : Set[TopologyModel] = get_related_objects(db_context, TopologyModel)
-        db_topologies = sorted(db_topologies, key=operator.attrgetter('pk'))
-        return TopologyIdList(topology_ids=[db_topology.dump_id() for db_topology in db_topologies])
+        with self.lock:
+            context_uuid = request.context_uuid.uuid
+            db_context : ContextModel = get_object(self.database, ContextModel, context_uuid)
+            db_topologies : Set[TopologyModel] = get_related_objects(db_context, TopologyModel)
+            db_topologies = sorted(db_topologies, key=operator.attrgetter('pk'))
+            return TopologyIdList(topology_ids=[db_topology.dump_id() for db_topology in db_topologies])
 
     @safe_and_metered_rpc_method(METRICS, LOGGER)
     def ListTopologies(self, request: ContextId, context : grpc.ServicerContext) -> TopologyList:
-        context_uuid = request.context_uuid.uuid
-        db_context : ContextModel = get_object(self.database, ContextModel, context_uuid)
-        db_topologies : Set[TopologyModel] = get_related_objects(db_context, TopologyModel)
-        db_topologies = sorted(db_topologies, key=operator.attrgetter('pk'))
-        return TopologyList(topologies=[db_topology.dump() for db_topology in db_topologies])
+        with self.lock:
+            context_uuid = request.context_uuid.uuid
+            db_context : ContextModel = get_object(self.database, ContextModel, context_uuid)
+            db_topologies : Set[TopologyModel] = get_related_objects(db_context, TopologyModel)
+            db_topologies = sorted(db_topologies, key=operator.attrgetter('pk'))
+            return TopologyList(topologies=[db_topology.dump() for db_topology in db_topologies])
 
     @safe_and_metered_rpc_method(METRICS, LOGGER)
     def GetTopology(self, request: TopologyId, context : grpc.ServicerContext) -> Topology:
-        str_key = key_to_str([request.context_id.context_uuid.uuid, request.topology_uuid.uuid])
-        db_topology : TopologyModel = get_object(self.database, TopologyModel, str_key)
-        return Topology(**db_topology.dump(include_devices=True, include_links=True))
+        with self.lock:
+            str_key = key_to_str([request.context_id.context_uuid.uuid, request.topology_uuid.uuid])
+            db_topology : TopologyModel = get_object(self.database, TopologyModel, str_key)
+            return Topology(**db_topology.dump(include_devices=True, include_links=True))
 
     @safe_and_metered_rpc_method(METRICS, LOGGER)
     def SetTopology(self, request: Topology, context : grpc.ServicerContext) -> TopologyId:
-        context_uuid = request.topology_id.context_id.context_uuid.uuid
-        db_context : ContextModel = get_object(self.database, ContextModel, context_uuid)
-
-        topology_uuid = request.topology_id.topology_uuid.uuid
-        str_topology_key = key_to_str([context_uuid, topology_uuid])
-        result : Tuple[TopologyModel, bool] = update_or_create_object(
-            self.database, TopologyModel, str_topology_key, {'context_fk': db_context, 'topology_uuid': topology_uuid})
-        db_topology,updated = result
-
-        for device_id in request.device_ids:
-            device_uuid = device_id.device_uuid.uuid
-            db_device = get_object(self.database, DeviceModel, device_uuid)
-            str_topology_device_key = key_to_str([str_topology_key, device_uuid], separator='--')
-            result : Tuple[TopologyDeviceModel, bool] = update_or_create_object(
-                self.database, TopologyDeviceModel, str_topology_device_key,
-                {'topology_fk': db_topology, 'device_fk': db_device})
-            #db_topology_device,topology_device_updated = result
-
-        for link_id in request.link_ids:
-            link_uuid = link_id.link_uuid.uuid
-            db_link = get_object(self.database, LinkModel, link_uuid)
-
-            str_topology_link_key = key_to_str([str_topology_key, link_uuid], separator='--')
-            result : Tuple[TopologyLinkModel, bool] = update_or_create_object(
-                self.database, TopologyLinkModel, str_topology_link_key,
-                {'topology_fk': db_topology, 'link_fk': db_link})
-            #db_topology_link,topology_link_updated = result
-
-        event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE
-        dict_topology_id = db_topology.dump_id()
-        notify_event(self.messagebroker, TOPIC_TOPOLOGY, event_type, {'topology_id': dict_topology_id})
-        return TopologyId(**dict_topology_id)
+        with self.lock:
+            context_uuid = request.topology_id.context_id.context_uuid.uuid
+            db_context : ContextModel = get_object(self.database, ContextModel, context_uuid)
+
+            topology_uuid = request.topology_id.topology_uuid.uuid
+            str_topology_key = key_to_str([context_uuid, topology_uuid])
+            result : Tuple[TopologyModel, bool] = update_or_create_object(
+                self.database, TopologyModel, str_topology_key, {'context_fk': db_context, 'topology_uuid': topology_uuid})
+            db_topology,updated = result
+
+            for device_id in request.device_ids:
+                device_uuid = device_id.device_uuid.uuid
+                db_device = get_object(self.database, DeviceModel, device_uuid)
+                str_topology_device_key = key_to_str([str_topology_key, device_uuid], separator='--')
+                result : Tuple[TopologyDeviceModel, bool] = update_or_create_object(
+                    self.database, TopologyDeviceModel, str_topology_device_key,
+                    {'topology_fk': db_topology, 'device_fk': db_device})
+                #db_topology_device,topology_device_updated = result
+
+            for link_id in request.link_ids:
+                link_uuid = link_id.link_uuid.uuid
+                db_link = get_object(self.database, LinkModel, link_uuid)
+
+                str_topology_link_key = key_to_str([str_topology_key, link_uuid], separator='--')
+                result : Tuple[TopologyLinkModel, bool] = update_or_create_object(
+                    self.database, TopologyLinkModel, str_topology_link_key,
+                    {'topology_fk': db_topology, 'link_fk': db_link})
+                #db_topology_link,topology_link_updated = result
+
+            event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE
+            dict_topology_id = db_topology.dump_id()
+            notify_event(self.messagebroker, TOPIC_TOPOLOGY, event_type, {'topology_id': dict_topology_id})
+            return TopologyId(**dict_topology_id)
 
     @safe_and_metered_rpc_method(METRICS, LOGGER)
     def RemoveTopology(self, request: TopologyId, context : grpc.ServicerContext) -> Empty:
-        context_uuid = request.context_id.context_uuid.uuid
-        topology_uuid = request.topology_uuid.uuid
-        db_topology = TopologyModel(self.database, key_to_str([context_uuid, topology_uuid]), auto_load=False)
-        found = db_topology.load()
-        if not found: return Empty()
+        with self.lock:
+            context_uuid = request.context_id.context_uuid.uuid
+            topology_uuid = request.topology_uuid.uuid
+            db_topology = TopologyModel(self.database, key_to_str([context_uuid, topology_uuid]), auto_load=False)
+            found = db_topology.load()
+            if not found: return Empty()
 
-        dict_topology_id = db_topology.dump_id()
-        db_topology.delete()
-        event_type = EventTypeEnum.EVENTTYPE_REMOVE
-        notify_event(self.messagebroker, TOPIC_TOPOLOGY, event_type, {'topology_id': dict_topology_id})
-        return Empty()
+            dict_topology_id = db_topology.dump_id()
+            db_topology.delete()
+            event_type = EventTypeEnum.EVENTTYPE_REMOVE
+            notify_event(self.messagebroker, TOPIC_TOPOLOGY, event_type, {'topology_id': dict_topology_id})
+            return Empty()
 
     @safe_and_metered_rpc_method(METRICS, LOGGER)
     def GetTopologyEvents(self, request: Empty, context : grpc.ServicerContext) -> Iterator[TopologyEvent]:
@@ -210,118 +235,123 @@ class ContextServiceServicerImpl(ContextServiceServicer):
 
     @safe_and_metered_rpc_method(METRICS, LOGGER)
     def ListDeviceIds(self, request: Empty, context : grpc.ServicerContext) -> DeviceIdList:
-        db_devices : List[DeviceModel] = get_all_objects(self.database, DeviceModel)
-        db_devices = sorted(db_devices, key=operator.attrgetter('pk'))
-        return DeviceIdList(device_ids=[db_device.dump_id() for db_device in db_devices])
+        with self.lock:
+            db_devices : List[DeviceModel] = get_all_objects(self.database, DeviceModel)
+            db_devices = sorted(db_devices, key=operator.attrgetter('pk'))
+            return DeviceIdList(device_ids=[db_device.dump_id() for db_device in db_devices])
 
     @safe_and_metered_rpc_method(METRICS, LOGGER)
     def ListDevices(self, request: Empty, context : grpc.ServicerContext) -> DeviceList:
-        db_devices : List[DeviceModel] = get_all_objects(self.database, DeviceModel)
-        db_devices = sorted(db_devices, key=operator.attrgetter('pk'))
-        return DeviceList(devices=[db_device.dump() for db_device in db_devices])
+        with self.lock:
+            db_devices : List[DeviceModel] = get_all_objects(self.database, DeviceModel)
+            db_devices = sorted(db_devices, key=operator.attrgetter('pk'))
+            return DeviceList(devices=[db_device.dump() for db_device in db_devices])
 
     @safe_and_metered_rpc_method(METRICS, LOGGER)
     def GetDevice(self, request: DeviceId, context : grpc.ServicerContext) -> Device:
-        device_uuid = request.device_uuid.uuid
-        db_device : DeviceModel = get_object(self.database, DeviceModel, device_uuid)
-        return Device(**db_device.dump(
-            include_config_rules=True, include_drivers=True, include_endpoints=True))
+        with self.lock:
+            device_uuid = request.device_uuid.uuid
+            db_device : DeviceModel = get_object(self.database, DeviceModel, device_uuid)
+            return Device(**db_device.dump(
+                include_config_rules=True, include_drivers=True, include_endpoints=True))
 
     @safe_and_metered_rpc_method(METRICS, LOGGER)
     def SetDevice(self, request: Device, context : grpc.ServicerContext) -> DeviceId:
-        device_uuid = request.device_id.device_uuid.uuid
-
-        for i,endpoint in enumerate(request.device_endpoints):
-            endpoint_device_uuid = endpoint.endpoint_id.device_id.device_uuid.uuid
-            if len(endpoint_device_uuid) == 0: endpoint_device_uuid = device_uuid
-            if device_uuid != endpoint_device_uuid:
-                raise InvalidArgumentException(
-                    'request.device_endpoints[{:d}].device_id.device_uuid.uuid'.format(i), endpoint_device_uuid,
-                    ['should be == {:s}({:s})'.format('request.device_id.device_uuid.uuid', device_uuid)])
-
-        config_rules = grpc_config_rules_to_raw(request.device_config.config_rules)
-        running_config_result = update_config(self.database, device_uuid, 'running', config_rules)
-        db_running_config = running_config_result[0][0]
-
-        result : Tuple[DeviceModel, bool] = update_or_create_object(self.database, DeviceModel, device_uuid, {
-            'device_uuid'              : device_uuid,
-            'device_type'              : request.device_type,
-            'device_operational_status': grpc_to_enum__device_operational_status(request.device_operational_status),
-            'device_config_fk'         : db_running_config,
-        })
-        db_device, updated = result
-
-        set_drivers(self.database, db_device, request.device_drivers)
-
-        for i,endpoint in enumerate(request.device_endpoints):
-            endpoint_uuid = endpoint.endpoint_id.endpoint_uuid.uuid
-            endpoint_device_uuid = endpoint.endpoint_id.device_id.device_uuid.uuid
-            if len(endpoint_device_uuid) == 0: endpoint_device_uuid = device_uuid
-
-            str_endpoint_key = key_to_str([device_uuid, endpoint_uuid])
-            endpoint_attributes = {
-                'device_fk'    : db_device,
-                'endpoint_uuid': endpoint_uuid,
-                'endpoint_type': endpoint.endpoint_type,
-            }
-
-            endpoint_topology_context_uuid = endpoint.endpoint_id.topology_id.context_id.context_uuid.uuid
-            endpoint_topology_uuid = endpoint.endpoint_id.topology_id.topology_uuid.uuid
-            if len(endpoint_topology_context_uuid) > 0 and len(endpoint_topology_uuid) > 0:
-                str_topology_key = key_to_str([endpoint_topology_context_uuid, endpoint_topology_uuid])
-                db_topology : TopologyModel = get_object(self.database, TopologyModel, str_topology_key)
-
-                str_topology_device_key = key_to_str([str_topology_key, device_uuid], separator='--')
-                result : Tuple[TopologyDeviceModel, bool] = get_or_create_object(
-                    self.database, TopologyDeviceModel, str_topology_device_key, {
-                        'topology_fk': db_topology, 'device_fk': db_device})
-                #db_topology_device, topology_device_created = result
-
-                str_endpoint_key = key_to_str([str_endpoint_key, str_topology_key], separator=':')
-                endpoint_attributes['topology_fk'] = db_topology
-
-            result : Tuple[EndPointModel, bool] = update_or_create_object(
-                self.database, EndPointModel, str_endpoint_key, endpoint_attributes)
-            db_endpoint, endpoint_updated = result
-
-            set_kpi_sample_types(self.database, db_endpoint, endpoint.kpi_sample_types)
-
-        event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE
-        dict_device_id = db_device.dump_id()
-        notify_event(self.messagebroker, TOPIC_DEVICE, event_type, {'device_id': dict_device_id})
-        return DeviceId(**dict_device_id)
+        with self.lock:
+            device_uuid = request.device_id.device_uuid.uuid
+
+            for i,endpoint in enumerate(request.device_endpoints):
+                endpoint_device_uuid = endpoint.endpoint_id.device_id.device_uuid.uuid
+                if len(endpoint_device_uuid) == 0: endpoint_device_uuid = device_uuid
+                if device_uuid != endpoint_device_uuid:
+                    raise InvalidArgumentException(
+                        'request.device_endpoints[{:d}].device_id.device_uuid.uuid'.format(i), endpoint_device_uuid,
+                        ['should be == {:s}({:s})'.format('request.device_id.device_uuid.uuid', device_uuid)])
+
+            config_rules = grpc_config_rules_to_raw(request.device_config.config_rules)
+            running_config_result = update_config(self.database, device_uuid, 'running', config_rules)
+            db_running_config = running_config_result[0][0]
+
+            result : Tuple[DeviceModel, bool] = update_or_create_object(self.database, DeviceModel, device_uuid, {
+                'device_uuid'              : device_uuid,
+                'device_type'              : request.device_type,
+                'device_operational_status': grpc_to_enum__device_operational_status(request.device_operational_status),
+                'device_config_fk'         : db_running_config,
+            })
+            db_device, updated = result
+
+            set_drivers(self.database, db_device, request.device_drivers)
+
+            for i,endpoint in enumerate(request.device_endpoints):
+                endpoint_uuid = endpoint.endpoint_id.endpoint_uuid.uuid
+                endpoint_device_uuid = endpoint.endpoint_id.device_id.device_uuid.uuid
+                if len(endpoint_device_uuid) == 0: endpoint_device_uuid = device_uuid
+
+                str_endpoint_key = key_to_str([device_uuid, endpoint_uuid])
+                endpoint_attributes = {
+                    'device_fk'    : db_device,
+                    'endpoint_uuid': endpoint_uuid,
+                    'endpoint_type': endpoint.endpoint_type,
+                }
+
+                endpoint_topology_context_uuid = endpoint.endpoint_id.topology_id.context_id.context_uuid.uuid
+                endpoint_topology_uuid = endpoint.endpoint_id.topology_id.topology_uuid.uuid
+                if len(endpoint_topology_context_uuid) > 0 and len(endpoint_topology_uuid) > 0:
+                    str_topology_key = key_to_str([endpoint_topology_context_uuid, endpoint_topology_uuid])
+                    db_topology : TopologyModel = get_object(self.database, TopologyModel, str_topology_key)
+
+                    str_topology_device_key = key_to_str([str_topology_key, device_uuid], separator='--')
+                    result : Tuple[TopologyDeviceModel, bool] = get_or_create_object(
+                        self.database, TopologyDeviceModel, str_topology_device_key, {
+                            'topology_fk': db_topology, 'device_fk': db_device})
+                    #db_topology_device, topology_device_created = result
+
+                    str_endpoint_key = key_to_str([str_endpoint_key, str_topology_key], separator=':')
+                    endpoint_attributes['topology_fk'] = db_topology
+
+                result : Tuple[EndPointModel, bool] = update_or_create_object(
+                    self.database, EndPointModel, str_endpoint_key, endpoint_attributes)
+                db_endpoint, endpoint_updated = result
+
+                set_kpi_sample_types(self.database, db_endpoint, endpoint.kpi_sample_types)
+
+            event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE
+            dict_device_id = db_device.dump_id()
+            notify_event(self.messagebroker, TOPIC_DEVICE, event_type, {'device_id': dict_device_id})
+            return DeviceId(**dict_device_id)
 
     @safe_and_metered_rpc_method(METRICS, LOGGER)
     def RemoveDevice(self, request: DeviceId, context : grpc.ServicerContext) -> Empty:
-        device_uuid = request.device_uuid.uuid
-        db_device = DeviceModel(self.database, device_uuid, auto_load=False)
-        found = db_device.load()
-        if not found: return Empty()
+        with self.lock:
+            device_uuid = request.device_uuid.uuid
+            db_device = DeviceModel(self.database, device_uuid, auto_load=False)
+            found = db_device.load()
+            if not found: return Empty()
 
-        dict_device_id = db_device.dump_id()
+            dict_device_id = db_device.dump_id()
 
-        for db_endpoint_pk,_ in db_device.references(EndPointModel):
-            db_endpoint = EndPointModel(self.database, db_endpoint_pk)
-            for db_kpi_sample_type_pk,_ in db_endpoint.references(KpiSampleTypeModel):
-                KpiSampleTypeModel(self.database, db_kpi_sample_type_pk).delete()
-            db_endpoint.delete()
+            for db_endpoint_pk,_ in db_device.references(EndPointModel):
+                db_endpoint = EndPointModel(self.database, db_endpoint_pk)
+                for db_kpi_sample_type_pk,_ in db_endpoint.references(KpiSampleTypeModel):
+                    KpiSampleTypeModel(self.database, db_kpi_sample_type_pk).delete()
+                db_endpoint.delete()
 
-        for db_topology_device_pk,_ in db_device.references(TopologyDeviceModel):
-            TopologyDeviceModel(self.database, db_topology_device_pk).delete()
+            for db_topology_device_pk,_ in db_device.references(TopologyDeviceModel):
+                TopologyDeviceModel(self.database, db_topology_device_pk).delete()
 
-        for db_driver_pk,_ in db_device.references(DriverModel):
-            DriverModel(self.database, db_driver_pk).delete()
+            for db_driver_pk,_ in db_device.references(DriverModel):
+                DriverModel(self.database, db_driver_pk).delete()
 
-        db_config = ConfigModel(self.database, db_device.device_config_fk)
-        for db_config_rule_pk,_ in db_config.references(ConfigRuleModel):
-            ConfigRuleModel(self.database, db_config_rule_pk).delete()
+            db_config = ConfigModel(self.database, db_device.device_config_fk)
+            for db_config_rule_pk,_ in db_config.references(ConfigRuleModel):
+                ConfigRuleModel(self.database, db_config_rule_pk).delete()
 
-        db_device.delete()
-        db_config.delete()
+            db_device.delete()
+            db_config.delete()
 
-        event_type = EventTypeEnum.EVENTTYPE_REMOVE
-        notify_event(self.messagebroker, TOPIC_DEVICE, event_type, {'device_id': dict_device_id})
-        return Empty()
+            event_type = EventTypeEnum.EVENTTYPE_REMOVE
+            notify_event(self.messagebroker, TOPIC_DEVICE, event_type, {'device_id': dict_device_id})
+            return Empty()
 
     @safe_and_metered_rpc_method(METRICS, LOGGER)
     def GetDeviceEvents(self, request: Empty, context : grpc.ServicerContext) -> Iterator[DeviceEvent]:
@@ -333,84 +363,89 @@ class ContextServiceServicerImpl(ContextServiceServicer):
 
     @safe_and_metered_rpc_method(METRICS, LOGGER)
     def ListLinkIds(self, request: Empty, context : grpc.ServicerContext) -> LinkIdList:
-        db_links : List[LinkModel] = get_all_objects(self.database, LinkModel)
-        db_links = sorted(db_links, key=operator.attrgetter('pk'))
-        return LinkIdList(link_ids=[db_link.dump_id() for db_link in db_links])
+        with self.lock:
+            db_links : List[LinkModel] = get_all_objects(self.database, LinkModel)
+            db_links = sorted(db_links, key=operator.attrgetter('pk'))
+            return LinkIdList(link_ids=[db_link.dump_id() for db_link in db_links])
 
     @safe_and_metered_rpc_method(METRICS, LOGGER)
     def ListLinks(self, request: Empty, context : grpc.ServicerContext) -> LinkList:
-        db_links : List[LinkModel] = get_all_objects(self.database, LinkModel)
-        db_links = sorted(db_links, key=operator.attrgetter('pk'))
-        return LinkList(links=[db_link.dump() for db_link in db_links])
+        with self.lock:
+            db_links : List[LinkModel] = get_all_objects(self.database, LinkModel)
+            db_links = sorted(db_links, key=operator.attrgetter('pk'))
+            return LinkList(links=[db_link.dump() for db_link in db_links])
 
     @safe_and_metered_rpc_method(METRICS, LOGGER)
     def GetLink(self, request: LinkId, context : grpc.ServicerContext) -> Link:
-        link_uuid = request.link_uuid.uuid
-        db_link : LinkModel = get_object(self.database, LinkModel, link_uuid)
-        return Link(**db_link.dump())
+        with self.lock:
+            link_uuid = request.link_uuid.uuid
+            db_link : LinkModel = get_object(self.database, LinkModel, link_uuid)
+            return Link(**db_link.dump())
 
     @safe_and_metered_rpc_method(METRICS, LOGGER)
     def SetLink(self, request: Link, context : grpc.ServicerContext) -> LinkId:
-        link_uuid = request.link_id.link_uuid.uuid
-        result : Tuple[LinkModel, bool] = update_or_create_object(
-            self.database, LinkModel, link_uuid, {'link_uuid': link_uuid})
-        db_link, updated = result
-
-        for endpoint_id in request.link_endpoint_ids:
-            endpoint_uuid                  = endpoint_id.endpoint_uuid.uuid
-            endpoint_device_uuid           = endpoint_id.device_id.device_uuid.uuid
-            endpoint_topology_uuid         = endpoint_id.topology_id.topology_uuid.uuid
-            endpoint_topology_context_uuid = endpoint_id.topology_id.context_id.context_uuid.uuid
-
-            str_endpoint_key = key_to_str([endpoint_device_uuid, endpoint_uuid])
-
-            db_topology = None
-            if len(endpoint_topology_context_uuid) > 0 and len(endpoint_topology_uuid) > 0:
-                str_topology_key = key_to_str([endpoint_topology_context_uuid, endpoint_topology_uuid])
-                db_topology : TopologyModel = get_object(self.database, TopologyModel, str_topology_key)
-                str_topology_device_key = key_to_str([str_topology_key, endpoint_device_uuid], separator='--')
-                get_object(self.database, TopologyDeviceModel, str_topology_device_key) # check device is in topology
-                str_endpoint_key = key_to_str([str_endpoint_key, str_topology_key], separator=':')
-
-            db_endpoint : EndPointModel = get_object(self.database, EndPointModel, str_endpoint_key)
-
-            str_link_endpoint_key = key_to_str([link_uuid, endpoint_device_uuid], separator='--')
-            result : Tuple[LinkEndPointModel, bool] = get_or_create_object(
-                self.database, LinkEndPointModel, str_link_endpoint_key, {
-                    'link_fk': db_link, 'endpoint_fk': db_endpoint})
-            #db_link_endpoint, link_endpoint_created = result
-
-            if db_topology is not None:
-                str_topology_link_key = key_to_str([str_topology_key, link_uuid], separator='--')
-                result : Tuple[TopologyLinkModel, bool] = get_or_create_object(
-                    self.database, TopologyLinkModel, str_topology_link_key, {
-                        'topology_fk': db_topology, 'link_fk': db_link})
-                #db_topology_link, topology_link_created = result
-
-        event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE
-        dict_link_id = db_link.dump_id()
-        notify_event(self.messagebroker, TOPIC_LINK, event_type, {'link_id': dict_link_id})
-        return LinkId(**dict_link_id)
+        with self.lock:
+            link_uuid = request.link_id.link_uuid.uuid
+            result : Tuple[LinkModel, bool] = update_or_create_object(
+                self.database, LinkModel, link_uuid, {'link_uuid': link_uuid})
+            db_link, updated = result
+
+            for endpoint_id in request.link_endpoint_ids:
+                endpoint_uuid                  = endpoint_id.endpoint_uuid.uuid
+                endpoint_device_uuid           = endpoint_id.device_id.device_uuid.uuid
+                endpoint_topology_uuid         = endpoint_id.topology_id.topology_uuid.uuid
+                endpoint_topology_context_uuid = endpoint_id.topology_id.context_id.context_uuid.uuid
+
+                str_endpoint_key = key_to_str([endpoint_device_uuid, endpoint_uuid])
+
+                db_topology = None
+                if len(endpoint_topology_context_uuid) > 0 and len(endpoint_topology_uuid) > 0:
+                    str_topology_key = key_to_str([endpoint_topology_context_uuid, endpoint_topology_uuid])
+                    db_topology : TopologyModel = get_object(self.database, TopologyModel, str_topology_key)
+                    str_topology_device_key = key_to_str([str_topology_key, endpoint_device_uuid], separator='--')
+                    get_object(self.database, TopologyDeviceModel, str_topology_device_key) # check device is in topology
+                    str_endpoint_key = key_to_str([str_endpoint_key, str_topology_key], separator=':')
+
+                db_endpoint : EndPointModel = get_object(self.database, EndPointModel, str_endpoint_key)
+
+                str_link_endpoint_key = key_to_str([link_uuid, endpoint_device_uuid], separator='--')
+                result : Tuple[LinkEndPointModel, bool] = get_or_create_object(
+                    self.database, LinkEndPointModel, str_link_endpoint_key, {
+                        'link_fk': db_link, 'endpoint_fk': db_endpoint})
+                #db_link_endpoint, link_endpoint_created = result
+
+                if db_topology is not None:
+                    str_topology_link_key = key_to_str([str_topology_key, link_uuid], separator='--')
+                    result : Tuple[TopologyLinkModel, bool] = get_or_create_object(
+                        self.database, TopologyLinkModel, str_topology_link_key, {
+                            'topology_fk': db_topology, 'link_fk': db_link})
+                    #db_topology_link, topology_link_created = result
+
+            event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE
+            dict_link_id = db_link.dump_id()
+            notify_event(self.messagebroker, TOPIC_LINK, event_type, {'link_id': dict_link_id})
+            return LinkId(**dict_link_id)
 
     @safe_and_metered_rpc_method(METRICS, LOGGER)
     def RemoveLink(self, request: LinkId, context : grpc.ServicerContext) -> Empty:
-        link_uuid = request.link_uuid.uuid
-        db_link = LinkModel(self.database, link_uuid, auto_load=False)
-        found = db_link.load()
-        if not found: return Empty()
+        with self.lock:
+            link_uuid = request.link_uuid.uuid
+            db_link = LinkModel(self.database, link_uuid, auto_load=False)
+            found = db_link.load()
+            if not found: return Empty()
 
-        dict_link_id = db_link.dump_id()
+            dict_link_id = db_link.dump_id()
 
-        for db_link_endpoint_pk,_ in db_link.references(LinkEndPointModel):
-            LinkEndPointModel(self.database, db_link_endpoint_pk).delete()
+            for db_link_endpoint_pk,_ in db_link.references(LinkEndPointModel):
+                LinkEndPointModel(self.database, db_link_endpoint_pk).delete()
 
-        for db_topology_link_pk,_ in db_link.references(TopologyLinkModel):
-            TopologyLinkModel(self.database, db_topology_link_pk).delete()
+            for db_topology_link_pk,_ in db_link.references(TopologyLinkModel):
+                TopologyLinkModel(self.database, db_topology_link_pk).delete()
 
-        db_link.delete()
-        event_type = EventTypeEnum.EVENTTYPE_REMOVE
-        notify_event(self.messagebroker, TOPIC_LINK, event_type, {'link_id': dict_link_id})
-        return Empty()
+            db_link.delete()
+            event_type = EventTypeEnum.EVENTTYPE_REMOVE
+            notify_event(self.messagebroker, TOPIC_LINK, event_type, {'link_id': dict_link_id})
+            return Empty()
 
     @safe_and_metered_rpc_method(METRICS, LOGGER)
     def GetLinkEvents(self, request: Empty, context : grpc.ServicerContext) -> Iterator[LinkEvent]:
@@ -422,111 +457,116 @@ class ContextServiceServicerImpl(ContextServiceServicer):
 
     @safe_and_metered_rpc_method(METRICS, LOGGER)
     def ListServiceIds(self, request: ContextId, context : grpc.ServicerContext) -> ServiceIdList:
-        db_context : ContextModel = get_object(self.database, ContextModel, request.context_uuid.uuid)
-        db_services : Set[ServiceModel] = get_related_objects(db_context, ServiceModel)
-        db_services = sorted(db_services, key=operator.attrgetter('pk'))
-        return ServiceIdList(service_ids=[db_service.dump_id() for db_service in db_services])
+        with self.lock:
+            db_context : ContextModel = get_object(self.database, ContextModel, request.context_uuid.uuid)
+            db_services : Set[ServiceModel] = get_related_objects(db_context, ServiceModel)
+            db_services = sorted(db_services, key=operator.attrgetter('pk'))
+            return ServiceIdList(service_ids=[db_service.dump_id() for db_service in db_services])
 
     @safe_and_metered_rpc_method(METRICS, LOGGER)
     def ListServices(self, request: ContextId, context : grpc.ServicerContext) -> ServiceList:
-        db_context : ContextModel = get_object(self.database, ContextModel, request.context_uuid.uuid)
-        db_services : Set[ServiceModel] = get_related_objects(db_context, ServiceModel)
-        db_services = sorted(db_services, key=operator.attrgetter('pk'))
-        return ServiceList(services=[db_service.dump() for db_service in db_services])
+        with self.lock:
+            db_context : ContextModel = get_object(self.database, ContextModel, request.context_uuid.uuid)
+            db_services : Set[ServiceModel] = get_related_objects(db_context, ServiceModel)
+            db_services = sorted(db_services, key=operator.attrgetter('pk'))
+            return ServiceList(services=[db_service.dump() for db_service in db_services])
 
     @safe_and_metered_rpc_method(METRICS, LOGGER)
     def GetService(self, request: ServiceId, context : grpc.ServicerContext) -> Service:
-        str_key = key_to_str([request.context_id.context_uuid.uuid, request.service_uuid.uuid])
-        db_service : ServiceModel = get_object(self.database, ServiceModel, str_key)
-        return Service(**db_service.dump(
-            include_endpoint_ids=True, include_constraints=True, include_config_rules=True))
+        with self.lock:
+            str_key = key_to_str([request.context_id.context_uuid.uuid, request.service_uuid.uuid])
+            db_service : ServiceModel = get_object(self.database, ServiceModel, str_key)
+            return Service(**db_service.dump(
+                include_endpoint_ids=True, include_constraints=True, include_config_rules=True))
 
     @safe_and_metered_rpc_method(METRICS, LOGGER)
     def SetService(self, request: Service, context : grpc.ServicerContext) -> ServiceId:
-        context_uuid = request.service_id.context_id.context_uuid.uuid
-        db_context : ContextModel = get_object(self.database, ContextModel, context_uuid)
-
-        for i,endpoint_id in enumerate(request.service_endpoint_ids):
-            endpoint_topology_context_uuid = endpoint_id.topology_id.context_id.context_uuid.uuid
-            if len(endpoint_topology_context_uuid) > 0 and context_uuid != endpoint_topology_context_uuid:
-                raise InvalidArgumentException(
-                    'request.service_endpoint_ids[{:d}].topology_id.context_id.context_uuid.uuid'.format(i),
-                    endpoint_topology_context_uuid,
-                    ['should be == {:s}({:s})'.format('request.service_id.context_id.context_uuid.uuid', context_uuid)])
-
-        service_uuid = request.service_id.service_uuid.uuid
-        str_service_key = key_to_str([context_uuid, service_uuid])
-
-        constraints_result = set_constraints(
-            self.database, str_service_key, 'constraints', request.service_constraints)
-        db_constraints = constraints_result[0][0]
-
-        config_rules = grpc_config_rules_to_raw(request.service_config.config_rules)
-        running_config_result = update_config(self.database, str_service_key, 'running', config_rules)
-        db_running_config = running_config_result[0][0]
-
-        result : Tuple[ServiceModel, bool] = update_or_create_object(self.database, ServiceModel, str_service_key, {
-            'context_fk'            : db_context,
-            'service_uuid'          : service_uuid,
-            'service_type'          : grpc_to_enum__service_type(request.service_type),
-            'service_constraints_fk': db_constraints,
-            'service_status'        : grpc_to_enum__service_status(request.service_status.service_status),
-            'service_config_fk'     : db_running_config,
-        })
-        db_service, updated = result
-
-        for i,endpoint_id in enumerate(request.service_endpoint_ids):
-            endpoint_uuid                  = endpoint_id.endpoint_uuid.uuid
-            endpoint_device_uuid           = endpoint_id.device_id.device_uuid.uuid
-            endpoint_topology_uuid         = endpoint_id.topology_id.topology_uuid.uuid
-            endpoint_topology_context_uuid = endpoint_id.topology_id.context_id.context_uuid.uuid
-
-            str_endpoint_key = key_to_str([endpoint_device_uuid, endpoint_uuid])
-            if len(endpoint_topology_context_uuid) > 0 and len(endpoint_topology_uuid) > 0:
-                str_topology_key = key_to_str([endpoint_topology_context_uuid, endpoint_topology_uuid])
-                str_endpoint_key = key_to_str([str_endpoint_key, str_topology_key], separator=':')
-
-            db_endpoint : EndPointModel = get_object(self.database, EndPointModel, str_endpoint_key)
-
-            str_service_endpoint_key = key_to_str([service_uuid, endpoint_device_uuid], separator='--')
-            result : Tuple[ServiceEndPointModel, bool] = get_or_create_object(
-                self.database, ServiceEndPointModel, str_service_endpoint_key, {
-                    'service_fk': db_service, 'endpoint_fk': db_endpoint})
-            #db_service_endpoint, service_endpoint_created = result
-
-        event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE
-        dict_service_id = db_service.dump_id()
-        notify_event(self.messagebroker, TOPIC_SERVICE, event_type, {'service_id': dict_service_id})
-        return ServiceId(**dict_service_id)
+        with self.lock:
+            context_uuid = request.service_id.context_id.context_uuid.uuid
+            db_context : ContextModel = get_object(self.database, ContextModel, context_uuid)
+
+            for i,endpoint_id in enumerate(request.service_endpoint_ids):
+                endpoint_topology_context_uuid = endpoint_id.topology_id.context_id.context_uuid.uuid
+                if len(endpoint_topology_context_uuid) > 0 and context_uuid != endpoint_topology_context_uuid:
+                    raise InvalidArgumentException(
+                        'request.service_endpoint_ids[{:d}].topology_id.context_id.context_uuid.uuid'.format(i),
+                        endpoint_topology_context_uuid,
+                        ['should be == {:s}({:s})'.format('request.service_id.context_id.context_uuid.uuid', context_uuid)])
+
+            service_uuid = request.service_id.service_uuid.uuid
+            str_service_key = key_to_str([context_uuid, service_uuid])
+
+            constraints_result = set_constraints(
+                self.database, str_service_key, 'constraints', request.service_constraints)
+            db_constraints = constraints_result[0][0]
+
+            config_rules = grpc_config_rules_to_raw(request.service_config.config_rules)
+            running_config_result = update_config(self.database, str_service_key, 'running', config_rules)
+            db_running_config = running_config_result[0][0]
+
+            result : Tuple[ServiceModel, bool] = update_or_create_object(self.database, ServiceModel, str_service_key, {
+                'context_fk'            : db_context,
+                'service_uuid'          : service_uuid,
+                'service_type'          : grpc_to_enum__service_type(request.service_type),
+                'service_constraints_fk': db_constraints,
+                'service_status'        : grpc_to_enum__service_status(request.service_status.service_status),
+                'service_config_fk'     : db_running_config,
+            })
+            db_service, updated = result
+
+            for i,endpoint_id in enumerate(request.service_endpoint_ids):
+                endpoint_uuid                  = endpoint_id.endpoint_uuid.uuid
+                endpoint_device_uuid           = endpoint_id.device_id.device_uuid.uuid
+                endpoint_topology_uuid         = endpoint_id.topology_id.topology_uuid.uuid
+                endpoint_topology_context_uuid = endpoint_id.topology_id.context_id.context_uuid.uuid
+
+                str_endpoint_key = key_to_str([endpoint_device_uuid, endpoint_uuid])
+                if len(endpoint_topology_context_uuid) > 0 and len(endpoint_topology_uuid) > 0:
+                    str_topology_key = key_to_str([endpoint_topology_context_uuid, endpoint_topology_uuid])
+                    str_endpoint_key = key_to_str([str_endpoint_key, str_topology_key], separator=':')
+
+                db_endpoint : EndPointModel = get_object(self.database, EndPointModel, str_endpoint_key)
+
+                str_service_endpoint_key = key_to_str([service_uuid, str_endpoint_key], separator='--')
+                result : Tuple[ServiceEndPointModel, bool] = get_or_create_object(
+                    self.database, ServiceEndPointModel, str_service_endpoint_key, {
+                        'service_fk': db_service, 'endpoint_fk': db_endpoint})
+                #db_service_endpoint, service_endpoint_created = result
+
+            event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE
+            dict_service_id = db_service.dump_id()
+            notify_event(self.messagebroker, TOPIC_SERVICE, event_type, {'service_id': dict_service_id})
+            return ServiceId(**dict_service_id)
 
     @safe_and_metered_rpc_method(METRICS, LOGGER)
     def RemoveService(self, request: ServiceId, context : grpc.ServicerContext) -> Empty:
-        context_uuid = request.context_id.context_uuid.uuid
-        service_uuid = request.service_uuid.uuid
-        db_service = ServiceModel(self.database, key_to_str([context_uuid, service_uuid]), auto_load=False)
-        found = db_service.load()
-        if not found: return Empty()
+        with self.lock:
+            context_uuid = request.context_id.context_uuid.uuid
+            service_uuid = request.service_uuid.uuid
+            db_service = ServiceModel(self.database, key_to_str([context_uuid, service_uuid]), auto_load=False)
+            found = db_service.load()
+            if not found: return Empty()
 
-        dict_service_id = db_service.dump_id()
+            dict_service_id = db_service.dump_id()
 
-        for db_service_endpoint_pk,_ in db_service.references(ServiceEndPointModel):
-            ServiceEndPointModel(self.database, db_service_endpoint_pk).delete()
+            for db_service_endpoint_pk,_ in db_service.references(ServiceEndPointModel):
+                ServiceEndPointModel(self.database, db_service_endpoint_pk).delete()
 
-        db_config = ConfigModel(self.database, db_service.service_config_fk)
-        for db_config_rule_pk,_ in db_config.references(ConfigRuleModel):
-            ConfigRuleModel(self.database, db_config_rule_pk).delete()
+            db_config = ConfigModel(self.database, db_service.service_config_fk)
+            for db_config_rule_pk,_ in db_config.references(ConfigRuleModel):
+                ConfigRuleModel(self.database, db_config_rule_pk).delete()
 
-        db_constraints = ConstraintsModel(self.database, db_service.service_constraints_fk)
-        for db_constraint_pk,_ in db_constraints.references(ConstraintModel):
-            ConstraintModel(self.database, db_constraint_pk).delete()
+            db_constraints = ConstraintsModel(self.database, db_service.service_constraints_fk)
+            for db_constraint_pk,_ in db_constraints.references(ConstraintModel):
+                ConstraintModel(self.database, db_constraint_pk).delete()
 
-        db_service.delete()
-        db_config.delete()
-        db_constraints.delete()
+            db_service.delete()
+            db_config.delete()
+            db_constraints.delete()
 
-        event_type = EventTypeEnum.EVENTTYPE_REMOVE
-        notify_event(self.messagebroker, TOPIC_SERVICE, event_type, {'service_id': dict_service_id})
-        return Empty()
+            event_type = EventTypeEnum.EVENTTYPE_REMOVE
+            notify_event(self.messagebroker, TOPIC_SERVICE, event_type, {'service_id': dict_service_id})
+            return Empty()
 
     @safe_and_metered_rpc_method(METRICS, LOGGER)
     def GetServiceEvents(self, request: Empty, context : grpc.ServicerContext) -> Iterator[ServiceEvent]:
@@ -538,88 +578,93 @@ class ContextServiceServicerImpl(ContextServiceServicer):
 
     @safe_and_metered_rpc_method(METRICS, LOGGER)
     def ListConnectionIds(self, request: ServiceId, context : grpc.ServicerContext) -> ConnectionIdList:
-        str_key = key_to_str([request.context_id.context_uuid.uuid, request.service_uuid.uuid])
-        db_service : ServiceModel = get_object(self.database, ServiceModel, str_key)
-        db_connections : Set[ConnectionModel] = get_related_objects(db_service, ConnectionModel)
-        db_connections = sorted(db_connections, key=operator.attrgetter('pk'))
-        return ConnectionIdList(connection_ids=[db_connection.dump_id() for db_connection in db_connections])
+        with self.lock:
+            str_key = key_to_str([request.context_id.context_uuid.uuid, request.service_uuid.uuid])
+            db_service : ServiceModel = get_object(self.database, ServiceModel, str_key)
+            db_connections : Set[ConnectionModel] = get_related_objects(db_service, ConnectionModel)
+            db_connections = sorted(db_connections, key=operator.attrgetter('pk'))
+            return ConnectionIdList(connection_ids=[db_connection.dump_id() for db_connection in db_connections])
 
     @safe_and_metered_rpc_method(METRICS, LOGGER)
     def ListConnections(self, request: ContextId, context : grpc.ServicerContext) -> ServiceList:
-        str_key = key_to_str([request.context_id.context_uuid.uuid, request.service_uuid.uuid])
-        db_service : ServiceModel = get_object(self.database, ServiceModel, str_key)
-        db_connections : Set[ConnectionModel] = get_related_objects(db_service, ConnectionModel)
-        db_connections = sorted(db_connections, key=operator.attrgetter('pk'))
-        return ConnectionList(connections=[db_connection.dump() for db_connection in db_connections])
+        with self.lock:
+            str_key = key_to_str([request.context_id.context_uuid.uuid, request.service_uuid.uuid])
+            db_service : ServiceModel = get_object(self.database, ServiceModel, str_key)
+            db_connections : Set[ConnectionModel] = get_related_objects(db_service, ConnectionModel)
+            db_connections = sorted(db_connections, key=operator.attrgetter('pk'))
+            return ConnectionList(connections=[db_connection.dump() for db_connection in db_connections])
 
     @safe_and_metered_rpc_method(METRICS, LOGGER)
     def GetConnection(self, request: ConnectionId, context : grpc.ServicerContext) -> Connection:
-        db_connection : ConnectionModel = get_object(self.database, ConnectionModel, request.connection_uuid.uuid)
-        return Connection(**db_connection.dump(include_path=True, include_sub_service_ids=True))
+        with self.lock:
+            db_connection : ConnectionModel = get_object(self.database, ConnectionModel, request.connection_uuid.uuid)
+            return Connection(**db_connection.dump(include_path=True, include_sub_service_ids=True))
 
     @safe_and_metered_rpc_method(METRICS, LOGGER)
     def SetConnection(self, request: Connection, context : grpc.ServicerContext) -> ConnectionId:
-        connection_uuid = request.connection_id.connection_uuid.uuid
+        with self.lock:
+            connection_uuid = request.connection_id.connection_uuid.uuid
 
-        connection_attributes = {'connection_uuid': connection_uuid}
+            connection_attributes = {'connection_uuid': connection_uuid}
 
-        service_context_uuid = request.service_id.context_id.context_uuid.uuid
-        service_uuid = request.service_id.service_uuid.uuid
-        if len(service_context_uuid) > 0 and len(service_uuid) > 0:
-            str_service_key = key_to_str([service_context_uuid, service_uuid])
-            db_service : ServiceModel = get_object(self.database, ServiceModel, str_service_key)
-            connection_attributes['service_fk'] = db_service
+            service_context_uuid = request.service_id.context_id.context_uuid.uuid
+            service_uuid = request.service_id.service_uuid.uuid
+            if len(service_context_uuid) > 0 and len(service_uuid) > 0:
+                str_service_key = key_to_str([service_context_uuid, service_uuid])
+                db_service : ServiceModel = get_object(self.database, ServiceModel, str_service_key)
+                connection_attributes['service_fk'] = db_service
 
-        path_hops_result = set_path(self.database, connection_uuid, request.path_hops_endpoint_ids, path_name = '')
-        db_path = path_hops_result[0]
-        connection_attributes['path_fk'] = db_path
+            path_hops_result = set_path(self.database, connection_uuid, request.path_hops_endpoint_ids, path_name = '')
+            db_path = path_hops_result[0]
+            connection_attributes['path_fk'] = db_path
 
-        result : Tuple[ConnectionModel, bool] = update_or_create_object(
-            self.database, ConnectionModel, connection_uuid, connection_attributes)
-        db_connection, updated = result
+            result : Tuple[ConnectionModel, bool] = update_or_create_object(
+                self.database, ConnectionModel, connection_uuid, connection_attributes)
+            db_connection, updated = result
 
-        for sub_service_id in request.sub_service_ids:
-            sub_service_uuid         = sub_service_id.service_uuid.uuid
-            sub_service_context_uuid = sub_service_id.context_id.context_uuid.uuid
-            str_sub_service_key = key_to_str([sub_service_context_uuid, sub_service_uuid])
-            db_service : ServiceModel = get_object(self.database, ServiceModel, str_sub_service_key)
+            for sub_service_id in request.sub_service_ids:
+                sub_service_uuid         = sub_service_id.service_uuid.uuid
+                sub_service_context_uuid = sub_service_id.context_id.context_uuid.uuid
+                str_sub_service_key = key_to_str([sub_service_context_uuid, sub_service_uuid])
+                db_service : ServiceModel = get_object(self.database, ServiceModel, str_sub_service_key)
 
-            str_connection_sub_service_key = key_to_str([connection_uuid, str_sub_service_key], separator='--')
-            result : Tuple[ConnectionSubServiceModel, bool] = get_or_create_object(
-                self.database, ConnectionSubServiceModel, str_connection_sub_service_key, {
-                    'connection_fk': db_connection, 'sub_service_fk': db_service})
-            #db_connection_sub_service, connection_sub_service_created = result
+                str_connection_sub_service_key = key_to_str([connection_uuid, str_sub_service_key], separator='--')
+                result : Tuple[ConnectionSubServiceModel, bool] = get_or_create_object(
+                    self.database, ConnectionSubServiceModel, str_connection_sub_service_key, {
+                        'connection_fk': db_connection, 'sub_service_fk': db_service})
+                #db_connection_sub_service, connection_sub_service_created = result
 
-        event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE
-        dict_connection_id = db_connection.dump_id()
-        notify_event(self.messagebroker, TOPIC_CONNECTION, event_type, {'connection_id': dict_connection_id})
-        return ConnectionId(**dict_connection_id)
+            event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE
+            dict_connection_id = db_connection.dump_id()
+            notify_event(self.messagebroker, TOPIC_CONNECTION, event_type, {'connection_id': dict_connection_id})
+            return ConnectionId(**dict_connection_id)
 
     @safe_and_metered_rpc_method(METRICS, LOGGER)
     def RemoveConnection(self, request: ConnectionId, context : grpc.ServicerContext) -> Empty:
-        db_connection = ConnectionModel(self.database, request.connection_uuid.uuid, auto_load=False)
-        found = db_connection.load()
-        if not found: return Empty()
-
-        dict_connection_id = db_connection.dump_id()
-
-        db_path = PathModel(self.database, db_connection.path_fk)
-        for db_path_hop_pk,_ in db_path.references(PathHopModel):
-            PathHopModel(self.database, db_path_hop_pk).delete()
-
-        # Do not remove sub-services automatically. They are supported by real services, so Service component should
-        # deal with the correct removal workflow to deconfigure the devices.
-        for db_connection_sub_service_pk,_ in db_connection.references(ConnectionSubServiceModel):
-            db_connection_sub_service : ConnectionSubServiceModel = get_object(
-                self.database, ConnectionSubServiceModel, db_connection_sub_service_pk)
-            db_connection_sub_service.delete()
-
-        db_connection.delete()
-        db_path.delete()
-
-        event_type = EventTypeEnum.EVENTTYPE_REMOVE
-        notify_event(self.messagebroker, TOPIC_CONNECTION, event_type, {'connection_id': dict_connection_id})
-        return Empty()
+        with self.lock:
+            db_connection = ConnectionModel(self.database, request.connection_uuid.uuid, auto_load=False)
+            found = db_connection.load()
+            if not found: return Empty()
+
+            dict_connection_id = db_connection.dump_id()
+
+            db_path = PathModel(self.database, db_connection.path_fk)
+            for db_path_hop_pk,_ in db_path.references(PathHopModel):
+                PathHopModel(self.database, db_path_hop_pk).delete()
+
+            # Do not remove sub-services automatically. They are supported by real services, so Service component should
+            # deal with the correct removal workflow to deconfigure the devices.
+            for db_connection_sub_service_pk,_ in db_connection.references(ConnectionSubServiceModel):
+                db_connection_sub_service : ConnectionSubServiceModel = get_object(
+                    self.database, ConnectionSubServiceModel, db_connection_sub_service_pk)
+                db_connection_sub_service.delete()
+
+            db_connection.delete()
+            db_path.delete()
+
+            event_type = EventTypeEnum.EVENTTYPE_REMOVE
+            notify_event(self.messagebroker, TOPIC_CONNECTION, event_type, {'connection_id': dict_connection_id})
+            return Empty()
 
     @safe_and_metered_rpc_method(METRICS, LOGGER)
     def GetConnectionEvents(self, request: Empty, context : grpc.ServicerContext) -> Iterator[ConnectionEvent]:
diff --git a/src/context/service/grpc_server/__init__.py b/src/context/service/grpc_server/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/context/service/grpc_server/__init__.py
+++ b/src/context/service/grpc_server/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/context/service/rest_server/Resources.py b/src/context/service/rest_server/Resources.py
index 9abf60d4855f2b5f996b85378c358d1e8460729c..966c62f968a32bfc06abeb0d69f612725fce8d5e 100644
--- a/src/context/service/rest_server/Resources.py
+++ b/src/context/service/rest_server/Resources.py
@@ -1,14 +1,27 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from flask import make_response
 from flask.json import jsonify
 from flask_restful import Resource
-from google.protobuf.json_format import MessageToDict
 from common.orm.Database import Database
+from common.tools.grpc.Tools import grpc_message_to_json
 from context.proto.context_pb2 import ConnectionId, ContextId, DeviceId, Empty, LinkId, ServiceId, TopologyId
 from context.service.grpc_server.ContextServiceServicerImpl import ContextServiceServicerImpl
 
 def format_grpc_to_json(grpc_reply):
-    return jsonify(MessageToDict(
-        grpc_reply, including_default_value_fields=True, preserving_proto_field_name=True,
-        use_integers_for_enums=False))
+    return jsonify(grpc_message_to_json(grpc_reply))
 
 def grpc_connection_id(connection_uuid):
     return ConnectionId(**{
@@ -120,6 +133,41 @@ class Connection(_Resource):
     def get(self, connection_uuid : str):
         return format_grpc_to_json(self.servicer.GetConnection(grpc_connection_id(connection_uuid), None))
 
+class DumpText(Resource):
+    def __init__(self, database : Database) -> None:
+        super().__init__()
+        self.database = database
+
+    def get(self):
+        db_entries = self.database.dump()
+        num_entries = len(db_entries)
+        response = ['----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))]
+        for db_entry in db_entries:
+            response.append('  [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover
+        response.append('-----------------------------------------------------------')
+        headers = {'Content-Type': 'text/plain'}
+        return make_response('\n'.join(response), 200, headers)
+
+class DumpHtml(Resource):
+    def __init__(self, database : Database) -> None:
+        super().__init__()
+        self.database = database
+
+    def get(self):
+        db_entries = self.database.dump()
+        num_entries = len(db_entries)
+        response = []
+        response.append('<HTML><HEAD><TITLE>Database Dump [{:3d} entries]</TITLE></HEAD><BODY>'.format(num_entries))
+        response.append('<H3>Database Dump [{:3d} entries]</H3><HR/>'.format(num_entries))
+        response.append('<TABLE border=1>')
+        response.append('<TR><TH>Type</TH><TH>Key</TH><TH>Value</TH></TR>')
+        for db_entry in db_entries:
+            response.append('<TR><TD>{:s}</TD><TD>{:s}</TD><TD>{:s}</TD></TR>'.format(*db_entry))
+        response.append('</TABLE></BODY></HTML>')
+
+        headers = {'Content-Type': 'text/html'}
+        return make_response(''.join(response), 200, headers)
+
 
 # Use 'path' type in Service and Sink because service_uuid and link_uuid might contain char '/' and Flask is unable to
 # recognize them in 'string' type.
@@ -148,4 +196,7 @@ RESOURCES = [
     ('api.connection_ids', ConnectionIds, '/context/<string:context_uuid>/service/<path:service_uuid>/connection_ids'),
     ('api.connections',    Connections,   '/context/<string:context_uuid>/service/<path:service_uuid>/connections'),
     ('api.connection',     Connection,    '/connection/<path:connection_uuid>'),
+
+    ('api.dump.text',      DumpText,      '/dump/text'),
+    ('api.dump.html',      DumpHtml,      '/dump/html'),
 ]
diff --git a/src/context/service/rest_server/Server.py b/src/context/service/rest_server/Server.py
index 5ff40017abdafe44a2fb6569e661457021bef9e5..ac4888d41bd9a84c57fc2d2f308bde4558787cbc 100644
--- a/src/context/service/rest_server/Server.py
+++ b/src/context/service/rest_server/Server.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging, threading
 from flask import Flask
 from flask_restful import Api
diff --git a/src/context/service/rest_server/__init__.py b/src/context/service/rest_server/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/context/service/rest_server/__init__.py
+++ b/src/context/service/rest_server/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/context/tests/Objects.py b/src/context/tests/Objects.py
new file mode 100644
index 0000000000000000000000000000000000000000..35c6cd51b7b286630a0171e5c614ea5effd1aa2d
--- /dev/null
+++ b/src/context/tests/Objects.py
@@ -0,0 +1,199 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from common.Constants import DEFAULT_CONTEXT_UUID, DEFAULT_TOPOLOGY_UUID
+from common.tools.object_factory.ConfigRule import json_config_rule_set
+from common.tools.object_factory.Connection import json_connection, json_connection_id
+from common.tools.object_factory.Constraint import json_constraint
+from common.tools.object_factory.Context import json_context, json_context_id
+from common.tools.object_factory.Device import json_device_id, json_device_packetrouter_disabled
+from common.tools.object_factory.EndPoint import json_endpoint, json_endpoint_id
+from common.tools.object_factory.Link import json_link, json_link_id
+from common.tools.object_factory.Service import json_service_id, json_service_l3nm_planned
+from common.tools.object_factory.Topology import json_topology, json_topology_id
+from context.proto.kpi_sample_types_pb2 import KpiSampleType
+
+
+# ----- Context --------------------------------------------------------------------------------------------------------
+CONTEXT_ID = json_context_id(DEFAULT_CONTEXT_UUID)
+CONTEXT    = json_context(DEFAULT_CONTEXT_UUID)
+
+
+# ----- Topology -------------------------------------------------------------------------------------------------------
+TOPOLOGY_ID = json_topology_id(DEFAULT_TOPOLOGY_UUID, context_id=CONTEXT_ID)
+TOPOLOGY    = json_topology(DEFAULT_TOPOLOGY_UUID, context_id=CONTEXT_ID)
+
+
+# ----- KPI Sample Types -----------------------------------------------------------------------------------------------
+PACKET_PORT_SAMPLE_TYPES = [
+    KpiSampleType.KPISAMPLETYPE_PACKETS_TRANSMITTED,
+    KpiSampleType.KPISAMPLETYPE_PACKETS_RECEIVED,
+    KpiSampleType.KPISAMPLETYPE_BYTES_TRANSMITTED,
+    KpiSampleType.KPISAMPLETYPE_BYTES_RECEIVED,
+]
+
+
+# ----- Device ---------------------------------------------------------------------------------------------------------
+DEVICE_R1_UUID  = 'R1'
+DEVICE_R1_ID    = json_device_id(DEVICE_R1_UUID)
+DEVICE_R1_EPS   = [
+    json_endpoint(DEVICE_R1_ID, 'EP2',   '10G', topology_id=TOPOLOGY_ID, kpi_sample_types=PACKET_PORT_SAMPLE_TYPES),
+    json_endpoint(DEVICE_R1_ID, 'EP3',   '10G', topology_id=TOPOLOGY_ID, kpi_sample_types=PACKET_PORT_SAMPLE_TYPES),
+    json_endpoint(DEVICE_R1_ID, 'EP100', '10G', topology_id=TOPOLOGY_ID, kpi_sample_types=PACKET_PORT_SAMPLE_TYPES),
+]
+DEVICE_R1_RULES = [
+    json_config_rule_set('dev/rsrc1/value', 'value1'),
+    json_config_rule_set('dev/rsrc2/value', 'value2'),
+    json_config_rule_set('dev/rsrc3/value', 'value3'),
+]
+DEVICE_R1       = json_device_packetrouter_disabled(
+    DEVICE_R1_UUID, endpoints=DEVICE_R1_EPS, config_rules=DEVICE_R1_RULES)
+
+
+DEVICE_R2_UUID  = 'R2'
+DEVICE_R2_ID    = json_device_id(DEVICE_R2_UUID)
+DEVICE_R2_EPS   = [
+    json_endpoint(DEVICE_R2_ID, 'EP1',   '10G', topology_id=TOPOLOGY_ID, kpi_sample_types=PACKET_PORT_SAMPLE_TYPES),
+    json_endpoint(DEVICE_R2_ID, 'EP3',   '10G', topology_id=TOPOLOGY_ID, kpi_sample_types=PACKET_PORT_SAMPLE_TYPES),
+    json_endpoint(DEVICE_R2_ID, 'EP100', '10G', topology_id=TOPOLOGY_ID, kpi_sample_types=PACKET_PORT_SAMPLE_TYPES),
+]
+DEVICE_R2_RULES = [
+    json_config_rule_set('dev/rsrc1/value', 'value4'),
+    json_config_rule_set('dev/rsrc2/value', 'value5'),
+    json_config_rule_set('dev/rsrc3/value', 'value6'),
+]
+DEVICE_R2       = json_device_packetrouter_disabled(
+    DEVICE_R2_UUID, endpoints=DEVICE_R2_EPS, config_rules=DEVICE_R2_RULES)
+
+
+DEVICE_R3_UUID  = 'R3'
+DEVICE_R3_ID    = json_device_id(DEVICE_R3_UUID)
+DEVICE_R3_EPS   = [
+    json_endpoint(DEVICE_R3_ID, 'EP1',   '10G', topology_id=TOPOLOGY_ID, kpi_sample_types=PACKET_PORT_SAMPLE_TYPES),
+    json_endpoint(DEVICE_R3_ID, 'EP2',   '10G', topology_id=TOPOLOGY_ID, kpi_sample_types=PACKET_PORT_SAMPLE_TYPES),
+    json_endpoint(DEVICE_R3_ID, 'EP100', '10G', topology_id=TOPOLOGY_ID, kpi_sample_types=PACKET_PORT_SAMPLE_TYPES),
+]
+DEVICE_R3_RULES = [
+    json_config_rule_set('dev/rsrc1/value', 'value4'),
+    json_config_rule_set('dev/rsrc2/value', 'value5'),
+    json_config_rule_set('dev/rsrc3/value', 'value6'),
+]
+DEVICE_R3       = json_device_packetrouter_disabled(
+    DEVICE_R3_UUID, endpoints=DEVICE_R3_EPS, config_rules=DEVICE_R3_RULES)
+
+
+# ----- Link -----------------------------------------------------------------------------------------------------------
+LINK_R1_R2_UUID  = 'R1/EP2-R2/EP1'
+LINK_R1_R2_ID    = json_link_id(LINK_R1_R2_UUID)
+LINK_R1_R2_EPIDS = [
+    json_endpoint_id(DEVICE_R1_ID, 'EP2', topology_id=TOPOLOGY_ID),
+    json_endpoint_id(DEVICE_R2_ID, 'EP1', topology_id=TOPOLOGY_ID),
+]
+LINK_R1_R2       = json_link(LINK_R1_R2_UUID, LINK_R1_R2_EPIDS)
+
+
+LINK_R2_R3_UUID  = 'R2/EP3-R3/EP2'
+LINK_R2_R3_ID    = json_link_id(LINK_R2_R3_UUID)
+LINK_R2_R3_EPIDS = [
+    json_endpoint_id(DEVICE_R2_ID, 'EP3', topology_id=TOPOLOGY_ID),
+    json_endpoint_id(DEVICE_R3_ID, 'EP2', topology_id=TOPOLOGY_ID),
+]
+LINK_R2_R3       = json_link(LINK_R2_R3_UUID, LINK_R2_R3_EPIDS)
+
+
+LINK_R1_R3_UUID  = 'R1/EP3-R3/EP1'
+LINK_R1_R3_ID    = json_link_id(LINK_R1_R3_UUID)
+LINK_R1_R3_EPIDS = [
+    json_endpoint_id(DEVICE_R1_ID, 'EP3', topology_id=TOPOLOGY_ID),
+    json_endpoint_id(DEVICE_R3_ID, 'EP1', topology_id=TOPOLOGY_ID),
+]
+LINK_R1_R3       = json_link(LINK_R1_R3_UUID, LINK_R1_R3_EPIDS)
+
+
+# ----- Service --------------------------------------------------------------------------------------------------------
+SERVICE_R1_R2_UUID  = 'SVC:R1/EP100-R2/EP100'
+SERVICE_R1_R2_ID    = json_service_id(SERVICE_R1_R2_UUID, context_id=CONTEXT_ID)
+SERVICE_R1_R2_EPIDS = [
+    json_endpoint_id(DEVICE_R1_ID, 'EP100', topology_id=TOPOLOGY_ID),
+    json_endpoint_id(DEVICE_R2_ID, 'EP100', topology_id=TOPOLOGY_ID),
+]
+SERVICE_R1_R2_CONST = [
+    json_constraint('latency_ms', '15.2'),
+    json_constraint('jitter_us',  '1.2'),
+]
+SERVICE_R1_R2_RULES = [
+    json_config_rule_set('svc/rsrc1/value', 'value7'),
+    json_config_rule_set('svc/rsrc2/value', 'value8'),
+    json_config_rule_set('svc/rsrc3/value', 'value9'),
+]
+SERVICE_R1_R2       = json_service_l3nm_planned(
+    SERVICE_R1_R2_UUID, endpoint_ids=SERVICE_R1_R2_EPIDS, constraints=SERVICE_R1_R2_CONST,
+    config_rules=SERVICE_R1_R2_RULES)
+
+
+SERVICE_R1_R3_UUID  = 'SVC:R1/EP100-R3/EP100'
+SERVICE_R1_R3_ID    = json_service_id(SERVICE_R1_R3_UUID, context_id=CONTEXT_ID)
+SERVICE_R1_R3_EPIDS = [
+    json_endpoint_id(DEVICE_R1_ID, 'EP100', topology_id=TOPOLOGY_ID),
+    json_endpoint_id(DEVICE_R3_ID, 'EP100', topology_id=TOPOLOGY_ID),
+]
+SERVICE_R1_R3_CONST = [
+    json_constraint('latency_ms', '5.8'),
+    json_constraint('jitter_us',  '0.1'),
+]
+SERVICE_R1_R3_RULES = [
+    json_config_rule_set('svc/rsrc1/value', 'value7'),
+    json_config_rule_set('svc/rsrc2/value', 'value8'),
+    json_config_rule_set('svc/rsrc3/value', 'value9'),
+]
+SERVICE_R1_R3       = json_service_l3nm_planned(
+    SERVICE_R1_R3_UUID, endpoint_ids=SERVICE_R1_R3_EPIDS, constraints=SERVICE_R1_R3_CONST,
+    config_rules=SERVICE_R1_R3_RULES)
+
+
+SERVICE_R2_R3_UUID  = 'SVC:R2/EP100-R3/EP100'
+SERVICE_R2_R3_ID    = json_service_id(SERVICE_R2_R3_UUID, context_id=CONTEXT_ID)
+SERVICE_R2_R3_EPIDS = [
+    json_endpoint_id(DEVICE_R2_ID, 'EP100', topology_id=TOPOLOGY_ID),
+    json_endpoint_id(DEVICE_R3_ID, 'EP100', topology_id=TOPOLOGY_ID),
+]
+SERVICE_R2_R3_CONST = [
+    json_constraint('latency_ms', '23.1'),
+    json_constraint('jitter_us',  '3.4'),
+]
+SERVICE_R2_R3_RULES = [
+    json_config_rule_set('svc/rsrc1/value', 'value7'),
+    json_config_rule_set('svc/rsrc2/value', 'value8'),
+    json_config_rule_set('svc/rsrc3/value', 'value9'),
+]
+SERVICE_R2_R3       = json_service_l3nm_planned(
+    SERVICE_R2_R3_UUID, endpoint_ids=SERVICE_R2_R3_EPIDS, constraints=SERVICE_R2_R3_CONST,
+    config_rules=SERVICE_R2_R3_RULES)
+
+
+# ----- Connection -----------------------------------------------------------------------------------------------------
+CONNECTION_R1_R3_UUID   = 'CON:R1/EP100-R3/EP100'
+CONNECTION_R1_R3_ID     = json_connection_id(CONNECTION_R1_R3_UUID)
+CONNECTION_R1_R3_EPIDS  = [
+    json_endpoint_id(DEVICE_R1_ID, 'EP100', topology_id=TOPOLOGY_ID),
+    json_endpoint_id(DEVICE_R1_ID, 'EP2',   topology_id=TOPOLOGY_ID),
+    json_endpoint_id(DEVICE_R2_ID, 'EP1',   topology_id=TOPOLOGY_ID),
+    json_endpoint_id(DEVICE_R2_ID, 'EP3',   topology_id=TOPOLOGY_ID),
+    json_endpoint_id(DEVICE_R3_ID, 'EP2',   topology_id=TOPOLOGY_ID),
+    json_endpoint_id(DEVICE_R3_ID, 'EP100', topology_id=TOPOLOGY_ID),
+]
+CONNECTION_R1_R3_SVCIDS = [SERVICE_R1_R2_ID, SERVICE_R2_R3_ID]
+CONNECTION_R1_R3        = json_connection(
+    CONNECTION_R1_R3_UUID, service_id=SERVICE_R1_R3_ID, path_hops_endpoint_ids=CONNECTION_R1_R3_EPIDS,
+    sub_service_ids=CONNECTION_R1_R3_SVCIDS)
diff --git a/src/context/tests/Tools.py b/src/context/tests/Tools.py
deleted file mode 100644
index b4d1d8953126fb74b8ab17a71fbba3b19acba733..0000000000000000000000000000000000000000
--- a/src/context/tests/Tools.py
+++ /dev/null
@@ -1,27 +0,0 @@
-import json
-from copy import deepcopy
-from typing import Any, Dict, Union
-from context.proto.context_pb2 import ConfigActionEnum
-
-def config_rule(action : ConfigActionEnum, resource_key : str, resource_value : Union[str, Dict[str, Any]]):
-    if not isinstance(resource_value, str): resource_value = json.dumps(resource_value, sort_keys=True)
-    return {'action': action, 'resource_key': resource_key, 'resource_value': resource_value}
-
-def config_rule_set(resource_key : str, resource_value : Union[str, Dict[str, Any]]):
-    return config_rule(ConfigActionEnum.CONFIGACTION_SET, resource_key, resource_value)
-
-def config_rule_delete(resource_key : str, resource_value : Union[str, Dict[str, Any]]):
-    return config_rule(ConfigActionEnum.CONFIGACTION_DELETE, resource_key, resource_value)
-
-def endpoint_id(device_id, endpoint_uuid, topology_id=None):
-    result = {'device_id': deepcopy(device_id), 'endpoint_uuid': {'uuid': endpoint_uuid}}
-    if topology_id is not None: result['topology_id'] = deepcopy(topology_id)
-    return result
-
-def endpoint(device_id, endpoint_uuid, endpoint_type, topology_id=None, kpi_sample_types=[]):
-    result = {
-        'endpoint_id': endpoint_id(device_id, endpoint_uuid, topology_id=topology_id),
-        'endpoint_type': endpoint_type,
-    }
-    if len(kpi_sample_types) > 0: result['kpi_sample_types'] = deepcopy(kpi_sample_types)
-    return result
diff --git a/src/context/tests/__init__.py b/src/context/tests/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/context/tests/__init__.py
+++ b/src/context/tests/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/context/tests/example_objects.py b/src/context/tests/example_objects.py
deleted file mode 100644
index ef2117bc60dff52e90bca5fd1191064086bdcc08..0000000000000000000000000000000000000000
--- a/src/context/tests/example_objects.py
+++ /dev/null
@@ -1,220 +0,0 @@
-from copy import deepcopy
-from common.Constants import DEFAULT_CONTEXT_UUID, DEFAULT_TOPOLOGY_UUID
-from context.proto.context_pb2 import (
-    DeviceDriverEnum, DeviceOperationalStatusEnum, ServiceStatusEnum, ServiceTypeEnum)
-from context.proto.kpi_sample_types_pb2 import KpiSampleType
-from .Tools import config_rule_set, endpoint, endpoint_id
-
-# Some example objects to be used by the tests
-
-## use "deepcopy" to prevent propagating forced changes during tests
-CONTEXT_ID = {'context_uuid': {'uuid': DEFAULT_CONTEXT_UUID}}
-CONTEXT = {
-    'context_id': deepcopy(CONTEXT_ID),
-    'topology_ids': [],
-    'service_ids': [],
-}
-
-TOPOLOGY_ID = {
-    'context_id': deepcopy(CONTEXT_ID),
-    'topology_uuid': {'uuid': DEFAULT_TOPOLOGY_UUID},
-}
-TOPOLOGY = {
-    'topology_id': deepcopy(TOPOLOGY_ID),
-    'device_ids': [],
-    'link_ids': [],
-}
-
-PACKET_PORT_SAMPLE_TYPES = [
-    KpiSampleType.KPISAMPLETYPE_PACKETS_TRANSMITTED,
-    KpiSampleType.KPISAMPLETYPE_PACKETS_RECEIVED,
-    KpiSampleType.KPISAMPLETYPE_BYTES_TRANSMITTED,
-    KpiSampleType.KPISAMPLETYPE_BYTES_RECEIVED,
-]
-
-def _endpoint_id(device_id, endpoint_uuid):
-    return endpoint_id(device_id, endpoint_uuid, topology_id=TOPOLOGY_ID)
-
-def _endpoint(device_id, endpoint_uuid, endpoint_type):
-    return endpoint(
-        device_id, endpoint_uuid, endpoint_type, topology_id=TOPOLOGY_ID, kpi_sample_types=PACKET_PORT_SAMPLE_TYPES)
-
-DEVICE1_UUID = 'DEV1'
-DEVICE1_ID = {'device_uuid': {'uuid': DEVICE1_UUID}}
-DEVICE1 = {
-    'device_id': deepcopy(DEVICE1_ID),
-    'device_type': 'packet-router',
-    'device_config': {'config_rules': [
-        config_rule_set('dev/rsrc1/value', 'value1'),
-        config_rule_set('dev/rsrc2/value', 'value2'),
-        config_rule_set('dev/rsrc3/value', 'value3'),
-    ]},
-    'device_operational_status': DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_ENABLED,
-    'device_drivers': [DeviceDriverEnum.DEVICEDRIVER_OPENCONFIG, DeviceDriverEnum.DEVICEDRIVER_P4],
-    'device_endpoints': [
-        _endpoint(DEVICE1_ID, 'EP2',   'port-packet-100G'),
-        _endpoint(DEVICE1_ID, 'EP3',   'port-packet-100G'),
-        _endpoint(DEVICE1_ID, 'EP100', 'port-packet-10G' ),
-    ],
-}
-
-DEVICE2_UUID = 'DEV2'
-DEVICE2_ID = {'device_uuid': {'uuid': DEVICE2_UUID}}
-DEVICE2 = {
-    'device_id': deepcopy(DEVICE2_ID),
-    'device_type': 'packet-router',
-    'device_config': {'config_rules': [
-        config_rule_set('dev/rsrc1/value', 'value4'),
-        config_rule_set('dev/rsrc2/value', 'value5'),
-        config_rule_set('dev/rsrc3/value', 'value6'),
-    ]},
-    'device_operational_status': DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_ENABLED,
-    'device_drivers': [DeviceDriverEnum.DEVICEDRIVER_OPENCONFIG, DeviceDriverEnum.DEVICEDRIVER_P4],
-    'device_endpoints': [
-        _endpoint(DEVICE2_ID, 'EP1',   'port-packet-100G'),
-        _endpoint(DEVICE2_ID, 'EP3',   'port-packet-100G'),
-        _endpoint(DEVICE2_ID, 'EP100', 'port-packet-10G' ),
-    ],
-}
-
-DEVICE3_UUID = 'DEV3'
-DEVICE3_ID = {'device_uuid': {'uuid': DEVICE3_UUID}}
-DEVICE3 = {
-    'device_id': deepcopy(DEVICE3_ID),
-    'device_type': 'packet-router',
-    'device_config': {'config_rules': [
-        config_rule_set('dev/rsrc1/value', 'value4'),
-        config_rule_set('dev/rsrc2/value', 'value5'),
-        config_rule_set('dev/rsrc3/value', 'value6'),
-    ]},
-    'device_operational_status': DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_ENABLED,
-    'device_drivers': [DeviceDriverEnum.DEVICEDRIVER_OPENCONFIG, DeviceDriverEnum.DEVICEDRIVER_P4],
-    'device_endpoints': [
-        _endpoint(DEVICE3_ID, 'EP1',   'port-packet-100G'),
-        _endpoint(DEVICE3_ID, 'EP2',   'port-packet-100G'),
-        _endpoint(DEVICE3_ID, 'EP100', 'port-packet-10G' ),
-    ],
-}
-
-LINK_DEV1_DEV2_UUID = 'DEV1/EP2 ==> DEV2/EP1'
-LINK_DEV1_DEV2_ID = {'link_uuid': {'uuid': LINK_DEV1_DEV2_UUID}}
-LINK_DEV1_DEV2 = {
-    'link_id': deepcopy(LINK_DEV1_DEV2_ID),
-    'link_endpoint_ids' : [
-        _endpoint_id(DEVICE1_ID, 'EP2'),
-        _endpoint_id(DEVICE2_ID, 'EP1'),
-    ]
-}
-
-LINK_DEV2_DEV3_UUID = 'DEV2/EP3 ==> DEV3/EP2'
-LINK_DEV2_DEV3_ID = {'link_uuid': {'uuid': LINK_DEV2_DEV3_UUID}}
-LINK_DEV2_DEV3 = {
-    'link_id': deepcopy(LINK_DEV2_DEV3_ID),
-    'link_endpoint_ids' : [
-        _endpoint_id(DEVICE2_ID, 'EP3'),
-        _endpoint_id(DEVICE3_ID, 'EP2'),
-    ]
-}
-
-LINK_DEV1_DEV3_UUID = 'DEV1/EP3 ==> DEV3/EP1'
-LINK_DEV1_DEV3_ID = {'link_uuid': {'uuid': LINK_DEV1_DEV3_UUID}}
-LINK_DEV1_DEV3 = {
-    'link_id': deepcopy(LINK_DEV1_DEV3_ID),
-    'link_endpoint_ids' : [
-        _endpoint_id(DEVICE1_ID, 'EP3'),
-        _endpoint_id(DEVICE3_ID, 'EP1'),
-    ]
-}
-
-SERVICE_DEV1_DEV2_UUID = 'SVC:DEV1/EP100-DEV2/EP100'
-SERVICE_DEV1_DEV2_ID = {
-    'context_id': deepcopy(CONTEXT_ID),
-    'service_uuid': {'uuid': SERVICE_DEV1_DEV2_UUID},
-}
-SERVICE_DEV1_DEV2 = {
-    'service_id': deepcopy(SERVICE_DEV1_DEV2_ID),
-    'service_type': ServiceTypeEnum.SERVICETYPE_L3NM,
-    'service_endpoint_ids' : [
-        _endpoint_id(DEVICE1_ID, 'EP100'),
-        _endpoint_id(DEVICE2_ID, 'EP100'),
-    ],
-    'service_constraints': [
-        {'constraint_type': 'latency_ms', 'constraint_value': '15.2'},
-        {'constraint_type': 'jitter_us', 'constraint_value': '1.2'},
-    ],
-    'service_status': {'service_status': ServiceStatusEnum.SERVICESTATUS_ACTIVE},
-    'service_config': {'config_rules': [
-        config_rule_set('svc/rsrc1/value', 'value7'),
-        config_rule_set('svc/rsrc2/value', 'value8'),
-        config_rule_set('svc/rsrc3/value', 'value9'),
-    ]},
-}
-
-SERVICE_DEV1_DEV3_UUID = 'SVC:DEV1/EP100-DEV3/EP100'
-SERVICE_DEV1_DEV3_ID = {
-    'context_id': deepcopy(CONTEXT_ID),
-    'service_uuid': {'uuid': SERVICE_DEV1_DEV3_UUID},
-}
-SERVICE_DEV1_DEV3 = {
-    'service_id': deepcopy(SERVICE_DEV1_DEV3_ID),
-    'service_type': ServiceTypeEnum.SERVICETYPE_L3NM,
-    'service_endpoint_ids' : [
-        _endpoint_id(DEVICE1_ID, 'EP100'),
-        _endpoint_id(DEVICE3_ID, 'EP100'),
-    ],
-    'service_constraints': [
-        {'constraint_type': 'latency_ms', 'constraint_value': '5.8'},
-        {'constraint_type': 'jitter_us', 'constraint_value': '0.1'},
-    ],
-    'service_status': {'service_status': ServiceStatusEnum.SERVICESTATUS_ACTIVE},
-    'service_config': {'config_rules': [
-        config_rule_set('svc/rsrc1/value', 'value7'),
-        config_rule_set('svc/rsrc2/value', 'value8'),
-        config_rule_set('svc/rsrc3/value', 'value9'),
-    ]},
-}
-
-SERVICE_DEV2_DEV3_UUID = 'SVC:DEV2/EP100-DEV3/EP100'
-SERVICE_DEV2_DEV3_ID = {
-    'context_id': deepcopy(CONTEXT_ID),
-    'service_uuid': {'uuid': SERVICE_DEV2_DEV3_UUID},
-}
-SERVICE_DEV2_DEV3 = {
-    'service_id': deepcopy(SERVICE_DEV2_DEV3_ID),
-    'service_type': ServiceTypeEnum.SERVICETYPE_L3NM,
-    'service_endpoint_ids' : [
-        _endpoint_id(DEVICE2_ID, 'EP100'),
-        _endpoint_id(DEVICE3_ID, 'EP100'),
-    ],
-    'service_constraints': [
-        {'constraint_type': 'latency_ms', 'constraint_value': '23.1'},
-        {'constraint_type': 'jitter_us', 'constraint_value': '3.4'},
-    ],
-    'service_status': {'service_status': ServiceStatusEnum.SERVICESTATUS_ACTIVE},
-    'service_config': {'config_rules': [
-        config_rule_set('svc/rsrc1/value', 'value7'),
-        config_rule_set('svc/rsrc2/value', 'value8'),
-        config_rule_set('svc/rsrc3/value', 'value9'),
-    ]},
-}
-
-CONNECTION_DEV1_DEV3_UUID = 'CON:DEV1/EP100-DEV3/EP100'
-CONNECTION_DEV1_DEV3_ID = {
-    'connection_uuid': {'uuid': CONNECTION_DEV1_DEV3_UUID},
-}
-CONNECTION_DEV1_DEV3 = {
-    'connection_id': deepcopy(CONNECTION_DEV1_DEV3_ID),
-    'service_id': deepcopy(SERVICE_DEV1_DEV3_ID),
-    'path_hops_endpoint_ids' : [
-        _endpoint_id(DEVICE1_ID, 'EP100'),
-        _endpoint_id(DEVICE1_ID, 'EP2'),
-        _endpoint_id(DEVICE2_ID, 'EP1'),
-        _endpoint_id(DEVICE2_ID, 'EP3'),
-        _endpoint_id(DEVICE3_ID, 'EP2'),
-        _endpoint_id(DEVICE3_ID, 'EP100'),
-    ],
-    'sub_service_ids': [
-        deepcopy(SERVICE_DEV1_DEV2_ID),
-        deepcopy(SERVICE_DEV2_DEV3_ID),
-    ],
-}
diff --git a/src/context/tests/test_unitary.py b/src/context/tests/test_unitary.py
index 7d3cd9b965fbc0e4df0acc2ec092a1448095d392..10f44d9ad87a71d5935151f4ae724e9d04b5d5ce 100644
--- a/src/context/tests/test_unitary.py
+++ b/src/context/tests/test_unitary.py
@@ -1,6 +1,19 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 # pylint: disable=too-many-lines
-import copy, grpc, logging, os, pytest, requests, threading, time, urllib
-from queue import Queue, Empty
+import copy, grpc, logging, os, pytest, requests, time, urllib
 from typing import Tuple
 from common.Constants import DEFAULT_CONTEXT_UUID, DEFAULT_TOPOLOGY_UUID
 from common.orm.Database import Database
@@ -15,6 +28,7 @@ from common.type_checkers.Assertions import (
 from context.Config import (
     GRPC_SERVICE_PORT, GRPC_MAX_WORKERS, GRPC_GRACE_PERIOD, RESTAPI_SERVICE_PORT, RESTAPI_BASE_URL)
 from context.client.ContextClient import ContextClient
+from context.client.EventsCollector import EventsCollector
 from context.proto.context_pb2 import (
     Connection, ConnectionEvent, ConnectionId, Context, ContextEvent, ContextId, Device, DeviceEvent, DeviceId,
     DeviceOperationalStatusEnum, Empty, EventTypeEnum, Link, LinkEvent, LinkId, Service, ServiceEvent, ServiceId,
@@ -25,12 +39,11 @@ from context.service.grpc_server.ContextService import ContextService
 from context.service.Populate import populate
 from context.service.rest_server.Server import Server as RestServer
 from context.service.rest_server.Resources import RESOURCES
-from .example_objects import (
-    CONNECTION_DEV1_DEV3, CONNECTION_DEV1_DEV3_ID, CONNECTION_DEV1_DEV3_UUID, CONTEXT, CONTEXT_ID, DEVICE1, DEVICE1_ID,
-    DEVICE1_UUID, DEVICE2, DEVICE2_ID, DEVICE2_UUID, DEVICE3, DEVICE3_ID, DEVICE3_UUID, LINK_DEV1_DEV2,
-    LINK_DEV1_DEV2_ID, LINK_DEV1_DEV2_UUID, SERVICE_DEV1_DEV2, SERVICE_DEV1_DEV2_ID, SERVICE_DEV1_DEV2_UUID,
-    SERVICE_DEV1_DEV3, SERVICE_DEV1_DEV3_ID, SERVICE_DEV1_DEV3_UUID, SERVICE_DEV2_DEV3, SERVICE_DEV2_DEV3_ID,
-    SERVICE_DEV2_DEV3_UUID, TOPOLOGY, TOPOLOGY_ID)
+from .Objects import (
+    CONNECTION_R1_R3, CONNECTION_R1_R3_ID, CONNECTION_R1_R3_UUID, CONTEXT, CONTEXT_ID, DEVICE_R1, DEVICE_R1_ID,
+    DEVICE_R1_UUID, DEVICE_R2, DEVICE_R2_ID, DEVICE_R2_UUID, DEVICE_R3, DEVICE_R3_ID, DEVICE_R3_UUID, LINK_R1_R2,
+    LINK_R1_R2_ID, LINK_R1_R2_UUID, SERVICE_R1_R2, SERVICE_R1_R2_ID, SERVICE_R1_R2_UUID, SERVICE_R1_R3,
+    SERVICE_R1_R3_ID, SERVICE_R1_R3_UUID, SERVICE_R2_R3, SERVICE_R2_R3_ID, SERVICE_R2_R3_UUID, TOPOLOGY, TOPOLOGY_ID)
 
 LOGGER = logging.getLogger(__name__)
 LOGGER.setLevel(logging.DEBUG)
@@ -98,74 +111,6 @@ def do_rest_request(url : str):
     assert reply.status_code == 200, 'Reply failed with code {}'.format(reply.status_code)
     return reply.json()
 
-class EventsCollector:
-    def __init__(self, context_client_grpc : ContextClient) -> None: # pylint: disable=redefined-outer-name
-        self._events_queue = Queue()
-
-        self._context_stream    = context_client_grpc.GetContextEvents(Empty())
-        self._topology_stream   = context_client_grpc.GetTopologyEvents(Empty())
-        self._device_stream     = context_client_grpc.GetDeviceEvents(Empty())
-        self._link_stream       = context_client_grpc.GetLinkEvents(Empty())
-        self._service_stream    = context_client_grpc.GetServiceEvents(Empty())
-        self._connection_stream = context_client_grpc.GetConnectionEvents(Empty())
-
-        self._context_thread    = threading.Thread(target=self._collect, args=(self._context_stream   ,), daemon=False)
-        self._topology_thread   = threading.Thread(target=self._collect, args=(self._topology_stream  ,), daemon=False)
-        self._device_thread     = threading.Thread(target=self._collect, args=(self._device_stream    ,), daemon=False)
-        self._link_thread       = threading.Thread(target=self._collect, args=(self._link_stream      ,), daemon=False)
-        self._service_thread    = threading.Thread(target=self._collect, args=(self._service_stream   ,), daemon=False)
-        self._connection_thread = threading.Thread(target=self._collect, args=(self._connection_stream,), daemon=False)
-
-    def _collect(self, events_stream) -> None:
-        try:
-            for event in events_stream:
-                self._events_queue.put_nowait(event)
-        except grpc.RpcError as e:
-            if e.code() != grpc.StatusCode.CANCELLED: # pylint: disable=no-member
-                raise # pragma: no cover
-
-    def start(self):
-        self._context_thread.start()
-        self._topology_thread.start()
-        self._device_thread.start()
-        self._link_thread.start()
-        self._service_thread.start()
-        self._connection_thread.start()
-
-    def get_event(self, block : bool = True, timeout : float = 0.1):
-        return self._events_queue.get(block=block, timeout=timeout)
-
-    def get_events(self, block : bool = True, timeout : float = 0.1, count : int = None):
-        events = []
-        if count is None:
-            while True:
-                try:
-                    events.append(self.get_event(block=block, timeout=timeout))
-                except Empty: # pylint: disable=catching-non-exception
-                    break
-        else:
-            for _ in range(count):
-                try:
-                    events.append(self.get_event(block=block, timeout=timeout))
-                except Empty: # pylint: disable=catching-non-exception
-                    pass
-        return sorted(events, key=lambda e: e.event.timestamp)
-
-    def stop(self):
-        self._context_stream.cancel()
-        self._topology_stream.cancel()
-        self._device_stream.cancel()
-        self._link_stream.cancel()
-        self._service_stream.cancel()
-        self._connection_stream.cancel()
-
-        self._context_thread.join()
-        self._topology_thread.join()
-        self._device_thread.join()
-        self._link_thread.join()
-        self._service_thread.join()
-        self._connection_thread.join()
-
 
 # ----- Test gRPC methods ----------------------------------------------------------------------------------------------
 
@@ -218,7 +163,7 @@ def test_grpc_context(
     assert e.value.details() == msg
 
     with pytest.raises(grpc.RpcError) as e:
-        WRONG_SERVICE_ID = copy.deepcopy(SERVICE_DEV1_DEV2_ID)
+        WRONG_SERVICE_ID = copy.deepcopy(SERVICE_R1_R2_ID)
         WRONG_SERVICE_ID['context_id']['context_uuid']['uuid'] = 'wrong-context-uuid'
         WRONG_CONTEXT = copy.deepcopy(CONTEXT)
         WRONG_CONTEXT['service_ids'].append(WRONG_SERVICE_ID)
@@ -455,9 +400,9 @@ def test_grpc_device(
 
     # ----- Get when the object does not exist -------------------------------------------------------------------------
     with pytest.raises(grpc.RpcError) as e:
-        context_client_grpc.GetDevice(DeviceId(**DEVICE1_ID))
+        context_client_grpc.GetDevice(DeviceId(**DEVICE_R1_ID))
     assert e.value.code() == grpc.StatusCode.NOT_FOUND
-    assert e.value.details() == 'Device({:s}) not found'.format(DEVICE1_UUID)
+    assert e.value.details() == 'Device({:s}) not found'.format(DEVICE_R1_UUID)
 
     # ----- List when the object does not exist ------------------------------------------------------------------------
     response = context_client_grpc.ListDeviceIds(Empty())
@@ -476,32 +421,32 @@ def test_grpc_device(
 
     # ----- Create the object ------------------------------------------------------------------------------------------
     with pytest.raises(grpc.RpcError) as e:
-        WRONG_DEVICE = copy.deepcopy(DEVICE1)
+        WRONG_DEVICE = copy.deepcopy(DEVICE_R1)
         WRONG_DEVICE['device_endpoints'][0]['endpoint_id']['device_id']['device_uuid']['uuid'] = 'wrong-device-uuid'
         context_client_grpc.SetDevice(Device(**WRONG_DEVICE))
     assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
     msg = 'request.device_endpoints[0].device_id.device_uuid.uuid(wrong-device-uuid) is invalid; '\
-          'should be == request.device_id.device_uuid.uuid(DEV1)'
+          'should be == request.device_id.device_uuid.uuid({:s})'.format(DEVICE_R1_UUID)
     assert e.value.details() == msg
 
-    response = context_client_grpc.SetDevice(Device(**DEVICE1))
-    assert response.device_uuid.uuid == DEVICE1_UUID
+    response = context_client_grpc.SetDevice(Device(**DEVICE_R1))
+    assert response.device_uuid.uuid == DEVICE_R1_UUID
 
     # ----- Check create event -----------------------------------------------------------------------------------------
     event = events_collector.get_event(block=True)
     assert isinstance(event, DeviceEvent)
     assert event.event.event_type == EventTypeEnum.EVENTTYPE_CREATE
-    assert event.device_id.device_uuid.uuid == DEVICE1_UUID
+    assert event.device_id.device_uuid.uuid == DEVICE_R1_UUID
 
     # ----- Update the object ------------------------------------------------------------------------------------------
-    response = context_client_grpc.SetDevice(Device(**DEVICE1))
-    assert response.device_uuid.uuid == DEVICE1_UUID
+    response = context_client_grpc.SetDevice(Device(**DEVICE_R1))
+    assert response.device_uuid.uuid == DEVICE_R1_UUID
 
     # ----- Check update event -----------------------------------------------------------------------------------------
     event = events_collector.get_event(block=True)
     assert isinstance(event, DeviceEvent)
     assert event.event.event_type == EventTypeEnum.EVENTTYPE_UPDATE
-    assert event.device_id.device_uuid.uuid == DEVICE1_UUID
+    assert event.device_id.device_uuid.uuid == DEVICE_R1_UUID
 
     # ----- Dump state of database after create/update the object ------------------------------------------------------
     db_entries = context_database.dump()
@@ -509,34 +454,34 @@ def test_grpc_device(
     for db_entry in db_entries:
         LOGGER.info('  [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover
     LOGGER.info('-----------------------------------------------------------')
-    assert len(db_entries) == 41
+    assert len(db_entries) == 40
 
     # ----- Get when the object exists ---------------------------------------------------------------------------------
-    response = context_client_grpc.GetDevice(DeviceId(**DEVICE1_ID))
-    assert response.device_id.device_uuid.uuid == DEVICE1_UUID
+    response = context_client_grpc.GetDevice(DeviceId(**DEVICE_R1_ID))
+    assert response.device_id.device_uuid.uuid == DEVICE_R1_UUID
     assert response.device_type == 'packet-router'
     assert len(response.device_config.config_rules) == 3
-    assert response.device_operational_status == DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_ENABLED
-    assert len(response.device_drivers) == 2
+    assert response.device_operational_status == DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_DISABLED
+    assert len(response.device_drivers) == 1
     assert len(response.device_endpoints) == 3
 
     # ----- List when the object exists --------------------------------------------------------------------------------
     response = context_client_grpc.ListDeviceIds(Empty())
     assert len(response.device_ids) == 1
-    assert response.device_ids[0].device_uuid.uuid == DEVICE1_UUID
+    assert response.device_ids[0].device_uuid.uuid == DEVICE_R1_UUID
 
     response = context_client_grpc.ListDevices(Empty())
     assert len(response.devices) == 1
-    assert response.devices[0].device_id.device_uuid.uuid == DEVICE1_UUID
+    assert response.devices[0].device_id.device_uuid.uuid == DEVICE_R1_UUID
     assert response.devices[0].device_type == 'packet-router'
     assert len(response.devices[0].device_config.config_rules) == 3
-    assert response.devices[0].device_operational_status == DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_ENABLED
-    assert len(response.devices[0].device_drivers) == 2
+    assert response.devices[0].device_operational_status == DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_DISABLED
+    assert len(response.devices[0].device_drivers) == 1
     assert len(response.devices[0].device_endpoints) == 3
 
     # ----- Create object relation -------------------------------------------------------------------------------------
     TOPOLOGY_WITH_DEVICE = copy.deepcopy(TOPOLOGY)
-    TOPOLOGY_WITH_DEVICE['device_ids'].append(DEVICE1_ID)
+    TOPOLOGY_WITH_DEVICE['device_ids'].append(DEVICE_R1_ID)
     response = context_client_grpc.SetTopology(Topology(**TOPOLOGY_WITH_DEVICE))
     assert response.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID
     assert response.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID
@@ -553,7 +498,7 @@ def test_grpc_device(
     assert response.topology_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID
     assert response.topology_id.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID
     assert len(response.device_ids) == 1
-    assert response.device_ids[0].device_uuid.uuid == DEVICE1_UUID
+    assert response.device_ids[0].device_uuid.uuid == DEVICE_R1_UUID
     assert len(response.link_ids) == 0
 
     # ----- Dump state of database after creating the object relation --------------------------------------------------
@@ -562,10 +507,10 @@ def test_grpc_device(
     for db_entry in db_entries:
         LOGGER.info('  [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover
     LOGGER.info('-----------------------------------------------------------')
-    assert len(db_entries) == 41
+    assert len(db_entries) == 40
 
     # ----- Remove the object ------------------------------------------------------------------------------------------
-    context_client_grpc.RemoveDevice(DeviceId(**DEVICE1_ID))
+    context_client_grpc.RemoveDevice(DeviceId(**DEVICE_R1_ID))
     context_client_grpc.RemoveTopology(TopologyId(**TOPOLOGY_ID))
     context_client_grpc.RemoveContext(ContextId(**CONTEXT_ID))
 
@@ -574,7 +519,7 @@ def test_grpc_device(
 
     assert isinstance(events[0], DeviceEvent)
     assert events[0].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE
-    assert events[0].device_id.device_uuid.uuid == DEVICE1_UUID
+    assert events[0].device_id.device_uuid.uuid == DEVICE_R1_UUID
 
     assert isinstance(events[1], TopologyEvent)
     assert events[1].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE
@@ -617,11 +562,11 @@ def test_grpc_link(
     assert response.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID
     assert response.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID
 
-    response = context_client_grpc.SetDevice(Device(**DEVICE1))
-    assert response.device_uuid.uuid == DEVICE1_UUID
+    response = context_client_grpc.SetDevice(Device(**DEVICE_R1))
+    assert response.device_uuid.uuid == DEVICE_R1_UUID
 
-    response = context_client_grpc.SetDevice(Device(**DEVICE2))
-    assert response.device_uuid.uuid == DEVICE2_UUID
+    response = context_client_grpc.SetDevice(Device(**DEVICE_R2))
+    assert response.device_uuid.uuid == DEVICE_R2_UUID
 
     events = events_collector.get_events(block=True, count=4)
 
@@ -636,17 +581,17 @@ def test_grpc_link(
 
     assert isinstance(events[2], DeviceEvent)
     assert events[2].event.event_type == EventTypeEnum.EVENTTYPE_CREATE
-    assert events[2].device_id.device_uuid.uuid == DEVICE1_UUID
+    assert events[2].device_id.device_uuid.uuid == DEVICE_R1_UUID
 
     assert isinstance(events[3], DeviceEvent)
     assert events[3].event.event_type == EventTypeEnum.EVENTTYPE_CREATE
-    assert events[3].device_id.device_uuid.uuid == DEVICE2_UUID
+    assert events[3].device_id.device_uuid.uuid == DEVICE_R2_UUID
 
     # ----- Get when the object does not exist -------------------------------------------------------------------------
     with pytest.raises(grpc.RpcError) as e:
-        context_client_grpc.GetLink(LinkId(**LINK_DEV1_DEV2_ID))
+        context_client_grpc.GetLink(LinkId(**LINK_R1_R2_ID))
     assert e.value.code() == grpc.StatusCode.NOT_FOUND
-    assert e.value.details() == 'Link({:s}) not found'.format(LINK_DEV1_DEV2_UUID)
+    assert e.value.details() == 'Link({:s}) not found'.format(LINK_R1_R2_UUID)
 
     # ----- List when the object does not exist ------------------------------------------------------------------------
     response = context_client_grpc.ListLinkIds(Empty())
@@ -661,27 +606,27 @@ def test_grpc_link(
     for db_entry in db_entries:
         LOGGER.info('  [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover
     LOGGER.info('-----------------------------------------------------------')
-    assert len(db_entries) == 69
+    assert len(db_entries) == 67
 
     # ----- Create the object ------------------------------------------------------------------------------------------
-    response = context_client_grpc.SetLink(Link(**LINK_DEV1_DEV2))
-    assert response.link_uuid.uuid == LINK_DEV1_DEV2_UUID
+    response = context_client_grpc.SetLink(Link(**LINK_R1_R2))
+    assert response.link_uuid.uuid == LINK_R1_R2_UUID
 
     # ----- Check create event -----------------------------------------------------------------------------------------
     event = events_collector.get_event(block=True)
     assert isinstance(event, LinkEvent)
     assert event.event.event_type == EventTypeEnum.EVENTTYPE_CREATE
-    assert event.link_id.link_uuid.uuid == LINK_DEV1_DEV2_UUID
+    assert event.link_id.link_uuid.uuid == LINK_R1_R2_UUID
 
     # ----- Update the object ------------------------------------------------------------------------------------------
-    response = context_client_grpc.SetLink(Link(**LINK_DEV1_DEV2))
-    assert response.link_uuid.uuid == LINK_DEV1_DEV2_UUID
+    response = context_client_grpc.SetLink(Link(**LINK_R1_R2))
+    assert response.link_uuid.uuid == LINK_R1_R2_UUID
 
     # ----- Check update event -----------------------------------------------------------------------------------------
     event = events_collector.get_event(block=True)
     assert isinstance(event, LinkEvent)
     assert event.event.event_type == EventTypeEnum.EVENTTYPE_UPDATE
-    assert event.link_id.link_uuid.uuid == LINK_DEV1_DEV2_UUID
+    assert event.link_id.link_uuid.uuid == LINK_R1_R2_UUID
 
     # ----- Dump state of database after create/update the object ------------------------------------------------------
     db_entries = context_database.dump()
@@ -689,26 +634,26 @@ def test_grpc_link(
     for db_entry in db_entries:
         LOGGER.info('  [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover
     LOGGER.info('-----------------------------------------------------------')
-    assert len(db_entries) == 77
+    assert len(db_entries) == 75
 
     # ----- Get when the object exists ---------------------------------------------------------------------------------
-    response = context_client_grpc.GetLink(LinkId(**LINK_DEV1_DEV2_ID))
-    assert response.link_id.link_uuid.uuid == LINK_DEV1_DEV2_UUID
+    response = context_client_grpc.GetLink(LinkId(**LINK_R1_R2_ID))
+    assert response.link_id.link_uuid.uuid == LINK_R1_R2_UUID
     assert len(response.link_endpoint_ids) == 2
 
     # ----- List when the object exists --------------------------------------------------------------------------------
     response = context_client_grpc.ListLinkIds(Empty())
     assert len(response.link_ids) == 1
-    assert response.link_ids[0].link_uuid.uuid == LINK_DEV1_DEV2_UUID
+    assert response.link_ids[0].link_uuid.uuid == LINK_R1_R2_UUID
 
     response = context_client_grpc.ListLinks(Empty())
     assert len(response.links) == 1
-    assert response.links[0].link_id.link_uuid.uuid == LINK_DEV1_DEV2_UUID
+    assert response.links[0].link_id.link_uuid.uuid == LINK_R1_R2_UUID
     assert len(response.links[0].link_endpoint_ids) == 2
 
     # ----- Create object relation -------------------------------------------------------------------------------------
     TOPOLOGY_WITH_LINK = copy.deepcopy(TOPOLOGY)
-    TOPOLOGY_WITH_LINK['link_ids'].append(LINK_DEV1_DEV2_ID)
+    TOPOLOGY_WITH_LINK['link_ids'].append(LINK_R1_R2_ID)
     response = context_client_grpc.SetTopology(Topology(**TOPOLOGY_WITH_LINK))
     assert response.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID
     assert response.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID
@@ -725,22 +670,22 @@ def test_grpc_link(
     assert response.topology_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID
     assert response.topology_id.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID
     assert len(response.device_ids) == 2
-    assert response.device_ids[0].device_uuid.uuid == DEVICE1_UUID
-    assert response.device_ids[1].device_uuid.uuid == DEVICE2_UUID
+    assert response.device_ids[0].device_uuid.uuid == DEVICE_R1_UUID
+    assert response.device_ids[1].device_uuid.uuid == DEVICE_R2_UUID
     assert len(response.link_ids) == 1
-    assert response.link_ids[0].link_uuid.uuid == LINK_DEV1_DEV2_UUID
+    assert response.link_ids[0].link_uuid.uuid == LINK_R1_R2_UUID
 
     db_entries = context_database.dump()
     LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries)))
     for db_entry in db_entries:
         LOGGER.info('  [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover
     LOGGER.info('-----------------------------------------------------------')
-    assert len(db_entries) == 77
+    assert len(db_entries) == 75
 
     # ----- Remove the object ------------------------------------------------------------------------------------------
-    context_client_grpc.RemoveLink(LinkId(**LINK_DEV1_DEV2_ID))
-    context_client_grpc.RemoveDevice(DeviceId(**DEVICE1_ID))
-    context_client_grpc.RemoveDevice(DeviceId(**DEVICE2_ID))
+    context_client_grpc.RemoveLink(LinkId(**LINK_R1_R2_ID))
+    context_client_grpc.RemoveDevice(DeviceId(**DEVICE_R1_ID))
+    context_client_grpc.RemoveDevice(DeviceId(**DEVICE_R2_ID))
     context_client_grpc.RemoveTopology(TopologyId(**TOPOLOGY_ID))
     context_client_grpc.RemoveContext(ContextId(**CONTEXT_ID))
 
@@ -749,15 +694,15 @@ def test_grpc_link(
 
     assert isinstance(events[0], LinkEvent)
     assert events[0].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE
-    assert events[0].link_id.link_uuid.uuid == LINK_DEV1_DEV2_UUID
+    assert events[0].link_id.link_uuid.uuid == LINK_R1_R2_UUID
 
     assert isinstance(events[1], DeviceEvent)
     assert events[1].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE
-    assert events[1].device_id.device_uuid.uuid == DEVICE1_UUID
+    assert events[1].device_id.device_uuid.uuid == DEVICE_R1_UUID
 
     assert isinstance(events[2], DeviceEvent)
     assert events[2].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE
-    assert events[2].device_id.device_uuid.uuid == DEVICE2_UUID
+    assert events[2].device_id.device_uuid.uuid == DEVICE_R2_UUID
 
     assert isinstance(events[3], TopologyEvent)
     assert events[3].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE
@@ -800,11 +745,11 @@ def test_grpc_service(
     assert response.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID
     assert response.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID
 
-    response = context_client_grpc.SetDevice(Device(**DEVICE1))
-    assert response.device_uuid.uuid == DEVICE1_UUID
+    response = context_client_grpc.SetDevice(Device(**DEVICE_R1))
+    assert response.device_uuid.uuid == DEVICE_R1_UUID
 
-    response = context_client_grpc.SetDevice(Device(**DEVICE2))
-    assert response.device_uuid.uuid == DEVICE2_UUID
+    response = context_client_grpc.SetDevice(Device(**DEVICE_R2))
+    assert response.device_uuid.uuid == DEVICE_R2_UUID
 
     events = events_collector.get_events(block=True, count=4)
 
@@ -819,17 +764,17 @@ def test_grpc_service(
 
     assert isinstance(events[2], DeviceEvent)
     assert events[2].event.event_type == EventTypeEnum.EVENTTYPE_CREATE
-    assert events[2].device_id.device_uuid.uuid == DEVICE1_UUID
+    assert events[2].device_id.device_uuid.uuid == DEVICE_R1_UUID
 
     assert isinstance(events[3], DeviceEvent)
     assert events[3].event.event_type == EventTypeEnum.EVENTTYPE_CREATE
-    assert events[3].device_id.device_uuid.uuid == DEVICE2_UUID
+    assert events[3].device_id.device_uuid.uuid == DEVICE_R2_UUID
 
     # ----- Get when the object does not exist -------------------------------------------------------------------------
     with pytest.raises(grpc.RpcError) as e:
-        context_client_grpc.GetService(ServiceId(**SERVICE_DEV1_DEV2_ID))
+        context_client_grpc.GetService(ServiceId(**SERVICE_R1_R2_ID))
     assert e.value.code() == grpc.StatusCode.NOT_FOUND
-    assert e.value.details() == 'Service({:s}/{:s}) not found'.format(DEFAULT_CONTEXT_UUID, SERVICE_DEV1_DEV2_UUID)
+    assert e.value.details() == 'Service({:s}/{:s}) not found'.format(DEFAULT_CONTEXT_UUID, SERVICE_R1_R2_UUID)
 
     # ----- List when the object does not exist ------------------------------------------------------------------------
     response = context_client_grpc.ListServiceIds(ContextId(**CONTEXT_ID))
@@ -844,25 +789,25 @@ def test_grpc_service(
     for db_entry in db_entries:
         LOGGER.info('  [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover
     LOGGER.info('-----------------------------------------------------------')
-    assert len(db_entries) == 69
+    assert len(db_entries) == 67
 
     # ----- Create the object ------------------------------------------------------------------------------------------
     with pytest.raises(grpc.RpcError) as e:
-        WRONG_SERVICE = copy.deepcopy(SERVICE_DEV1_DEV2)
+        WRONG_SERVICE = copy.deepcopy(SERVICE_R1_R2)
         WRONG_SERVICE['service_endpoint_ids'][0]\
             ['topology_id']['context_id']['context_uuid']['uuid'] = 'wrong-context-uuid'
         context_client_grpc.SetService(Service(**WRONG_SERVICE))
     assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
     msg = 'request.service_endpoint_ids[0].topology_id.context_id.context_uuid.uuid(wrong-context-uuid) is invalid; '\
-          'should be == request.service_id.context_id.context_uuid.uuid(admin)'
+          'should be == request.service_id.context_id.context_uuid.uuid({:s})'.format(DEFAULT_CONTEXT_UUID)
     assert e.value.details() == msg
 
-    response = context_client_grpc.SetService(Service(**SERVICE_DEV1_DEV2))
+    response = context_client_grpc.SetService(Service(**SERVICE_R1_R2))
     assert response.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID
-    assert response.service_uuid.uuid == SERVICE_DEV1_DEV2_UUID
+    assert response.service_uuid.uuid == SERVICE_R1_R2_UUID
 
     CONTEXT_WITH_SERVICE = copy.deepcopy(CONTEXT)
-    CONTEXT_WITH_SERVICE['service_ids'].append(SERVICE_DEV1_DEV2_ID)
+    CONTEXT_WITH_SERVICE['service_ids'].append(SERVICE_R1_R2_ID)
     response = context_client_grpc.SetContext(Context(**CONTEXT_WITH_SERVICE))
     assert response.context_uuid.uuid == DEFAULT_CONTEXT_UUID
 
@@ -872,23 +817,23 @@ def test_grpc_service(
     assert isinstance(events[0], ServiceEvent)
     assert events[0].event.event_type == EventTypeEnum.EVENTTYPE_CREATE
     assert events[0].service_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID
-    assert events[0].service_id.service_uuid.uuid == SERVICE_DEV1_DEV2_UUID
+    assert events[0].service_id.service_uuid.uuid == SERVICE_R1_R2_UUID
 
     assert isinstance(events[1], ContextEvent)
     assert events[1].event.event_type == EventTypeEnum.EVENTTYPE_UPDATE
     assert events[1].context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID
 
     # ----- Update the object ------------------------------------------------------------------------------------------
-    response = context_client_grpc.SetService(Service(**SERVICE_DEV1_DEV2))
+    response = context_client_grpc.SetService(Service(**SERVICE_R1_R2))
     assert response.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID
-    assert response.service_uuid.uuid == SERVICE_DEV1_DEV2_UUID
+    assert response.service_uuid.uuid == SERVICE_R1_R2_UUID
 
     # ----- Check update event -----------------------------------------------------------------------------------------
     event = events_collector.get_event(block=True)
     assert isinstance(event, ServiceEvent)
     assert event.event.event_type == EventTypeEnum.EVENTTYPE_UPDATE
     assert event.service_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID
-    assert event.service_id.service_uuid.uuid == SERVICE_DEV1_DEV2_UUID
+    assert event.service_id.service_uuid.uuid == SERVICE_R1_R2_UUID
 
     # ----- Dump state of database after create/update the object ------------------------------------------------------
     db_entries = context_database.dump()
@@ -896,38 +841,38 @@ def test_grpc_service(
     for db_entry in db_entries:
         LOGGER.info('  [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover
     LOGGER.info('-----------------------------------------------------------')
-    assert len(db_entries) == 86
+    assert len(db_entries) == 84
 
     # ----- Get when the object exists ---------------------------------------------------------------------------------
-    response = context_client_grpc.GetService(ServiceId(**SERVICE_DEV1_DEV2_ID))
+    response = context_client_grpc.GetService(ServiceId(**SERVICE_R1_R2_ID))
     assert response.service_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID
-    assert response.service_id.service_uuid.uuid == SERVICE_DEV1_DEV2_UUID
+    assert response.service_id.service_uuid.uuid == SERVICE_R1_R2_UUID
     assert response.service_type == ServiceTypeEnum.SERVICETYPE_L3NM
     assert len(response.service_endpoint_ids) == 2
     assert len(response.service_constraints) == 2
-    assert response.service_status.service_status == ServiceStatusEnum.SERVICESTATUS_ACTIVE
+    assert response.service_status.service_status == ServiceStatusEnum.SERVICESTATUS_PLANNED
     assert len(response.service_config.config_rules) == 3
 
     # ----- List when the object exists --------------------------------------------------------------------------------
     response = context_client_grpc.ListServiceIds(ContextId(**CONTEXT_ID))
     assert len(response.service_ids) == 1
     assert response.service_ids[0].context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID
-    assert response.service_ids[0].service_uuid.uuid == SERVICE_DEV1_DEV2_UUID
+    assert response.service_ids[0].service_uuid.uuid == SERVICE_R1_R2_UUID
 
     response = context_client_grpc.ListServices(ContextId(**CONTEXT_ID))
     assert len(response.services) == 1
     assert response.services[0].service_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID
-    assert response.services[0].service_id.service_uuid.uuid == SERVICE_DEV1_DEV2_UUID
+    assert response.services[0].service_id.service_uuid.uuid == SERVICE_R1_R2_UUID
     assert response.services[0].service_type == ServiceTypeEnum.SERVICETYPE_L3NM
     assert len(response.services[0].service_endpoint_ids) == 2
     assert len(response.services[0].service_constraints) == 2
-    assert response.services[0].service_status.service_status == ServiceStatusEnum.SERVICESTATUS_ACTIVE
+    assert response.services[0].service_status.service_status == ServiceStatusEnum.SERVICESTATUS_PLANNED
     assert len(response.services[0].service_config.config_rules) == 3
 
     # ----- Remove the object ------------------------------------------------------------------------------------------
-    context_client_grpc.RemoveService(ServiceId(**SERVICE_DEV1_DEV2_ID))
-    context_client_grpc.RemoveDevice(DeviceId(**DEVICE1_ID))
-    context_client_grpc.RemoveDevice(DeviceId(**DEVICE2_ID))
+    context_client_grpc.RemoveService(ServiceId(**SERVICE_R1_R2_ID))
+    context_client_grpc.RemoveDevice(DeviceId(**DEVICE_R1_ID))
+    context_client_grpc.RemoveDevice(DeviceId(**DEVICE_R2_ID))
     context_client_grpc.RemoveTopology(TopologyId(**TOPOLOGY_ID))
     context_client_grpc.RemoveContext(ContextId(**CONTEXT_ID))
 
@@ -936,15 +881,15 @@ def test_grpc_service(
 
     assert isinstance(events[0], ServiceEvent)
     assert events[0].service_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID
-    assert events[0].service_id.service_uuid.uuid == SERVICE_DEV1_DEV2_UUID
+    assert events[0].service_id.service_uuid.uuid == SERVICE_R1_R2_UUID
 
     assert isinstance(events[1], DeviceEvent)
     assert events[1].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE
-    assert events[1].device_id.device_uuid.uuid == DEVICE1_UUID
+    assert events[1].device_id.device_uuid.uuid == DEVICE_R1_UUID
 
     assert isinstance(events[2], DeviceEvent)
     assert events[2].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE
-    assert events[2].device_id.device_uuid.uuid == DEVICE2_UUID
+    assert events[2].device_id.device_uuid.uuid == DEVICE_R2_UUID
 
     assert isinstance(events[3], TopologyEvent)
     assert events[3].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE
@@ -987,39 +932,39 @@ def test_grpc_connection(
     assert response.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID
     assert response.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID
 
-    response = context_client_grpc.SetDevice(Device(**DEVICE1))
-    assert response.device_uuid.uuid == DEVICE1_UUID
+    response = context_client_grpc.SetDevice(Device(**DEVICE_R1))
+    assert response.device_uuid.uuid == DEVICE_R1_UUID
 
-    response = context_client_grpc.SetDevice(Device(**DEVICE2))
-    assert response.device_uuid.uuid == DEVICE2_UUID
+    response = context_client_grpc.SetDevice(Device(**DEVICE_R2))
+    assert response.device_uuid.uuid == DEVICE_R2_UUID
 
-    response = context_client_grpc.SetDevice(Device(**DEVICE3))
-    assert response.device_uuid.uuid == DEVICE3_UUID
+    response = context_client_grpc.SetDevice(Device(**DEVICE_R3))
+    assert response.device_uuid.uuid == DEVICE_R3_UUID
 
-    response = context_client_grpc.SetService(Service(**SERVICE_DEV1_DEV2))
+    response = context_client_grpc.SetService(Service(**SERVICE_R1_R2))
     assert response.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID
-    assert response.service_uuid.uuid == SERVICE_DEV1_DEV2_UUID
+    assert response.service_uuid.uuid == SERVICE_R1_R2_UUID
 
     CONTEXT_WITH_SERVICE = copy.deepcopy(CONTEXT)
-    CONTEXT_WITH_SERVICE['service_ids'].append(SERVICE_DEV1_DEV2_ID)
+    CONTEXT_WITH_SERVICE['service_ids'].append(SERVICE_R1_R2_ID)
     response = context_client_grpc.SetContext(Context(**CONTEXT_WITH_SERVICE))
     assert response.context_uuid.uuid == DEFAULT_CONTEXT_UUID
 
-    response = context_client_grpc.SetService(Service(**SERVICE_DEV2_DEV3))
+    response = context_client_grpc.SetService(Service(**SERVICE_R2_R3))
     assert response.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID
-    assert response.service_uuid.uuid == SERVICE_DEV2_DEV3_UUID
+    assert response.service_uuid.uuid == SERVICE_R2_R3_UUID
 
     CONTEXT_WITH_SERVICE = copy.deepcopy(CONTEXT)
-    CONTEXT_WITH_SERVICE['service_ids'].append(SERVICE_DEV2_DEV3_ID)
+    CONTEXT_WITH_SERVICE['service_ids'].append(SERVICE_R2_R3_ID)
     response = context_client_grpc.SetContext(Context(**CONTEXT_WITH_SERVICE))
     assert response.context_uuid.uuid == DEFAULT_CONTEXT_UUID
 
-    response = context_client_grpc.SetService(Service(**SERVICE_DEV1_DEV3))
+    response = context_client_grpc.SetService(Service(**SERVICE_R1_R3))
     assert response.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID
-    assert response.service_uuid.uuid == SERVICE_DEV1_DEV3_UUID
+    assert response.service_uuid.uuid == SERVICE_R1_R3_UUID
 
     CONTEXT_WITH_SERVICE = copy.deepcopy(CONTEXT)
-    CONTEXT_WITH_SERVICE['service_ids'].append(SERVICE_DEV1_DEV3_ID)
+    CONTEXT_WITH_SERVICE['service_ids'].append(SERVICE_R1_R3_ID)
     response = context_client_grpc.SetContext(Context(**CONTEXT_WITH_SERVICE))
     assert response.context_uuid.uuid == DEFAULT_CONTEXT_UUID
 
@@ -1036,20 +981,20 @@ def test_grpc_connection(
 
     assert isinstance(events[2], DeviceEvent)
     assert events[2].event.event_type == EventTypeEnum.EVENTTYPE_CREATE
-    assert events[2].device_id.device_uuid.uuid == DEVICE1_UUID
+    assert events[2].device_id.device_uuid.uuid == DEVICE_R1_UUID
 
     assert isinstance(events[3], DeviceEvent)
     assert events[3].event.event_type == EventTypeEnum.EVENTTYPE_CREATE
-    assert events[3].device_id.device_uuid.uuid == DEVICE2_UUID
+    assert events[3].device_id.device_uuid.uuid == DEVICE_R2_UUID
 
     assert isinstance(events[4], DeviceEvent)
     assert events[4].event.event_type == EventTypeEnum.EVENTTYPE_CREATE
-    assert events[4].device_id.device_uuid.uuid == DEVICE3_UUID
+    assert events[4].device_id.device_uuid.uuid == DEVICE_R3_UUID
 
     assert isinstance(events[5], ServiceEvent)
     assert events[5].event.event_type == EventTypeEnum.EVENTTYPE_CREATE
     assert events[5].service_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID
-    assert events[5].service_id.service_uuid.uuid == SERVICE_DEV1_DEV2_UUID
+    assert events[5].service_id.service_uuid.uuid == SERVICE_R1_R2_UUID
 
     assert isinstance(events[6], ContextEvent)
     assert events[6].event.event_type == EventTypeEnum.EVENTTYPE_UPDATE
@@ -1058,7 +1003,7 @@ def test_grpc_connection(
     assert isinstance(events[7], ServiceEvent)
     assert events[7].event.event_type == EventTypeEnum.EVENTTYPE_CREATE
     assert events[7].service_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID
-    assert events[7].service_id.service_uuid.uuid == SERVICE_DEV2_DEV3_UUID
+    assert events[7].service_id.service_uuid.uuid == SERVICE_R2_R3_UUID
 
     assert isinstance(events[8], ContextEvent)
     assert events[8].event.event_type == EventTypeEnum.EVENTTYPE_UPDATE
@@ -1067,7 +1012,7 @@ def test_grpc_connection(
     assert isinstance(events[9], ServiceEvent)
     assert events[9].event.event_type == EventTypeEnum.EVENTTYPE_CREATE
     assert events[9].service_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID
-    assert events[9].service_id.service_uuid.uuid == SERVICE_DEV1_DEV3_UUID
+    assert events[9].service_id.service_uuid.uuid == SERVICE_R1_R3_UUID
 
     assert isinstance(events[10], ContextEvent)
     assert events[10].event.event_type == EventTypeEnum.EVENTTYPE_UPDATE
@@ -1075,15 +1020,15 @@ def test_grpc_connection(
 
     # ----- Get when the object does not exist -------------------------------------------------------------------------
     with pytest.raises(grpc.RpcError) as e:
-        context_client_grpc.GetConnection(ConnectionId(**CONNECTION_DEV1_DEV3_ID))
+        context_client_grpc.GetConnection(ConnectionId(**CONNECTION_R1_R3_ID))
     assert e.value.code() == grpc.StatusCode.NOT_FOUND
-    assert e.value.details() == 'Connection({:s}) not found'.format(CONNECTION_DEV1_DEV3_UUID)
+    assert e.value.details() == 'Connection({:s}) not found'.format(CONNECTION_R1_R3_UUID)
 
     # ----- List when the object does not exist ------------------------------------------------------------------------
-    response = context_client_grpc.ListConnectionIds(ServiceId(**SERVICE_DEV1_DEV3_ID))
+    response = context_client_grpc.ListConnectionIds(ServiceId(**SERVICE_R1_R3_ID))
     assert len(response.connection_ids) == 0
 
-    response = context_client_grpc.ListConnections(ServiceId(**SERVICE_DEV1_DEV3_ID))
+    response = context_client_grpc.ListConnections(ServiceId(**SERVICE_R1_R3_ID))
     assert len(response.connections) == 0
 
     # ----- Dump state of database before create the object ------------------------------------------------------------
@@ -1092,38 +1037,39 @@ def test_grpc_connection(
     for db_entry in db_entries:
         LOGGER.info('  [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover
     LOGGER.info('-----------------------------------------------------------')
-    assert len(db_entries) == 140
+    assert len(db_entries) == 137
 
     # ----- Create the object ------------------------------------------------------------------------------------------
     with pytest.raises(grpc.RpcError) as e:
-        WRONG_CONNECTION = copy.deepcopy(CONNECTION_DEV1_DEV3)
+        WRONG_CONNECTION = copy.deepcopy(CONNECTION_R1_R3)
         WRONG_CONNECTION['path_hops_endpoint_ids'][0]\
             ['topology_id']['context_id']['context_uuid']['uuid'] = 'wrong-context-uuid'
         context_client_grpc.SetConnection(Connection(**WRONG_CONNECTION))
     assert e.value.code() == grpc.StatusCode.NOT_FOUND
     # TODO: should we check that all endpoints belong to same topology?
     # TODO: should we check that endpoints form links over the topology?
-    msg = 'EndPoint(DEV1/EP100:wrong-context-uuid/admin) not found'
+    msg = 'EndPoint({:s}/{:s}:wrong-context-uuid/{:s}) not found'.format(
+        DEVICE_R1_UUID, WRONG_CONNECTION['path_hops_endpoint_ids'][0]['endpoint_uuid']['uuid'], DEFAULT_TOPOLOGY_UUID)
     assert e.value.details() == msg
 
-    response = context_client_grpc.SetConnection(Connection(**CONNECTION_DEV1_DEV3))
-    assert response.connection_uuid.uuid == CONNECTION_DEV1_DEV3_UUID
+    response = context_client_grpc.SetConnection(Connection(**CONNECTION_R1_R3))
+    assert response.connection_uuid.uuid == CONNECTION_R1_R3_UUID
 
     # ----- Check create event -----------------------------------------------------------------------------------------
     event = events_collector.get_event(block=True)
     assert isinstance(event, ConnectionEvent)
     assert event.event.event_type == EventTypeEnum.EVENTTYPE_CREATE
-    assert event.connection_id.connection_uuid.uuid == CONNECTION_DEV1_DEV3_UUID
+    assert event.connection_id.connection_uuid.uuid == CONNECTION_R1_R3_UUID
 
     # ----- Update the object ------------------------------------------------------------------------------------------
-    response = context_client_grpc.SetConnection(Connection(**CONNECTION_DEV1_DEV3))
-    assert response.connection_uuid.uuid == CONNECTION_DEV1_DEV3_UUID
+    response = context_client_grpc.SetConnection(Connection(**CONNECTION_R1_R3))
+    assert response.connection_uuid.uuid == CONNECTION_R1_R3_UUID
 
     # ----- Check update event -----------------------------------------------------------------------------------------
     event = events_collector.get_event(block=True)
     assert isinstance(event, ConnectionEvent)
     assert event.event.event_type == EventTypeEnum.EVENTTYPE_UPDATE
-    assert event.connection_id.connection_uuid.uuid == CONNECTION_DEV1_DEV3_UUID
+    assert event.connection_id.connection_uuid.uuid == CONNECTION_R1_R3_UUID
 
     # ----- Dump state of database after create/update the object ------------------------------------------------------
     db_entries = context_database.dump()
@@ -1131,35 +1077,35 @@ def test_grpc_connection(
     for db_entry in db_entries:
         LOGGER.info('  [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover
     LOGGER.info('-----------------------------------------------------------')
-    assert len(db_entries) == 156
+    assert len(db_entries) == 153
 
     # ----- Get when the object exists ---------------------------------------------------------------------------------
-    response = context_client_grpc.GetConnection(ConnectionId(**CONNECTION_DEV1_DEV3_ID))
-    assert response.connection_id.connection_uuid.uuid == CONNECTION_DEV1_DEV3_UUID
+    response = context_client_grpc.GetConnection(ConnectionId(**CONNECTION_R1_R3_ID))
+    assert response.connection_id.connection_uuid.uuid == CONNECTION_R1_R3_UUID
     assert response.service_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID
-    assert response.service_id.service_uuid.uuid == SERVICE_DEV1_DEV3_UUID
+    assert response.service_id.service_uuid.uuid == SERVICE_R1_R3_UUID
     assert len(response.path_hops_endpoint_ids) == 6
     assert len(response.sub_service_ids) == 2
 
     # ----- List when the object exists --------------------------------------------------------------------------------
-    response = context_client_grpc.ListConnectionIds(ServiceId(**SERVICE_DEV1_DEV3_ID))
+    response = context_client_grpc.ListConnectionIds(ServiceId(**SERVICE_R1_R3_ID))
     assert len(response.connection_ids) == 1
-    assert response.connection_ids[0].connection_uuid.uuid == CONNECTION_DEV1_DEV3_UUID
+    assert response.connection_ids[0].connection_uuid.uuid == CONNECTION_R1_R3_UUID
 
-    response = context_client_grpc.ListConnections(ServiceId(**SERVICE_DEV1_DEV3_ID))
+    response = context_client_grpc.ListConnections(ServiceId(**SERVICE_R1_R3_ID))
     assert len(response.connections) == 1
-    assert response.connections[0].connection_id.connection_uuid.uuid == CONNECTION_DEV1_DEV3_UUID
+    assert response.connections[0].connection_id.connection_uuid.uuid == CONNECTION_R1_R3_UUID
     assert len(response.connections[0].path_hops_endpoint_ids) == 6
     assert len(response.connections[0].sub_service_ids) == 2
 
     # ----- Remove the object ------------------------------------------------------------------------------------------
-    context_client_grpc.RemoveConnection(ConnectionId(**CONNECTION_DEV1_DEV3_ID))
-    context_client_grpc.RemoveService(ServiceId(**SERVICE_DEV1_DEV3_ID))
-    context_client_grpc.RemoveService(ServiceId(**SERVICE_DEV2_DEV3_ID))
-    context_client_grpc.RemoveService(ServiceId(**SERVICE_DEV1_DEV2_ID))
-    context_client_grpc.RemoveDevice(DeviceId(**DEVICE1_ID))
-    context_client_grpc.RemoveDevice(DeviceId(**DEVICE2_ID))
-    context_client_grpc.RemoveDevice(DeviceId(**DEVICE3_ID))
+    context_client_grpc.RemoveConnection(ConnectionId(**CONNECTION_R1_R3_ID))
+    context_client_grpc.RemoveService(ServiceId(**SERVICE_R1_R3_ID))
+    context_client_grpc.RemoveService(ServiceId(**SERVICE_R2_R3_ID))
+    context_client_grpc.RemoveService(ServiceId(**SERVICE_R1_R2_ID))
+    context_client_grpc.RemoveDevice(DeviceId(**DEVICE_R1_ID))
+    context_client_grpc.RemoveDevice(DeviceId(**DEVICE_R2_ID))
+    context_client_grpc.RemoveDevice(DeviceId(**DEVICE_R3_ID))
     context_client_grpc.RemoveTopology(TopologyId(**TOPOLOGY_ID))
     context_client_grpc.RemoveContext(ContextId(**CONTEXT_ID))
 
@@ -1168,34 +1114,34 @@ def test_grpc_connection(
 
     assert isinstance(events[0], ConnectionEvent)
     assert events[0].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE
-    assert events[0].connection_id.connection_uuid.uuid == CONNECTION_DEV1_DEV3_UUID
+    assert events[0].connection_id.connection_uuid.uuid == CONNECTION_R1_R3_UUID
 
     assert isinstance(events[1], ServiceEvent)
     assert events[1].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE
     assert events[1].service_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID
-    assert events[1].service_id.service_uuid.uuid == SERVICE_DEV1_DEV3_UUID
+    assert events[1].service_id.service_uuid.uuid == SERVICE_R1_R3_UUID
 
     assert isinstance(events[2], ServiceEvent)
     assert events[2].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE
     assert events[2].service_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID
-    assert events[2].service_id.service_uuid.uuid == SERVICE_DEV2_DEV3_UUID
+    assert events[2].service_id.service_uuid.uuid == SERVICE_R2_R3_UUID
 
     assert isinstance(events[3], ServiceEvent)
     assert events[3].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE
     assert events[3].service_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID
-    assert events[3].service_id.service_uuid.uuid == SERVICE_DEV1_DEV2_UUID
+    assert events[3].service_id.service_uuid.uuid == SERVICE_R1_R2_UUID
 
     assert isinstance(events[4], DeviceEvent)
     assert events[4].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE
-    assert events[4].device_id.device_uuid.uuid == DEVICE1_UUID
+    assert events[4].device_id.device_uuid.uuid == DEVICE_R1_UUID
 
     assert isinstance(events[5], DeviceEvent)
     assert events[5].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE
-    assert events[5].device_id.device_uuid.uuid == DEVICE2_UUID
+    assert events[5].device_id.device_uuid.uuid == DEVICE_R2_UUID
 
     assert isinstance(events[6], DeviceEvent)
     assert events[6].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE
-    assert events[6].device_id.device_uuid.uuid == DEVICE3_UUID
+    assert events[6].device_id.device_uuid.uuid == DEVICE_R3_UUID
 
     assert isinstance(events[7], TopologyEvent)
     assert events[7].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE
@@ -1269,7 +1215,7 @@ def test_rest_get_services(context_service_rest : RestServer): # pylint: disable
 
 def test_rest_get_service(context_service_rest : RestServer): # pylint: disable=redefined-outer-name
     context_uuid = urllib.parse.quote(DEFAULT_CONTEXT_UUID)
-    service_uuid = urllib.parse.quote(SERVICE_DEV1_DEV2_UUID, safe='')
+    service_uuid = urllib.parse.quote(SERVICE_R1_R2_UUID, safe='')
     reply = do_rest_request('/context/{:s}/service/{:s}'.format(context_uuid, service_uuid))
     validate_service(reply)
 
@@ -1282,7 +1228,7 @@ def test_rest_get_devices(context_service_rest : RestServer): # pylint: disable=
     validate_devices(reply)
 
 def test_rest_get_device(context_service_rest : RestServer): # pylint: disable=redefined-outer-name
-    device_uuid = urllib.parse.quote(DEVICE1_UUID, safe='')
+    device_uuid = urllib.parse.quote(DEVICE_R1_UUID, safe='')
     reply = do_rest_request('/device/{:s}'.format(device_uuid))
     validate_device(reply)
 
@@ -1295,24 +1241,24 @@ def test_rest_get_links(context_service_rest : RestServer): # pylint: disable=re
     validate_links(reply)
 
 def test_rest_get_link(context_service_rest : RestServer): # pylint: disable=redefined-outer-name
-    link_uuid = urllib.parse.quote(LINK_DEV1_DEV2_UUID, safe='')
+    link_uuid = urllib.parse.quote(LINK_R1_R2_UUID, safe='')
     reply = do_rest_request('/link/{:s}'.format(link_uuid))
     validate_link(reply)
 
 def test_rest_get_connection_ids(context_service_rest : RestServer): # pylint: disable=redefined-outer-name
     context_uuid = urllib.parse.quote(DEFAULT_CONTEXT_UUID)
-    service_uuid = urllib.parse.quote(SERVICE_DEV1_DEV3_UUID, safe='')
+    service_uuid = urllib.parse.quote(SERVICE_R1_R3_UUID, safe='')
     reply = do_rest_request('/context/{:s}/service/{:s}/connection_ids'.format(context_uuid, service_uuid))
     validate_connection_ids(reply)
 
 def test_rest_get_connections(context_service_rest : RestServer): # pylint: disable=redefined-outer-name
     context_uuid = urllib.parse.quote(DEFAULT_CONTEXT_UUID)
-    service_uuid = urllib.parse.quote(SERVICE_DEV1_DEV3_UUID, safe='')
+    service_uuid = urllib.parse.quote(SERVICE_R1_R3_UUID, safe='')
     reply = do_rest_request('/context/{:s}/service/{:s}/connections'.format(context_uuid, service_uuid))
     validate_connections(reply)
 
 def test_rest_get_connection(context_service_rest : RestServer): # pylint: disable=redefined-outer-name
-    connection_uuid = urllib.parse.quote(CONNECTION_DEV1_DEV3_UUID, safe='')
+    connection_uuid = urllib.parse.quote(CONNECTION_R1_R3_UUID, safe='')
     reply = do_rest_request('/connection/{:s}'.format(connection_uuid))
     validate_connection(reply)
 
diff --git a/src/dbscanserving/.gitlab-ci.yml b/src/dbscanserving/.gitlab-ci.yml
index de704cf861f82e8e3a976461db6658d7ad2e888a..1e499fae232ff805fe3f70d1b1c586a9c9a8de5f 100644
--- a/src/dbscanserving/.gitlab-ci.yml
+++ b/src/dbscanserving/.gitlab-ci.yml
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 # build, tag and push the Docker image to the gitlab registry
 build dbscanserving:
   variables:
@@ -10,8 +24,8 @@ build dbscanserving:
     - 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"' 
@@ -20,7 +34,7 @@ build dbscanserving:
       - src/$IMAGE_NAME/Dockerfile
       - src/$IMAGE_NAME/tests/*.py
       - src/$IMAGE_NAME/tests/Dockerfile
-      - manifests/$IMAGE_NAME.yaml
+      - manifests/${IMAGE_NAME}service.yaml
       - .gitlab-ci.yml
 
 # apply unit test to the dbscanserving component
@@ -37,12 +51,16 @@ unit test dbscanserving:
     - 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 10006:10006 --network=teraflowbridge --rm $CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG
+    - docker run --name $IMAGE_NAME -d -p 10006:10006 -v "$PWD/src/$IMAGE_NAME/tests:/opt/results" --network=teraflowbridge --rm $CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG
     - sleep 5
     - docker ps -a
-    - docker exec -i $IMAGE_NAME bash -c "pytest --log-level=DEBUG --verbose $IMAGE_NAME/tests/test_unitary.py"
+    - docker logs $IMAGE_NAME
+    - docker exec -i $IMAGE_NAME bash -c "coverage run -m pytest --log-level=INFO --verbose $IMAGE_NAME/tests/test_unitary.py --junitxml=/opt/results/${IMAGE_NAME}_report.xml"
+    - docker exec -i $IMAGE_NAME bash -c "coverage xml -o /opt/results/${IMAGE_NAME}_coverage.xml"
+    - docker exec -i $IMAGE_NAME bash -c "coverage report --include='${IMAGE_NAME}/*' --show-missing"
+  coverage: '/TOTAL\s+\d+\s+\d+\s+(\d+%)/'
   after_script:
-    #- docker rm -f $IMAGE_NAME
+    - docker rm -f $IMAGE_NAME
     - docker network rm teraflowbridge
   rules:
     - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && ($CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "develop" || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == $CI_DEFAULT_BRANCH)'
@@ -52,8 +70,13 @@ unit test dbscanserving:
       - src/$IMAGE_NAME/Dockerfile
       - src/$IMAGE_NAME/tests/*.py
       - src/$IMAGE_NAME/tests/Dockerfile
-      - manifests/$IMAGE_NAMEservice.yaml
+      - manifests/${IMAGE_NAME}service.yaml
       - .gitlab-ci.yml
+  artifacts:
+    when: always
+    reports:
+      junit: src/$IMAGE_NAME/tests/${IMAGE_NAME}_report.xml
+      cobertura: src/$IMAGE_NAME/tests/${IMAGE_NAME}_coverage.xml
 
 
 # Deployment of the dbscanserving service in Kubernetes Cluster
@@ -69,7 +92,7 @@ deploy dbscanserving:
     - 'sed -i "s/$IMAGE_NAME:.*/$IMAGE_NAME:$IMAGE_TAG/" manifests/${IMAGE_NAME}service.yaml'
     - kubectl version
     - kubectl get all
-    - kubectl apply -f "manifests/$IMAGE_NAME.yaml"
+    - kubectl apply -f "manifests/${IMAGE_NAME}service.yaml"
     - kubectl get all
   # environment:
   #   name: test
diff --git a/src/dbscanserving/Config.py b/src/dbscanserving/Config.py
index ec89bbb0f8b37539e06f9cc101c0f461a3bc2a82..6604abe6cda5bd203621de9f09f48ca5f6c8256e 100644
--- a/src/dbscanserving/Config.py
+++ b/src/dbscanserving/Config.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging
 
 # General settings
diff --git a/src/dbscanserving/Dockerfile b/src/dbscanserving/Dockerfile
index aa62cbe93cb008085762abbe0ddaae5a31f0fb73..31cf21b0ea8da830b72c726ee3e63dd1bc61d094 100644
--- a/src/dbscanserving/Dockerfile
+++ b/src/dbscanserving/Dockerfile
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 FROM python:3-slim
 
 # Install dependencies
diff --git a/src/dbscanserving/__init__.py b/src/dbscanserving/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/dbscanserving/__init__.py
+++ b/src/dbscanserving/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/dbscanserving/client/DbscanServingClient.py b/src/dbscanserving/client/DbscanServingClient.py
index 4d6614b5dc44c2b916fb298217fef4a8f1b8e72c..286b3318e7568f3edcfff9032c30b1244d826533 100644
--- a/src/dbscanserving/client/DbscanServingClient.py
+++ b/src/dbscanserving/client/DbscanServingClient.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import grpc, logging
 from common.tools.client.RetryDecorator import retry, delay_exponential
 from dbscanserving.proto.dbscanserving_pb2 import DetectionRequest, DetectionResponse
@@ -6,6 +20,7 @@ from dbscanserving.proto.dbscanserving_pb2_grpc import DetectorStub
 LOGGER = logging.getLogger(__name__)
 MAX_RETRIES = 15
 DELAY_FUNCTION = delay_exponential(initial=0.01, increment=2.0, maximum=5.0)
+RETRY_DECORATOR = retry(max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
 
 class DbscanServingClient:
     def __init__(self, address, port):
@@ -25,7 +40,7 @@ class DbscanServingClient:
         self.channel = None
         self.stub = None
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def Detect(self, request : DetectionRequest) -> DetectionResponse:
         LOGGER.debug('Detect request: {:s}'.format(str(request)))
         response = self.stub.Detect(request)
diff --git a/src/dbscanserving/client/__init__.py b/src/dbscanserving/client/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/dbscanserving/client/__init__.py
+++ b/src/dbscanserving/client/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/dbscanserving/proto/__init__.py b/src/dbscanserving/proto/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/dbscanserving/proto/__init__.py
+++ b/src/dbscanserving/proto/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/dbscanserving/requirements.in b/src/dbscanserving/requirements.in
index b4272e5c4e709f5893c0e750d263fc24b7be4f7d..56f9e0673f4899ea9b4d6abcba5e13d005454b13 100644
--- a/src/dbscanserving/requirements.in
+++ b/src/dbscanserving/requirements.in
@@ -3,4 +3,5 @@ prometheus-client
 pytest
 pytest-benchmark
 grpcio
-scikit-learn
\ No newline at end of file
+scikit-learn
+coverage
\ No newline at end of file
diff --git a/src/dbscanserving/service/DbscanService.py b/src/dbscanserving/service/DbscanService.py
index 43336ab089b6848971cda4e79e4da7aa26c1ca65..97654fa30532d2544cfdbbc9260b049f293ed395 100644
--- a/src/dbscanserving/service/DbscanService.py
+++ b/src/dbscanserving/service/DbscanService.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import grpc
 import logging
 from concurrent import futures
diff --git a/src/dbscanserving/service/DbscanServiceServicerImpl.py b/src/dbscanserving/service/DbscanServiceServicerImpl.py
index 251b3eb1ad4c5e51eb80207ce5036351ea65572f..5560eec1e333c16c0ea980a5af14e856c3909431 100644
--- a/src/dbscanserving/service/DbscanServiceServicerImpl.py
+++ b/src/dbscanserving/service/DbscanServiceServicerImpl.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import os, grpc, logging
 from sklearn.cluster import DBSCAN
 from common.rpc_method_wrapper.Decorator import create_metrics, safe_and_metered_rpc_method
diff --git a/src/dbscanserving/service/__init__.py b/src/dbscanserving/service/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/dbscanserving/service/__init__.py
+++ b/src/dbscanserving/service/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/dbscanserving/service/__main__.py b/src/dbscanserving/service/__main__.py
index bc3f1c681fe88d723d36a28bff64c0e6b04fd089..cad074a409af9e3106d84c81d3f20c6e6d9b1274 100644
--- a/src/dbscanserving/service/__main__.py
+++ b/src/dbscanserving/service/__main__.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import os, logging, signal, sys, time, threading, multiprocessing
 from prometheus_client import start_http_server
 from common.Settings import get_setting
diff --git a/src/dbscanserving/tests/__init__.py b/src/dbscanserving/tests/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/dbscanserving/tests/__init__.py
+++ b/src/dbscanserving/tests/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/dbscanserving/tests/test_unitary.py b/src/dbscanserving/tests/test_unitary.py
index 061d2f07d2e1a6876318214a909a26140c98197d..8198c9e215d13cfee62bc7ee13a0daa8224f37a5 100644
--- a/src/dbscanserving/tests/test_unitary.py
+++ b/src/dbscanserving/tests/test_unitary.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import random, logging, pytest, numpy
 from dbscanserving.Config import GRPC_SERVICE_PORT, GRPC_MAX_WORKERS, GRPC_GRACE_PERIOD
 from dbscanserving.client.DbscanServingClient import DbscanServingClient
diff --git a/src/device/.gitlab-ci.yml b/src/device/.gitlab-ci.yml
index 2cd6cd848e548c32dbd01901b7fc13da3fe071f8..aa2f9114a8c1507397171286e79d4daf8c348899 100644
--- a/src/device/.gitlab-ci.yml
+++ b/src/device/.gitlab-ci.yml
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 # Build, tag and push the Docker image to the GitLab registry
 build device:
   variables:
@@ -40,7 +54,9 @@ unit test device:
     - sleep 5
     - docker ps -a
     - docker logs $IMAGE_NAME
-    - docker exec -i $IMAGE_NAME bash -c "coverage run -m pytest --log-level=INFO --verbose $IMAGE_NAME/tests/test_unitary.py --junitxml=/opt/results/${IMAGE_NAME}_report.xml; coverage xml -o /opt/results/${IMAGE_NAME}_coverage.xml; coverage report --include='${IMAGE_NAME}/*' --show-missing"
+    - docker exec -i $IMAGE_NAME bash -c "coverage run -m pytest --log-level=INFO --verbose $IMAGE_NAME/tests/test_unitary.py --junitxml=/opt/results/${IMAGE_NAME}_report.xml"
+    - docker exec -i $IMAGE_NAME bash -c "coverage xml -o /opt/results/${IMAGE_NAME}_coverage.xml"
+    - docker exec -i $IMAGE_NAME bash -c "coverage report --include='${IMAGE_NAME}/*' --show-missing"
   coverage: '/TOTAL\s+\d+\s+\d+\s+(\d+%)/'
   after_script:
     - docker rm -f $IMAGE_NAME
diff --git a/src/device/Config.py b/src/device/Config.py
index 753f4b57b2767552317882e72622c613f40e3ea2..415ae7b01ced740a0dc09f215ae71ad553a2672e 100644
--- a/src/device/Config.py
+++ b/src/device/Config.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging
 
 # General settings
diff --git a/src/device/Dockerfile b/src/device/Dockerfile
index 70e3c8dcbbc6b78a53b468e51034ffb8e69f8e53..2f63e775c3004efda3b76e074155bd0e1962920f 100644
--- a/src/device/Dockerfile
+++ b/src/device/Dockerfile
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 FROM python:3-slim
 
 # Install dependencies
@@ -25,7 +39,7 @@ RUN mkdir -p /var/teraflow/device
 # Get Python packages per module
 COPY device/requirements.in device/requirements.in
 RUN pip-compile --output-file=device/requirements.txt device/requirements.in
-RUN python3 -m pip install -r device/requirements.in
+RUN python3 -m pip install -r device/requirements.txt
 
 # Add files into working directory
 COPY common/. common
diff --git a/src/device/__init__.py b/src/device/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/device/__init__.py
+++ b/src/device/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/device/client/DeviceClient.py b/src/device/client/DeviceClient.py
index 7c5fa0ca2561a429d240b36f38357a874b882aa8..2a9512411a2d9e55e4c5b0fa75d48fc54d810713 100644
--- a/src/device/client/DeviceClient.py
+++ b/src/device/client/DeviceClient.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import grpc, logging
 from common.tools.client.RetryDecorator import retry, delay_exponential
 from device.proto.context_pb2 import Device, DeviceConfig, DeviceId, Empty
@@ -7,6 +21,7 @@ from device.proto.device_pb2_grpc import DeviceServiceStub
 LOGGER = logging.getLogger(__name__)
 MAX_RETRIES = 15
 DELAY_FUNCTION = delay_exponential(initial=0.01, increment=2.0, maximum=5.0)
+RETRY_DECORATOR = retry(max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
 
 class DeviceClient:
     def __init__(self, address, port):
@@ -22,39 +37,39 @@ class DeviceClient:
         self.stub = DeviceServiceStub(self.channel)
 
     def close(self):
-        if self.channel is not None: self.channel.close()
+        if(self.channel is not None): self.channel.close()
         self.channel = None
         self.stub = None
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def AddDevice(self, request : Device) -> DeviceId:
         LOGGER.debug('AddDevice request: {:s}'.format(str(request)))
         response = self.stub.AddDevice(request)
         LOGGER.debug('AddDevice result: {:s}'.format(str(response)))
         return response
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def ConfigureDevice(self, request : Device) -> DeviceId:
         LOGGER.debug('ConfigureDevice request: {:s}'.format(str(request)))
         response = self.stub.ConfigureDevice(request)
         LOGGER.debug('ConfigureDevice result: {:s}'.format(str(response)))
         return response
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def DeleteDevice(self, request : DeviceId) -> Empty:
         LOGGER.debug('DeleteDevice request: {:s}'.format(str(request)))
         response = self.stub.DeleteDevice(request)
         LOGGER.debug('DeleteDevice result: {:s}'.format(str(response)))
         return response
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def GetInitialConfig(self, request : DeviceId) -> DeviceConfig:
         LOGGER.debug('GetInitialConfig request: {:s}'.format(str(request)))
         response = self.stub.GetInitialConfig(request)
         LOGGER.debug('GetInitialConfig result: {:s}'.format(str(response)))
         return response
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def MonitorDeviceKpi(self, request : MonitoringSettings) -> Empty:
         LOGGER.debug('MonitorDeviceKpi request: {:s}'.format(str(request)))
         response = self.stub.MonitorDeviceKpi(request)
diff --git a/src/device/client/__init__.py b/src/device/client/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/device/client/__init__.py
+++ b/src/device/client/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/device/proto/__init__.py b/src/device/proto/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/device/proto/__init__.py
+++ b/src/device/proto/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/device/requirements.in b/src/device/requirements.in
index 6a86f5385a313ddc52ebf0d1372246e2ffd4838b..dde08cf195ecb503c7a10f75a2df9ca9f1a0c651 100644
--- a/src/device/requirements.in
+++ b/src/device/requirements.in
@@ -1,17 +1,20 @@
-anytree
-apscheduler
-fastcache
-grpcio-health-checking
-grpcio
-Jinja2
-netconf-client #1.7.3
-prometheus-client
-pytest
-pytest-benchmark
-python-json-logger
-pytz
-redis
-requests
-xmltodict
+anytree==2.8.0
+APScheduler==3.8.1
+fastcache==1.1.0
+grpcio==1.43.0
+grpcio-health-checking==1.43.0
+Jinja2==3.0.3
+netconf-client==2.0.0 #1.7.3
 p4runtime==1.3.0
-coverage
+paramiko==2.9.2
+prometheus-client==0.13.0
+protobuf==3.19.3
+pytest==6.2.5
+pytest-benchmark==3.4.1
+python-dateutil==2.8.2
+python-json-logger==2.0.2
+pytz==2021.3
+redis==4.1.2
+requests==2.27.1
+xmltodict==0.12.0
+coverage==6.3
diff --git a/src/device/service/DeviceService.py b/src/device/service/DeviceService.py
index 11cb8d3e39236002ee7ca6471c69112bbcfa059a..bb2cc09535af579a24cf05687d2883d81c7c914b 100644
--- a/src/device/service/DeviceService.py
+++ b/src/device/service/DeviceService.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import grpc, logging
 from concurrent import futures
 from grpc_health.v1.health import HealthServicer, OVERALL_HEALTH
diff --git a/src/device/service/DeviceServiceServicerImpl.py b/src/device/service/DeviceServiceServicerImpl.py
index 485cf0a27994b4943df6d8aadf3d54d3e63bfdcf..8e00b344f0462ae56b289cfc5d33e6c1b1c42b7e 100644
--- a/src/device/service/DeviceServiceServicerImpl.py
+++ b/src/device/service/DeviceServiceServicerImpl.py
@@ -1,18 +1,30 @@
-import grpc, json, logging
-from typing import Any, List, Tuple
-from google.protobuf.json_format import MessageToDict
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import grpc, json, logging, re
+from typing import Any, Dict, List, Tuple
 from common.orm.Database import Database
 from common.orm.HighLevel import get_object, update_or_create_object
 from common.orm.backend.Tools import key_to_str
 from common.rpc_method_wrapper.Decorator import create_metrics, safe_and_metered_rpc_method
 from common.rpc_method_wrapper.ServiceExceptions import InvalidArgumentException, OperationFailedException
+from common.tools.grpc.Tools import grpc_message_to_json
 from context.client.ContextClient import ContextClient
 from context.proto.kpi_sample_types_pb2 import KpiSampleType
 from device.proto.context_pb2 import ConfigActionEnum, Device, DeviceConfig, DeviceId, Empty
 from device.proto.device_pb2 import MonitoringSettings
 from device.proto.device_pb2_grpc import DeviceServiceServicer
-from device.service.database.RelationModels import EndPointMonitorKpiModel
-from .MonitoringLoops import MonitoringLoops
 from .database.ConfigModel import (
     ConfigModel, ConfigRuleModel, ORM_ConfigActionEnum, get_config_rules, grpc_config_rules_to_raw, update_config)
 from .database.DatabaseTools import (
@@ -22,10 +34,12 @@ from .database.DeviceModel import DeviceModel, DriverModel
 from .database.EndPointModel import EndPointModel, EndPointMonitorModel
 from .database.KpiModel import KpiModel
 from .database.KpiSampleType import ORM_KpiSampleTypeEnum, grpc_to_enum__kpi_sample_type
+from .database.RelationModels import EndPointMonitorKpiModel
 from .driver_api._Driver import _Driver, RESOURCE_ENDPOINTS #, RESOURCE_INTERFACES, RESOURCE_NETWORK_INSTANCES
 from .driver_api.DriverInstanceCache import DriverInstanceCache
 from .driver_api.Tools import (
     check_delete_errors, check_set_errors, check_subscribe_errors, check_unsubscribe_errors)
+from .MonitoringLoops import MonitoringLoops
 
 LOGGER = logging.getLogger(__name__)
 
@@ -59,9 +73,7 @@ class DeviceServiceServicerImpl(DeviceServiceServicer):
             else:
                 unexpected_config_rules.append(config_rule)
         if len(unexpected_config_rules) > 0:
-            unexpected_config_rules = MessageToDict(
-                request.device_config, including_default_value_fields=True,
-                preserving_proto_field_name=True, use_integers_for_enums=True)
+            unexpected_config_rules = grpc_message_to_json(request.device_config)
             unexpected_config_rules = unexpected_config_rules['config_rules']
             unexpected_config_rules = list(filter(
                 lambda cr: cr['resource_key'].replace('_connect/', '') not in connection_config_rules,
@@ -75,9 +87,7 @@ class DeviceServiceServicerImpl(DeviceServiceServicer):
         if len(request.device_endpoints) > 0:
             unexpected_endpoints = []
             for device_endpoint in request.device_endpoints:
-                unexpected_endpoints.append(MessageToDict(
-                    device_endpoint, including_default_value_fields=True, preserving_proto_field_name=True,
-                    use_integers_for_enums=True))
+                unexpected_endpoints.append(grpc_message_to_json(device_endpoint))
             str_unexpected_endpoints = json.dumps(unexpected_endpoints, sort_keys=True)
             raise InvalidArgumentException(
                 'device.device_endpoints', str_unexpected_endpoints,
@@ -85,9 +95,7 @@ class DeviceServiceServicerImpl(DeviceServiceServicer):
                               'interrogation of the physical device.')
 
         # Remove device configuration
-        json_request = MessageToDict(
-            request, including_default_value_fields=True, preserving_proto_field_name=True,
-            use_integers_for_enums=True)
+        json_request = grpc_message_to_json(request, use_integers_for_enums=True)
         json_request['device_config'] = {}
         request = Device(**json_request)
 
@@ -111,32 +119,42 @@ class DeviceServiceServicerImpl(DeviceServiceServicer):
         driver.Connect()
 
         endpoints = driver.GetConfig([RESOURCE_ENDPOINTS])
-        #LOGGER.info('[AddDevice] endpoints = {:s}'.format(str(endpoints)))
-        for resource_key, resource_value in endpoints:
-            endpoint_uuid = resource_value.get('uuid')
-            endpoint_type = resource_value.get('type')
-            str_endpoint_key = key_to_str([device_uuid, endpoint_uuid])
-            db_endpoint, _ = update_or_create_object(
-                self.database, EndPointModel, str_endpoint_key, {
-                'device_fk'    : db_device,
-                'endpoint_uuid': endpoint_uuid,
-                'endpoint_type': endpoint_type,
-                'resource_key' : resource_key,
-            })
-            sample_types = resource_value.get('sample_types', {})
-            for sample_type, monitor_resource_key in sample_types.items():
-                str_endpoint_monitor_key = key_to_str([str_endpoint_key, str(sample_type)])
-                update_or_create_object(self.database, EndPointMonitorModel, str_endpoint_monitor_key, {
-                    'endpoint_fk'    : db_endpoint,
-                    'resource_key'   : monitor_resource_key,
-                    'kpi_sample_type': grpc_to_enum__kpi_sample_type(sample_type),
+        try:
+            for resource_key, resource_value in endpoints:
+                if isinstance(resource_value, Exception):
+                    LOGGER.error('Error retrieving "{:s}": {:s}'.format(str(RESOURCE_ENDPOINTS), str(resource_value)))
+                    continue
+                endpoint_uuid = resource_value.get('uuid')
+                endpoint_type = resource_value.get('type')
+                str_endpoint_key = key_to_str([device_uuid, endpoint_uuid])
+                db_endpoint, _ = update_or_create_object(
+                    self.database, EndPointModel, str_endpoint_key, {
+                    'device_fk'    : db_device,
+                    'endpoint_uuid': endpoint_uuid,
+                    'endpoint_type': endpoint_type,
+                    'resource_key' : resource_key,
                 })
+                sample_types : Dict[int, str] = resource_value.get('sample_types', {})
+                for sample_type, monitor_resource_key in sample_types.items():
+                    str_endpoint_monitor_key = key_to_str([str_endpoint_key, str(sample_type)])
+                    update_or_create_object(self.database, EndPointMonitorModel, str_endpoint_monitor_key, {
+                        'endpoint_fk'    : db_endpoint,
+                        'resource_key'   : monitor_resource_key,
+                        'kpi_sample_type': grpc_to_enum__kpi_sample_type(sample_type),
+                    })
+        except: # pylint: disable=bare-except
+            LOGGER.exception('[AddDevice] endpoints = {:s}'.format(str(endpoints)))
+
+        raw_running_config_rules = driver.GetConfig()
+        running_config_rules = []
+        for resource_key, resource_value in raw_running_config_rules:
+            if isinstance(resource_value, Exception):
+                msg = 'Error retrieving config rules: {:s} => {:s}'
+                LOGGER.error(msg.format(str(resource_key), str(resource_value)))
+                continue
+            config_rule = (ORM_ConfigActionEnum.SET, resource_key, json.dumps(resource_value, sort_keys=True))
+            running_config_rules.append(config_rule)
 
-        running_config_rules = driver.GetConfig()
-        running_config_rules = [
-            (ORM_ConfigActionEnum.SET, config_rule[0], json.dumps(config_rule[1], sort_keys=True))
-            for config_rule in running_config_rules
-        ]
         #for running_config_rule in running_config_rules:
         #    LOGGER.info('[AddDevice] running_config_rule: {:s}'.format(str(running_config_rule)))
         update_config(self.database, device_uuid, 'running', running_config_rules)
@@ -216,6 +234,8 @@ class DeviceServiceServicerImpl(DeviceServiceServicer):
     def DeleteDevice(self, request : DeviceId, context : grpc.ServicerContext) -> Empty:
         device_uuid = request.device_uuid.uuid
 
+        self.monitoring_loops.remove(device_uuid)
+
         sync_device_from_context(device_uuid, self.context_client, self.database)
         db_device : DeviceModel = get_object(self.database, DeviceModel, device_uuid, raise_if_not_found=False)
         if db_device is None: return Empty()
@@ -224,24 +244,27 @@ class DeviceServiceServicerImpl(DeviceServiceServicer):
         delete_device_from_context(db_device, self.context_client)
 
         for db_kpi_pk,_ in db_device.references(KpiModel):
-            KpiModel(self.database, db_kpi_pk).delete()
+            db_kpi = get_object(self.database, KpiModel, db_kpi_pk)
+            for db_endpoint_monitor_kpi_pk,_ in db_kpi.references(EndPointMonitorKpiModel):
+                get_object(self.database, EndPointMonitorKpiModel, db_endpoint_monitor_kpi_pk).delete()
+            db_kpi.delete()
 
         for db_endpoint_pk,_ in db_device.references(EndPointModel):
             db_endpoint = EndPointModel(self.database, db_endpoint_pk)
             for db_endpoint_monitor_pk,_ in db_endpoint.references(EndPointMonitorModel):
-                EndPointMonitorModel(self.database, db_endpoint_monitor_pk).delete()
+                get_object(self.database, EndPointMonitorModel, db_endpoint_monitor_pk).delete()
             db_endpoint.delete()
 
         for db_driver_pk,_ in db_device.references(DriverModel):
-            DriverModel(self.database, db_driver_pk).delete()
+            get_object(self.database, DriverModel, db_driver_pk).delete()
 
         db_initial_config = ConfigModel(self.database, db_device.device_initial_config_fk)
         for db_config_rule_pk,_ in db_initial_config.references(ConfigRuleModel):
-            ConfigRuleModel(self.database, db_config_rule_pk).delete()
+            get_object(self.database, ConfigRuleModel, db_config_rule_pk).delete()
 
         db_running_config = ConfigModel(self.database, db_device.device_running_config_fk)
         for db_config_rule_pk,_ in db_running_config.references(ConfigRuleModel):
-            ConfigRuleModel(self.database, db_config_rule_pk).delete()
+            get_object(self.database, ConfigRuleModel, db_config_rule_pk).delete()
 
         db_device.delete()
         db_initial_config.delete()
@@ -309,12 +332,13 @@ class DeviceServiceServicerImpl(DeviceServiceServicer):
             db_endpoint_monitor : EndPointMonitorModel = get_object(
                 self.database, EndPointMonitorModel, str_endpoint_monitor_key, raise_if_not_found=False)
             if db_endpoint_monitor is None:
-                msg = 'SampleType({:s}/{:s}) not supported for EndPoint({:s}).'.format(
+                msg = 'SampleType({:s}/{:s}) not supported for Device({:s})/EndPoint({:s}).'.format(
                     str(sample_type), str(KpiSampleType.Name(sample_type).upper().replace('KPISAMPLETYPE_', '')),
-                    str(endpoint_uuid))
+                    str(device_uuid), str(endpoint_uuid))
                 raise OperationFailedException('MonitorDeviceKpi', extra_details=msg)
 
-            str_endpoint_monitor_kpi_key = key_to_str([device_uuid, db_endpoint_monitor.resource_key], separator=':')
+            endpoint_monitor_resource_key = re.sub('[^A-Za-z0-9]', '.', db_endpoint_monitor.resource_key)
+            str_endpoint_monitor_kpi_key = key_to_str([device_uuid, endpoint_monitor_resource_key], separator=':')
             attributes = {
                 'endpoint_monitor_fk': db_endpoint_monitor,
                 'kpi_fk'             : db_kpi,
@@ -363,7 +387,8 @@ class DeviceServiceServicerImpl(DeviceServiceServicer):
                 msg = 'EndPointMonitor({:s}) not found.'.format(str(str_endpoint_monitor_key))
                 raise OperationFailedException('MonitorDeviceKpi', extra_details=msg)
 
-            str_endpoint_monitor_kpi_key = key_to_str([device_uuid, db_endpoint_monitor.resource_key], separator=':')
+            endpoint_monitor_resource_key = re.sub('[^A-Za-z0-9]', '.', db_endpoint_monitor.resource_key)
+            str_endpoint_monitor_kpi_key = key_to_str([device_uuid, endpoint_monitor_resource_key], separator=':')
             db_endpoint_monitor_kpi : EndPointMonitorKpiModel = get_object(
                 self.database, EndPointMonitorKpiModel, str_endpoint_monitor_kpi_key, raise_if_not_found=False)
             if db_endpoint_monitor_kpi is None:
@@ -390,4 +415,32 @@ class DeviceServiceServicerImpl(DeviceServiceServicer):
             # requests.
             #self.monitoring_loops.remove(device_uuid)
 
+        # Subscriptions are not stored as classical driver config.
+        # TODO: consider adding it somehow in the configuration.
+        # Warning: GetConfig might be very slow in OpenConfig devices
+        #running_config_rules = [
+        #    (config_rule[0], json.dumps(config_rule[1], sort_keys=True))
+        #    for config_rule in driver.GetConfig()
+        #]
+        #context_config_rules = {
+        #    config_rule[1]: config_rule[2]
+        #    for config_rule in get_config_rules(self.database, device_uuid, 'running')
+        #}
+
+        ## each in context, not in running => delete in context
+        ## each in running, not in context => add to context
+        ## each in context and in running, context.value != running.value => update in context
+        #running_config_rules_actions : List[Tuple[ORM_ConfigActionEnum, str, str]] = []
+        #for config_rule_key,config_rule_value in running_config_rules:
+        #    running_config_rules_actions.append((ORM_ConfigActionEnum.SET, config_rule_key, config_rule_value))
+        #    context_config_rules.pop(config_rule_key, None)
+        #for context_rule_key,context_rule_value in context_config_rules.items():
+        #    running_config_rules_actions.append((ORM_ConfigActionEnum.DELETE, context_rule_key, context_rule_value))
+
+        ##msg = '[MonitorDeviceKpi] running_config_rules_action[{:d}]: {:s}'
+        ##for i,running_config_rules_action in enumerate(running_config_rules_actions):
+        ##    LOGGER.info(msg.format(i, str(running_config_rules_action)))
+        #update_config(self.database, device_uuid, 'running', running_config_rules_actions)
+
+        sync_device_to_context(db_device, self.context_client)
         return Empty()
diff --git a/src/device/service/MonitoringLoops.py b/src/device/service/MonitoringLoops.py
index 2e96e8df1dfd7a050aae004b5d5a41bae469e438..e5b671f7f06beade5ab9f8b6539527999d49b9e8 100644
--- a/src/device/service/MonitoringLoops.py
+++ b/src/device/service/MonitoringLoops.py
@@ -1,12 +1,27 @@
-import logging, queue, threading
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging, queue, re, threading
+from datetime import datetime
 from typing import Dict
 from common.orm.Database import Database
 from common.orm.HighLevel import get_object
 from common.orm.backend.Tools import key_to_str
-from device.service.database.RelationModels import EndPointMonitorKpiModel
 from monitoring.client.monitoring_client import MonitoringClient
 from monitoring.proto.monitoring_pb2 import Kpi
 from .database.KpiModel import KpiModel
+from .database.RelationModels import EndPointMonitorKpiModel
 from .driver_api._Driver import _Driver
 
 LOGGER = logging.getLogger(__name__)
@@ -19,7 +34,7 @@ class MonitoringLoop:
         self._samples_queue = samples_queue
         self._running = threading.Event()
         self._terminate = threading.Event()
-        self._samples_stream = self._driver.GetState(blocking=True)
+        self._samples_stream = self._driver.GetState(blocking=True, terminate=self._terminate)
         self._collector_thread = threading.Thread(target=self._collect, daemon=True)
 
     def _collect(self) -> None:
@@ -37,7 +52,6 @@ class MonitoringLoop:
 
     def stop(self):
         self._terminate.set()
-        self._samples_stream.cancel()
         self._collector_thread.join()
 
 class MonitoringLoops:
@@ -90,6 +104,7 @@ class MonitoringLoops:
                 continue
 
             device_uuid, timestamp, endpoint_monitor_resource_key, value = sample
+            endpoint_monitor_resource_key = re.sub('[^A-Za-z0-9]', '.', endpoint_monitor_resource_key)
             str_endpoint_monitor_kpi_key = key_to_str([device_uuid, endpoint_monitor_resource_key], separator=':')
 
             #db_entries = self._database.dump()
@@ -111,9 +126,11 @@ class MonitoringLoops:
                 LOGGER.warning('Kpi({:s}) not found'.format(str_kpi_key))
                 continue
 
+            # FIXME: uint32 used for intVal results in out of range issues. Temporarily changed to float
+            #        extend the 'kpi_value' to support long integers (uint64 / int64 / ...)
             if isinstance(value, int):
-                kpi_value_field_name = 'intVal'
-                kpi_value_field_cast = int
+                kpi_value_field_name = 'floatVal'   # 'intVal'
+                kpi_value_field_cast = float        # int
             elif isinstance(value, float):
                 kpi_value_field_name = 'floatVal'
                 kpi_value_field_cast = float
@@ -127,7 +144,7 @@ class MonitoringLoops:
             try:
                 self._monitoring_client.IncludeKpi(Kpi(**{
                     'kpi_id'   : {'kpi_id': {'uuid': db_kpi.kpi_uuid}},
-                    'timestamp': str(timestamp),
+                    'timestamp': datetime.utcfromtimestamp(timestamp).isoformat() + 'Z',
                     'kpi_value': {kpi_value_field_name: kpi_value_field_cast(value)}
                 }))
             except: # pylint: disable=bare-except
diff --git a/src/device/service/__init__.py b/src/device/service/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/device/service/__init__.py
+++ b/src/device/service/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/device/service/__main__.py b/src/device/service/__main__.py
index 86f9b3ec1091883df068d3a7d2d4409dcffc9e90..0e92cabba4ddae84a9d4cd938c7e8b31ab4f0531 100644
--- a/src/device/service/__main__.py
+++ b/src/device/service/__main__.py
@@ -1,6 +1,20 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging, signal, sys, threading
 from prometheus_client import start_http_server
-from common.Settings import get_setting
+from common.Settings import get_setting, wait_for_environment_variables
 from context.client.ContextClient import ContextClient
 from device.Config import (
     CONTEXT_SERVICE_HOST, CONTEXT_SERVICE_PORT, GRPC_SERVICE_PORT, GRPC_MAX_WORKERS, GRPC_GRACE_PERIOD, LOG_LEVEL,
@@ -12,7 +26,7 @@ from .driver_api.DriverInstanceCache import DriverInstanceCache
 from .drivers import DRIVERS
 
 terminate = threading.Event()
-LOGGER = None
+LOGGER : logging.Logger = None
 
 def signal_handler(signal, frame): # pylint: disable=redefined-outer-name
     LOGGER.warning('Terminate signal received')
@@ -26,14 +40,23 @@ def main():
     grace_period            = get_setting('GRACE_PERIOD',                        default=GRPC_GRACE_PERIOD      )
     log_level               = get_setting('LOG_LEVEL',                           default=LOG_LEVEL              )
     metrics_port            = get_setting('METRICS_PORT',                        default=METRICS_PORT           )
+
+    logging.basicConfig(level=log_level)
+    logging.getLogger('apscheduler.executors.default').setLevel(logging.WARNING)
+    logging.getLogger('apscheduler.scheduler').setLevel(logging.WARNING)
+    logging.getLogger('monitoring-client').setLevel(logging.WARNING)
+    LOGGER = logging.getLogger(__name__)
+
+    wait_for_environment_variables([
+        'CONTEXTSERVICE_SERVICE_HOST', 'CONTEXTSERVICE_SERVICE_PORT_GRPC',
+        'MONITORINGSERVICE_SERVICE_HOST', 'MONITORINGSERVICE_SERVICE_PORT_GRPC'
+    ])
+
     context_service_host    = get_setting('CONTEXTSERVICE_SERVICE_HOST',         default=CONTEXT_SERVICE_HOST   )
     context_service_port    = get_setting('CONTEXTSERVICE_SERVICE_PORT_GRPC',    default=CONTEXT_SERVICE_PORT   )
     monitoring_service_host = get_setting('MONITORINGSERVICE_SERVICE_HOST',      default=MONITORING_SERVICE_HOST)
     monitoring_service_port = get_setting('MONITORINGSERVICE_SERVICE_PORT_GRPC', default=MONITORING_SERVICE_PORT)
 
-    logging.basicConfig(level=log_level)
-    LOGGER = logging.getLogger(__name__)
-
     signal.signal(signal.SIGINT,  signal_handler)
     signal.signal(signal.SIGTERM, signal_handler)
 
diff --git a/src/device/service/database/ConfigModel.py b/src/device/service/database/ConfigModel.py
index 82697bfbae7d1f58fdc851aa66f86f0fc78a767e..466ad2ff4a85a132ee8fd02a6a0b2c0b6f8a232a 100644
--- a/src/device/service/database/ConfigModel.py
+++ b/src/device/service/database/ConfigModel.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import functools, logging, operator
 from enum import Enum
 from typing import Dict, List, Tuple, Union
diff --git a/src/device/service/database/ContextModel.py b/src/device/service/database/ContextModel.py
index f4da5097e3f1ab0298ec66ff6007fb6a21be65ed..0ca13269c52d02ea663e10be986ab28d12d8f144 100644
--- a/src/device/service/database/ContextModel.py
+++ b/src/device/service/database/ContextModel.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging
 from typing import Dict, List
 from common.orm.fields.PrimaryKeyField import PrimaryKeyField
diff --git a/src/device/service/database/DatabaseTools.py b/src/device/service/database/DatabaseTools.py
index 27a5f89a579451e9e512bc7288ca8690a25de27d..5b1593612dcdba7ee3df3f416aaf519dd11ce9dc 100644
--- a/src/device/service/database/DatabaseTools.py
+++ b/src/device/service/database/DatabaseTools.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import grpc
 from typing import Any, Dict, Tuple
 from common.orm.Database import Database
diff --git a/src/device/service/database/DeviceModel.py b/src/device/service/database/DeviceModel.py
index bba19d787622019b7b8f25de9c07b7c0984ec42c..555e113084cff367878a3eabf81b17d89dcb6df9 100644
--- a/src/device/service/database/DeviceModel.py
+++ b/src/device/service/database/DeviceModel.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import functools, logging
 from enum import Enum
 from typing import Dict, List
diff --git a/src/device/service/database/EndPointModel.py b/src/device/service/database/EndPointModel.py
index 7e0832c51f70cabe49cd4b19d7c23bf923bcf98e..286a51db69738782a6d1acaed5b1d7846ac67b2b 100644
--- a/src/device/service/database/EndPointModel.py
+++ b/src/device/service/database/EndPointModel.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging
 from typing import Dict, List
 from common.orm.Database import Database
diff --git a/src/device/service/database/KpiModel.py b/src/device/service/database/KpiModel.py
index 3ec78f60f233f5492d4780009e7cbf815f5e5248..e3631d38099c02cd459af7f8393b6991c476bd92 100644
--- a/src/device/service/database/KpiModel.py
+++ b/src/device/service/database/KpiModel.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging
 from typing import Dict
 from common.orm.fields.EnumeratedField import EnumeratedField
diff --git a/src/device/service/database/KpiSampleType.py b/src/device/service/database/KpiSampleType.py
index 24ac67200e85bb7fe29cf0971de020351b2b45da..43249792f2a33cf56b43d2301ecbae0faca4c767 100644
--- a/src/device/service/database/KpiSampleType.py
+++ b/src/device/service/database/KpiSampleType.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import functools
 from enum import Enum
 from device.proto.kpi_sample_types_pb2 import KpiSampleType
diff --git a/src/device/service/database/RelationModels.py b/src/device/service/database/RelationModels.py
index 6d1a9780f3bcd12d4aa3718c94e910b80a40ba18..0f6caa646f7548fe0d4aa23829183a132069c589 100644
--- a/src/device/service/database/RelationModels.py
+++ b/src/device/service/database/RelationModels.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging
 from common.orm.fields.ForeignKeyField import ForeignKeyField
 from common.orm.fields.PrimaryKeyField import PrimaryKeyField
diff --git a/src/device/service/database/Tools.py b/src/device/service/database/Tools.py
index 36ffbcd46fcf686371b0799445ce4f9ce5b75838..43bb71bd90582644c67d3ca528611eae937b6460 100644
--- a/src/device/service/database/Tools.py
+++ b/src/device/service/database/Tools.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import hashlib, re
 from enum import Enum
 from typing import Dict, List, Tuple, Union
diff --git a/src/device/service/database/TopologyModel.py b/src/device/service/database/TopologyModel.py
index 71c1067f03803c6d89c69910c82a3c1e24970eee..a099c8adfd8bb64d94c8326c90094f39d7fe9b6b 100644
--- a/src/device/service/database/TopologyModel.py
+++ b/src/device/service/database/TopologyModel.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging
 from typing import Dict
 from common.orm.fields.ForeignKeyField import ForeignKeyField
diff --git a/src/device/service/database/__init__.py b/src/device/service/database/__init__.py
index ff9b7da0cd0f83f8e01bbb904d8e2471503f00ca..c59423e79961c8503f4469d69c53946988cae24e 100644
--- a/src/device/service/database/__init__.py
+++ b/src/device/service/database/__init__.py
@@ -1,2 +1,16 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 # In-Memory database with a simplified representation of Context Database focused on the Device model.
 # Used as an internal configuration cache, for message validation, and message formatting purposes.
diff --git a/src/device/service/driver_api/AnyTreeTools.py b/src/device/service/driver_api/AnyTreeTools.py
index 3b247eca6af25d1c34ad46fd824f8303c12c74d9..f4999b64bab9ddd19d97037f99767ee17827aa24 100644
--- a/src/device/service/driver_api/AnyTreeTools.py
+++ b/src/device/service/driver_api/AnyTreeTools.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import anytree
 from typing import Any, List, Optional, Union
 from apscheduler.job import Job
diff --git a/src/device/service/driver_api/DriverFactory.py b/src/device/service/driver_api/DriverFactory.py
index 10b4961fcf98871cc4cbf3e078391006591b606c..1e79b4ba45593d3f24f7193648010071d766ec58 100644
--- a/src/device/service/driver_api/DriverFactory.py
+++ b/src/device/service/driver_api/DriverFactory.py
@@ -1,11 +1,25 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging, operator
 from enum import Enum
 from typing import Any, Dict, Iterable, List, Set, Tuple
-from device.service.driver_api._Driver import _Driver
-from device.service.driver_api.Exceptions import (
+from ._Driver import _Driver
+from .Exceptions import (
     UnsatisfiedFilterException, UnsupportedDriverClassException, UnsupportedFilterFieldException,
     UnsupportedFilterFieldValueException)
-from device.service.driver_api.FilterFields import FILTER_FIELD_ALLOWED_VALUES, FilterFieldEnum
+from .FilterFields import FILTER_FIELD_ALLOWED_VALUES, FilterFieldEnum
 
 LOGGER = logging.getLogger(__name__)
 
diff --git a/src/device/service/driver_api/DriverInstanceCache.py b/src/device/service/driver_api/DriverInstanceCache.py
index f960e37cbd22a7d4c06c9119ffaf64540dfc791a..41cc66363885e28082aa353ec46950fbf6ce10e0 100644
--- a/src/device/service/driver_api/DriverInstanceCache.py
+++ b/src/device/service/driver_api/DriverInstanceCache.py
@@ -1,10 +1,23 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging, threading
 from typing import Any, Dict, Optional
-
-from device.service.driver_api.FilterFields import FilterFieldEnum
 from ._Driver import _Driver
 from .DriverFactory import DriverFactory
 from .Exceptions import DriverInstanceCacheTerminatedException
+from .FilterFields import FilterFieldEnum
 
 LOGGER = logging.getLogger(__name__)
 
diff --git a/src/device/service/driver_api/Exceptions.py b/src/device/service/driver_api/Exceptions.py
index c35d6372d60b4ea465b40d98e9e43e979835451d..1a03da97f3fcd42c5417318864277f36e62f6233 100644
--- a/src/device/service/driver_api/Exceptions.py
+++ b/src/device/service/driver_api/Exceptions.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 class UnsatisfiedFilterException(Exception):
     def __init__(self, filter_fields):
         msg = 'No Driver satisfies FilterFields({:s})'
diff --git a/src/device/service/driver_api/FilterFields.py b/src/device/service/driver_api/FilterFields.py
index 892e7f72056cd3342ce04190710d492ec83a02d3..9ea5445903958286d68ff3246e0801e0a7955d2a 100644
--- a/src/device/service/driver_api/FilterFields.py
+++ b/src/device/service/driver_api/FilterFields.py
@@ -1,15 +1,21 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 from enum import Enum
+from common.DeviceTypes import DeviceTypeEnum
 from device.service.database.DeviceModel import ORM_DeviceDriverEnum
 
-class DeviceTypeFilterFieldEnum(Enum):
-    EMULATED            = 'emulated'
-    OPTICAL_ROADM       = 'optical-roadm'
-    OPTICAL_TRANDPONDER = 'optical-trandponder'
-    OPTICAL_LINE_SYSTEM = 'optical-line-system'
-    PACKET_ROUTER       = 'packet-router'
-    PACKET_SWITCH       = 'packet-switch'
-    P4_SWITCH           = 'p4-switch'
-
 class FilterFieldEnum(Enum):
     DEVICE_TYPE   = 'device_type'
     DRIVER        = 'driver'
@@ -19,7 +25,7 @@ class FilterFieldEnum(Enum):
 
 # Map allowed filter fields to allowed values per Filter field. If no restriction (free text) None is specified
 FILTER_FIELD_ALLOWED_VALUES = {
-    FilterFieldEnum.DEVICE_TYPE.value   : {i.value for i in DeviceTypeFilterFieldEnum},
+    FilterFieldEnum.DEVICE_TYPE.value   : {i.value for i in DeviceTypeEnum},
     FilterFieldEnum.DRIVER.value        : {i.value for i in ORM_DeviceDriverEnum},
     FilterFieldEnum.VENDOR.value        : None,
     FilterFieldEnum.MODEL.value         : None,
diff --git a/src/device/service/driver_api/Tools.py b/src/device/service/driver_api/Tools.py
index ab22cbc040daee7e47e80b0235b79b19cdb7acbc..19c81d89bfe7e7e1bd46edb205eaf1f2b4bee778 100644
--- a/src/device/service/driver_api/Tools.py
+++ b/src/device/service/driver_api/Tools.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import operator
 from typing import Any, Callable, List, Tuple, Union
 
diff --git a/src/device/service/driver_api/_Driver.py b/src/device/service/driver_api/_Driver.py
index 73174ba53ac6e0b357d3bd790c53f429bea36d87..f30165a178a5946c414157da5d09df07bf060a39 100644
--- a/src/device/service/driver_api/_Driver.py
+++ b/src/device/service/driver_api/_Driver.py
@@ -1,10 +1,26 @@
-from typing import Any, Iterator, List, Tuple, Union
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import threading
+from typing import Any, Iterator, List, Optional, Tuple, Union
 
 # Special resource names to request to the driver to retrieve the specified configuration/structural resources.
 # These resource names should be used with GetConfig() method.
 RESOURCE_ENDPOINTS         = '__endpoints__'
 RESOURCE_INTERFACES        = '__interfaces__'
 RESOURCE_NETWORK_INSTANCES = '__network_instances__'
+RESOURCE_ROUTING_POLICIES  = '__routing_policies__'
 
 class _Driver:
     def __init__(self, address : str, port : int, **settings) -> None:
@@ -56,17 +72,6 @@ class _Driver:
         """
         raise NotImplementedError()
 
-    #def GetResource(self, endpoint_uuid : str) -> Optional[str]:
-    #    """ Retrieve the endpoint path for subscriptions.
-    #        Parameters:
-    #            endpoint_uuid : str
-    #                Target endpoint UUID
-    #        Returns:
-    #            resource_path : Optional[str]
-    #                The path of the endpoint, or None if it is not found.
-    #    """
-    #    raise NotImplementedError()
-
     def SetConfig(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]:
         """ Create/Update configuration for a list of resources.
             Parameters:
@@ -129,21 +134,30 @@ class _Driver:
         """
         raise NotImplementedError()
 
-    def GetState(self, blocking=False) -> Iterator[Tuple[float, str, Any]]:
+    def GetState(
+        self, blocking=False, terminate : Optional[threading.Event] = None
+    ) -> Iterator[Tuple[float, str, Any]]:
         """ Retrieve last collected values for subscribed resources. Operates as a generator, so this method should be
             called once and will block until values are available. When values are available, it should yield each of
             them and block again until new values are available. When the driver is destroyed, GetState() can return
-            instead of yield to terminate the loop.
+            instead of yield to terminate the loop. Terminate enables to request interruption of the generation.
             Examples:
                 # keep looping waiting for extra samples (generator loop)
-                for timestamp,resource_key,resource_value in my_driver.GetState(blocking=True):
+                terminate = threading.Event()
+                i = 0
+                for timestamp,resource_key,resource_value in my_driver.GetState(blocking=True, terminate=terminate):
                     process(timestamp, resource_key, resource_value)
+                    i += 1
+                    if i == 10: terminate.set()
 
                 # just retrieve accumulated samples
-                samples = my_driver.GetState(blocking=False)
+                samples = my_driver.GetState(blocking=False, terminate=terminate)
                 # or (as classical loop)
-                for timestamp,resource_key,resource_value in my_driver.GetState(blocking=False):
+                i = 0
+                for timestamp,resource_key,resource_value in my_driver.GetState(blocking=False, terminate=terminate):
                     process(timestamp, resource_key, resource_value)
+                    i += 1
+                    if i == 10: terminate.set()
             Parameters:
                 blocking : bool
                     Select the driver behaviour. In both cases, the driver will first retrieve the samples accumulated
@@ -152,6 +166,8 @@ class _Driver:
                     terminates the loop and returns. Non-blocking behaviour can be used for periodically polling the
                     driver, while blocking can be used when a separate thread is in charge of collecting the samples
                     produced by the driver.
+                terminate : threading.Event
+                    Signals the interruption of the GetState method as soon as possible.
             Returns:
                 results : Iterator[Tuple[float, str, Any]]
                     Sequences of state sample. Each State sample contains a float Unix-like timestamps of the samples in
diff --git a/src/device/service/driver_api/__init__.py b/src/device/service/driver_api/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/device/service/driver_api/__init__.py
+++ b/src/device/service/driver_api/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/device/service/drivers/__init__.py b/src/device/service/drivers/__init__.py
index 54c944bab8183d57478a07ea46a9aa55b439f0b1..7479d43641b1ecd0803b3c790f51256a03e2c7fa 100644
--- a/src/device/service/drivers/__init__.py
+++ b/src/device/service/drivers/__init__.py
@@ -1,4 +1,19 @@
-from ..driver_api.FilterFields import FilterFieldEnum, DeviceTypeFilterFieldEnum, ORM_DeviceDriverEnum
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from common.DeviceTypes import DeviceTypeEnum
+from ..driver_api.FilterFields import FilterFieldEnum, ORM_DeviceDriverEnum
 from .emulated.EmulatedDriver import EmulatedDriver
 from .openconfig.OpenConfigDriver import OpenConfigDriver
 from .transport_api.TransportApiDriver import TransportApiDriver
@@ -7,25 +22,32 @@ from .p4.p4_driver import P4Driver
 DRIVERS = [
     (EmulatedDriver, [
         {
-            FilterFieldEnum.DEVICE_TYPE: DeviceTypeFilterFieldEnum.EMULATED,
-            FilterFieldEnum.DRIVER     : ORM_DeviceDriverEnum.UNDEFINED,
+            FilterFieldEnum.DEVICE_TYPE: [
+                DeviceTypeEnum.EMULATED_OPTICAL_LINE_SYSTEM,
+                DeviceTypeEnum.EMULATED_PACKET_ROUTER,
+            ],
+            FilterFieldEnum.DRIVER     : [
+                ORM_DeviceDriverEnum.UNDEFINED,
+                ORM_DeviceDriverEnum.OPENCONFIG,
+                ORM_DeviceDriverEnum.TRANSPORT_API
+            ],
         }
     ]),
     (OpenConfigDriver, [
         {
-            FilterFieldEnum.DEVICE_TYPE: DeviceTypeFilterFieldEnum.PACKET_ROUTER,
+            FilterFieldEnum.DEVICE_TYPE: DeviceTypeEnum.PACKET_ROUTER,
             FilterFieldEnum.DRIVER     : ORM_DeviceDriverEnum.OPENCONFIG,
         }
     ]),
     (TransportApiDriver, [
         {
-            FilterFieldEnum.DEVICE_TYPE: DeviceTypeFilterFieldEnum.OPTICAL_LINE_SYSTEM,
+            FilterFieldEnum.DEVICE_TYPE: DeviceTypeEnum.OPTICAL_LINE_SYSTEM,
             FilterFieldEnum.DRIVER     : ORM_DeviceDriverEnum.TRANSPORT_API,
         }
     ]),
     (P4Driver, [
         {
-            FilterFieldEnum.DEVICE_TYPE: DeviceTypeFilterFieldEnum.P4_SWITCH,
+            FilterFieldEnum.DEVICE_TYPE: DeviceTypeEnum.P4_SWITCH,
             FilterFieldEnum.DRIVER     : ORM_DeviceDriverEnum.P4,
         }
     ]),
diff --git a/src/device/service/drivers/emulated/EmulatedDriver.py b/src/device/service/drivers/emulated/EmulatedDriver.py
index c92554fe30bd86066e3b9e31f09412b1dd82020a..2ee9a10ca93ceead96115528873c8876fadcf8ed 100644
--- a/src/device/service/drivers/emulated/EmulatedDriver.py
+++ b/src/device/service/drivers/emulated/EmulatedDriver.py
@@ -1,5 +1,18 @@
-import json
-import anytree, logging, pytz, queue, random, threading
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import anytree, json, logging, math, pytz, queue, random, re, threading
 from datetime import datetime, timedelta
 from typing import Any, Dict, Iterator, List, Optional, Tuple, Union
 from apscheduler.executors.pool import ThreadPoolExecutor
@@ -47,14 +60,74 @@ def compose_resource_endpoint(endpoint_data : Dict[str, Any]) -> Tuple[str, Any]
     endpoint_resource_value = {'uuid': endpoint_uuid, 'type': endpoint_type, 'sample_types': sample_types}
     return endpoint_resource_key, endpoint_resource_value
 
-def do_sampling(resource_key : str, out_samples : queue.Queue):
-    out_samples.put_nowait((datetime.timestamp(datetime.utcnow()), resource_key, random.random()))
+RE_GET_ENDPOINT_METRIC = re.compile(r'.*\/endpoint\[([^\]]+)\]\/state\/(.*)')
+RE_GET_ENDPOINT_FROM_INTERFACE = re.compile(r'.*\/interface\[([^\]]+)\].*')
+
+class SyntheticSamplingParameters:
+    def __init__(self) -> None:
+        self.__lock = threading.Lock()
+        self.__data = {}
+        self.__configured_endpoints = set()
+
+    def set_endpoint_configured(self, endpoint_uuid : str):
+        with self.__lock:
+            self.__configured_endpoints.add(endpoint_uuid)
+
+    def unset_endpoint_configured(self, endpoint_uuid : str):
+        with self.__lock:
+            self.__configured_endpoints.discard(endpoint_uuid)
+
+    def get(self, resource_key : str) -> Tuple[float, float, float, float]:
+        with self.__lock:
+            match = RE_GET_ENDPOINT_METRIC.match(resource_key)
+            if match is None:
+                msg = '[SyntheticSamplingParameters:get] unable to extract endpoint-metric from resource_key "{:s}"'
+                LOGGER.error(msg.format(resource_key))
+                return (0, 0, 1, 0, 0)
+            endpoint_uuid = match.group(1)
+
+            # If endpoint is not configured, generate a flat synthetic traffic aligned at 0
+            if endpoint_uuid not in self.__configured_endpoints: return (0, 0, 1, 0, 0)
+
+            metric = match.group(2)
+            metric_sense = metric.lower().replace('packets_', '').replace('bytes_', '')
+
+            msg = '[SyntheticSamplingParameters:get] resource_key={:s}, endpoint_uuid={:s}, metric={:s}, metric_sense={:s}'
+            LOGGER.info(msg.format(resource_key, endpoint_uuid, metric, metric_sense))
+
+            parameters_key = '{:s}-{:s}'.format(endpoint_uuid, metric_sense)
+            parameters = self.__data.get(parameters_key)
+            if parameters is not None: return parameters
+
+            # assume packets
+            amplitude  = 1.e7 * random.random()
+            phase      = 60 * random.random()
+            period     = 3600 * random.random()
+            offset     = 1.e8 * random.random() + amplitude
+            avg_bytes_per_packet = random.randint(500, 1500)
+            parameters = (amplitude, phase, period, offset, avg_bytes_per_packet)
+            return self.__data.setdefault(parameters_key, parameters)
+
+def do_sampling(
+        synthetic_sampling_parameters : SyntheticSamplingParameters, resource_key : str, out_samples : queue.Queue
+    ):
+    amplitude, phase, period, offset, avg_bytes_per_packet = synthetic_sampling_parameters.get(resource_key)
+    if 'bytes' in resource_key.lower():
+        # convert to bytes
+        amplitude = avg_bytes_per_packet * amplitude
+        offset = avg_bytes_per_packet * offset
+    timestamp = datetime.timestamp(datetime.utcnow())
+    waveform  = amplitude * math.sin(2 * math.pi * timestamp / period + phase) + offset
+    noise     = amplitude * random.random()
+    value     = abs(0.95 * waveform + 0.05 * noise)
+    out_samples.put_nowait((timestamp, resource_key, value))
 
 class EmulatedDriver(_Driver):
     def __init__(self, address : str, port : int, **settings) -> None: # pylint: disable=super-init-not-called
         self.__lock = threading.Lock()
         self.__initial = TreeNode('.')
         self.__running = TreeNode('.')
+        self.__subscriptions = TreeNode('.')
 
         endpoints = settings.get('endpoints', [])
         endpoint_resources = []
@@ -73,6 +146,7 @@ class EmulatedDriver(_Driver):
             job_defaults = {'coalesce': False, 'max_instances': 3},
             timezone=pytz.utc)
         self.__out_samples = queue.Queue()
+        self.__synthetic_sampling_parameters = SyntheticSamplingParameters()
 
     def Connect(self) -> bool:
         # If started, assume it is already connected
@@ -148,6 +222,12 @@ class EmulatedDriver(_Driver):
                     pass
 
                 set_subnode_value(resolver, self.__running, resource_path, resource_value)
+
+                match = RE_GET_ENDPOINT_FROM_INTERFACE.match(resource_key)
+                if match is not None:
+                    endpoint_uuid = match.group(1)
+                    self.__synthetic_sampling_parameters.set_endpoint_configured(endpoint_uuid)
+
                 results.append(True)
         return results
 
@@ -176,6 +256,11 @@ class EmulatedDriver(_Driver):
                     results.append(False)
                     continue
 
+                match = RE_GET_ENDPOINT_FROM_INTERFACE.match(resource_key)
+                if match is not None:
+                    endpoint_uuid = match.group(1)
+                    self.__synthetic_sampling_parameters.unset_endpoint_configured(endpoint_uuid)
+
                 parent = resource_node.parent
                 children = list(parent.children)
                 children.remove(resource_node)
@@ -211,12 +296,12 @@ class EmulatedDriver(_Driver):
 
                 job_id = 'k={:s}/d={:f}/i={:f}'.format(resource_key, sampling_duration, sampling_interval)
                 job = self.__scheduler.add_job(
-                    do_sampling, args=(resource_key, self.__out_samples), kwargs={},
-                    id=job_id, trigger='interval', seconds=sampling_interval,
-                    start_date=start_date, end_date=end_date, timezone=pytz.utc)
+                    do_sampling, args=(self.__synthetic_sampling_parameters, resource_key, self.__out_samples),
+                    kwargs={}, id=job_id, trigger='interval', seconds=sampling_interval, start_date=start_date,
+                    end_date=end_date, timezone=pytz.utc)
 
                 subscription_path = resource_path + ['{:.3f}:{:.3f}'.format(sampling_duration, sampling_interval)]
-                set_subnode_value(resolver, self.__running, subscription_path, job)
+                set_subnode_value(resolver, self.__subscriptions, subscription_path, job)
                 results.append(True)
         return results
 
@@ -242,7 +327,7 @@ class EmulatedDriver(_Driver):
                     continue
 
                 subscription_path = resource_path + ['{:.3f}:{:.3f}'.format(sampling_duration, sampling_interval)]
-                subscription_node = get_subnode(resolver, self.__running, subscription_path)
+                subscription_node = get_subnode(resolver, self.__subscriptions, subscription_path)
 
                 # if not found, resource_node is None
                 if subscription_node is None:
@@ -262,8 +347,10 @@ class EmulatedDriver(_Driver):
                 results.append(True)
         return results
 
-    def GetState(self, blocking=False) -> Iterator[Tuple[str, Any]]:
-        while not self.__terminate.is_set():
+    def GetState(self, blocking=False, terminate : Optional[threading.Event] = None) -> Iterator[Tuple[str, Any]]:
+        while True:
+            if self.__terminate.is_set(): break
+            if terminate is not None and terminate.is_set(): break
             try:
                 sample = self.__out_samples.get(block=blocking, timeout=0.1)
             except queue.Empty:
diff --git a/src/device/service/drivers/emulated/__init__.py b/src/device/service/drivers/emulated/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/device/service/drivers/emulated/__init__.py
+++ b/src/device/service/drivers/emulated/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/device/service/drivers/openconfig/OpenConfigDriver.py b/src/device/service/drivers/openconfig/OpenConfigDriver.py
index e2381d81e5e1aa81907f90b6e473efb154e92e79..7f582c4880bafd08aee0204c7498ea3a3e7ad279 100644
--- a/src/device/service/drivers/openconfig/OpenConfigDriver.py
+++ b/src/device/service/drivers/openconfig/OpenConfigDriver.py
@@ -1,57 +1,191 @@
-import logging, pytz, queue, threading
-#from datetime import datetime, timedelta
-from typing import Any, List, Tuple, Union
-#from apscheduler.executors.pool import ThreadPoolExecutor
-#from apscheduler.job import Job
-#from apscheduler.jobstores.memory import MemoryJobStore
-#from apscheduler.schedulers.background import BackgroundScheduler
-from netconf_client.connect import connect_ssh
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import anytree, copy, logging, pytz, queue, re, threading
+#import lxml.etree as ET
+from datetime import datetime, timedelta
+from typing import Any, Dict, Iterator, List, Optional, Tuple, Union
+from apscheduler.executors.pool import ThreadPoolExecutor
+from apscheduler.job import Job
+from apscheduler.jobstores.memory import MemoryJobStore
+from apscheduler.schedulers.background import BackgroundScheduler
+from netconf_client.connect import connect_ssh, Session
 from netconf_client.ncclient import Manager
-from common.type_checkers.Checkers import chk_length, chk_string, chk_type
+from common.tools.client.RetryDecorator import delay_exponential
+from common.type_checkers.Checkers import chk_length, chk_string, chk_type, chk_float
 from device.service.driver_api.Exceptions import UnsupportedResourceKeyException
 from device.service.driver_api._Driver import _Driver
-from device.service.driver_api.AnyTreeTools import set_subnode_value
-from device.service.drivers.openconfig.Tools import xml_pretty_print, xml_to_dict, xml_to_file
-from device.service.drivers.openconfig.templates import ALL_RESOURCE_KEYS, compose_config, get_filter, parse
+from device.service.driver_api.AnyTreeTools import TreeNode, get_subnode, set_subnode_value #dump_subtree
+#from .Tools import xml_pretty_print, xml_to_dict, xml_to_file
+from .templates import ALL_RESOURCE_KEYS, EMPTY_CONFIG, compose_config, get_filter, parse
+from .RetryDecorator import retry
 
-#logging.getLogger('ncclient.transport.ssh').setLevel(logging.WARNING)
+DEBUG_MODE = False
+#logging.getLogger('ncclient.transport.ssh').setLevel(logging.DEBUG if DEBUG_MODE else logging.WARNING)
+logging.getLogger('apscheduler.executors.default').setLevel(logging.INFO if DEBUG_MODE else logging.ERROR)
+logging.getLogger('apscheduler.scheduler').setLevel(logging.INFO if DEBUG_MODE else logging.ERROR)
+logging.getLogger('monitoring-client').setLevel(logging.INFO if DEBUG_MODE else logging.ERROR)
 
 LOGGER = logging.getLogger(__name__)
 
-#def do_sampling(resource_key : str, out_samples : queue.Queue):
-#    out_samples.put_nowait((datetime.timestamp(datetime.utcnow()), resource_key, random.random()))
+RE_GET_ENDPOINT_FROM_INTERFACE_KEY = re.compile(r'.*interface\[([^\]]+)\].*')
+RE_GET_ENDPOINT_FROM_INTERFACE_XPATH = re.compile(r".*interface\[oci\:name\='([^\]]+)'\].*")
 
-class OpenConfigDriver(_Driver):
-    def __init__(self, address : str, port : int, **settings) -> None: # pylint: disable=super-init-not-called
+# Collection of samples through NetConf is very slow and each request collects all the data.
+# Populate a cache periodically (when first interface is interrogated).
+# Evict data after some seconds, when data is considered as outdated
+
+SAMPLE_EVICTION_SECONDS = 30.0 # seconds
+SAMPLE_RESOURCE_KEY = 'interfaces/interface/state/counters'
+
+MAX_RETRIES = 15
+DELAY_FUNCTION = delay_exponential(initial=0.01, increment=2.0, maximum=5.0)
+RETRY_DECORATOR = retry(max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+
+class NetconfSessionHandler:
+    def __init__(self, address : str, port : int, **settings) -> None:
+        self.__lock = threading.RLock()
+        self.__connected = threading.Event()
         self.__address = address
         self.__port = int(port)
-        self.__settings = settings
+        self.__username = settings.get('username')
+        self.__password = settings.get('password')
+        self.__timeout = int(settings.get('timeout', 120))
+        self.__netconf_session : Session = None
+        self.__netconf_manager : Manager = None
+
+    def connect(self):
+        with self.__lock:
+            self.__netconf_session = connect_ssh(
+                host=self.__address, port=self.__port, username=self.__username, password=self.__password)
+            self.__netconf_manager = Manager(self.__netconf_session, timeout=self.__timeout)
+            self.__netconf_manager.set_logger_level(logging.DEBUG if DEBUG_MODE else logging.WARNING)
+            self.__connected.set()
+
+    def disconnect(self):
+        if not self.__connected.is_set(): return
+        with self.__lock:
+            self.__netconf_manager.close_session()
+
+    @RETRY_DECORATOR
+    def get(self, filter=None, with_defaults=None): # pylint: disable=redefined-builtin
+        with self.__lock:
+            return self.__netconf_manager.get(filter=filter, with_defaults=with_defaults)
+
+    @RETRY_DECORATOR
+    def edit_config(
+        self, config, target='running', default_operation=None, test_option=None,
+        error_option=None, format='xml'                                             # pylint: disable=redefined-builtin
+    ):
+        if config == EMPTY_CONFIG: return
+        with self.__lock:
+            self.__netconf_manager.edit_config(
+                config, target=target, default_operation=default_operation, test_option=test_option,
+                error_option=error_option, format=format)
+
+def compute_delta_sample(previous_sample, previous_timestamp, current_sample, current_timestamp):
+    if previous_sample is None: return None
+    if previous_timestamp is None: return None
+    if current_sample is None: return None
+    if current_timestamp is None: return None
+    delay = current_timestamp - previous_timestamp
+    field_keys = set(previous_sample.keys()).union(current_sample.keys())
+    field_keys.discard('name')
+    delta_sample = {'name': previous_sample['name']}
+    for field_key in field_keys:
+        previous_sample_value = previous_sample[field_key]
+        if not isinstance(previous_sample_value, (int, float)): continue
+        current_sample_value = current_sample[field_key]
+        if not isinstance(current_sample_value, (int, float)): continue
+        delta_value = current_sample_value - previous_sample_value
+        if delta_value < 0: continue
+        delta_sample[field_key] = delta_value / delay
+    return delta_sample
+
+class SamplesCache:
+    def __init__(self, netconf_handler : NetconfSessionHandler) -> None:
+        self.__netconf_handler = netconf_handler
+        self.__lock = threading.Lock()
+        self.__timestamp = None
+        self.__absolute_samples = {}
+        self.__delta_samples = {}
+
+    def _refresh_samples(self) -> None:
+        with self.__lock:
+            try:
+                now = datetime.timestamp(datetime.utcnow())
+                if self.__timestamp is not None and (now - self.__timestamp) < SAMPLE_EVICTION_SECONDS: return
+                str_filter = get_filter(SAMPLE_RESOURCE_KEY)
+                xml_data = self.__netconf_handler.get(filter=str_filter).data_ele
+                interface_samples = parse(SAMPLE_RESOURCE_KEY, xml_data)
+                for interface,samples in interface_samples:
+                    match = RE_GET_ENDPOINT_FROM_INTERFACE_KEY.match(interface)
+                    if match is None: continue
+                    interface = match.group(1)
+                    delta_sample = compute_delta_sample(
+                        self.__absolute_samples.get(interface), self.__timestamp, samples, now)
+                    if delta_sample is not None: self.__delta_samples[interface] = delta_sample
+                    self.__absolute_samples[interface] = samples
+                self.__timestamp = now
+            except: # pylint: disable=bare-except
+                LOGGER.exception('Error collecting samples')
+
+    def get(self, resource_key : str) -> Tuple[float, Dict]:
+        self._refresh_samples()
+        match = RE_GET_ENDPOINT_FROM_INTERFACE_XPATH.match(resource_key)
+        with self.__lock:
+            if match is None: return self.__timestamp, {}
+            interface = match.group(1)
+            return self.__timestamp, copy.deepcopy(self.__delta_samples.get(interface, {}))
+
+def do_sampling(samples_cache : SamplesCache, resource_key : str, out_samples : queue.Queue) -> None:
+    try:
+        timestamp, samples = samples_cache.get(resource_key)
+        counter_name = resource_key.split('/')[-1].split(':')[-1]
+        value = samples.get(counter_name)
+        if value is None:
+            LOGGER.warning('[do_sampling] value not found for {:s}'.format(resource_key))
+            return
+        sample = (timestamp, resource_key, value)
+        out_samples.put_nowait(sample)
+    except: # pylint: disable=bare-except
+        LOGGER.exception('Error retrieving samples')
+
+class OpenConfigDriver(_Driver):
+    def __init__(self, address : str, port : int, **settings) -> None: # pylint: disable=super-init-not-called
         self.__lock = threading.Lock()
         #self.__initial = TreeNode('.')
         #self.__running = TreeNode('.')
+        self.__subscriptions = TreeNode('.')
         self.__started = threading.Event()
         self.__terminate = threading.Event()
-        self.__netconf_manager : Manager = None
-        #self.__scheduler = BackgroundScheduler(daemon=True) # scheduler used to emulate sampling events
-        #self.__scheduler.configure(
-        #    jobstores = {'default': MemoryJobStore()},
-        #    executors = {'default': ThreadPoolExecutor(max_workers=1)},
-        #    job_defaults = {'coalesce': False, 'max_instances': 3},
-        #    timezone=pytz.utc)
-        #self.__out_samples = queue.Queue()
+        self.__scheduler = BackgroundScheduler(daemon=True) # scheduler used to emulate sampling events
+        self.__scheduler.configure(
+            jobstores = {'default': MemoryJobStore()},
+            executors = {'default': ThreadPoolExecutor(max_workers=1)},
+            job_defaults = {'coalesce': False, 'max_instances': 3},
+            timezone=pytz.utc)
+        self.__out_samples = queue.Queue()
+        self.__netconf_handler : NetconfSessionHandler = NetconfSessionHandler(address, port, **settings)
+        self.__samples_cache = SamplesCache(self.__netconf_handler)
 
     def Connect(self) -> bool:
         with self.__lock:
             if self.__started.is_set(): return True
-            username = self.__settings.get('username')
-            password = self.__settings.get('password')
-            timeout = int(self.__settings.get('timeout', 120))
-            session = connect_ssh(
-                host=self.__address, port=self.__port, username=username, password=password)
-            self.__netconf_manager = Manager(session, timeout=timeout)
-            self.__netconf_manager.set_logger_level(logging.DEBUG)
+            self.__netconf_handler.connect()
             # Connect triggers activation of sampling events that will be scheduled based on subscriptions
-            #self.__scheduler.start()
+            self.__scheduler.start()
             self.__started.set()
             return True
 
@@ -62,8 +196,8 @@ class OpenConfigDriver(_Driver):
             # If not started, assume it is already disconnected
             if not self.__started.is_set(): return True
             # Disconnect triggers deactivation of sampling events
-            #self.__scheduler.shutdown()
-            self.__netconf_manager.close_session()
+            self.__scheduler.shutdown()
+            self.__netconf_handler.disconnect()
             return True
 
     def GetInitialConfig(self) -> List[Tuple[str, Any]]:
@@ -80,8 +214,9 @@ class OpenConfigDriver(_Driver):
                 try:
                     chk_string(str_resource_name, resource_key, allow_empty=False)
                     str_filter = get_filter(resource_key)
+                    LOGGER.info('[GetConfig] str_filter = {:s}'.format(str(str_filter)))
                     if str_filter is None: str_filter = resource_key
-                    xml_data = self.__netconf_manager.get(filter=str_filter).data_ele
+                    xml_data = self.__netconf_handler.get(filter=str_filter).data_ele
                     if isinstance(xml_data, Exception): raise xml_data
                     results.extend(parse(resource_key, xml_data))
                 except Exception as e: # pylint: disable=broad-except
@@ -89,29 +224,25 @@ class OpenConfigDriver(_Driver):
                     results.append((resource_key, e)) # if validation fails, store the exception
         return results
 
-    #def GetResource(self, endpoint_uuid : str) -> Optional[str]:
-    #    chk_string('endpoint_uuid', endpoint_uuid)
-    #    return {
-    #        #'key': 'value',
-    #    }.get(endpoint_uuid)
-
     def SetConfig(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]:
         chk_type('resources', resources, list)
         if len(resources) == 0: return []
         results = []
+        LOGGER.info('[SetConfig] resources = {:s}'.format(str(resources)))
         with self.__lock:
             for i,resource in enumerate(resources):
                 str_resource_name = 'resources[#{:d}]'.format(i)
                 try:
-                    #LOGGER.info('resource = {:s}'.format(str(resource)))
+                    LOGGER.info('[SetConfig] resource = {:s}'.format(str(resource)))
                     chk_type(str_resource_name, resource, (list, tuple))
                     chk_length(str_resource_name, resource, min_length=2, max_length=2)
                     resource_key,resource_value = resource
                     chk_string(str_resource_name + '.key', resource_key, allow_empty=False)
                     str_config_message = compose_config(resource_key, resource_value)
                     if str_config_message is None: raise UnsupportedResourceKeyException(resource_key)
-                    #LOGGER.info('str_config_message = {:s}'.format(str(str_config_message)))
-                    self.__netconf_manager.edit_config(str_config_message, target='running')
+                    LOGGER.info('[SetConfig] str_config_message[{:d}] = {:s}'.format(
+                        len(str_config_message), str(str_config_message)))
+                    self.__netconf_handler.edit_config(str_config_message, target='running')
                     results.append(True)
                 except Exception as e: # pylint: disable=broad-except
                     LOGGER.exception('Exception setting {:s}: {:s}'.format(str_resource_name, str(resource)))
@@ -122,110 +253,114 @@ class OpenConfigDriver(_Driver):
         chk_type('resources', resources, list)
         if len(resources) == 0: return []
         results = []
+        LOGGER.info('[DeleteConfig] resources = {:s}'.format(str(resources)))
         with self.__lock:
             for i,resource in enumerate(resources):
                 str_resource_name = 'resources[#{:d}]'.format(i)
                 try:
-                    #LOGGER.info('resource = {:s}'.format(str(resource)))
+                    LOGGER.info('[DeleteConfig] resource = {:s}'.format(str(resource)))
                     chk_type(str_resource_name, resource, (list, tuple))
                     chk_length(str_resource_name, resource, min_length=2, max_length=2)
                     resource_key,resource_value = resource
                     chk_string(str_resource_name + '.key', resource_key, allow_empty=False)
                     str_config_message = compose_config(resource_key, resource_value, delete=True)
                     if str_config_message is None: raise UnsupportedResourceKeyException(resource_key)
-                    #LOGGER.info('str_config_message = {:s}'.format(str(str_config_message)))
-                    self.__netconf_manager.edit_config(str_config_message, target='running')
+                    LOGGER.info('[DeleteConfig] str_config_message[{:d}] = {:s}'.format(
+                        len(str_config_message), str(str_config_message)))
+                    self.__netconf_handler.edit_config(str_config_message, target='running')
                     results.append(True)
                 except Exception as e: # pylint: disable=broad-except
                     LOGGER.exception('Exception deleting {:s}: {:s}'.format(str_resource_name, str(resource_key)))
                     results.append(e) # if validation fails, store the exception
         return results
 
-#    def SubscribeState(self, subscriptions : List[Tuple[str, float, float]]) -> List[Union[bool, Exception]]:
-#        chk_type('subscriptions', subscriptions, list)
-#        if len(subscriptions) == 0: return []
-#        results = []
-#        resolver = anytree.Resolver(pathattr='name')
-#        with self.__lock:
-#            for i,subscription in enumerate(subscriptions):
-#                str_subscription_name = 'subscriptions[#{:d}]'.format(i)
-#                try:
-#                    chk_type(str_subscription_name, subscription, (list, tuple))
-#                    chk_length(str_subscription_name, subscription, min_length=3, max_length=3)
-#                    resource_key,sampling_duration,sampling_interval = subscription
-#                    chk_string(str_subscription_name + '.resource_key', resource_key, allow_empty=False)
-#                    resource_path = resource_key.split('/')
-#                    chk_float(str_subscription_name + '.sampling_duration', sampling_duration, min_value=0)
-#                    chk_float(str_subscription_name + '.sampling_interval', sampling_interval, min_value=0)
-#                except Exception as e: # pylint: disable=broad-except
-#                    LOGGER.exception('Exception validating {:s}: {:s}'.format(str_subscription_name, str(resource_key)))
-#                    results.append(e) # if validation fails, store the exception
-#                    continue
-#
-#                start_date,end_date = None,None
-#                if sampling_duration <= 1.e-12:
-#                    start_date = datetime.utcnow()
-#                    end_date = start_date + timedelta(seconds=sampling_duration)
-#
-#                job_id = 'k={:s}/d={:f}/i={:f}'.format(resource_key, sampling_duration, sampling_interval)
-#                job = self.__scheduler.add_job(
-#                    do_sampling, args=(resource_key, self.__out_samples), kwargs={},
-#                    id=job_id, trigger='interval', seconds=sampling_interval,
-#                    start_date=start_date, end_date=end_date, timezone=pytz.utc)
-#
-#                subscription_path = resource_path + ['{:.3f}:{:.3f}'.format(sampling_duration, sampling_interval)]
-#                set_subnode_value(resolver, self.__running, subscription_path, job)
-#                results.append(True)
-#        return results
-#
-#    def UnsubscribeState(self, subscriptions : List[Tuple[str, float, float]]) -> List[Union[bool, Exception]]:
-#        chk_type('subscriptions', subscriptions, list)
-#        if len(subscriptions) == 0: return []
-#        results = []
-#        resolver = anytree.Resolver(pathattr='name')
-#        with self.__lock:
-#            for i,resource in enumerate(subscriptions):
-#                str_subscription_name = 'resources[#{:d}]'.format(i)
-#                try:
-#                    chk_type(str_subscription_name, resource, (list, tuple))
-#                    chk_length(str_subscription_name, resource, min_length=3, max_length=3)
-#                    resource_key,sampling_duration,sampling_interval = resource
-#                    chk_string(str_subscription_name + '.resource_key', resource_key, allow_empty=False)
-#                    resource_path = resource_key.split('/')
-#                    chk_float(str_subscription_name + '.sampling_duration', sampling_duration, min_value=0)
-#                    chk_float(str_subscription_name + '.sampling_interval', sampling_interval, min_value=0)
-#                except Exception as e: # pylint: disable=broad-except
-#                    LOGGER.exception('Exception validating {:s}: {:s}'.format(str_subscription_name, str(resource_key)))
-#                    results.append(e) # if validation fails, store the exception
-#                    continue
-#
-#                subscription_path = resource_path + ['{:.3f}:{:.3f}'.format(sampling_duration, sampling_interval)]
-#                subscription_node = get_subnode(resolver, self.__running, subscription_path)
-#
-#                # if not found, resource_node is None
-#                if subscription_node is None:
-#                    results.append(False)
-#                    continue
-#
-#                job : Job = getattr(subscription_node, 'value', None)
-#                if job is None or not isinstance(job, Job):
-#                    raise Exception('Malformed subscription node or wrong resource key: {:s}'.format(str(resource)))
-#                job.remove()
-#
-#                parent = subscription_node.parent
-#                children = list(parent.children)
-#                children.remove(subscription_node)
-#                parent.children = tuple(children)
-#
-#                results.append(True)
-#        return results
-#
-#    def GetState(self, blocking=False) -> Iterator[Tuple[str, Any]]:
-#        while not self.__terminate.is_set():
-#            try:
-#                sample = self.__out_samples.get(block=blocking, timeout=0.1)
-#            except queue.Empty:
-#                if blocking: continue
-#                return
-#            if sample is None: continue
-#            yield sample
+    def SubscribeState(self, subscriptions : List[Tuple[str, float, float]]) -> List[Union[bool, Exception]]:
+        chk_type('subscriptions', subscriptions, list)
+        if len(subscriptions) == 0: return []
+        results = []
+        resolver = anytree.Resolver(pathattr='name')
+        with self.__lock:
+            for i,subscription in enumerate(subscriptions):
+                str_subscription_name = 'subscriptions[#{:d}]'.format(i)
+                try:
+                    chk_type(str_subscription_name, subscription, (list, tuple))
+                    chk_length(str_subscription_name, subscription, min_length=3, max_length=3)
+                    resource_key,sampling_duration,sampling_interval = subscription
+                    chk_string(str_subscription_name + '.resource_key', resource_key, allow_empty=False)
+                    resource_path = resource_key.split('/')
+                    chk_float(str_subscription_name + '.sampling_duration', sampling_duration, min_value=0)
+                    chk_float(str_subscription_name + '.sampling_interval', sampling_interval, min_value=0)
+                except Exception as e: # pylint: disable=broad-except
+                    LOGGER.exception('Exception validating {:s}: {:s}'.format(str_subscription_name, str(resource_key)))
+                    results.append(e) # if validation fails, store the exception
+                    continue
+
+                start_date,end_date = None,None
+                if sampling_duration >= 1.e-12:
+                    start_date = datetime.utcnow()
+                    end_date = start_date + timedelta(seconds=sampling_duration)
+
+                job_id = 'k={:s}/d={:f}/i={:f}'.format(resource_key, sampling_duration, sampling_interval)
+                job = self.__scheduler.add_job(
+                    do_sampling, args=(self.__samples_cache, resource_key, self.__out_samples),
+                    kwargs={}, id=job_id, trigger='interval', seconds=sampling_interval,
+                    start_date=start_date, end_date=end_date, timezone=pytz.utc)
+
+                subscription_path = resource_path + ['{:.3f}:{:.3f}'.format(sampling_duration, sampling_interval)]
+                set_subnode_value(resolver, self.__subscriptions, subscription_path, job)
+                results.append(True)
+        return results
+
+    def UnsubscribeState(self, subscriptions : List[Tuple[str, float, float]]) -> List[Union[bool, Exception]]:
+        chk_type('subscriptions', subscriptions, list)
+        if len(subscriptions) == 0: return []
+        results = []
+        resolver = anytree.Resolver(pathattr='name')
+        with self.__lock:
+            for i,resource in enumerate(subscriptions):
+                str_subscription_name = 'resources[#{:d}]'.format(i)
+                try:
+                    chk_type(str_subscription_name, resource, (list, tuple))
+                    chk_length(str_subscription_name, resource, min_length=3, max_length=3)
+                    resource_key,sampling_duration,sampling_interval = resource
+                    chk_string(str_subscription_name + '.resource_key', resource_key, allow_empty=False)
+                    resource_path = resource_key.split('/')
+                    chk_float(str_subscription_name + '.sampling_duration', sampling_duration, min_value=0)
+                    chk_float(str_subscription_name + '.sampling_interval', sampling_interval, min_value=0)
+                except Exception as e: # pylint: disable=broad-except
+                    LOGGER.exception('Exception validating {:s}: {:s}'.format(str_subscription_name, str(resource_key)))
+                    results.append(e) # if validation fails, store the exception
+                    continue
+
+                subscription_path = resource_path + ['{:.3f}:{:.3f}'.format(sampling_duration, sampling_interval)]
+                subscription_node = get_subnode(resolver, self.__subscriptions, subscription_path)
+
+                # if not found, resource_node is None
+                if subscription_node is None:
+                    results.append(False)
+                    continue
+
+                job : Job = getattr(subscription_node, 'value', None)
+                if job is None or not isinstance(job, Job):
+                    raise Exception('Malformed subscription node or wrong resource key: {:s}'.format(str(resource)))
+                job.remove()
+
+                parent = subscription_node.parent
+                children = list(parent.children)
+                children.remove(subscription_node)
+                parent.children = tuple(children)
+
+                results.append(True)
+        return results
+
+    def GetState(self, blocking=False, terminate : Optional[threading.Event] = None) -> Iterator[Tuple[str, Any]]:
+        while True:
+            if self.__terminate.is_set(): break
+            if terminate is not None and terminate.is_set(): break
+            try:
+                sample = self.__out_samples.get(block=blocking, timeout=0.1)
+            except queue.Empty:
+                if blocking: continue
+                return
+            if sample is None: continue
+            yield sample
diff --git a/src/device/service/drivers/openconfig/RetryDecorator.py b/src/device/service/drivers/openconfig/RetryDecorator.py
new file mode 100644
index 0000000000000000000000000000000000000000..d8ccddb4d09dd8863cedc2893fb3c6ec4e0491cd
--- /dev/null
+++ b/src/device/service/drivers/openconfig/RetryDecorator.py
@@ -0,0 +1,46 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging, time
+from common.tools.client.RetryDecorator import delay_linear
+
+LOGGER = logging.getLogger(__name__)
+
+def retry(max_retries=0, delay_function=delay_linear(initial=0, increment=0),
+          prepare_method_name=None, prepare_method_args=[], prepare_method_kwargs={}):
+    def _reconnect(func):
+        def wrapper(self, *args, **kwargs):
+            if prepare_method_name is not None:
+                prepare_method = getattr(self, prepare_method_name, None)
+                if prepare_method is None: raise Exception('Prepare Method ({}) not found'.format(prepare_method_name))
+            num_try, given_up = 0, False
+            while not given_up:
+                try:
+                    return func(self, *args, **kwargs)
+                except OSError as e:
+                    if str(e) != 'Socket is closed': raise
+
+                    num_try += 1
+                    given_up = num_try > max_retries
+                    if given_up: raise Exception('Giving up... {:d} tries failed'.format(max_retries)) from e
+                    if delay_function is not None:
+                        delay = delay_function(num_try)
+                        time.sleep(delay)
+                        LOGGER.info('Retry {:d}/{:d} after {:f} seconds...'.format(num_try, max_retries, delay))
+                    else:
+                        LOGGER.info('Retry {:d}/{:d} immediate...'.format(num_try, max_retries))
+
+                    if prepare_method_name is not None: prepare_method(*prepare_method_args, **prepare_method_kwargs)
+        return wrapper
+    return _reconnect
diff --git a/src/device/service/drivers/openconfig/Tools.py b/src/device/service/drivers/openconfig/Tools.py
index 230a6e3c33586c5d1771a6da2fd9dabc533e9538..73d46145cd159257af086ad9498261c9accf97b3 100644
--- a/src/device/service/drivers/openconfig/Tools.py
+++ b/src/device/service/drivers/openconfig/Tools.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import xml.dom.minidom, xmltodict
 
 def xml_pretty_print(data : str):
diff --git a/src/device/service/drivers/openconfig/__init__.py b/src/device/service/drivers/openconfig/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/device/service/drivers/openconfig/__init__.py
+++ b/src/device/service/drivers/openconfig/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/device/service/drivers/openconfig/templates/EndPoints.py b/src/device/service/drivers/openconfig/templates/EndPoints.py
index a7d5c9be6ce9dd53d3b31998b4d4a269c972eb22..c11b1669d5b4cf3ca47986817ded28f75ae8358f 100644
--- a/src/device/service/drivers/openconfig/templates/EndPoints.py
+++ b/src/device/service/drivers/openconfig/templates/EndPoints.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging, lxml.etree as ET
 from typing import Any, Dict, List, Tuple
 from device.service.database.KpiSampleType import ORM_KpiSampleTypeEnum
@@ -33,5 +47,5 @@ def parse(xml_data : ET.Element) -> List[Tuple[str, Dict[str, Any]]]:
         add_value_from_collection(endpoint, 'sample_types', sample_types)
 
         if len(endpoint) == 0: continue
-        response.append(('endpoint[{:s}]'.format(endpoint['uuid']), endpoint))
+        response.append(('/endpoint[{:s}]'.format(endpoint['uuid']), endpoint))
     return response
diff --git a/src/device/service/drivers/openconfig/templates/Interfaces.py b/src/device/service/drivers/openconfig/templates/Interfaces.py
index 0bbd70d04a3c093dcdbad4966be3030dbc8ba0fb..33f977524c6f65655fbe17f6d2d95a7cfc223967 100644
--- a/src/device/service/drivers/openconfig/templates/Interfaces.py
+++ b/src/device/service/drivers/openconfig/templates/Interfaces.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging, lxml.etree as ET
 from typing import Any, Dict, List, Tuple
 from .Namespace import NAMESPACES
@@ -12,7 +26,7 @@ XPATH_IPV4ADDRESSES = ".//ociip:ipv4/ociip:addresses/ociip:address"
 def parse(xml_data : ET.Element) -> List[Tuple[str, Dict[str, Any]]]:
     response = []
     for xml_interface in xml_data.xpath(XPATH_INTERFACES, namespaces=NAMESPACES):
-        LOGGER.info('xml_interface = {:s}'.format(str(ET.tostring(xml_interface))))
+        #LOGGER.info('xml_interface = {:s}'.format(str(ET.tostring(xml_interface))))
 
         interface = {}
 
@@ -20,46 +34,96 @@ def parse(xml_data : ET.Element) -> List[Tuple[str, Dict[str, Any]]]:
         if interface_name is None or interface_name.text is None: continue
         add_value_from_tag(interface, 'name', interface_name)
 
-        interface_type = xml_interface.find('oci:config/oci:type', namespaces=NAMESPACES)
-        add_value_from_tag(interface, 'type', interface_type)
+        #interface_type = xml_interface.find('oci:config/oci:type', namespaces=NAMESPACES)
+        #add_value_from_tag(interface, 'type', interface_type)
+
+        interface_mtu = xml_interface.find('oci:config/oci:mtu', namespaces=NAMESPACES)
+        add_value_from_tag(interface, 'mtu', interface_mtu, cast=int)
+
+        interface_description = xml_interface.find('oci:config/oci:description', namespaces=NAMESPACES)
+        add_value_from_tag(interface, 'description', interface_description)
 
         for xml_subinterface in xml_interface.xpath(XPATH_SUBINTERFACES, namespaces=NAMESPACES):
-            LOGGER.info('xml_subinterface = {:s}'.format(str(ET.tostring(xml_subinterface))))
+            #LOGGER.info('xml_subinterface = {:s}'.format(str(ET.tostring(xml_subinterface))))
 
             subinterface = {}
 
+            add_value_from_tag(subinterface, 'name', interface_name)
+
             subinterface_index = xml_subinterface.find('oci:index', namespaces=NAMESPACES)
             if subinterface_index is None or subinterface_index.text is None: continue
-            add_value_from_tag(subinterface, 'index', subinterface_index)
+            add_value_from_tag(subinterface, 'index', subinterface_index, cast=int)
 
             vlan_id = xml_subinterface.find('ocv:vlan/ocv:config/ocv:vlan-id', namespaces=NAMESPACES)
             add_value_from_tag(subinterface, 'vlan_id', vlan_id, cast=int)
 
-            ipv4_addresses = []
+            # TODO: implement support for multiple IP addresses per subinterface
+            #ipv4_addresses = []
             for xml_ipv4_address in xml_subinterface.xpath(XPATH_IPV4ADDRESSES, namespaces=NAMESPACES):
-                LOGGER.info('xml_ipv4_address = {:s}'.format(str(ET.tostring(xml_ipv4_address))))
+                #LOGGER.info('xml_ipv4_address = {:s}'.format(str(ET.tostring(xml_ipv4_address))))
 
-                ipv4_address = {}
+                #ipv4_address = {}
 
-                origin = xml_ipv4_address.find('ociip:state/ociip:origin', namespaces=NAMESPACES)
-                add_value_from_tag(ipv4_address, 'origin', origin)
+                #origin = xml_ipv4_address.find('ociip:state/ociip:origin', namespaces=NAMESPACES)
+                #add_value_from_tag(ipv4_address, 'origin', origin)
 
                 address = xml_ipv4_address.find('ociip:state/ociip:ip', namespaces=NAMESPACES)
-                add_value_from_tag(ipv4_address, 'ip', address)
+                #add_value_from_tag(ipv4_address, 'ip', address)
+                add_value_from_tag(subinterface, 'address_ip', address)
 
                 prefix = xml_ipv4_address.find('ociip:state/ociip:prefix-length', namespaces=NAMESPACES)
-                add_value_from_tag(ipv4_address, 'prefix_length', prefix)
+                #add_value_from_tag(ipv4_address, 'prefix_length', prefix)
+                add_value_from_tag(subinterface, 'address_prefix', prefix, cast=int)
 
-                if len(ipv4_address) == 0: continue
-                ipv4_addresses.append(ipv4_address)
+                #if len(ipv4_address) == 0: continue
+                #ipv4_addresses.append(ipv4_address)
 
-            add_value_from_collection(subinterface, 'ipv4_addresses', ipv4_addresses)
+            #add_value_from_collection(subinterface, 'ipv4_addresses', ipv4_addresses)
 
             if len(subinterface) == 0: continue
-            resource_key = 'interface[{:s}]/subinterface[{:s}]'.format(interface['name'], subinterface['index'])
+            resource_key = '/interface[{:s}]/subinterface[{:s}]'.format(interface['name'], str(subinterface['index']))
             response.append((resource_key, subinterface))
 
         if len(interface) == 0: continue
-        response.append(('interface[{:s}]'.format(interface['name']), interface))
+        response.append(('/interface[{:s}]'.format(interface['name']), interface))
+
+    return response
+
+def parse_counters(xml_data : ET.Element) -> List[Tuple[str, Dict[str, Any]]]:
+    response = []
+    for xml_interface in xml_data.xpath(XPATH_INTERFACES, namespaces=NAMESPACES):
+        #LOGGER.info('[parse_counters] xml_interface = {:s}'.format(str(ET.tostring(xml_interface))))
+
+        interface = {}
+
+        interface_name = xml_interface.find('oci:name', namespaces=NAMESPACES)
+        if interface_name is None or interface_name.text is None: continue
+        add_value_from_tag(interface, 'name', interface_name)
+
+        interface_in_pkts = xml_interface.find('oci:state/oci:counters/oci:in-pkts', namespaces=NAMESPACES)
+        add_value_from_tag(interface, 'in-pkts', interface_in_pkts, cast=int)
+
+        interface_in_octets = xml_interface.find('oci:state/oci:counters/oci:in-octets', namespaces=NAMESPACES)
+        add_value_from_tag(interface, 'in-octets', interface_in_octets, cast=int)
+
+        interface_in_errors = xml_interface.find('oci:state/oci:counters/oci:in-errors', namespaces=NAMESPACES)
+        add_value_from_tag(interface, 'in-errors', interface_in_errors, cast=int)
+
+        interface_out_octets = xml_interface.find('oci:state/oci:counters/oci:out-octets', namespaces=NAMESPACES)
+        add_value_from_tag(interface, 'out-octets', interface_out_octets, cast=int)
+
+        interface_out_pkts = xml_interface.find('oci:state/oci:counters/oci:out-pkts', namespaces=NAMESPACES)
+        add_value_from_tag(interface, 'out-pkts', interface_out_pkts, cast=int)
+
+        interface_out_errors = xml_interface.find('oci:state/oci:counters/oci:out-errors', namespaces=NAMESPACES)
+        add_value_from_tag(interface, 'out-errors', interface_out_errors, cast=int)
+
+        interface_out_discards = xml_interface.find('oci:state/oci:counters/oci:out-discards', namespaces=NAMESPACES)
+        add_value_from_tag(interface, 'out-discards', interface_out_discards, cast=int)
+
+        #LOGGER.info('[parse_counters] interface = {:s}'.format(str(interface)))
+
+        if len(interface) == 0: continue
+        response.append(('/interface[{:s}]'.format(interface['name']), interface))
 
     return response
diff --git a/src/device/service/drivers/openconfig/templates/Namespace.py b/src/device/service/drivers/openconfig/templates/Namespace.py
index ff4b7c9c747b4efa872ba177e6e6a4fd7f6203b4..35be5827db892541847a3c02af42e2fd08ee0e1d 100644
--- a/src/device/service/drivers/openconfig/templates/Namespace.py
+++ b/src/device/service/drivers/openconfig/templates/Namespace.py
@@ -1,6 +1,21 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 
 NAMESPACE_NETCONF       = 'urn:ietf:params:xml:ns:netconf:base:1.0'
 
+NAMESPACE_BGP_POLICY             = 'http://openconfig.net/yang/bgp-policy'
 NAMESPACE_INTERFACES             = 'http://openconfig.net/yang/interfaces'
 NAMESPACE_INTERFACES_IP          = 'http://openconfig.net/yang/interfaces/ip'
 NAMESPACE_NETWORK_INSTANCE       = 'http://openconfig.net/yang/network-instance'
@@ -8,10 +23,14 @@ NAMESPACE_NETWORK_INSTANCE_TYPES = 'http://openconfig.net/yang/network-instance-
 NAMESPACE_OPENCONFIG_TYPES       = 'http://openconfig.net/yang/openconfig-types'
 NAMESPACE_PLATFORM               = 'http://openconfig.net/yang/platform'
 NAMESPACE_PLATFORM_PORT          = 'http://openconfig.net/yang/platform/port'
+NAMESPACE_POLICY_TYPES           = 'http://openconfig.net/yang/policy-types'
+NAMESPACE_POLICY_TYPES_2         = 'http://openconfig.net/yang/policy_types'
+NAMESPACE_ROUTING_POLICY         = 'http://openconfig.net/yang/routing-policy'
 NAMESPACE_VLAN                   = 'http://openconfig.net/yang/vlan'
 
 NAMESPACES = {
     'nc'   : NAMESPACE_NETCONF,
+    'ocbp' : NAMESPACE_BGP_POLICY,
     'oci'  : NAMESPACE_INTERFACES,
     'ociip': NAMESPACE_INTERFACES_IP,
     'ocni' : NAMESPACE_NETWORK_INSTANCE,
@@ -19,5 +38,8 @@ NAMESPACES = {
     'ococt': NAMESPACE_OPENCONFIG_TYPES,
     'ocp'  : NAMESPACE_PLATFORM,
     'ocpp' : NAMESPACE_PLATFORM_PORT,
+    'ocpt' : NAMESPACE_POLICY_TYPES,
+    'ocpt2': NAMESPACE_POLICY_TYPES_2,
+    'ocrp' : NAMESPACE_ROUTING_POLICY,
     'ocv'  : NAMESPACE_VLAN,
 }
diff --git a/src/device/service/drivers/openconfig/templates/NetworkInstances.py b/src/device/service/drivers/openconfig/templates/NetworkInstances.py
index 06bf5cd0f38aaf8c61f2919c25891e15f9ac4343..b091a0d206195a6c2ce94008628071cd9e30944f 100644
--- a/src/device/service/drivers/openconfig/templates/NetworkInstances.py
+++ b/src/device/service/drivers/openconfig/templates/NetworkInstances.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging, lxml.etree as ET
 from typing import Any, Dict, List, Tuple
 from .Namespace import NAMESPACES
@@ -6,6 +20,12 @@ from .Tools import add_value_from_collection, add_value_from_tag
 LOGGER = logging.getLogger(__name__)
 
 XPATH_NETWORK_INSTANCES = "//ocni:network-instances/ocni:network-instance"
+XPATH_NI_PROTOCOLS      = ".//ocni:protocols/ocni:protocol"
+XPATH_NI_TABLE_CONNECTS = ".//ocni:table-connections/ocni:table-connection"
+
+XPATH_NI_IIP_AP         = ".//ocni:inter-instance-policies/ocni:apply-policy"
+XPATH_NI_IIP_AP_IMPORT  = ".//ocni:config/ocni:import-policy"
+XPATH_NI_IIP_AP_EXPORT  = ".//ocni:config/ocni:export-policy"
 
 def parse(xml_data : ET.Element) -> List[Tuple[str, Dict[str, Any]]]:
     response = []
@@ -21,15 +41,88 @@ def parse(xml_data : ET.Element) -> List[Tuple[str, Dict[str, Any]]]:
         ni_type = xml_network_instance.find('ocni:config/ocni:type', namespaces=NAMESPACES)
         add_value_from_tag(network_instance, 'type', ni_type)
 
-        ni_router_id = xml_network_instance.find('ocni:config/ocni:router-id', namespaces=NAMESPACES)
-        add_value_from_tag(network_instance, 'router_id', ni_router_id)
+        #ni_router_id = xml_network_instance.find('ocni:config/ocni:router-id', namespaces=NAMESPACES)
+        #add_value_from_tag(network_instance, 'router_id', ni_router_id)
 
         ni_route_dist = xml_network_instance.find('ocni:config/ocni:route-distinguisher', namespaces=NAMESPACES)
         add_value_from_tag(network_instance, 'route_distinguisher', ni_route_dist)
 
-        ni_address_families = []
-        add_value_from_collection(network_instance, 'address_families', ni_address_families)
+        #ni_address_families = []
+        #add_value_from_collection(network_instance, 'address_families', ni_address_families)
 
         if len(network_instance) == 0: continue
-        response.append(('network_instance[{:s}]'.format(network_instance['name']), network_instance))
+        response.append(('/network_instance[{:s}]'.format(network_instance['name']), network_instance))
+
+        for xml_protocol in xml_network_instance.xpath(XPATH_NI_PROTOCOLS, namespaces=NAMESPACES):
+            #LOGGER.info('xml_protocol = {:s}'.format(str(ET.tostring(xml_protocol))))
+
+            protocol = {}
+            add_value_from_tag(protocol, 'name', ni_name)
+
+            identifier = xml_protocol.find('ocni:identifier', namespaces=NAMESPACES)
+            if identifier is None: identifier = xml_protocol.find('ocpt:identifier', namespaces=NAMESPACES)
+            if identifier is None: identifier = xml_protocol.find('ocpt2:identifier', namespaces=NAMESPACES)
+            if identifier is None or identifier.text is None: continue
+            add_value_from_tag(protocol, 'identifier', identifier, cast=lambda s: s.replace('oc-pol-types:', ''))
+
+            name = xml_protocol.find('ocni:name', namespaces=NAMESPACES)
+            add_value_from_tag(protocol, 'protocol_name', name)
+
+            if protocol['identifier'] == 'BGP':
+                bgp_as = xml_protocol.find('ocni:bgp/ocni:global/ocni:config/ocni:as', namespaces=NAMESPACES)
+                add_value_from_tag(protocol, 'as', bgp_as, cast=int)
+
+            resource_key = '/network_instance[{:s}]/protocols[{:s}]'.format(
+                network_instance['name'], protocol['identifier'])
+            response.append((resource_key, protocol))
+
+        for xml_table_connection in xml_network_instance.xpath(XPATH_NI_TABLE_CONNECTS, namespaces=NAMESPACES):
+            #LOGGER.info('xml_table_connection = {:s}'.format(str(ET.tostring(xml_table_connection))))
+
+            table_connection = {}
+            add_value_from_tag(table_connection, 'name', ni_name)
+
+            src_protocol = xml_table_connection.find('ocni:src-protocol', namespaces=NAMESPACES)
+            add_value_from_tag(table_connection, 'src_protocol', src_protocol,
+                               cast=lambda s: s.replace('oc-pol-types:', ''))
+
+            dst_protocol = xml_table_connection.find('ocni:dst-protocol', namespaces=NAMESPACES)
+            add_value_from_tag(table_connection, 'dst_protocol', dst_protocol,
+                               cast=lambda s: s.replace('oc-pol-types:', ''))
+
+            address_family = xml_table_connection.find('ocni:address-family', namespaces=NAMESPACES)
+            add_value_from_tag(table_connection, 'address_family', address_family,
+                               cast=lambda s: s.replace('oc-types:', ''))
+
+            default_import_policy = xml_table_connection.find('ocni:default-import-policy', namespaces=NAMESPACES)
+            add_value_from_tag(table_connection, 'default_import_policy', default_import_policy)
+
+            resource_key = '/network_instance[{:s}]/table_connections[{:s}][{:s}][{:s}]'.format(
+                network_instance['name'], table_connection['src_protocol'], table_connection['dst_protocol'],
+                table_connection['address_family'])
+            response.append((resource_key, table_connection))
+
+        for xml_iip_ap in xml_network_instance.xpath(XPATH_NI_IIP_AP, namespaces=NAMESPACES):
+            #LOGGER.info('xml_iip_ap = {:s}'.format(str(ET.tostring(xml_iip_ap))))
+
+            for xml_import_policy in xml_iip_ap.xpath(XPATH_NI_IIP_AP_IMPORT, namespaces=NAMESPACES):
+                #LOGGER.info('xml_import_policy = {:s}'.format(str(ET.tostring(xml_import_policy))))
+                if xml_import_policy.text is None: continue
+                iip_ap = {}
+                add_value_from_tag(iip_ap, 'name', ni_name)
+                add_value_from_tag(iip_ap, 'import_policy', xml_import_policy)
+                resource_key = '/network_instance[{:s}]/inter_instance_policies[{:s}]'.format(
+                    iip_ap['name'], iip_ap['import_policy'])
+                response.append((resource_key, iip_ap))
+
+            for xml_export_policy in xml_iip_ap.xpath(XPATH_NI_IIP_AP_EXPORT, namespaces=NAMESPACES):
+                #LOGGER.info('xml_export_policy = {:s}'.format(str(ET.tostring(xml_export_policy))))
+                if xml_export_policy.text is None: continue
+                iip_ap = {}
+                add_value_from_tag(iip_ap, 'name', ni_name)
+                add_value_from_tag(iip_ap, 'export_policy', xml_export_policy)
+                resource_key = '/network_instance[{:s}]/inter_instance_policies[{:s}]'.format(
+                    iip_ap['name'], iip_ap['export_policy'])
+                response.append((resource_key, iip_ap))
+
     return response
diff --git a/src/device/service/drivers/openconfig/templates/RoutingPolicy.py b/src/device/service/drivers/openconfig/templates/RoutingPolicy.py
new file mode 100644
index 0000000000000000000000000000000000000000..aae8483706646801dccf6d3018eb9860209bf52b
--- /dev/null
+++ b/src/device/service/drivers/openconfig/templates/RoutingPolicy.py
@@ -0,0 +1,85 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import copy, logging, lxml.etree as ET
+from typing import Any, Dict, List, Tuple
+from .Namespace import NAMESPACES
+from .Tools import add_value_from_collection, add_value_from_tag
+
+LOGGER = logging.getLogger(__name__)
+
+XPATH_POLICY_DEFINITIONS = "//ocrp:routing-policy/ocrp:policy-definitions/ocrp:policy-definition"
+XPATH_PD_STATEMENTS      = ".//ocrp:statements/ocrp:statement"
+XPATH_PD_ST_CONDITIONS   = ".//ocrp:conditions/ocbp:bgp-conditions/ocbp:match-ext-community-set"
+XPATH_PD_ST_ACTIONS      = ".//ocrp:actions"
+
+XPATH_BGP_EXT_COMMUN_SET = "//ocrp:routing-policy/ocrp:defined-sets/ocbp:bgp-defined-sets/" + \
+                           "ocbp:ext-community-sets/ocbp:ext-community-set"
+
+def parse(xml_data : ET.Element) -> List[Tuple[str, Dict[str, Any]]]:
+    #LOGGER.info('[RoutePolicy] xml_data = {:s}'.format(str(ET.tostring(xml_data))))
+
+    response = []
+    for xml_policy_definition in xml_data.xpath(XPATH_POLICY_DEFINITIONS, namespaces=NAMESPACES):
+        #LOGGER.info('xml_policy_definition = {:s}'.format(str(ET.tostring(xml_policy_definition))))
+
+        policy_definition = {}
+
+        policy_name = xml_policy_definition.find('ocrp:name', namespaces=NAMESPACES)
+        if policy_name is None or policy_name.text is None: continue
+        add_value_from_tag(policy_definition, 'policy_name', policy_name)
+
+        resource_key = '/routing_policy/policy_definition[{:s}]'.format(policy_definition['policy_name'])
+        response.append((resource_key, copy.deepcopy(policy_definition)))
+
+        for xml_statement in xml_policy_definition.xpath(XPATH_PD_STATEMENTS, namespaces=NAMESPACES):
+            statement_name = xml_statement.find('ocrp:name', namespaces=NAMESPACES)
+            add_value_from_tag(policy_definition, 'statement_name', statement_name)
+
+            for xml_condition in xml_statement.xpath(XPATH_PD_ST_CONDITIONS, namespaces=NAMESPACES):
+                ext_community_set_name = xml_condition.find('ocbp:config/ocbp:ext-community-set', namespaces=NAMESPACES)
+                add_value_from_tag(policy_definition, 'ext_community_set_name', ext_community_set_name)
+
+                match_set_options = xml_condition.find('ocbp:config/ocbp:match-set-options', namespaces=NAMESPACES)
+                add_value_from_tag(policy_definition, 'match_set_options', match_set_options)
+
+            for xml_action in xml_statement.xpath(XPATH_PD_ST_ACTIONS, namespaces=NAMESPACES):
+                policy_result = xml_action.find('ocbp:config/ocbp:policy-result', namespaces=NAMESPACES)
+                add_value_from_tag(policy_definition, 'policy_result', policy_result)
+
+        resource_key = '/routing_policy/policy_definition[{:s}]/statement[{:s}]'.format(
+            policy_definition['policy_name'], policy_definition['statement_name'])
+        response.append((resource_key, copy.deepcopy(policy_definition)))
+
+    for xml_bgp_ext_community_set in xml_data.xpath(XPATH_BGP_EXT_COMMUN_SET, namespaces=NAMESPACES):
+        #LOGGER.info('xml_bgp_ext_community_set = {:s}'.format(str(ET.tostring(xml_bgp_ext_community_set))))
+
+        bgp_ext_community_set = {}
+
+        ext_community_set_name = xml_bgp_ext_community_set.find('ocbp:ext-community-set-name', namespaces=NAMESPACES)
+        if ext_community_set_name is None or ext_community_set_name.text is None: continue
+        add_value_from_tag(bgp_ext_community_set, 'ext_community_set_name', ext_community_set_name)
+
+        resource_key = '/routing_policy/bgp_defined_set[{:s}]'.format(bgp_ext_community_set['ext_community_set_name'])
+        response.append((resource_key, copy.deepcopy(bgp_ext_community_set)))
+
+        ext_community_member = xml_bgp_ext_community_set.find('ocbp:ext-community-member', namespaces=NAMESPACES)
+        if ext_community_member is not None and ext_community_member.text is not None:
+            add_value_from_tag(bgp_ext_community_set, 'ext_community_member', ext_community_member)
+
+            resource_key = '/routing_policy/bgp_defined_set[{:s}][{:s}]'.format(
+                bgp_ext_community_set['ext_community_set_name'], bgp_ext_community_set['ext_community_member'])
+            response.append((resource_key, copy.deepcopy(bgp_ext_community_set)))
+
+    return response
diff --git a/src/device/service/drivers/openconfig/templates/Tools.py b/src/device/service/drivers/openconfig/templates/Tools.py
index e95167dc56dfd323b6fe88a716573a4246305df0..67b6ee89fb051b35afee34d7b35057cce5239d96 100644
--- a/src/device/service/drivers/openconfig/templates/Tools.py
+++ b/src/device/service/drivers/openconfig/templates/Tools.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import lxml.etree as ET
 from typing import Collection, Dict
 
diff --git a/src/device/service/drivers/openconfig/templates/__init__.py b/src/device/service/drivers/openconfig/templates/__init__.py
index c1d1450568ac6e7efc7265b434de8f6ebabd61bc..eb7842ea8d5b62798f08429776700a792f69dc91 100644
--- a/src/device/service/drivers/openconfig/templates/__init__.py
+++ b/src/device/service/drivers/openconfig/templates/__init__.py
@@ -1,14 +1,31 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import json, logging, lxml.etree as ET, re
 from typing import Any, Dict
 from jinja2 import Environment, PackageLoader, select_autoescape
-from device.service.driver_api._Driver import RESOURCE_ENDPOINTS, RESOURCE_INTERFACES, RESOURCE_NETWORK_INSTANCES
+from device.service.driver_api._Driver import (
+    RESOURCE_ENDPOINTS, RESOURCE_INTERFACES, RESOURCE_NETWORK_INSTANCES, RESOURCE_ROUTING_POLICIES)
 from .EndPoints import parse as parse_endpoints
-from .Interfaces import parse as parse_interfaces
+from .Interfaces import parse as parse_interfaces, parse_counters
 from .NetworkInstances import parse as parse_network_instances
+from .RoutingPolicy import parse as parse_routing_policy
 
 ALL_RESOURCE_KEYS = [
     RESOURCE_ENDPOINTS,
     RESOURCE_INTERFACES,
+    RESOURCE_ROUTING_POLICIES,      # routing policies should come before network instances
     RESOURCE_NETWORK_INSTANCES,
 ]
 
@@ -16,26 +33,43 @@ RESOURCE_KEY_MAPPINGS = {
     RESOURCE_ENDPOINTS        : 'component',
     RESOURCE_INTERFACES       : 'interface',
     RESOURCE_NETWORK_INSTANCES: 'network_instance',
+    RESOURCE_ROUTING_POLICIES : 'routing_policy',
 }
 
 RESOURCE_PARSERS = {
     'component'       : parse_endpoints,
     'interface'       : parse_interfaces,
     'network_instance': parse_network_instances,
+    'routing_policy'  : parse_routing_policy,
+    'interfaces/interface/state/counters': parse_counters,
 }
 
 LOGGER = logging.getLogger(__name__)
 RE_REMOVE_FILTERS = re.compile(r'\[[^\]]+\]')
+RE_REMOVE_FILTERS_2 = re.compile(r'\/[a-z]+:')
+EMPTY_CONFIG = '<config></config>'
+EMPTY_FILTER = '<filter></filter>'
 JINJA_ENV = Environment(loader=PackageLoader('device.service.drivers.openconfig'), autoescape=select_autoescape())
 
 def get_filter(resource_key : str):
     resource_key = RESOURCE_KEY_MAPPINGS.get(resource_key, resource_key)
-    template_name = '{:s}/get.xml'.format(RE_REMOVE_FILTERS.sub('', resource_key))
+    resource_key = RE_REMOVE_FILTERS.sub('', resource_key)
+    resource_key = RE_REMOVE_FILTERS_2.sub('/', resource_key)
+    resource_key = resource_key.replace('//', '')
+    template_name = '{:s}/get.xml'.format(resource_key)
     template = JINJA_ENV.get_template(template_name)
-    return '<filter>{:s}</filter>'.format(template.render())
+    return '<filter>{:s}</filter>'.format(template.render().strip())
 
 def parse(resource_key : str, xml_data : ET.Element):
-    parser = RESOURCE_PARSERS.get(RESOURCE_KEY_MAPPINGS.get(resource_key, resource_key))
+    resource_key = RESOURCE_KEY_MAPPINGS.get(resource_key, resource_key)
+    resource_key = RE_REMOVE_FILTERS.sub('', resource_key)
+    resource_key = RE_REMOVE_FILTERS_2.sub('/', resource_key)
+    resource_key = resource_key.replace('//', '')
+    #resource_key_parts = resource_key.split('/')
+    #if len(resource_key_parts) > 1: resource_key_parts = resource_key_parts[:-1]
+    #resource_key = '/'.join(resource_key_parts)
+    #resource_key = RESOURCE_KEY_MAPPINGS.get(resource_key, resource_key)
+    parser = RESOURCE_PARSERS.get(resource_key)
     if parser is None: return [(resource_key, xml_data)]
     return parser(xml_data)
 
@@ -44,4 +78,4 @@ def compose_config(resource_key : str, resource_value : str, delete : bool = Fal
     template = JINJA_ENV.get_template(template_name)
     data : Dict[str, Any] = json.loads(resource_value)
     operation = 'delete' if delete else 'merge'
-    return '<config>{:s}</config>'.format(template.render(**data, operation=operation))
+    return '<config>{:s}</config>'.format(template.render(**data, operation=operation).strip())
diff --git a/src/device/service/drivers/openconfig/templates/interface/edit_config.xml b/src/device/service/drivers/openconfig/templates/interface/edit_config.xml
index ae29586a607b8ffd25e61c0aa9056109aacd3cb9..ff15d1d682ea910208237c32adcc93029fb036d8 100644
--- a/src/device/service/drivers/openconfig/templates/interface/edit_config.xml
+++ b/src/device/service/drivers/openconfig/templates/interface/edit_config.xml
@@ -1,12 +1,14 @@
 <interfaces xmlns="http://openconfig.net/yang/interfaces">
-    <interface{% if operation is defined %} xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" nc:operation="{{operation}}"{% endif %}>
+    <interface{% if operation is defined and operation != 'delete' %} xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" nc:operation="{{operation}}"{% endif %}>
         <name>{{name}}</name>
-        {% if operation is not defined or operation != 'delete' %}
         <config>
             <name>{{name}}</name>
+            {% if operation is defined and operation == 'delete' %}
+            <description></description>
+            {% else %}
             <description>{{description}}</description>
             <mtu>{{mtu}}</mtu>
+            {% endif %}
         </config>
-        {% endif %}
     </interface>
 </interfaces>
diff --git a/src/device/service/drivers/openconfig/templates/interface/subinterface/edit_config.xml b/src/device/service/drivers/openconfig/templates/interface/subinterface/edit_config.xml
index bdc698eeffd626076f6f62e576fe6ef916361412..d266f819c41355ba8a30086415f2bba3b68f1f3d 100644
--- a/src/device/service/drivers/openconfig/templates/interface/subinterface/edit_config.xml
+++ b/src/device/service/drivers/openconfig/templates/interface/subinterface/edit_config.xml
@@ -4,8 +4,6 @@
         {% if operation is not defined or operation != 'delete' %}
         <config>
             <name>{{name}}</name>
-            <description>{{description}}</description>
-            <mtu>{{mtu}}</mtu>
         </config>
         {% endif %}
         <subinterfaces>
@@ -14,7 +12,24 @@
                 {% if operation is not defined or operation != 'delete' %}
                 <config>
                     <index>{{index}}</index>
+                    <enabled>true</enabled>
                 </config>
+                <vlan xmlns="http://openconfig.net/yang/vlan">
+                    <config>
+                        <vlan-id>{{vlan_id}}</vlan-id>
+                    </config>
+                </vlan>
+                <ipv4 xmlns="http://openconfig.net/yang/interfaces/ip">
+                    <addresses>
+                        <address>
+                            <ip>{{address_ip}}</ip>
+                            <config>
+                                <ip>{{address_ip}}</ip>
+                                <prefix-length>{{address_prefix}}</prefix-length>
+                            </config>
+                        </address>
+                    </addresses>
+                </ipv4>
                 {% endif %}
             </subinterface>
         </subinterfaces>
diff --git a/src/device/service/drivers/openconfig/templates/interfaces/interface/state/counters/get.xml b/src/device/service/drivers/openconfig/templates/interfaces/interface/state/counters/get.xml
new file mode 100644
index 0000000000000000000000000000000000000000..90145e25c268db9af2944ba8d2a7ed757f10123a
--- /dev/null
+++ b/src/device/service/drivers/openconfig/templates/interfaces/interface/state/counters/get.xml
@@ -0,0 +1,7 @@
+<interfaces xmlns="http://openconfig.net/yang/interfaces">
+  <interface>
+    <state>
+      <counters/>
+    </state>
+  </interface>
+</interfaces>
diff --git a/src/device/service/drivers/openconfig/templates/interfaces/interface/state/counters/in-octets/get.xml b/src/device/service/drivers/openconfig/templates/interfaces/interface/state/counters/in-octets/get.xml
new file mode 100644
index 0000000000000000000000000000000000000000..83da4b223c6abdc8d1bc20a0367b550e78522e39
--- /dev/null
+++ b/src/device/service/drivers/openconfig/templates/interfaces/interface/state/counters/in-octets/get.xml
@@ -0,0 +1,9 @@
+<interfaces xmlns="http://openconfig.net/yang/interfaces">
+  <interface>
+    <state>
+      <counters>
+        <in-octets></in-octets>
+      </counters>
+    </state>
+  </interface>
+</interfaces>
diff --git a/src/device/service/drivers/openconfig/templates/interfaces/interface/state/counters/in-pkts/get.xml b/src/device/service/drivers/openconfig/templates/interfaces/interface/state/counters/in-pkts/get.xml
new file mode 100644
index 0000000000000000000000000000000000000000..645204d20335cc60a3db4839a69955cacce85092
--- /dev/null
+++ b/src/device/service/drivers/openconfig/templates/interfaces/interface/state/counters/in-pkts/get.xml
@@ -0,0 +1,9 @@
+<interfaces xmlns="http://openconfig.net/yang/interfaces">
+  <interface>
+    <state>
+      <counters>
+        <in-pkts></in-pkts>
+      </counters>
+    </state>
+  </interface>
+</interfaces>
diff --git a/src/device/service/drivers/openconfig/templates/interfaces/interface/state/counters/out-octets/get.xml b/src/device/service/drivers/openconfig/templates/interfaces/interface/state/counters/out-octets/get.xml
new file mode 100644
index 0000000000000000000000000000000000000000..28958ec6a724131c073847dc98f6a8a60e51190a
--- /dev/null
+++ b/src/device/service/drivers/openconfig/templates/interfaces/interface/state/counters/out-octets/get.xml
@@ -0,0 +1,9 @@
+<interfaces xmlns="http://openconfig.net/yang/interfaces">
+  <interface>
+    <state>
+      <counters>
+        <out-octets></out-octets>
+      </counters>
+    </state>
+  </interface>
+</interfaces>
diff --git a/src/device/service/drivers/openconfig/templates/interfaces/interface/state/counters/out-pkts/get.xml b/src/device/service/drivers/openconfig/templates/interfaces/interface/state/counters/out-pkts/get.xml
new file mode 100644
index 0000000000000000000000000000000000000000..52c27410c4b4a69a2ce5c440fe101301c4136510
--- /dev/null
+++ b/src/device/service/drivers/openconfig/templates/interfaces/interface/state/counters/out-pkts/get.xml
@@ -0,0 +1,9 @@
+<interfaces xmlns="http://openconfig.net/yang/interfaces">
+  <interface>
+    <state>
+      <counters>
+        <out-pkts></out-pkts>
+      </counters>
+    </state>
+  </interface>
+</interfaces>
diff --git a/src/device/service/drivers/openconfig/templates/network_instance/edit_config.xml b/src/device/service/drivers/openconfig/templates/network_instance/edit_config.xml
index fab1fdc61f0b6add9c915e914f830270d9875a0a..9362c09c6cfebcd1f83b05002f58eda51724b911 100644
--- a/src/device/service/drivers/openconfig/templates/network_instance/edit_config.xml
+++ b/src/device/service/drivers/openconfig/templates/network_instance/edit_config.xml
@@ -5,12 +5,16 @@
         <config>
             <name>{{name}}</name>
             <type xmlns:oc-ni-types="http://openconfig.net/yang/network-instance-types">oc-ni-types:{{type}}</type>
-            {% for address_family in address_families %}
-            <enabled-address-families xmlns:oc-types="http://openconfig.net/yang/openconfig-types">oc-types:{{address_family}}</enabled-address-families>
-            {% endfor %}
-            <router-id>{{router_id}}</router-id>
+            <description>{{description}}</description>
+            {% if router_id is defined %}<router-id>{{router_id}}</router-id>{% endif %}
             <route-distinguisher>{{route_distinguisher}}</route-distinguisher>
+            <enabled>true</enabled>
         </config>
+        <encapsulation>
+            <config>
+                <encapsulation-type xmlns:oc-ni-types="http://openconfig.net/yang/network-instance-types">oc-ni-types:MPLS</encapsulation-type>
+            </config>
+        </encapsulation>
         {% endif %}
     </network-instance>
 </network-instances>
diff --git a/src/device/service/drivers/openconfig/templates/network_instance/inter_instance_policies/edit_config.xml b/src/device/service/drivers/openconfig/templates/network_instance/inter_instance_policies/edit_config.xml
new file mode 100644
index 0000000000000000000000000000000000000000..b7c87c7ab13317b5bb2a15c43d241673196bf6d2
--- /dev/null
+++ b/src/device/service/drivers/openconfig/templates/network_instance/inter_instance_policies/edit_config.xml
@@ -0,0 +1,15 @@
+{% if operation is not defined or operation != 'delete' %}
+<network-instances xmlns="http://openconfig.net/yang/network-instance">
+    <network-instance>
+        <name>{{name}}</name>
+        <inter-instance-policies>
+            <apply-policy>
+                <config>
+                    {% if import_policy is defined %}<import-policy>{{import_policy}}</import-policy>{% endif%}
+                    {% if export_policy is defined %}<export-policy>{{export_policy}}</export-policy>{% endif%}
+                </config>
+            </apply-policy>
+        </inter-instance-policies>
+    </network-instance>
+</network-instances>
+{% endif %}
diff --git a/src/device/service/drivers/openconfig/templates/network_instance/interface/edit_config.xml b/src/device/service/drivers/openconfig/templates/network_instance/interface/edit_config.xml
index 51f2d4318fbdd2d6da120851a72a6edafe173c73..d5c33d31a6d671216db55c0eded94dc15a56bec8 100644
--- a/src/device/service/drivers/openconfig/templates/network_instance/interface/edit_config.xml
+++ b/src/device/service/drivers/openconfig/templates/network_instance/interface/edit_config.xml
@@ -7,7 +7,8 @@
                 {% if operation is not defined or operation != 'delete' %}
                 <config>
                     <id>{{id}}</id>
-                    <interface>{{id}}</interface>
+                    <interface>{{interface}}</interface>
+                    <subinterface>{{subinterface}}</subinterface>
                 </config>
                 {% endif %}
             </interface>
diff --git a/src/device/service/drivers/openconfig/templates/network_instance/protocols/edit_config.xml b/src/device/service/drivers/openconfig/templates/network_instance/protocols/edit_config.xml
new file mode 100644
index 0000000000000000000000000000000000000000..da05d0467605e6cec0c3448cc325ff60dfc7cfc9
--- /dev/null
+++ b/src/device/service/drivers/openconfig/templates/network_instance/protocols/edit_config.xml
@@ -0,0 +1,27 @@
+<network-instances xmlns="http://openconfig.net/yang/network-instance">
+    <network-instance>
+        <name>{{name}}</name>
+        <protocols>
+            <protocol{% if operation is defined %} xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" nc:operation="{{operation}}"{% endif %}>
+                <identifier>{{identifier}}</identifier>
+                <name>{{protocol_name}}</name>
+                {% if operation is not defined or operation != 'delete' %}
+                <config>
+                    <identifier>{{identifier}}</identifier>
+                    <name>{{protocol_name}}</name>
+                    <enabled>true</enabled>
+                </config>
+                {% if identifier=='BGP' %}
+                <bgp>
+                    <global>
+                        <config>
+                            <as>{{as}}</as>
+                        </config>
+                    </global>
+                </bgp>
+                {% endif %}
+                {% endif %}
+            </protocol>
+        </protocols>
+    </network-instance>
+</network-instances>
diff --git a/src/device/service/drivers/openconfig/templates/network_instance/table_connections/edit_config.xml b/src/device/service/drivers/openconfig/templates/network_instance/table_connections/edit_config.xml
new file mode 100644
index 0000000000000000000000000000000000000000..46bf5e387789c7efc800ad96ed759748273bed34
--- /dev/null
+++ b/src/device/service/drivers/openconfig/templates/network_instance/table_connections/edit_config.xml
@@ -0,0 +1,20 @@
+<network-instances xmlns="http://openconfig.net/yang/network-instance">
+    <network-instance>
+        <name>{{name}}</name>
+        <table-connections>
+            <table-connection{% if operation is defined %} xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" nc:operation="{{operation}}"{% endif %}>
+                <src-protocol xmlns:oc-pol-types="http://openconfig.net/yang/policy-types">oc-pol-types:{{src_protocol}}</src-protocol>
+                <dst-protocol xmlns:oc-pol-types="http://openconfig.net/yang/policy-types">oc-pol-types:{{dst_protocol}}</dst-protocol>
+                <address-family xmlns:oc-types="http://openconfig.net/yang/openconfig-types">oc-types:{{address_family}}</address-family>
+                {% if operation is not defined or operation != 'delete' %}
+                <config>
+                    <src-protocol xmlns:oc-pol-types="http://openconfig.net/yang/policy-types">oc-pol-types:{{src_protocol}}</src-protocol>
+                    <dst-protocol xmlns:oc-pol-types="http://openconfig.net/yang/policy-types">oc-pol-types:{{dst_protocol}}</dst-protocol>
+                    <address-family xmlns:oc-types="http://openconfig.net/yang/openconfig-types">oc-types:{{address_family}}</address-family>
+                    {% if default_import_policy is defined %}<default-import-policy>{{default_import_policy}}</default-import-policy>{% endif %}
+                </config>
+                {% endif %}
+            </table-connection>
+        </table-connections>
+    </network-instance>
+</network-instances>
diff --git a/src/device/service/drivers/openconfig/templates/routing_policy/bgp_defined_set/edit_config.xml b/src/device/service/drivers/openconfig/templates/routing_policy/bgp_defined_set/edit_config.xml
new file mode 100644
index 0000000000000000000000000000000000000000..df64606ae5ab434e5e3453f7294db02bb749bdce
--- /dev/null
+++ b/src/device/service/drivers/openconfig/templates/routing_policy/bgp_defined_set/edit_config.xml
@@ -0,0 +1,14 @@
+<routing-policy xmlns="http://openconfig.net/yang/routing-policy">
+    <defined-sets>
+        <bgp-defined-sets xmlns="http://openconfig.net/yang/bgp-policy">
+            <ext-community-sets>
+                <ext-community-set{% if operation is defined %} xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" nc:operation="{{operation}}"{% endif %}>
+                    <ext-community-set-name>{{ext_community_set_name}}</ext-community-set-name>
+                    {% if operation is not defined or operation != 'delete' %}
+                    {% if ext_community_member is defined %} <ext-community-member>{{ext_community_member}}</ext-community-member>{% endif %}
+                    {% endif %}
+                </ext-community-set>
+            </ext-community-sets>
+        </bgp-defined-sets>
+    </defined-sets>
+</routing-policy>
diff --git a/src/device/service/drivers/openconfig/templates/routing_policy/get.xml b/src/device/service/drivers/openconfig/templates/routing_policy/get.xml
new file mode 100644
index 0000000000000000000000000000000000000000..797e970264fa25c2d45f54cb9c0a6cd0b769f29a
--- /dev/null
+++ b/src/device/service/drivers/openconfig/templates/routing_policy/get.xml
@@ -0,0 +1,8 @@
+<routing-policy xmlns="http://openconfig.net/yang/routing-policy">
+    <policy-definitions/>
+    <defined-sets>
+        <bgp-defined-sets xmlns="http://openconfig.net/yang/bgp-policy">
+            <ext-community-sets/>
+        </bgp-defined-sets>
+    </defined-sets>
+</routing-policy>
diff --git a/src/device/service/drivers/openconfig/templates/routing_policy/policy_definition/edit_config.xml b/src/device/service/drivers/openconfig/templates/routing_policy/policy_definition/edit_config.xml
new file mode 100644
index 0000000000000000000000000000000000000000..c3a31866be5fc072bced339dd7b54f9f92bab290
--- /dev/null
+++ b/src/device/service/drivers/openconfig/templates/routing_policy/policy_definition/edit_config.xml
@@ -0,0 +1,12 @@
+<routing-policy xmlns="http://openconfig.net/yang/routing-policy">
+    <policy-definitions>
+        <policy-definition{% if operation is defined %} xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" nc:operation="{{operation}}"{% endif %}>
+            <name>{{policy_name}}</name>
+            {% if operation is not defined or operation != 'delete' %}
+            <config>
+                <name>{{policy_name}}</name>
+            </config>
+            {% endif %}
+        </policy-definition>
+    </policy-definitions>
+</routing-policy>
diff --git a/src/device/service/drivers/openconfig/templates/routing_policy/policy_definition/statement/edit_config.xml b/src/device/service/drivers/openconfig/templates/routing_policy/policy_definition/statement/edit_config.xml
new file mode 100644
index 0000000000000000000000000000000000000000..711067f424b68da0e69913ce01f5133c5cbbfe02
--- /dev/null
+++ b/src/device/service/drivers/openconfig/templates/routing_policy/policy_definition/statement/edit_config.xml
@@ -0,0 +1,30 @@
+{% if operation is not defined or operation != 'delete' %}
+<routing-policy xmlns="http://openconfig.net/yang/routing-policy">
+    <policy-definitions>
+        <policy-definition>
+            <name>{{policy_name}}</name>
+            <statements>
+                <statement>
+                    <name>{{statement_name}}</name>
+                    <config>
+                        <name>{{statement_name}}</name>
+                    </config>
+                    <conditions>
+                        <bgp-conditions xmlns="http://openconfig.net/yang/bgp-policy">
+                            <match-ext-community-set>
+                                <ext-community-set>{{ext_community_set_name}}</ext-community-set>
+                                <match-set-options>{{match_set_options}}</match-set-options>
+                            </match-ext-community-set>
+                        </bgp-conditions>
+                    </conditions>
+                    <actions>
+                        <config>
+                            <policy-result>{{policy_result}}</policy-result>
+                        </config>
+                    </actions>
+                </statement>
+            </statements>
+        </policy-definition>
+    </policy-definitions>
+</routing-policy>
+{% endif %}
diff --git a/src/device/service/drivers/p4/__init__.py b/src/device/service/drivers/p4/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/device/service/drivers/p4/__init__.py
+++ b/src/device/service/drivers/p4/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/device/service/drivers/p4/p4_driver.py b/src/device/service/drivers/p4/p4_driver.py
index 3d3abf236f016608ef93e3d63ab04ac86830da7d..3e0cccffabb7aad4ab5b5c6e88642c36b7e12166 100644
--- a/src/device/service/drivers/p4/p4_driver.py
+++ b/src/device/service/drivers/p4/p4_driver.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 """
 P4 driver plugin for the TeraFlow SDN controller.
 """
@@ -212,7 +226,7 @@ class P4Driver(_Driver):
         LOGGER.info('P4 GetResource()')
         return ""
 
-    def GetState(self, blocking=False) -> Iterator[Tuple[str, Any]]:
+    def GetState(self, blocking=False, terminate : Optional[threading.Event] = None) -> Iterator[Tuple[str, Any]]:
         """
         Retrieves the state of a P4 device.
 
diff --git a/src/device/service/drivers/p4/p4_util.py b/src/device/service/drivers/p4/p4_util.py
index 8d6f258ddb0285a5fa4ee5bd11811d02df380345..b3d54499f56772768dc19bc1cae3bbf9a25e7dc2 100644
--- a/src/device/service/drivers/p4/p4_util.py
+++ b/src/device/service/drivers/p4/p4_util.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 """
 P4 driver utilities.
 """
diff --git a/src/device/service/drivers/transport_api/Tools.py b/src/device/service/drivers/transport_api/Tools.py
index c569404bf7884501f9c8ea494f27983ca78ac3eb..8f18c56b3ac6ae79ead86199c56486cf9c00eae4 100644
--- a/src/device/service/drivers/transport_api/Tools.py
+++ b/src/device/service/drivers/transport_api/Tools.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import json, logging, requests
 from device.service.driver_api._Driver import RESOURCE_ENDPOINTS
 
diff --git a/src/device/service/drivers/transport_api/TransportApiDriver.py b/src/device/service/drivers/transport_api/TransportApiDriver.py
index b3e5f4fa33f20836629c06968261fb1ceac8f075..b0ecfe32f63b2568d7b5fb7498e532935aee018c 100644
--- a/src/device/service/drivers/transport_api/TransportApiDriver.py
+++ b/src/device/service/drivers/transport_api/TransportApiDriver.py
@@ -1,5 +1,19 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging, requests, threading
-from typing import Any, Iterator, List, Tuple, Union
+from typing import Any, Iterator, List, Optional, Tuple, Union
 from common.type_checkers.Checkers import chk_string, chk_type
 from device.service.driver_api._Driver import _Driver
 from . import ALL_RESOURCE_KEYS
@@ -92,6 +106,8 @@ class TransportApiDriver(_Driver):
         # TODO: TAPI does not support monitoring by now
         return [False for _ in subscriptions]
 
-    def GetState(self, blocking=False) -> Iterator[Tuple[float, str, Any]]:
+    def GetState(
+        self, blocking=False, terminate : Optional[threading.Event] = None
+    ) -> Iterator[Tuple[float, str, Any]]:
         # TODO: TAPI does not support monitoring by now
         return []
diff --git a/src/device/service/drivers/transport_api/__init__.py b/src/device/service/drivers/transport_api/__init__.py
index d2a2d4b1a6c224c370479103553a769ce8a0956f..925746998061f4e05c468133dfacaaa0414551c8 100644
--- a/src/device/service/drivers/transport_api/__init__.py
+++ b/src/device/service/drivers/transport_api/__init__.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 from device.service.driver_api._Driver import RESOURCE_ENDPOINTS, RESOURCE_INTERFACES, RESOURCE_NETWORK_INSTANCES
 
 ALL_RESOURCE_KEYS = [
diff --git a/src/device/tests/.gitignore b/src/device/tests/.gitignore
index 5cb8b444d357c5e39eb31759d67b92fca7beabb2..b5f6bc13b7b17daa79d9e67c5fc0c50338d089a1 100644
--- a/src/device/tests/.gitignore
+++ b/src/device/tests/.gitignore
@@ -1,3 +1,3 @@
 # Add here your files containing confidential testbed details such as IP addresses, ports, usernames, passwords, etc.
-Device_OpenConfig_Infinera.py
-Device_Transport_Api_CTTC.py
+Device_OpenConfig_Infinera*
+Device_Transport_Api*
diff --git a/src/device/tests/CommonObjects.py b/src/device/tests/CommonObjects.py
index 2b51dcd2322e70dff9a5229e65e6a220708a834f..de82cfcc01164ee95bf3d77a0946f4a4b85598fc 100644
--- a/src/device/tests/CommonObjects.py
+++ b/src/device/tests/CommonObjects.py
@@ -1,21 +1,36 @@
-from copy import deepcopy
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 from common.Constants import DEFAULT_CONTEXT_UUID, DEFAULT_TOPOLOGY_UUID
+from common.tools.object_factory.Context import json_context, json_context_id
+from common.tools.object_factory.Topology import json_topology, json_topology_id
+from context.proto.kpi_sample_types_pb2 import KpiSampleType
+
+# ----- Context --------------------------------------------------------------------------------------------------------
+CONTEXT_ID = json_context_id(DEFAULT_CONTEXT_UUID)
+CONTEXT    = json_context(DEFAULT_CONTEXT_UUID)
+
 
-# use "deepcopy" to prevent propagating forced changes during tests
+# ----- Topology -------------------------------------------------------------------------------------------------------
+TOPOLOGY_ID = json_topology_id(DEFAULT_TOPOLOGY_UUID, context_id=CONTEXT_ID)
+TOPOLOGY    = json_topology(DEFAULT_TOPOLOGY_UUID, context_id=CONTEXT_ID)
 
-CONTEXT_ID = {'context_uuid': {'uuid': DEFAULT_CONTEXT_UUID}}
-CONTEXT = {
-    'context_id': deepcopy(CONTEXT_ID),
-    'topology_ids': [],
-    'service_ids': [],
-}
 
-TOPOLOGY_ID = {
-    'context_id': deepcopy(CONTEXT_ID),
-    'topology_uuid': {'uuid': DEFAULT_TOPOLOGY_UUID},
-}
-TOPOLOGY = {
-    'topology_id': deepcopy(TOPOLOGY_ID),
-    'device_ids': [],
-    'link_ids': [],
-}
+# ----- KPI Sample Types -----------------------------------------------------------------------------------------------
+PACKET_PORT_SAMPLE_TYPES = [
+    KpiSampleType.KPISAMPLETYPE_PACKETS_TRANSMITTED,
+    KpiSampleType.KPISAMPLETYPE_PACKETS_RECEIVED,
+    KpiSampleType.KPISAMPLETYPE_BYTES_TRANSMITTED,
+    KpiSampleType.KPISAMPLETYPE_BYTES_RECEIVED,
+]
diff --git a/src/device/tests/Device_Emulated.py b/src/device/tests/Device_Emulated.py
index 27595dd8afed8b2985c76338ffc88d81fc9cc2d0..467017d6e61c2949a36319a8b84e1725ecdd6b7f 100644
--- a/src/device/tests/Device_Emulated.py
+++ b/src/device/tests/Device_Emulated.py
@@ -1,101 +1,90 @@
-import operator
-from copy import deepcopy
-from device.proto.context_pb2 import DeviceDriverEnum, DeviceOperationalStatusEnum
-from device.service.database.KpiSampleType import ORM_KpiSampleTypeEnum
-from .Tools import config_rule_set, config_rule_delete
-
-# use "deepcopy" to prevent propagating forced changes during tests
-
-DEVICE_EMU_UUID           = 'EMULATED'
-DEVICE_EMU_TYPE           = 'emulated'
-DEVICE_EMU_ADDRESS        = '127.0.0.1'
-DEVICE_EMU_PORT           = '0'
-DEVICE_EMU_DRIVERS        = [DeviceDriverEnum.DEVICEDRIVER_UNDEFINED]
-
-DEVICE_EMU_ID = {'device_uuid': {'uuid': DEVICE_EMU_UUID}}
-DEVICE_EMU = {
-    'device_id': deepcopy(DEVICE_EMU_ID),
-    'device_type': DEVICE_EMU_TYPE,
-    'device_config': {'config_rules': []},
-    'device_operational_status': DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_DISABLED,
-    'device_drivers': DEVICE_EMU_DRIVERS,
-    'device_endpoints': [],
-}
-
-PACKET_PORT_SAMPLE_TYPES = [
-    ORM_KpiSampleTypeEnum.PACKETS_TRANSMITTED,
-    ORM_KpiSampleTypeEnum.PACKETS_RECEIVED,
-    ORM_KpiSampleTypeEnum.BYTES_TRANSMITTED,
-    ORM_KpiSampleTypeEnum.BYTES_RECEIVED,
-]
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
 
-ENDPOINT_UUIDS = ['EP1', 'EP2', 'EP3', 'EP4']
+from common.tools.object_factory.ConfigRule import json_config_rule_delete, json_config_rule_set
+from common.tools.object_factory.Device import (
+    json_device_emulated_connect_rules, json_device_emulated_packet_router_disabled, json_device_id)
+from context.proto.kpi_sample_types_pb2 import KpiSampleType
+from device.tests.CommonObjects import PACKET_PORT_SAMPLE_TYPES
 
-DEVICE_EMU_ENDPOINTS = []
-for endpoint_uuid in ENDPOINT_UUIDS:
-    DEVICE_EMU_ENDPOINTS.append((endpoint_uuid, '10Gbps', PACKET_PORT_SAMPLE_TYPES))
+DEVICE_EMU_UUID     = 'EMULATED'
+DEVICE_EMU_ID       = json_device_id(DEVICE_EMU_UUID)
+DEVICE_EMU          = json_device_emulated_packet_router_disabled(DEVICE_EMU_UUID)
+DEVICE_EMU_EP_UUIDS = ['EP1', 'EP2', 'EP3', 'EP4']
+DEVICE_EMU_EP_DESCS = [(ep_uuid, '10Gbps', PACKET_PORT_SAMPLE_TYPES) for ep_uuid in DEVICE_EMU_EP_UUIDS]
+DEVICE_EMU_CONNECT_RULES = json_device_emulated_connect_rules(DEVICE_EMU_EP_DESCS)
 
 RSRC_EP       = '/endpoints/endpoint[{:s}]'
 RSRC_SUBIF    = RSRC_EP    + '/subinterfaces/subinterface[{:d}]'
 RSRC_ADDRIPV4 = RSRC_SUBIF + '/ipv4/address[{:s}]'
 
 DEVICE_EMU_ENDPOINTS_COOKED = []
-for endpoint_uuid,endpoint_type,endpoint_sample_types in DEVICE_EMU_ENDPOINTS:
+for endpoint_uuid,endpoint_type,endpoint_sample_types in DEVICE_EMU_EP_DESCS:
     endpoint_resource_key = RSRC_EP.format(str(endpoint_uuid))
     sample_types = {}
     for endpoint_sample_type in endpoint_sample_types:
-        sample_type_name = endpoint_sample_type.name.lower()
-        sample_types[endpoint_sample_type.value] = '{:s}/state/{:s}'.format(endpoint_resource_key, sample_type_name)
+        sample_type_name = KpiSampleType.Name(endpoint_sample_type).lower().replace('kpisampletype_', '')
+        sample_types[endpoint_sample_type] = '{:s}/state/{:s}'.format(endpoint_resource_key, sample_type_name)
     endpoint_resource_value = {'uuid': endpoint_uuid, 'type': endpoint_type, 'sample_types': sample_types}
     DEVICE_EMU_ENDPOINTS_COOKED.append((endpoint_resource_key, endpoint_resource_value))
 
-DEVICE_EMU_CONNECT_RULES = [
-    config_rule_set('_connect/address',  DEVICE_EMU_ADDRESS ),
-    config_rule_set('_connect/port',     DEVICE_EMU_PORT    ),
-    config_rule_set('_connect/settings', {'endpoints': [
-        {
-            'uuid': endpoint_uuid, 'type': endpoint_type,
-            'sample_types': list(map(operator.attrgetter('value'), endpoint_sample_types)),
-        }
-        for endpoint_uuid,endpoint_type,endpoint_sample_types in DEVICE_EMU_ENDPOINTS
-    ]}),
+DEVICE_EMU_CONFIG_ENDPOINTS = [
+    json_config_rule_set(RSRC_EP.format('EP1'), {'enabled' : True}),
+    json_config_rule_set(RSRC_EP.format('EP2'), {'enabled' : True}),
+    json_config_rule_set(RSRC_EP.format('EP3'), {'enabled' : True}),
+    json_config_rule_set(RSRC_EP.format('EP4'), {'enabled' : True}),
 ]
 
-DEVICE_EMU_CONFIG_ENDPOINTS = []
-for endpoint_uuid in ENDPOINT_UUIDS:
-    DEVICE_EMU_CONFIG_ENDPOINTS.append(config_rule_set(RSRC_EP.format(endpoint_uuid), {'enabled' : True}))
-
-DEVICE_EMU_CONFIG_ADDRESSES = []
-for endpoint_uuid in ENDPOINT_UUIDS:
-    endpoint_number = int(endpoint_uuid.replace('EP', ''))
-    subinterface_index = 0
-    subinterface_address = '10.{:d}.{:d}.1'.format(endpoint_number, subinterface_index)
-    subinterface_prefix_length = 24
-    DEVICE_EMU_CONFIG_ADDRESSES.extend([
-        config_rule_set(RSRC_SUBIF   .format(endpoint_uuid, subinterface_index), {
-            'index': subinterface_index}),
-        config_rule_set(RSRC_ADDRIPV4.format(endpoint_uuid, subinterface_index, subinterface_address), {
-            'ip': subinterface_address, 'prefix_length': subinterface_prefix_length}),
-    ])
+DEVICE_EMU_CONFIG_ADDRESSES = [
+    json_config_rule_set(RSRC_SUBIF   .format('EP1', 0), {'index': 0}),
+    json_config_rule_set(RSRC_ADDRIPV4.format('EP1', 0, '10.1.0.1'), {'ip': '10.1.0.1', 'prefix_length': 24}),
+
+    json_config_rule_set(RSRC_SUBIF   .format('EP2', 0), {'index': 0}),
+    json_config_rule_set(RSRC_ADDRIPV4.format('EP2', 0, '10.2.0.1'), {'ip': '10.2.0.1', 'prefix_length': 24}),
+
+    json_config_rule_set(RSRC_SUBIF   .format('EP3', 0), {'index': 0}),
+    json_config_rule_set(RSRC_ADDRIPV4.format('EP3', 0, '10.3.0.1'), {'ip': '10.3.0.1', 'prefix_length': 24}),
+
+    json_config_rule_set(RSRC_SUBIF   .format('EP4', 0), {'index': 0}),
+    json_config_rule_set(RSRC_ADDRIPV4.format('EP4', 0, '10.4.0.1'), {'ip': '10.4.0.1', 'prefix_length': 24}),
+]
 
 DEVICE_EMU_RECONFIG_ADDRESSES = [
-    config_rule_delete(RSRC_SUBIF   .format('EP2', 0            ), {}),
-    config_rule_delete(RSRC_ADDRIPV4.format('EP2', 0, '10.2.0.1'), {'ip': '10.2.0.1', 'prefix_length': 24}),
+    json_config_rule_delete(RSRC_SUBIF   .format('EP2', 0), {}),
+    json_config_rule_delete(RSRC_ADDRIPV4.format('EP2', 0, '10.2.0.1'), {'ip': '10.2.0.1', 'prefix_length': 24}),
 
-    config_rule_set   (RSRC_SUBIF   .format('EP2', 1            ), {'index': 1}),
-    config_rule_set   (RSRC_ADDRIPV4.format('EP2', 1, '10.2.1.1'), {'ip': '10.2.1.1', 'prefix_length': 24}),
+    json_config_rule_set   (RSRC_SUBIF   .format('EP2', 1), {'index': 1}),
+    json_config_rule_set   (RSRC_ADDRIPV4.format('EP2', 1, '10.2.1.1'), {'ip': '10.2.1.1', 'prefix_length': 24}),
 ]
 
-DEVICE_EMU_DECONFIG_ADDRESSES = []
-for endpoint_uuid in ENDPOINT_UUIDS:
-    endpoint_number = int(endpoint_uuid.replace('EP', ''))
-    subinterface_index = 1 if endpoint_uuid == 'EP2' else 0
-    subinterface_address = '10.{:d}.{:d}.1'.format(endpoint_number, subinterface_index)
-    DEVICE_EMU_DECONFIG_ADDRESSES.extend([
-        config_rule_delete(RSRC_SUBIF   .format(endpoint_uuid, subinterface_index), {}),
-        config_rule_delete(RSRC_ADDRIPV4.format(endpoint_uuid, subinterface_index, subinterface_address), {}),
-    ])
-
-DEVICE_EMU_DECONFIG_ENDPOINTS = []
-for endpoint_uuid in ENDPOINT_UUIDS:
-    DEVICE_EMU_DECONFIG_ENDPOINTS.append(config_rule_delete(RSRC_EP.format(endpoint_uuid), {}))
+DEVICE_EMU_DECONFIG_ADDRESSES = [
+    json_config_rule_delete(RSRC_SUBIF   .format('EP1', 0), {}),
+    json_config_rule_delete(RSRC_ADDRIPV4.format('EP1', 0, '10.1.0.1'), {}),
+
+    json_config_rule_delete(RSRC_SUBIF   .format('EP2', 1), {}),
+    json_config_rule_delete(RSRC_ADDRIPV4.format('EP2', 1, '10.2.1.1'), {}),
+
+    json_config_rule_delete(RSRC_SUBIF   .format('EP3', 0), {}),
+    json_config_rule_delete(RSRC_ADDRIPV4.format('EP3', 0, '10.3.0.1'), {}),
+
+    json_config_rule_delete(RSRC_SUBIF   .format('EP4', 0), {}),
+    json_config_rule_delete(RSRC_ADDRIPV4.format('EP4', 0, '10.4.0.1'), {}),
+]
+
+DEVICE_EMU_DECONFIG_ENDPOINTS = [
+    json_config_rule_delete(RSRC_EP.format('EP1'), {}),
+    json_config_rule_delete(RSRC_EP.format('EP2'), {}),
+    json_config_rule_delete(RSRC_EP.format('EP3'), {}),
+    json_config_rule_delete(RSRC_EP.format('EP4'), {}),
+]
diff --git a/src/device/tests/Device_OpenConfig_Template.py b/src/device/tests/Device_OpenConfig_Template.py
index 5f917c5c4057727a64697251696fdeb0283ab7cd..df588b3d8a65883e0ebbfeacb222648bda0455ed 100644
--- a/src/device/tests/Device_OpenConfig_Template.py
+++ b/src/device/tests/Device_OpenConfig_Template.py
@@ -1,38 +1,35 @@
-from copy import deepcopy
-from device.proto.context_pb2 import DeviceDriverEnum, DeviceOperationalStatusEnum
-from .Tools import config_rule_set, config_rule_delete
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
 
-# use "deepcopy" to prevent propagating forced changes during tests
+from common.tools.object_factory.Device import (
+    json_device_connect_rules, json_device_id, json_device_packetrouter_disabled)
 
-DEVICE_OC_UUID     = 'DEV2'
-DEVICE_OC_TYPE     = 'packet-router'
+DEVICE_OC_UUID     = 'DEV-UUID'   # populate the name of the device to test
 DEVICE_OC_ADDRESS  = '127.0.0.1'  # populate the Netconf Server IP address of the device to test
-DEVICE_OC_PORT     = '830'        # populate the Netconf Server port of the device to test
+DEVICE_OC_PORT     = 830          # populate the Netconf Server port of the device to test
 DEVICE_OC_USERNAME = 'username'   # populate the Netconf Server username of the device to test
 DEVICE_OC_PASSWORD = 'password'   # populate the Netconf Server password of the device to test
 DEVICE_OC_TIMEOUT  = 120
-DEVICE_OC_DRIVERS  = [DeviceDriverEnum.DEVICEDRIVER_OPENCONFIG]
 
-DEVICE_OC_ID = {'device_uuid': {'uuid': DEVICE_OC_UUID}}
-DEVICE_OC = {
-    'device_id': deepcopy(DEVICE_OC_ID),
-    'device_type': DEVICE_OC_TYPE,
-    'device_config': {'config_rules': []},
-    'device_operational_status': DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_DISABLED,
-    'device_drivers': DEVICE_OC_DRIVERS,
-    'device_endpoints': [],                     # populated through the driver, leave this list empty
-}
+DEVICE_OC_ID = json_device_id(DEVICE_OC_UUID)
+DEVICE_OC    = json_device_packetrouter_disabled(DEVICE_OC_UUID)
 
-DEVICE_OC_CONNECT_RULES = [
-    config_rule_set('_connect/address', DEVICE_OC_ADDRESS),
-    config_rule_set('_connect/port',    DEVICE_OC_PORT   ),
-    config_rule_set('_connect/settings', {
-        'username': DEVICE_OC_USERNAME,
-        'password': DEVICE_OC_PASSWORD,
-        'timeout' : DEVICE_OC_TIMEOUT,
-    }),
-]
+DEVICE_OC_CONNECT_RULES = json_device_connect_rules(DEVICE_OC_ADDRESS, DEVICE_OC_PORT, {
+    'username': DEVICE_OC_USERNAME,
+    'password': DEVICE_OC_PASSWORD,
+    'timeout' : DEVICE_OC_TIMEOUT,
+})
 
 DEVICE_OC_CONFIG_RULES   = []           # populate your configuration rules to test
-
 DEVICE_OC_DECONFIG_RULES = []           # populate your deconfiguration rules to test
diff --git a/src/device/tests/Device_Transport_Api_Template.py b/src/device/tests/Device_Transport_Api_Template.py
index 6032f0ff8ba683cd3a39bb6bd3c7a8c905974ce6..ef4fa4d57700637121f7d64113c57120ffbc49a4 100644
--- a/src/device/tests/Device_Transport_Api_Template.py
+++ b/src/device/tests/Device_Transport_Api_Template.py
@@ -1,40 +1,48 @@
-from copy import deepcopy
-from device.proto.context_pb2 import DeviceDriverEnum, DeviceOperationalStatusEnum
-from .Tools import config_rule_set, config_rule_delete
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
 
-# use "deepcopy" to prevent propagating forced changes during tests
+from common.tools.object_factory.ConfigRule import json_config_rule_delete, json_config_rule_set
+from common.tools.object_factory.Device import (
+    json_device_connect_rules, json_device_id, json_device_tapi_disabled)
 
-DEVICE_TAPI_UUID         = 'DEVICE-TAPI'
-DEVICE_TAPI_TYPE         = 'optical-line-system'
-DEVICE_TAPI_ADDRESS      = '0.0.0.0'
-DEVICE_TAPI_PORT         = '4900'
-DEVICE_TAPI_TIMEOUT      = '120'
-DEVICE_TAPI_DRIVERS      = [DeviceDriverEnum.DEVICEDRIVER_TRANSPORT_API]
+DEVICE_TAPI_UUID    = 'DEVICE-TAPI'     # populate 'device-uuid' of the TAPI server
+DEVICE_TAPI_ADDRESS = '0.0.0.0'         # populate 'address' of the TAPI server
+DEVICE_TAPI_PORT    = 4900              # populate 'port' of the TAPI server
+DEVICE_TAPI_TIMEOUT = 120               # populate 'timeout' of the TAPI server
 
-DEVICE_TAPI_ID = {'device_uuid': {'uuid': DEVICE_TAPI_UUID}}
-DEVICE_TAPI = {
-    'device_id': deepcopy(DEVICE_TAPI_ID),
-    'device_type': DEVICE_TAPI_TYPE,
-    'device_config': {'config_rules': []},
-    'device_operational_status': DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_DISABLED,
-    'device_drivers': DEVICE_TAPI_DRIVERS,
-    'device_endpoints': [],
-}
+DEVICE_TAPI_ID = json_device_id(DEVICE_TAPI_UUID)
+DEVICE_TAPI    = json_device_tapi_disabled(DEVICE_TAPI_UUID)
 
-DEVICE_TAPI_CONNECT_RULES = [
-    config_rule_set('_connect/address',  DEVICE_TAPI_ADDRESS),
-    config_rule_set('_connect/port',     DEVICE_TAPI_PORT),
-    config_rule_set('_connect/timeout',  DEVICE_TAPI_TIMEOUT),
-]
+DEVICE_TAPI_CONNECT_RULES = json_device_connect_rules(DEVICE_TAPI_ADDRESS, DEVICE_TAPI_PORT, {
+    'timeout' : DEVICE_TAPI_TIMEOUT,
+})
 
 DEVICE_TAPI_CONFIG_RULES = [
-    config_rule_set('network_instance[DemoOFC-NetInst]/interface[13/1/3]', {
-        'name': 'DemoOFC-NetInst', 'id': '13/1/3',
+    json_config_rule_set('node_4_port_16-input_to_node_2_port_17-output', {
+        'uuid'                    : 'service-uuid',     # populate 'service-uuid' of the service to test
+        'input_sip'               : 'input-sip-uuid',   # populate 'input-sip-uuid' of the service to test
+        'output_sip'              : 'output-sip-uuid',  # populate 'output-sip-uuid' of the service to test
+        'capacity_unit'           : 'GHz',              # populate 'capacity-unit' of the service to test
+        'capacity_value'          : 1,                  # populate 'capacity-value' of the service to test
+        'direction'               : 'UNIDIRECTIONAL',   # populate 'direction' of the service to test
+        'layer_protocol_name'     : 'PHOTONIC_MEDIA',
+        'layer_protocol_qualifier': 'tapi-photonic-media:PHOTONIC_LAYER_QUALIFIER_NMC',
     })
 ]
 
 DEVICE_TAPI_DECONFIG_RULES = [
-    config_rule_delete('network_instance[DemoOFC-NetInst]/interface[13/1/3]', {
-        'name': 'DemoOFC-NetInst', 'id': '13/1/3'
+    json_config_rule_delete('node_4_port_16-input_to_node_2_port_17-output', {
+        'uuid': 'service-uuid'                          # populate 'service-uuid' of the service to test
     })
-]
\ No newline at end of file
+]
diff --git a/src/device/tests/MockMonitoringService.py b/src/device/tests/MockMonitoringService.py
index 6f51f0d8360f96ed183c11b336eb300fe3695a36..3e8550058daa905517f26a659a08c66db1172d74 100644
--- a/src/device/tests/MockMonitoringService.py
+++ b/src/device/tests/MockMonitoringService.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import grpc, logging
 from concurrent import futures
 from queue import Queue
diff --git a/src/device/tests/MockMonitoringServiceServicerImpl.py b/src/device/tests/MockMonitoringServiceServicerImpl.py
index 1787acdaa68fd80037ab1f4d14d7f86599f9ac14..05ca43dda0418df151bc3dfe255a90d75b50a088 100644
--- a/src/device/tests/MockMonitoringServiceServicerImpl.py
+++ b/src/device/tests/MockMonitoringServiceServicerImpl.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging
 from queue import Queue
 from monitoring.proto.context_pb2 import Empty
diff --git a/src/device/tests/Tools.py b/src/device/tests/Tools.py
deleted file mode 100644
index 2d8e99de30ebfcc4ed257a7bee512f4d416bd64c..0000000000000000000000000000000000000000
--- a/src/device/tests/Tools.py
+++ /dev/null
@@ -1,25 +0,0 @@
-import json
-from copy import deepcopy
-from typing import Any, Dict, Union
-from device.proto.context_pb2 import ConfigActionEnum
-
-def config_rule(action : ConfigActionEnum, resource_key : str, resource_value : Union[str, Dict[str, Any]]):
-    if not isinstance(resource_value, str): resource_value = json.dumps(resource_value, sort_keys=True)
-    return {'action': action, 'resource_key': resource_key, 'resource_value': resource_value}
-
-def config_rule_set(resource_key : str, resource_value : Union[str, Dict[str, Any]]):
-    return config_rule(ConfigActionEnum.CONFIGACTION_SET, resource_key, resource_value)
-
-def config_rule_delete(resource_key : str, resource_value : Union[str, Dict[str, Any]]):
-    return config_rule(ConfigActionEnum.CONFIGACTION_DELETE, resource_key, resource_value)
-
-def endpoint_id(device_id, endpoint_uuid, topology_id=None):
-    result = {'device_id': deepcopy(device_id), 'endpoint_uuid': {'uuid': endpoint_uuid}}
-    if topology_id is not None: result['topology_id'] = deepcopy(topology_id)
-    return result
-
-def endpoint(device_id, endpoint_uuid, endpoint_type, topology_id=None):
-    return {
-        'endpoint_id': endpoint_id(device_id, endpoint_uuid, topology_id=topology_id),
-        'endpoint_type': endpoint_type,
-    }
diff --git a/src/device/tests/__init__.py b/src/device/tests/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/device/tests/__init__.py
+++ b/src/device/tests/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/device/tests/device_p4.py b/src/device/tests/device_p4.py
index 55c1025861ad1a00fff6b692eaeaaadcd639a519..577855dd449b1aa67b8b36e78bd57abb47b03b23 100644
--- a/src/device/tests/device_p4.py
+++ b/src/device/tests/device_p4.py
@@ -1,20 +1,28 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 """
 P4 device example configuration.
 """
 
-from copy import deepcopy
-try:
-    from .context_pb2 import DeviceDriverEnum, DeviceOperationalStatusEnum
-except ImportError:
-    from device.proto.context_pb2 import DeviceDriverEnum, DeviceOperationalStatusEnum
-from .Tools import config_rule_set
+from common.tools.object_factory.ConfigRule import json_config_rule_set
+from common.tools.object_factory.Device import json_device_p4_disabled
 
 DEVICE_P4_ID = 0
 DEVICE_P4_NAME = 'device:leaf1'
-DEVICE_P4_TYPE = 'p4-switch'
 DEVICE_P4_ADDRESS = '127.0.0.1'
 DEVICE_P4_PORT = '50101'
-DEVICE_P4_DRIVERS = [DeviceDriverEnum.DEVICEDRIVER_P4]
 DEVICE_P4_VENDOR = 'Open Networking Foundation'
 DEVICE_P4_HW_VER = 'BMv2 simple_switch'
 DEVICE_P4_SW_VER = 'Stratum'
@@ -23,27 +31,20 @@ DEVICE_P4_WORKERS = 2
 DEVICE_P4_GRACE_PERIOD = 60
 
 DEVICE_P4_UUID = {'device_uuid': {'uuid': DEVICE_P4_NAME}}
-DEVICE_P4 = {
-    'device_id': deepcopy(DEVICE_P4_UUID),
-    'device_type': DEVICE_P4_TYPE,
-    'device_config': {'config_rules': []},
-    'device_operational_status': DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_DISABLED,
-    'device_drivers': DEVICE_P4_DRIVERS,
-    'device_endpoints': [],
-}
+DEVICE_P4 = json_device_p4_disabled(DEVICE_P4_UUID)
 
 DEVICE_P4_CONNECT_RULES = [
-    config_rule_set('_connect/address', DEVICE_P4_ADDRESS),
-    config_rule_set('_connect/port', DEVICE_P4_PORT),
-    config_rule_set('_connect/settings', {
-        'id': int(DEVICE_P4_ID),
-        'name': DEVICE_P4_NAME,
-        'hw-ver': DEVICE_P4_HW_VER,
-        'sw-ver': DEVICE_P4_SW_VER,
+    json_config_rule_set('_connect/address', DEVICE_P4_ADDRESS),
+    json_config_rule_set('_connect/port', DEVICE_P4_PORT),
+    json_config_rule_set('_connect/settings', {
+        'id'      : int(DEVICE_P4_ID),
+        'name'    : DEVICE_P4_NAME,
+        'hw-ver'  : DEVICE_P4_HW_VER,
+        'sw-ver'  : DEVICE_P4_SW_VER,
         'pipeconf': DEVICE_P4_PIPECONF
     }),
 ]
 
 DEVICE_P4_CONFIG_RULES = [
-    config_rule_set('key1', 'value1'),
+    json_config_rule_set('key1', 'value1'),
 ]
diff --git a/src/device/tests/mock_p4runtime_service.py b/src/device/tests/mock_p4runtime_service.py
index f1cf673e34a1dd604a17589ad39c93f0ca1375b8..77da0113676dc6f820d995b34915df6d0ba30f01 100644
--- a/src/device/tests/mock_p4runtime_service.py
+++ b/src/device/tests/mock_p4runtime_service.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 """
 A mock P4Runtime server.
 """
diff --git a/src/device/tests/mock_p4runtime_servicer_impl.py b/src/device/tests/mock_p4runtime_servicer_impl.py
index f29f05ba2d504794a085c4499ebfefba08604811..d29445da43afb58ef062f62c496b0780f92a4648 100644
--- a/src/device/tests/mock_p4runtime_servicer_impl.py
+++ b/src/device/tests/mock_p4runtime_servicer_impl.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 """
 A mock P4Runtime service implementation.
 """
diff --git a/src/device/tests/test_unit_p4.py b/src/device/tests/test_unit_p4.py
index 8f2f89f05f7cbee1e5263464a4c59e77ccde2092..853d9d8a5fa4795fbec1445bd0f9d518402023c7 100644
--- a/src/device/tests/test_unit_p4.py
+++ b/src/device/tests/test_unit_p4.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import pytest
 from device.service.drivers.p4.p4_driver import P4Driver
 from .device_p4 import(
diff --git a/src/device/tests/test_unitary.py b/src/device/tests/test_unitary.py
index 7eaee6e9fe232998e9f7a56aaaaf1c1ed136420a..64c54deb0e784fbf075d7d6973ed01b0708d4867 100644
--- a/src/device/tests/test_unitary.py
+++ b/src/device/tests/test_unitary.py
@@ -1,12 +1,26 @@
-import copy, grpc, json, logging, operator, os, pytest, time
-from datetime import datetime
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import calendar, copy, dateutil.parser, grpc, json, logging, operator, os, pytest, queue, time
+from datetime import datetime, timezone
 from typing import Tuple
-from queue import Queue, Empty
-from google.protobuf.json_format import MessageToDict
 from common.orm.Database import Database
 from common.orm.Factory import get_database_backend, BackendEnum as DatabaseBackendEnum
 from common.message_broker.Factory import get_messagebroker_backend, BackendEnum as MessageBrokerBackendEnum
 from common.message_broker.MessageBroker import MessageBroker
+from common.tools.grpc.Tools import grpc_message_to_json_string
+from common.tools.object_factory.EndPoint import json_endpoint, json_endpoint_id
 from context.Config import (
     GRPC_SERVICE_PORT as CONTEXT_GRPC_SERVICE_PORT, GRPC_MAX_WORKERS as CONTEXT_GRPC_MAX_WORKERS,
     GRPC_GRACE_PERIOD as CONTEXT_GRPC_GRACE_PERIOD)
@@ -26,28 +40,26 @@ from device.service.driver_api.DriverFactory import DriverFactory
 from device.service.driver_api.DriverInstanceCache import DriverInstanceCache
 from device.service.drivers import DRIVERS
 from device.tests.MockMonitoringService import MockMonitoringService
-from device.tests.Tools import endpoint, endpoint_id
 from monitoring.Config import (
     GRPC_SERVICE_PORT as MONITORING_GRPC_SERVICE_PORT, GRPC_MAX_WORKERS as MONITORING_GRPC_MAX_WORKERS,
     GRPC_GRACE_PERIOD as MONITORING_GRPC_GRACE_PERIOD)
 from monitoring.client.monitoring_client import MonitoringClient
 from .CommonObjects import CONTEXT, TOPOLOGY
+
 from .Device_Emulated import (
     DEVICE_EMU, DEVICE_EMU_CONFIG_ADDRESSES, DEVICE_EMU_CONFIG_ENDPOINTS, DEVICE_EMU_CONNECT_RULES,
-    DEVICE_EMU_DECONFIG_ADDRESSES, DEVICE_EMU_DECONFIG_ENDPOINTS, DEVICE_EMU_ENDPOINTS, DEVICE_EMU_ENDPOINTS_COOKED,
+    DEVICE_EMU_DECONFIG_ADDRESSES, DEVICE_EMU_DECONFIG_ENDPOINTS, DEVICE_EMU_EP_DESCS, DEVICE_EMU_ENDPOINTS_COOKED,
     DEVICE_EMU_ID, DEVICE_EMU_RECONFIG_ADDRESSES, DEVICE_EMU_UUID)
+ENABLE_EMULATED = True
+
 try:
-    from .Device_OpenConfig_Infinera import(
+    from .Device_OpenConfig_Infinera1 import(
+    #from .Device_OpenConfig_Infinera2 import(
         DEVICE_OC, DEVICE_OC_CONFIG_RULES, DEVICE_OC_DECONFIG_RULES, DEVICE_OC_CONNECT_RULES, DEVICE_OC_ID,
         DEVICE_OC_UUID)
     ENABLE_OPENCONFIG = True
 except ImportError:
     ENABLE_OPENCONFIG = False
-    # Create a Device_OpenConfig_??.py file with the details for your device to test it and import it as follows in the
-    # try block of this import statement.
-    #   from .Device_OpenConfig_?? import(
-    #       DEVICE_OC, DEVICE_OC_CONFIG_RULES, DEVICE_OC_DECONFIG_RULES, DEVICE_OC_CONNECT_RULES, DEVICE_OC_ID,
-    #       DEVICE_OC_UUID)
 
 try:
     from .Device_Transport_Api_CTTC import (
@@ -56,24 +68,29 @@ try:
     ENABLE_TAPI = True
 except ImportError:
     ENABLE_TAPI = False
-    # Create a Device_Transport_Api_??.py file with the details for your device to test it and import it as follows in
-    # the try block of this import statement.
-    #   from .Device_Transport_Api_?? import(
-    #       DEVICE_TAPI, DEVICE_TAPI_CONFIG_RULES, DEVICE_TAPI_DECONFIG_RULES, DEVICE_TAPI_CONNECT_RULES,
-    #       DEVICE_TAPI_ID, DEVICE_TAPI_UUID)
-
-#ENABLE_OPENCONFIG = False
-#ENABLE_TAPI       = False
 
 from .mock_p4runtime_service import MockP4RuntimeService
 try:
     from .device_p4 import(
-        DEVICE_P4, DEVICE_P4_ID, DEVICE_P4_UUID, DEVICE_P4_NAME,
-        DEVICE_P4_ADDRESS, DEVICE_P4_PORT, DEVICE_P4_WORKERS,
-        DEVICE_P4_GRACE_PERIOD, DEVICE_P4_CONNECT_RULES,
-        DEVICE_P4_CONFIG_RULES)
+        DEVICE_P4, DEVICE_P4_ID, DEVICE_P4_UUID, DEVICE_P4_NAME, DEVICE_P4_ADDRESS, DEVICE_P4_PORT, DEVICE_P4_WORKERS,
+        DEVICE_P4_GRACE_PERIOD, DEVICE_P4_CONNECT_RULES, DEVICE_P4_CONFIG_RULES)
+    ENABLE_P4 = True
 except ImportError:
-    raise ImportError("Test configuration for P4 devices not found")
+    ENABLE_P4 = False
+
+#ENABLE_EMULATED   = False # set to False to disable tests of Emulated devices
+#ENABLE_OPENCONFIG = False # set to False to disable tests of OpenConfig devices
+#ENABLE_TAPI       = False # set to False to disable tests of TAPI devices
+ENABLE_P4         = False # set to False to disable tests of P4 devices (P4 device not available in GitLab)
+
+ENABLE_OPENCONFIG_CONFIGURE   = True
+ENABLE_OPENCONFIG_MONITOR     = True
+ENABLE_OPENCONFIG_DECONFIGURE = True
+
+
+logging.getLogger('apscheduler.executors.default').setLevel(logging.WARNING)
+logging.getLogger('apscheduler.scheduler').setLevel(logging.WARNING)
+logging.getLogger('monitoring-client').setLevel(logging.WARNING)
 
 LOGGER = logging.getLogger(__name__)
 LOGGER.setLevel(logging.DEBUG)
@@ -167,10 +184,6 @@ def p4runtime_service():
     yield _service
     _service.stop()
 
-def grpc_message_to_json_string(message):
-    return str(MessageToDict(
-        message, including_default_value_fields=True, preserving_proto_field_name=True, use_integers_for_enums=False))
-
 
 def test_prepare_environment(
     context_client : ContextClient,     # pylint: disable=redefined-outer-name
@@ -191,9 +204,11 @@ def test_device_emulated_add_error_cases(
     device_client : DeviceClient,       # pylint: disable=redefined-outer-name
     device_service : DeviceService):    # pylint: disable=redefined-outer-name
 
+    if not ENABLE_EMULATED: pytest.skip('Skipping test: No Emulated device has been configured')
+
     with pytest.raises(grpc.RpcError) as e:
         DEVICE_EMU_WITH_ENDPOINTS = copy.deepcopy(DEVICE_EMU)
-        DEVICE_EMU_WITH_ENDPOINTS['device_endpoints'].append(endpoint(DEVICE_EMU_ID, 'ep-id', 'ep-type'))
+        DEVICE_EMU_WITH_ENDPOINTS['device_endpoints'].append(json_endpoint(DEVICE_EMU_ID, 'ep-id', 'ep-type'))
         device_client.AddDevice(Device(**DEVICE_EMU_WITH_ENDPOINTS))
     assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
     msg_head = 'device.device_endpoints(['
@@ -220,6 +235,8 @@ def test_device_emulated_add_correct(
     device_client : DeviceClient,       # pylint: disable=redefined-outer-name
     device_service : DeviceService):    # pylint: disable=redefined-outer-name
 
+    if not ENABLE_EMULATED: pytest.skip('Skipping test: No Emulated device has been configured')
+
     DEVICE_EMU_WITH_CONNECT_RULES = copy.deepcopy(DEVICE_EMU)
     DEVICE_EMU_WITH_CONNECT_RULES['device_config']['config_rules'].extend(DEVICE_EMU_CONNECT_RULES)
     device_client.AddDevice(Device(**DEVICE_EMU_WITH_CONNECT_RULES))
@@ -232,6 +249,8 @@ def test_device_emulated_get(
     device_client : DeviceClient,       # pylint: disable=redefined-outer-name
     device_service : DeviceService):    # pylint: disable=redefined-outer-name
 
+    if not ENABLE_EMULATED: pytest.skip('Skipping test: No Emulated device has been configured')
+
     initial_config = device_client.GetInitialConfig(DeviceId(**DEVICE_EMU_ID))
     LOGGER.info('initial_config = {:s}'.format(grpc_message_to_json_string(initial_config)))
 
@@ -244,6 +263,8 @@ def test_device_emulated_configure(
     device_client : DeviceClient,       # pylint: disable=redefined-outer-name
     device_service : DeviceService):    # pylint: disable=redefined-outer-name
 
+    if not ENABLE_EMULATED: pytest.skip('Skipping test: No Emulated device has been configured')
+
     driver : _Driver = device_service.driver_instance_cache.get(DEVICE_EMU_UUID) # we know the driver exists now
     assert driver is not None
 
@@ -266,7 +287,6 @@ def test_device_emulated_configure(
         DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_ENABLED
     device_client.ConfigureDevice(Device(**DEVICE_EMU_WITH_OPERATIONAL_STATUS))
 
-
     driver_config = sorted(driver.GetConfig(), key=operator.itemgetter(0))
     #LOGGER.info('driver_config = {:s}'.format(str(driver_config)))
     assert len(driver_config) == len(DEVICE_EMU_ENDPOINTS_COOKED) + len(DEVICE_EMU_CONFIG_ADDRESSES)
@@ -351,33 +371,38 @@ def test_device_emulated_monitor(
     device_service : DeviceService,                 # pylint: disable=redefined-outer-name
     monitoring_service : MockMonitoringService):    # pylint: disable=redefined-outer-name
 
-    #device_data = context_client.GetDevice(DeviceId(**DEVICE_EMU_ID))
+    if not ENABLE_EMULATED: pytest.skip('Skipping test: No Emulated device has been configured')
+
+    device_uuid = DEVICE_EMU_UUID
+    json_device_id = DEVICE_EMU_ID
+    device_id = DeviceId(**json_device_id)
+    device_data = context_client.GetDevice(device_id)
     #LOGGER.info('device_data = \n{:s}'.format(str(device_data)))
 
-    driver : _Driver = device_service.driver_instance_cache.get(DEVICE_EMU_UUID) # we know the driver exists now
+    driver : _Driver = device_service.driver_instance_cache.get(device_uuid) # we know the driver exists now
     assert driver is not None
-    driver_config = sorted(driver.GetConfig(), key=operator.itemgetter(0))
+    #driver_config = sorted(driver.GetConfig(), key=operator.itemgetter(0))
     #LOGGER.info('driver_config = {:s}'.format(str(driver_config)))
-    assert len(driver_config) == len(DEVICE_EMU_ENDPOINTS_COOKED) + len(DEVICE_EMU_CONFIG_ADDRESSES)
+    #assert len(driver_config) == len(DEVICE_EMU_ENDPOINTS_COOKED) + len(DEVICE_EMU_CONFIG_ADDRESSES)
 
-    SAMPLING_DURATION_SEC = 3.0
-    SAMPLING_INTERVAL_SEC = 0.5
+    SAMPLING_DURATION_SEC = 10.0
+    SAMPLING_INTERVAL_SEC = 2.0
 
     MONITORING_SETTINGS_LIST = []
     KPI_UUIDS__TO__NUM_SAMPLES_RECEIVED = {}
-    for endpoint_uuid,_,sample_types in DEVICE_EMU_ENDPOINTS:
-        for sample_type in sample_types:
-            sample_type_id = sample_type.value
+    for endpoint in device_data.device_endpoints:
+        endpoint_uuid = endpoint.endpoint_id.endpoint_uuid.uuid
+        for sample_type_id in endpoint.kpi_sample_types:
             sample_type_name = str(KpiSampleType.Name(sample_type_id)).upper().replace('KPISAMPLETYPE_', '')
-            kpi_uuid = '{:s}-{:s}-{:s}-kpi_uuid'.format(DEVICE_EMU_UUID, endpoint_uuid, str(sample_type_id))
+            kpi_uuid = '{:s}-{:s}-{:s}-kpi_uuid'.format(device_uuid, endpoint_uuid, str(sample_type_id))
             monitoring_settings = {
                 'kpi_id'        : {'kpi_id': {'uuid': kpi_uuid}},
                 'kpi_descriptor': {
                     'kpi_description': 'Metric {:s} for Endpoint {:s} in Device {:s}'.format(
-                        sample_type_name, endpoint_uuid, DEVICE_EMU_UUID),
+                        sample_type_name, endpoint_uuid, device_uuid),
                     'kpi_sample_type': sample_type_id,
-                    'device_id': DEVICE_EMU_ID,
-                    'endpoint_id': endpoint_id(DEVICE_EMU_ID, endpoint_uuid),
+                    'device_id': json_device_id,
+                    'endpoint_id': json_endpoint_id(json_device_id, endpoint_uuid),
                 },
                 'sampling_duration_s': SAMPLING_DURATION_SEC,
                 'sampling_interval_s': SAMPLING_INTERVAL_SEC,
@@ -396,29 +421,36 @@ def test_device_emulated_monitor(
     # wait to receive the expected number of samples
     # if takes more than 1.5 times the sampling duration, assume there is an error
     time_ini = time.time()
-    queue_samples : Queue = monitoring_service.queue_samples
+    queue_samples : queue.Queue = monitoring_service.queue_samples
     received_samples = []
     while (len(received_samples) < NUM_SAMPLES_EXPECTED) and (time.time() - time_ini < SAMPLING_DURATION_SEC * 1.5):
         try:
             received_sample = queue_samples.get(block=True, timeout=SAMPLING_INTERVAL_SEC / NUM_SAMPLES_EXPECTED)
             #LOGGER.info('received_sample = {:s}'.format(str(received_sample)))
             received_samples.append(received_sample)
-        except Empty:
+        except queue.Empty:
             continue
 
     t_end_monitoring = datetime.timestamp(datetime.utcnow())
 
-    LOGGER.info('received_samples = {:s}'.format(str(received_samples)))
+    #LOGGER.info('received_samples = {:s}'.format(str(received_samples)))
+    LOGGER.info('len(received_samples) = {:s}'.format(str(len(received_samples))))
+    LOGGER.info('NUM_SAMPLES_EXPECTED = {:s}'.format(str(NUM_SAMPLES_EXPECTED)))
     assert len(received_samples) == NUM_SAMPLES_EXPECTED
     for received_sample in received_samples:
         kpi_uuid = received_sample.kpi_id.kpi_id.uuid
         assert kpi_uuid in KPI_UUIDS__TO__NUM_SAMPLES_RECEIVED
         assert isinstance(received_sample.timestamp, str)
-        timestamp = float(received_sample.timestamp)
+        try:
+            timestamp = float(received_sample.timestamp)
+        except ValueError:
+            dt_time = dateutil.parser.isoparse(received_sample.timestamp).replace(tzinfo=timezone.utc)
+            timestamp = float(calendar.timegm(dt_time.timetuple())) + (dt_time.microsecond / 1.e6)
         assert timestamp > t_start_monitoring
         assert timestamp < t_end_monitoring
-        assert received_sample.kpi_value.HasField('floatVal')
-        assert isinstance(received_sample.kpi_value.floatVal, float)
+        assert received_sample.kpi_value.HasField('floatVal') or received_sample.kpi_value.HasField('intVal')
+        kpi_value = getattr(received_sample.kpi_value, received_sample.kpi_value.WhichOneof('value'))
+        assert isinstance(kpi_value, (float, int))
         KPI_UUIDS__TO__NUM_SAMPLES_RECEIVED[kpi_uuid] += 1
 
     LOGGER.info('KPI_UUIDS__TO__NUM_SAMPLES_RECEIVED = {:s}'.format(str(KPI_UUIDS__TO__NUM_SAMPLES_RECEIVED)))
@@ -440,6 +472,8 @@ def test_device_emulated_deconfigure(
     device_client : DeviceClient,       # pylint: disable=redefined-outer-name
     device_service : DeviceService):    # pylint: disable=redefined-outer-name
 
+    if not ENABLE_EMULATED: pytest.skip('Skipping test: No Emulated device has been configured')
+
     driver : _Driver = device_service.driver_instance_cache.get(DEVICE_EMU_UUID) # we know the driver exists now
     assert driver is not None
 
@@ -460,9 +494,13 @@ def test_device_emulated_deconfigure(
     RESULTING_CONFIG_RULES = RESULTING_CONFIG_RULES.values()
     driver_config = sorted(driver.GetConfig(), key=operator.itemgetter(0))
     driver_config = json.loads(json.dumps(driver_config)) # prevent integer keys to fail matching with string keys
-    #LOGGER.info('driver_config = {:s}'.format(str(driver_config)))
+    driver_config = list(filter(
+        lambda config_rule: (
+            not isinstance(config_rule[1], str) or not config_rule[1].startswith('do_sampling (trigger:')),
+        driver_config))
+    LOGGER.info('driver_config = {:s}'.format(str(driver_config)))
+    LOGGER.info('RESULTING_CONFIG_RULES = {:s}'.format(str(RESULTING_CONFIG_RULES)))
     assert len(driver_config) == len(RESULTING_CONFIG_RULES)
-    #LOGGER.info('RESULTING_CONFIG_RULES = {:s}'.format(str(RESULTING_CONFIG_RULES)))
     for config_rule in RESULTING_CONFIG_RULES:
         config_rule = [config_rule['resource_key'], json.loads(config_rule['resource_value'])]
         #LOGGER.info('config_rule = {:s}'.format(str(config_rule)))
@@ -478,7 +516,16 @@ def test_device_emulated_deconfigure(
     assert len(driver_config) == 0
 
     device_data = context_client.GetDevice(DeviceId(**DEVICE_EMU_ID))
-    assert len(device_data.device_config.config_rules) == 0
+    config_rules = device_data.device_config.config_rules
+    LOGGER.info('config_rules = {:s}'.format(str(config_rules)))
+    clean_config_rules = []
+    for config_rule in config_rules:
+        config_rule_value = json.loads(config_rule.resource_value)
+        if not isinstance(config_rule_value, str): clean_config_rules.append(config_rule)
+        if config_rule_value.startswith('do_sampling (trigger:'): continue
+        clean_config_rules.append(config_rule)
+    LOGGER.info('clean_config_rules = {:s}'.format(str(clean_config_rules)))
+    assert len(clean_config_rules) == 0
 
 
 def test_device_emulated_delete(
@@ -486,6 +533,8 @@ def test_device_emulated_delete(
     device_client : DeviceClient,       # pylint: disable=redefined-outer-name
     device_service : DeviceService):    # pylint: disable=redefined-outer-name
 
+    if not ENABLE_EMULATED: pytest.skip('Skipping test: No Emulated device has been configured')
+
     device_client.DeleteDevice(DeviceId(**DEVICE_EMU_ID))
     driver : _Driver = device_service.driver_instance_cache.get(DEVICE_EMU_UUID, {})
     assert driver is None
@@ -498,7 +547,7 @@ def test_device_openconfig_add_error_cases(
     device_client : DeviceClient,       # pylint: disable=redefined-outer-name
     device_service : DeviceService):    # pylint: disable=redefined-outer-name
 
-    if not ENABLE_OPENCONFIG: return # if there is no device to test against, asusme test is silently passed.
+    if not ENABLE_OPENCONFIG: pytest.skip('Skipping test: No OpenConfig device has been configured')
 
     with pytest.raises(grpc.RpcError) as e:
         DEVICE_OC_WITH_EXTRA_RULES = copy.deepcopy(DEVICE_OC)
@@ -518,7 +567,7 @@ def test_device_openconfig_add_correct(
     device_client : DeviceClient,       # pylint: disable=redefined-outer-name
     device_service : DeviceService):    # pylint: disable=redefined-outer-name
 
-    if not ENABLE_OPENCONFIG: return # if there is no device to test against, asusme test is silently passed.
+    if not ENABLE_OPENCONFIG: pytest.skip('Skipping test: No OpenConfig device has been configured')
 
     DEVICE_OC_WITH_CONNECT_RULES = copy.deepcopy(DEVICE_OC)
     DEVICE_OC_WITH_CONNECT_RULES['device_config']['config_rules'].extend(DEVICE_OC_CONNECT_RULES)
@@ -526,13 +575,21 @@ def test_device_openconfig_add_correct(
     driver : _Driver = device_service.driver_instance_cache.get(DEVICE_OC_UUID) # we know the driver exists now
     assert driver is not None
 
+    device_data = context_client.GetDevice(DeviceId(**DEVICE_OC_ID))
+    config_rules = [
+        (ConfigActionEnum.Name(config_rule.action), config_rule.resource_key, config_rule.resource_value)
+        for config_rule in device_data.device_config.config_rules
+    ]
+    LOGGER.info('device_data.device_config.config_rules = \n{:s}'.format(
+        '\n'.join(['{:s} {:s} = {:s}'.format(*config_rule) for config_rule in config_rules])))
+
 
 def test_device_openconfig_get(
     context_client : ContextClient,     # pylint: disable=redefined-outer-name
     device_client : DeviceClient,       # pylint: disable=redefined-outer-name
     device_service : DeviceService):    # pylint: disable=redefined-outer-name
 
-    if not ENABLE_OPENCONFIG: return # if there is no device to test against, asusme test is silently passed.
+    if not ENABLE_OPENCONFIG: pytest.skip('Skipping test: No OpenConfig device has been configured')
 
     initial_config = device_client.GetInitialConfig(DeviceId(**DEVICE_OC_ID))
     LOGGER.info('initial_config = {:s}'.format(grpc_message_to_json_string(initial_config)))
@@ -546,7 +603,8 @@ def test_device_openconfig_configure(
     device_client : DeviceClient,       # pylint: disable=redefined-outer-name
     device_service : DeviceService):    # pylint: disable=redefined-outer-name
 
-    if not ENABLE_OPENCONFIG: return # if there is no device to test against, asusme test is silently passed.
+    if not ENABLE_OPENCONFIG: pytest.skip('Skipping test: No OpenConfig device has been configured')
+    if not ENABLE_OPENCONFIG_CONFIGURE: pytest.skip('Skipping test OpenConfig configure')
 
     driver : _Driver = device_service.driver_instance_cache.get(DEVICE_OC_UUID) # we know the driver exists now
     assert driver is not None
@@ -576,12 +634,114 @@ def test_device_openconfig_configure(
         assert config_rule in config_rules
 
 
+def test_device_openconfig_monitor(
+    context_client : ContextClient,                 # pylint: disable=redefined-outer-name
+    device_client : DeviceClient,                   # pylint: disable=redefined-outer-name
+    device_service : DeviceService,                 # pylint: disable=redefined-outer-name
+    monitoring_service : MockMonitoringService):    # pylint: disable=redefined-outer-name
+
+    if not ENABLE_OPENCONFIG: pytest.skip('Skipping test: No OpenConfig device has been configured')
+    if not ENABLE_OPENCONFIG_MONITOR: pytest.skip('Skipping test OpenConfig monitor')
+
+    device_uuid = DEVICE_OC_UUID
+    json_device_id = DEVICE_OC_ID
+    device_id = DeviceId(**json_device_id)
+    device_data = context_client.GetDevice(device_id)
+    #LOGGER.info('device_data = \n{:s}'.format(str(device_data)))
+
+    driver : _Driver = device_service.driver_instance_cache.get(device_uuid) # we know the driver exists now
+    assert driver is not None
+
+    SAMPLING_DURATION_SEC = 60.0
+    SAMPLING_INTERVAL_SEC = 15.0
+
+    MONITORING_SETTINGS_LIST = []
+    KPI_UUIDS__TO__NUM_SAMPLES_RECEIVED = {}
+    for endpoint in device_data.device_endpoints:
+        endpoint_uuid = endpoint.endpoint_id.endpoint_uuid.uuid
+        for sample_type_id in endpoint.kpi_sample_types:
+            sample_type_name = str(KpiSampleType.Name(sample_type_id)).upper().replace('KPISAMPLETYPE_', '')
+            kpi_uuid = '{:s}-{:s}-{:s}-kpi_uuid'.format(device_uuid, endpoint_uuid, str(sample_type_id))
+            monitoring_settings = {
+                'kpi_id'        : {'kpi_id': {'uuid': kpi_uuid}},
+                'kpi_descriptor': {
+                    'kpi_description': 'Metric {:s} for Endpoint {:s} in Device {:s}'.format(
+                        sample_type_name, endpoint_uuid, device_uuid),
+                    'kpi_sample_type': sample_type_id,
+                    'device_id': json_device_id,
+                    'endpoint_id': json_endpoint_id(json_device_id, endpoint_uuid),
+                },
+                'sampling_duration_s': SAMPLING_DURATION_SEC,
+                'sampling_interval_s': SAMPLING_INTERVAL_SEC,
+            }
+            MONITORING_SETTINGS_LIST.append(monitoring_settings)
+            KPI_UUIDS__TO__NUM_SAMPLES_RECEIVED[kpi_uuid] = 0
+
+    NUM_SAMPLES_EXPECTED_PER_KPI = SAMPLING_DURATION_SEC / SAMPLING_INTERVAL_SEC
+    NUM_SAMPLES_EXPECTED = len(MONITORING_SETTINGS_LIST) * NUM_SAMPLES_EXPECTED_PER_KPI
+
+    # Start monitoring the device
+    t_start_monitoring = datetime.timestamp(datetime.utcnow())
+    for monitoring_settings in MONITORING_SETTINGS_LIST:
+        device_client.MonitorDeviceKpi(MonitoringSettings(**monitoring_settings))
+
+    # wait to receive the expected number of samples
+    # if takes more than 1.5 times the sampling duration, assume there is an error
+    time_ini = time.time()
+    queue_samples : queue.Queue = monitoring_service.queue_samples
+    received_samples = []
+    while (len(received_samples) < NUM_SAMPLES_EXPECTED) and (time.time() - time_ini < SAMPLING_DURATION_SEC * 1.5):
+        try:
+            received_sample = queue_samples.get(block=True, timeout=SAMPLING_INTERVAL_SEC / NUM_SAMPLES_EXPECTED)
+            #LOGGER.info('received_sample = {:s}'.format(str(received_sample)))
+            received_samples.append(received_sample)
+        except queue.Empty:
+            continue
+
+    t_end_monitoring = datetime.timestamp(datetime.utcnow())
+
+    #LOGGER.info('received_samples = {:s}'.format(str(received_samples)))
+    LOGGER.info('len(received_samples) = {:s}'.format(str(len(received_samples))))
+    LOGGER.info('NUM_SAMPLES_EXPECTED = {:s}'.format(str(NUM_SAMPLES_EXPECTED)))
+    #assert len(received_samples) == NUM_SAMPLES_EXPECTED
+    for received_sample in received_samples:
+        kpi_uuid = received_sample.kpi_id.kpi_id.uuid
+        assert kpi_uuid in KPI_UUIDS__TO__NUM_SAMPLES_RECEIVED
+        assert isinstance(received_sample.timestamp, str)
+        try:
+            timestamp = float(received_sample.timestamp)
+        except ValueError:
+            dt_time = dateutil.parser.isoparse(received_sample.timestamp).replace(tzinfo=timezone.utc)
+            timestamp = float(calendar.timegm(dt_time.timetuple())) + (dt_time.microsecond / 1.e6)
+        assert timestamp > t_start_monitoring
+        assert timestamp < t_end_monitoring
+        assert received_sample.kpi_value.HasField('floatVal') or received_sample.kpi_value.HasField('intVal')
+        kpi_value = getattr(received_sample.kpi_value, received_sample.kpi_value.WhichOneof('value'))
+        assert isinstance(kpi_value, (float, int))
+        KPI_UUIDS__TO__NUM_SAMPLES_RECEIVED[kpi_uuid] += 1
+
+    LOGGER.info('KPI_UUIDS__TO__NUM_SAMPLES_RECEIVED = {:s}'.format(str(KPI_UUIDS__TO__NUM_SAMPLES_RECEIVED)))
+    # TODO: review why num_samples_received per KPI != NUM_SAMPLES_EXPECTED_PER_KPI
+    #for kpi_uuid, num_samples_received in KPI_UUIDS__TO__NUM_SAMPLES_RECEIVED.items():
+    #    assert num_samples_received == NUM_SAMPLES_EXPECTED_PER_KPI
+
+    # Unsubscribe monitoring
+    for kpi_uuid in KPI_UUIDS__TO__NUM_SAMPLES_RECEIVED.keys():
+        MONITORING_SETTINGS_UNSUBSCRIBE = {
+            'kpi_id'             : {'kpi_id': {'uuid': kpi_uuid}},
+            'sampling_duration_s': -1, # negative value in sampling_duration_s or sampling_interval_s means unsibscribe
+            'sampling_interval_s': -1, # kpi_id is mandatory to unsibscribe
+        }
+        device_client.MonitorDeviceKpi(MonitoringSettings(**MONITORING_SETTINGS_UNSUBSCRIBE))
+
+
 def test_device_openconfig_deconfigure(
     context_client : ContextClient,     # pylint: disable=redefined-outer-name
     device_client : DeviceClient,       # pylint: disable=redefined-outer-name
     device_service : DeviceService):    # pylint: disable=redefined-outer-name
 
-    if not ENABLE_OPENCONFIG: return # if there is no device to test against, asusme test is silently passed.
+    if not ENABLE_OPENCONFIG: pytest.skip('Skipping test: No OpenConfig device has been configured')
+    if not ENABLE_OPENCONFIG_DECONFIGURE: pytest.skip('Skipping test OpenConfig deconfigure')
 
     driver : _Driver = device_service.driver_instance_cache.get(DEVICE_OC_UUID) # we know the driver exists now
     assert driver is not None
@@ -616,7 +776,7 @@ def test_device_openconfig_delete(
     device_client : DeviceClient,       # pylint: disable=redefined-outer-name
     device_service : DeviceService):    # pylint: disable=redefined-outer-name
 
-    if not ENABLE_OPENCONFIG: return # if there is no device to test against, asusme test is silently passed.
+    if not ENABLE_OPENCONFIG: pytest.skip('Skipping test: No OpenConfig device has been configured')
 
     device_client.DeleteDevice(DeviceId(**DEVICE_OC_ID))
     driver : _Driver = device_service.driver_instance_cache.get(DEVICE_OC_UUID, {})
@@ -628,7 +788,7 @@ def test_device_openconfig_delete(
 def test_device_tapi_add_error_cases(
     device_client : DeviceClient):      # pylint: disable=redefined-outer-name
 
-    if not ENABLE_TAPI: return # if there is no device to test against, asusme test is silently passed.
+    if not ENABLE_TAPI: pytest.skip('Skipping test: No TAPI device has been configured')
 
     with pytest.raises(grpc.RpcError) as e:
         DEVICE_TAPI_WITH_EXTRA_RULES = copy.deepcopy(DEVICE_TAPI)
@@ -647,7 +807,7 @@ def test_device_tapi_add_correct(
     device_client: DeviceClient,        # pylint: disable=redefined-outer-name
     device_service: DeviceService):     # pylint: disable=redefined-outer-name
 
-    if not ENABLE_TAPI: return # if there is no device to test against, asusme test is silently passed.
+    if not ENABLE_TAPI: pytest.skip('Skipping test: No TAPI device has been configured')
 
     DEVICE_TAPI_WITH_CONNECT_RULES = copy.deepcopy(DEVICE_TAPI)
     DEVICE_TAPI_WITH_CONNECT_RULES['device_config']['config_rules'].extend(DEVICE_TAPI_CONNECT_RULES)
@@ -660,7 +820,7 @@ def test_device_tapi_get(
     context_client: ContextClient,      # pylint: disable=redefined-outer-name
     device_client: DeviceClient):       # pylint: disable=redefined-outer-name
 
-    if not ENABLE_TAPI: return # if there is no device to test against, asusme test is silently passed.
+    if not ENABLE_TAPI: pytest.skip('Skipping test: No TAPI device has been configured')
 
     initial_config = device_client.GetInitialConfig(DeviceId(**DEVICE_TAPI_ID))
     LOGGER.info('initial_config = {:s}'.format(grpc_message_to_json_string(initial_config)))
@@ -674,7 +834,7 @@ def test_device_tapi_configure(
     device_client: DeviceClient,        # pylint: disable=redefined-outer-name
     device_service: DeviceService):     # pylint: disable=redefined-outer-name
 
-    if not ENABLE_TAPI: return # if there is no device to test against, asusme test is silently passed.
+    if not ENABLE_TAPI: pytest.skip('Skipping test: No TAPI device has been configured')
 
     driver : _Driver = device_service.driver_instance_cache.get(DEVICE_TAPI_UUID)
     assert driver is not None
@@ -709,7 +869,7 @@ def test_device_tapi_deconfigure(
     device_client: DeviceClient,        # pylint: disable=redefined-outer-name
     device_service: DeviceService):     # pylint: disable=redefined-outer-name
 
-    if not ENABLE_TAPI: return # if there is no device to test against, asusme test is silently passed.
+    if not ENABLE_TAPI: pytest.skip('Skipping test: No TAPI device has been configured')
 
     driver: _Driver = device_service.driver_instance_cache.get(DEVICE_TAPI_UUID)
     assert driver is not None
@@ -743,7 +903,7 @@ def test_device_tapi_delete(
     device_client : DeviceClient,       # pylint: disable=redefined-outer-name
     device_service : DeviceService):    # pylint: disable=redefined-outer-name
 
-    if not ENABLE_TAPI: return # if there is no device to test against, asusme test is silently passed.
+    if not ENABLE_TAPI: pytest.skip('Skipping test: No TAPI device has been configured')
 
     device_client.DeleteDevice(DeviceId(**DEVICE_TAPI_ID))
     driver : _Driver = device_service.driver_instance_cache.get(DEVICE_TAPI_UUID, {})
@@ -757,6 +917,8 @@ def test_device_p4_add_error_cases(
         device_client: DeviceClient,     # pylint: disable=redefined-outer-name
         device_service: DeviceService):  # pylint: disable=redefined-outer-name
 
+    if not ENABLE_P4: pytest.skip('Skipping test: No P4 device has been configured')
+
     with pytest.raises(grpc.RpcError) as e:
         device_p4_with_extra_rules = copy.deepcopy(DEVICE_P4)
         device_p4_with_extra_rules['device_config']['config_rules'].extend(
@@ -773,10 +935,12 @@ def test_device_p4_add_error_cases(
 
 
 def test_device_p4_add_correct(
-        context_client: ContextClient,   # pylint: disable=redefined-outer-name
-        device_client: DeviceClient,     # pylint: disable=redefined-outer-name
-        device_service: DeviceService,   # pylint: disable=redefined-outer-name
-        p4runtime_service: MockP4RuntimeService):
+        context_client: ContextClient,              # pylint: disable=redefined-outer-name
+        device_client: DeviceClient,                # pylint: disable=redefined-outer-name
+        device_service: DeviceService,              # pylint: disable=redefined-outer-name
+        p4runtime_service: MockP4RuntimeService):   # pylint: disable=redefined-outer-name
+
+    if not ENABLE_P4: pytest.skip('Skipping test: No P4 device has been configured')
 
     device_p4_with_connect_rules = copy.deepcopy(DEVICE_P4)
     device_p4_with_connect_rules['device_config']['config_rules'].extend(
@@ -787,10 +951,12 @@ def test_device_p4_add_correct(
 
 
 def test_device_p4_get(
-        context_client: ContextClient,   # pylint: disable=redefined-outer-name
-        device_client: DeviceClient,     # pylint: disable=redefined-outer-name
-        device_service: DeviceService,   # pylint: disable=redefined-outer-name
-        p4runtime_service: MockP4RuntimeService):
+        context_client: ContextClient,              # pylint: disable=redefined-outer-name
+        device_client: DeviceClient,                # pylint: disable=redefined-outer-name
+        device_service: DeviceService,              # pylint: disable=redefined-outer-name
+        p4runtime_service: MockP4RuntimeService):   # pylint: disable=redefined-outer-name
+
+    if not ENABLE_P4: pytest.skip('Skipping test: No P4 device has been configured')
 
     initial_config = device_client.GetInitialConfig(DeviceId(**DEVICE_P4_UUID))
     LOGGER.info('initial_config = {:s}'.format(
@@ -802,26 +968,28 @@ def test_device_p4_get(
 
 
 def test_device_p4_configure(
-        context_client: ContextClient,   # pylint: disable=redefined-outer-name
-        device_client: DeviceClient,     # pylint: disable=redefined-outer-name
-        device_service: DeviceService,   # pylint: disable=redefined-outer-name
-        p4runtime_service: MockP4RuntimeService):
-    pytest.skip("Skipping test for unimplemented method")
+        context_client: ContextClient,              # pylint: disable=redefined-outer-name
+        device_client: DeviceClient,                # pylint: disable=redefined-outer-name
+        device_service: DeviceService,              # pylint: disable=redefined-outer-name
+        p4runtime_service: MockP4RuntimeService):   # pylint: disable=redefined-outer-name
+    pytest.skip('Skipping test for unimplemented method')
 
 
 def test_device_p4_deconfigure(
-        context_client: ContextClient,   # pylint: disable=redefined-outer-name
-        device_client: DeviceClient,     # pylint: disable=redefined-outer-name
-        device_service: DeviceService,   # pylint: disable=redefined-outer-name
-        p4runtime_service: MockP4RuntimeService):
-    pytest.skip("Skipping test for unimplemented method")
+        context_client: ContextClient,              # pylint: disable=redefined-outer-name
+        device_client: DeviceClient,                # pylint: disable=redefined-outer-name
+        device_service: DeviceService,              # pylint: disable=redefined-outer-name
+        p4runtime_service: MockP4RuntimeService):   # pylint: disable=redefined-outer-name
+    pytest.skip('Skipping test for unimplemented method')
 
 
 def test_device_p4_delete(
-        context_client: ContextClient,   # pylint: disable=redefined-outer-name
-        device_client: DeviceClient,     # pylint: disable=redefined-outer-name
-        device_service: DeviceService,   # pylint: disable=redefined-outer-name
-        p4runtime_service: MockP4RuntimeService):
+        context_client: ContextClient,              # pylint: disable=redefined-outer-name
+        device_client: DeviceClient,                # pylint: disable=redefined-outer-name
+        device_service: DeviceService,              # pylint: disable=redefined-outer-name
+        p4runtime_service: MockP4RuntimeService):   # pylint: disable=redefined-outer-name
+
+    if not ENABLE_P4: pytest.skip('Skipping test: No P4 device has been configured')
 
     device_client.DeleteDevice(DeviceId(**DEVICE_P4_UUID))
     driver : _Driver = device_service.driver_instance_cache.get(DEVICE_P4_NAME)
diff --git a/src/gitlab-ci.yml_generator.py b/src/gitlab-ci.yml_generator.py
index 00739e305159ef7b6b0733203434d190d61ad840..8e367eb02b85b74a38932a2bb57004712d7fb883 100755
--- a/src/gitlab-ci.yml_generator.py
+++ b/src/gitlab-ci.yml_generator.py
@@ -1,4 +1,18 @@
 #!/usr/bin/python
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import argparse
 import os.path
 
diff --git a/src/l3_attackmitigator/.gitlab-ci.yml b/src/l3_attackmitigator/.gitlab-ci.yml
index 1fb3d88ee50ab2f330049f737fc455ac05aa8375..dbf8d90fcbcb38a0574de4130b4181e77cf5afd6 100644
--- a/src/l3_attackmitigator/.gitlab-ci.yml
+++ b/src/l3_attackmitigator/.gitlab-ci.yml
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 # Build, tag, and push the Docker images to the GitLab Docker registry
 build l3_attackmitigator:
   variables:
@@ -36,11 +50,14 @@ unit test l3_attackmitigator:
     - 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 10002:10002 --network=teraflowbridge $CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG
+    - docker run --name $IMAGE_NAME -d -p 10002:10002 -v "$PWD/src/$IMAGE_NAME/tests:/opt/results" --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"
+    - docker exec -i $IMAGE_NAME bash -c "coverage run -m pytest --log-level=INFO --verbose $IMAGE_NAME/tests/test_unitary.py --junitxml=/opt/results/${IMAGE_NAME}_report.xml"
+    - docker exec -i $IMAGE_NAME bash -c "coverage xml -o /opt/results/${IMAGE_NAME}_coverage.xml"
+    - docker exec -i $IMAGE_NAME bash -c "coverage report --include='${IMAGE_NAME}/*' --show-missing"
+  coverage: '/TOTAL\s+\d+\s+\d+\s+(\d+%)/'
   after_script:
     - docker rm -f $IMAGE_NAME
     - docker network rm teraflowbridge
@@ -54,6 +71,11 @@ unit test l3_attackmitigator:
       - src/$IMAGE_NAME/tests/Dockerfile
       - manifests/${IMAGE_NAME}service.yaml
       - .gitlab-ci.yml
+  artifacts:
+      when: always
+      reports:
+        junit: src/$IMAGE_NAME/tests/${IMAGE_NAME}_report.xml
+        cobertura: src/$IMAGE_NAME/tests/${IMAGE_NAME}_coverage.xml
 
 # Deployment of the service in Kubernetes Cluster
 deploy l3_attackmitigator:
diff --git a/src/l3_attackmitigator/Config.py b/src/l3_attackmitigator/Config.py
index 1c2d658637fa3285d736a41328dfb64fefab6a80..429977bc585a01fb288aab121cdb3cedb8fa9ac0 100644
--- a/src/l3_attackmitigator/Config.py
+++ b/src/l3_attackmitigator/Config.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging
 
 # General settings
diff --git a/src/l3_attackmitigator/Dockerfile b/src/l3_attackmitigator/Dockerfile
index 0065f273c6831a314c5a5f88802ec40a00c416d1..2ed76a70b6ca0e2a140cb926b3343aab7fed37a5 100644
--- a/src/l3_attackmitigator/Dockerfile
+++ b/src/l3_attackmitigator/Dockerfile
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 FROM python:3-slim
 
 # Install dependencies
diff --git a/src/l3_attackmitigator/__init__.py b/src/l3_attackmitigator/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/l3_attackmitigator/__init__.py
+++ b/src/l3_attackmitigator/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/l3_attackmitigator/client/__init__.py b/src/l3_attackmitigator/client/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/l3_attackmitigator/client/__init__.py
+++ b/src/l3_attackmitigator/client/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/l3_attackmitigator/client/l3_attackmitigatorClient.py b/src/l3_attackmitigator/client/l3_attackmitigatorClient.py
index 190d6388459151e42fdebd0642fb9ccde3621ce0..ba061c15d7f27383aefec63a92b15557d803fb80 100644
--- a/src/l3_attackmitigator/client/l3_attackmitigatorClient.py
+++ b/src/l3_attackmitigator/client/l3_attackmitigatorClient.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import grpc, logging
 from common.tools.client.RetryDecorator import retry, delay_exponential
 from l3_attackmitigator.proto.l3_attackmitigator_pb2_grpc import (
@@ -11,7 +25,7 @@ from l3_attackmitigator.proto.l3_attackmitigator_pb2 import (
 LOGGER = logging.getLogger(__name__)
 MAX_RETRIES = 15
 DELAY_FUNCTION = delay_exponential(initial=0.01, increment=2.0, maximum=5.0)
-
+RETRY_DECORATOR = retry(max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
 
 class l3_attackmitigatorClient:
     def __init__(self, address, port):
@@ -32,14 +46,14 @@ class l3_attackmitigatorClient:
         self.channel = None
         self.stub = None
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def SendOutput(self, request: Output) -> EmptyMitigator:
         LOGGER.debug('SendOutput request: {}'.format(request))
         response = self.stub.SendOutput(request)
         LOGGER.debug('SendOutput result: {}'.format(response))
         return response
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def GetMitigation(self, request: EmptyMitigator) -> EmptyMitigator:
         LOGGER.debug('GetMitigation request: {}'.format(request))
         response = self.stub.GetMitigation(request)
diff --git a/src/l3_attackmitigator/proto/__init__.py b/src/l3_attackmitigator/proto/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/l3_attackmitigator/proto/__init__.py
+++ b/src/l3_attackmitigator/proto/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/l3_attackmitigator/requirements.in b/src/l3_attackmitigator/requirements.in
index e11480b450adad83b398c2a6a21cf8fe1a7b186d..ede9c839d93cc377e93c525bc1e85243576faa00 100644
--- a/src/l3_attackmitigator/requirements.in
+++ b/src/l3_attackmitigator/requirements.in
@@ -6,3 +6,4 @@ pytest
 pytest-benchmark
 numpy
 scikit-learn
+coverage
\ No newline at end of file
diff --git a/src/l3_attackmitigator/service/__init__.py b/src/l3_attackmitigator/service/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/l3_attackmitigator/service/__init__.py
+++ b/src/l3_attackmitigator/service/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/l3_attackmitigator/service/__main__.py b/src/l3_attackmitigator/service/__main__.py
index baa85988bd169e9ce54afcb97689d3f1c9ac4b3a..ac2ff528ae2df5d0604978534a256e111080d181 100644
--- a/src/l3_attackmitigator/service/__main__.py
+++ b/src/l3_attackmitigator/service/__main__.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging, signal, sys, threading, os
 from prometheus_client import start_http_server
 from common.Settings import get_setting
diff --git a/src/l3_attackmitigator/service/l3_attackmitigatorService.py b/src/l3_attackmitigator/service/l3_attackmitigatorService.py
index 9bfd2e06c774ee19f989004015e6ffbf3e035b87..ae52d6285c74e121f6062c42de69597d05c37b45 100644
--- a/src/l3_attackmitigator/service/l3_attackmitigatorService.py
+++ b/src/l3_attackmitigator/service/l3_attackmitigatorService.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import grpc
 import logging
 from concurrent import futures
diff --git a/src/l3_attackmitigator/service/l3_attackmitigatorServiceServicerImpl.py b/src/l3_attackmitigator/service/l3_attackmitigatorServiceServicerImpl.py
index 725dc36b4907579c494845a3e8e594acca31059e..6e74cd455b8a166802f2a1f781451a0aa5e743ca 100644
--- a/src/l3_attackmitigator/service/l3_attackmitigatorServiceServicerImpl.py
+++ b/src/l3_attackmitigator/service/l3_attackmitigatorServiceServicerImpl.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 from __future__ import print_function
 import logging
 from l3_attackmitigator.proto.l3_attackmitigator_pb2 import (
diff --git a/src/l3_attackmitigator/tests/__init__.py b/src/l3_attackmitigator/tests/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/l3_attackmitigator/tests/__init__.py
+++ b/src/l3_attackmitigator/tests/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/l3_attackmitigator/tests/test_unitary.py b/src/l3_attackmitigator/tests/test_unitary.py
index 1aa6fc4a843ae04e4d520507c789c669eefcd31a..7a33cbd8a29c8d64ca468ac366d38bdc7199aacd 100644
--- a/src/l3_attackmitigator/tests/test_unitary.py
+++ b/src/l3_attackmitigator/tests/test_unitary.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging
 import pytest
 from l3_attackmitigator.Config import GRPC_SERVICE_PORT, GRPC_MAX_WORKERS, GRPC_GRACE_PERIOD
diff --git a/src/l3_centralizedattackdetector/.gitlab-ci.yml b/src/l3_centralizedattackdetector/.gitlab-ci.yml
index cd410cb9cdf8df5a7f41db46304192a9ba591443..43ca269d3c8c1456d86717f423c81fafbc5b03af 100644
--- a/src/l3_centralizedattackdetector/.gitlab-ci.yml
+++ b/src/l3_centralizedattackdetector/.gitlab-ci.yml
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 # Build, tag, and push the Docker images to the GitLab Docker registry
 build l3_centralizedattackdetector:
   variables:
@@ -36,11 +50,14 @@ unit test l3_centralizedattackdetector:
     - 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 10001:10001 --network=teraflowbridge $CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG
+    - docker run --name $IMAGE_NAME -d -p 10001:10001 -v "$PWD/src/$IMAGE_NAME/tests:/opt/results" --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"
+    - docker exec -i $IMAGE_NAME bash -c "coverage run -m pytest --log-level=INFO --verbose $IMAGE_NAME/tests/test_unitary.py --junitxml=/opt/results/${IMAGE_NAME}_report.xml"
+    - docker exec -i $IMAGE_NAME bash -c "coverage xml -o /opt/results/${IMAGE_NAME}_coverage.xml"
+    - docker exec -i $IMAGE_NAME bash -c "coverage report --include='${IMAGE_NAME}/*' --show-missing"
+  coverage: '/TOTAL\s+\d+\s+\d+\s+(\d+%)/'
   after_script:
     - docker rm -f $IMAGE_NAME
     - docker network rm teraflowbridge
@@ -54,6 +71,11 @@ unit test l3_centralizedattackdetector:
       - src/$IMAGE_NAME/tests/Dockerfile
       - manifests/${IMAGE_NAME}service.yaml
       - .gitlab-ci.yml
+  artifacts:
+      when: always
+      reports:
+        junit: src/$IMAGE_NAME/tests/${IMAGE_NAME}_report.xml
+        cobertura: src/$IMAGE_NAME/tests/${IMAGE_NAME}_coverage.xml
 
 # Deployment of the service in Kubernetes Cluster
 deploy l3_centralizedattackdetector:
diff --git a/src/l3_centralizedattackdetector/Config.py b/src/l3_centralizedattackdetector/Config.py
index 9c8a5cef5598e811ebf391718ce6e3afd79bd9f3..2b7ec70f13e273c5913607f8b9cd4b6cb59555f7 100644
--- a/src/l3_centralizedattackdetector/Config.py
+++ b/src/l3_centralizedattackdetector/Config.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging
 
 # General settings
diff --git a/src/l3_centralizedattackdetector/Dockerfile b/src/l3_centralizedattackdetector/Dockerfile
index cb29b6987ed1882380782c9c60e327b3bf64fc6f..6a52cda86e9de16213bc058a0ffeeba04f85f6f9 100644
--- a/src/l3_centralizedattackdetector/Dockerfile
+++ b/src/l3_centralizedattackdetector/Dockerfile
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 FROM python:3-slim
 
 # Install dependencies
diff --git a/src/l3_centralizedattackdetector/__init__.py b/src/l3_centralizedattackdetector/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/l3_centralizedattackdetector/__init__.py
+++ b/src/l3_centralizedattackdetector/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/l3_centralizedattackdetector/client/__init__.py b/src/l3_centralizedattackdetector/client/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/l3_centralizedattackdetector/client/__init__.py
+++ b/src/l3_centralizedattackdetector/client/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/l3_centralizedattackdetector/client/l3_centralizedattackdetectorClient.py b/src/l3_centralizedattackdetector/client/l3_centralizedattackdetectorClient.py
index 64b66f3cede33e4511f8eb00a94239d680e55f78..b5181d63e02bcb77ec92e8c53374b38cb4bc72d3 100644
--- a/src/l3_centralizedattackdetector/client/l3_centralizedattackdetectorClient.py
+++ b/src/l3_centralizedattackdetector/client/l3_centralizedattackdetectorClient.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import grpc, logging
 from common.tools.client.RetryDecorator import retry, delay_exponential
 from l3_centralizedattackdetector.proto.l3_centralizedattackdetector_pb2_grpc import (
@@ -12,6 +26,7 @@ from l3_centralizedattackdetector.proto.l3_centralizedattackdetector_pb2 import
 LOGGER = logging.getLogger(__name__)
 MAX_RETRIES = 15
 DELAY_FUNCTION = delay_exponential(initial=0.01, increment=2.0, maximum=5.0)
+RETRY_DECORATOR = retry(max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
 
 class l3_centralizedattackdetectorClient:
     def __init__(self, address, port):
@@ -32,14 +47,14 @@ class l3_centralizedattackdetectorClient:
         self.channel = None
         self.stub = None
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def SendInput(self, request: ModelInput) -> Empty:
         LOGGER.debug('SendInput request: {}'.format(request))
         response = self.stub.SendInput(request)
         LOGGER.debug('SendInput result: {}'.format(response))
         return response
     
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def GetOutput(self, request: Empty) -> ModelOutput:
         LOGGER.debug('GetOutput request: {}'.format(request))
         response = self.stub.GetOutput(request)
diff --git a/src/l3_centralizedattackdetector/proto/__init__.py b/src/l3_centralizedattackdetector/proto/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/l3_centralizedattackdetector/proto/__init__.py
+++ b/src/l3_centralizedattackdetector/proto/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/l3_centralizedattackdetector/requirements.in b/src/l3_centralizedattackdetector/requirements.in
index 31aba06dccdcd151699628fc5162e733a9c768b3..e14d501b5a23d29e4950a56f28ee08fc54568d4d 100644
--- a/src/l3_centralizedattackdetector/requirements.in
+++ b/src/l3_centralizedattackdetector/requirements.in
@@ -7,3 +7,4 @@ pytest-benchmark
 numpy
 scikit-learn
 onnxruntime
+coverage
\ No newline at end of file
diff --git a/src/l3_centralizedattackdetector/service/__init__.py b/src/l3_centralizedattackdetector/service/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/l3_centralizedattackdetector/service/__init__.py
+++ b/src/l3_centralizedattackdetector/service/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/l3_centralizedattackdetector/service/__main__.py b/src/l3_centralizedattackdetector/service/__main__.py
index b05d5b14782a7dae8b34a49e03c0d00e1b39d3ca..42c7412cb85a7c168d09347eda32d73ea61dbf0e 100644
--- a/src/l3_centralizedattackdetector/service/__main__.py
+++ b/src/l3_centralizedattackdetector/service/__main__.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging, signal, sys, threading, os
 from prometheus_client import start_http_server
 from common.Settings import get_setting
diff --git a/src/l3_centralizedattackdetector/service/l3_centralizedattackdetectorService.py b/src/l3_centralizedattackdetector/service/l3_centralizedattackdetectorService.py
index dd4a2fce9d078645c08a26090f209cc2425698e2..0eb101f0e36fc59e5c6e8b1346609693259e0ccf 100644
--- a/src/l3_centralizedattackdetector/service/l3_centralizedattackdetectorService.py
+++ b/src/l3_centralizedattackdetector/service/l3_centralizedattackdetectorService.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import grpc
 import logging
 from concurrent import futures
diff --git a/src/l3_centralizedattackdetector/service/l3_centralizedattackdetectorServiceServicerImpl.py b/src/l3_centralizedattackdetector/service/l3_centralizedattackdetectorServiceServicerImpl.py
index 2f11e1cfb5d57a50703cb3d44e2b05a7fb1a1b4f..18006f5a60de4c6f5596d03e3b9482dc4040014d 100644
--- a/src/l3_centralizedattackdetector/service/l3_centralizedattackdetectorServiceServicerImpl.py
+++ b/src/l3_centralizedattackdetector/service/l3_centralizedattackdetectorServiceServicerImpl.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 from __future__ import print_function
 from datetime import datetime
 import os
diff --git a/src/l3_centralizedattackdetector/tests/__init__.py b/src/l3_centralizedattackdetector/tests/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/l3_centralizedattackdetector/tests/__init__.py
+++ b/src/l3_centralizedattackdetector/tests/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/l3_centralizedattackdetector/tests/test_unitary.py b/src/l3_centralizedattackdetector/tests/test_unitary.py
index 2f991feda3c9b421b5cffc591fa404f38e038e35..c63975cc896373f4d33a7dc8dc4c6d15692c8fe6 100644
--- a/src/l3_centralizedattackdetector/tests/test_unitary.py
+++ b/src/l3_centralizedattackdetector/tests/test_unitary.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging
 import pytest
 from l3_centralizedattackdetector.Config import GRPC_SERVICE_PORT, GRPC_MAX_WORKERS, GRPC_GRACE_PERIOD
diff --git a/src/l3_distributedattackdetector/.gitlab-ci.yml b/src/l3_distributedattackdetector/.gitlab-ci.yml
index 435e6ee833ca29fdfd204d9e30990a3c655f2284..1e34c6c7e44d5bfdac06aabb2e738e368b633675 100644
--- a/src/l3_distributedattackdetector/.gitlab-ci.yml
+++ b/src/l3_distributedattackdetector/.gitlab-ci.yml
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 # Build, tag, and push the Docker images to the GitLab Docker registry
 build l3_distributedattackdetector:
   variables:
@@ -36,11 +50,14 @@ unit test l3_distributedattackdetector:
     - 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 10000:10000 --network=teraflowbridge $CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG
+    - docker run --name $IMAGE_NAME -d -p 10000:10000 -v "$PWD/src/$IMAGE_NAME/tests:/opt/results" --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"
+    - docker exec -i $IMAGE_NAME bash -c "coverage run -m pytest --log-level=INFO --verbose $IMAGE_NAME/tests/test_unitary.py --junitxml=/opt/results/${IMAGE_NAME}_report.xml"
+    - docker exec -i $IMAGE_NAME bash -c "coverage xml -o /opt/results/${IMAGE_NAME}_coverage.xml"
+    - docker exec -i $IMAGE_NAME bash -c "coverage report --include='${IMAGE_NAME}/*' --show-missing"
+  coverage: '/TOTAL\s+\d+\s+\d+\s+(\d+%)/'
   after_script:
     - docker rm -f $IMAGE_NAME
     - docker network rm teraflowbridge
@@ -54,6 +71,11 @@ unit test l3_distributedattackdetector:
       - src/$IMAGE_NAME/tests/Dockerfile
       - manifests/${IMAGE_NAME}service.yaml
       - .gitlab-ci.yml
+  artifacts:
+      when: always
+      reports:
+        junit: src/$IMAGE_NAME/tests/${IMAGE_NAME}_report.xml
+        cobertura: src/$IMAGE_NAME/tests/${IMAGE_NAME}_coverage.xml
 
 # Deployment of the service in Kubernetes Cluster
 deploy l3_distributedattackdetector:
diff --git a/src/l3_distributedattackdetector/Config.py b/src/l3_distributedattackdetector/Config.py
index 8cb99bb0d21b238037ace527e581232b7394f4e8..73d3e471a2fc1b4ccc2a053064abd70e0b145a0f 100644
--- a/src/l3_distributedattackdetector/Config.py
+++ b/src/l3_distributedattackdetector/Config.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging
 
 # General settings
diff --git a/src/l3_distributedattackdetector/Dockerfile b/src/l3_distributedattackdetector/Dockerfile
index 3b50a89735e1d96bc334fb1d78663fcd37cfb1ed..b0f1714e80055a328e90f070e9f957f8ec99708b 100644
--- a/src/l3_distributedattackdetector/Dockerfile
+++ b/src/l3_distributedattackdetector/Dockerfile
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 FROM python:3-slim
 
 # Install dependencies
diff --git a/src/l3_distributedattackdetector/__init__.py b/src/l3_distributedattackdetector/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/l3_distributedattackdetector/__init__.py
+++ b/src/l3_distributedattackdetector/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/l3_distributedattackdetector/proto/__init__.py b/src/l3_distributedattackdetector/proto/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/l3_distributedattackdetector/proto/__init__.py
+++ b/src/l3_distributedattackdetector/proto/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/l3_distributedattackdetector/requirements.in b/src/l3_distributedattackdetector/requirements.in
index e11480b450adad83b398c2a6a21cf8fe1a7b186d..ede9c839d93cc377e93c525bc1e85243576faa00 100644
--- a/src/l3_distributedattackdetector/requirements.in
+++ b/src/l3_distributedattackdetector/requirements.in
@@ -6,3 +6,4 @@ pytest
 pytest-benchmark
 numpy
 scikit-learn
+coverage
\ No newline at end of file
diff --git a/src/l3_distributedattackdetector/service/__init__.py b/src/l3_distributedattackdetector/service/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/l3_distributedattackdetector/service/__init__.py
+++ b/src/l3_distributedattackdetector/service/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/l3_distributedattackdetector/service/__main__.py b/src/l3_distributedattackdetector/service/__main__.py
index 1194e2634a337e06e4698cced41ce5fcf9846895..29f943ce745bc1355ac063fea83a63b42e19f36b 100644
--- a/src/l3_distributedattackdetector/service/__main__.py
+++ b/src/l3_distributedattackdetector/service/__main__.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging
 import sys 
 import os 
diff --git a/src/l3_distributedattackdetector/service/piped/0demo/log_tcp_temp_complete b/src/l3_distributedattackdetector/service/piped/0demo/log_tcp_temp_complete
index 52bac35ef3f46d8162ba5d21b5c63fa74cb7f6e6..97d1e3d33602eea453b0dc423ba98f8fe651d91b 100644
--- a/src/l3_distributedattackdetector/service/piped/0demo/log_tcp_temp_complete
+++ b/src/l3_distributedattackdetector/service/piped/0demo/log_tcp_temp_complete
@@ -350,3 +350,31 @@
 221.181.185.159 26406 11 0 10 4 1111 6 1111 0 0 0 1 0 10.25.0.6 22 10 0 10 5 1497 4 1497 0 0 0 1 0 1631699183883.141113 1631699185936.677002 2053.536000 327.059000 334.471000 2053.536000 1726.893000 326.976000 327.081000 1 1 0 0 0 0 0 13.977100 0.022000 42.534000 21.563233 6 40 40 334.779504 326.582000 367.107000 18.068151 5 64 64 0 0 0 0 0 0 1 1 7 1 0 1460 856 15 33536 29200 0 856 15 15 0 0 0 0 0 0 0 0 0 1 1 7 1 0 1410 1080 41 64384 64128 0 1080 41 41 0 0 0 0 0 0 0 0 0 0 0 --- 6 4 - - 0 0 0 0.000000 0.000000 0.000000 0.000000 0 0 - - 0.0 0.0 -
 221.181.185.159 26406 11 0 10 4 1111 6 1111 0 0 0 1 0 10.25.0.6 22 10 0 10 5 1497 4 1497 0 0 0 1 0 1631699183883.141113 1631699185936.677002 2053.536000 327.059000 334.471000 2053.536000 1726.893000 326.976000 327.081000 1 1 0 0 0 0 0 13.977100 0.022000 42.534000 21.563233 6 40 40 334.779504 326.582000 367.107000 18.068151 5 64 64 0 0 0 0 0 0 1 1 7 1 0 1460 856 15 33536 29200 0 856 15 15 0 0 0 0 0 0 0 0 0 1 1 7 1 0 1410 1080 41 64384 64128 0 1080 41 41 0 0 0 0 0 0 0 0 0 0 0 --- 6 4 - - 0 0 0 0.000000 0.000000 0.000000 0.000000 0 0 - - 0.0 0.0 -
 221.181.185.159 26406 11 0 10 4 1111 6 1111 0 0 0 1 0 10.25.0.6 22 10 0 10 5 1497 4 1497 0 0 0 1 0 1631699183883.141113 1631699185936.677002 2053.536000 327.059000 334.471000 2053.536000 1726.893000 326.976000 327.081000 1 1 0 0 0 0 0 13.977100 0.022000 42.534000 21.563233 6 40 40 334.779504 326.582000 367.107000 18.068151 5 64 64 0 0 0 0 0 0 1 1 7 1 0 1460 856 15 33536 29200 0 856 15 15 0 0 0 0 0 0 0 0 0 1 1 7 1 0 1410 1080 41 64384 64128 0 1080 41 41 0 0 0 0 0 0 0 0 0 0 0 --- 6 4 - - 0 0 0 0.000000 0.000000 0.000000 0.000000 0 0 - - 0.0 0.0 -
+221.181.185.159 26406 11 0 10 4 1111 6 1111 0 0 0 1 0 10.25.0.6 22 10 0 10 5 1497 4 1497 0 0 0 1 0 1631699183883.141113 1631699185936.677002 2053.536000 327.059000 334.471000 2053.536000 1726.893000 326.976000 327.081000 1 1 0 0 0 0 0 13.977100 0.022000 42.534000 21.563233 6 40 40 334.779504 326.582000 367.107000 18.068151 5 64 64 0 0 0 0 0 0 1 1 7 1 0 1460 856 15 33536 29200 0 856 15 15 0 0 0 0 0 0 0 0 0 1 1 7 1 0 1410 1080 41 64384 64128 0 1080 41 41 0 0 0 0 0 0 0 0 0 0 0 --- 6 4 - - 0 0 0 0.000000 0.000000 0.000000 0.000000 0 0 - - 0.0 0.0 -
+221.181.185.159 26406 11 0 10 4 1111 6 1111 0 0 0 1 0 10.25.0.6 22 10 0 10 5 1497 4 1497 0 0 0 1 0 1631699183883.141113 1631699185936.677002 2053.536000 327.059000 334.471000 2053.536000 1726.893000 326.976000 327.081000 1 1 0 0 0 0 0 13.977100 0.022000 42.534000 21.563233 6 40 40 334.779504 326.582000 367.107000 18.068151 5 64 64 0 0 0 0 0 0 1 1 7 1 0 1460 856 15 33536 29200 0 856 15 15 0 0 0 0 0 0 0 0 0 1 1 7 1 0 1410 1080 41 64384 64128 0 1080 41 41 0 0 0 0 0 0 0 0 0 0 0 --- 6 4 - - 0 0 0 0.000000 0.000000 0.000000 0.000000 0 0 - - 0.0 0.0 -
+221.181.185.159 26406 11 0 10 4 1111 6 1111 0 0 0 1 0 10.25.0.6 22 10 0 10 5 1497 4 1497 0 0 0 1 0 1631699183883.141113 1631699185936.677002 2053.536000 327.059000 334.471000 2053.536000 1726.893000 326.976000 327.081000 1 1 0 0 0 0 0 13.977100 0.022000 42.534000 21.563233 6 40 40 334.779504 326.582000 367.107000 18.068151 5 64 64 0 0 0 0 0 0 1 1 7 1 0 1460 856 15 33536 29200 0 856 15 15 0 0 0 0 0 0 0 0 0 1 1 7 1 0 1410 1080 41 64384 64128 0 1080 41 41 0 0 0 0 0 0 0 0 0 0 0 --- 6 4 - - 0 0 0 0.000000 0.000000 0.000000 0.000000 0 0 - - 0.0 0.0 -
+221.181.185.159 26406 11 0 10 4 1111 6 1111 0 0 0 1 0 10.25.0.6 22 10 0 10 5 1497 4 1497 0 0 0 1 0 1631699183883.141113 1631699185936.677002 2053.536000 327.059000 334.471000 2053.536000 1726.893000 326.976000 327.081000 1 1 0 0 0 0 0 13.977100 0.022000 42.534000 21.563233 6 40 40 334.779504 326.582000 367.107000 18.068151 5 64 64 0 0 0 0 0 0 1 1 7 1 0 1460 856 15 33536 29200 0 856 15 15 0 0 0 0 0 0 0 0 0 1 1 7 1 0 1410 1080 41 64384 64128 0 1080 41 41 0 0 0 0 0 0 0 0 0 0 0 --- 6 4 - - 0 0 0 0.000000 0.000000 0.000000 0.000000 0 0 - - 0.0 0.0 -
+221.181.185.159 26406 11 0 10 4 1111 6 1111 0 0 0 1 0 10.25.0.6 22 10 0 10 5 1497 4 1497 0 0 0 1 0 1631699183883.141113 1631699185936.677002 2053.536000 327.059000 334.471000 2053.536000 1726.893000 326.976000 327.081000 1 1 0 0 0 0 0 13.977100 0.022000 42.534000 21.563233 6 40 40 334.779504 326.582000 367.107000 18.068151 5 64 64 0 0 0 0 0 0 1 1 7 1 0 1460 856 15 33536 29200 0 856 15 15 0 0 0 0 0 0 0 0 0 1 1 7 1 0 1410 1080 41 64384 64128 0 1080 41 41 0 0 0 0 0 0 0 0 0 0 0 --- 6 4 - - 0 0 0 0.000000 0.000000 0.000000 0.000000 0 0 - - 0.0 0.0 -
+221.181.185.159 26406 11 0 10 4 1111 6 1111 0 0 0 1 0 10.25.0.6 22 10 0 10 5 1497 4 1497 0 0 0 1 0 1631699183883.141113 1631699185936.677002 2053.536000 327.059000 334.471000 2053.536000 1726.893000 326.976000 327.081000 1 1 0 0 0 0 0 13.977100 0.022000 42.534000 21.563233 6 40 40 334.779504 326.582000 367.107000 18.068151 5 64 64 0 0 0 0 0 0 1 1 7 1 0 1460 856 15 33536 29200 0 856 15 15 0 0 0 0 0 0 0 0 0 1 1 7 1 0 1410 1080 41 64384 64128 0 1080 41 41 0 0 0 0 0 0 0 0 0 0 0 --- 6 4 - - 0 0 0 0.000000 0.000000 0.000000 0.000000 0 0 - - 0.0 0.0 -
+221.181.185.159 26406 11 0 10 4 1111 6 1111 0 0 0 1 0 10.25.0.6 22 10 0 10 5 1497 4 1497 0 0 0 1 0 1631699183883.141113 1631699185936.677002 2053.536000 327.059000 334.471000 2053.536000 1726.893000 326.976000 327.081000 1 1 0 0 0 0 0 13.977100 0.022000 42.534000 21.563233 6 40 40 334.779504 326.582000 367.107000 18.068151 5 64 64 0 0 0 0 0 0 1 1 7 1 0 1460 856 15 33536 29200 0 856 15 15 0 0 0 0 0 0 0 0 0 1 1 7 1 0 1410 1080 41 64384 64128 0 1080 41 41 0 0 0 0 0 0 0 0 0 0 0 --- 6 4 - - 0 0 0 0.000000 0.000000 0.000000 0.000000 0 0 - - 0.0 0.0 -
+221.181.185.159 26406 11 0 10 4 1111 6 1111 0 0 0 1 0 10.25.0.6 22 10 0 10 5 1497 4 1497 0 0 0 1 0 1631699183883.141113 1631699185936.677002 2053.536000 327.059000 334.471000 2053.536000 1726.893000 326.976000 327.081000 1 1 0 0 0 0 0 13.977100 0.022000 42.534000 21.563233 6 40 40 334.779504 326.582000 367.107000 18.068151 5 64 64 0 0 0 0 0 0 1 1 7 1 0 1460 856 15 33536 29200 0 856 15 15 0 0 0 0 0 0 0 0 0 1 1 7 1 0 1410 1080 41 64384 64128 0 1080 41 41 0 0 0 0 0 0 0 0 0 0 0 --- 6 4 - - 0 0 0 0.000000 0.000000 0.000000 0.000000 0 0 - - 0.0 0.0 -
+221.181.185.159 26406 11 0 10 4 1111 6 1111 0 0 0 1 0 10.25.0.6 22 10 0 10 5 1497 4 1497 0 0 0 1 0 1631699183883.141113 1631699185936.677002 2053.536000 327.059000 334.471000 2053.536000 1726.893000 326.976000 327.081000 1 1 0 0 0 0 0 13.977100 0.022000 42.534000 21.563233 6 40 40 334.779504 326.582000 367.107000 18.068151 5 64 64 0 0 0 0 0 0 1 1 7 1 0 1460 856 15 33536 29200 0 856 15 15 0 0 0 0 0 0 0 0 0 1 1 7 1 0 1410 1080 41 64384 64128 0 1080 41 41 0 0 0 0 0 0 0 0 0 0 0 --- 6 4 - - 0 0 0 0.000000 0.000000 0.000000 0.000000 0 0 - - 0.0 0.0 -
+221.181.185.159 26406 11 0 10 4 1111 6 1111 0 0 0 1 0 10.25.0.6 22 10 0 10 5 1497 4 1497 0 0 0 1 0 1631699183883.141113 1631699185936.677002 2053.536000 327.059000 334.471000 2053.536000 1726.893000 326.976000 327.081000 1 1 0 0 0 0 0 13.977100 0.022000 42.534000 21.563233 6 40 40 334.779504 326.582000 367.107000 18.068151 5 64 64 0 0 0 0 0 0 1 1 7 1 0 1460 856 15 33536 29200 0 856 15 15 0 0 0 0 0 0 0 0 0 1 1 7 1 0 1410 1080 41 64384 64128 0 1080 41 41 0 0 0 0 0 0 0 0 0 0 0 --- 6 4 - - 0 0 0 0.000000 0.000000 0.000000 0.000000 0 0 - - 0.0 0.0 -
+221.181.185.159 26406 11 0 10 4 1111 6 1111 0 0 0 1 0 10.25.0.6 22 10 0 10 5 1497 4 1497 0 0 0 1 0 1631699183883.141113 1631699185936.677002 2053.536000 327.059000 334.471000 2053.536000 1726.893000 326.976000 327.081000 1 1 0 0 0 0 0 13.977100 0.022000 42.534000 21.563233 6 40 40 334.779504 326.582000 367.107000 18.068151 5 64 64 0 0 0 0 0 0 1 1 7 1 0 1460 856 15 33536 29200 0 856 15 15 0 0 0 0 0 0 0 0 0 1 1 7 1 0 1410 1080 41 64384 64128 0 1080 41 41 0 0 0 0 0 0 0 0 0 0 0 --- 6 4 - - 0 0 0 0.000000 0.000000 0.000000 0.000000 0 0 - - 0.0 0.0 -
+221.181.185.159 26406 11 0 10 4 1111 6 1111 0 0 0 1 0 10.25.0.6 22 10 0 10 5 1497 4 1497 0 0 0 1 0 1631699183883.141113 1631699185936.677002 2053.536000 327.059000 334.471000 2053.536000 1726.893000 326.976000 327.081000 1 1 0 0 0 0 0 13.977100 0.022000 42.534000 21.563233 6 40 40 334.779504 326.582000 367.107000 18.068151 5 64 64 0 0 0 0 0 0 1 1 7 1 0 1460 856 15 33536 29200 0 856 15 15 0 0 0 0 0 0 0 0 0 1 1 7 1 0 1410 1080 41 64384 64128 0 1080 41 41 0 0 0 0 0 0 0 0 0 0 0 --- 6 4 - - 0 0 0 0.000000 0.000000 0.000000 0.000000 0 0 - - 0.0 0.0 -
+221.181.185.159 26406 11 0 10 4 1111 6 1111 0 0 0 1 0 10.25.0.6 22 10 0 10 5 1497 4 1497 0 0 0 1 0 1631699183883.141113 1631699185936.677002 2053.536000 327.059000 334.471000 2053.536000 1726.893000 326.976000 327.081000 1 1 0 0 0 0 0 13.977100 0.022000 42.534000 21.563233 6 40 40 334.779504 326.582000 367.107000 18.068151 5 64 64 0 0 0 0 0 0 1 1 7 1 0 1460 856 15 33536 29200 0 856 15 15 0 0 0 0 0 0 0 0 0 1 1 7 1 0 1410 1080 41 64384 64128 0 1080 41 41 0 0 0 0 0 0 0 0 0 0 0 --- 6 4 - - 0 0 0 0.000000 0.000000 0.000000 0.000000 0 0 - - 0.0 0.0 -
+221.181.185.159 26406 11 0 10 4 1111 6 1111 0 0 0 1 0 10.25.0.6 22 10 0 10 5 1497 4 1497 0 0 0 1 0 1631699183883.141113 1631699185936.677002 2053.536000 327.059000 334.471000 2053.536000 1726.893000 326.976000 327.081000 1 1 0 0 0 0 0 13.977100 0.022000 42.534000 21.563233 6 40 40 334.779504 326.582000 367.107000 18.068151 5 64 64 0 0 0 0 0 0 1 1 7 1 0 1460 856 15 33536 29200 0 856 15 15 0 0 0 0 0 0 0 0 0 1 1 7 1 0 1410 1080 41 64384 64128 0 1080 41 41 0 0 0 0 0 0 0 0 0 0 0 --- 6 4 - - 0 0 0 0.000000 0.000000 0.000000 0.000000 0 0 - - 0.0 0.0 -
+221.181.185.159 26406 11 0 10 4 1111 6 1111 0 0 0 1 0 10.25.0.6 22 10 0 10 5 1497 4 1497 0 0 0 1 0 1631699183883.141113 1631699185936.677002 2053.536000 327.059000 334.471000 2053.536000 1726.893000 326.976000 327.081000 1 1 0 0 0 0 0 13.977100 0.022000 42.534000 21.563233 6 40 40 334.779504 326.582000 367.107000 18.068151 5 64 64 0 0 0 0 0 0 1 1 7 1 0 1460 856 15 33536 29200 0 856 15 15 0 0 0 0 0 0 0 0 0 1 1 7 1 0 1410 1080 41 64384 64128 0 1080 41 41 0 0 0 0 0 0 0 0 0 0 0 --- 6 4 - - 0 0 0 0.000000 0.000000 0.000000 0.000000 0 0 - - 0.0 0.0 -
+221.181.185.159 26406 11 0 10 4 1111 6 1111 0 0 0 1 0 10.25.0.6 22 10 0 10 5 1497 4 1497 0 0 0 1 0 1631699183883.141113 1631699185936.677002 2053.536000 327.059000 334.471000 2053.536000 1726.893000 326.976000 327.081000 1 1 0 0 0 0 0 13.977100 0.022000 42.534000 21.563233 6 40 40 334.779504 326.582000 367.107000 18.068151 5 64 64 0 0 0 0 0 0 1 1 7 1 0 1460 856 15 33536 29200 0 856 15 15 0 0 0 0 0 0 0 0 0 1 1 7 1 0 1410 1080 41 64384 64128 0 1080 41 41 0 0 0 0 0 0 0 0 0 0 0 --- 6 4 - - 0 0 0 0.000000 0.000000 0.000000 0.000000 0 0 - - 0.0 0.0 -
+221.181.185.159 26406 11 0 10 4 1111 6 1111 0 0 0 1 0 10.25.0.6 22 10 0 10 5 1497 4 1497 0 0 0 1 0 1631699183883.141113 1631699185936.677002 2053.536000 327.059000 334.471000 2053.536000 1726.893000 326.976000 327.081000 1 1 0 0 0 0 0 13.977100 0.022000 42.534000 21.563233 6 40 40 334.779504 326.582000 367.107000 18.068151 5 64 64 0 0 0 0 0 0 1 1 7 1 0 1460 856 15 33536 29200 0 856 15 15 0 0 0 0 0 0 0 0 0 1 1 7 1 0 1410 1080 41 64384 64128 0 1080 41 41 0 0 0 0 0 0 0 0 0 0 0 --- 6 4 - - 0 0 0 0.000000 0.000000 0.000000 0.000000 0 0 - - 0.0 0.0 -
+221.181.185.159 26406 11 0 10 4 1111 6 1111 0 0 0 1 0 10.25.0.6 22 10 0 10 5 1497 4 1497 0 0 0 1 0 1631699183883.141113 1631699185936.677002 2053.536000 327.059000 334.471000 2053.536000 1726.893000 326.976000 327.081000 1 1 0 0 0 0 0 13.977100 0.022000 42.534000 21.563233 6 40 40 334.779504 326.582000 367.107000 18.068151 5 64 64 0 0 0 0 0 0 1 1 7 1 0 1460 856 15 33536 29200 0 856 15 15 0 0 0 0 0 0 0 0 0 1 1 7 1 0 1410 1080 41 64384 64128 0 1080 41 41 0 0 0 0 0 0 0 0 0 0 0 --- 6 4 - - 0 0 0 0.000000 0.000000 0.000000 0.000000 0 0 - - 0.0 0.0 -
+221.181.185.159 26406 11 0 10 4 1111 6 1111 0 0 0 1 0 10.25.0.6 22 10 0 10 5 1497 4 1497 0 0 0 1 0 1631699183883.141113 1631699185936.677002 2053.536000 327.059000 334.471000 2053.536000 1726.893000 326.976000 327.081000 1 1 0 0 0 0 0 13.977100 0.022000 42.534000 21.563233 6 40 40 334.779504 326.582000 367.107000 18.068151 5 64 64 0 0 0 0 0 0 1 1 7 1 0 1460 856 15 33536 29200 0 856 15 15 0 0 0 0 0 0 0 0 0 1 1 7 1 0 1410 1080 41 64384 64128 0 1080 41 41 0 0 0 0 0 0 0 0 0 0 0 --- 6 4 - - 0 0 0 0.000000 0.000000 0.000000 0.000000 0 0 - - 0.0 0.0 -
+221.181.185.159 26406 11 0 10 4 1111 6 1111 0 0 0 1 0 10.25.0.6 22 10 0 10 5 1497 4 1497 0 0 0 1 0 1631699183883.141113 1631699185936.677002 2053.536000 327.059000 334.471000 2053.536000 1726.893000 326.976000 327.081000 1 1 0 0 0 0 0 13.977100 0.022000 42.534000 21.563233 6 40 40 334.779504 326.582000 367.107000 18.068151 5 64 64 0 0 0 0 0 0 1 1 7 1 0 1460 856 15 33536 29200 0 856 15 15 0 0 0 0 0 0 0 0 0 1 1 7 1 0 1410 1080 41 64384 64128 0 1080 41 41 0 0 0 0 0 0 0 0 0 0 0 --- 6 4 - - 0 0 0 0.000000 0.000000 0.000000 0.000000 0 0 - - 0.0 0.0 -
+221.181.185.159 26406 11 0 10 4 1111 6 1111 0 0 0 1 0 10.25.0.6 22 10 0 10 5 1497 4 1497 0 0 0 1 0 1631699183883.141113 1631699185936.677002 2053.536000 327.059000 334.471000 2053.536000 1726.893000 326.976000 327.081000 1 1 0 0 0 0 0 13.977100 0.022000 42.534000 21.563233 6 40 40 334.779504 326.582000 367.107000 18.068151 5 64 64 0 0 0 0 0 0 1 1 7 1 0 1460 856 15 33536 29200 0 856 15 15 0 0 0 0 0 0 0 0 0 1 1 7 1 0 1410 1080 41 64384 64128 0 1080 41 41 0 0 0 0 0 0 0 0 0 0 0 --- 6 4 - - 0 0 0 0.000000 0.000000 0.000000 0.000000 0 0 - - 0.0 0.0 -
+221.181.185.159 26406 11 0 10 4 1111 6 1111 0 0 0 1 0 10.25.0.6 22 10 0 10 5 1497 4 1497 0 0 0 1 0 1631699183883.141113 1631699185936.677002 2053.536000 327.059000 334.471000 2053.536000 1726.893000 326.976000 327.081000 1 1 0 0 0 0 0 13.977100 0.022000 42.534000 21.563233 6 40 40 334.779504 326.582000 367.107000 18.068151 5 64 64 0 0 0 0 0 0 1 1 7 1 0 1460 856 15 33536 29200 0 856 15 15 0 0 0 0 0 0 0 0 0 1 1 7 1 0 1410 1080 41 64384 64128 0 1080 41 41 0 0 0 0 0 0 0 0 0 0 0 --- 6 4 - - 0 0 0 0.000000 0.000000 0.000000 0.000000 0 0 - - 0.0 0.0 -
+221.181.185.159 26406 11 0 10 4 1111 6 1111 0 0 0 1 0 10.25.0.6 22 10 0 10 5 1497 4 1497 0 0 0 1 0 1631699183883.141113 1631699185936.677002 2053.536000 327.059000 334.471000 2053.536000 1726.893000 326.976000 327.081000 1 1 0 0 0 0 0 13.977100 0.022000 42.534000 21.563233 6 40 40 334.779504 326.582000 367.107000 18.068151 5 64 64 0 0 0 0 0 0 1 1 7 1 0 1460 856 15 33536 29200 0 856 15 15 0 0 0 0 0 0 0 0 0 1 1 7 1 0 1410 1080 41 64384 64128 0 1080 41 41 0 0 0 0 0 0 0 0 0 0 0 --- 6 4 - - 0 0 0 0.000000 0.000000 0.000000 0.000000 0 0 - - 0.0 0.0 -
+221.181.185.159 26406 11 0 10 4 1111 6 1111 0 0 0 1 0 10.25.0.6 22 10 0 10 5 1497 4 1497 0 0 0 1 0 1631699183883.141113 1631699185936.677002 2053.536000 327.059000 334.471000 2053.536000 1726.893000 326.976000 327.081000 1 1 0 0 0 0 0 13.977100 0.022000 42.534000 21.563233 6 40 40 334.779504 326.582000 367.107000 18.068151 5 64 64 0 0 0 0 0 0 1 1 7 1 0 1460 856 15 33536 29200 0 856 15 15 0 0 0 0 0 0 0 0 0 1 1 7 1 0 1410 1080 41 64384 64128 0 1080 41 41 0 0 0 0 0 0 0 0 0 0 0 --- 6 4 - - 0 0 0 0.000000 0.000000 0.000000 0.000000 0 0 - - 0.0 0.0 -
+221.181.185.159 26406 11 0 10 4 1111 6 1111 0 0 0 1 0 10.25.0.6 22 10 0 10 5 1497 4 1497 0 0 0 1 0 1631699183883.141113 1631699185936.677002 2053.536000 327.059000 334.471000 2053.536000 1726.893000 326.976000 327.081000 1 1 0 0 0 0 0 13.977100 0.022000 42.534000 21.563233 6 40 40 334.779504 326.582000 367.107000 18.068151 5 64 64 0 0 0 0 0 0 1 1 7 1 0 1460 856 15 33536 29200 0 856 15 15 0 0 0 0 0 0 0 0 0 1 1 7 1 0 1410 1080 41 64384 64128 0 1080 41 41 0 0 0 0 0 0 0 0 0 0 0 --- 6 4 - - 0 0 0 0.000000 0.000000 0.000000 0.000000 0 0 - - 0.0 0.0 -
+221.181.185.159 26406 11 0 10 4 1111 6 1111 0 0 0 1 0 10.25.0.6 22 10 0 10 5 1497 4 1497 0 0 0 1 0 1631699183883.141113 1631699185936.677002 2053.536000 327.059000 334.471000 2053.536000 1726.893000 326.976000 327.081000 1 1 0 0 0 0 0 13.977100 0.022000 42.534000 21.563233 6 40 40 334.779504 326.582000 367.107000 18.068151 5 64 64 0 0 0 0 0 0 1 1 7 1 0 1460 856 15 33536 29200 0 856 15 15 0 0 0 0 0 0 0 0 0 1 1 7 1 0 1410 1080 41 64384 64128 0 1080 41 41 0 0 0 0 0 0 0 0 0 0 0 --- 6 4 - - 0 0 0 0.000000 0.000000 0.000000 0.000000 0 0 - - 0.0 0.0 -
+221.181.185.159 26406 11 0 10 4 1111 6 1111 0 0 0 1 0 10.25.0.6 22 10 0 10 5 1497 4 1497 0 0 0 1 0 1631699183883.141113 1631699185936.677002 2053.536000 327.059000 334.471000 2053.536000 1726.893000 326.976000 327.081000 1 1 0 0 0 0 0 13.977100 0.022000 42.534000 21.563233 6 40 40 334.779504 326.582000 367.107000 18.068151 5 64 64 0 0 0 0 0 0 1 1 7 1 0 1460 856 15 33536 29200 0 856 15 15 0 0 0 0 0 0 0 0 0 1 1 7 1 0 1410 1080 41 64384 64128 0 1080 41 41 0 0 0 0 0 0 0 0 0 0 0 --- 6 4 - - 0 0 0 0.000000 0.000000 0.000000 0.000000 0 0 - - 0.0 0.0 -
+221.181.185.159 26406 11 0 10 4 1111 6 1111 0 0 0 1 0 10.25.0.6 22 10 0 10 5 1497 4 1497 0 0 0 1 0 1631699183883.141113 1631699185936.677002 2053.536000 327.059000 334.471000 2053.536000 1726.893000 326.976000 327.081000 1 1 0 0 0 0 0 13.977100 0.022000 42.534000 21.563233 6 40 40 334.779504 326.582000 367.107000 18.068151 5 64 64 0 0 0 0 0 0 1 1 7 1 0 1460 856 15 33536 29200 0 856 15 15 0 0 0 0 0 0 0 0 0 1 1 7 1 0 1410 1080 41 64384 64128 0 1080 41 41 0 0 0 0 0 0 0 0 0 0 0 --- 6 4 - - 0 0 0 0.000000 0.000000 0.000000 0.000000 0 0 - - 0.0 0.0 -
diff --git a/src/l3_distributedattackdetector/tests/__init__.py b/src/l3_distributedattackdetector/tests/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/l3_distributedattackdetector/tests/__init__.py
+++ b/src/l3_distributedattackdetector/tests/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/l3_distributedattackdetector/tests/data_generator.py b/src/l3_distributedattackdetector/tests/data_generator.py
index 18b861ebbbf2547829dc3b11b12965acf4eef20a..43c3d707c8ccd52ccc63afdc7278bf67ec3f628a 100644
--- a/src/l3_distributedattackdetector/tests/data_generator.py
+++ b/src/l3_distributedattackdetector/tests/data_generator.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import os
 from time import sleep
 
diff --git a/src/l3_distributedattackdetector/tests/test_unitary.py b/src/l3_distributedattackdetector/tests/test_unitary.py
index 96eae5f67adf70fe523f0e1e2e5167d84294c6b9..0e868d7815ec0c3837bb2e9a1e439b98f3674f39 100644
--- a/src/l3_distributedattackdetector/tests/test_unitary.py
+++ b/src/l3_distributedattackdetector/tests/test_unitary.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging
 import os
 from subprocess import Popen, DEVNULL
diff --git a/src/monitoring/.gitlab-ci.yml b/src/monitoring/.gitlab-ci.yml
index 3fad3a185ed44c657af4b96ce3f026a8e0b5192b..f715417765de6d7517f2430e9bf3a2a9937fa8d6 100644
--- a/src/monitoring/.gitlab-ci.yml
+++ b/src/monitoring/.gitlab-ci.yml
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 # Build, tag and push the Docker image to the GitLab registry
 build monitoring:
   variables:
@@ -39,11 +53,13 @@ unit test monitoring:
     - docker pull "$CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG"
     - docker run --name influxdb -d -p 8086:8086 -e INFLUXDB_DB=$INFLUXDB_DATABASE -e INFLUXDB_ADMIN_USER=$INFLUXDB_USER -e INFLUXDB_ADMIN_PASSWORD=$INFLUXDB_PASSWORD -e INFLUXDB_HTTP_AUTH_ENABLED=True --network=teraflowbridge --rm influxdb:1.8
     - sleep 10
-    - docker run --name $IMAGE_NAME -d -p 7070:7070 --env INFLUXDB_USER=$INFLUXDB_USER --env INFLUXDB_PASSWORD=$INFLUXDB_PASSWORD --env INFLUXDB_DATABASE=$INFLUXDB_DATABASE --env INFLUXDB_HOSTNAME=influxdb -v "$PWD/src/$IMAGE_NAME/tests:/opt/results" --network=teraflowbridge --rm $CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG
+    - docker run --name $IMAGE_NAME -d -p 7070:7070 --env INFLUXDB_USER=$INFLUXDB_USER --env INFLUXDB_PASSWORD=$INFLUXDB_PASSWORD --env INFLUXDB_DATABASE=$INFLUXDB_DATABASE --env INFLUXDB_HOSTNAME=influxdb --env INFLUXDB_PORT=8086 -v "$PWD/src/$IMAGE_NAME/tests:/opt/results" --network=teraflowbridge --rm $CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG
     - sleep 30
     - docker ps -a
     - docker logs $IMAGE_NAME
-    - docker exec -i $IMAGE_NAME bash -c "coverage run -m pytest --log-level=INFO --verbose $IMAGE_NAME/tests/test_unitary.py --junitxml=/opt/results/${IMAGE_NAME}_report.xml; coverage xml -o /opt/results/${IMAGE_NAME}_coverage.xml; coverage report --include='${IMAGE_NAME}/*' --show-missing"
+    - docker exec -i $IMAGE_NAME bash -c "coverage run -m pytest --log-level=INFO --verbose $IMAGE_NAME/tests/test_unitary.py --junitxml=/opt/results/${IMAGE_NAME}_report.xml"
+    - docker exec -i $IMAGE_NAME bash -c "coverage xml -o /opt/results/${IMAGE_NAME}_coverage.xml"
+    - docker exec -i $IMAGE_NAME bash -c "coverage report --include='${IMAGE_NAME}/*' --show-missing"
   coverage: '/TOTAL\s+\d+\s+\d+\s+(\d+%)/'
   after_script:
     - docker rm -f $IMAGE_NAME
diff --git a/src/monitoring/Config.py b/src/monitoring/Config.py
index 11036b0d17dc640418fd4d0be1c7e81acb56c559..cbae00509d8196b69bc2d6bacb39bfa5918be495 100644
--- a/src/monitoring/Config.py
+++ b/src/monitoring/Config.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging
 
 # General settings
@@ -7,6 +21,20 @@ LOG_LEVEL = logging.WARNING
 GRPC_SERVICE_PORT = 7070
 GRPC_MAX_WORKERS  = 10
 GRPC_GRACE_PERIOD = 60
+GRPC_SERVICE_HOST = '127.0.0.1'
 
 # Prometheus settings
 METRICS_PORT = 9192
+
+# Dependency micro-service connection settings
+CONTEXT_SERVICE_HOST = '127.0.0.1'
+CONTEXT_GRPC_SERVICE_PORT = 1010
+
+DEVICE_SERVICE_HOST         = '127.0.0.1'
+DEVICE_GRPC_SERVICE_PORT    = 2020
+DEVICE_GRPC_MAX_WORKERS     = 10
+DEVICE_GRPC_GRACE_PERIOD    = 60
+
+
+
+
diff --git a/src/monitoring/Dockerfile b/src/monitoring/Dockerfile
index af2fd608521c297f7aa97b45f0e3fc5d88e5615c..c1bba549eaaf5cfaaacd16d01a9d2204bc09c393 100644
--- a/src/monitoring/Dockerfile
+++ b/src/monitoring/Dockerfile
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 FROM python:3-slim
 
 # Install dependencies
@@ -29,7 +43,6 @@ RUN mkdir -p /var/teraflow/device/proto
 RUN mkdir -p /var/teraflow/device/client
 RUN mkdir -p /var/teraflow/context
 
-
 # Get Python packages per module
 COPY monitoring/requirements.in requirements.in
 RUN pip-compile --output-file=requirements.txt requirements.in
@@ -49,3 +62,4 @@ RUN rm -r common/rpc_method_wrapper/tests
 RUN rm -r context/tests/test_unitary.py
 
 ENTRYPOINT ["python", "-m", "monitoring.service"]
+
diff --git a/src/monitoring/__init__.py b/src/monitoring/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/monitoring/__init__.py
+++ b/src/monitoring/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/monitoring/client/__init__.py b/src/monitoring/client/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/monitoring/client/__init__.py
+++ b/src/monitoring/client/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/monitoring/client/monitoring_client.py b/src/monitoring/client/monitoring_client.py
index c3ab508c947ae07835801ad724f4589af92379c8..62bfb519e7649427cad5b8f9e3bc0f849a9b9a39 100644
--- a/src/monitoring/client/monitoring_client.py
+++ b/src/monitoring/client/monitoring_client.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import sys
 import grpc
 
diff --git a/src/monitoring/proto/__init__.py b/src/monitoring/proto/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/monitoring/proto/__init__.py
+++ b/src/monitoring/proto/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/monitoring/requirements.in b/src/monitoring/requirements.in
index 70d0c3ee926a9b136f2b1d79f91aecfe2c451ae3..77b66b79498d6df89a1d3b8989a02c06078eb1b9 100644
--- a/src/monitoring/requirements.in
+++ b/src/monitoring/requirements.in
@@ -10,4 +10,7 @@ pytest
 pytest-benchmark
 influxdb
 redis
-coverage
\ No newline at end of file
+anytree
+apscheduler
+xmltodict
+coverage
diff --git a/src/monitoring/service/EventTools.py b/src/monitoring/service/EventTools.py
index a28ff36a3f469bc0967be71e7b0005c316e9282a..636556425af9ac02487386d81b9d8d4e786aa560 100644
--- a/src/monitoring/service/EventTools.py
+++ b/src/monitoring/service/EventTools.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import threading
 from queue import Queue
 
@@ -19,13 +33,21 @@ class EventsDeviceCollector:
     def __init__(self, context_client_grpc : ContextClient, monitoring_client_grpc : MonitoringClient) -> None: # pylint: disable=redefined-outer-name
         self._events_queue = Queue()
 
-        self._device_stream   = context_client_grpc.GetDeviceEvents(Empty())
-        self._context_client  = context_client_grpc
+        self._device_stream     = context_client_grpc.GetDeviceEvents(Empty())
+        self._context_client    = context_client_grpc
+        self._channel           = context_client_grpc.channel
         self._monitoring_client = monitoring_client_grpc
 
         self._device_thread   = threading.Thread(target=self._collect, args=(self._device_stream  ,), daemon=False)
 
-    def _collect(self, events_stream) -> None:
+    def grpc_server_on(self):
+        try:
+            grpc.channel_ready_future(self._channel).result(timeout=15)
+            return True
+        except grpc.FutureTimeoutError:
+            return False
+
+    def _collect(self, events_stream):
         try:
             for event in events_stream:
                 self._events_queue.put_nowait(event)
@@ -34,39 +56,39 @@ class EventsDeviceCollector:
                 raise # pragma: no cover
 
     def start(self):
-        self._device_thread.start()
+        try:
+            self._device_thread.start()
+        except RuntimeError as e:
+            LOGGER.exception('Start EventTools exception')
 
     def get_event(self, block : bool = True, timeout : float = 0.1):
         return self._events_queue.get(block=block, timeout=timeout)
 
     def stop(self):
-
         self._device_stream.cancel()
-
         self._device_thread.join()
 
     def listen_events(self):
-        LOGGER.info('getting Kpi by KpiID')
-        qsize = self._events_queue.qsize()
         try:
             kpi_id_list = []
-            if qsize > 0:
-                for i in range(qsize):
-                    print("Queue size: "+str(qsize))
-                    event = self.get_event(block=True)
-                    if event.event.event_type == EventTypeEnum.EVENTTYPE_CREATE:
-                        device = self._context_client.GetDevice(event.device_id)
-                        print("Endpoints value: " + str(len(device.device_endpoints)))
-                        for j,end_point in enumerate(device.device_endpoints):
-
-                            # for k,rule in enumerate(device.device_config.config_rules):
+
+            while not self._events_queue.empty():
+                LOGGER.info('getting Kpi by KpiID')
+                event = self.get_event(block=True)
+                if event.event.event_type == EventTypeEnum.EVENTTYPE_CREATE:
+                    device = self._context_client.GetDevice(event.device_id)
+                    for j,end_point in enumerate(device.device_endpoints):
+                        #for i, value in enumerate(kpi_sample_types_pb2.KpiSampleType.values()):
+                        for i, value in enumerate(end_point.kpi_sample_types):
+                            #if value == kpi_sample_types_pb2.KpiSampleType.KPISAMPLETYPE_UNKNOWN: continue
+
                             kpi_descriptor = monitoring_pb2.KpiDescriptor()
 
                             kpi_descriptor.kpi_description                      = device.device_type
-                            kpi_descriptor.kpi_sample_type                      = kpi_sample_types_pb2.KpiSampleType.KPISAMPLETYPE_PACKETS_TRANSMITTED
+                            kpi_descriptor.kpi_sample_type                      = value
+                            #kpi_descriptor.service_id.service_uuid.uuid         = ""
                             kpi_descriptor.device_id.CopyFrom(device.device_id)
                             kpi_descriptor.endpoint_id.CopyFrom(end_point.endpoint_id)
-                            kpi_descriptor.service_id.service_uuid.uuid         = "SERV"+str(i+1)
 
                             kpi_id = self._monitoring_client.CreateKpi(kpi_descriptor)
                             kpi_id_list.append(kpi_id)
@@ -78,3 +100,4 @@ class EventsDeviceCollector:
 
         except Exception as e:  # pragma: no cover
             LOGGER.exception('ListenEvents exception')
+
diff --git a/src/monitoring/service/InfluxTools.py b/src/monitoring/service/InfluxTools.py
index df0cf01b2adddff4bcd0410d12505ed65c24e145..f19555437000acc3b44e2872f45d8a8e118a9b22 100644
--- a/src/monitoring/service/InfluxTools.py
+++ b/src/monitoring/service/InfluxTools.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 from influxdb import InfluxDBClient
 
 class Influx():
diff --git a/src/monitoring/service/MonitoringService.py b/src/monitoring/service/MonitoringService.py
index 665ce44f7e87ca73a92dfd6123ed3aa8935f0e92..f1ecba3664dfee74fddb7093ff352724791b4f7d 100644
--- a/src/monitoring/service/MonitoringService.py
+++ b/src/monitoring/service/MonitoringService.py
@@ -1,6 +1,20 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 from concurrent import futures
 
-import grpc
+import grpc, logging
 
 from monitoring.service.MonitoringServiceServicerImpl import MonitoringServiceServicerImpl
 from monitoring.Config import GRPC_SERVICE_PORT, GRPC_MAX_WORKERS, GRPC_GRACE_PERIOD
@@ -11,8 +25,7 @@ from grpc_health.v1 import health_pb2
 from grpc_health.v1.health_pb2_grpc import add_HealthServicer_to_server
 
 from common.logger import getJSONLogger
-LOGGER = getJSONLogger('monitoringservice-server')
-LOGGER.setLevel('DEBUG')
+LOGGER = getJSONLogger('monitoring-server')
 
 BIND_ADDRESS = '0.0.0.0'
 
@@ -43,9 +56,11 @@ class MonitoringService:
         add_HealthServicer_to_server(self.health_servicer, self.server)
 
         # start server
-        endpoint = '{}:{}'.format(self.address, self.port)
-        LOGGER.info('Listening on {}'.format(endpoint))
-        self.server.add_insecure_port(endpoint)
+        self.endpoint = '{:s}:{:s}'.format(str(self.address), str(self.port))
+        LOGGER.info('Starting Service (tentative endpoint: {:s}, max_workers: {:s})...'.format(
+            str(self.endpoint), str(self.max_workers)))
+
+        self.server.add_insecure_port(self.endpoint)
         self.server.start()
         self.health_servicer.set('', health_pb2.HealthCheckResponse.SERVING) # pylint: disable=maybe-no-member
 
diff --git a/src/monitoring/service/MonitoringServiceServicerImpl.py b/src/monitoring/service/MonitoringServiceServicerImpl.py
index fcadaa0c910cf912055ad30efc1ed389f256e3e4..88cd2d3a83357dec5c49e1894ed4243ceb1b4b6e 100644
--- a/src/monitoring/service/MonitoringServiceServicerImpl.py
+++ b/src/monitoring/service/MonitoringServiceServicerImpl.py
@@ -1,24 +1,38 @@
-import os,grpc
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os,grpc, logging
+import socket
 
 from prometheus_client import Summary
 from prometheus_client import Counter
+from common.Settings import get_setting
 
+from monitoring.Config import DEVICE_GRPC_SERVICE_PORT, DEVICE_SERVICE_HOST
+from monitoring.proto.kpi_sample_types_pb2 import KpiSampleType
 from monitoring.service import SqliteTools, InfluxTools
 from monitoring.proto import monitoring_pb2
 from monitoring.proto import monitoring_pb2_grpc
 
 from common.rpc_method_wrapper.ServiceExceptions import ServiceException
-from common.logger import getJSONLogger
 
 from context.proto import context_pb2
 
-
-from device.Config import GRPC_SERVICE_PORT
 from device.client.DeviceClient import DeviceClient
 from device.proto import device_pb2
 
-LOGGER = getJSONLogger('monitoringservice-server')
-LOGGER.setLevel('DEBUG')
+LOGGER = logging.getLogger(__name__)
 
 MONITORING_GETINSTANTKPI_REQUEST_TIME = Summary('monitoring_getinstantkpi_processing_seconds', 'Time spent processing monitoring instant kpi request')
 MONITORING_INCLUDEKPI_COUNTER = Counter('monitoring_includekpi_counter', 'Monitoring include kpi request counter')
@@ -28,6 +42,9 @@ INFLUXDB_USER = os.environ.get("INFLUXDB_USER")
 INFLUXDB_PASSWORD = os.environ.get("INFLUXDB_PASSWORD")
 INFLUXDB_DATABASE = os.environ.get("INFLUXDB_DATABASE")
 
+DEVICE_SERVICE_HOST = get_setting('DEVICESERVICE_SERVICE_HOST',      default=DEVICE_SERVICE_HOST     )
+DEVICE_SERVICE_PORT = get_setting('DEVICESERVICE_SERVICE_PORT_GRPC', default=DEVICE_GRPC_SERVICE_PORT)
+
 
 class MonitoringServiceServicerImpl(monitoring_pb2_grpc.MonitoringServiceServicer):
     def __init__(self):
@@ -35,6 +52,7 @@ class MonitoringServiceServicerImpl(monitoring_pb2_grpc.MonitoringServiceService
 
         # Init sqlite monitoring db
         self.sql_db = SqliteTools.SQLite('monitoring.db')
+        self.deviceClient = DeviceClient(address=DEVICE_SERVICE_HOST, port=DEVICE_GRPC_SERVICE_PORT)  # instantiate the client
 
         # Create influx_db client
         self.influx_db = InfluxTools.Influx(INFLUXDB_HOSTNAME,"8086",INFLUXDB_USER,INFLUXDB_PASSWORD,INFLUXDB_DATABASE)
@@ -83,8 +101,11 @@ class MonitoringServiceServicerImpl(monitoring_pb2_grpc.MonitoringServiceService
             monitor_device_request.sampling_duration_s                              = request.sampling_duration_s
             monitor_device_request.sampling_interval_s                              = request.sampling_interval_s
 
-            deviceClient = DeviceClient(address="localhost", port=GRPC_SERVICE_PORT )  # instantiate the client
-            # deviceClient.MonitorDeviceKpi(monitor_device_request)
+            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+            if s.connect_ex((DEVICE_SERVICE_HOST, DEVICE_GRPC_SERVICE_PORT)) == 0:
+                self.deviceClient.MonitorDeviceKpi(monitor_device_request)
+            else:
+                LOGGER.warning('Device service is not reachable')
 
             return context_pb2.Empty()
         except ServiceException as e:
@@ -103,21 +124,23 @@ class MonitoringServiceServicerImpl(monitoring_pb2_grpc.MonitoringServiceService
 
         try:
             kpiDescriptor = self.GetKpiDescriptor(request.kpi_id, grpc_context)
+            if kpiDescriptor is None:
+                LOGGER.warning('Ignoring sample with KPIId({:s}): not found in database'.format(str(request.kpi_id)))
+                return context_pb2.Empty()
 
-            kpiSampleType   = kpiDescriptor.kpi_sample_type
+            kpiSampleType   = KpiSampleType.Name(kpiDescriptor.kpi_sample_type).upper().replace('KPISAMPLETYPE_', '')
             kpiId           = request.kpi_id.kpi_id.uuid
             deviceId        = kpiDescriptor.device_id.device_uuid.uuid
             endpointId      = kpiDescriptor.endpoint_id.endpoint_uuid.uuid
             serviceId       = kpiDescriptor.service_id.service_uuid.uuid
             time_stamp      = request.timestamp
-            kpi_value       = request.kpi_value.intVal
+            kpi_value       = getattr(request.kpi_value, request.kpi_value.WhichOneof('value'))
 
             # Build the structure to be included as point in the influxDB
             self.influx_db.write_KPI(time_stamp,kpiId,kpiSampleType,deviceId,endpointId,serviceId,kpi_value)
 
-            self.influx_db.read_KPI_points()
+            #self.influx_db.read_KPI_points()
 
-            return context_pb2.Empty()
         except ServiceException as e:
             LOGGER.exception('IncludeKpi exception')
             # CREATEKPI_COUNTER_FAILED.inc()
@@ -125,6 +148,7 @@ class MonitoringServiceServicerImpl(monitoring_pb2_grpc.MonitoringServiceService
         except Exception as e:  # pragma: no cover
             LOGGER.exception('IncludeKpi exception')
             # CREATEKPI_COUNTER_FAILED.inc()
+        return context_pb2.Empty()
 
     def GetStreamKpi ( self, request, grpc_context : grpc.ServicerContext):
         # receives monitoring.KpiId returns stream monitoring.Kpi
@@ -142,7 +166,9 @@ class MonitoringServiceServicerImpl(monitoring_pb2_grpc.MonitoringServiceService
         LOGGER.info('getting Kpi by KpiID')
         try:
             kpi_db = self.sql_db.get_KPI(int(request.kpi_id.uuid))
-            print(self.sql_db.get_KPIS())
+            #LOGGER.info('sql_db.get_KPIS={:s}'.format(str(self.sql_db.get_KPIS())))
+            #LOGGER.info('kpi_db={:s}'.format(str(kpi_db)))
+            if kpi_db is None: return None
 
             kpiDescriptor = monitoring_pb2.KpiDescriptor()
 
@@ -159,5 +185,3 @@ class MonitoringServiceServicerImpl(monitoring_pb2_grpc.MonitoringServiceService
 
         except Exception as e:  # pragma: no cover
             LOGGER.exception('GetKpiDescriptor exception')
-
-
diff --git a/src/monitoring/service/SqliteTools.py b/src/monitoring/service/SqliteTools.py
index d03d6699b5ff29bc4bad4bd087cb6c0a965e397f..092d07e9b961e98a91bb244bcc992c701ad3cd72 100644
--- a/src/monitoring/service/SqliteTools.py
+++ b/src/monitoring/service/SqliteTools.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import sqlite3 as sl
 
 class SQLite():
@@ -53,6 +67,7 @@ class SQLite():
         
     def get_KPIS(self):
         data = self.client.execute("SELECT * FROM KPI")
-        for row in data:
-            print(row)
+        #print("\n")
+        #for row in data:
+        #    print(row)
         return data.fetchall()
\ No newline at end of file
diff --git a/src/monitoring/service/__init__.py b/src/monitoring/service/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/monitoring/service/__init__.py
+++ b/src/monitoring/service/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/monitoring/service/__main__.py b/src/monitoring/service/__main__.py
index ea189e1c905b07f92ef3e9cd100763eb7dffa08f..7835b4fc8a37f27419cebebfd0bf75b86820f38d 100644
--- a/src/monitoring/service/__main__.py
+++ b/src/monitoring/service/__main__.py
@@ -1,43 +1,59 @@
-import logging, os, signal, sys, threading
-import time
-
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging, signal, sys, threading, socket
+
+from common.Settings import get_setting, wait_for_environment_variables
 from context.client.ContextClient import ContextClient
-from monitoring.Config import GRPC_SERVICE_PORT, GRPC_MAX_WORKERS, GRPC_GRACE_PERIOD, LOG_LEVEL, METRICS_PORT
+from monitoring.Config import (
+    GRPC_SERVICE_PORT, GRPC_MAX_WORKERS, GRPC_GRACE_PERIOD, LOG_LEVEL, METRICS_PORT, CONTEXT_GRPC_SERVICE_PORT,
+    CONTEXT_SERVICE_HOST)
 
-from common.logger import getJSONLogger
 from monitoring.client.monitoring_client import MonitoringClient
 from monitoring.proto import monitoring_pb2
 from monitoring.service.EventTools import EventsDeviceCollector
 from monitoring.service.MonitoringService import MonitoringService
 
-LOGGER = getJSONLogger('monitoringservice-server')
-LOGGER.setLevel('DEBUG')
-
 from prometheus_client import start_http_server
 
 terminate = threading.Event()
-logger = None
+LOGGER = None
+LOCALHOST = '127.0.0.1'
 
 def signal_handler(signal, frame):
-    global terminate, logger
     LOGGER.warning('Terminate signal received')
     terminate.set()
 
 def start_monitoring():
-    LOGGER.info('Start Monitoring...')
-    context_client_grpc = ContextClient(address='localhost', port='2020')
-    monitoring_client = MonitoringClient(server='localhost', port='7070')  # instantiate the client
+    LOGGER.info('Start Monitoring...',)
 
-    while True:
-        if terminate.is_set():
-            LOGGER.warning("Stopping execution...")
+    grpc_service_port    = get_setting('MONITORINGSERVICE_SERVICE_PORT_GRPC', default=GRPC_SERVICE_PORT        )
+    context_service_host = get_setting('CONTEXTSERVICE_SERVICE_HOST',         default=CONTEXT_SERVICE_HOST     )
+    context_service_port = get_setting('CONTEXTSERVICE_SERVICE_PORT_GRPC',    default=CONTEXT_GRPC_SERVICE_PORT)
 
-            break
+    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    if s.connect_ex((context_service_host, int(context_service_port))) != 0:
+        LOGGER.info('Context service is not reachable')
+        return
 
-        # Start Listening Events
-        events_collector = EventsDeviceCollector(context_client_grpc, monitoring_client)
-        events_collector.start()
+    context_client_grpc = ContextClient(address=context_service_host, port=context_service_port)
+    monitoring_client = MonitoringClient(server=LOCALHOST, port=grpc_service_port)  # instantiate the client
+    events_collector = EventsDeviceCollector(context_client_grpc, monitoring_client)
+    events_collector.start()
 
+    # Iterate while terminate is not set
+    while not terminate.is_set():
         list_new_kpi_ids = events_collector.listen_events()
 
         # Monitor Kpis
@@ -46,46 +62,53 @@ def start_monitoring():
                 # Create Monitor Kpi Requests
                 monitor_kpi_request = monitoring_pb2.MonitorKpiRequest()
                 monitor_kpi_request.kpi_id.CopyFrom(kpi_id)
-                monitor_kpi_request.sampling_duration_s = 120
-                monitor_kpi_request.sampling_interval_s = 5
-
-                # MonitorKpi(monitor_kpi_request)
-
+                monitor_kpi_request.sampling_duration_s = 86400
+                monitor_kpi_request.sampling_interval_s = 30
 
+                monitoring_client.MonitorKpi(monitor_kpi_request)
+    else:
+        # Terminate is set, looping terminates
+        LOGGER.warning("Stopping execution...")
 
+    events_collector.start()
 
 def main():
-    global terminate, logger
+    global LOGGER
 
-    service_port = GRPC_SERVICE_PORT
-    max_workers  = GRPC_MAX_WORKERS
-    grace_period = GRPC_GRACE_PERIOD
-    log_level    = LOG_LEVEL
-    metrics_port = METRICS_PORT
+    grpc_service_port = get_setting('MONITORINGSERVICE_SERVICE_PORT_GRPC', default=GRPC_SERVICE_PORT)
+    max_workers       = get_setting('MAX_WORKERS',                         default=GRPC_MAX_WORKERS )
+    grace_period      = get_setting('GRACE_PERIOD',                        default=GRPC_GRACE_PERIOD)
+    log_level         = get_setting('LOG_LEVEL',                           default=LOG_LEVEL        )
+    metrics_port      = get_setting('METRICS_PORT',                        default=METRICS_PORT     )
 
     logging.basicConfig(level=log_level)
-    logger = logging.getLogger(__name__)
+    LOGGER = logging.getLogger(__name__)
+
+    wait_for_environment_variables([
+        'CONTEXTSERVICE_SERVICE_HOST', 'CONTEXTSERVICE_SERVICE_PORT_GRPC',
+        'DEVICESERVICE_SERVICE_HOST', 'DEVICESERVICE_SERVICE_PORT_GRPC'
+    ])
 
     signal.signal(signal.SIGINT,  signal_handler)
     signal.signal(signal.SIGTERM, signal_handler)
 
-    logger.info('Starting...')
+    LOGGER.info('Starting...')
     # Start metrics server
     start_http_server(metrics_port)
 
     # Starting monitoring service
-    grpc_service = MonitoringService(port=service_port, max_workers=max_workers, grace_period=grace_period)
+    grpc_service = MonitoringService(port=grpc_service_port, max_workers=max_workers, grace_period=grace_period)
     grpc_service.start()
 
-    # start_monitoring()
+    start_monitoring()
 
     # Wait for Ctrl+C or termination signal
     while not terminate.wait(timeout=0.1): pass
 
-    logger.info('Terminating...')
+    LOGGER.info('Terminating...')
     grpc_service.stop()
 
-    logger.info('Bye')
+    LOGGER.info('Bye')
     return 0
 
 if __name__ == '__main__':
diff --git a/src/monitoring/tests/__init__.py b/src/monitoring/tests/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/monitoring/tests/__init__.py
+++ b/src/monitoring/tests/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/monitoring/tests/test_unitary.py b/src/monitoring/tests/test_unitary.py
index b77699c5b00b58d19c724a98516983754097b5f7..97cdf48905cfc0dfae0dc1a4fa1f2c84ea50df7a 100644
--- a/src/monitoring/tests/test_unitary.py
+++ b/src/monitoring/tests/test_unitary.py
@@ -1,14 +1,29 @@
-import logging, grpc
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging
 import os
-import sqlite3
-
+import socket
 import pytest
 from typing import Tuple
 
+
 from monitoring.proto import context_pb2, kpi_sample_types_pb2
 from monitoring.proto import monitoring_pb2
 from monitoring.client.monitoring_client import MonitoringClient
-from monitoring.Config import GRPC_SERVICE_PORT, GRPC_MAX_WORKERS, GRPC_GRACE_PERIOD
+from monitoring.Config import GRPC_SERVICE_PORT, GRPC_MAX_WORKERS, GRPC_GRACE_PERIOD, DEVICE_GRPC_GRACE_PERIOD, DEVICE_GRPC_MAX_WORKERS, DEVICE_GRPC_SERVICE_PORT, DEVICE_SERVICE_HOST
+from monitoring.proto.kpi_sample_types_pb2 import KpiSampleType
 from monitoring.service import SqliteTools, InfluxTools
 from monitoring.service.MonitoringService import MonitoringService
 from monitoring.service.EventTools import EventsDeviceCollector
@@ -23,7 +38,7 @@ from context.client.ContextClient import ContextClient
 from context.service.grpc_server.ContextService import ContextService
 from context.service.Populate import populate
 from context.proto.context_pb2 import EventTypeEnum, DeviceEvent, Device
-from context.tests.example_objects import (DEVICE1, DEVICE1_UUID)
+from context.tests.Objects import (DEVICE_R1, DEVICE_R1_UUID)
 
 LOGGER = logging.getLogger(__name__)
 LOGGER.setLevel(logging.DEBUG)
@@ -37,12 +52,16 @@ LISTEN_ADDRESS = '[::]'
 GRPC_PORT_MONITORING = 7070
 
 GRPC_PORT_CONTEXT    = 10000 + grpc_port_context    # avoid privileged ports
+DEVICE_GRPC_SERVICE_PORT = 10000 + DEVICE_GRPC_SERVICE_PORT # avoid privileged ports
+MONITORING_GRPC_SERVICE_PORT = GRPC_PORT_MONITORING # avoid privileged ports
+
 
 SCENARIOS = [ # comment/uncomment scenarios to activate/deactivate them in the test unit
     ('all_inmemory', DatabaseBackendEnum.INMEMORY, {},           MessageBrokerBackendEnum.INMEMORY, {}          ),
 ]
 
 INFLUXDB_HOSTNAME = os.environ.get("INFLUXDB_HOSTNAME")
+INFLUXDB_PORT = os.environ.get("INFLUXDB_PORT")
 INFLUXDB_USER = os.environ.get("INFLUXDB_USER")
 INFLUXDB_PASSWORD = os.environ.get("INFLUXDB_PASSWORD")
 INFLUXDB_DATABASE = os.environ.get("INFLUXDB_DATABASE")
@@ -131,7 +150,7 @@ def sql_db():
 
 @pytest.fixture(scope='session')
 def influx_db():
-    influx_db = InfluxTools.Influx(INFLUXDB_HOSTNAME, "8086", INFLUXDB_USER, INFLUXDB_PASSWORD, INFLUXDB_DATABASE)
+    influx_db = InfluxTools.Influx(INFLUXDB_HOSTNAME, INFLUXDB_PORT, INFLUXDB_USER, INFLUXDB_PASSWORD, INFLUXDB_DATABASE)
     return influx_db
 
 @pytest.fixture(scope='session')
@@ -170,6 +189,16 @@ def include_kpi_request():
 
     return include_kpi_request
 
+@pytest.fixture(scope='session')
+def address():
+    address = '127.0.0.1'
+    return address
+
+@pytest.fixture(scope='session')
+def port():
+    port = 7070
+    return port
+
 ###########################
 # Tests Implementation
 ###########################
@@ -328,8 +357,8 @@ def test_events_tools(context_client_grpc: ContextClient,  # pylint: disable=red
     populate('localhost', GRPC_PORT_CONTEXT) # place this call in the appropriate line, according to your tests
 
     # ----- Update the object ------------------------------------------------------------------------------------------
-    response = context_client_grpc.SetDevice(Device(**DEVICE1))
-    assert response.device_uuid.uuid == DEVICE1_UUID
+    response = context_client_grpc.SetDevice(Device(**DEVICE_R1))
+    assert response.device_uuid.uuid == DEVICE_R1_UUID
 
     events_collector.stop()
 
@@ -364,7 +393,7 @@ def test_get_device_events(context_client_grpc: ContextClient,  # pylint: disabl
 
     assert isinstance(event, DeviceEvent)
     assert event.event.event_type == EventTypeEnum.EVENTTYPE_CREATE
-    assert event.device_id.device_uuid.uuid == DEVICE1_UUID
+    assert event.device_id.device_uuid.uuid == DEVICE_R1_UUID
 
     events_collector.stop()
 
@@ -397,3 +426,12 @@ def test_listen_events(monitoring_client: MonitoringClient,
 
     assert bool(kpi_id_list) == True
 
+def test_socket_ports(address, port):
+    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    result = s.connect_ex((address,port))
+
+    if result == 0:
+        print('socket is open')
+    else:
+        print('socket is not open')
+    s.close()
diff --git a/src/opticalattackmitigator/.gitlab-ci.yml b/src/opticalattackmitigator/.gitlab-ci.yml
index 47a25be3c9c93c008821b1015fe66d1e06a75920..a39a83f6f4a7f98cce08fd4b7d5b9a0379105935 100644
--- a/src/opticalattackmitigator/.gitlab-ci.yml
+++ b/src/opticalattackmitigator/.gitlab-ci.yml
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 # build, tag and push the Docker image to the gitlab registry
 build opticalattackmitigator:
   variables:
@@ -10,8 +24,8 @@ build opticalattackmitigator:
     - 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"' 
@@ -20,7 +34,7 @@ build opticalattackmitigator:
       - src/$IMAGE_NAME/Dockerfile
       - src/$IMAGE_NAME/tests/*.py
       - src/$IMAGE_NAME/tests/Dockerfile
-      - manifests/$IMAGE_NAME.yaml
+      - manifests/${IMAGE_NAME}service.yaml
       - .gitlab-ci.yml
 
 # apply unit test to the opticalattackmitigator component
@@ -37,12 +51,15 @@ unit test opticalattackmitigator:
     - 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 10007:10007 --network=teraflowbridge --rm $CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG
+    - docker run --name $IMAGE_NAME -d -p 10007:10007 -v "$PWD/src/$IMAGE_NAME/tests:/opt/results" --network=teraflowbridge --rm $CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG
     - sleep 5
     - docker ps -a
-    - docker exec -i $IMAGE_NAME bash -c "pytest --log-level=DEBUG --verbose $IMAGE_NAME/tests/test_unitary.py"
+    - docker exec -i $IMAGE_NAME bash -c "coverage run -m pytest --log-level=INFO --verbose $IMAGE_NAME/tests/test_unitary.py --junitxml=/opt/results/${IMAGE_NAME}_report.xml"
+    - docker exec -i $IMAGE_NAME bash -c "coverage xml -o /opt/results/${IMAGE_NAME}_coverage.xml"
+    - docker exec -i $IMAGE_NAME bash -c "coverage report --include='${IMAGE_NAME}/*' --show-missing"
+  coverage: '/TOTAL\s+\d+\s+\d+\s+(\d+%)/'
   after_script:
-    #- docker rm -f $IMAGE_NAME
+    - docker rm -f $IMAGE_NAME
     - docker network rm teraflowbridge
   rules:
     - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && ($CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "develop" || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == $CI_DEFAULT_BRANCH)'
@@ -54,6 +71,11 @@ unit test opticalattackmitigator:
       - src/$IMAGE_NAME/tests/Dockerfile
       - manifests/$IMAGE_NAMEservice.yaml
       - .gitlab-ci.yml
+  artifacts:
+    when: always
+    reports:
+      junit: src/$IMAGE_NAME/tests/${IMAGE_NAME}_report.xml
+      cobertura: src/$IMAGE_NAME/tests/${IMAGE_NAME}_coverage.xml
 
 
 # Deployment of the opticalattackmitigator service in Kubernetes Cluster
@@ -69,7 +91,7 @@ deploy opticalattackmitigator:
     - 'sed -i "s/$IMAGE_NAME:.*/$IMAGE_NAME:$IMAGE_TAG/" manifests/${IMAGE_NAME}service.yaml'
     - kubectl version
     - kubectl get all
-    - kubectl apply -f "manifests/$IMAGE_NAME.yaml"
+    - kubectl apply -f "manifests/${IMAGE_NAME}service.yaml"
     - kubectl get all
   # environment:
   #   name: test
diff --git a/src/opticalattackmitigator/Config.py b/src/opticalattackmitigator/Config.py
index 64785206cff4f3b5049d78e15a8d8ac0b82caf9b..130381d8bd1db49803aefa992435808bed3a87d3 100644
--- a/src/opticalattackmitigator/Config.py
+++ b/src/opticalattackmitigator/Config.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging
 
 # General settings
diff --git a/src/opticalattackmitigator/Dockerfile b/src/opticalattackmitigator/Dockerfile
index 126786edd5466fbfdfadef1af804aba540f0bd3a..40469805410e26b145d8a84beda8247fb7c7a56d 100644
--- a/src/opticalattackmitigator/Dockerfile
+++ b/src/opticalattackmitigator/Dockerfile
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 FROM python:3-slim
 
 # Install dependencies
diff --git a/src/opticalattackmitigator/__init__.py b/src/opticalattackmitigator/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/opticalattackmitigator/__init__.py
+++ b/src/opticalattackmitigator/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/opticalattackmitigator/client/OpticalAttackMitigatorClient.py b/src/opticalattackmitigator/client/OpticalAttackMitigatorClient.py
index f303dcc3fc8593b20b0a0ab1a7f6d46a724edec1..d0c53e57ee54a42955210308492e27d0bb332729 100644
--- a/src/opticalattackmitigator/client/OpticalAttackMitigatorClient.py
+++ b/src/opticalattackmitigator/client/OpticalAttackMitigatorClient.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import grpc, logging
 from common.tools.client.RetryDecorator import retry, delay_exponential
 from opticalattackmitigator.proto.optical_attack_mitigator_pb2 import AttackDescription, AttackResponse
@@ -6,6 +20,7 @@ from opticalattackmitigator.proto.optical_attack_mitigator_pb2_grpc import Attac
 LOGGER = logging.getLogger(__name__)
 MAX_RETRIES = 15
 DELAY_FUNCTION = delay_exponential(initial=0.01, increment=2.0, maximum=5.0)
+RETRY_DECORATOR = retry(max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
 
 class OpticalAttackMitigatorClient:
     def __init__(self, address, port):
@@ -25,7 +40,7 @@ class OpticalAttackMitigatorClient:
         self.channel = None
         self.stub = None
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def NotifyAttack(self, request : AttackDescription) -> AttackResponse:
         LOGGER.debug('NotifyAttack request: {:s}'.format(str(request)))
         response = self.stub.NotifyAttack(request)
diff --git a/src/opticalattackmitigator/client/__init__.py b/src/opticalattackmitigator/client/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/opticalattackmitigator/client/__init__.py
+++ b/src/opticalattackmitigator/client/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/opticalattackmitigator/proto/__init__.py b/src/opticalattackmitigator/proto/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/opticalattackmitigator/proto/__init__.py
+++ b/src/opticalattackmitigator/proto/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/opticalattackmitigator/requirements.in b/src/opticalattackmitigator/requirements.in
index 00acd77fe30ca10ffe3af04f7cf7ced2cb9256f8..ea6b450c9c240c76b9f54ce4f3c0c6c25ec62289 100644
--- a/src/opticalattackmitigator/requirements.in
+++ b/src/opticalattackmitigator/requirements.in
@@ -7,3 +7,4 @@ redis
 # from the monitoring component
 influxdb
 python-json-logger
+coverage
diff --git a/src/opticalattackmitigator/service/OpticalAttackMitigatorService.py b/src/opticalattackmitigator/service/OpticalAttackMitigatorService.py
index fc2e86067b437bb4facd1e6608f48439fbf2a03e..875276b4ce80335b9eb0081a05d38bfa2c378e43 100644
--- a/src/opticalattackmitigator/service/OpticalAttackMitigatorService.py
+++ b/src/opticalattackmitigator/service/OpticalAttackMitigatorService.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import grpc
 import logging
 from concurrent import futures
diff --git a/src/opticalattackmitigator/service/OpticalAttackMitigatorServiceServicerImpl.py b/src/opticalattackmitigator/service/OpticalAttackMitigatorServiceServicerImpl.py
index d7cbc6b0956f26fb87e7d2567cc1c9065482cfd4..4a2dd041b52eaf89bda65acb7ae1e46beed8c48a 100644
--- a/src/opticalattackmitigator/service/OpticalAttackMitigatorServiceServicerImpl.py
+++ b/src/opticalattackmitigator/service/OpticalAttackMitigatorServiceServicerImpl.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import os, grpc, logging, random
 from influxdb import InfluxDBClient
 from common.rpc_method_wrapper.Decorator import create_metrics, safe_and_metered_rpc_method
diff --git a/src/opticalattackmitigator/service/__init__.py b/src/opticalattackmitigator/service/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/opticalattackmitigator/service/__init__.py
+++ b/src/opticalattackmitigator/service/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/opticalattackmitigator/service/__main__.py b/src/opticalattackmitigator/service/__main__.py
index bcbf4e3e4c9c74ef25e6d95f6d70edf5fba25c68..10b50ac8ba5f5c7a11c5963eb8533bf6d05dfee2 100644
--- a/src/opticalattackmitigator/service/__main__.py
+++ b/src/opticalattackmitigator/service/__main__.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import os, logging, signal, sys, time, threading, multiprocessing
 from prometheus_client import start_http_server
 from common.Settings import get_setting
diff --git a/src/opticalattackmitigator/tests/__init__.py b/src/opticalattackmitigator/tests/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/opticalattackmitigator/tests/__init__.py
+++ b/src/opticalattackmitigator/tests/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/opticalattackmitigator/tests/test_unitary.py b/src/opticalattackmitigator/tests/test_unitary.py
index afcb1a699186fb10408cfdc4309fc790fa91c198..74f91a837bf0c83026c88b28ae45f21493a2599d 100644
--- a/src/opticalattackmitigator/tests/test_unitary.py
+++ b/src/opticalattackmitigator/tests/test_unitary.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging, pytest
 from opticalattackmitigator.Config import GRPC_SERVICE_PORT, GRPC_MAX_WORKERS, GRPC_GRACE_PERIOD
 from opticalattackmitigator.client.OpticalAttackMitigatorClient import OpticalAttackMitigatorClient
diff --git a/src/opticalcentralizedattackdetector/.gitlab-ci.yml b/src/opticalcentralizedattackdetector/.gitlab-ci.yml
index 11a2c97e6b1944584d5aa48774f7f7a85b8e902c..8a1445e80347fe64dac1beb1780894fa98ba85fd 100644
--- a/src/opticalcentralizedattackdetector/.gitlab-ci.yml
+++ b/src/opticalcentralizedattackdetector/.gitlab-ci.yml
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 # build, tag and push the Docker image to the gitlab registry
 build opticalcentralizedattackdetector:
   variables:
@@ -10,8 +24,8 @@ build opticalcentralizedattackdetector:
     - 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"' 
@@ -20,7 +34,7 @@ build opticalcentralizedattackdetector:
       - src/$IMAGE_NAME/Dockerfile
       - src/$IMAGE_NAME/tests/*.py
       - src/$IMAGE_NAME/tests/Dockerfile
-      - manifests/$IMAGE_NAME.yaml
+      - manifests/${IMAGE_NAME}service.yaml
       - .gitlab-ci.yml
 
 # apply unit test to the opticalcentralizedattackdetector component
@@ -37,12 +51,15 @@ unit test opticalcentralizedattackdetector:
     - 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 10005:10005 --network=teraflowbridge --rm $CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG
+    - docker run --name $IMAGE_NAME -d -p 10005:10005 -v "$PWD/src/$IMAGE_NAME/tests:/opt/results" --network=teraflowbridge --rm $CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG
     - sleep 5
     - docker ps -a
-    - docker exec -i $IMAGE_NAME bash -c "pytest --log-level=DEBUG --verbose $IMAGE_NAME/tests/test_unitary.py"
+    - docker exec -i $IMAGE_NAME bash -c "coverage run -m pytest --log-level=INFO --verbose $IMAGE_NAME/tests/test_unitary.py --junitxml=/opt/results/${IMAGE_NAME}_report.xml"
+    - docker exec -i $IMAGE_NAME bash -c "coverage xml -o /opt/results/${IMAGE_NAME}_coverage.xml"
+    - docker exec -i $IMAGE_NAME bash -c "coverage report --include='${IMAGE_NAME}/*' --show-missing"
+  coverage: '/TOTAL\s+\d+\s+\d+\s+(\d+%)/'
   after_script:
-    #- docker rm -f $IMAGE_NAME
+    - docker rm -f $IMAGE_NAME
     - docker network rm teraflowbridge
   rules:
     - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && ($CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "develop" || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == $CI_DEFAULT_BRANCH)'
@@ -54,6 +71,11 @@ unit test opticalcentralizedattackdetector:
       - src/$IMAGE_NAME/tests/Dockerfile
       - manifests/$IMAGE_NAMEservice.yaml
       - .gitlab-ci.yml
+  artifacts:
+    when: always
+    reports:
+      junit: src/$IMAGE_NAME/tests/${IMAGE_NAME}_report.xml
+      cobertura: src/$IMAGE_NAME/tests/${IMAGE_NAME}_coverage.xml
 
 
 # Deployment of the opticalcentralizedattackdetector service in Kubernetes Cluster
@@ -69,7 +91,7 @@ deploy opticalcentralizedattackdetector:
     - 'sed -i "s/$IMAGE_NAME:.*/$IMAGE_NAME:$IMAGE_TAG/" manifests/${IMAGE_NAME}service.yaml'
     - kubectl version
     - kubectl get all
-    - kubectl apply -f "manifests/$IMAGE_NAME.yaml"
+    - kubectl apply -f "manifests/${IMAGE_NAME}service.yaml"
     - kubectl get all
   # environment:
   #   name: test
diff --git a/src/opticalcentralizedattackdetector/Config.py b/src/opticalcentralizedattackdetector/Config.py
index 5a6b7aa5651566fb2305f08baec7c6f33872fd36..5fd3e97e9f590bea9cdfc6ebf8e93e90fb0a4fea 100644
--- a/src/opticalcentralizedattackdetector/Config.py
+++ b/src/opticalcentralizedattackdetector/Config.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging
 
 # General settings
@@ -11,7 +25,7 @@ GRPC_GRACE_PERIOD = 60
 # service settings
 MONITORING_INTERVAL = 2  # monitoring interval in seconds
 #TODO: adjust the addresses below for the specific case
-MONITORING_SERVICE_ADDRESS = 'monitoring'  # address/name of the monitoring service
+MONITORING_SERVICE_ADDRESS = 'monitoringservice'  # address/name of the monitoring service
 # MONITORING_SERVICE_ADDRESS = '10.99.41.20'  # address/name of the monitoring service
 CONTEXT_SERVICE_ADDRESS = 'contextservice'  # address/name of the monitoring service
 # CONTEXT_SERVICE_ADDRESS = '10.107.199.65'  # address/name of the monitoring service
diff --git a/src/opticalcentralizedattackdetector/Dockerfile b/src/opticalcentralizedattackdetector/Dockerfile
index 43f7b9457fe1413ca9d9dc520433f184717d0c2a..190df9beae66e82900a8ab78a12d062e4d319fda 100644
--- a/src/opticalcentralizedattackdetector/Dockerfile
+++ b/src/opticalcentralizedattackdetector/Dockerfile
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 FROM python:3-slim
 
 # Install dependencies
diff --git a/src/opticalcentralizedattackdetector/__init__.py b/src/opticalcentralizedattackdetector/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/opticalcentralizedattackdetector/__init__.py
+++ b/src/opticalcentralizedattackdetector/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/opticalcentralizedattackdetector/client/OpticalCentralizedAttackDetectorClient.py b/src/opticalcentralizedattackdetector/client/OpticalCentralizedAttackDetectorClient.py
index c28507581d99c1df47e92eead1fce6ab1c7db3a7..cfb78b41ec6a6d5385692813e1a839566d1c90e7 100644
--- a/src/opticalcentralizedattackdetector/client/OpticalCentralizedAttackDetectorClient.py
+++ b/src/opticalcentralizedattackdetector/client/OpticalCentralizedAttackDetectorClient.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import grpc, logging
 from common.tools.client.RetryDecorator import retry, delay_exponential
 from opticalcentralizedattackdetector.proto.context_pb2 import Empty, Service
@@ -7,6 +21,7 @@ from opticalcentralizedattackdetector.proto.optical_centralized_attack_detector_
 LOGGER = logging.getLogger(__name__)
 MAX_RETRIES = 15
 DELAY_FUNCTION = delay_exponential(initial=0.01, increment=2.0, maximum=5.0)
+RETRY_DECORATOR = retry(max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
 
 class OpticalCentralizedAttackDetectorClient:
     def __init__(self, address, port):
@@ -26,28 +41,28 @@ class OpticalCentralizedAttackDetectorClient:
         self.channel = None
         self.stub = None
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def NotifyServiceUpdate(self, request : Service) -> Empty:
         LOGGER.debug('NotifyServiceUpdate request: {:s}'.format(str(request)))
         response = self.stub.NotifyServiceUpdate(request)
         LOGGER.debug('NotifyServiceUpdate result: {:s}'.format(str(response)))
         return response
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def DetectAttack(self, request : Empty) -> Empty:
         LOGGER.debug('DetectAttack request: {:s}'.format(str(request)))
         response = self.stub.DetectAttack(request)
         LOGGER.debug('DetectAttack result: {:s}'.format(str(response)))
         return response
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def ReportSummarizedKpi(self, request : KpiList) -> Empty:
         LOGGER.debug('ReportSummarizedKpi request: {:s}'.format(str(request)))
         response = self.stub.ReportSummarizedKpi(request)
         LOGGER.debug('ReportSummarizedKpi result: {:s}'.format(str(response)))
         return response
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def ReportKpi(self, request : KpiList) -> Empty:
         LOGGER.debug('ReportKpi request: {:s}'.format(str(request)))
         response = self.stub.ReportKpi(request)
diff --git a/src/opticalcentralizedattackdetector/client/__init__.py b/src/opticalcentralizedattackdetector/client/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/opticalcentralizedattackdetector/client/__init__.py
+++ b/src/opticalcentralizedattackdetector/client/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/opticalcentralizedattackdetector/proto/__init__.py b/src/opticalcentralizedattackdetector/proto/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/opticalcentralizedattackdetector/proto/__init__.py
+++ b/src/opticalcentralizedattackdetector/proto/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/opticalcentralizedattackdetector/requirements.in b/src/opticalcentralizedattackdetector/requirements.in
index 00acd77fe30ca10ffe3af04f7cf7ced2cb9256f8..ea6b450c9c240c76b9f54ce4f3c0c6c25ec62289 100644
--- a/src/opticalcentralizedattackdetector/requirements.in
+++ b/src/opticalcentralizedattackdetector/requirements.in
@@ -7,3 +7,4 @@ redis
 # from the monitoring component
 influxdb
 python-json-logger
+coverage
diff --git a/src/opticalcentralizedattackdetector/service/OpticalCentralizedAttackDetectorService.py b/src/opticalcentralizedattackdetector/service/OpticalCentralizedAttackDetectorService.py
index a017292da6228bdf1ba67b48c1b1dcba5dff6ee8..e29566833a781afc8bf85b41b7d5dd10f2a4a2f5 100644
--- a/src/opticalcentralizedattackdetector/service/OpticalCentralizedAttackDetectorService.py
+++ b/src/opticalcentralizedattackdetector/service/OpticalCentralizedAttackDetectorService.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import grpc
 import logging
 from concurrent import futures
diff --git a/src/opticalcentralizedattackdetector/service/OpticalCentralizedAttackDetectorServiceServicerImpl.py b/src/opticalcentralizedattackdetector/service/OpticalCentralizedAttackDetectorServiceServicerImpl.py
index 48e72a72d2d68e4107957228fca04c9deb55ae4b..9c77a959cbdc484af143015a2868121af3845b0a 100644
--- a/src/opticalcentralizedattackdetector/service/OpticalCentralizedAttackDetectorServiceServicerImpl.py
+++ b/src/opticalcentralizedattackdetector/service/OpticalCentralizedAttackDetectorServiceServicerImpl.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import os, grpc, logging, random
 from influxdb import InfluxDBClient
 from common.rpc_method_wrapper.Decorator import create_metrics, safe_and_metered_rpc_method
diff --git a/src/opticalcentralizedattackdetector/service/__init__.py b/src/opticalcentralizedattackdetector/service/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/opticalcentralizedattackdetector/service/__init__.py
+++ b/src/opticalcentralizedattackdetector/service/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/opticalcentralizedattackdetector/service/__main__.py b/src/opticalcentralizedattackdetector/service/__main__.py
index 37cff6b7dec1554b69d93148307e074240f2dca2..d21b96cf8adc6f9d9968f3553c9249a3b8cedf31 100644
--- a/src/opticalcentralizedattackdetector/service/__main__.py
+++ b/src/opticalcentralizedattackdetector/service/__main__.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import os, logging, signal, sys, time, threading, multiprocessing
 from prometheus_client import start_http_server
 
diff --git a/src/opticalcentralizedattackdetector/tests/__init__.py b/src/opticalcentralizedattackdetector/tests/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/opticalcentralizedattackdetector/tests/__init__.py
+++ b/src/opticalcentralizedattackdetector/tests/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/opticalcentralizedattackdetector/tests/example_objects.py b/src/opticalcentralizedattackdetector/tests/example_objects.py
index 206d17cb70869a66cf1739f54a83598b1e031edb..3c5a26b6d0bde888560741f052906e0d2694c91d 100644
--- a/src/opticalcentralizedattackdetector/tests/example_objects.py
+++ b/src/opticalcentralizedattackdetector/tests/example_objects.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 from copy import deepcopy
 from common.Constants import DEFAULT_CONTEXT_UUID, DEFAULT_TOPOLOGY_UUID
 from context.proto.context_pb2 import (
diff --git a/src/opticalcentralizedattackdetector/tests/test_unitary.py b/src/opticalcentralizedattackdetector/tests/test_unitary.py
index a04afdf2379569ec4d723fa9c5c0895a0d9c47f8..da0f4ca9f78478073354d8645cf0b455a5f7a874 100644
--- a/src/opticalcentralizedattackdetector/tests/test_unitary.py
+++ b/src/opticalcentralizedattackdetector/tests/test_unitary.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging, pytest
 from unittest.mock import patch
 from opticalcentralizedattackdetector.Config import GRPC_SERVICE_PORT, GRPC_MAX_WORKERS, GRPC_GRACE_PERIOD
diff --git a/src/policy/.gitlab-ci.yml b/src/policy/.gitlab-ci.yml
index 13a071124e2cac693401e155412dee48e1f36b56..1f855bb18188790697b1adcaa998fe9196d71055 100644
--- a/src/policy/.gitlab-ci.yml
+++ b/src/policy/.gitlab-ci.yml
@@ -1,15 +1,32 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 # Package application needed to run tests & build the image on next stage
 build policy:
   variables:
     IMAGE_NAME: 'policy' # name of the microservice
     IMAGE_NAME_TEST: 'policy-test' # name of the microservice
-    IMAGE_TAG: '0.0.1' # tag of the container image (production, development, etc)
+    IMAGE_TAG: '0.1.0' # tag of the container image (production, development, etc)
   stage: build
   script:
     - docker build -t "$IMAGE_NAME:$IMAGE_TAG" -f ./src/$IMAGE_NAME/src/main/docker/Dockerfile.multistage.jvm ./src/$IMAGE_NAME/ --target builder
   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"'
     - changes:
         - src/$IMAGE_NAME/**
+        - manifests/${IMAGE_NAME}service.yaml
         - .gitlab-ci.yml
 
 # Run tests, build & push the image
@@ -17,7 +34,7 @@ unit_test policy:
   variables:
     IMAGE_NAME: 'policy' # name of the microservice
     IMAGE_NAME_TEST: 'policy-test' # name of the microservice
-    IMAGE_TAG: '0.0.1' # tag of the container image (production, development, etc)
+    IMAGE_TAG: '0.1.0' # tag of the container image (production, development, etc)
   stage: unit_test
   needs:
     - build policy
@@ -29,8 +46,11 @@ unit_test policy:
     - docker tag "$IMAGE_NAME:$IMAGE_TAG" "$CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG"
     - docker push "$CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG"
   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"'
     - changes:
         - src/$IMAGE_NAME/**
+        - manifests/${IMAGE_NAME}service.yaml
         - .gitlab-ci.yml
 
 # Deployment of policy service in Kubernetes Cluster
@@ -42,6 +62,12 @@ deploy policy:
   script:
     - kubectl version
     - kubectl get all
+    - kubectl delete --ignore-not-found=true -f "manifests/policyservice.yaml"
     - kubectl apply -f "manifests/policyservice.yaml"
     - kubectl delete pods --selector app=policyservice
-    - kubectl get all
\ No newline at end of file
+    - kubectl get all
+  rules:
+    - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && ($CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "develop" || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == $CI_DEFAULT_BRANCH)'
+      when: manual
+    - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "develop"'
+      when: manual
\ No newline at end of file
diff --git a/src/policy/pom.xml b/src/policy/pom.xml
index 399c3a18c8f4fb62c42290191f59b179b1f0f2c3..02077d79a1649b0531aa7dca09fa08fe806f82b5 100644
--- a/src/policy/pom.xml
+++ b/src/policy/pom.xml
@@ -1,11 +1,27 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
 
     <groupId>eu.teraflow</groupId>
     <artifactId>policy</artifactId>
-    <version>0.0.1</version>
+    <version>0.1.0</version>
     <name>TeraFlow Policy Component</name>
     <description>TeraFlow Policy Component</description>
 
diff --git a/src/policy/src/main/docker/Dockerfile.multistage.jvm b/src/policy/src/main/docker/Dockerfile.multistage.jvm
index 1e21abcb49499f7f407602494db9ac91bb9eb187..280e4142d00fa6acdf837e27def9881015484e3c 100644
--- a/src/policy/src/main/docker/Dockerfile.multistage.jvm
+++ b/src/policy/src/main/docker/Dockerfile.multistage.jvm
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 # Multi-stage Docker image build
 
 # Stage 1
diff --git a/src/policy/src/main/java/eu/teraflow/policy/PolicyGateway.java b/src/policy/src/main/java/eu/teraflow/policy/PolicyGateway.java
index 6ac456cbdbce39376f34c78b739f953d9f61c45f..b6d1de94a45b2ac5b09d28d6b749cbf1c02413ea 100644
--- a/src/policy/src/main/java/eu/teraflow/policy/PolicyGateway.java
+++ b/src/policy/src/main/java/eu/teraflow/policy/PolicyGateway.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package eu.teraflow.policy;
 
 import policy.PolicyService;
diff --git a/src/policy/src/main/java/eu/teraflow/policy/PolicyGatewayImpl.java b/src/policy/src/main/java/eu/teraflow/policy/PolicyGatewayImpl.java
index 5b371ceda7f712f0199930653015ed33e3f66b51..45eed4c910a95eee2851231be33e944a5b779668 100644
--- a/src/policy/src/main/java/eu/teraflow/policy/PolicyGatewayImpl.java
+++ b/src/policy/src/main/java/eu/teraflow/policy/PolicyGatewayImpl.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package eu.teraflow.policy;
 
 import context.ContextOuterClass;
diff --git a/src/policy/src/main/java/eu/teraflow/policy/PolicyService.java b/src/policy/src/main/java/eu/teraflow/policy/PolicyService.java
index 40bca174a1015f17a0b43a8cb40d084fac16692a..5659fd61bbb40ff0a8877bb66de734ad707f7da0 100644
--- a/src/policy/src/main/java/eu/teraflow/policy/PolicyService.java
+++ b/src/policy/src/main/java/eu/teraflow/policy/PolicyService.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package eu.teraflow.policy;
 
 import eu.teraflow.policy.model.PolicyRule;
diff --git a/src/policy/src/main/java/eu/teraflow/policy/PolicyServiceImpl.java b/src/policy/src/main/java/eu/teraflow/policy/PolicyServiceImpl.java
index c3453c33d97103c8b2042892ff32180b4a4b8235..4b8ceba90b3c969912ad813cc934a7bd4cde40fc 100644
--- a/src/policy/src/main/java/eu/teraflow/policy/PolicyServiceImpl.java
+++ b/src/policy/src/main/java/eu/teraflow/policy/PolicyServiceImpl.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package eu.teraflow.policy;
 
 import eu.teraflow.policy.model.PolicyRule;
diff --git a/src/policy/src/main/java/eu/teraflow/policy/SimpleLivenessCheck.java b/src/policy/src/main/java/eu/teraflow/policy/SimpleLivenessCheck.java
index 8619e304c77d1f557e62f4db69a8f7fb21b05c00..3b60e1ad772d8356776d131e251b7d217bf92a24 100644
--- a/src/policy/src/main/java/eu/teraflow/policy/SimpleLivenessCheck.java
+++ b/src/policy/src/main/java/eu/teraflow/policy/SimpleLivenessCheck.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package eu.teraflow.policy;
 
 import javax.enterprise.context.ApplicationScoped;
diff --git a/src/policy/src/main/java/eu/teraflow/policy/context/ContextService.java b/src/policy/src/main/java/eu/teraflow/policy/context/ContextService.java
index ce3a46158115ffb1d06243c4f4fdd849aa72f81e..f8803f5147155ac795a4b852ab424ef96c1cbef5 100644
--- a/src/policy/src/main/java/eu/teraflow/policy/context/ContextService.java
+++ b/src/policy/src/main/java/eu/teraflow/policy/context/ContextService.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package eu.teraflow.policy.context;
 
 public interface ContextService {}
diff --git a/src/policy/src/main/java/eu/teraflow/policy/context/model/ContextId.java b/src/policy/src/main/java/eu/teraflow/policy/context/model/ContextId.java
index c1390aaf7f45a8ca77ee2d5f9a86a8f84b629018..c5c2229427358ac0a1d5495f0c21653026141c03 100644
--- a/src/policy/src/main/java/eu/teraflow/policy/context/model/ContextId.java
+++ b/src/policy/src/main/java/eu/teraflow/policy/context/model/ContextId.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package eu.teraflow.policy.context.model;
 
 public class ContextId {
diff --git a/src/policy/src/main/java/eu/teraflow/policy/context/model/DeviceId.java b/src/policy/src/main/java/eu/teraflow/policy/context/model/DeviceId.java
index aed8752b67075387a4b04a7dd1d7c91895e5fc7f..af82cd4d41c417e201d5d014527304842ca472bb 100644
--- a/src/policy/src/main/java/eu/teraflow/policy/context/model/DeviceId.java
+++ b/src/policy/src/main/java/eu/teraflow/policy/context/model/DeviceId.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package eu.teraflow.policy.context.model;
 
 public class DeviceId {
diff --git a/src/policy/src/main/java/eu/teraflow/policy/context/model/Event.java b/src/policy/src/main/java/eu/teraflow/policy/context/model/Event.java
index a13f4baf5b303236a26d51828945e3bd702106e5..309f8e077b67409f92ab0b83f784b0a14b5583e8 100644
--- a/src/policy/src/main/java/eu/teraflow/policy/context/model/Event.java
+++ b/src/policy/src/main/java/eu/teraflow/policy/context/model/Event.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package eu.teraflow.policy.context.model;
 
 public class Event {
diff --git a/src/policy/src/main/java/eu/teraflow/policy/context/model/EventTypeEnum.java b/src/policy/src/main/java/eu/teraflow/policy/context/model/EventTypeEnum.java
index 70a47815ddbb5f9e9c78c1dd67c1e681461caeb7..30b890e60e6fe1fd73521a7445e2143ef0805a0c 100644
--- a/src/policy/src/main/java/eu/teraflow/policy/context/model/EventTypeEnum.java
+++ b/src/policy/src/main/java/eu/teraflow/policy/context/model/EventTypeEnum.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package eu.teraflow.policy.context.model;
 
 public enum EventTypeEnum {
diff --git a/src/policy/src/main/java/eu/teraflow/policy/context/model/ServiceId.java b/src/policy/src/main/java/eu/teraflow/policy/context/model/ServiceId.java
index f76613ac4644a8e1867c2f8c0b15737e91695a12..3680cb5ef385e4333f5e8e9707dca17d5351c9b7 100644
--- a/src/policy/src/main/java/eu/teraflow/policy/context/model/ServiceId.java
+++ b/src/policy/src/main/java/eu/teraflow/policy/context/model/ServiceId.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package eu.teraflow.policy.context.model;
 
 public class ServiceId {
diff --git a/src/policy/src/main/java/eu/teraflow/policy/context/model/Uuid.java b/src/policy/src/main/java/eu/teraflow/policy/context/model/Uuid.java
index f805ce40a6c728dae39687f3b15d228a0c8abed3..857f7a51a2a8fed2ee954da8a9f4244a57c237be 100644
--- a/src/policy/src/main/java/eu/teraflow/policy/context/model/Uuid.java
+++ b/src/policy/src/main/java/eu/teraflow/policy/context/model/Uuid.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package eu.teraflow.policy.context.model;
 
 public class Uuid {
diff --git a/src/policy/src/main/java/eu/teraflow/policy/model/PolicyRule.java b/src/policy/src/main/java/eu/teraflow/policy/model/PolicyRule.java
index 17e403e6372891a6defc6b9fcc6cad03e546cb07..d9f597405300c941b3ab569fdf925fcc29b5e802 100644
--- a/src/policy/src/main/java/eu/teraflow/policy/model/PolicyRule.java
+++ b/src/policy/src/main/java/eu/teraflow/policy/model/PolicyRule.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package eu.teraflow.policy.model;
 
 import eu.teraflow.policy.context.model.ContextId;
diff --git a/src/policy/src/main/java/eu/teraflow/policy/model/PolicyRuleAction.java b/src/policy/src/main/java/eu/teraflow/policy/model/PolicyRuleAction.java
index 2ad0a1cf78d704aca860f2c057a8135123e15a77..d3a3fd036b0b257e5b230860a529c30ad858b8b1 100644
--- a/src/policy/src/main/java/eu/teraflow/policy/model/PolicyRuleAction.java
+++ b/src/policy/src/main/java/eu/teraflow/policy/model/PolicyRuleAction.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package eu.teraflow.policy.model;
 
 public class PolicyRuleAction {
diff --git a/src/policy/src/main/java/eu/teraflow/policy/model/PolicyRuleCondition.java b/src/policy/src/main/java/eu/teraflow/policy/model/PolicyRuleCondition.java
index e9430ba33b00ccba8a47ee599487f6868127e731..8f7b74e3f4de1d0019555dcc58de8958042ece13 100644
--- a/src/policy/src/main/java/eu/teraflow/policy/model/PolicyRuleCondition.java
+++ b/src/policy/src/main/java/eu/teraflow/policy/model/PolicyRuleCondition.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package eu.teraflow.policy.model;
 
 public class PolicyRuleCondition {
diff --git a/src/policy/src/main/java/eu/teraflow/policy/model/PolicyRuleEvent.java b/src/policy/src/main/java/eu/teraflow/policy/model/PolicyRuleEvent.java
index 09906960376e9b5e44b8b76f6b18a99b4f361120..c5660b95d456d6322eafacf5c32580778bb78c2f 100644
--- a/src/policy/src/main/java/eu/teraflow/policy/model/PolicyRuleEvent.java
+++ b/src/policy/src/main/java/eu/teraflow/policy/model/PolicyRuleEvent.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package eu.teraflow.policy.model;
 
 import eu.teraflow.policy.context.model.Event;
diff --git a/src/policy/src/main/java/eu/teraflow/policy/model/PolicyRuleId.java b/src/policy/src/main/java/eu/teraflow/policy/model/PolicyRuleId.java
index 27920ae4a8a7877212f229ec236010aacf67f47a..e3a25557a8ee7012196572a6db41e5095ccc4581 100644
--- a/src/policy/src/main/java/eu/teraflow/policy/model/PolicyRuleId.java
+++ b/src/policy/src/main/java/eu/teraflow/policy/model/PolicyRuleId.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package eu.teraflow.policy.model;
 
 import eu.teraflow.policy.context.model.Uuid;
diff --git a/src/policy/src/main/java/eu/teraflow/policy/model/PolicyRulePriority.java b/src/policy/src/main/java/eu/teraflow/policy/model/PolicyRulePriority.java
index f9e42aef28f7ee43ee33e77d38a54dd92851ddbd..7113a20fa7756ecc707197232dcca6fe74d27898 100644
--- a/src/policy/src/main/java/eu/teraflow/policy/model/PolicyRulePriority.java
+++ b/src/policy/src/main/java/eu/teraflow/policy/model/PolicyRulePriority.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package eu.teraflow.policy.model;
 
 public class PolicyRulePriority {
diff --git a/src/policy/src/main/java/eu/teraflow/policy/model/PolicyRuleState.java b/src/policy/src/main/java/eu/teraflow/policy/model/PolicyRuleState.java
index 97d3280e3ab2dcec8e5c732315bd4562616ea370..465187a5fd2a717e8b022fabd4f0062d4a454dda 100644
--- a/src/policy/src/main/java/eu/teraflow/policy/model/PolicyRuleState.java
+++ b/src/policy/src/main/java/eu/teraflow/policy/model/PolicyRuleState.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package eu.teraflow.policy.model;
 
 import eu.teraflow.policy.context.model.Uuid;
diff --git a/src/policy/src/main/java/eu/teraflow/policy/model/PolicyRuleType.java b/src/policy/src/main/java/eu/teraflow/policy/model/PolicyRuleType.java
index 89dfd30013e2f9c2a86609c878f2ef59ce62e27a..1b46b249e3ca5ac49f8d23c3210cf1bb08204237 100644
--- a/src/policy/src/main/java/eu/teraflow/policy/model/PolicyRuleType.java
+++ b/src/policy/src/main/java/eu/teraflow/policy/model/PolicyRuleType.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package eu.teraflow.policy.model;
 
 public enum PolicyRuleType {
diff --git a/src/policy/src/main/java/eu/teraflow/policy/model/PolicyRuleValue.java b/src/policy/src/main/java/eu/teraflow/policy/model/PolicyRuleValue.java
index 60f7c00568905a0ee7cfa7eb1ababf85d310f79b..2e6498f233d9a31a21d7349cd53a9f36874b7413 100644
--- a/src/policy/src/main/java/eu/teraflow/policy/model/PolicyRuleValue.java
+++ b/src/policy/src/main/java/eu/teraflow/policy/model/PolicyRuleValue.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package eu.teraflow.policy.model;
 
 public class PolicyRuleValue {
diff --git a/src/policy/src/main/java/eu/teraflow/policy/model/PolicyRuleVariable.java b/src/policy/src/main/java/eu/teraflow/policy/model/PolicyRuleVariable.java
index 1998cb7c65289182d854ed3d14c76239e7d0ac5d..10f86bbcadfcf936b2260fe81d0b6cfa4721752d 100644
--- a/src/policy/src/main/java/eu/teraflow/policy/model/PolicyRuleVariable.java
+++ b/src/policy/src/main/java/eu/teraflow/policy/model/PolicyRuleVariable.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package eu.teraflow.policy.model;
 
 public class PolicyRuleVariable {
diff --git a/src/policy/src/main/java/eu/teraflow/policy/model/RuleState.java b/src/policy/src/main/java/eu/teraflow/policy/model/RuleState.java
index ec1ba135fee4a98d9b22f7ec2511c6769c8c693c..05e051348652256bb0707c686e9e201b565042d8 100644
--- a/src/policy/src/main/java/eu/teraflow/policy/model/RuleState.java
+++ b/src/policy/src/main/java/eu/teraflow/policy/model/RuleState.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package eu.teraflow.policy.model;
 
 public enum RuleState {
diff --git a/src/policy/src/main/proto/kpi_sample_types.proto b/src/policy/src/main/proto/kpi_sample_types.proto
new file mode 120000
index 0000000000000000000000000000000000000000..98e748bbf4fbadbc04c3657f458d733f1bc7bdb8
--- /dev/null
+++ b/src/policy/src/main/proto/kpi_sample_types.proto
@@ -0,0 +1 @@
+../../../../../proto/kpi_sample_types.proto
\ No newline at end of file
diff --git a/src/policy/src/main/resources/application.yml b/src/policy/src/main/resources/application.yml
index 45c42a3c15c5c1b5521cdc4b55e2f472c7728640..2a5a96ccc973f519d7515f7f13e6d8b50048da2a 100644
--- a/src/policy/src/main/resources/application.yml
+++ b/src/policy/src/main/resources/application.yml
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 quarkus:
   grpc:
     server:
@@ -8,12 +22,11 @@ quarkus:
   container-image:
     group: teraflow-h2020
     name: controller/policy
-    tag: 0.0.1
     registry: registry.gitlab.com
   kubernetes:
     name: policyservice
     image-pull-policy: Always
-    service-type: NodePort
+    add-version-to-label-selectors: false
     labels:
       app: policyservice
     readiness-probe:
diff --git a/src/policy/src/test/java/eu/teraflow/policy/PolicyServiceTest.java b/src/policy/src/test/java/eu/teraflow/policy/PolicyServiceTest.java
index a9ba00bdf1ff4c7190df717c07802336073ec5aa..abb9bdd369a7722b201e209ac27557e99e957d65 100644
--- a/src/policy/src/test/java/eu/teraflow/policy/PolicyServiceTest.java
+++ b/src/policy/src/test/java/eu/teraflow/policy/PolicyServiceTest.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package eu.teraflow.policy;
 
 import static org.assertj.core.api.Assertions.assertThat;
diff --git a/src/policy/target/generated-sources/grpc/context/ContextOuterClass.java b/src/policy/target/generated-sources/grpc/context/ContextOuterClass.java
index 473bfa689e1a6df4d08ed8ddb76c746969f317dd..168ddf78b3a0233f3157e984fb7d925783bcd3b0 100644
--- a/src/policy/target/generated-sources/grpc/context/ContextOuterClass.java
+++ b/src/policy/target/generated-sources/grpc/context/ContextOuterClass.java
@@ -27653,79 +27653,49 @@ public final class ContextOuterClass {
 
   }
 
-  public interface EndPointIdOrBuilder extends
-      // @@protoc_insertion_point(interface_extends:context.EndPointId)
+  public interface ConnectionIdOrBuilder extends
+      // @@protoc_insertion_point(interface_extends:context.ConnectionId)
       com.google.protobuf.MessageOrBuilder {
 
     /**
-     * <code>.context.TopologyId topology_id = 1;</code>
-     * @return Whether the topologyId field is set.
-     */
-    boolean hasTopologyId();
-    /**
-     * <code>.context.TopologyId topology_id = 1;</code>
-     * @return The topologyId.
-     */
-    context.ContextOuterClass.TopologyId getTopologyId();
-    /**
-     * <code>.context.TopologyId topology_id = 1;</code>
-     */
-    context.ContextOuterClass.TopologyIdOrBuilder getTopologyIdOrBuilder();
-
-    /**
-     * <code>.context.DeviceId device_id = 2;</code>
-     * @return Whether the deviceId field is set.
-     */
-    boolean hasDeviceId();
-    /**
-     * <code>.context.DeviceId device_id = 2;</code>
-     * @return The deviceId.
-     */
-    context.ContextOuterClass.DeviceId getDeviceId();
-    /**
-     * <code>.context.DeviceId device_id = 2;</code>
-     */
-    context.ContextOuterClass.DeviceIdOrBuilder getDeviceIdOrBuilder();
-
-    /**
-     * <code>.context.Uuid endpoint_uuid = 3;</code>
-     * @return Whether the endpointUuid field is set.
+     * <code>.context.Uuid connection_uuid = 1;</code>
+     * @return Whether the connectionUuid field is set.
      */
-    boolean hasEndpointUuid();
+    boolean hasConnectionUuid();
     /**
-     * <code>.context.Uuid endpoint_uuid = 3;</code>
-     * @return The endpointUuid.
+     * <code>.context.Uuid connection_uuid = 1;</code>
+     * @return The connectionUuid.
      */
-    context.ContextOuterClass.Uuid getEndpointUuid();
+    context.ContextOuterClass.Uuid getConnectionUuid();
     /**
-     * <code>.context.Uuid endpoint_uuid = 3;</code>
+     * <code>.context.Uuid connection_uuid = 1;</code>
      */
-    context.ContextOuterClass.UuidOrBuilder getEndpointUuidOrBuilder();
+    context.ContextOuterClass.UuidOrBuilder getConnectionUuidOrBuilder();
   }
   /**
    * <pre>
-   * ----- Endpoint ------------------------------------------------------------------------------------------------------
+   * ----- Connection ----------------------------------------------------------------------------------------------------
    * </pre>
    *
-   * Protobuf type {@code context.EndPointId}
+   * Protobuf type {@code context.ConnectionId}
    */
-  public static final class EndPointId extends
+  public static final class ConnectionId extends
       com.google.protobuf.GeneratedMessageV3 implements
-      // @@protoc_insertion_point(message_implements:context.EndPointId)
-      EndPointIdOrBuilder {
+      // @@protoc_insertion_point(message_implements:context.ConnectionId)
+      ConnectionIdOrBuilder {
   private static final long serialVersionUID = 0L;
-    // Use EndPointId.newBuilder() to construct.
-    private EndPointId(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
+    // Use ConnectionId.newBuilder() to construct.
+    private ConnectionId(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
       super(builder);
     }
-    private EndPointId() {
+    private ConnectionId() {
     }
 
     @java.lang.Override
     @SuppressWarnings({"unused"})
     protected java.lang.Object newInstance(
         UnusedPrivateParameter unused) {
-      return new EndPointId();
+      return new ConnectionId();
     }
 
     @java.lang.Override
@@ -27733,7 +27703,7 @@ public final class ContextOuterClass {
     getUnknownFields() {
       return this.unknownFields;
     }
-    private EndPointId(
+    private ConnectionId(
         com.google.protobuf.CodedInputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
@@ -27752,40 +27722,14 @@ public final class ContextOuterClass {
               done = true;
               break;
             case 10: {
-              context.ContextOuterClass.TopologyId.Builder subBuilder = null;
-              if (topologyId_ != null) {
-                subBuilder = topologyId_.toBuilder();
-              }
-              topologyId_ = input.readMessage(context.ContextOuterClass.TopologyId.parser(), extensionRegistry);
-              if (subBuilder != null) {
-                subBuilder.mergeFrom(topologyId_);
-                topologyId_ = subBuilder.buildPartial();
-              }
-
-              break;
-            }
-            case 18: {
-              context.ContextOuterClass.DeviceId.Builder subBuilder = null;
-              if (deviceId_ != null) {
-                subBuilder = deviceId_.toBuilder();
-              }
-              deviceId_ = input.readMessage(context.ContextOuterClass.DeviceId.parser(), extensionRegistry);
-              if (subBuilder != null) {
-                subBuilder.mergeFrom(deviceId_);
-                deviceId_ = subBuilder.buildPartial();
-              }
-
-              break;
-            }
-            case 26: {
               context.ContextOuterClass.Uuid.Builder subBuilder = null;
-              if (endpointUuid_ != null) {
-                subBuilder = endpointUuid_.toBuilder();
+              if (connectionUuid_ != null) {
+                subBuilder = connectionUuid_.toBuilder();
               }
-              endpointUuid_ = input.readMessage(context.ContextOuterClass.Uuid.parser(), extensionRegistry);
+              connectionUuid_ = input.readMessage(context.ContextOuterClass.Uuid.parser(), extensionRegistry);
               if (subBuilder != null) {
-                subBuilder.mergeFrom(endpointUuid_);
-                endpointUuid_ = subBuilder.buildPartial();
+                subBuilder.mergeFrom(connectionUuid_);
+                connectionUuid_ = subBuilder.buildPartial();
               }
 
               break;
@@ -27811,93 +27755,41 @@ public final class ContextOuterClass {
     }
     public static final com.google.protobuf.Descriptors.Descriptor
         getDescriptor() {
-      return context.ContextOuterClass.internal_static_context_EndPointId_descriptor;
+      return context.ContextOuterClass.internal_static_context_ConnectionId_descriptor;
     }
 
     @java.lang.Override
     protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
         internalGetFieldAccessorTable() {
-      return context.ContextOuterClass.internal_static_context_EndPointId_fieldAccessorTable
+      return context.ContextOuterClass.internal_static_context_ConnectionId_fieldAccessorTable
           .ensureFieldAccessorsInitialized(
-              context.ContextOuterClass.EndPointId.class, context.ContextOuterClass.EndPointId.Builder.class);
-    }
-
-    public static final int TOPOLOGY_ID_FIELD_NUMBER = 1;
-    private context.ContextOuterClass.TopologyId topologyId_;
-    /**
-     * <code>.context.TopologyId topology_id = 1;</code>
-     * @return Whether the topologyId field is set.
-     */
-    @java.lang.Override
-    public boolean hasTopologyId() {
-      return topologyId_ != null;
-    }
-    /**
-     * <code>.context.TopologyId topology_id = 1;</code>
-     * @return The topologyId.
-     */
-    @java.lang.Override
-    public context.ContextOuterClass.TopologyId getTopologyId() {
-      return topologyId_ == null ? context.ContextOuterClass.TopologyId.getDefaultInstance() : topologyId_;
-    }
-    /**
-     * <code>.context.TopologyId topology_id = 1;</code>
-     */
-    @java.lang.Override
-    public context.ContextOuterClass.TopologyIdOrBuilder getTopologyIdOrBuilder() {
-      return getTopologyId();
-    }
-
-    public static final int DEVICE_ID_FIELD_NUMBER = 2;
-    private context.ContextOuterClass.DeviceId deviceId_;
-    /**
-     * <code>.context.DeviceId device_id = 2;</code>
-     * @return Whether the deviceId field is set.
-     */
-    @java.lang.Override
-    public boolean hasDeviceId() {
-      return deviceId_ != null;
-    }
-    /**
-     * <code>.context.DeviceId device_id = 2;</code>
-     * @return The deviceId.
-     */
-    @java.lang.Override
-    public context.ContextOuterClass.DeviceId getDeviceId() {
-      return deviceId_ == null ? context.ContextOuterClass.DeviceId.getDefaultInstance() : deviceId_;
-    }
-    /**
-     * <code>.context.DeviceId device_id = 2;</code>
-     */
-    @java.lang.Override
-    public context.ContextOuterClass.DeviceIdOrBuilder getDeviceIdOrBuilder() {
-      return getDeviceId();
+              context.ContextOuterClass.ConnectionId.class, context.ContextOuterClass.ConnectionId.Builder.class);
     }
 
-    public static final int ENDPOINT_UUID_FIELD_NUMBER = 3;
-    private context.ContextOuterClass.Uuid endpointUuid_;
+    public static final int CONNECTION_UUID_FIELD_NUMBER = 1;
+    private context.ContextOuterClass.Uuid connectionUuid_;
     /**
-     * <code>.context.Uuid endpoint_uuid = 3;</code>
-     * @return Whether the endpointUuid field is set.
+     * <code>.context.Uuid connection_uuid = 1;</code>
+     * @return Whether the connectionUuid field is set.
      */
     @java.lang.Override
-    public boolean hasEndpointUuid() {
-      return endpointUuid_ != null;
+    public boolean hasConnectionUuid() {
+      return connectionUuid_ != null;
     }
     /**
-     * <code>.context.Uuid endpoint_uuid = 3;</code>
-     * @return The endpointUuid.
+     * <code>.context.Uuid connection_uuid = 1;</code>
+     * @return The connectionUuid.
      */
     @java.lang.Override
-    public context.ContextOuterClass.Uuid getEndpointUuid() {
-      return endpointUuid_ == null ? context.ContextOuterClass.Uuid.getDefaultInstance() : endpointUuid_;
+    public context.ContextOuterClass.Uuid getConnectionUuid() {
+      return connectionUuid_ == null ? context.ContextOuterClass.Uuid.getDefaultInstance() : connectionUuid_;
     }
     /**
-     * <code>.context.Uuid endpoint_uuid = 3;</code>
+     * <code>.context.Uuid connection_uuid = 1;</code>
      */
     @java.lang.Override
-    public context.ContextOuterClass.UuidOrBuilder getEndpointUuidOrBuilder() {
-      return getEndpointUuid();
+    public context.ContextOuterClass.UuidOrBuilder getConnectionUuidOrBuilder() {
+      return getConnectionUuid();
     }
 
     private byte memoizedIsInitialized = -1;
@@ -27914,14 +27806,8 @@ public final class ContextOuterClass {
     @java.lang.Override
     public void writeTo(com.google.protobuf.CodedOutputStream output)
                         throws java.io.IOException {
-      if (topologyId_ != null) {
-        output.writeMessage(1, getTopologyId());
-      }
-      if (deviceId_ != null) {
-        output.writeMessage(2, getDeviceId());
-      }
-      if (endpointUuid_ != null) {
-        output.writeMessage(3, getEndpointUuid());
+      if (connectionUuid_ != null) {
+        output.writeMessage(1, getConnectionUuid());
       }
       unknownFields.writeTo(output);
     }
@@ -27932,17 +27818,9 @@ public final class ContextOuterClass {
       if (size != -1) return size;
 
       size = 0;
-      if (topologyId_ != null) {
-        size += com.google.protobuf.CodedOutputStream
-          .computeMessageSize(1, getTopologyId());
-      }
-      if (deviceId_ != null) {
-        size += com.google.protobuf.CodedOutputStream
-          .computeMessageSize(2, getDeviceId());
-      }
-      if (endpointUuid_ != null) {
+      if (connectionUuid_ != null) {
         size += com.google.protobuf.CodedOutputStream
-          .computeMessageSize(3, getEndpointUuid());
+          .computeMessageSize(1, getConnectionUuid());
       }
       size += unknownFields.getSerializedSize();
       memoizedSize = size;
@@ -27954,25 +27832,15 @@ public final class ContextOuterClass {
       if (obj == this) {
        return true;
       }
-      if (!(obj instanceof context.ContextOuterClass.EndPointId)) {
+      if (!(obj instanceof context.ContextOuterClass.ConnectionId)) {
         return super.equals(obj);
       }
-      context.ContextOuterClass.EndPointId other = (context.ContextOuterClass.EndPointId) obj;
+      context.ContextOuterClass.ConnectionId other = (context.ContextOuterClass.ConnectionId) obj;
 
-      if (hasTopologyId() != other.hasTopologyId()) return false;
-      if (hasTopologyId()) {
-        if (!getTopologyId()
-            .equals(other.getTopologyId())) return false;
-      }
-      if (hasDeviceId() != other.hasDeviceId()) return false;
-      if (hasDeviceId()) {
-        if (!getDeviceId()
-            .equals(other.getDeviceId())) return false;
-      }
-      if (hasEndpointUuid() != other.hasEndpointUuid()) return false;
-      if (hasEndpointUuid()) {
-        if (!getEndpointUuid()
-            .equals(other.getEndpointUuid())) return false;
+      if (hasConnectionUuid() != other.hasConnectionUuid()) return false;
+      if (hasConnectionUuid()) {
+        if (!getConnectionUuid()
+            .equals(other.getConnectionUuid())) return false;
       }
       if (!unknownFields.equals(other.unknownFields)) return false;
       return true;
@@ -27985,86 +27853,78 @@ public final class ContextOuterClass {
       }
       int hash = 41;
       hash = (19 * hash) + getDescriptor().hashCode();
-      if (hasTopologyId()) {
-        hash = (37 * hash) + TOPOLOGY_ID_FIELD_NUMBER;
-        hash = (53 * hash) + getTopologyId().hashCode();
-      }
-      if (hasDeviceId()) {
-        hash = (37 * hash) + DEVICE_ID_FIELD_NUMBER;
-        hash = (53 * hash) + getDeviceId().hashCode();
-      }
-      if (hasEndpointUuid()) {
-        hash = (37 * hash) + ENDPOINT_UUID_FIELD_NUMBER;
-        hash = (53 * hash) + getEndpointUuid().hashCode();
+      if (hasConnectionUuid()) {
+        hash = (37 * hash) + CONNECTION_UUID_FIELD_NUMBER;
+        hash = (53 * hash) + getConnectionUuid().hashCode();
       }
       hash = (29 * hash) + unknownFields.hashCode();
       memoizedHashCode = hash;
       return hash;
     }
 
-    public static context.ContextOuterClass.EndPointId parseFrom(
+    public static context.ContextOuterClass.ConnectionId parseFrom(
         java.nio.ByteBuffer data)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data);
     }
-    public static context.ContextOuterClass.EndPointId parseFrom(
+    public static context.ContextOuterClass.ConnectionId parseFrom(
         java.nio.ByteBuffer data,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data, extensionRegistry);
     }
-    public static context.ContextOuterClass.EndPointId parseFrom(
+    public static context.ContextOuterClass.ConnectionId parseFrom(
         com.google.protobuf.ByteString data)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data);
     }
-    public static context.ContextOuterClass.EndPointId parseFrom(
+    public static context.ContextOuterClass.ConnectionId parseFrom(
         com.google.protobuf.ByteString data,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data, extensionRegistry);
     }
-    public static context.ContextOuterClass.EndPointId parseFrom(byte[] data)
+    public static context.ContextOuterClass.ConnectionId parseFrom(byte[] data)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data);
     }
-    public static context.ContextOuterClass.EndPointId parseFrom(
+    public static context.ContextOuterClass.ConnectionId parseFrom(
         byte[] data,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data, extensionRegistry);
     }
-    public static context.ContextOuterClass.EndPointId parseFrom(java.io.InputStream input)
+    public static context.ContextOuterClass.ConnectionId parseFrom(java.io.InputStream input)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseWithIOException(PARSER, input);
     }
-    public static context.ContextOuterClass.EndPointId parseFrom(
+    public static context.ContextOuterClass.ConnectionId parseFrom(
         java.io.InputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseWithIOException(PARSER, input, extensionRegistry);
     }
-    public static context.ContextOuterClass.EndPointId parseDelimitedFrom(java.io.InputStream input)
+    public static context.ContextOuterClass.ConnectionId parseDelimitedFrom(java.io.InputStream input)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseDelimitedWithIOException(PARSER, input);
     }
-    public static context.ContextOuterClass.EndPointId parseDelimitedFrom(
+    public static context.ContextOuterClass.ConnectionId parseDelimitedFrom(
         java.io.InputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseDelimitedWithIOException(PARSER, input, extensionRegistry);
     }
-    public static context.ContextOuterClass.EndPointId parseFrom(
+    public static context.ContextOuterClass.ConnectionId parseFrom(
         com.google.protobuf.CodedInputStream input)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseWithIOException(PARSER, input);
     }
-    public static context.ContextOuterClass.EndPointId parseFrom(
+    public static context.ContextOuterClass.ConnectionId parseFrom(
         com.google.protobuf.CodedInputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws java.io.IOException {
@@ -28077,7 +27937,7 @@ public final class ContextOuterClass {
     public static Builder newBuilder() {
       return DEFAULT_INSTANCE.toBuilder();
     }
-    public static Builder newBuilder(context.ContextOuterClass.EndPointId prototype) {
+    public static Builder newBuilder(context.ContextOuterClass.ConnectionId prototype) {
       return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
     }
     @java.lang.Override
@@ -28094,29 +27954,29 @@ public final class ContextOuterClass {
     }
     /**
      * <pre>
-     * ----- Endpoint ------------------------------------------------------------------------------------------------------
+     * ----- Connection ----------------------------------------------------------------------------------------------------
      * </pre>
      *
-     * Protobuf type {@code context.EndPointId}
+     * Protobuf type {@code context.ConnectionId}
      */
     public static final class Builder extends
         com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
-        // @@protoc_insertion_point(builder_implements:context.EndPointId)
-        context.ContextOuterClass.EndPointIdOrBuilder {
+        // @@protoc_insertion_point(builder_implements:context.ConnectionId)
+        context.ContextOuterClass.ConnectionIdOrBuilder {
       public static final com.google.protobuf.Descriptors.Descriptor
           getDescriptor() {
-        return context.ContextOuterClass.internal_static_context_EndPointId_descriptor;
+        return context.ContextOuterClass.internal_static_context_ConnectionId_descriptor;
       }
 
       @java.lang.Override
       protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
           internalGetFieldAccessorTable() {
-        return context.ContextOuterClass.internal_static_context_EndPointId_fieldAccessorTable
+        return context.ContextOuterClass.internal_static_context_ConnectionId_fieldAccessorTable
             .ensureFieldAccessorsInitialized(
-                context.ContextOuterClass.EndPointId.class, context.ContextOuterClass.EndPointId.Builder.class);
+                context.ContextOuterClass.ConnectionId.class, context.ContextOuterClass.ConnectionId.Builder.class);
       }
 
-      // Construct using context.ContextOuterClass.EndPointId.newBuilder()
+      // Construct using context.ContextOuterClass.ConnectionId.newBuilder()
       private Builder() {
         maybeForceBuilderInitialization();
       }
@@ -28134,23 +27994,11 @@ public final class ContextOuterClass {
       @java.lang.Override
       public Builder clear() {
         super.clear();
-        if (topologyIdBuilder_ == null) {
-          topologyId_ = null;
-        } else {
-          topologyId_ = null;
-          topologyIdBuilder_ = null;
-        }
-        if (deviceIdBuilder_ == null) {
-          deviceId_ = null;
-        } else {
-          deviceId_ = null;
-          deviceIdBuilder_ = null;
-        }
-        if (endpointUuidBuilder_ == null) {
-          endpointUuid_ = null;
+        if (connectionUuidBuilder_ == null) {
+          connectionUuid_ = null;
         } else {
-          endpointUuid_ = null;
-          endpointUuidBuilder_ = null;
+          connectionUuid_ = null;
+          connectionUuidBuilder_ = null;
         }
         return this;
       }
@@ -28158,17 +28006,17 @@ public final class ContextOuterClass {
       @java.lang.Override
       public com.google.protobuf.Descriptors.Descriptor
           getDescriptorForType() {
-        return context.ContextOuterClass.internal_static_context_EndPointId_descriptor;
+        return context.ContextOuterClass.internal_static_context_ConnectionId_descriptor;
       }
 
       @java.lang.Override
-      public context.ContextOuterClass.EndPointId getDefaultInstanceForType() {
-        return context.ContextOuterClass.EndPointId.getDefaultInstance();
+      public context.ContextOuterClass.ConnectionId getDefaultInstanceForType() {
+        return context.ContextOuterClass.ConnectionId.getDefaultInstance();
       }
 
       @java.lang.Override
-      public context.ContextOuterClass.EndPointId build() {
-        context.ContextOuterClass.EndPointId result = buildPartial();
+      public context.ContextOuterClass.ConnectionId build() {
+        context.ContextOuterClass.ConnectionId result = buildPartial();
         if (!result.isInitialized()) {
           throw newUninitializedMessageException(result);
         }
@@ -28176,22 +28024,12 @@ public final class ContextOuterClass {
       }
 
       @java.lang.Override
-      public context.ContextOuterClass.EndPointId buildPartial() {
-        context.ContextOuterClass.EndPointId result = new context.ContextOuterClass.EndPointId(this);
-        if (topologyIdBuilder_ == null) {
-          result.topologyId_ = topologyId_;
-        } else {
-          result.topologyId_ = topologyIdBuilder_.build();
-        }
-        if (deviceIdBuilder_ == null) {
-          result.deviceId_ = deviceId_;
-        } else {
-          result.deviceId_ = deviceIdBuilder_.build();
-        }
-        if (endpointUuidBuilder_ == null) {
-          result.endpointUuid_ = endpointUuid_;
+      public context.ContextOuterClass.ConnectionId buildPartial() {
+        context.ContextOuterClass.ConnectionId result = new context.ContextOuterClass.ConnectionId(this);
+        if (connectionUuidBuilder_ == null) {
+          result.connectionUuid_ = connectionUuid_;
         } else {
-          result.endpointUuid_ = endpointUuidBuilder_.build();
+          result.connectionUuid_ = connectionUuidBuilder_.build();
         }
         onBuilt();
         return result;
@@ -28231,24 +28069,18 @@ public final class ContextOuterClass {
       }
       @java.lang.Override
       public Builder mergeFrom(com.google.protobuf.Message other) {
-        if (other instanceof context.ContextOuterClass.EndPointId) {
-          return mergeFrom((context.ContextOuterClass.EndPointId)other);
+        if (other instanceof context.ContextOuterClass.ConnectionId) {
+          return mergeFrom((context.ContextOuterClass.ConnectionId)other);
         } else {
           super.mergeFrom(other);
           return this;
         }
       }
 
-      public Builder mergeFrom(context.ContextOuterClass.EndPointId other) {
-        if (other == context.ContextOuterClass.EndPointId.getDefaultInstance()) return this;
-        if (other.hasTopologyId()) {
-          mergeTopologyId(other.getTopologyId());
-        }
-        if (other.hasDeviceId()) {
-          mergeDeviceId(other.getDeviceId());
-        }
-        if (other.hasEndpointUuid()) {
-          mergeEndpointUuid(other.getEndpointUuid());
+      public Builder mergeFrom(context.ContextOuterClass.ConnectionId other) {
+        if (other == context.ContextOuterClass.ConnectionId.getDefaultInstance()) return this;
+        if (other.hasConnectionUuid()) {
+          mergeConnectionUuid(other.getConnectionUuid());
         }
         this.mergeUnknownFields(other.unknownFields);
         onChanged();
@@ -28265,11 +28097,11 @@ public final class ContextOuterClass {
           com.google.protobuf.CodedInputStream input,
           com.google.protobuf.ExtensionRegistryLite extensionRegistry)
           throws java.io.IOException {
-        context.ContextOuterClass.EndPointId parsedMessage = null;
+        context.ContextOuterClass.ConnectionId parsedMessage = null;
         try {
           parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
         } catch (com.google.protobuf.InvalidProtocolBufferException e) {
-          parsedMessage = (context.ContextOuterClass.EndPointId) e.getUnfinishedMessage();
+          parsedMessage = (context.ContextOuterClass.ConnectionId) e.getUnfinishedMessage();
           throw e.unwrapIOException();
         } finally {
           if (parsedMessage != null) {
@@ -28279,366 +28111,128 @@ public final class ContextOuterClass {
         return this;
       }
 
-      private context.ContextOuterClass.TopologyId topologyId_;
+      private context.ContextOuterClass.Uuid connectionUuid_;
       private com.google.protobuf.SingleFieldBuilderV3<
-          context.ContextOuterClass.TopologyId, context.ContextOuterClass.TopologyId.Builder, context.ContextOuterClass.TopologyIdOrBuilder> topologyIdBuilder_;
+          context.ContextOuterClass.Uuid, context.ContextOuterClass.Uuid.Builder, context.ContextOuterClass.UuidOrBuilder> connectionUuidBuilder_;
       /**
-       * <code>.context.TopologyId topology_id = 1;</code>
-       * @return Whether the topologyId field is set.
+       * <code>.context.Uuid connection_uuid = 1;</code>
+       * @return Whether the connectionUuid field is set.
        */
-      public boolean hasTopologyId() {
-        return topologyIdBuilder_ != null || topologyId_ != null;
+      public boolean hasConnectionUuid() {
+        return connectionUuidBuilder_ != null || connectionUuid_ != null;
       }
       /**
-       * <code>.context.TopologyId topology_id = 1;</code>
-       * @return The topologyId.
+       * <code>.context.Uuid connection_uuid = 1;</code>
+       * @return The connectionUuid.
        */
-      public context.ContextOuterClass.TopologyId getTopologyId() {
-        if (topologyIdBuilder_ == null) {
-          return topologyId_ == null ? context.ContextOuterClass.TopologyId.getDefaultInstance() : topologyId_;
+      public context.ContextOuterClass.Uuid getConnectionUuid() {
+        if (connectionUuidBuilder_ == null) {
+          return connectionUuid_ == null ? context.ContextOuterClass.Uuid.getDefaultInstance() : connectionUuid_;
         } else {
-          return topologyIdBuilder_.getMessage();
+          return connectionUuidBuilder_.getMessage();
         }
       }
       /**
-       * <code>.context.TopologyId topology_id = 1;</code>
+       * <code>.context.Uuid connection_uuid = 1;</code>
        */
-      public Builder setTopologyId(context.ContextOuterClass.TopologyId value) {
-        if (topologyIdBuilder_ == null) {
+      public Builder setConnectionUuid(context.ContextOuterClass.Uuid value) {
+        if (connectionUuidBuilder_ == null) {
           if (value == null) {
             throw new NullPointerException();
           }
-          topologyId_ = value;
+          connectionUuid_ = value;
           onChanged();
         } else {
-          topologyIdBuilder_.setMessage(value);
+          connectionUuidBuilder_.setMessage(value);
         }
 
         return this;
       }
       /**
-       * <code>.context.TopologyId topology_id = 1;</code>
+       * <code>.context.Uuid connection_uuid = 1;</code>
        */
-      public Builder setTopologyId(
-          context.ContextOuterClass.TopologyId.Builder builderForValue) {
-        if (topologyIdBuilder_ == null) {
-          topologyId_ = builderForValue.build();
+      public Builder setConnectionUuid(
+          context.ContextOuterClass.Uuid.Builder builderForValue) {
+        if (connectionUuidBuilder_ == null) {
+          connectionUuid_ = builderForValue.build();
           onChanged();
         } else {
-          topologyIdBuilder_.setMessage(builderForValue.build());
+          connectionUuidBuilder_.setMessage(builderForValue.build());
         }
 
         return this;
       }
       /**
-       * <code>.context.TopologyId topology_id = 1;</code>
+       * <code>.context.Uuid connection_uuid = 1;</code>
        */
-      public Builder mergeTopologyId(context.ContextOuterClass.TopologyId value) {
-        if (topologyIdBuilder_ == null) {
-          if (topologyId_ != null) {
-            topologyId_ =
-              context.ContextOuterClass.TopologyId.newBuilder(topologyId_).mergeFrom(value).buildPartial();
+      public Builder mergeConnectionUuid(context.ContextOuterClass.Uuid value) {
+        if (connectionUuidBuilder_ == null) {
+          if (connectionUuid_ != null) {
+            connectionUuid_ =
+              context.ContextOuterClass.Uuid.newBuilder(connectionUuid_).mergeFrom(value).buildPartial();
           } else {
-            topologyId_ = value;
+            connectionUuid_ = value;
           }
           onChanged();
         } else {
-          topologyIdBuilder_.mergeFrom(value);
+          connectionUuidBuilder_.mergeFrom(value);
         }
 
         return this;
       }
       /**
-       * <code>.context.TopologyId topology_id = 1;</code>
+       * <code>.context.Uuid connection_uuid = 1;</code>
        */
-      public Builder clearTopologyId() {
-        if (topologyIdBuilder_ == null) {
-          topologyId_ = null;
+      public Builder clearConnectionUuid() {
+        if (connectionUuidBuilder_ == null) {
+          connectionUuid_ = null;
           onChanged();
         } else {
-          topologyId_ = null;
-          topologyIdBuilder_ = null;
+          connectionUuid_ = null;
+          connectionUuidBuilder_ = null;
         }
 
         return this;
       }
       /**
-       * <code>.context.TopologyId topology_id = 1;</code>
+       * <code>.context.Uuid connection_uuid = 1;</code>
        */
-      public context.ContextOuterClass.TopologyId.Builder getTopologyIdBuilder() {
+      public context.ContextOuterClass.Uuid.Builder getConnectionUuidBuilder() {
         
         onChanged();
-        return getTopologyIdFieldBuilder().getBuilder();
+        return getConnectionUuidFieldBuilder().getBuilder();
       }
       /**
-       * <code>.context.TopologyId topology_id = 1;</code>
+       * <code>.context.Uuid connection_uuid = 1;</code>
        */
-      public context.ContextOuterClass.TopologyIdOrBuilder getTopologyIdOrBuilder() {
-        if (topologyIdBuilder_ != null) {
-          return topologyIdBuilder_.getMessageOrBuilder();
+      public context.ContextOuterClass.UuidOrBuilder getConnectionUuidOrBuilder() {
+        if (connectionUuidBuilder_ != null) {
+          return connectionUuidBuilder_.getMessageOrBuilder();
         } else {
-          return topologyId_ == null ?
-              context.ContextOuterClass.TopologyId.getDefaultInstance() : topologyId_;
+          return connectionUuid_ == null ?
+              context.ContextOuterClass.Uuid.getDefaultInstance() : connectionUuid_;
         }
       }
       /**
-       * <code>.context.TopologyId topology_id = 1;</code>
+       * <code>.context.Uuid connection_uuid = 1;</code>
        */
       private com.google.protobuf.SingleFieldBuilderV3<
-          context.ContextOuterClass.TopologyId, context.ContextOuterClass.TopologyId.Builder, context.ContextOuterClass.TopologyIdOrBuilder> 
-          getTopologyIdFieldBuilder() {
-        if (topologyIdBuilder_ == null) {
-          topologyIdBuilder_ = new com.google.protobuf.SingleFieldBuilderV3<
-              context.ContextOuterClass.TopologyId, context.ContextOuterClass.TopologyId.Builder, context.ContextOuterClass.TopologyIdOrBuilder>(
-                  getTopologyId(),
+          context.ContextOuterClass.Uuid, context.ContextOuterClass.Uuid.Builder, context.ContextOuterClass.UuidOrBuilder> 
+          getConnectionUuidFieldBuilder() {
+        if (connectionUuidBuilder_ == null) {
+          connectionUuidBuilder_ = new com.google.protobuf.SingleFieldBuilderV3<
+              context.ContextOuterClass.Uuid, context.ContextOuterClass.Uuid.Builder, context.ContextOuterClass.UuidOrBuilder>(
+                  getConnectionUuid(),
                   getParentForChildren(),
                   isClean());
-          topologyId_ = null;
+          connectionUuid_ = null;
         }
-        return topologyIdBuilder_;
-      }
-
-      private context.ContextOuterClass.DeviceId deviceId_;
-      private com.google.protobuf.SingleFieldBuilderV3<
-          context.ContextOuterClass.DeviceId, context.ContextOuterClass.DeviceId.Builder, context.ContextOuterClass.DeviceIdOrBuilder> deviceIdBuilder_;
-      /**
-       * <code>.context.DeviceId device_id = 2;</code>
-       * @return Whether the deviceId field is set.
-       */
-      public boolean hasDeviceId() {
-        return deviceIdBuilder_ != null || deviceId_ != null;
+        return connectionUuidBuilder_;
       }
-      /**
-       * <code>.context.DeviceId device_id = 2;</code>
-       * @return The deviceId.
-       */
-      public context.ContextOuterClass.DeviceId getDeviceId() {
-        if (deviceIdBuilder_ == null) {
-          return deviceId_ == null ? context.ContextOuterClass.DeviceId.getDefaultInstance() : deviceId_;
-        } else {
-          return deviceIdBuilder_.getMessage();
-        }
-      }
-      /**
-       * <code>.context.DeviceId device_id = 2;</code>
-       */
-      public Builder setDeviceId(context.ContextOuterClass.DeviceId value) {
-        if (deviceIdBuilder_ == null) {
-          if (value == null) {
-            throw new NullPointerException();
-          }
-          deviceId_ = value;
-          onChanged();
-        } else {
-          deviceIdBuilder_.setMessage(value);
-        }
-
-        return this;
-      }
-      /**
-       * <code>.context.DeviceId device_id = 2;</code>
-       */
-      public Builder setDeviceId(
-          context.ContextOuterClass.DeviceId.Builder builderForValue) {
-        if (deviceIdBuilder_ == null) {
-          deviceId_ = builderForValue.build();
-          onChanged();
-        } else {
-          deviceIdBuilder_.setMessage(builderForValue.build());
-        }
-
-        return this;
-      }
-      /**
-       * <code>.context.DeviceId device_id = 2;</code>
-       */
-      public Builder mergeDeviceId(context.ContextOuterClass.DeviceId value) {
-        if (deviceIdBuilder_ == null) {
-          if (deviceId_ != null) {
-            deviceId_ =
-              context.ContextOuterClass.DeviceId.newBuilder(deviceId_).mergeFrom(value).buildPartial();
-          } else {
-            deviceId_ = value;
-          }
-          onChanged();
-        } else {
-          deviceIdBuilder_.mergeFrom(value);
-        }
-
-        return this;
-      }
-      /**
-       * <code>.context.DeviceId device_id = 2;</code>
-       */
-      public Builder clearDeviceId() {
-        if (deviceIdBuilder_ == null) {
-          deviceId_ = null;
-          onChanged();
-        } else {
-          deviceId_ = null;
-          deviceIdBuilder_ = null;
-        }
-
-        return this;
-      }
-      /**
-       * <code>.context.DeviceId device_id = 2;</code>
-       */
-      public context.ContextOuterClass.DeviceId.Builder getDeviceIdBuilder() {
-        
-        onChanged();
-        return getDeviceIdFieldBuilder().getBuilder();
-      }
-      /**
-       * <code>.context.DeviceId device_id = 2;</code>
-       */
-      public context.ContextOuterClass.DeviceIdOrBuilder getDeviceIdOrBuilder() {
-        if (deviceIdBuilder_ != null) {
-          return deviceIdBuilder_.getMessageOrBuilder();
-        } else {
-          return deviceId_ == null ?
-              context.ContextOuterClass.DeviceId.getDefaultInstance() : deviceId_;
-        }
-      }
-      /**
-       * <code>.context.DeviceId device_id = 2;</code>
-       */
-      private com.google.protobuf.SingleFieldBuilderV3<
-          context.ContextOuterClass.DeviceId, context.ContextOuterClass.DeviceId.Builder, context.ContextOuterClass.DeviceIdOrBuilder> 
-          getDeviceIdFieldBuilder() {
-        if (deviceIdBuilder_ == null) {
-          deviceIdBuilder_ = new com.google.protobuf.SingleFieldBuilderV3<
-              context.ContextOuterClass.DeviceId, context.ContextOuterClass.DeviceId.Builder, context.ContextOuterClass.DeviceIdOrBuilder>(
-                  getDeviceId(),
-                  getParentForChildren(),
-                  isClean());
-          deviceId_ = null;
-        }
-        return deviceIdBuilder_;
-      }
-
-      private context.ContextOuterClass.Uuid endpointUuid_;
-      private com.google.protobuf.SingleFieldBuilderV3<
-          context.ContextOuterClass.Uuid, context.ContextOuterClass.Uuid.Builder, context.ContextOuterClass.UuidOrBuilder> endpointUuidBuilder_;
-      /**
-       * <code>.context.Uuid endpoint_uuid = 3;</code>
-       * @return Whether the endpointUuid field is set.
-       */
-      public boolean hasEndpointUuid() {
-        return endpointUuidBuilder_ != null || endpointUuid_ != null;
-      }
-      /**
-       * <code>.context.Uuid endpoint_uuid = 3;</code>
-       * @return The endpointUuid.
-       */
-      public context.ContextOuterClass.Uuid getEndpointUuid() {
-        if (endpointUuidBuilder_ == null) {
-          return endpointUuid_ == null ? context.ContextOuterClass.Uuid.getDefaultInstance() : endpointUuid_;
-        } else {
-          return endpointUuidBuilder_.getMessage();
-        }
-      }
-      /**
-       * <code>.context.Uuid endpoint_uuid = 3;</code>
-       */
-      public Builder setEndpointUuid(context.ContextOuterClass.Uuid value) {
-        if (endpointUuidBuilder_ == null) {
-          if (value == null) {
-            throw new NullPointerException();
-          }
-          endpointUuid_ = value;
-          onChanged();
-        } else {
-          endpointUuidBuilder_.setMessage(value);
-        }
-
-        return this;
-      }
-      /**
-       * <code>.context.Uuid endpoint_uuid = 3;</code>
-       */
-      public Builder setEndpointUuid(
-          context.ContextOuterClass.Uuid.Builder builderForValue) {
-        if (endpointUuidBuilder_ == null) {
-          endpointUuid_ = builderForValue.build();
-          onChanged();
-        } else {
-          endpointUuidBuilder_.setMessage(builderForValue.build());
-        }
-
-        return this;
-      }
-      /**
-       * <code>.context.Uuid endpoint_uuid = 3;</code>
-       */
-      public Builder mergeEndpointUuid(context.ContextOuterClass.Uuid value) {
-        if (endpointUuidBuilder_ == null) {
-          if (endpointUuid_ != null) {
-            endpointUuid_ =
-              context.ContextOuterClass.Uuid.newBuilder(endpointUuid_).mergeFrom(value).buildPartial();
-          } else {
-            endpointUuid_ = value;
-          }
-          onChanged();
-        } else {
-          endpointUuidBuilder_.mergeFrom(value);
-        }
-
-        return this;
-      }
-      /**
-       * <code>.context.Uuid endpoint_uuid = 3;</code>
-       */
-      public Builder clearEndpointUuid() {
-        if (endpointUuidBuilder_ == null) {
-          endpointUuid_ = null;
-          onChanged();
-        } else {
-          endpointUuid_ = null;
-          endpointUuidBuilder_ = null;
-        }
-
-        return this;
-      }
-      /**
-       * <code>.context.Uuid endpoint_uuid = 3;</code>
-       */
-      public context.ContextOuterClass.Uuid.Builder getEndpointUuidBuilder() {
-        
-        onChanged();
-        return getEndpointUuidFieldBuilder().getBuilder();
-      }
-      /**
-       * <code>.context.Uuid endpoint_uuid = 3;</code>
-       */
-      public context.ContextOuterClass.UuidOrBuilder getEndpointUuidOrBuilder() {
-        if (endpointUuidBuilder_ != null) {
-          return endpointUuidBuilder_.getMessageOrBuilder();
-        } else {
-          return endpointUuid_ == null ?
-              context.ContextOuterClass.Uuid.getDefaultInstance() : endpointUuid_;
-        }
-      }
-      /**
-       * <code>.context.Uuid endpoint_uuid = 3;</code>
-       */
-      private com.google.protobuf.SingleFieldBuilderV3<
-          context.ContextOuterClass.Uuid, context.ContextOuterClass.Uuid.Builder, context.ContextOuterClass.UuidOrBuilder> 
-          getEndpointUuidFieldBuilder() {
-        if (endpointUuidBuilder_ == null) {
-          endpointUuidBuilder_ = new com.google.protobuf.SingleFieldBuilderV3<
-              context.ContextOuterClass.Uuid, context.ContextOuterClass.Uuid.Builder, context.ContextOuterClass.UuidOrBuilder>(
-                  getEndpointUuid(),
-                  getParentForChildren(),
-                  isClean());
-          endpointUuid_ = null;
-        }
-        return endpointUuidBuilder_;
-      }
-      @java.lang.Override
-      public final Builder setUnknownFields(
-          final com.google.protobuf.UnknownFieldSet unknownFields) {
-        return super.setUnknownFields(unknownFields);
+      @java.lang.Override
+      public final Builder setUnknownFields(
+          final com.google.protobuf.UnknownFieldSet unknownFields) {
+        return super.setUnknownFields(unknownFields);
       }
 
       @java.lang.Override
@@ -28648,98 +28242,150 @@ public final class ContextOuterClass {
       }
 
 
-      // @@protoc_insertion_point(builder_scope:context.EndPointId)
+      // @@protoc_insertion_point(builder_scope:context.ConnectionId)
     }
 
-    // @@protoc_insertion_point(class_scope:context.EndPointId)
-    private static final context.ContextOuterClass.EndPointId DEFAULT_INSTANCE;
+    // @@protoc_insertion_point(class_scope:context.ConnectionId)
+    private static final context.ContextOuterClass.ConnectionId DEFAULT_INSTANCE;
     static {
-      DEFAULT_INSTANCE = new context.ContextOuterClass.EndPointId();
+      DEFAULT_INSTANCE = new context.ContextOuterClass.ConnectionId();
     }
 
-    public static context.ContextOuterClass.EndPointId getDefaultInstance() {
+    public static context.ContextOuterClass.ConnectionId getDefaultInstance() {
       return DEFAULT_INSTANCE;
     }
 
-    private static final com.google.protobuf.Parser<EndPointId>
-        PARSER = new com.google.protobuf.AbstractParser<EndPointId>() {
+    private static final com.google.protobuf.Parser<ConnectionId>
+        PARSER = new com.google.protobuf.AbstractParser<ConnectionId>() {
       @java.lang.Override
-      public EndPointId parsePartialFrom(
+      public ConnectionId parsePartialFrom(
           com.google.protobuf.CodedInputStream input,
           com.google.protobuf.ExtensionRegistryLite extensionRegistry)
           throws com.google.protobuf.InvalidProtocolBufferException {
-        return new EndPointId(input, extensionRegistry);
+        return new ConnectionId(input, extensionRegistry);
       }
     };
 
-    public static com.google.protobuf.Parser<EndPointId> parser() {
+    public static com.google.protobuf.Parser<ConnectionId> parser() {
       return PARSER;
     }
 
     @java.lang.Override
-    public com.google.protobuf.Parser<EndPointId> getParserForType() {
+    public com.google.protobuf.Parser<ConnectionId> getParserForType() {
       return PARSER;
     }
 
     @java.lang.Override
-    public context.ContextOuterClass.EndPointId getDefaultInstanceForType() {
+    public context.ContextOuterClass.ConnectionId getDefaultInstanceForType() {
       return DEFAULT_INSTANCE;
     }
 
   }
 
-  public interface EndPointOrBuilder extends
-      // @@protoc_insertion_point(interface_extends:context.EndPoint)
+  public interface ConnectionOrBuilder extends
+      // @@protoc_insertion_point(interface_extends:context.Connection)
       com.google.protobuf.MessageOrBuilder {
 
     /**
-     * <code>.context.EndPointId endpoint_id = 1;</code>
-     * @return Whether the endpointId field is set.
+     * <code>.context.ConnectionId connection_id = 1;</code>
+     * @return Whether the connectionId field is set.
      */
-    boolean hasEndpointId();
+    boolean hasConnectionId();
     /**
-     * <code>.context.EndPointId endpoint_id = 1;</code>
-     * @return The endpointId.
+     * <code>.context.ConnectionId connection_id = 1;</code>
+     * @return The connectionId.
      */
-    context.ContextOuterClass.EndPointId getEndpointId();
+    context.ContextOuterClass.ConnectionId getConnectionId();
     /**
-     * <code>.context.EndPointId endpoint_id = 1;</code>
+     * <code>.context.ConnectionId connection_id = 1;</code>
      */
-    context.ContextOuterClass.EndPointIdOrBuilder getEndpointIdOrBuilder();
+    context.ContextOuterClass.ConnectionIdOrBuilder getConnectionIdOrBuilder();
 
     /**
-     * <code>string endpoint_type = 2;</code>
-     * @return The endpointType.
+     * <code>.context.ServiceId service_id = 2;</code>
+     * @return Whether the serviceId field is set.
      */
-    java.lang.String getEndpointType();
+    boolean hasServiceId();
     /**
-     * <code>string endpoint_type = 2;</code>
-     * @return The bytes for endpointType.
+     * <code>.context.ServiceId service_id = 2;</code>
+     * @return The serviceId.
      */
-    com.google.protobuf.ByteString
-        getEndpointTypeBytes();
+    context.ContextOuterClass.ServiceId getServiceId();
+    /**
+     * <code>.context.ServiceId service_id = 2;</code>
+     */
+    context.ContextOuterClass.ServiceIdOrBuilder getServiceIdOrBuilder();
+
+    /**
+     * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
+     */
+    java.util.List<context.ContextOuterClass.EndPointId> 
+        getPathHopsEndpointIdsList();
+    /**
+     * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
+     */
+    context.ContextOuterClass.EndPointId getPathHopsEndpointIds(int index);
+    /**
+     * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
+     */
+    int getPathHopsEndpointIdsCount();
+    /**
+     * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
+     */
+    java.util.List<? extends context.ContextOuterClass.EndPointIdOrBuilder> 
+        getPathHopsEndpointIdsOrBuilderList();
+    /**
+     * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
+     */
+    context.ContextOuterClass.EndPointIdOrBuilder getPathHopsEndpointIdsOrBuilder(
+        int index);
+
+    /**
+     * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
+     */
+    java.util.List<context.ContextOuterClass.ServiceId> 
+        getSubServiceIdsList();
+    /**
+     * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
+     */
+    context.ContextOuterClass.ServiceId getSubServiceIds(int index);
+    /**
+     * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
+     */
+    int getSubServiceIdsCount();
+    /**
+     * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
+     */
+    java.util.List<? extends context.ContextOuterClass.ServiceIdOrBuilder> 
+        getSubServiceIdsOrBuilderList();
+    /**
+     * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
+     */
+    context.ContextOuterClass.ServiceIdOrBuilder getSubServiceIdsOrBuilder(
+        int index);
   }
   /**
-   * Protobuf type {@code context.EndPoint}
+   * Protobuf type {@code context.Connection}
    */
-  public static final class EndPoint extends
+  public static final class Connection extends
       com.google.protobuf.GeneratedMessageV3 implements
-      // @@protoc_insertion_point(message_implements:context.EndPoint)
-      EndPointOrBuilder {
+      // @@protoc_insertion_point(message_implements:context.Connection)
+      ConnectionOrBuilder {
   private static final long serialVersionUID = 0L;
-    // Use EndPoint.newBuilder() to construct.
-    private EndPoint(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
+    // Use Connection.newBuilder() to construct.
+    private Connection(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
       super(builder);
     }
-    private EndPoint() {
-      endpointType_ = "";
+    private Connection() {
+      pathHopsEndpointIds_ = java.util.Collections.emptyList();
+      subServiceIds_ = java.util.Collections.emptyList();
     }
 
     @java.lang.Override
     @SuppressWarnings({"unused"})
     protected java.lang.Object newInstance(
         UnusedPrivateParameter unused) {
-      return new EndPoint();
+      return new Connection();
     }
 
     @java.lang.Override
@@ -28747,7 +28393,7 @@ public final class ContextOuterClass {
     getUnknownFields() {
       return this.unknownFields;
     }
-    private EndPoint(
+    private Connection(
         com.google.protobuf.CodedInputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
@@ -28755,6 +28401,7 @@ public final class ContextOuterClass {
       if (extensionRegistry == null) {
         throw new java.lang.NullPointerException();
       }
+      int mutable_bitField0_ = 0;
       com.google.protobuf.UnknownFieldSet.Builder unknownFields =
           com.google.protobuf.UnknownFieldSet.newBuilder();
       try {
@@ -28766,22 +28413,47 @@ public final class ContextOuterClass {
               done = true;
               break;
             case 10: {
-              context.ContextOuterClass.EndPointId.Builder subBuilder = null;
-              if (endpointId_ != null) {
-                subBuilder = endpointId_.toBuilder();
+              context.ContextOuterClass.ConnectionId.Builder subBuilder = null;
+              if (connectionId_ != null) {
+                subBuilder = connectionId_.toBuilder();
               }
-              endpointId_ = input.readMessage(context.ContextOuterClass.EndPointId.parser(), extensionRegistry);
+              connectionId_ = input.readMessage(context.ContextOuterClass.ConnectionId.parser(), extensionRegistry);
               if (subBuilder != null) {
-                subBuilder.mergeFrom(endpointId_);
-                endpointId_ = subBuilder.buildPartial();
+                subBuilder.mergeFrom(connectionId_);
+                connectionId_ = subBuilder.buildPartial();
               }
 
               break;
             }
             case 18: {
-              java.lang.String s = input.readStringRequireUtf8();
+              context.ContextOuterClass.ServiceId.Builder subBuilder = null;
+              if (serviceId_ != null) {
+                subBuilder = serviceId_.toBuilder();
+              }
+              serviceId_ = input.readMessage(context.ContextOuterClass.ServiceId.parser(), extensionRegistry);
+              if (subBuilder != null) {
+                subBuilder.mergeFrom(serviceId_);
+                serviceId_ = subBuilder.buildPartial();
+              }
 
-              endpointType_ = s;
+              break;
+            }
+            case 26: {
+              if (!((mutable_bitField0_ & 0x00000001) != 0)) {
+                pathHopsEndpointIds_ = new java.util.ArrayList<context.ContextOuterClass.EndPointId>();
+                mutable_bitField0_ |= 0x00000001;
+              }
+              pathHopsEndpointIds_.add(
+                  input.readMessage(context.ContextOuterClass.EndPointId.parser(), extensionRegistry));
+              break;
+            }
+            case 34: {
+              if (!((mutable_bitField0_ & 0x00000002) != 0)) {
+                subServiceIds_ = new java.util.ArrayList<context.ContextOuterClass.ServiceId>();
+                mutable_bitField0_ |= 0x00000002;
+              }
+              subServiceIds_.add(
+                  input.readMessage(context.ContextOuterClass.ServiceId.parser(), extensionRegistry));
               break;
             }
             default: {
@@ -28799,85 +28471,159 @@ public final class ContextOuterClass {
         throw new com.google.protobuf.InvalidProtocolBufferException(
             e).setUnfinishedMessage(this);
       } finally {
+        if (((mutable_bitField0_ & 0x00000001) != 0)) {
+          pathHopsEndpointIds_ = java.util.Collections.unmodifiableList(pathHopsEndpointIds_);
+        }
+        if (((mutable_bitField0_ & 0x00000002) != 0)) {
+          subServiceIds_ = java.util.Collections.unmodifiableList(subServiceIds_);
+        }
         this.unknownFields = unknownFields.build();
         makeExtensionsImmutable();
       }
     }
     public static final com.google.protobuf.Descriptors.Descriptor
         getDescriptor() {
-      return context.ContextOuterClass.internal_static_context_EndPoint_descriptor;
+      return context.ContextOuterClass.internal_static_context_Connection_descriptor;
     }
 
     @java.lang.Override
     protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
         internalGetFieldAccessorTable() {
-      return context.ContextOuterClass.internal_static_context_EndPoint_fieldAccessorTable
+      return context.ContextOuterClass.internal_static_context_Connection_fieldAccessorTable
           .ensureFieldAccessorsInitialized(
-              context.ContextOuterClass.EndPoint.class, context.ContextOuterClass.EndPoint.Builder.class);
+              context.ContextOuterClass.Connection.class, context.ContextOuterClass.Connection.Builder.class);
     }
 
-    public static final int ENDPOINT_ID_FIELD_NUMBER = 1;
-    private context.ContextOuterClass.EndPointId endpointId_;
+    public static final int CONNECTION_ID_FIELD_NUMBER = 1;
+    private context.ContextOuterClass.ConnectionId connectionId_;
     /**
-     * <code>.context.EndPointId endpoint_id = 1;</code>
-     * @return Whether the endpointId field is set.
+     * <code>.context.ConnectionId connection_id = 1;</code>
+     * @return Whether the connectionId field is set.
      */
     @java.lang.Override
-    public boolean hasEndpointId() {
-      return endpointId_ != null;
+    public boolean hasConnectionId() {
+      return connectionId_ != null;
     }
     /**
-     * <code>.context.EndPointId endpoint_id = 1;</code>
-     * @return The endpointId.
+     * <code>.context.ConnectionId connection_id = 1;</code>
+     * @return The connectionId.
      */
     @java.lang.Override
-    public context.ContextOuterClass.EndPointId getEndpointId() {
-      return endpointId_ == null ? context.ContextOuterClass.EndPointId.getDefaultInstance() : endpointId_;
+    public context.ContextOuterClass.ConnectionId getConnectionId() {
+      return connectionId_ == null ? context.ContextOuterClass.ConnectionId.getDefaultInstance() : connectionId_;
     }
     /**
-     * <code>.context.EndPointId endpoint_id = 1;</code>
+     * <code>.context.ConnectionId connection_id = 1;</code>
      */
     @java.lang.Override
-    public context.ContextOuterClass.EndPointIdOrBuilder getEndpointIdOrBuilder() {
-      return getEndpointId();
+    public context.ContextOuterClass.ConnectionIdOrBuilder getConnectionIdOrBuilder() {
+      return getConnectionId();
     }
 
-    public static final int ENDPOINT_TYPE_FIELD_NUMBER = 2;
-    private volatile java.lang.Object endpointType_;
+    public static final int SERVICE_ID_FIELD_NUMBER = 2;
+    private context.ContextOuterClass.ServiceId serviceId_;
     /**
-     * <code>string endpoint_type = 2;</code>
-     * @return The endpointType.
+     * <code>.context.ServiceId service_id = 2;</code>
+     * @return Whether the serviceId field is set.
      */
     @java.lang.Override
-    public java.lang.String getEndpointType() {
-      java.lang.Object ref = endpointType_;
-      if (ref instanceof java.lang.String) {
-        return (java.lang.String) ref;
-      } else {
-        com.google.protobuf.ByteString bs = 
-            (com.google.protobuf.ByteString) ref;
-        java.lang.String s = bs.toStringUtf8();
-        endpointType_ = s;
-        return s;
-      }
+    public boolean hasServiceId() {
+      return serviceId_ != null;
     }
     /**
-     * <code>string endpoint_type = 2;</code>
-     * @return The bytes for endpointType.
+     * <code>.context.ServiceId service_id = 2;</code>
+     * @return The serviceId.
      */
     @java.lang.Override
-    public com.google.protobuf.ByteString
-        getEndpointTypeBytes() {
-      java.lang.Object ref = endpointType_;
-      if (ref instanceof java.lang.String) {
-        com.google.protobuf.ByteString b = 
-            com.google.protobuf.ByteString.copyFromUtf8(
-                (java.lang.String) ref);
-        endpointType_ = b;
-        return b;
-      } else {
-        return (com.google.protobuf.ByteString) ref;
-      }
+    public context.ContextOuterClass.ServiceId getServiceId() {
+      return serviceId_ == null ? context.ContextOuterClass.ServiceId.getDefaultInstance() : serviceId_;
+    }
+    /**
+     * <code>.context.ServiceId service_id = 2;</code>
+     */
+    @java.lang.Override
+    public context.ContextOuterClass.ServiceIdOrBuilder getServiceIdOrBuilder() {
+      return getServiceId();
+    }
+
+    public static final int PATH_HOPS_ENDPOINT_IDS_FIELD_NUMBER = 3;
+    private java.util.List<context.ContextOuterClass.EndPointId> pathHopsEndpointIds_;
+    /**
+     * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
+     */
+    @java.lang.Override
+    public java.util.List<context.ContextOuterClass.EndPointId> getPathHopsEndpointIdsList() {
+      return pathHopsEndpointIds_;
+    }
+    /**
+     * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
+     */
+    @java.lang.Override
+    public java.util.List<? extends context.ContextOuterClass.EndPointIdOrBuilder> 
+        getPathHopsEndpointIdsOrBuilderList() {
+      return pathHopsEndpointIds_;
+    }
+    /**
+     * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
+     */
+    @java.lang.Override
+    public int getPathHopsEndpointIdsCount() {
+      return pathHopsEndpointIds_.size();
+    }
+    /**
+     * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
+     */
+    @java.lang.Override
+    public context.ContextOuterClass.EndPointId getPathHopsEndpointIds(int index) {
+      return pathHopsEndpointIds_.get(index);
+    }
+    /**
+     * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
+     */
+    @java.lang.Override
+    public context.ContextOuterClass.EndPointIdOrBuilder getPathHopsEndpointIdsOrBuilder(
+        int index) {
+      return pathHopsEndpointIds_.get(index);
+    }
+
+    public static final int SUB_SERVICE_IDS_FIELD_NUMBER = 4;
+    private java.util.List<context.ContextOuterClass.ServiceId> subServiceIds_;
+    /**
+     * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
+     */
+    @java.lang.Override
+    public java.util.List<context.ContextOuterClass.ServiceId> getSubServiceIdsList() {
+      return subServiceIds_;
+    }
+    /**
+     * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
+     */
+    @java.lang.Override
+    public java.util.List<? extends context.ContextOuterClass.ServiceIdOrBuilder> 
+        getSubServiceIdsOrBuilderList() {
+      return subServiceIds_;
+    }
+    /**
+     * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
+     */
+    @java.lang.Override
+    public int getSubServiceIdsCount() {
+      return subServiceIds_.size();
+    }
+    /**
+     * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
+     */
+    @java.lang.Override
+    public context.ContextOuterClass.ServiceId getSubServiceIds(int index) {
+      return subServiceIds_.get(index);
+    }
+    /**
+     * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
+     */
+    @java.lang.Override
+    public context.ContextOuterClass.ServiceIdOrBuilder getSubServiceIdsOrBuilder(
+        int index) {
+      return subServiceIds_.get(index);
     }
 
     private byte memoizedIsInitialized = -1;
@@ -28894,11 +28640,17 @@ public final class ContextOuterClass {
     @java.lang.Override
     public void writeTo(com.google.protobuf.CodedOutputStream output)
                         throws java.io.IOException {
-      if (endpointId_ != null) {
-        output.writeMessage(1, getEndpointId());
+      if (connectionId_ != null) {
+        output.writeMessage(1, getConnectionId());
       }
-      if (!getEndpointTypeBytes().isEmpty()) {
-        com.google.protobuf.GeneratedMessageV3.writeString(output, 2, endpointType_);
+      if (serviceId_ != null) {
+        output.writeMessage(2, getServiceId());
+      }
+      for (int i = 0; i < pathHopsEndpointIds_.size(); i++) {
+        output.writeMessage(3, pathHopsEndpointIds_.get(i));
+      }
+      for (int i = 0; i < subServiceIds_.size(); i++) {
+        output.writeMessage(4, subServiceIds_.get(i));
       }
       unknownFields.writeTo(output);
     }
@@ -28909,12 +28661,21 @@ public final class ContextOuterClass {
       if (size != -1) return size;
 
       size = 0;
-      if (endpointId_ != null) {
+      if (connectionId_ != null) {
         size += com.google.protobuf.CodedOutputStream
-          .computeMessageSize(1, getEndpointId());
+          .computeMessageSize(1, getConnectionId());
       }
-      if (!getEndpointTypeBytes().isEmpty()) {
-        size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, endpointType_);
+      if (serviceId_ != null) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeMessageSize(2, getServiceId());
+      }
+      for (int i = 0; i < pathHopsEndpointIds_.size(); i++) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeMessageSize(3, pathHopsEndpointIds_.get(i));
+      }
+      for (int i = 0; i < subServiceIds_.size(); i++) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeMessageSize(4, subServiceIds_.get(i));
       }
       size += unknownFields.getSerializedSize();
       memoizedSize = size;
@@ -28926,18 +28687,25 @@ public final class ContextOuterClass {
       if (obj == this) {
        return true;
       }
-      if (!(obj instanceof context.ContextOuterClass.EndPoint)) {
+      if (!(obj instanceof context.ContextOuterClass.Connection)) {
         return super.equals(obj);
       }
-      context.ContextOuterClass.EndPoint other = (context.ContextOuterClass.EndPoint) obj;
+      context.ContextOuterClass.Connection other = (context.ContextOuterClass.Connection) obj;
 
-      if (hasEndpointId() != other.hasEndpointId()) return false;
-      if (hasEndpointId()) {
-        if (!getEndpointId()
-            .equals(other.getEndpointId())) return false;
-      }
-      if (!getEndpointType()
-          .equals(other.getEndpointType())) return false;
+      if (hasConnectionId() != other.hasConnectionId()) return false;
+      if (hasConnectionId()) {
+        if (!getConnectionId()
+            .equals(other.getConnectionId())) return false;
+      }
+      if (hasServiceId() != other.hasServiceId()) return false;
+      if (hasServiceId()) {
+        if (!getServiceId()
+            .equals(other.getServiceId())) return false;
+      }
+      if (!getPathHopsEndpointIdsList()
+          .equals(other.getPathHopsEndpointIdsList())) return false;
+      if (!getSubServiceIdsList()
+          .equals(other.getSubServiceIdsList())) return false;
       if (!unknownFields.equals(other.unknownFields)) return false;
       return true;
     }
@@ -28949,80 +28717,90 @@ public final class ContextOuterClass {
       }
       int hash = 41;
       hash = (19 * hash) + getDescriptor().hashCode();
-      if (hasEndpointId()) {
-        hash = (37 * hash) + ENDPOINT_ID_FIELD_NUMBER;
-        hash = (53 * hash) + getEndpointId().hashCode();
+      if (hasConnectionId()) {
+        hash = (37 * hash) + CONNECTION_ID_FIELD_NUMBER;
+        hash = (53 * hash) + getConnectionId().hashCode();
+      }
+      if (hasServiceId()) {
+        hash = (37 * hash) + SERVICE_ID_FIELD_NUMBER;
+        hash = (53 * hash) + getServiceId().hashCode();
+      }
+      if (getPathHopsEndpointIdsCount() > 0) {
+        hash = (37 * hash) + PATH_HOPS_ENDPOINT_IDS_FIELD_NUMBER;
+        hash = (53 * hash) + getPathHopsEndpointIdsList().hashCode();
+      }
+      if (getSubServiceIdsCount() > 0) {
+        hash = (37 * hash) + SUB_SERVICE_IDS_FIELD_NUMBER;
+        hash = (53 * hash) + getSubServiceIdsList().hashCode();
       }
-      hash = (37 * hash) + ENDPOINT_TYPE_FIELD_NUMBER;
-      hash = (53 * hash) + getEndpointType().hashCode();
       hash = (29 * hash) + unknownFields.hashCode();
       memoizedHashCode = hash;
       return hash;
     }
 
-    public static context.ContextOuterClass.EndPoint parseFrom(
+    public static context.ContextOuterClass.Connection parseFrom(
         java.nio.ByteBuffer data)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data);
     }
-    public static context.ContextOuterClass.EndPoint parseFrom(
+    public static context.ContextOuterClass.Connection parseFrom(
         java.nio.ByteBuffer data,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data, extensionRegistry);
     }
-    public static context.ContextOuterClass.EndPoint parseFrom(
+    public static context.ContextOuterClass.Connection parseFrom(
         com.google.protobuf.ByteString data)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data);
     }
-    public static context.ContextOuterClass.EndPoint parseFrom(
+    public static context.ContextOuterClass.Connection parseFrom(
         com.google.protobuf.ByteString data,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data, extensionRegistry);
     }
-    public static context.ContextOuterClass.EndPoint parseFrom(byte[] data)
+    public static context.ContextOuterClass.Connection parseFrom(byte[] data)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data);
     }
-    public static context.ContextOuterClass.EndPoint parseFrom(
+    public static context.ContextOuterClass.Connection parseFrom(
         byte[] data,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data, extensionRegistry);
     }
-    public static context.ContextOuterClass.EndPoint parseFrom(java.io.InputStream input)
+    public static context.ContextOuterClass.Connection parseFrom(java.io.InputStream input)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseWithIOException(PARSER, input);
     }
-    public static context.ContextOuterClass.EndPoint parseFrom(
+    public static context.ContextOuterClass.Connection parseFrom(
         java.io.InputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseWithIOException(PARSER, input, extensionRegistry);
     }
-    public static context.ContextOuterClass.EndPoint parseDelimitedFrom(java.io.InputStream input)
+    public static context.ContextOuterClass.Connection parseDelimitedFrom(java.io.InputStream input)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseDelimitedWithIOException(PARSER, input);
     }
-    public static context.ContextOuterClass.EndPoint parseDelimitedFrom(
+    public static context.ContextOuterClass.Connection parseDelimitedFrom(
         java.io.InputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseDelimitedWithIOException(PARSER, input, extensionRegistry);
     }
-    public static context.ContextOuterClass.EndPoint parseFrom(
+    public static context.ContextOuterClass.Connection parseFrom(
         com.google.protobuf.CodedInputStream input)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseWithIOException(PARSER, input);
     }
-    public static context.ContextOuterClass.EndPoint parseFrom(
+    public static context.ContextOuterClass.Connection parseFrom(
         com.google.protobuf.CodedInputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws java.io.IOException {
@@ -29035,7 +28813,7 @@ public final class ContextOuterClass {
     public static Builder newBuilder() {
       return DEFAULT_INSTANCE.toBuilder();
     }
-    public static Builder newBuilder(context.ContextOuterClass.EndPoint prototype) {
+    public static Builder newBuilder(context.ContextOuterClass.Connection prototype) {
       return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
     }
     @java.lang.Override
@@ -29051,26 +28829,26 @@ public final class ContextOuterClass {
       return builder;
     }
     /**
-     * Protobuf type {@code context.EndPoint}
+     * Protobuf type {@code context.Connection}
      */
     public static final class Builder extends
         com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
-        // @@protoc_insertion_point(builder_implements:context.EndPoint)
-        context.ContextOuterClass.EndPointOrBuilder {
+        // @@protoc_insertion_point(builder_implements:context.Connection)
+        context.ContextOuterClass.ConnectionOrBuilder {
       public static final com.google.protobuf.Descriptors.Descriptor
           getDescriptor() {
-        return context.ContextOuterClass.internal_static_context_EndPoint_descriptor;
+        return context.ContextOuterClass.internal_static_context_Connection_descriptor;
       }
 
       @java.lang.Override
       protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
           internalGetFieldAccessorTable() {
-        return context.ContextOuterClass.internal_static_context_EndPoint_fieldAccessorTable
+        return context.ContextOuterClass.internal_static_context_Connection_fieldAccessorTable
             .ensureFieldAccessorsInitialized(
-                context.ContextOuterClass.EndPoint.class, context.ContextOuterClass.EndPoint.Builder.class);
+                context.ContextOuterClass.Connection.class, context.ContextOuterClass.Connection.Builder.class);
       }
 
-      // Construct using context.ContextOuterClass.EndPoint.newBuilder()
+      // Construct using context.ContextOuterClass.Connection.newBuilder()
       private Builder() {
         maybeForceBuilderInitialization();
       }
@@ -29083,36 +28861,54 @@ public final class ContextOuterClass {
       private void maybeForceBuilderInitialization() {
         if (com.google.protobuf.GeneratedMessageV3
                 .alwaysUseFieldBuilders) {
+          getPathHopsEndpointIdsFieldBuilder();
+          getSubServiceIdsFieldBuilder();
         }
       }
       @java.lang.Override
       public Builder clear() {
         super.clear();
-        if (endpointIdBuilder_ == null) {
-          endpointId_ = null;
+        if (connectionIdBuilder_ == null) {
+          connectionId_ = null;
         } else {
-          endpointId_ = null;
-          endpointIdBuilder_ = null;
+          connectionId_ = null;
+          connectionIdBuilder_ = null;
+        }
+        if (serviceIdBuilder_ == null) {
+          serviceId_ = null;
+        } else {
+          serviceId_ = null;
+          serviceIdBuilder_ = null;
+        }
+        if (pathHopsEndpointIdsBuilder_ == null) {
+          pathHopsEndpointIds_ = java.util.Collections.emptyList();
+          bitField0_ = (bitField0_ & ~0x00000001);
+        } else {
+          pathHopsEndpointIdsBuilder_.clear();
+        }
+        if (subServiceIdsBuilder_ == null) {
+          subServiceIds_ = java.util.Collections.emptyList();
+          bitField0_ = (bitField0_ & ~0x00000002);
+        } else {
+          subServiceIdsBuilder_.clear();
         }
-        endpointType_ = "";
-
         return this;
       }
 
       @java.lang.Override
       public com.google.protobuf.Descriptors.Descriptor
           getDescriptorForType() {
-        return context.ContextOuterClass.internal_static_context_EndPoint_descriptor;
+        return context.ContextOuterClass.internal_static_context_Connection_descriptor;
       }
 
       @java.lang.Override
-      public context.ContextOuterClass.EndPoint getDefaultInstanceForType() {
-        return context.ContextOuterClass.EndPoint.getDefaultInstance();
+      public context.ContextOuterClass.Connection getDefaultInstanceForType() {
+        return context.ContextOuterClass.Connection.getDefaultInstance();
       }
 
       @java.lang.Override
-      public context.ContextOuterClass.EndPoint build() {
-        context.ContextOuterClass.EndPoint result = buildPartial();
+      public context.ContextOuterClass.Connection build() {
+        context.ContextOuterClass.Connection result = buildPartial();
         if (!result.isInitialized()) {
           throw newUninitializedMessageException(result);
         }
@@ -29120,14 +28916,37 @@ public final class ContextOuterClass {
       }
 
       @java.lang.Override
-      public context.ContextOuterClass.EndPoint buildPartial() {
-        context.ContextOuterClass.EndPoint result = new context.ContextOuterClass.EndPoint(this);
-        if (endpointIdBuilder_ == null) {
-          result.endpointId_ = endpointId_;
+      public context.ContextOuterClass.Connection buildPartial() {
+        context.ContextOuterClass.Connection result = new context.ContextOuterClass.Connection(this);
+        int from_bitField0_ = bitField0_;
+        if (connectionIdBuilder_ == null) {
+          result.connectionId_ = connectionId_;
         } else {
-          result.endpointId_ = endpointIdBuilder_.build();
+          result.connectionId_ = connectionIdBuilder_.build();
+        }
+        if (serviceIdBuilder_ == null) {
+          result.serviceId_ = serviceId_;
+        } else {
+          result.serviceId_ = serviceIdBuilder_.build();
+        }
+        if (pathHopsEndpointIdsBuilder_ == null) {
+          if (((bitField0_ & 0x00000001) != 0)) {
+            pathHopsEndpointIds_ = java.util.Collections.unmodifiableList(pathHopsEndpointIds_);
+            bitField0_ = (bitField0_ & ~0x00000001);
+          }
+          result.pathHopsEndpointIds_ = pathHopsEndpointIds_;
+        } else {
+          result.pathHopsEndpointIds_ = pathHopsEndpointIdsBuilder_.build();
+        }
+        if (subServiceIdsBuilder_ == null) {
+          if (((bitField0_ & 0x00000002) != 0)) {
+            subServiceIds_ = java.util.Collections.unmodifiableList(subServiceIds_);
+            bitField0_ = (bitField0_ & ~0x00000002);
+          }
+          result.subServiceIds_ = subServiceIds_;
+        } else {
+          result.subServiceIds_ = subServiceIdsBuilder_.build();
         }
-        result.endpointType_ = endpointType_;
         onBuilt();
         return result;
       }
@@ -29166,22 +28985,73 @@ public final class ContextOuterClass {
       }
       @java.lang.Override
       public Builder mergeFrom(com.google.protobuf.Message other) {
-        if (other instanceof context.ContextOuterClass.EndPoint) {
-          return mergeFrom((context.ContextOuterClass.EndPoint)other);
+        if (other instanceof context.ContextOuterClass.Connection) {
+          return mergeFrom((context.ContextOuterClass.Connection)other);
         } else {
           super.mergeFrom(other);
           return this;
         }
       }
 
-      public Builder mergeFrom(context.ContextOuterClass.EndPoint other) {
-        if (other == context.ContextOuterClass.EndPoint.getDefaultInstance()) return this;
-        if (other.hasEndpointId()) {
-          mergeEndpointId(other.getEndpointId());
+      public Builder mergeFrom(context.ContextOuterClass.Connection other) {
+        if (other == context.ContextOuterClass.Connection.getDefaultInstance()) return this;
+        if (other.hasConnectionId()) {
+          mergeConnectionId(other.getConnectionId());
         }
-        if (!other.getEndpointType().isEmpty()) {
-          endpointType_ = other.endpointType_;
-          onChanged();
+        if (other.hasServiceId()) {
+          mergeServiceId(other.getServiceId());
+        }
+        if (pathHopsEndpointIdsBuilder_ == null) {
+          if (!other.pathHopsEndpointIds_.isEmpty()) {
+            if (pathHopsEndpointIds_.isEmpty()) {
+              pathHopsEndpointIds_ = other.pathHopsEndpointIds_;
+              bitField0_ = (bitField0_ & ~0x00000001);
+            } else {
+              ensurePathHopsEndpointIdsIsMutable();
+              pathHopsEndpointIds_.addAll(other.pathHopsEndpointIds_);
+            }
+            onChanged();
+          }
+        } else {
+          if (!other.pathHopsEndpointIds_.isEmpty()) {
+            if (pathHopsEndpointIdsBuilder_.isEmpty()) {
+              pathHopsEndpointIdsBuilder_.dispose();
+              pathHopsEndpointIdsBuilder_ = null;
+              pathHopsEndpointIds_ = other.pathHopsEndpointIds_;
+              bitField0_ = (bitField0_ & ~0x00000001);
+              pathHopsEndpointIdsBuilder_ = 
+                com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders ?
+                   getPathHopsEndpointIdsFieldBuilder() : null;
+            } else {
+              pathHopsEndpointIdsBuilder_.addAllMessages(other.pathHopsEndpointIds_);
+            }
+          }
+        }
+        if (subServiceIdsBuilder_ == null) {
+          if (!other.subServiceIds_.isEmpty()) {
+            if (subServiceIds_.isEmpty()) {
+              subServiceIds_ = other.subServiceIds_;
+              bitField0_ = (bitField0_ & ~0x00000002);
+            } else {
+              ensureSubServiceIdsIsMutable();
+              subServiceIds_.addAll(other.subServiceIds_);
+            }
+            onChanged();
+          }
+        } else {
+          if (!other.subServiceIds_.isEmpty()) {
+            if (subServiceIdsBuilder_.isEmpty()) {
+              subServiceIdsBuilder_.dispose();
+              subServiceIdsBuilder_ = null;
+              subServiceIds_ = other.subServiceIds_;
+              bitField0_ = (bitField0_ & ~0x00000002);
+              subServiceIdsBuilder_ = 
+                com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders ?
+                   getSubServiceIdsFieldBuilder() : null;
+            } else {
+              subServiceIdsBuilder_.addAllMessages(other.subServiceIds_);
+            }
+          }
         }
         this.mergeUnknownFields(other.unknownFields);
         onChanged();
@@ -29198,11 +29068,11 @@ public final class ContextOuterClass {
           com.google.protobuf.CodedInputStream input,
           com.google.protobuf.ExtensionRegistryLite extensionRegistry)
           throws java.io.IOException {
-        context.ContextOuterClass.EndPoint parsedMessage = null;
+        context.ContextOuterClass.Connection parsedMessage = null;
         try {
           parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
         } catch (com.google.protobuf.InvalidProtocolBufferException e) {
-          parsedMessage = (context.ContextOuterClass.EndPoint) e.getUnfinishedMessage();
+          parsedMessage = (context.ContextOuterClass.Connection) e.getUnfinishedMessage();
           throw e.unwrapIOException();
         } finally {
           if (parsedMessage != null) {
@@ -29211,316 +29081,1620 @@ public final class ContextOuterClass {
         }
         return this;
       }
+      private int bitField0_;
 
-      private context.ContextOuterClass.EndPointId endpointId_;
+      private context.ContextOuterClass.ConnectionId connectionId_;
       private com.google.protobuf.SingleFieldBuilderV3<
-          context.ContextOuterClass.EndPointId, context.ContextOuterClass.EndPointId.Builder, context.ContextOuterClass.EndPointIdOrBuilder> endpointIdBuilder_;
+          context.ContextOuterClass.ConnectionId, context.ContextOuterClass.ConnectionId.Builder, context.ContextOuterClass.ConnectionIdOrBuilder> connectionIdBuilder_;
       /**
-       * <code>.context.EndPointId endpoint_id = 1;</code>
-       * @return Whether the endpointId field is set.
+       * <code>.context.ConnectionId connection_id = 1;</code>
+       * @return Whether the connectionId field is set.
        */
-      public boolean hasEndpointId() {
-        return endpointIdBuilder_ != null || endpointId_ != null;
+      public boolean hasConnectionId() {
+        return connectionIdBuilder_ != null || connectionId_ != null;
       }
       /**
-       * <code>.context.EndPointId endpoint_id = 1;</code>
-       * @return The endpointId.
+       * <code>.context.ConnectionId connection_id = 1;</code>
+       * @return The connectionId.
        */
-      public context.ContextOuterClass.EndPointId getEndpointId() {
-        if (endpointIdBuilder_ == null) {
-          return endpointId_ == null ? context.ContextOuterClass.EndPointId.getDefaultInstance() : endpointId_;
+      public context.ContextOuterClass.ConnectionId getConnectionId() {
+        if (connectionIdBuilder_ == null) {
+          return connectionId_ == null ? context.ContextOuterClass.ConnectionId.getDefaultInstance() : connectionId_;
         } else {
-          return endpointIdBuilder_.getMessage();
+          return connectionIdBuilder_.getMessage();
         }
       }
       /**
-       * <code>.context.EndPointId endpoint_id = 1;</code>
+       * <code>.context.ConnectionId connection_id = 1;</code>
        */
-      public Builder setEndpointId(context.ContextOuterClass.EndPointId value) {
-        if (endpointIdBuilder_ == null) {
+      public Builder setConnectionId(context.ContextOuterClass.ConnectionId value) {
+        if (connectionIdBuilder_ == null) {
           if (value == null) {
             throw new NullPointerException();
           }
-          endpointId_ = value;
+          connectionId_ = value;
           onChanged();
         } else {
-          endpointIdBuilder_.setMessage(value);
+          connectionIdBuilder_.setMessage(value);
         }
 
         return this;
       }
       /**
-       * <code>.context.EndPointId endpoint_id = 1;</code>
+       * <code>.context.ConnectionId connection_id = 1;</code>
        */
-      public Builder setEndpointId(
-          context.ContextOuterClass.EndPointId.Builder builderForValue) {
-        if (endpointIdBuilder_ == null) {
-          endpointId_ = builderForValue.build();
+      public Builder setConnectionId(
+          context.ContextOuterClass.ConnectionId.Builder builderForValue) {
+        if (connectionIdBuilder_ == null) {
+          connectionId_ = builderForValue.build();
           onChanged();
         } else {
-          endpointIdBuilder_.setMessage(builderForValue.build());
+          connectionIdBuilder_.setMessage(builderForValue.build());
         }
 
         return this;
       }
       /**
-       * <code>.context.EndPointId endpoint_id = 1;</code>
+       * <code>.context.ConnectionId connection_id = 1;</code>
        */
-      public Builder mergeEndpointId(context.ContextOuterClass.EndPointId value) {
-        if (endpointIdBuilder_ == null) {
-          if (endpointId_ != null) {
-            endpointId_ =
-              context.ContextOuterClass.EndPointId.newBuilder(endpointId_).mergeFrom(value).buildPartial();
+      public Builder mergeConnectionId(context.ContextOuterClass.ConnectionId value) {
+        if (connectionIdBuilder_ == null) {
+          if (connectionId_ != null) {
+            connectionId_ =
+              context.ContextOuterClass.ConnectionId.newBuilder(connectionId_).mergeFrom(value).buildPartial();
           } else {
-            endpointId_ = value;
+            connectionId_ = value;
           }
           onChanged();
         } else {
-          endpointIdBuilder_.mergeFrom(value);
+          connectionIdBuilder_.mergeFrom(value);
         }
 
         return this;
       }
       /**
-       * <code>.context.EndPointId endpoint_id = 1;</code>
+       * <code>.context.ConnectionId connection_id = 1;</code>
        */
-      public Builder clearEndpointId() {
-        if (endpointIdBuilder_ == null) {
-          endpointId_ = null;
+      public Builder clearConnectionId() {
+        if (connectionIdBuilder_ == null) {
+          connectionId_ = null;
           onChanged();
         } else {
-          endpointId_ = null;
-          endpointIdBuilder_ = null;
+          connectionId_ = null;
+          connectionIdBuilder_ = null;
         }
 
         return this;
       }
       /**
-       * <code>.context.EndPointId endpoint_id = 1;</code>
+       * <code>.context.ConnectionId connection_id = 1;</code>
        */
-      public context.ContextOuterClass.EndPointId.Builder getEndpointIdBuilder() {
+      public context.ContextOuterClass.ConnectionId.Builder getConnectionIdBuilder() {
         
         onChanged();
-        return getEndpointIdFieldBuilder().getBuilder();
+        return getConnectionIdFieldBuilder().getBuilder();
       }
       /**
-       * <code>.context.EndPointId endpoint_id = 1;</code>
+       * <code>.context.ConnectionId connection_id = 1;</code>
        */
-      public context.ContextOuterClass.EndPointIdOrBuilder getEndpointIdOrBuilder() {
-        if (endpointIdBuilder_ != null) {
-          return endpointIdBuilder_.getMessageOrBuilder();
+      public context.ContextOuterClass.ConnectionIdOrBuilder getConnectionIdOrBuilder() {
+        if (connectionIdBuilder_ != null) {
+          return connectionIdBuilder_.getMessageOrBuilder();
         } else {
-          return endpointId_ == null ?
-              context.ContextOuterClass.EndPointId.getDefaultInstance() : endpointId_;
+          return connectionId_ == null ?
+              context.ContextOuterClass.ConnectionId.getDefaultInstance() : connectionId_;
         }
       }
       /**
-       * <code>.context.EndPointId endpoint_id = 1;</code>
+       * <code>.context.ConnectionId connection_id = 1;</code>
        */
       private com.google.protobuf.SingleFieldBuilderV3<
-          context.ContextOuterClass.EndPointId, context.ContextOuterClass.EndPointId.Builder, context.ContextOuterClass.EndPointIdOrBuilder> 
-          getEndpointIdFieldBuilder() {
-        if (endpointIdBuilder_ == null) {
-          endpointIdBuilder_ = new com.google.protobuf.SingleFieldBuilderV3<
-              context.ContextOuterClass.EndPointId, context.ContextOuterClass.EndPointId.Builder, context.ContextOuterClass.EndPointIdOrBuilder>(
-                  getEndpointId(),
+          context.ContextOuterClass.ConnectionId, context.ContextOuterClass.ConnectionId.Builder, context.ContextOuterClass.ConnectionIdOrBuilder> 
+          getConnectionIdFieldBuilder() {
+        if (connectionIdBuilder_ == null) {
+          connectionIdBuilder_ = new com.google.protobuf.SingleFieldBuilderV3<
+              context.ContextOuterClass.ConnectionId, context.ContextOuterClass.ConnectionId.Builder, context.ContextOuterClass.ConnectionIdOrBuilder>(
+                  getConnectionId(),
                   getParentForChildren(),
                   isClean());
-          endpointId_ = null;
+          connectionId_ = null;
         }
-        return endpointIdBuilder_;
+        return connectionIdBuilder_;
       }
 
-      private java.lang.Object endpointType_ = "";
+      private context.ContextOuterClass.ServiceId serviceId_;
+      private com.google.protobuf.SingleFieldBuilderV3<
+          context.ContextOuterClass.ServiceId, context.ContextOuterClass.ServiceId.Builder, context.ContextOuterClass.ServiceIdOrBuilder> serviceIdBuilder_;
       /**
-       * <code>string endpoint_type = 2;</code>
-       * @return The endpointType.
+       * <code>.context.ServiceId service_id = 2;</code>
+       * @return Whether the serviceId field is set.
        */
-      public java.lang.String getEndpointType() {
-        java.lang.Object ref = endpointType_;
-        if (!(ref instanceof java.lang.String)) {
-          com.google.protobuf.ByteString bs =
-              (com.google.protobuf.ByteString) ref;
-          java.lang.String s = bs.toStringUtf8();
-          endpointType_ = s;
-          return s;
+      public boolean hasServiceId() {
+        return serviceIdBuilder_ != null || serviceId_ != null;
+      }
+      /**
+       * <code>.context.ServiceId service_id = 2;</code>
+       * @return The serviceId.
+       */
+      public context.ContextOuterClass.ServiceId getServiceId() {
+        if (serviceIdBuilder_ == null) {
+          return serviceId_ == null ? context.ContextOuterClass.ServiceId.getDefaultInstance() : serviceId_;
         } else {
-          return (java.lang.String) ref;
+          return serviceIdBuilder_.getMessage();
         }
       }
       /**
-       * <code>string endpoint_type = 2;</code>
-       * @return The bytes for endpointType.
+       * <code>.context.ServiceId service_id = 2;</code>
        */
-      public com.google.protobuf.ByteString
-          getEndpointTypeBytes() {
-        java.lang.Object ref = endpointType_;
-        if (ref instanceof String) {
-          com.google.protobuf.ByteString b = 
-              com.google.protobuf.ByteString.copyFromUtf8(
-                  (java.lang.String) ref);
-          endpointType_ = b;
-          return b;
+      public Builder setServiceId(context.ContextOuterClass.ServiceId value) {
+        if (serviceIdBuilder_ == null) {
+          if (value == null) {
+            throw new NullPointerException();
+          }
+          serviceId_ = value;
+          onChanged();
         } else {
-          return (com.google.protobuf.ByteString) ref;
+          serviceIdBuilder_.setMessage(value);
         }
+
+        return this;
       }
       /**
-       * <code>string endpoint_type = 2;</code>
-       * @param value The endpointType to set.
-       * @return This builder for chaining.
+       * <code>.context.ServiceId service_id = 2;</code>
        */
-      public Builder setEndpointType(
-          java.lang.String value) {
-        if (value == null) {
-    throw new NullPointerException();
-  }
-  
-        endpointType_ = value;
-        onChanged();
+      public Builder setServiceId(
+          context.ContextOuterClass.ServiceId.Builder builderForValue) {
+        if (serviceIdBuilder_ == null) {
+          serviceId_ = builderForValue.build();
+          onChanged();
+        } else {
+          serviceIdBuilder_.setMessage(builderForValue.build());
+        }
+
         return this;
       }
       /**
-       * <code>string endpoint_type = 2;</code>
-       * @return This builder for chaining.
+       * <code>.context.ServiceId service_id = 2;</code>
        */
-      public Builder clearEndpointType() {
-        
-        endpointType_ = getDefaultInstance().getEndpointType();
-        onChanged();
+      public Builder mergeServiceId(context.ContextOuterClass.ServiceId value) {
+        if (serviceIdBuilder_ == null) {
+          if (serviceId_ != null) {
+            serviceId_ =
+              context.ContextOuterClass.ServiceId.newBuilder(serviceId_).mergeFrom(value).buildPartial();
+          } else {
+            serviceId_ = value;
+          }
+          onChanged();
+        } else {
+          serviceIdBuilder_.mergeFrom(value);
+        }
+
         return this;
       }
       /**
-       * <code>string endpoint_type = 2;</code>
-       * @param value The bytes for endpointType to set.
-       * @return This builder for chaining.
+       * <code>.context.ServiceId service_id = 2;</code>
        */
-      public Builder setEndpointTypeBytes(
-          com.google.protobuf.ByteString value) {
-        if (value == null) {
-    throw new NullPointerException();
-  }
-  checkByteStringIsUtf8(value);
+      public Builder clearServiceId() {
+        if (serviceIdBuilder_ == null) {
+          serviceId_ = null;
+          onChanged();
+        } else {
+          serviceId_ = null;
+          serviceIdBuilder_ = null;
+        }
+
+        return this;
+      }
+      /**
+       * <code>.context.ServiceId service_id = 2;</code>
+       */
+      public context.ContextOuterClass.ServiceId.Builder getServiceIdBuilder() {
         
-        endpointType_ = value;
         onChanged();
-        return this;
+        return getServiceIdFieldBuilder().getBuilder();
       }
-      @java.lang.Override
-      public final Builder setUnknownFields(
-          final com.google.protobuf.UnknownFieldSet unknownFields) {
-        return super.setUnknownFields(unknownFields);
+      /**
+       * <code>.context.ServiceId service_id = 2;</code>
+       */
+      public context.ContextOuterClass.ServiceIdOrBuilder getServiceIdOrBuilder() {
+        if (serviceIdBuilder_ != null) {
+          return serviceIdBuilder_.getMessageOrBuilder();
+        } else {
+          return serviceId_ == null ?
+              context.ContextOuterClass.ServiceId.getDefaultInstance() : serviceId_;
+        }
       }
-
-      @java.lang.Override
-      public final Builder mergeUnknownFields(
-          final com.google.protobuf.UnknownFieldSet unknownFields) {
-        return super.mergeUnknownFields(unknownFields);
+      /**
+       * <code>.context.ServiceId service_id = 2;</code>
+       */
+      private com.google.protobuf.SingleFieldBuilderV3<
+          context.ContextOuterClass.ServiceId, context.ContextOuterClass.ServiceId.Builder, context.ContextOuterClass.ServiceIdOrBuilder> 
+          getServiceIdFieldBuilder() {
+        if (serviceIdBuilder_ == null) {
+          serviceIdBuilder_ = new com.google.protobuf.SingleFieldBuilderV3<
+              context.ContextOuterClass.ServiceId, context.ContextOuterClass.ServiceId.Builder, context.ContextOuterClass.ServiceIdOrBuilder>(
+                  getServiceId(),
+                  getParentForChildren(),
+                  isClean());
+          serviceId_ = null;
+        }
+        return serviceIdBuilder_;
       }
 
-
-      // @@protoc_insertion_point(builder_scope:context.EndPoint)
-    }
-
-    // @@protoc_insertion_point(class_scope:context.EndPoint)
-    private static final context.ContextOuterClass.EndPoint DEFAULT_INSTANCE;
-    static {
-      DEFAULT_INSTANCE = new context.ContextOuterClass.EndPoint();
-    }
-
-    public static context.ContextOuterClass.EndPoint getDefaultInstance() {
-      return DEFAULT_INSTANCE;
-    }
-
-    private static final com.google.protobuf.Parser<EndPoint>
-        PARSER = new com.google.protobuf.AbstractParser<EndPoint>() {
-      @java.lang.Override
-      public EndPoint parsePartialFrom(
-          com.google.protobuf.CodedInputStream input,
-          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
-          throws com.google.protobuf.InvalidProtocolBufferException {
-        return new EndPoint(input, extensionRegistry);
+      private java.util.List<context.ContextOuterClass.EndPointId> pathHopsEndpointIds_ =
+        java.util.Collections.emptyList();
+      private void ensurePathHopsEndpointIdsIsMutable() {
+        if (!((bitField0_ & 0x00000001) != 0)) {
+          pathHopsEndpointIds_ = new java.util.ArrayList<context.ContextOuterClass.EndPointId>(pathHopsEndpointIds_);
+          bitField0_ |= 0x00000001;
+         }
       }
-    };
-
-    public static com.google.protobuf.Parser<EndPoint> parser() {
-      return PARSER;
-    }
-
-    @java.lang.Override
-    public com.google.protobuf.Parser<EndPoint> getParserForType() {
-      return PARSER;
-    }
-
-    @java.lang.Override
-    public context.ContextOuterClass.EndPoint getDefaultInstanceForType() {
-      return DEFAULT_INSTANCE;
-    }
-
-  }
-
-  public interface ConfigRuleOrBuilder extends
-      // @@protoc_insertion_point(interface_extends:context.ConfigRule)
-      com.google.protobuf.MessageOrBuilder {
-
-    /**
-     * <code>.context.ConfigActionEnum action = 1;</code>
-     * @return The enum numeric value on the wire for action.
-     */
-    int getActionValue();
-    /**
-     * <code>.context.ConfigActionEnum action = 1;</code>
-     * @return The action.
-     */
-    context.ContextOuterClass.ConfigActionEnum getAction();
 
-    /**
-     * <code>string resource_key = 2;</code>
-     * @return The resourceKey.
-     */
-    java.lang.String getResourceKey();
-    /**
-     * <code>string resource_key = 2;</code>
-     * @return The bytes for resourceKey.
-     */
-    com.google.protobuf.ByteString
-        getResourceKeyBytes();
+      private com.google.protobuf.RepeatedFieldBuilderV3<
+          context.ContextOuterClass.EndPointId, context.ContextOuterClass.EndPointId.Builder, context.ContextOuterClass.EndPointIdOrBuilder> pathHopsEndpointIdsBuilder_;
 
-    /**
-     * <code>string resource_value = 3;</code>
-     * @return The resourceValue.
-     */
-    java.lang.String getResourceValue();
-    /**
-     * <code>string resource_value = 3;</code>
-     * @return The bytes for resourceValue.
+      /**
+       * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
+       */
+      public java.util.List<context.ContextOuterClass.EndPointId> getPathHopsEndpointIdsList() {
+        if (pathHopsEndpointIdsBuilder_ == null) {
+          return java.util.Collections.unmodifiableList(pathHopsEndpointIds_);
+        } else {
+          return pathHopsEndpointIdsBuilder_.getMessageList();
+        }
+      }
+      /**
+       * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
+       */
+      public int getPathHopsEndpointIdsCount() {
+        if (pathHopsEndpointIdsBuilder_ == null) {
+          return pathHopsEndpointIds_.size();
+        } else {
+          return pathHopsEndpointIdsBuilder_.getCount();
+        }
+      }
+      /**
+       * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
+       */
+      public context.ContextOuterClass.EndPointId getPathHopsEndpointIds(int index) {
+        if (pathHopsEndpointIdsBuilder_ == null) {
+          return pathHopsEndpointIds_.get(index);
+        } else {
+          return pathHopsEndpointIdsBuilder_.getMessage(index);
+        }
+      }
+      /**
+       * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
+       */
+      public Builder setPathHopsEndpointIds(
+          int index, context.ContextOuterClass.EndPointId value) {
+        if (pathHopsEndpointIdsBuilder_ == null) {
+          if (value == null) {
+            throw new NullPointerException();
+          }
+          ensurePathHopsEndpointIdsIsMutable();
+          pathHopsEndpointIds_.set(index, value);
+          onChanged();
+        } else {
+          pathHopsEndpointIdsBuilder_.setMessage(index, value);
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
+       */
+      public Builder setPathHopsEndpointIds(
+          int index, context.ContextOuterClass.EndPointId.Builder builderForValue) {
+        if (pathHopsEndpointIdsBuilder_ == null) {
+          ensurePathHopsEndpointIdsIsMutable();
+          pathHopsEndpointIds_.set(index, builderForValue.build());
+          onChanged();
+        } else {
+          pathHopsEndpointIdsBuilder_.setMessage(index, builderForValue.build());
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
+       */
+      public Builder addPathHopsEndpointIds(context.ContextOuterClass.EndPointId value) {
+        if (pathHopsEndpointIdsBuilder_ == null) {
+          if (value == null) {
+            throw new NullPointerException();
+          }
+          ensurePathHopsEndpointIdsIsMutable();
+          pathHopsEndpointIds_.add(value);
+          onChanged();
+        } else {
+          pathHopsEndpointIdsBuilder_.addMessage(value);
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
+       */
+      public Builder addPathHopsEndpointIds(
+          int index, context.ContextOuterClass.EndPointId value) {
+        if (pathHopsEndpointIdsBuilder_ == null) {
+          if (value == null) {
+            throw new NullPointerException();
+          }
+          ensurePathHopsEndpointIdsIsMutable();
+          pathHopsEndpointIds_.add(index, value);
+          onChanged();
+        } else {
+          pathHopsEndpointIdsBuilder_.addMessage(index, value);
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
+       */
+      public Builder addPathHopsEndpointIds(
+          context.ContextOuterClass.EndPointId.Builder builderForValue) {
+        if (pathHopsEndpointIdsBuilder_ == null) {
+          ensurePathHopsEndpointIdsIsMutable();
+          pathHopsEndpointIds_.add(builderForValue.build());
+          onChanged();
+        } else {
+          pathHopsEndpointIdsBuilder_.addMessage(builderForValue.build());
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
+       */
+      public Builder addPathHopsEndpointIds(
+          int index, context.ContextOuterClass.EndPointId.Builder builderForValue) {
+        if (pathHopsEndpointIdsBuilder_ == null) {
+          ensurePathHopsEndpointIdsIsMutable();
+          pathHopsEndpointIds_.add(index, builderForValue.build());
+          onChanged();
+        } else {
+          pathHopsEndpointIdsBuilder_.addMessage(index, builderForValue.build());
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
+       */
+      public Builder addAllPathHopsEndpointIds(
+          java.lang.Iterable<? extends context.ContextOuterClass.EndPointId> values) {
+        if (pathHopsEndpointIdsBuilder_ == null) {
+          ensurePathHopsEndpointIdsIsMutable();
+          com.google.protobuf.AbstractMessageLite.Builder.addAll(
+              values, pathHopsEndpointIds_);
+          onChanged();
+        } else {
+          pathHopsEndpointIdsBuilder_.addAllMessages(values);
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
+       */
+      public Builder clearPathHopsEndpointIds() {
+        if (pathHopsEndpointIdsBuilder_ == null) {
+          pathHopsEndpointIds_ = java.util.Collections.emptyList();
+          bitField0_ = (bitField0_ & ~0x00000001);
+          onChanged();
+        } else {
+          pathHopsEndpointIdsBuilder_.clear();
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
+       */
+      public Builder removePathHopsEndpointIds(int index) {
+        if (pathHopsEndpointIdsBuilder_ == null) {
+          ensurePathHopsEndpointIdsIsMutable();
+          pathHopsEndpointIds_.remove(index);
+          onChanged();
+        } else {
+          pathHopsEndpointIdsBuilder_.remove(index);
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
+       */
+      public context.ContextOuterClass.EndPointId.Builder getPathHopsEndpointIdsBuilder(
+          int index) {
+        return getPathHopsEndpointIdsFieldBuilder().getBuilder(index);
+      }
+      /**
+       * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
+       */
+      public context.ContextOuterClass.EndPointIdOrBuilder getPathHopsEndpointIdsOrBuilder(
+          int index) {
+        if (pathHopsEndpointIdsBuilder_ == null) {
+          return pathHopsEndpointIds_.get(index);  } else {
+          return pathHopsEndpointIdsBuilder_.getMessageOrBuilder(index);
+        }
+      }
+      /**
+       * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
+       */
+      public java.util.List<? extends context.ContextOuterClass.EndPointIdOrBuilder> 
+           getPathHopsEndpointIdsOrBuilderList() {
+        if (pathHopsEndpointIdsBuilder_ != null) {
+          return pathHopsEndpointIdsBuilder_.getMessageOrBuilderList();
+        } else {
+          return java.util.Collections.unmodifiableList(pathHopsEndpointIds_);
+        }
+      }
+      /**
+       * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
+       */
+      public context.ContextOuterClass.EndPointId.Builder addPathHopsEndpointIdsBuilder() {
+        return getPathHopsEndpointIdsFieldBuilder().addBuilder(
+            context.ContextOuterClass.EndPointId.getDefaultInstance());
+      }
+      /**
+       * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
+       */
+      public context.ContextOuterClass.EndPointId.Builder addPathHopsEndpointIdsBuilder(
+          int index) {
+        return getPathHopsEndpointIdsFieldBuilder().addBuilder(
+            index, context.ContextOuterClass.EndPointId.getDefaultInstance());
+      }
+      /**
+       * <code>repeated .context.EndPointId path_hops_endpoint_ids = 3;</code>
+       */
+      public java.util.List<context.ContextOuterClass.EndPointId.Builder> 
+           getPathHopsEndpointIdsBuilderList() {
+        return getPathHopsEndpointIdsFieldBuilder().getBuilderList();
+      }
+      private com.google.protobuf.RepeatedFieldBuilderV3<
+          context.ContextOuterClass.EndPointId, context.ContextOuterClass.EndPointId.Builder, context.ContextOuterClass.EndPointIdOrBuilder> 
+          getPathHopsEndpointIdsFieldBuilder() {
+        if (pathHopsEndpointIdsBuilder_ == null) {
+          pathHopsEndpointIdsBuilder_ = new com.google.protobuf.RepeatedFieldBuilderV3<
+              context.ContextOuterClass.EndPointId, context.ContextOuterClass.EndPointId.Builder, context.ContextOuterClass.EndPointIdOrBuilder>(
+                  pathHopsEndpointIds_,
+                  ((bitField0_ & 0x00000001) != 0),
+                  getParentForChildren(),
+                  isClean());
+          pathHopsEndpointIds_ = null;
+        }
+        return pathHopsEndpointIdsBuilder_;
+      }
+
+      private java.util.List<context.ContextOuterClass.ServiceId> subServiceIds_ =
+        java.util.Collections.emptyList();
+      private void ensureSubServiceIdsIsMutable() {
+        if (!((bitField0_ & 0x00000002) != 0)) {
+          subServiceIds_ = new java.util.ArrayList<context.ContextOuterClass.ServiceId>(subServiceIds_);
+          bitField0_ |= 0x00000002;
+         }
+      }
+
+      private com.google.protobuf.RepeatedFieldBuilderV3<
+          context.ContextOuterClass.ServiceId, context.ContextOuterClass.ServiceId.Builder, context.ContextOuterClass.ServiceIdOrBuilder> subServiceIdsBuilder_;
+
+      /**
+       * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
+       */
+      public java.util.List<context.ContextOuterClass.ServiceId> getSubServiceIdsList() {
+        if (subServiceIdsBuilder_ == null) {
+          return java.util.Collections.unmodifiableList(subServiceIds_);
+        } else {
+          return subServiceIdsBuilder_.getMessageList();
+        }
+      }
+      /**
+       * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
+       */
+      public int getSubServiceIdsCount() {
+        if (subServiceIdsBuilder_ == null) {
+          return subServiceIds_.size();
+        } else {
+          return subServiceIdsBuilder_.getCount();
+        }
+      }
+      /**
+       * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
+       */
+      public context.ContextOuterClass.ServiceId getSubServiceIds(int index) {
+        if (subServiceIdsBuilder_ == null) {
+          return subServiceIds_.get(index);
+        } else {
+          return subServiceIdsBuilder_.getMessage(index);
+        }
+      }
+      /**
+       * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
+       */
+      public Builder setSubServiceIds(
+          int index, context.ContextOuterClass.ServiceId value) {
+        if (subServiceIdsBuilder_ == null) {
+          if (value == null) {
+            throw new NullPointerException();
+          }
+          ensureSubServiceIdsIsMutable();
+          subServiceIds_.set(index, value);
+          onChanged();
+        } else {
+          subServiceIdsBuilder_.setMessage(index, value);
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
+       */
+      public Builder setSubServiceIds(
+          int index, context.ContextOuterClass.ServiceId.Builder builderForValue) {
+        if (subServiceIdsBuilder_ == null) {
+          ensureSubServiceIdsIsMutable();
+          subServiceIds_.set(index, builderForValue.build());
+          onChanged();
+        } else {
+          subServiceIdsBuilder_.setMessage(index, builderForValue.build());
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
+       */
+      public Builder addSubServiceIds(context.ContextOuterClass.ServiceId value) {
+        if (subServiceIdsBuilder_ == null) {
+          if (value == null) {
+            throw new NullPointerException();
+          }
+          ensureSubServiceIdsIsMutable();
+          subServiceIds_.add(value);
+          onChanged();
+        } else {
+          subServiceIdsBuilder_.addMessage(value);
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
+       */
+      public Builder addSubServiceIds(
+          int index, context.ContextOuterClass.ServiceId value) {
+        if (subServiceIdsBuilder_ == null) {
+          if (value == null) {
+            throw new NullPointerException();
+          }
+          ensureSubServiceIdsIsMutable();
+          subServiceIds_.add(index, value);
+          onChanged();
+        } else {
+          subServiceIdsBuilder_.addMessage(index, value);
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
+       */
+      public Builder addSubServiceIds(
+          context.ContextOuterClass.ServiceId.Builder builderForValue) {
+        if (subServiceIdsBuilder_ == null) {
+          ensureSubServiceIdsIsMutable();
+          subServiceIds_.add(builderForValue.build());
+          onChanged();
+        } else {
+          subServiceIdsBuilder_.addMessage(builderForValue.build());
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
+       */
+      public Builder addSubServiceIds(
+          int index, context.ContextOuterClass.ServiceId.Builder builderForValue) {
+        if (subServiceIdsBuilder_ == null) {
+          ensureSubServiceIdsIsMutable();
+          subServiceIds_.add(index, builderForValue.build());
+          onChanged();
+        } else {
+          subServiceIdsBuilder_.addMessage(index, builderForValue.build());
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
+       */
+      public Builder addAllSubServiceIds(
+          java.lang.Iterable<? extends context.ContextOuterClass.ServiceId> values) {
+        if (subServiceIdsBuilder_ == null) {
+          ensureSubServiceIdsIsMutable();
+          com.google.protobuf.AbstractMessageLite.Builder.addAll(
+              values, subServiceIds_);
+          onChanged();
+        } else {
+          subServiceIdsBuilder_.addAllMessages(values);
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
+       */
+      public Builder clearSubServiceIds() {
+        if (subServiceIdsBuilder_ == null) {
+          subServiceIds_ = java.util.Collections.emptyList();
+          bitField0_ = (bitField0_ & ~0x00000002);
+          onChanged();
+        } else {
+          subServiceIdsBuilder_.clear();
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
+       */
+      public Builder removeSubServiceIds(int index) {
+        if (subServiceIdsBuilder_ == null) {
+          ensureSubServiceIdsIsMutable();
+          subServiceIds_.remove(index);
+          onChanged();
+        } else {
+          subServiceIdsBuilder_.remove(index);
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
+       */
+      public context.ContextOuterClass.ServiceId.Builder getSubServiceIdsBuilder(
+          int index) {
+        return getSubServiceIdsFieldBuilder().getBuilder(index);
+      }
+      /**
+       * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
+       */
+      public context.ContextOuterClass.ServiceIdOrBuilder getSubServiceIdsOrBuilder(
+          int index) {
+        if (subServiceIdsBuilder_ == null) {
+          return subServiceIds_.get(index);  } else {
+          return subServiceIdsBuilder_.getMessageOrBuilder(index);
+        }
+      }
+      /**
+       * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
+       */
+      public java.util.List<? extends context.ContextOuterClass.ServiceIdOrBuilder> 
+           getSubServiceIdsOrBuilderList() {
+        if (subServiceIdsBuilder_ != null) {
+          return subServiceIdsBuilder_.getMessageOrBuilderList();
+        } else {
+          return java.util.Collections.unmodifiableList(subServiceIds_);
+        }
+      }
+      /**
+       * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
+       */
+      public context.ContextOuterClass.ServiceId.Builder addSubServiceIdsBuilder() {
+        return getSubServiceIdsFieldBuilder().addBuilder(
+            context.ContextOuterClass.ServiceId.getDefaultInstance());
+      }
+      /**
+       * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
+       */
+      public context.ContextOuterClass.ServiceId.Builder addSubServiceIdsBuilder(
+          int index) {
+        return getSubServiceIdsFieldBuilder().addBuilder(
+            index, context.ContextOuterClass.ServiceId.getDefaultInstance());
+      }
+      /**
+       * <code>repeated .context.ServiceId sub_service_ids = 4;</code>
+       */
+      public java.util.List<context.ContextOuterClass.ServiceId.Builder> 
+           getSubServiceIdsBuilderList() {
+        return getSubServiceIdsFieldBuilder().getBuilderList();
+      }
+      private com.google.protobuf.RepeatedFieldBuilderV3<
+          context.ContextOuterClass.ServiceId, context.ContextOuterClass.ServiceId.Builder, context.ContextOuterClass.ServiceIdOrBuilder> 
+          getSubServiceIdsFieldBuilder() {
+        if (subServiceIdsBuilder_ == null) {
+          subServiceIdsBuilder_ = new com.google.protobuf.RepeatedFieldBuilderV3<
+              context.ContextOuterClass.ServiceId, context.ContextOuterClass.ServiceId.Builder, context.ContextOuterClass.ServiceIdOrBuilder>(
+                  subServiceIds_,
+                  ((bitField0_ & 0x00000002) != 0),
+                  getParentForChildren(),
+                  isClean());
+          subServiceIds_ = null;
+        }
+        return subServiceIdsBuilder_;
+      }
+      @java.lang.Override
+      public final Builder setUnknownFields(
+          final com.google.protobuf.UnknownFieldSet unknownFields) {
+        return super.setUnknownFields(unknownFields);
+      }
+
+      @java.lang.Override
+      public final Builder mergeUnknownFields(
+          final com.google.protobuf.UnknownFieldSet unknownFields) {
+        return super.mergeUnknownFields(unknownFields);
+      }
+
+
+      // @@protoc_insertion_point(builder_scope:context.Connection)
+    }
+
+    // @@protoc_insertion_point(class_scope:context.Connection)
+    private static final context.ContextOuterClass.Connection DEFAULT_INSTANCE;
+    static {
+      DEFAULT_INSTANCE = new context.ContextOuterClass.Connection();
+    }
+
+    public static context.ContextOuterClass.Connection getDefaultInstance() {
+      return DEFAULT_INSTANCE;
+    }
+
+    private static final com.google.protobuf.Parser<Connection>
+        PARSER = new com.google.protobuf.AbstractParser<Connection>() {
+      @java.lang.Override
+      public Connection parsePartialFrom(
+          com.google.protobuf.CodedInputStream input,
+          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+          throws com.google.protobuf.InvalidProtocolBufferException {
+        return new Connection(input, extensionRegistry);
+      }
+    };
+
+    public static com.google.protobuf.Parser<Connection> parser() {
+      return PARSER;
+    }
+
+    @java.lang.Override
+    public com.google.protobuf.Parser<Connection> getParserForType() {
+      return PARSER;
+    }
+
+    @java.lang.Override
+    public context.ContextOuterClass.Connection getDefaultInstanceForType() {
+      return DEFAULT_INSTANCE;
+    }
+
+  }
+
+  public interface ConnectionIdListOrBuilder extends
+      // @@protoc_insertion_point(interface_extends:context.ConnectionIdList)
+      com.google.protobuf.MessageOrBuilder {
+
+    /**
+     * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+     */
+    java.util.List<context.ContextOuterClass.ConnectionId> 
+        getConnectionIdsList();
+    /**
+     * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+     */
+    context.ContextOuterClass.ConnectionId getConnectionIds(int index);
+    /**
+     * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+     */
+    int getConnectionIdsCount();
+    /**
+     * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+     */
+    java.util.List<? extends context.ContextOuterClass.ConnectionIdOrBuilder> 
+        getConnectionIdsOrBuilderList();
+    /**
+     * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+     */
+    context.ContextOuterClass.ConnectionIdOrBuilder getConnectionIdsOrBuilder(
+        int index);
+  }
+  /**
+   * Protobuf type {@code context.ConnectionIdList}
+   */
+  public static final class ConnectionIdList extends
+      com.google.protobuf.GeneratedMessageV3 implements
+      // @@protoc_insertion_point(message_implements:context.ConnectionIdList)
+      ConnectionIdListOrBuilder {
+  private static final long serialVersionUID = 0L;
+    // Use ConnectionIdList.newBuilder() to construct.
+    private ConnectionIdList(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
+      super(builder);
+    }
+    private ConnectionIdList() {
+      connectionIds_ = java.util.Collections.emptyList();
+    }
+
+    @java.lang.Override
+    @SuppressWarnings({"unused"})
+    protected java.lang.Object newInstance(
+        UnusedPrivateParameter unused) {
+      return new ConnectionIdList();
+    }
+
+    @java.lang.Override
+    public final com.google.protobuf.UnknownFieldSet
+    getUnknownFields() {
+      return this.unknownFields;
+    }
+    private ConnectionIdList(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      this();
+      if (extensionRegistry == null) {
+        throw new java.lang.NullPointerException();
+      }
+      int mutable_bitField0_ = 0;
+      com.google.protobuf.UnknownFieldSet.Builder unknownFields =
+          com.google.protobuf.UnknownFieldSet.newBuilder();
+      try {
+        boolean done = false;
+        while (!done) {
+          int tag = input.readTag();
+          switch (tag) {
+            case 0:
+              done = true;
+              break;
+            case 10: {
+              if (!((mutable_bitField0_ & 0x00000001) != 0)) {
+                connectionIds_ = new java.util.ArrayList<context.ContextOuterClass.ConnectionId>();
+                mutable_bitField0_ |= 0x00000001;
+              }
+              connectionIds_.add(
+                  input.readMessage(context.ContextOuterClass.ConnectionId.parser(), extensionRegistry));
+              break;
+            }
+            default: {
+              if (!parseUnknownField(
+                  input, unknownFields, extensionRegistry, tag)) {
+                done = true;
+              }
+              break;
+            }
+          }
+        }
+      } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+        throw e.setUnfinishedMessage(this);
+      } catch (java.io.IOException e) {
+        throw new com.google.protobuf.InvalidProtocolBufferException(
+            e).setUnfinishedMessage(this);
+      } finally {
+        if (((mutable_bitField0_ & 0x00000001) != 0)) {
+          connectionIds_ = java.util.Collections.unmodifiableList(connectionIds_);
+        }
+        this.unknownFields = unknownFields.build();
+        makeExtensionsImmutable();
+      }
+    }
+    public static final com.google.protobuf.Descriptors.Descriptor
+        getDescriptor() {
+      return context.ContextOuterClass.internal_static_context_ConnectionIdList_descriptor;
+    }
+
+    @java.lang.Override
+    protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+        internalGetFieldAccessorTable() {
+      return context.ContextOuterClass.internal_static_context_ConnectionIdList_fieldAccessorTable
+          .ensureFieldAccessorsInitialized(
+              context.ContextOuterClass.ConnectionIdList.class, context.ContextOuterClass.ConnectionIdList.Builder.class);
+    }
+
+    public static final int CONNECTION_IDS_FIELD_NUMBER = 1;
+    private java.util.List<context.ContextOuterClass.ConnectionId> connectionIds_;
+    /**
+     * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+     */
+    @java.lang.Override
+    public java.util.List<context.ContextOuterClass.ConnectionId> getConnectionIdsList() {
+      return connectionIds_;
+    }
+    /**
+     * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+     */
+    @java.lang.Override
+    public java.util.List<? extends context.ContextOuterClass.ConnectionIdOrBuilder> 
+        getConnectionIdsOrBuilderList() {
+      return connectionIds_;
+    }
+    /**
+     * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+     */
+    @java.lang.Override
+    public int getConnectionIdsCount() {
+      return connectionIds_.size();
+    }
+    /**
+     * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+     */
+    @java.lang.Override
+    public context.ContextOuterClass.ConnectionId getConnectionIds(int index) {
+      return connectionIds_.get(index);
+    }
+    /**
+     * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+     */
+    @java.lang.Override
+    public context.ContextOuterClass.ConnectionIdOrBuilder getConnectionIdsOrBuilder(
+        int index) {
+      return connectionIds_.get(index);
+    }
+
+    private byte memoizedIsInitialized = -1;
+    @java.lang.Override
+    public final boolean isInitialized() {
+      byte isInitialized = memoizedIsInitialized;
+      if (isInitialized == 1) return true;
+      if (isInitialized == 0) return false;
+
+      memoizedIsInitialized = 1;
+      return true;
+    }
+
+    @java.lang.Override
+    public void writeTo(com.google.protobuf.CodedOutputStream output)
+                        throws java.io.IOException {
+      for (int i = 0; i < connectionIds_.size(); i++) {
+        output.writeMessage(1, connectionIds_.get(i));
+      }
+      unknownFields.writeTo(output);
+    }
+
+    @java.lang.Override
+    public int getSerializedSize() {
+      int size = memoizedSize;
+      if (size != -1) return size;
+
+      size = 0;
+      for (int i = 0; i < connectionIds_.size(); i++) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeMessageSize(1, connectionIds_.get(i));
+      }
+      size += unknownFields.getSerializedSize();
+      memoizedSize = size;
+      return size;
+    }
+
+    @java.lang.Override
+    public boolean equals(final java.lang.Object obj) {
+      if (obj == this) {
+       return true;
+      }
+      if (!(obj instanceof context.ContextOuterClass.ConnectionIdList)) {
+        return super.equals(obj);
+      }
+      context.ContextOuterClass.ConnectionIdList other = (context.ContextOuterClass.ConnectionIdList) obj;
+
+      if (!getConnectionIdsList()
+          .equals(other.getConnectionIdsList())) return false;
+      if (!unknownFields.equals(other.unknownFields)) return false;
+      return true;
+    }
+
+    @java.lang.Override
+    public int hashCode() {
+      if (memoizedHashCode != 0) {
+        return memoizedHashCode;
+      }
+      int hash = 41;
+      hash = (19 * hash) + getDescriptor().hashCode();
+      if (getConnectionIdsCount() > 0) {
+        hash = (37 * hash) + CONNECTION_IDS_FIELD_NUMBER;
+        hash = (53 * hash) + getConnectionIdsList().hashCode();
+      }
+      hash = (29 * hash) + unknownFields.hashCode();
+      memoizedHashCode = hash;
+      return hash;
+    }
+
+    public static context.ContextOuterClass.ConnectionIdList parseFrom(
+        java.nio.ByteBuffer data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data);
+    }
+    public static context.ContextOuterClass.ConnectionIdList parseFrom(
+        java.nio.ByteBuffer data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data, extensionRegistry);
+    }
+    public static context.ContextOuterClass.ConnectionIdList parseFrom(
+        com.google.protobuf.ByteString data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data);
+    }
+    public static context.ContextOuterClass.ConnectionIdList parseFrom(
+        com.google.protobuf.ByteString data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data, extensionRegistry);
+    }
+    public static context.ContextOuterClass.ConnectionIdList parseFrom(byte[] data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data);
+    }
+    public static context.ContextOuterClass.ConnectionIdList parseFrom(
+        byte[] data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data, extensionRegistry);
+    }
+    public static context.ContextOuterClass.ConnectionIdList parseFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      return com.google.protobuf.GeneratedMessageV3
+          .parseWithIOException(PARSER, input);
+    }
+    public static context.ContextOuterClass.ConnectionIdList parseFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return com.google.protobuf.GeneratedMessageV3
+          .parseWithIOException(PARSER, input, extensionRegistry);
+    }
+    public static context.ContextOuterClass.ConnectionIdList parseDelimitedFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      return com.google.protobuf.GeneratedMessageV3
+          .parseDelimitedWithIOException(PARSER, input);
+    }
+    public static context.ContextOuterClass.ConnectionIdList parseDelimitedFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return com.google.protobuf.GeneratedMessageV3
+          .parseDelimitedWithIOException(PARSER, input, extensionRegistry);
+    }
+    public static context.ContextOuterClass.ConnectionIdList parseFrom(
+        com.google.protobuf.CodedInputStream input)
+        throws java.io.IOException {
+      return com.google.protobuf.GeneratedMessageV3
+          .parseWithIOException(PARSER, input);
+    }
+    public static context.ContextOuterClass.ConnectionIdList parseFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return com.google.protobuf.GeneratedMessageV3
+          .parseWithIOException(PARSER, input, extensionRegistry);
+    }
+
+    @java.lang.Override
+    public Builder newBuilderForType() { return newBuilder(); }
+    public static Builder newBuilder() {
+      return DEFAULT_INSTANCE.toBuilder();
+    }
+    public static Builder newBuilder(context.ContextOuterClass.ConnectionIdList prototype) {
+      return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
+    }
+    @java.lang.Override
+    public Builder toBuilder() {
+      return this == DEFAULT_INSTANCE
+          ? new Builder() : new Builder().mergeFrom(this);
+    }
+
+    @java.lang.Override
+    protected Builder newBuilderForType(
+        com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+      Builder builder = new Builder(parent);
+      return builder;
+    }
+    /**
+     * Protobuf type {@code context.ConnectionIdList}
+     */
+    public static final class Builder extends
+        com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
+        // @@protoc_insertion_point(builder_implements:context.ConnectionIdList)
+        context.ContextOuterClass.ConnectionIdListOrBuilder {
+      public static final com.google.protobuf.Descriptors.Descriptor
+          getDescriptor() {
+        return context.ContextOuterClass.internal_static_context_ConnectionIdList_descriptor;
+      }
+
+      @java.lang.Override
+      protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+          internalGetFieldAccessorTable() {
+        return context.ContextOuterClass.internal_static_context_ConnectionIdList_fieldAccessorTable
+            .ensureFieldAccessorsInitialized(
+                context.ContextOuterClass.ConnectionIdList.class, context.ContextOuterClass.ConnectionIdList.Builder.class);
+      }
+
+      // Construct using context.ContextOuterClass.ConnectionIdList.newBuilder()
+      private Builder() {
+        maybeForceBuilderInitialization();
+      }
+
+      private Builder(
+          com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+        super(parent);
+        maybeForceBuilderInitialization();
+      }
+      private void maybeForceBuilderInitialization() {
+        if (com.google.protobuf.GeneratedMessageV3
+                .alwaysUseFieldBuilders) {
+          getConnectionIdsFieldBuilder();
+        }
+      }
+      @java.lang.Override
+      public Builder clear() {
+        super.clear();
+        if (connectionIdsBuilder_ == null) {
+          connectionIds_ = java.util.Collections.emptyList();
+          bitField0_ = (bitField0_ & ~0x00000001);
+        } else {
+          connectionIdsBuilder_.clear();
+        }
+        return this;
+      }
+
+      @java.lang.Override
+      public com.google.protobuf.Descriptors.Descriptor
+          getDescriptorForType() {
+        return context.ContextOuterClass.internal_static_context_ConnectionIdList_descriptor;
+      }
+
+      @java.lang.Override
+      public context.ContextOuterClass.ConnectionIdList getDefaultInstanceForType() {
+        return context.ContextOuterClass.ConnectionIdList.getDefaultInstance();
+      }
+
+      @java.lang.Override
+      public context.ContextOuterClass.ConnectionIdList build() {
+        context.ContextOuterClass.ConnectionIdList result = buildPartial();
+        if (!result.isInitialized()) {
+          throw newUninitializedMessageException(result);
+        }
+        return result;
+      }
+
+      @java.lang.Override
+      public context.ContextOuterClass.ConnectionIdList buildPartial() {
+        context.ContextOuterClass.ConnectionIdList result = new context.ContextOuterClass.ConnectionIdList(this);
+        int from_bitField0_ = bitField0_;
+        if (connectionIdsBuilder_ == null) {
+          if (((bitField0_ & 0x00000001) != 0)) {
+            connectionIds_ = java.util.Collections.unmodifiableList(connectionIds_);
+            bitField0_ = (bitField0_ & ~0x00000001);
+          }
+          result.connectionIds_ = connectionIds_;
+        } else {
+          result.connectionIds_ = connectionIdsBuilder_.build();
+        }
+        onBuilt();
+        return result;
+      }
+
+      @java.lang.Override
+      public Builder clone() {
+        return super.clone();
+      }
+      @java.lang.Override
+      public Builder setField(
+          com.google.protobuf.Descriptors.FieldDescriptor field,
+          java.lang.Object value) {
+        return super.setField(field, value);
+      }
+      @java.lang.Override
+      public Builder clearField(
+          com.google.protobuf.Descriptors.FieldDescriptor field) {
+        return super.clearField(field);
+      }
+      @java.lang.Override
+      public Builder clearOneof(
+          com.google.protobuf.Descriptors.OneofDescriptor oneof) {
+        return super.clearOneof(oneof);
+      }
+      @java.lang.Override
+      public Builder setRepeatedField(
+          com.google.protobuf.Descriptors.FieldDescriptor field,
+          int index, java.lang.Object value) {
+        return super.setRepeatedField(field, index, value);
+      }
+      @java.lang.Override
+      public Builder addRepeatedField(
+          com.google.protobuf.Descriptors.FieldDescriptor field,
+          java.lang.Object value) {
+        return super.addRepeatedField(field, value);
+      }
+      @java.lang.Override
+      public Builder mergeFrom(com.google.protobuf.Message other) {
+        if (other instanceof context.ContextOuterClass.ConnectionIdList) {
+          return mergeFrom((context.ContextOuterClass.ConnectionIdList)other);
+        } else {
+          super.mergeFrom(other);
+          return this;
+        }
+      }
+
+      public Builder mergeFrom(context.ContextOuterClass.ConnectionIdList other) {
+        if (other == context.ContextOuterClass.ConnectionIdList.getDefaultInstance()) return this;
+        if (connectionIdsBuilder_ == null) {
+          if (!other.connectionIds_.isEmpty()) {
+            if (connectionIds_.isEmpty()) {
+              connectionIds_ = other.connectionIds_;
+              bitField0_ = (bitField0_ & ~0x00000001);
+            } else {
+              ensureConnectionIdsIsMutable();
+              connectionIds_.addAll(other.connectionIds_);
+            }
+            onChanged();
+          }
+        } else {
+          if (!other.connectionIds_.isEmpty()) {
+            if (connectionIdsBuilder_.isEmpty()) {
+              connectionIdsBuilder_.dispose();
+              connectionIdsBuilder_ = null;
+              connectionIds_ = other.connectionIds_;
+              bitField0_ = (bitField0_ & ~0x00000001);
+              connectionIdsBuilder_ = 
+                com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders ?
+                   getConnectionIdsFieldBuilder() : null;
+            } else {
+              connectionIdsBuilder_.addAllMessages(other.connectionIds_);
+            }
+          }
+        }
+        this.mergeUnknownFields(other.unknownFields);
+        onChanged();
+        return this;
+      }
+
+      @java.lang.Override
+      public final boolean isInitialized() {
+        return true;
+      }
+
+      @java.lang.Override
+      public Builder mergeFrom(
+          com.google.protobuf.CodedInputStream input,
+          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+          throws java.io.IOException {
+        context.ContextOuterClass.ConnectionIdList parsedMessage = null;
+        try {
+          parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
+        } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+          parsedMessage = (context.ContextOuterClass.ConnectionIdList) e.getUnfinishedMessage();
+          throw e.unwrapIOException();
+        } finally {
+          if (parsedMessage != null) {
+            mergeFrom(parsedMessage);
+          }
+        }
+        return this;
+      }
+      private int bitField0_;
+
+      private java.util.List<context.ContextOuterClass.ConnectionId> connectionIds_ =
+        java.util.Collections.emptyList();
+      private void ensureConnectionIdsIsMutable() {
+        if (!((bitField0_ & 0x00000001) != 0)) {
+          connectionIds_ = new java.util.ArrayList<context.ContextOuterClass.ConnectionId>(connectionIds_);
+          bitField0_ |= 0x00000001;
+         }
+      }
+
+      private com.google.protobuf.RepeatedFieldBuilderV3<
+          context.ContextOuterClass.ConnectionId, context.ContextOuterClass.ConnectionId.Builder, context.ContextOuterClass.ConnectionIdOrBuilder> connectionIdsBuilder_;
+
+      /**
+       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+       */
+      public java.util.List<context.ContextOuterClass.ConnectionId> getConnectionIdsList() {
+        if (connectionIdsBuilder_ == null) {
+          return java.util.Collections.unmodifiableList(connectionIds_);
+        } else {
+          return connectionIdsBuilder_.getMessageList();
+        }
+      }
+      /**
+       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+       */
+      public int getConnectionIdsCount() {
+        if (connectionIdsBuilder_ == null) {
+          return connectionIds_.size();
+        } else {
+          return connectionIdsBuilder_.getCount();
+        }
+      }
+      /**
+       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+       */
+      public context.ContextOuterClass.ConnectionId getConnectionIds(int index) {
+        if (connectionIdsBuilder_ == null) {
+          return connectionIds_.get(index);
+        } else {
+          return connectionIdsBuilder_.getMessage(index);
+        }
+      }
+      /**
+       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+       */
+      public Builder setConnectionIds(
+          int index, context.ContextOuterClass.ConnectionId value) {
+        if (connectionIdsBuilder_ == null) {
+          if (value == null) {
+            throw new NullPointerException();
+          }
+          ensureConnectionIdsIsMutable();
+          connectionIds_.set(index, value);
+          onChanged();
+        } else {
+          connectionIdsBuilder_.setMessage(index, value);
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+       */
+      public Builder setConnectionIds(
+          int index, context.ContextOuterClass.ConnectionId.Builder builderForValue) {
+        if (connectionIdsBuilder_ == null) {
+          ensureConnectionIdsIsMutable();
+          connectionIds_.set(index, builderForValue.build());
+          onChanged();
+        } else {
+          connectionIdsBuilder_.setMessage(index, builderForValue.build());
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+       */
+      public Builder addConnectionIds(context.ContextOuterClass.ConnectionId value) {
+        if (connectionIdsBuilder_ == null) {
+          if (value == null) {
+            throw new NullPointerException();
+          }
+          ensureConnectionIdsIsMutable();
+          connectionIds_.add(value);
+          onChanged();
+        } else {
+          connectionIdsBuilder_.addMessage(value);
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+       */
+      public Builder addConnectionIds(
+          int index, context.ContextOuterClass.ConnectionId value) {
+        if (connectionIdsBuilder_ == null) {
+          if (value == null) {
+            throw new NullPointerException();
+          }
+          ensureConnectionIdsIsMutable();
+          connectionIds_.add(index, value);
+          onChanged();
+        } else {
+          connectionIdsBuilder_.addMessage(index, value);
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+       */
+      public Builder addConnectionIds(
+          context.ContextOuterClass.ConnectionId.Builder builderForValue) {
+        if (connectionIdsBuilder_ == null) {
+          ensureConnectionIdsIsMutable();
+          connectionIds_.add(builderForValue.build());
+          onChanged();
+        } else {
+          connectionIdsBuilder_.addMessage(builderForValue.build());
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+       */
+      public Builder addConnectionIds(
+          int index, context.ContextOuterClass.ConnectionId.Builder builderForValue) {
+        if (connectionIdsBuilder_ == null) {
+          ensureConnectionIdsIsMutable();
+          connectionIds_.add(index, builderForValue.build());
+          onChanged();
+        } else {
+          connectionIdsBuilder_.addMessage(index, builderForValue.build());
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+       */
+      public Builder addAllConnectionIds(
+          java.lang.Iterable<? extends context.ContextOuterClass.ConnectionId> values) {
+        if (connectionIdsBuilder_ == null) {
+          ensureConnectionIdsIsMutable();
+          com.google.protobuf.AbstractMessageLite.Builder.addAll(
+              values, connectionIds_);
+          onChanged();
+        } else {
+          connectionIdsBuilder_.addAllMessages(values);
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+       */
+      public Builder clearConnectionIds() {
+        if (connectionIdsBuilder_ == null) {
+          connectionIds_ = java.util.Collections.emptyList();
+          bitField0_ = (bitField0_ & ~0x00000001);
+          onChanged();
+        } else {
+          connectionIdsBuilder_.clear();
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+       */
+      public Builder removeConnectionIds(int index) {
+        if (connectionIdsBuilder_ == null) {
+          ensureConnectionIdsIsMutable();
+          connectionIds_.remove(index);
+          onChanged();
+        } else {
+          connectionIdsBuilder_.remove(index);
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+       */
+      public context.ContextOuterClass.ConnectionId.Builder getConnectionIdsBuilder(
+          int index) {
+        return getConnectionIdsFieldBuilder().getBuilder(index);
+      }
+      /**
+       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+       */
+      public context.ContextOuterClass.ConnectionIdOrBuilder getConnectionIdsOrBuilder(
+          int index) {
+        if (connectionIdsBuilder_ == null) {
+          return connectionIds_.get(index);  } else {
+          return connectionIdsBuilder_.getMessageOrBuilder(index);
+        }
+      }
+      /**
+       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+       */
+      public java.util.List<? extends context.ContextOuterClass.ConnectionIdOrBuilder> 
+           getConnectionIdsOrBuilderList() {
+        if (connectionIdsBuilder_ != null) {
+          return connectionIdsBuilder_.getMessageOrBuilderList();
+        } else {
+          return java.util.Collections.unmodifiableList(connectionIds_);
+        }
+      }
+      /**
+       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+       */
+      public context.ContextOuterClass.ConnectionId.Builder addConnectionIdsBuilder() {
+        return getConnectionIdsFieldBuilder().addBuilder(
+            context.ContextOuterClass.ConnectionId.getDefaultInstance());
+      }
+      /**
+       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+       */
+      public context.ContextOuterClass.ConnectionId.Builder addConnectionIdsBuilder(
+          int index) {
+        return getConnectionIdsFieldBuilder().addBuilder(
+            index, context.ContextOuterClass.ConnectionId.getDefaultInstance());
+      }
+      /**
+       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+       */
+      public java.util.List<context.ContextOuterClass.ConnectionId.Builder> 
+           getConnectionIdsBuilderList() {
+        return getConnectionIdsFieldBuilder().getBuilderList();
+      }
+      private com.google.protobuf.RepeatedFieldBuilderV3<
+          context.ContextOuterClass.ConnectionId, context.ContextOuterClass.ConnectionId.Builder, context.ContextOuterClass.ConnectionIdOrBuilder> 
+          getConnectionIdsFieldBuilder() {
+        if (connectionIdsBuilder_ == null) {
+          connectionIdsBuilder_ = new com.google.protobuf.RepeatedFieldBuilderV3<
+              context.ContextOuterClass.ConnectionId, context.ContextOuterClass.ConnectionId.Builder, context.ContextOuterClass.ConnectionIdOrBuilder>(
+                  connectionIds_,
+                  ((bitField0_ & 0x00000001) != 0),
+                  getParentForChildren(),
+                  isClean());
+          connectionIds_ = null;
+        }
+        return connectionIdsBuilder_;
+      }
+      @java.lang.Override
+      public final Builder setUnknownFields(
+          final com.google.protobuf.UnknownFieldSet unknownFields) {
+        return super.setUnknownFields(unknownFields);
+      }
+
+      @java.lang.Override
+      public final Builder mergeUnknownFields(
+          final com.google.protobuf.UnknownFieldSet unknownFields) {
+        return super.mergeUnknownFields(unknownFields);
+      }
+
+
+      // @@protoc_insertion_point(builder_scope:context.ConnectionIdList)
+    }
+
+    // @@protoc_insertion_point(class_scope:context.ConnectionIdList)
+    private static final context.ContextOuterClass.ConnectionIdList DEFAULT_INSTANCE;
+    static {
+      DEFAULT_INSTANCE = new context.ContextOuterClass.ConnectionIdList();
+    }
+
+    public static context.ContextOuterClass.ConnectionIdList getDefaultInstance() {
+      return DEFAULT_INSTANCE;
+    }
+
+    private static final com.google.protobuf.Parser<ConnectionIdList>
+        PARSER = new com.google.protobuf.AbstractParser<ConnectionIdList>() {
+      @java.lang.Override
+      public ConnectionIdList parsePartialFrom(
+          com.google.protobuf.CodedInputStream input,
+          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+          throws com.google.protobuf.InvalidProtocolBufferException {
+        return new ConnectionIdList(input, extensionRegistry);
+      }
+    };
+
+    public static com.google.protobuf.Parser<ConnectionIdList> parser() {
+      return PARSER;
+    }
+
+    @java.lang.Override
+    public com.google.protobuf.Parser<ConnectionIdList> getParserForType() {
+      return PARSER;
+    }
+
+    @java.lang.Override
+    public context.ContextOuterClass.ConnectionIdList getDefaultInstanceForType() {
+      return DEFAULT_INSTANCE;
+    }
+
+  }
+
+  public interface ConnectionListOrBuilder extends
+      // @@protoc_insertion_point(interface_extends:context.ConnectionList)
+      com.google.protobuf.MessageOrBuilder {
+
+    /**
+     * <code>repeated .context.Connection connections = 1;</code>
      */
-    com.google.protobuf.ByteString
-        getResourceValueBytes();
+    java.util.List<context.ContextOuterClass.Connection> 
+        getConnectionsList();
+    /**
+     * <code>repeated .context.Connection connections = 1;</code>
+     */
+    context.ContextOuterClass.Connection getConnections(int index);
+    /**
+     * <code>repeated .context.Connection connections = 1;</code>
+     */
+    int getConnectionsCount();
+    /**
+     * <code>repeated .context.Connection connections = 1;</code>
+     */
+    java.util.List<? extends context.ContextOuterClass.ConnectionOrBuilder> 
+        getConnectionsOrBuilderList();
+    /**
+     * <code>repeated .context.Connection connections = 1;</code>
+     */
+    context.ContextOuterClass.ConnectionOrBuilder getConnectionsOrBuilder(
+        int index);
   }
   /**
-   * Protobuf type {@code context.ConfigRule}
+   * Protobuf type {@code context.ConnectionList}
    */
-  public static final class ConfigRule extends
+  public static final class ConnectionList extends
       com.google.protobuf.GeneratedMessageV3 implements
-      // @@protoc_insertion_point(message_implements:context.ConfigRule)
-      ConfigRuleOrBuilder {
+      // @@protoc_insertion_point(message_implements:context.ConnectionList)
+      ConnectionListOrBuilder {
   private static final long serialVersionUID = 0L;
-    // Use ConfigRule.newBuilder() to construct.
-    private ConfigRule(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
+    // Use ConnectionList.newBuilder() to construct.
+    private ConnectionList(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
       super(builder);
     }
-    private ConfigRule() {
-      action_ = 0;
-      resourceKey_ = "";
-      resourceValue_ = "";
+    private ConnectionList() {
+      connections_ = java.util.Collections.emptyList();
     }
 
     @java.lang.Override
     @SuppressWarnings({"unused"})
     protected java.lang.Object newInstance(
         UnusedPrivateParameter unused) {
-      return new ConfigRule();
+      return new ConnectionList();
     }
 
     @java.lang.Override
@@ -29528,7 +30702,7 @@ public final class ContextOuterClass {
     getUnknownFields() {
       return this.unknownFields;
     }
-    private ConfigRule(
+    private ConnectionList(
         com.google.protobuf.CodedInputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
@@ -29536,6 +30710,7 @@ public final class ContextOuterClass {
       if (extensionRegistry == null) {
         throw new java.lang.NullPointerException();
       }
+      int mutable_bitField0_ = 0;
       com.google.protobuf.UnknownFieldSet.Builder unknownFields =
           com.google.protobuf.UnknownFieldSet.newBuilder();
       try {
@@ -29546,22 +30721,13 @@ public final class ContextOuterClass {
             case 0:
               done = true;
               break;
-            case 8: {
-              int rawValue = input.readEnum();
-
-              action_ = rawValue;
-              break;
-            }
-            case 18: {
-              java.lang.String s = input.readStringRequireUtf8();
-
-              resourceKey_ = s;
-              break;
-            }
-            case 26: {
-              java.lang.String s = input.readStringRequireUtf8();
-
-              resourceValue_ = s;
+            case 10: {
+              if (!((mutable_bitField0_ & 0x00000001) != 0)) {
+                connections_ = new java.util.ArrayList<context.ContextOuterClass.Connection>();
+                mutable_bitField0_ |= 0x00000001;
+              }
+              connections_.add(
+                  input.readMessage(context.ContextOuterClass.Connection.parser(), extensionRegistry));
               break;
             }
             default: {
@@ -29579,116 +30745,64 @@ public final class ContextOuterClass {
         throw new com.google.protobuf.InvalidProtocolBufferException(
             e).setUnfinishedMessage(this);
       } finally {
+        if (((mutable_bitField0_ & 0x00000001) != 0)) {
+          connections_ = java.util.Collections.unmodifiableList(connections_);
+        }
         this.unknownFields = unknownFields.build();
         makeExtensionsImmutable();
       }
     }
     public static final com.google.protobuf.Descriptors.Descriptor
         getDescriptor() {
-      return context.ContextOuterClass.internal_static_context_ConfigRule_descriptor;
+      return context.ContextOuterClass.internal_static_context_ConnectionList_descriptor;
     }
 
     @java.lang.Override
     protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
-        internalGetFieldAccessorTable() {
-      return context.ContextOuterClass.internal_static_context_ConfigRule_fieldAccessorTable
-          .ensureFieldAccessorsInitialized(
-              context.ContextOuterClass.ConfigRule.class, context.ContextOuterClass.ConfigRule.Builder.class);
-    }
-
-    public static final int ACTION_FIELD_NUMBER = 1;
-    private int action_;
-    /**
-     * <code>.context.ConfigActionEnum action = 1;</code>
-     * @return The enum numeric value on the wire for action.
-     */
-    @java.lang.Override public int getActionValue() {
-      return action_;
+        internalGetFieldAccessorTable() {
+      return context.ContextOuterClass.internal_static_context_ConnectionList_fieldAccessorTable
+          .ensureFieldAccessorsInitialized(
+              context.ContextOuterClass.ConnectionList.class, context.ContextOuterClass.ConnectionList.Builder.class);
     }
+
+    public static final int CONNECTIONS_FIELD_NUMBER = 1;
+    private java.util.List<context.ContextOuterClass.Connection> connections_;
     /**
-     * <code>.context.ConfigActionEnum action = 1;</code>
-     * @return The action.
+     * <code>repeated .context.Connection connections = 1;</code>
      */
-    @java.lang.Override public context.ContextOuterClass.ConfigActionEnum getAction() {
-      @SuppressWarnings("deprecation")
-      context.ContextOuterClass.ConfigActionEnum result = context.ContextOuterClass.ConfigActionEnum.valueOf(action_);
-      return result == null ? context.ContextOuterClass.ConfigActionEnum.UNRECOGNIZED : result;
+    @java.lang.Override
+    public java.util.List<context.ContextOuterClass.Connection> getConnectionsList() {
+      return connections_;
     }
-
-    public static final int RESOURCE_KEY_FIELD_NUMBER = 2;
-    private volatile java.lang.Object resourceKey_;
     /**
-     * <code>string resource_key = 2;</code>
-     * @return The resourceKey.
+     * <code>repeated .context.Connection connections = 1;</code>
      */
     @java.lang.Override
-    public java.lang.String getResourceKey() {
-      java.lang.Object ref = resourceKey_;
-      if (ref instanceof java.lang.String) {
-        return (java.lang.String) ref;
-      } else {
-        com.google.protobuf.ByteString bs = 
-            (com.google.protobuf.ByteString) ref;
-        java.lang.String s = bs.toStringUtf8();
-        resourceKey_ = s;
-        return s;
-      }
+    public java.util.List<? extends context.ContextOuterClass.ConnectionOrBuilder> 
+        getConnectionsOrBuilderList() {
+      return connections_;
     }
     /**
-     * <code>string resource_key = 2;</code>
-     * @return The bytes for resourceKey.
+     * <code>repeated .context.Connection connections = 1;</code>
      */
     @java.lang.Override
-    public com.google.protobuf.ByteString
-        getResourceKeyBytes() {
-      java.lang.Object ref = resourceKey_;
-      if (ref instanceof java.lang.String) {
-        com.google.protobuf.ByteString b = 
-            com.google.protobuf.ByteString.copyFromUtf8(
-                (java.lang.String) ref);
-        resourceKey_ = b;
-        return b;
-      } else {
-        return (com.google.protobuf.ByteString) ref;
-      }
+    public int getConnectionsCount() {
+      return connections_.size();
     }
-
-    public static final int RESOURCE_VALUE_FIELD_NUMBER = 3;
-    private volatile java.lang.Object resourceValue_;
     /**
-     * <code>string resource_value = 3;</code>
-     * @return The resourceValue.
+     * <code>repeated .context.Connection connections = 1;</code>
      */
     @java.lang.Override
-    public java.lang.String getResourceValue() {
-      java.lang.Object ref = resourceValue_;
-      if (ref instanceof java.lang.String) {
-        return (java.lang.String) ref;
-      } else {
-        com.google.protobuf.ByteString bs = 
-            (com.google.protobuf.ByteString) ref;
-        java.lang.String s = bs.toStringUtf8();
-        resourceValue_ = s;
-        return s;
-      }
+    public context.ContextOuterClass.Connection getConnections(int index) {
+      return connections_.get(index);
     }
     /**
-     * <code>string resource_value = 3;</code>
-     * @return The bytes for resourceValue.
+     * <code>repeated .context.Connection connections = 1;</code>
      */
     @java.lang.Override
-    public com.google.protobuf.ByteString
-        getResourceValueBytes() {
-      java.lang.Object ref = resourceValue_;
-      if (ref instanceof java.lang.String) {
-        com.google.protobuf.ByteString b = 
-            com.google.protobuf.ByteString.copyFromUtf8(
-                (java.lang.String) ref);
-        resourceValue_ = b;
-        return b;
-      } else {
-        return (com.google.protobuf.ByteString) ref;
-      }
+    public context.ContextOuterClass.ConnectionOrBuilder getConnectionsOrBuilder(
+        int index) {
+      return connections_.get(index);
     }
 
     private byte memoizedIsInitialized = -1;
@@ -29705,14 +30819,8 @@ public final class ContextOuterClass {
     @java.lang.Override
     public void writeTo(com.google.protobuf.CodedOutputStream output)
                         throws java.io.IOException {
-      if (action_ != context.ContextOuterClass.ConfigActionEnum.CONFIGACTION_UNDEFINED.getNumber()) {
-        output.writeEnum(1, action_);
-      }
-      if (!getResourceKeyBytes().isEmpty()) {
-        com.google.protobuf.GeneratedMessageV3.writeString(output, 2, resourceKey_);
-      }
-      if (!getResourceValueBytes().isEmpty()) {
-        com.google.protobuf.GeneratedMessageV3.writeString(output, 3, resourceValue_);
+      for (int i = 0; i < connections_.size(); i++) {
+        output.writeMessage(1, connections_.get(i));
       }
       unknownFields.writeTo(output);
     }
@@ -29723,15 +30831,9 @@ public final class ContextOuterClass {
       if (size != -1) return size;
 
       size = 0;
-      if (action_ != context.ContextOuterClass.ConfigActionEnum.CONFIGACTION_UNDEFINED.getNumber()) {
+      for (int i = 0; i < connections_.size(); i++) {
         size += com.google.protobuf.CodedOutputStream
-          .computeEnumSize(1, action_);
-      }
-      if (!getResourceKeyBytes().isEmpty()) {
-        size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, resourceKey_);
-      }
-      if (!getResourceValueBytes().isEmpty()) {
-        size += com.google.protobuf.GeneratedMessageV3.computeStringSize(3, resourceValue_);
+          .computeMessageSize(1, connections_.get(i));
       }
       size += unknownFields.getSerializedSize();
       memoizedSize = size;
@@ -29743,16 +30845,13 @@ public final class ContextOuterClass {
       if (obj == this) {
        return true;
       }
-      if (!(obj instanceof context.ContextOuterClass.ConfigRule)) {
+      if (!(obj instanceof context.ContextOuterClass.ConnectionList)) {
         return super.equals(obj);
       }
-      context.ContextOuterClass.ConfigRule other = (context.ContextOuterClass.ConfigRule) obj;
+      context.ContextOuterClass.ConnectionList other = (context.ContextOuterClass.ConnectionList) obj;
 
-      if (action_ != other.action_) return false;
-      if (!getResourceKey()
-          .equals(other.getResourceKey())) return false;
-      if (!getResourceValue()
-          .equals(other.getResourceValue())) return false;
+      if (!getConnectionsList()
+          .equals(other.getConnectionsList())) return false;
       if (!unknownFields.equals(other.unknownFields)) return false;
       return true;
     }
@@ -29764,80 +30863,78 @@ public final class ContextOuterClass {
       }
       int hash = 41;
       hash = (19 * hash) + getDescriptor().hashCode();
-      hash = (37 * hash) + ACTION_FIELD_NUMBER;
-      hash = (53 * hash) + action_;
-      hash = (37 * hash) + RESOURCE_KEY_FIELD_NUMBER;
-      hash = (53 * hash) + getResourceKey().hashCode();
-      hash = (37 * hash) + RESOURCE_VALUE_FIELD_NUMBER;
-      hash = (53 * hash) + getResourceValue().hashCode();
+      if (getConnectionsCount() > 0) {
+        hash = (37 * hash) + CONNECTIONS_FIELD_NUMBER;
+        hash = (53 * hash) + getConnectionsList().hashCode();
+      }
       hash = (29 * hash) + unknownFields.hashCode();
       memoizedHashCode = hash;
       return hash;
     }
 
-    public static context.ContextOuterClass.ConfigRule parseFrom(
+    public static context.ContextOuterClass.ConnectionList parseFrom(
         java.nio.ByteBuffer data)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data);
     }
-    public static context.ContextOuterClass.ConfigRule parseFrom(
+    public static context.ContextOuterClass.ConnectionList parseFrom(
         java.nio.ByteBuffer data,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data, extensionRegistry);
     }
-    public static context.ContextOuterClass.ConfigRule parseFrom(
+    public static context.ContextOuterClass.ConnectionList parseFrom(
         com.google.protobuf.ByteString data)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data);
     }
-    public static context.ContextOuterClass.ConfigRule parseFrom(
+    public static context.ContextOuterClass.ConnectionList parseFrom(
         com.google.protobuf.ByteString data,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data, extensionRegistry);
     }
-    public static context.ContextOuterClass.ConfigRule parseFrom(byte[] data)
+    public static context.ContextOuterClass.ConnectionList parseFrom(byte[] data)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data);
     }
-    public static context.ContextOuterClass.ConfigRule parseFrom(
+    public static context.ContextOuterClass.ConnectionList parseFrom(
         byte[] data,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data, extensionRegistry);
     }
-    public static context.ContextOuterClass.ConfigRule parseFrom(java.io.InputStream input)
+    public static context.ContextOuterClass.ConnectionList parseFrom(java.io.InputStream input)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseWithIOException(PARSER, input);
     }
-    public static context.ContextOuterClass.ConfigRule parseFrom(
+    public static context.ContextOuterClass.ConnectionList parseFrom(
         java.io.InputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseWithIOException(PARSER, input, extensionRegistry);
     }
-    public static context.ContextOuterClass.ConfigRule parseDelimitedFrom(java.io.InputStream input)
+    public static context.ContextOuterClass.ConnectionList parseDelimitedFrom(java.io.InputStream input)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseDelimitedWithIOException(PARSER, input);
     }
-    public static context.ContextOuterClass.ConfigRule parseDelimitedFrom(
+    public static context.ContextOuterClass.ConnectionList parseDelimitedFrom(
         java.io.InputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseDelimitedWithIOException(PARSER, input, extensionRegistry);
     }
-    public static context.ContextOuterClass.ConfigRule parseFrom(
+    public static context.ContextOuterClass.ConnectionList parseFrom(
         com.google.protobuf.CodedInputStream input)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseWithIOException(PARSER, input);
     }
-    public static context.ContextOuterClass.ConfigRule parseFrom(
+    public static context.ContextOuterClass.ConnectionList parseFrom(
         com.google.protobuf.CodedInputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws java.io.IOException {
@@ -29850,7 +30947,7 @@ public final class ContextOuterClass {
     public static Builder newBuilder() {
       return DEFAULT_INSTANCE.toBuilder();
     }
-    public static Builder newBuilder(context.ContextOuterClass.ConfigRule prototype) {
+    public static Builder newBuilder(context.ContextOuterClass.ConnectionList prototype) {
       return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
     }
     @java.lang.Override
@@ -29866,26 +30963,26 @@ public final class ContextOuterClass {
       return builder;
     }
     /**
-     * Protobuf type {@code context.ConfigRule}
+     * Protobuf type {@code context.ConnectionList}
      */
     public static final class Builder extends
         com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
-        // @@protoc_insertion_point(builder_implements:context.ConfigRule)
-        context.ContextOuterClass.ConfigRuleOrBuilder {
+        // @@protoc_insertion_point(builder_implements:context.ConnectionList)
+        context.ContextOuterClass.ConnectionListOrBuilder {
       public static final com.google.protobuf.Descriptors.Descriptor
           getDescriptor() {
-        return context.ContextOuterClass.internal_static_context_ConfigRule_descriptor;
+        return context.ContextOuterClass.internal_static_context_ConnectionList_descriptor;
       }
 
       @java.lang.Override
       protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
           internalGetFieldAccessorTable() {
-        return context.ContextOuterClass.internal_static_context_ConfigRule_fieldAccessorTable
+        return context.ContextOuterClass.internal_static_context_ConnectionList_fieldAccessorTable
             .ensureFieldAccessorsInitialized(
-                context.ContextOuterClass.ConfigRule.class, context.ContextOuterClass.ConfigRule.Builder.class);
+                context.ContextOuterClass.ConnectionList.class, context.ContextOuterClass.ConnectionList.Builder.class);
       }
 
-      // Construct using context.ContextOuterClass.ConfigRule.newBuilder()
+      // Construct using context.ContextOuterClass.ConnectionList.newBuilder()
       private Builder() {
         maybeForceBuilderInitialization();
       }
@@ -29898,34 +30995,35 @@ public final class ContextOuterClass {
       private void maybeForceBuilderInitialization() {
         if (com.google.protobuf.GeneratedMessageV3
                 .alwaysUseFieldBuilders) {
+          getConnectionsFieldBuilder();
         }
       }
       @java.lang.Override
       public Builder clear() {
         super.clear();
-        action_ = 0;
-
-        resourceKey_ = "";
-
-        resourceValue_ = "";
-
+        if (connectionsBuilder_ == null) {
+          connections_ = java.util.Collections.emptyList();
+          bitField0_ = (bitField0_ & ~0x00000001);
+        } else {
+          connectionsBuilder_.clear();
+        }
         return this;
       }
 
       @java.lang.Override
       public com.google.protobuf.Descriptors.Descriptor
           getDescriptorForType() {
-        return context.ContextOuterClass.internal_static_context_ConfigRule_descriptor;
+        return context.ContextOuterClass.internal_static_context_ConnectionList_descriptor;
       }
 
       @java.lang.Override
-      public context.ContextOuterClass.ConfigRule getDefaultInstanceForType() {
-        return context.ContextOuterClass.ConfigRule.getDefaultInstance();
+      public context.ContextOuterClass.ConnectionList getDefaultInstanceForType() {
+        return context.ContextOuterClass.ConnectionList.getDefaultInstance();
       }
 
       @java.lang.Override
-      public context.ContextOuterClass.ConfigRule build() {
-        context.ContextOuterClass.ConfigRule result = buildPartial();
+      public context.ContextOuterClass.ConnectionList build() {
+        context.ContextOuterClass.ConnectionList result = buildPartial();
         if (!result.isInitialized()) {
           throw newUninitializedMessageException(result);
         }
@@ -29933,11 +31031,18 @@ public final class ContextOuterClass {
       }
 
       @java.lang.Override
-      public context.ContextOuterClass.ConfigRule buildPartial() {
-        context.ContextOuterClass.ConfigRule result = new context.ContextOuterClass.ConfigRule(this);
-        result.action_ = action_;
-        result.resourceKey_ = resourceKey_;
-        result.resourceValue_ = resourceValue_;
+      public context.ContextOuterClass.ConnectionList buildPartial() {
+        context.ContextOuterClass.ConnectionList result = new context.ContextOuterClass.ConnectionList(this);
+        int from_bitField0_ = bitField0_;
+        if (connectionsBuilder_ == null) {
+          if (((bitField0_ & 0x00000001) != 0)) {
+            connections_ = java.util.Collections.unmodifiableList(connections_);
+            bitField0_ = (bitField0_ & ~0x00000001);
+          }
+          result.connections_ = connections_;
+        } else {
+          result.connections_ = connectionsBuilder_.build();
+        }
         onBuilt();
         return result;
       }
@@ -29976,26 +31081,41 @@ public final class ContextOuterClass {
       }
       @java.lang.Override
       public Builder mergeFrom(com.google.protobuf.Message other) {
-        if (other instanceof context.ContextOuterClass.ConfigRule) {
-          return mergeFrom((context.ContextOuterClass.ConfigRule)other);
+        if (other instanceof context.ContextOuterClass.ConnectionList) {
+          return mergeFrom((context.ContextOuterClass.ConnectionList)other);
         } else {
           super.mergeFrom(other);
           return this;
         }
       }
 
-      public Builder mergeFrom(context.ContextOuterClass.ConfigRule other) {
-        if (other == context.ContextOuterClass.ConfigRule.getDefaultInstance()) return this;
-        if (other.action_ != 0) {
-          setActionValue(other.getActionValue());
-        }
-        if (!other.getResourceKey().isEmpty()) {
-          resourceKey_ = other.resourceKey_;
-          onChanged();
-        }
-        if (!other.getResourceValue().isEmpty()) {
-          resourceValue_ = other.resourceValue_;
-          onChanged();
+      public Builder mergeFrom(context.ContextOuterClass.ConnectionList other) {
+        if (other == context.ContextOuterClass.ConnectionList.getDefaultInstance()) return this;
+        if (connectionsBuilder_ == null) {
+          if (!other.connections_.isEmpty()) {
+            if (connections_.isEmpty()) {
+              connections_ = other.connections_;
+              bitField0_ = (bitField0_ & ~0x00000001);
+            } else {
+              ensureConnectionsIsMutable();
+              connections_.addAll(other.connections_);
+            }
+            onChanged();
+          }
+        } else {
+          if (!other.connections_.isEmpty()) {
+            if (connectionsBuilder_.isEmpty()) {
+              connectionsBuilder_.dispose();
+              connectionsBuilder_ = null;
+              connections_ = other.connections_;
+              bitField0_ = (bitField0_ & ~0x00000001);
+              connectionsBuilder_ = 
+                com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders ?
+                   getConnectionsFieldBuilder() : null;
+            } else {
+              connectionsBuilder_.addAllMessages(other.connections_);
+            }
+          }
         }
         this.mergeUnknownFields(other.unknownFields);
         onChanged();
@@ -30012,11 +31132,11 @@ public final class ContextOuterClass {
           com.google.protobuf.CodedInputStream input,
           com.google.protobuf.ExtensionRegistryLite extensionRegistry)
           throws java.io.IOException {
-        context.ContextOuterClass.ConfigRule parsedMessage = null;
+        context.ContextOuterClass.ConnectionList parsedMessage = null;
         try {
           parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
         } catch (com.google.protobuf.InvalidProtocolBufferException e) {
-          parsedMessage = (context.ContextOuterClass.ConfigRule) e.getUnfinishedMessage();
+          parsedMessage = (context.ContextOuterClass.ConnectionList) e.getUnfinishedMessage();
           throw e.unwrapIOException();
         } finally {
           if (parsedMessage != null) {
@@ -30025,211 +31145,246 @@ public final class ContextOuterClass {
         }
         return this;
       }
+      private int bitField0_;
+
+      private java.util.List<context.ContextOuterClass.Connection> connections_ =
+        java.util.Collections.emptyList();
+      private void ensureConnectionsIsMutable() {
+        if (!((bitField0_ & 0x00000001) != 0)) {
+          connections_ = new java.util.ArrayList<context.ContextOuterClass.Connection>(connections_);
+          bitField0_ |= 0x00000001;
+         }
+      }
+
+      private com.google.protobuf.RepeatedFieldBuilderV3<
+          context.ContextOuterClass.Connection, context.ContextOuterClass.Connection.Builder, context.ContextOuterClass.ConnectionOrBuilder> connectionsBuilder_;
 
-      private int action_ = 0;
       /**
-       * <code>.context.ConfigActionEnum action = 1;</code>
-       * @return The enum numeric value on the wire for action.
+       * <code>repeated .context.Connection connections = 1;</code>
        */
-      @java.lang.Override public int getActionValue() {
-        return action_;
+      public java.util.List<context.ContextOuterClass.Connection> getConnectionsList() {
+        if (connectionsBuilder_ == null) {
+          return java.util.Collections.unmodifiableList(connections_);
+        } else {
+          return connectionsBuilder_.getMessageList();
+        }
       }
       /**
-       * <code>.context.ConfigActionEnum action = 1;</code>
-       * @param value The enum numeric value on the wire for action to set.
-       * @return This builder for chaining.
+       * <code>repeated .context.Connection connections = 1;</code>
        */
-      public Builder setActionValue(int value) {
-        
-        action_ = value;
-        onChanged();
-        return this;
+      public int getConnectionsCount() {
+        if (connectionsBuilder_ == null) {
+          return connections_.size();
+        } else {
+          return connectionsBuilder_.getCount();
+        }
       }
       /**
-       * <code>.context.ConfigActionEnum action = 1;</code>
-       * @return The action.
+       * <code>repeated .context.Connection connections = 1;</code>
        */
-      @java.lang.Override
-      public context.ContextOuterClass.ConfigActionEnum getAction() {
-        @SuppressWarnings("deprecation")
-        context.ContextOuterClass.ConfigActionEnum result = context.ContextOuterClass.ConfigActionEnum.valueOf(action_);
-        return result == null ? context.ContextOuterClass.ConfigActionEnum.UNRECOGNIZED : result;
+      public context.ContextOuterClass.Connection getConnections(int index) {
+        if (connectionsBuilder_ == null) {
+          return connections_.get(index);
+        } else {
+          return connectionsBuilder_.getMessage(index);
+        }
       }
       /**
-       * <code>.context.ConfigActionEnum action = 1;</code>
-       * @param value The action to set.
-       * @return This builder for chaining.
+       * <code>repeated .context.Connection connections = 1;</code>
        */
-      public Builder setAction(context.ContextOuterClass.ConfigActionEnum value) {
-        if (value == null) {
-          throw new NullPointerException();
+      public Builder setConnections(
+          int index, context.ContextOuterClass.Connection value) {
+        if (connectionsBuilder_ == null) {
+          if (value == null) {
+            throw new NullPointerException();
+          }
+          ensureConnectionsIsMutable();
+          connections_.set(index, value);
+          onChanged();
+        } else {
+          connectionsBuilder_.setMessage(index, value);
         }
-        
-        action_ = value.getNumber();
-        onChanged();
         return this;
       }
       /**
-       * <code>.context.ConfigActionEnum action = 1;</code>
-       * @return This builder for chaining.
+       * <code>repeated .context.Connection connections = 1;</code>
        */
-      public Builder clearAction() {
-        
-        action_ = 0;
-        onChanged();
+      public Builder setConnections(
+          int index, context.ContextOuterClass.Connection.Builder builderForValue) {
+        if (connectionsBuilder_ == null) {
+          ensureConnectionsIsMutable();
+          connections_.set(index, builderForValue.build());
+          onChanged();
+        } else {
+          connectionsBuilder_.setMessage(index, builderForValue.build());
+        }
         return this;
       }
-
-      private java.lang.Object resourceKey_ = "";
       /**
-       * <code>string resource_key = 2;</code>
-       * @return The resourceKey.
+       * <code>repeated .context.Connection connections = 1;</code>
        */
-      public java.lang.String getResourceKey() {
-        java.lang.Object ref = resourceKey_;
-        if (!(ref instanceof java.lang.String)) {
-          com.google.protobuf.ByteString bs =
-              (com.google.protobuf.ByteString) ref;
-          java.lang.String s = bs.toStringUtf8();
-          resourceKey_ = s;
-          return s;
+      public Builder addConnections(context.ContextOuterClass.Connection value) {
+        if (connectionsBuilder_ == null) {
+          if (value == null) {
+            throw new NullPointerException();
+          }
+          ensureConnectionsIsMutable();
+          connections_.add(value);
+          onChanged();
         } else {
-          return (java.lang.String) ref;
+          connectionsBuilder_.addMessage(value);
         }
+        return this;
       }
       /**
-       * <code>string resource_key = 2;</code>
-       * @return The bytes for resourceKey.
+       * <code>repeated .context.Connection connections = 1;</code>
        */
-      public com.google.protobuf.ByteString
-          getResourceKeyBytes() {
-        java.lang.Object ref = resourceKey_;
-        if (ref instanceof String) {
-          com.google.protobuf.ByteString b = 
-              com.google.protobuf.ByteString.copyFromUtf8(
-                  (java.lang.String) ref);
-          resourceKey_ = b;
-          return b;
+      public Builder addConnections(
+          int index, context.ContextOuterClass.Connection value) {
+        if (connectionsBuilder_ == null) {
+          if (value == null) {
+            throw new NullPointerException();
+          }
+          ensureConnectionsIsMutable();
+          connections_.add(index, value);
+          onChanged();
         } else {
-          return (com.google.protobuf.ByteString) ref;
+          connectionsBuilder_.addMessage(index, value);
         }
+        return this;
       }
       /**
-       * <code>string resource_key = 2;</code>
-       * @param value The resourceKey to set.
-       * @return This builder for chaining.
+       * <code>repeated .context.Connection connections = 1;</code>
        */
-      public Builder setResourceKey(
-          java.lang.String value) {
-        if (value == null) {
-    throw new NullPointerException();
-  }
-  
-        resourceKey_ = value;
-        onChanged();
+      public Builder addConnections(
+          context.ContextOuterClass.Connection.Builder builderForValue) {
+        if (connectionsBuilder_ == null) {
+          ensureConnectionsIsMutable();
+          connections_.add(builderForValue.build());
+          onChanged();
+        } else {
+          connectionsBuilder_.addMessage(builderForValue.build());
+        }
         return this;
       }
       /**
-       * <code>string resource_key = 2;</code>
-       * @return This builder for chaining.
+       * <code>repeated .context.Connection connections = 1;</code>
        */
-      public Builder clearResourceKey() {
-        
-        resourceKey_ = getDefaultInstance().getResourceKey();
-        onChanged();
+      public Builder addConnections(
+          int index, context.ContextOuterClass.Connection.Builder builderForValue) {
+        if (connectionsBuilder_ == null) {
+          ensureConnectionsIsMutable();
+          connections_.add(index, builderForValue.build());
+          onChanged();
+        } else {
+          connectionsBuilder_.addMessage(index, builderForValue.build());
+        }
         return this;
       }
       /**
-       * <code>string resource_key = 2;</code>
-       * @param value The bytes for resourceKey to set.
-       * @return This builder for chaining.
+       * <code>repeated .context.Connection connections = 1;</code>
        */
-      public Builder setResourceKeyBytes(
-          com.google.protobuf.ByteString value) {
-        if (value == null) {
-    throw new NullPointerException();
-  }
-  checkByteStringIsUtf8(value);
-        
-        resourceKey_ = value;
-        onChanged();
+      public Builder addAllConnections(
+          java.lang.Iterable<? extends context.ContextOuterClass.Connection> values) {
+        if (connectionsBuilder_ == null) {
+          ensureConnectionsIsMutable();
+          com.google.protobuf.AbstractMessageLite.Builder.addAll(
+              values, connections_);
+          onChanged();
+        } else {
+          connectionsBuilder_.addAllMessages(values);
+        }
         return this;
       }
-
-      private java.lang.Object resourceValue_ = "";
       /**
-       * <code>string resource_value = 3;</code>
-       * @return The resourceValue.
+       * <code>repeated .context.Connection connections = 1;</code>
        */
-      public java.lang.String getResourceValue() {
-        java.lang.Object ref = resourceValue_;
-        if (!(ref instanceof java.lang.String)) {
-          com.google.protobuf.ByteString bs =
-              (com.google.protobuf.ByteString) ref;
-          java.lang.String s = bs.toStringUtf8();
-          resourceValue_ = s;
-          return s;
+      public Builder clearConnections() {
+        if (connectionsBuilder_ == null) {
+          connections_ = java.util.Collections.emptyList();
+          bitField0_ = (bitField0_ & ~0x00000001);
+          onChanged();
         } else {
-          return (java.lang.String) ref;
+          connectionsBuilder_.clear();
         }
+        return this;
       }
       /**
-       * <code>string resource_value = 3;</code>
-       * @return The bytes for resourceValue.
+       * <code>repeated .context.Connection connections = 1;</code>
+       */
+      public Builder removeConnections(int index) {
+        if (connectionsBuilder_ == null) {
+          ensureConnectionsIsMutable();
+          connections_.remove(index);
+          onChanged();
+        } else {
+          connectionsBuilder_.remove(index);
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .context.Connection connections = 1;</code>
+       */
+      public context.ContextOuterClass.Connection.Builder getConnectionsBuilder(
+          int index) {
+        return getConnectionsFieldBuilder().getBuilder(index);
+      }
+      /**
+       * <code>repeated .context.Connection connections = 1;</code>
+       */
+      public context.ContextOuterClass.ConnectionOrBuilder getConnectionsOrBuilder(
+          int index) {
+        if (connectionsBuilder_ == null) {
+          return connections_.get(index);  } else {
+          return connectionsBuilder_.getMessageOrBuilder(index);
+        }
+      }
+      /**
+       * <code>repeated .context.Connection connections = 1;</code>
        */
-      public com.google.protobuf.ByteString
-          getResourceValueBytes() {
-        java.lang.Object ref = resourceValue_;
-        if (ref instanceof String) {
-          com.google.protobuf.ByteString b = 
-              com.google.protobuf.ByteString.copyFromUtf8(
-                  (java.lang.String) ref);
-          resourceValue_ = b;
-          return b;
+      public java.util.List<? extends context.ContextOuterClass.ConnectionOrBuilder> 
+           getConnectionsOrBuilderList() {
+        if (connectionsBuilder_ != null) {
+          return connectionsBuilder_.getMessageOrBuilderList();
         } else {
-          return (com.google.protobuf.ByteString) ref;
+          return java.util.Collections.unmodifiableList(connections_);
         }
       }
       /**
-       * <code>string resource_value = 3;</code>
-       * @param value The resourceValue to set.
-       * @return This builder for chaining.
+       * <code>repeated .context.Connection connections = 1;</code>
        */
-      public Builder setResourceValue(
-          java.lang.String value) {
-        if (value == null) {
-    throw new NullPointerException();
-  }
-  
-        resourceValue_ = value;
-        onChanged();
-        return this;
+      public context.ContextOuterClass.Connection.Builder addConnectionsBuilder() {
+        return getConnectionsFieldBuilder().addBuilder(
+            context.ContextOuterClass.Connection.getDefaultInstance());
       }
       /**
-       * <code>string resource_value = 3;</code>
-       * @return This builder for chaining.
+       * <code>repeated .context.Connection connections = 1;</code>
        */
-      public Builder clearResourceValue() {
-        
-        resourceValue_ = getDefaultInstance().getResourceValue();
-        onChanged();
-        return this;
+      public context.ContextOuterClass.Connection.Builder addConnectionsBuilder(
+          int index) {
+        return getConnectionsFieldBuilder().addBuilder(
+            index, context.ContextOuterClass.Connection.getDefaultInstance());
       }
       /**
-       * <code>string resource_value = 3;</code>
-       * @param value The bytes for resourceValue to set.
-       * @return This builder for chaining.
+       * <code>repeated .context.Connection connections = 1;</code>
        */
-      public Builder setResourceValueBytes(
-          com.google.protobuf.ByteString value) {
-        if (value == null) {
-    throw new NullPointerException();
-  }
-  checkByteStringIsUtf8(value);
-        
-        resourceValue_ = value;
-        onChanged();
-        return this;
+      public java.util.List<context.ContextOuterClass.Connection.Builder> 
+           getConnectionsBuilderList() {
+        return getConnectionsFieldBuilder().getBuilderList();
+      }
+      private com.google.protobuf.RepeatedFieldBuilderV3<
+          context.ContextOuterClass.Connection, context.ContextOuterClass.Connection.Builder, context.ContextOuterClass.ConnectionOrBuilder> 
+          getConnectionsFieldBuilder() {
+        if (connectionsBuilder_ == null) {
+          connectionsBuilder_ = new com.google.protobuf.RepeatedFieldBuilderV3<
+              context.ContextOuterClass.Connection, context.ContextOuterClass.Connection.Builder, context.ContextOuterClass.ConnectionOrBuilder>(
+                  connections_,
+                  ((bitField0_ & 0x00000001) != 0),
+                  getParentForChildren(),
+                  isClean());
+          connections_ = null;
+        }
+        return connectionsBuilder_;
       }
       @java.lang.Override
       public final Builder setUnknownFields(
@@ -30244,100 +31399,100 @@ public final class ContextOuterClass {
       }
 
 
-      // @@protoc_insertion_point(builder_scope:context.ConfigRule)
+      // @@protoc_insertion_point(builder_scope:context.ConnectionList)
     }
 
-    // @@protoc_insertion_point(class_scope:context.ConfigRule)
-    private static final context.ContextOuterClass.ConfigRule DEFAULT_INSTANCE;
+    // @@protoc_insertion_point(class_scope:context.ConnectionList)
+    private static final context.ContextOuterClass.ConnectionList DEFAULT_INSTANCE;
     static {
-      DEFAULT_INSTANCE = new context.ContextOuterClass.ConfigRule();
+      DEFAULT_INSTANCE = new context.ContextOuterClass.ConnectionList();
     }
 
-    public static context.ContextOuterClass.ConfigRule getDefaultInstance() {
+    public static context.ContextOuterClass.ConnectionList getDefaultInstance() {
       return DEFAULT_INSTANCE;
     }
 
-    private static final com.google.protobuf.Parser<ConfigRule>
-        PARSER = new com.google.protobuf.AbstractParser<ConfigRule>() {
+    private static final com.google.protobuf.Parser<ConnectionList>
+        PARSER = new com.google.protobuf.AbstractParser<ConnectionList>() {
       @java.lang.Override
-      public ConfigRule parsePartialFrom(
+      public ConnectionList parsePartialFrom(
           com.google.protobuf.CodedInputStream input,
           com.google.protobuf.ExtensionRegistryLite extensionRegistry)
           throws com.google.protobuf.InvalidProtocolBufferException {
-        return new ConfigRule(input, extensionRegistry);
+        return new ConnectionList(input, extensionRegistry);
       }
     };
 
-    public static com.google.protobuf.Parser<ConfigRule> parser() {
+    public static com.google.protobuf.Parser<ConnectionList> parser() {
       return PARSER;
     }
 
     @java.lang.Override
-    public com.google.protobuf.Parser<ConfigRule> getParserForType() {
+    public com.google.protobuf.Parser<ConnectionList> getParserForType() {
       return PARSER;
     }
 
     @java.lang.Override
-    public context.ContextOuterClass.ConfigRule getDefaultInstanceForType() {
+    public context.ContextOuterClass.ConnectionList getDefaultInstanceForType() {
       return DEFAULT_INSTANCE;
     }
 
   }
 
-  public interface ConstraintOrBuilder extends
-      // @@protoc_insertion_point(interface_extends:context.Constraint)
+  public interface ConnectionEventOrBuilder extends
+      // @@protoc_insertion_point(interface_extends:context.ConnectionEvent)
       com.google.protobuf.MessageOrBuilder {
 
     /**
-     * <code>string constraint_type = 1;</code>
-     * @return The constraintType.
+     * <code>.context.Event event = 1;</code>
+     * @return Whether the event field is set.
      */
-    java.lang.String getConstraintType();
+    boolean hasEvent();
     /**
-     * <code>string constraint_type = 1;</code>
-     * @return The bytes for constraintType.
+     * <code>.context.Event event = 1;</code>
+     * @return The event.
      */
-    com.google.protobuf.ByteString
-        getConstraintTypeBytes();
+    context.ContextOuterClass.Event getEvent();
+    /**
+     * <code>.context.Event event = 1;</code>
+     */
+    context.ContextOuterClass.EventOrBuilder getEventOrBuilder();
 
     /**
-     * <code>string constraint_value = 2;</code>
-     * @return The constraintValue.
+     * <code>.context.ConnectionId connection_id = 2;</code>
+     * @return Whether the connectionId field is set.
      */
-    java.lang.String getConstraintValue();
+    boolean hasConnectionId();
     /**
-     * <code>string constraint_value = 2;</code>
-     * @return The bytes for constraintValue.
+     * <code>.context.ConnectionId connection_id = 2;</code>
+     * @return The connectionId.
      */
-    com.google.protobuf.ByteString
-        getConstraintValueBytes();
+    context.ContextOuterClass.ConnectionId getConnectionId();
+    /**
+     * <code>.context.ConnectionId connection_id = 2;</code>
+     */
+    context.ContextOuterClass.ConnectionIdOrBuilder getConnectionIdOrBuilder();
   }
   /**
-   * <pre>
-   * ----- Constraint ----------------------------------------------------------------------------------------------------
-   * </pre>
-   *
-   * Protobuf type {@code context.Constraint}
+   * Protobuf type {@code context.ConnectionEvent}
    */
-  public static final class Constraint extends
+  public static final class ConnectionEvent extends
       com.google.protobuf.GeneratedMessageV3 implements
-      // @@protoc_insertion_point(message_implements:context.Constraint)
-      ConstraintOrBuilder {
+      // @@protoc_insertion_point(message_implements:context.ConnectionEvent)
+      ConnectionEventOrBuilder {
   private static final long serialVersionUID = 0L;
-    // Use Constraint.newBuilder() to construct.
-    private Constraint(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
+    // Use ConnectionEvent.newBuilder() to construct.
+    private ConnectionEvent(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
       super(builder);
     }
-    private Constraint() {
-      constraintType_ = "";
-      constraintValue_ = "";
+    private ConnectionEvent() {
     }
 
     @java.lang.Override
     @SuppressWarnings({"unused"})
     protected java.lang.Object newInstance(
         UnusedPrivateParameter unused) {
-      return new Constraint();
+      return new ConnectionEvent();
     }
 
     @java.lang.Override
@@ -30345,7 +31500,7 @@ public final class ContextOuterClass {
     getUnknownFields() {
       return this.unknownFields;
     }
-    private Constraint(
+    private ConnectionEvent(
         com.google.protobuf.CodedInputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
@@ -30364,15 +31519,29 @@ public final class ContextOuterClass {
               done = true;
               break;
             case 10: {
-              java.lang.String s = input.readStringRequireUtf8();
+              context.ContextOuterClass.Event.Builder subBuilder = null;
+              if (event_ != null) {
+                subBuilder = event_.toBuilder();
+              }
+              event_ = input.readMessage(context.ContextOuterClass.Event.parser(), extensionRegistry);
+              if (subBuilder != null) {
+                subBuilder.mergeFrom(event_);
+                event_ = subBuilder.buildPartial();
+              }
 
-              constraintType_ = s;
               break;
             }
             case 18: {
-              java.lang.String s = input.readStringRequireUtf8();
+              context.ContextOuterClass.ConnectionId.Builder subBuilder = null;
+              if (connectionId_ != null) {
+                subBuilder = connectionId_.toBuilder();
+              }
+              connectionId_ = input.readMessage(context.ContextOuterClass.ConnectionId.parser(), extensionRegistry);
+              if (subBuilder != null) {
+                subBuilder.mergeFrom(connectionId_);
+                connectionId_ = subBuilder.buildPartial();
+              }
 
-              constraintValue_ = s;
               break;
             }
             default: {
@@ -30396,91 +31565,67 @@ public final class ContextOuterClass {
     }
     public static final com.google.protobuf.Descriptors.Descriptor
         getDescriptor() {
-      return context.ContextOuterClass.internal_static_context_Constraint_descriptor;
+      return context.ContextOuterClass.internal_static_context_ConnectionEvent_descriptor;
     }
 
     @java.lang.Override
     protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
         internalGetFieldAccessorTable() {
-      return context.ContextOuterClass.internal_static_context_Constraint_fieldAccessorTable
+      return context.ContextOuterClass.internal_static_context_ConnectionEvent_fieldAccessorTable
           .ensureFieldAccessorsInitialized(
-              context.ContextOuterClass.Constraint.class, context.ContextOuterClass.Constraint.Builder.class);
+              context.ContextOuterClass.ConnectionEvent.class, context.ContextOuterClass.ConnectionEvent.Builder.class);
     }
 
-    public static final int CONSTRAINT_TYPE_FIELD_NUMBER = 1;
-    private volatile java.lang.Object constraintType_;
+    public static final int EVENT_FIELD_NUMBER = 1;
+    private context.ContextOuterClass.Event event_;
     /**
-     * <code>string constraint_type = 1;</code>
-     * @return The constraintType.
+     * <code>.context.Event event = 1;</code>
+     * @return Whether the event field is set.
      */
     @java.lang.Override
-    public java.lang.String getConstraintType() {
-      java.lang.Object ref = constraintType_;
-      if (ref instanceof java.lang.String) {
-        return (java.lang.String) ref;
-      } else {
-        com.google.protobuf.ByteString bs = 
-            (com.google.protobuf.ByteString) ref;
-        java.lang.String s = bs.toStringUtf8();
-        constraintType_ = s;
-        return s;
-      }
+    public boolean hasEvent() {
+      return event_ != null;
     }
     /**
-     * <code>string constraint_type = 1;</code>
-     * @return The bytes for constraintType.
+     * <code>.context.Event event = 1;</code>
+     * @return The event.
      */
     @java.lang.Override
-    public com.google.protobuf.ByteString
-        getConstraintTypeBytes() {
-      java.lang.Object ref = constraintType_;
-      if (ref instanceof java.lang.String) {
-        com.google.protobuf.ByteString b = 
-            com.google.protobuf.ByteString.copyFromUtf8(
-                (java.lang.String) ref);
-        constraintType_ = b;
-        return b;
-      } else {
-        return (com.google.protobuf.ByteString) ref;
-      }
+    public context.ContextOuterClass.Event getEvent() {
+      return event_ == null ? context.ContextOuterClass.Event.getDefaultInstance() : event_;
+    }
+    /**
+     * <code>.context.Event event = 1;</code>
+     */
+    @java.lang.Override
+    public context.ContextOuterClass.EventOrBuilder getEventOrBuilder() {
+      return getEvent();
     }
 
-    public static final int CONSTRAINT_VALUE_FIELD_NUMBER = 2;
-    private volatile java.lang.Object constraintValue_;
+    public static final int CONNECTION_ID_FIELD_NUMBER = 2;
+    private context.ContextOuterClass.ConnectionId connectionId_;
     /**
-     * <code>string constraint_value = 2;</code>
-     * @return The constraintValue.
+     * <code>.context.ConnectionId connection_id = 2;</code>
+     * @return Whether the connectionId field is set.
      */
     @java.lang.Override
-    public java.lang.String getConstraintValue() {
-      java.lang.Object ref = constraintValue_;
-      if (ref instanceof java.lang.String) {
-        return (java.lang.String) ref;
-      } else {
-        com.google.protobuf.ByteString bs = 
-            (com.google.protobuf.ByteString) ref;
-        java.lang.String s = bs.toStringUtf8();
-        constraintValue_ = s;
-        return s;
-      }
+    public boolean hasConnectionId() {
+      return connectionId_ != null;
     }
     /**
-     * <code>string constraint_value = 2;</code>
-     * @return The bytes for constraintValue.
+     * <code>.context.ConnectionId connection_id = 2;</code>
+     * @return The connectionId.
      */
     @java.lang.Override
-    public com.google.protobuf.ByteString
-        getConstraintValueBytes() {
-      java.lang.Object ref = constraintValue_;
-      if (ref instanceof java.lang.String) {
-        com.google.protobuf.ByteString b = 
-            com.google.protobuf.ByteString.copyFromUtf8(
-                (java.lang.String) ref);
-        constraintValue_ = b;
-        return b;
-      } else {
-        return (com.google.protobuf.ByteString) ref;
-      }
+    public context.ContextOuterClass.ConnectionId getConnectionId() {
+      return connectionId_ == null ? context.ContextOuterClass.ConnectionId.getDefaultInstance() : connectionId_;
+    }
+    /**
+     * <code>.context.ConnectionId connection_id = 2;</code>
+     */
+    @java.lang.Override
+    public context.ContextOuterClass.ConnectionIdOrBuilder getConnectionIdOrBuilder() {
+      return getConnectionId();
     }
 
     private byte memoizedIsInitialized = -1;
@@ -30497,11 +31642,11 @@ public final class ContextOuterClass {
     @java.lang.Override
     public void writeTo(com.google.protobuf.CodedOutputStream output)
                         throws java.io.IOException {
-      if (!getConstraintTypeBytes().isEmpty()) {
-        com.google.protobuf.GeneratedMessageV3.writeString(output, 1, constraintType_);
+      if (event_ != null) {
+        output.writeMessage(1, getEvent());
       }
-      if (!getConstraintValueBytes().isEmpty()) {
-        com.google.protobuf.GeneratedMessageV3.writeString(output, 2, constraintValue_);
+      if (connectionId_ != null) {
+        output.writeMessage(2, getConnectionId());
       }
       unknownFields.writeTo(output);
     }
@@ -30512,11 +31657,13 @@ public final class ContextOuterClass {
       if (size != -1) return size;
 
       size = 0;
-      if (!getConstraintTypeBytes().isEmpty()) {
-        size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, constraintType_);
+      if (event_ != null) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeMessageSize(1, getEvent());
       }
-      if (!getConstraintValueBytes().isEmpty()) {
-        size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, constraintValue_);
+      if (connectionId_ != null) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeMessageSize(2, getConnectionId());
       }
       size += unknownFields.getSerializedSize();
       memoizedSize = size;
@@ -30528,15 +31675,21 @@ public final class ContextOuterClass {
       if (obj == this) {
        return true;
       }
-      if (!(obj instanceof context.ContextOuterClass.Constraint)) {
+      if (!(obj instanceof context.ContextOuterClass.ConnectionEvent)) {
         return super.equals(obj);
       }
-      context.ContextOuterClass.Constraint other = (context.ContextOuterClass.Constraint) obj;
+      context.ContextOuterClass.ConnectionEvent other = (context.ContextOuterClass.ConnectionEvent) obj;
 
-      if (!getConstraintType()
-          .equals(other.getConstraintType())) return false;
-      if (!getConstraintValue()
-          .equals(other.getConstraintValue())) return false;
+      if (hasEvent() != other.hasEvent()) return false;
+      if (hasEvent()) {
+        if (!getEvent()
+            .equals(other.getEvent())) return false;
+      }
+      if (hasConnectionId() != other.hasConnectionId()) return false;
+      if (hasConnectionId()) {
+        if (!getConnectionId()
+            .equals(other.getConnectionId())) return false;
+      }
       if (!unknownFields.equals(other.unknownFields)) return false;
       return true;
     }
@@ -30548,78 +31701,82 @@ public final class ContextOuterClass {
       }
       int hash = 41;
       hash = (19 * hash) + getDescriptor().hashCode();
-      hash = (37 * hash) + CONSTRAINT_TYPE_FIELD_NUMBER;
-      hash = (53 * hash) + getConstraintType().hashCode();
-      hash = (37 * hash) + CONSTRAINT_VALUE_FIELD_NUMBER;
-      hash = (53 * hash) + getConstraintValue().hashCode();
+      if (hasEvent()) {
+        hash = (37 * hash) + EVENT_FIELD_NUMBER;
+        hash = (53 * hash) + getEvent().hashCode();
+      }
+      if (hasConnectionId()) {
+        hash = (37 * hash) + CONNECTION_ID_FIELD_NUMBER;
+        hash = (53 * hash) + getConnectionId().hashCode();
+      }
       hash = (29 * hash) + unknownFields.hashCode();
       memoizedHashCode = hash;
       return hash;
     }
 
-    public static context.ContextOuterClass.Constraint parseFrom(
+    public static context.ContextOuterClass.ConnectionEvent parseFrom(
         java.nio.ByteBuffer data)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data);
     }
-    public static context.ContextOuterClass.Constraint parseFrom(
+    public static context.ContextOuterClass.ConnectionEvent parseFrom(
         java.nio.ByteBuffer data,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data, extensionRegistry);
     }
-    public static context.ContextOuterClass.Constraint parseFrom(
+    public static context.ContextOuterClass.ConnectionEvent parseFrom(
         com.google.protobuf.ByteString data)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data);
     }
-    public static context.ContextOuterClass.Constraint parseFrom(
+    public static context.ContextOuterClass.ConnectionEvent parseFrom(
         com.google.protobuf.ByteString data,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data, extensionRegistry);
     }
-    public static context.ContextOuterClass.Constraint parseFrom(byte[] data)
+    public static context.ContextOuterClass.ConnectionEvent parseFrom(byte[] data)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data);
     }
-    public static context.ContextOuterClass.Constraint parseFrom(
+    public static context.ContextOuterClass.ConnectionEvent parseFrom(
         byte[] data,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data, extensionRegistry);
     }
-    public static context.ContextOuterClass.Constraint parseFrom(java.io.InputStream input)
+    public static context.ContextOuterClass.ConnectionEvent parseFrom(java.io.InputStream input)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseWithIOException(PARSER, input);
     }
-    public static context.ContextOuterClass.Constraint parseFrom(
+    public static context.ContextOuterClass.ConnectionEvent parseFrom(
         java.io.InputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseWithIOException(PARSER, input, extensionRegistry);
     }
-    public static context.ContextOuterClass.Constraint parseDelimitedFrom(java.io.InputStream input)
+    public static context.ContextOuterClass.ConnectionEvent parseDelimitedFrom(java.io.InputStream input)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseDelimitedWithIOException(PARSER, input);
     }
-    public static context.ContextOuterClass.Constraint parseDelimitedFrom(
+    public static context.ContextOuterClass.ConnectionEvent parseDelimitedFrom(
         java.io.InputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseDelimitedWithIOException(PARSER, input, extensionRegistry);
     }
-    public static context.ContextOuterClass.Constraint parseFrom(
+    public static context.ContextOuterClass.ConnectionEvent parseFrom(
         com.google.protobuf.CodedInputStream input)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseWithIOException(PARSER, input);
     }
-    public static context.ContextOuterClass.Constraint parseFrom(
+    public static context.ContextOuterClass.ConnectionEvent parseFrom(
         com.google.protobuf.CodedInputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws java.io.IOException {
@@ -30632,7 +31789,7 @@ public final class ContextOuterClass {
     public static Builder newBuilder() {
       return DEFAULT_INSTANCE.toBuilder();
     }
-    public static Builder newBuilder(context.ContextOuterClass.Constraint prototype) {
+    public static Builder newBuilder(context.ContextOuterClass.ConnectionEvent prototype) {
       return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
     }
     @java.lang.Override
@@ -30648,30 +31805,26 @@ public final class ContextOuterClass {
       return builder;
     }
     /**
-     * <pre>
-     * ----- Constraint ----------------------------------------------------------------------------------------------------
-     * </pre>
-     *
-     * Protobuf type {@code context.Constraint}
+     * Protobuf type {@code context.ConnectionEvent}
      */
     public static final class Builder extends
         com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
-        // @@protoc_insertion_point(builder_implements:context.Constraint)
-        context.ContextOuterClass.ConstraintOrBuilder {
+        // @@protoc_insertion_point(builder_implements:context.ConnectionEvent)
+        context.ContextOuterClass.ConnectionEventOrBuilder {
       public static final com.google.protobuf.Descriptors.Descriptor
           getDescriptor() {
-        return context.ContextOuterClass.internal_static_context_Constraint_descriptor;
+        return context.ContextOuterClass.internal_static_context_ConnectionEvent_descriptor;
       }
 
       @java.lang.Override
       protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
           internalGetFieldAccessorTable() {
-        return context.ContextOuterClass.internal_static_context_Constraint_fieldAccessorTable
+        return context.ContextOuterClass.internal_static_context_ConnectionEvent_fieldAccessorTable
             .ensureFieldAccessorsInitialized(
-                context.ContextOuterClass.Constraint.class, context.ContextOuterClass.Constraint.Builder.class);
+                context.ContextOuterClass.ConnectionEvent.class, context.ContextOuterClass.ConnectionEvent.Builder.class);
       }
 
-      // Construct using context.ContextOuterClass.Constraint.newBuilder()
+      // Construct using context.ContextOuterClass.ConnectionEvent.newBuilder()
       private Builder() {
         maybeForceBuilderInitialization();
       }
@@ -30689,27 +31842,35 @@ public final class ContextOuterClass {
       @java.lang.Override
       public Builder clear() {
         super.clear();
-        constraintType_ = "";
-
-        constraintValue_ = "";
-
+        if (eventBuilder_ == null) {
+          event_ = null;
+        } else {
+          event_ = null;
+          eventBuilder_ = null;
+        }
+        if (connectionIdBuilder_ == null) {
+          connectionId_ = null;
+        } else {
+          connectionId_ = null;
+          connectionIdBuilder_ = null;
+        }
         return this;
       }
 
       @java.lang.Override
       public com.google.protobuf.Descriptors.Descriptor
           getDescriptorForType() {
-        return context.ContextOuterClass.internal_static_context_Constraint_descriptor;
+        return context.ContextOuterClass.internal_static_context_ConnectionEvent_descriptor;
       }
 
       @java.lang.Override
-      public context.ContextOuterClass.Constraint getDefaultInstanceForType() {
-        return context.ContextOuterClass.Constraint.getDefaultInstance();
+      public context.ContextOuterClass.ConnectionEvent getDefaultInstanceForType() {
+        return context.ContextOuterClass.ConnectionEvent.getDefaultInstance();
       }
 
       @java.lang.Override
-      public context.ContextOuterClass.Constraint build() {
-        context.ContextOuterClass.Constraint result = buildPartial();
+      public context.ContextOuterClass.ConnectionEvent build() {
+        context.ContextOuterClass.ConnectionEvent result = buildPartial();
         if (!result.isInitialized()) {
           throw newUninitializedMessageException(result);
         }
@@ -30717,10 +31878,18 @@ public final class ContextOuterClass {
       }
 
       @java.lang.Override
-      public context.ContextOuterClass.Constraint buildPartial() {
-        context.ContextOuterClass.Constraint result = new context.ContextOuterClass.Constraint(this);
-        result.constraintType_ = constraintType_;
-        result.constraintValue_ = constraintValue_;
+      public context.ContextOuterClass.ConnectionEvent buildPartial() {
+        context.ContextOuterClass.ConnectionEvent result = new context.ContextOuterClass.ConnectionEvent(this);
+        if (eventBuilder_ == null) {
+          result.event_ = event_;
+        } else {
+          result.event_ = eventBuilder_.build();
+        }
+        if (connectionIdBuilder_ == null) {
+          result.connectionId_ = connectionId_;
+        } else {
+          result.connectionId_ = connectionIdBuilder_.build();
+        }
         onBuilt();
         return result;
       }
@@ -30759,23 +31928,21 @@ public final class ContextOuterClass {
       }
       @java.lang.Override
       public Builder mergeFrom(com.google.protobuf.Message other) {
-        if (other instanceof context.ContextOuterClass.Constraint) {
-          return mergeFrom((context.ContextOuterClass.Constraint)other);
+        if (other instanceof context.ContextOuterClass.ConnectionEvent) {
+          return mergeFrom((context.ContextOuterClass.ConnectionEvent)other);
         } else {
           super.mergeFrom(other);
           return this;
         }
       }
 
-      public Builder mergeFrom(context.ContextOuterClass.Constraint other) {
-        if (other == context.ContextOuterClass.Constraint.getDefaultInstance()) return this;
-        if (!other.getConstraintType().isEmpty()) {
-          constraintType_ = other.constraintType_;
-          onChanged();
+      public Builder mergeFrom(context.ContextOuterClass.ConnectionEvent other) {
+        if (other == context.ContextOuterClass.ConnectionEvent.getDefaultInstance()) return this;
+        if (other.hasEvent()) {
+          mergeEvent(other.getEvent());
         }
-        if (!other.getConstraintValue().isEmpty()) {
-          constraintValue_ = other.constraintValue_;
-          onChanged();
+        if (other.hasConnectionId()) {
+          mergeConnectionId(other.getConnectionId());
         }
         this.mergeUnknownFields(other.unknownFields);
         onChanged();
@@ -30792,11 +31959,11 @@ public final class ContextOuterClass {
           com.google.protobuf.CodedInputStream input,
           com.google.protobuf.ExtensionRegistryLite extensionRegistry)
           throws java.io.IOException {
-        context.ContextOuterClass.Constraint parsedMessage = null;
+        context.ContextOuterClass.ConnectionEvent parsedMessage = null;
         try {
           parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
         } catch (com.google.protobuf.InvalidProtocolBufferException e) {
-          parsedMessage = (context.ContextOuterClass.Constraint) e.getUnfinishedMessage();
+          parsedMessage = (context.ContextOuterClass.ConnectionEvent) e.getUnfinishedMessage();
           throw e.unwrapIOException();
         } finally {
           if (parsedMessage != null) {
@@ -30806,156 +31973,242 @@ public final class ContextOuterClass {
         return this;
       }
 
-      private java.lang.Object constraintType_ = "";
+      private context.ContextOuterClass.Event event_;
+      private com.google.protobuf.SingleFieldBuilderV3<
+          context.ContextOuterClass.Event, context.ContextOuterClass.Event.Builder, context.ContextOuterClass.EventOrBuilder> eventBuilder_;
       /**
-       * <code>string constraint_type = 1;</code>
-       * @return The constraintType.
+       * <code>.context.Event event = 1;</code>
+       * @return Whether the event field is set.
        */
-      public java.lang.String getConstraintType() {
-        java.lang.Object ref = constraintType_;
-        if (!(ref instanceof java.lang.String)) {
-          com.google.protobuf.ByteString bs =
-              (com.google.protobuf.ByteString) ref;
-          java.lang.String s = bs.toStringUtf8();
-          constraintType_ = s;
-          return s;
+      public boolean hasEvent() {
+        return eventBuilder_ != null || event_ != null;
+      }
+      /**
+       * <code>.context.Event event = 1;</code>
+       * @return The event.
+       */
+      public context.ContextOuterClass.Event getEvent() {
+        if (eventBuilder_ == null) {
+          return event_ == null ? context.ContextOuterClass.Event.getDefaultInstance() : event_;
         } else {
-          return (java.lang.String) ref;
+          return eventBuilder_.getMessage();
         }
       }
       /**
-       * <code>string constraint_type = 1;</code>
-       * @return The bytes for constraintType.
+       * <code>.context.Event event = 1;</code>
        */
-      public com.google.protobuf.ByteString
-          getConstraintTypeBytes() {
-        java.lang.Object ref = constraintType_;
-        if (ref instanceof String) {
-          com.google.protobuf.ByteString b = 
-              com.google.protobuf.ByteString.copyFromUtf8(
-                  (java.lang.String) ref);
-          constraintType_ = b;
-          return b;
+      public Builder setEvent(context.ContextOuterClass.Event value) {
+        if (eventBuilder_ == null) {
+          if (value == null) {
+            throw new NullPointerException();
+          }
+          event_ = value;
+          onChanged();
         } else {
-          return (com.google.protobuf.ByteString) ref;
+          eventBuilder_.setMessage(value);
+        }
+
+        return this;
+      }
+      /**
+       * <code>.context.Event event = 1;</code>
+       */
+      public Builder setEvent(
+          context.ContextOuterClass.Event.Builder builderForValue) {
+        if (eventBuilder_ == null) {
+          event_ = builderForValue.build();
+          onChanged();
+        } else {
+          eventBuilder_.setMessage(builderForValue.build());
+        }
+
+        return this;
+      }
+      /**
+       * <code>.context.Event event = 1;</code>
+       */
+      public Builder mergeEvent(context.ContextOuterClass.Event value) {
+        if (eventBuilder_ == null) {
+          if (event_ != null) {
+            event_ =
+              context.ContextOuterClass.Event.newBuilder(event_).mergeFrom(value).buildPartial();
+          } else {
+            event_ = value;
+          }
+          onChanged();
+        } else {
+          eventBuilder_.mergeFrom(value);
+        }
+
+        return this;
+      }
+      /**
+       * <code>.context.Event event = 1;</code>
+       */
+      public Builder clearEvent() {
+        if (eventBuilder_ == null) {
+          event_ = null;
+          onChanged();
+        } else {
+          event_ = null;
+          eventBuilder_ = null;
+        }
+
+        return this;
+      }
+      /**
+       * <code>.context.Event event = 1;</code>
+       */
+      public context.ContextOuterClass.Event.Builder getEventBuilder() {
+        
+        onChanged();
+        return getEventFieldBuilder().getBuilder();
+      }
+      /**
+       * <code>.context.Event event = 1;</code>
+       */
+      public context.ContextOuterClass.EventOrBuilder getEventOrBuilder() {
+        if (eventBuilder_ != null) {
+          return eventBuilder_.getMessageOrBuilder();
+        } else {
+          return event_ == null ?
+              context.ContextOuterClass.Event.getDefaultInstance() : event_;
+        }
+      }
+      /**
+       * <code>.context.Event event = 1;</code>
+       */
+      private com.google.protobuf.SingleFieldBuilderV3<
+          context.ContextOuterClass.Event, context.ContextOuterClass.Event.Builder, context.ContextOuterClass.EventOrBuilder> 
+          getEventFieldBuilder() {
+        if (eventBuilder_ == null) {
+          eventBuilder_ = new com.google.protobuf.SingleFieldBuilderV3<
+              context.ContextOuterClass.Event, context.ContextOuterClass.Event.Builder, context.ContextOuterClass.EventOrBuilder>(
+                  getEvent(),
+                  getParentForChildren(),
+                  isClean());
+          event_ = null;
         }
+        return eventBuilder_;
       }
+
+      private context.ContextOuterClass.ConnectionId connectionId_;
+      private com.google.protobuf.SingleFieldBuilderV3<
+          context.ContextOuterClass.ConnectionId, context.ContextOuterClass.ConnectionId.Builder, context.ContextOuterClass.ConnectionIdOrBuilder> connectionIdBuilder_;
       /**
-       * <code>string constraint_type = 1;</code>
-       * @param value The constraintType to set.
-       * @return This builder for chaining.
+       * <code>.context.ConnectionId connection_id = 2;</code>
+       * @return Whether the connectionId field is set.
        */
-      public Builder setConstraintType(
-          java.lang.String value) {
-        if (value == null) {
-    throw new NullPointerException();
-  }
-  
-        constraintType_ = value;
-        onChanged();
-        return this;
+      public boolean hasConnectionId() {
+        return connectionIdBuilder_ != null || connectionId_ != null;
       }
       /**
-       * <code>string constraint_type = 1;</code>
-       * @return This builder for chaining.
+       * <code>.context.ConnectionId connection_id = 2;</code>
+       * @return The connectionId.
        */
-      public Builder clearConstraintType() {
-        
-        constraintType_ = getDefaultInstance().getConstraintType();
-        onChanged();
-        return this;
+      public context.ContextOuterClass.ConnectionId getConnectionId() {
+        if (connectionIdBuilder_ == null) {
+          return connectionId_ == null ? context.ContextOuterClass.ConnectionId.getDefaultInstance() : connectionId_;
+        } else {
+          return connectionIdBuilder_.getMessage();
+        }
       }
       /**
-       * <code>string constraint_type = 1;</code>
-       * @param value The bytes for constraintType to set.
-       * @return This builder for chaining.
+       * <code>.context.ConnectionId connection_id = 2;</code>
        */
-      public Builder setConstraintTypeBytes(
-          com.google.protobuf.ByteString value) {
-        if (value == null) {
-    throw new NullPointerException();
-  }
-  checkByteStringIsUtf8(value);
-        
-        constraintType_ = value;
-        onChanged();
+      public Builder setConnectionId(context.ContextOuterClass.ConnectionId value) {
+        if (connectionIdBuilder_ == null) {
+          if (value == null) {
+            throw new NullPointerException();
+          }
+          connectionId_ = value;
+          onChanged();
+        } else {
+          connectionIdBuilder_.setMessage(value);
+        }
+
         return this;
       }
-
-      private java.lang.Object constraintValue_ = "";
       /**
-       * <code>string constraint_value = 2;</code>
-       * @return The constraintValue.
+       * <code>.context.ConnectionId connection_id = 2;</code>
        */
-      public java.lang.String getConstraintValue() {
-        java.lang.Object ref = constraintValue_;
-        if (!(ref instanceof java.lang.String)) {
-          com.google.protobuf.ByteString bs =
-              (com.google.protobuf.ByteString) ref;
-          java.lang.String s = bs.toStringUtf8();
-          constraintValue_ = s;
-          return s;
+      public Builder setConnectionId(
+          context.ContextOuterClass.ConnectionId.Builder builderForValue) {
+        if (connectionIdBuilder_ == null) {
+          connectionId_ = builderForValue.build();
+          onChanged();
         } else {
-          return (java.lang.String) ref;
+          connectionIdBuilder_.setMessage(builderForValue.build());
         }
+
+        return this;
       }
       /**
-       * <code>string constraint_value = 2;</code>
-       * @return The bytes for constraintValue.
+       * <code>.context.ConnectionId connection_id = 2;</code>
        */
-      public com.google.protobuf.ByteString
-          getConstraintValueBytes() {
-        java.lang.Object ref = constraintValue_;
-        if (ref instanceof String) {
-          com.google.protobuf.ByteString b = 
-              com.google.protobuf.ByteString.copyFromUtf8(
-                  (java.lang.String) ref);
-          constraintValue_ = b;
-          return b;
+      public Builder mergeConnectionId(context.ContextOuterClass.ConnectionId value) {
+        if (connectionIdBuilder_ == null) {
+          if (connectionId_ != null) {
+            connectionId_ =
+              context.ContextOuterClass.ConnectionId.newBuilder(connectionId_).mergeFrom(value).buildPartial();
+          } else {
+            connectionId_ = value;
+          }
+          onChanged();
         } else {
-          return (com.google.protobuf.ByteString) ref;
+          connectionIdBuilder_.mergeFrom(value);
         }
+
+        return this;
       }
       /**
-       * <code>string constraint_value = 2;</code>
-       * @param value The constraintValue to set.
-       * @return This builder for chaining.
+       * <code>.context.ConnectionId connection_id = 2;</code>
        */
-      public Builder setConstraintValue(
-          java.lang.String value) {
-        if (value == null) {
-    throw new NullPointerException();
-  }
-  
-        constraintValue_ = value;
-        onChanged();
+      public Builder clearConnectionId() {
+        if (connectionIdBuilder_ == null) {
+          connectionId_ = null;
+          onChanged();
+        } else {
+          connectionId_ = null;
+          connectionIdBuilder_ = null;
+        }
+
         return this;
       }
       /**
-       * <code>string constraint_value = 2;</code>
-       * @return This builder for chaining.
+       * <code>.context.ConnectionId connection_id = 2;</code>
        */
-      public Builder clearConstraintValue() {
+      public context.ContextOuterClass.ConnectionId.Builder getConnectionIdBuilder() {
         
-        constraintValue_ = getDefaultInstance().getConstraintValue();
         onChanged();
-        return this;
+        return getConnectionIdFieldBuilder().getBuilder();
       }
       /**
-       * <code>string constraint_value = 2;</code>
-       * @param value The bytes for constraintValue to set.
-       * @return This builder for chaining.
+       * <code>.context.ConnectionId connection_id = 2;</code>
        */
-      public Builder setConstraintValueBytes(
-          com.google.protobuf.ByteString value) {
-        if (value == null) {
-    throw new NullPointerException();
-  }
-  checkByteStringIsUtf8(value);
-        
-        constraintValue_ = value;
-        onChanged();
-        return this;
+      public context.ContextOuterClass.ConnectionIdOrBuilder getConnectionIdOrBuilder() {
+        if (connectionIdBuilder_ != null) {
+          return connectionIdBuilder_.getMessageOrBuilder();
+        } else {
+          return connectionId_ == null ?
+              context.ContextOuterClass.ConnectionId.getDefaultInstance() : connectionId_;
+        }
+      }
+      /**
+       * <code>.context.ConnectionId connection_id = 2;</code>
+       */
+      private com.google.protobuf.SingleFieldBuilderV3<
+          context.ContextOuterClass.ConnectionId, context.ContextOuterClass.ConnectionId.Builder, context.ContextOuterClass.ConnectionIdOrBuilder> 
+          getConnectionIdFieldBuilder() {
+        if (connectionIdBuilder_ == null) {
+          connectionIdBuilder_ = new com.google.protobuf.SingleFieldBuilderV3<
+              context.ContextOuterClass.ConnectionId, context.ContextOuterClass.ConnectionId.Builder, context.ContextOuterClass.ConnectionIdOrBuilder>(
+                  getConnectionId(),
+                  getParentForChildren(),
+                  isClean());
+          connectionId_ = null;
+        }
+        return connectionIdBuilder_;
       }
       @java.lang.Override
       public final Builder setUnknownFields(
@@ -30970,89 +32223,119 @@ public final class ContextOuterClass {
       }
 
 
-      // @@protoc_insertion_point(builder_scope:context.Constraint)
+      // @@protoc_insertion_point(builder_scope:context.ConnectionEvent)
     }
 
-    // @@protoc_insertion_point(class_scope:context.Constraint)
-    private static final context.ContextOuterClass.Constraint DEFAULT_INSTANCE;
+    // @@protoc_insertion_point(class_scope:context.ConnectionEvent)
+    private static final context.ContextOuterClass.ConnectionEvent DEFAULT_INSTANCE;
     static {
-      DEFAULT_INSTANCE = new context.ContextOuterClass.Constraint();
+      DEFAULT_INSTANCE = new context.ContextOuterClass.ConnectionEvent();
     }
 
-    public static context.ContextOuterClass.Constraint getDefaultInstance() {
+    public static context.ContextOuterClass.ConnectionEvent getDefaultInstance() {
       return DEFAULT_INSTANCE;
     }
 
-    private static final com.google.protobuf.Parser<Constraint>
-        PARSER = new com.google.protobuf.AbstractParser<Constraint>() {
+    private static final com.google.protobuf.Parser<ConnectionEvent>
+        PARSER = new com.google.protobuf.AbstractParser<ConnectionEvent>() {
       @java.lang.Override
-      public Constraint parsePartialFrom(
+      public ConnectionEvent parsePartialFrom(
           com.google.protobuf.CodedInputStream input,
           com.google.protobuf.ExtensionRegistryLite extensionRegistry)
           throws com.google.protobuf.InvalidProtocolBufferException {
-        return new Constraint(input, extensionRegistry);
+        return new ConnectionEvent(input, extensionRegistry);
       }
     };
 
-    public static com.google.protobuf.Parser<Constraint> parser() {
+    public static com.google.protobuf.Parser<ConnectionEvent> parser() {
       return PARSER;
     }
 
     @java.lang.Override
-    public com.google.protobuf.Parser<Constraint> getParserForType() {
+    public com.google.protobuf.Parser<ConnectionEvent> getParserForType() {
       return PARSER;
     }
 
     @java.lang.Override
-    public context.ContextOuterClass.Constraint getDefaultInstanceForType() {
+    public context.ContextOuterClass.ConnectionEvent getDefaultInstanceForType() {
       return DEFAULT_INSTANCE;
     }
 
   }
 
-  public interface ConnectionIdOrBuilder extends
-      // @@protoc_insertion_point(interface_extends:context.ConnectionId)
+  public interface EndPointIdOrBuilder extends
+      // @@protoc_insertion_point(interface_extends:context.EndPointId)
       com.google.protobuf.MessageOrBuilder {
 
     /**
-     * <code>.context.Uuid connection_uuid = 1;</code>
-     * @return Whether the connectionUuid field is set.
+     * <code>.context.TopologyId topology_id = 1;</code>
+     * @return Whether the topologyId field is set.
      */
-    boolean hasConnectionUuid();
+    boolean hasTopologyId();
     /**
-     * <code>.context.Uuid connection_uuid = 1;</code>
-     * @return The connectionUuid.
+     * <code>.context.TopologyId topology_id = 1;</code>
+     * @return The topologyId.
      */
-    context.ContextOuterClass.Uuid getConnectionUuid();
+    context.ContextOuterClass.TopologyId getTopologyId();
     /**
-     * <code>.context.Uuid connection_uuid = 1;</code>
+     * <code>.context.TopologyId topology_id = 1;</code>
      */
-    context.ContextOuterClass.UuidOrBuilder getConnectionUuidOrBuilder();
+    context.ContextOuterClass.TopologyIdOrBuilder getTopologyIdOrBuilder();
+
+    /**
+     * <code>.context.DeviceId device_id = 2;</code>
+     * @return Whether the deviceId field is set.
+     */
+    boolean hasDeviceId();
+    /**
+     * <code>.context.DeviceId device_id = 2;</code>
+     * @return The deviceId.
+     */
+    context.ContextOuterClass.DeviceId getDeviceId();
+    /**
+     * <code>.context.DeviceId device_id = 2;</code>
+     */
+    context.ContextOuterClass.DeviceIdOrBuilder getDeviceIdOrBuilder();
+
+    /**
+     * <code>.context.Uuid endpoint_uuid = 3;</code>
+     * @return Whether the endpointUuid field is set.
+     */
+    boolean hasEndpointUuid();
+    /**
+     * <code>.context.Uuid endpoint_uuid = 3;</code>
+     * @return The endpointUuid.
+     */
+    context.ContextOuterClass.Uuid getEndpointUuid();
+    /**
+     * <code>.context.Uuid endpoint_uuid = 3;</code>
+     */
+    context.ContextOuterClass.UuidOrBuilder getEndpointUuidOrBuilder();
   }
   /**
    * <pre>
-   * ----- Connection ----------------------------------------------------------------------------------------------------
+   * ----- Endpoint ------------------------------------------------------------------------------------------------------
    * </pre>
    *
-   * Protobuf type {@code context.ConnectionId}
+   * Protobuf type {@code context.EndPointId}
    */
-  public static final class ConnectionId extends
+  public static final class EndPointId extends
       com.google.protobuf.GeneratedMessageV3 implements
-      // @@protoc_insertion_point(message_implements:context.ConnectionId)
-      ConnectionIdOrBuilder {
+      // @@protoc_insertion_point(message_implements:context.EndPointId)
+      EndPointIdOrBuilder {
   private static final long serialVersionUID = 0L;
-    // Use ConnectionId.newBuilder() to construct.
-    private ConnectionId(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
+    // Use EndPointId.newBuilder() to construct.
+    private EndPointId(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
       super(builder);
     }
-    private ConnectionId() {
+    private EndPointId() {
     }
 
     @java.lang.Override
     @SuppressWarnings({"unused"})
     protected java.lang.Object newInstance(
         UnusedPrivateParameter unused) {
-      return new ConnectionId();
+      return new EndPointId();
     }
 
     @java.lang.Override
@@ -31060,7 +32343,7 @@ public final class ContextOuterClass {
     getUnknownFields() {
       return this.unknownFields;
     }
-    private ConnectionId(
+    private EndPointId(
         com.google.protobuf.CodedInputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
@@ -31078,15 +32361,41 @@ public final class ContextOuterClass {
             case 0:
               done = true;
               break;
-            case 10: {
+            case 10: {
+              context.ContextOuterClass.TopologyId.Builder subBuilder = null;
+              if (topologyId_ != null) {
+                subBuilder = topologyId_.toBuilder();
+              }
+              topologyId_ = input.readMessage(context.ContextOuterClass.TopologyId.parser(), extensionRegistry);
+              if (subBuilder != null) {
+                subBuilder.mergeFrom(topologyId_);
+                topologyId_ = subBuilder.buildPartial();
+              }
+
+              break;
+            }
+            case 18: {
+              context.ContextOuterClass.DeviceId.Builder subBuilder = null;
+              if (deviceId_ != null) {
+                subBuilder = deviceId_.toBuilder();
+              }
+              deviceId_ = input.readMessage(context.ContextOuterClass.DeviceId.parser(), extensionRegistry);
+              if (subBuilder != null) {
+                subBuilder.mergeFrom(deviceId_);
+                deviceId_ = subBuilder.buildPartial();
+              }
+
+              break;
+            }
+            case 26: {
               context.ContextOuterClass.Uuid.Builder subBuilder = null;
-              if (connectionUuid_ != null) {
-                subBuilder = connectionUuid_.toBuilder();
+              if (endpointUuid_ != null) {
+                subBuilder = endpointUuid_.toBuilder();
               }
-              connectionUuid_ = input.readMessage(context.ContextOuterClass.Uuid.parser(), extensionRegistry);
+              endpointUuid_ = input.readMessage(context.ContextOuterClass.Uuid.parser(), extensionRegistry);
               if (subBuilder != null) {
-                subBuilder.mergeFrom(connectionUuid_);
-                connectionUuid_ = subBuilder.buildPartial();
+                subBuilder.mergeFrom(endpointUuid_);
+                endpointUuid_ = subBuilder.buildPartial();
               }
 
               break;
@@ -31110,481 +32419,831 @@ public final class ContextOuterClass {
         makeExtensionsImmutable();
       }
     }
-    public static final com.google.protobuf.Descriptors.Descriptor
-        getDescriptor() {
-      return context.ContextOuterClass.internal_static_context_ConnectionId_descriptor;
+    public static final com.google.protobuf.Descriptors.Descriptor
+        getDescriptor() {
+      return context.ContextOuterClass.internal_static_context_EndPointId_descriptor;
+    }
+
+    @java.lang.Override
+    protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+        internalGetFieldAccessorTable() {
+      return context.ContextOuterClass.internal_static_context_EndPointId_fieldAccessorTable
+          .ensureFieldAccessorsInitialized(
+              context.ContextOuterClass.EndPointId.class, context.ContextOuterClass.EndPointId.Builder.class);
+    }
+
+    public static final int TOPOLOGY_ID_FIELD_NUMBER = 1;
+    private context.ContextOuterClass.TopologyId topologyId_;
+    /**
+     * <code>.context.TopologyId topology_id = 1;</code>
+     * @return Whether the topologyId field is set.
+     */
+    @java.lang.Override
+    public boolean hasTopologyId() {
+      return topologyId_ != null;
+    }
+    /**
+     * <code>.context.TopologyId topology_id = 1;</code>
+     * @return The topologyId.
+     */
+    @java.lang.Override
+    public context.ContextOuterClass.TopologyId getTopologyId() {
+      return topologyId_ == null ? context.ContextOuterClass.TopologyId.getDefaultInstance() : topologyId_;
+    }
+    /**
+     * <code>.context.TopologyId topology_id = 1;</code>
+     */
+    @java.lang.Override
+    public context.ContextOuterClass.TopologyIdOrBuilder getTopologyIdOrBuilder() {
+      return getTopologyId();
+    }
+
+    public static final int DEVICE_ID_FIELD_NUMBER = 2;
+    private context.ContextOuterClass.DeviceId deviceId_;
+    /**
+     * <code>.context.DeviceId device_id = 2;</code>
+     * @return Whether the deviceId field is set.
+     */
+    @java.lang.Override
+    public boolean hasDeviceId() {
+      return deviceId_ != null;
+    }
+    /**
+     * <code>.context.DeviceId device_id = 2;</code>
+     * @return The deviceId.
+     */
+    @java.lang.Override
+    public context.ContextOuterClass.DeviceId getDeviceId() {
+      return deviceId_ == null ? context.ContextOuterClass.DeviceId.getDefaultInstance() : deviceId_;
+    }
+    /**
+     * <code>.context.DeviceId device_id = 2;</code>
+     */
+    @java.lang.Override
+    public context.ContextOuterClass.DeviceIdOrBuilder getDeviceIdOrBuilder() {
+      return getDeviceId();
+    }
+
+    public static final int ENDPOINT_UUID_FIELD_NUMBER = 3;
+    private context.ContextOuterClass.Uuid endpointUuid_;
+    /**
+     * <code>.context.Uuid endpoint_uuid = 3;</code>
+     * @return Whether the endpointUuid field is set.
+     */
+    @java.lang.Override
+    public boolean hasEndpointUuid() {
+      return endpointUuid_ != null;
+    }
+    /**
+     * <code>.context.Uuid endpoint_uuid = 3;</code>
+     * @return The endpointUuid.
+     */
+    @java.lang.Override
+    public context.ContextOuterClass.Uuid getEndpointUuid() {
+      return endpointUuid_ == null ? context.ContextOuterClass.Uuid.getDefaultInstance() : endpointUuid_;
+    }
+    /**
+     * <code>.context.Uuid endpoint_uuid = 3;</code>
+     */
+    @java.lang.Override
+    public context.ContextOuterClass.UuidOrBuilder getEndpointUuidOrBuilder() {
+      return getEndpointUuid();
+    }
+
+    private byte memoizedIsInitialized = -1;
+    @java.lang.Override
+    public final boolean isInitialized() {
+      byte isInitialized = memoizedIsInitialized;
+      if (isInitialized == 1) return true;
+      if (isInitialized == 0) return false;
+
+      memoizedIsInitialized = 1;
+      return true;
+    }
+
+    @java.lang.Override
+    public void writeTo(com.google.protobuf.CodedOutputStream output)
+                        throws java.io.IOException {
+      if (topologyId_ != null) {
+        output.writeMessage(1, getTopologyId());
+      }
+      if (deviceId_ != null) {
+        output.writeMessage(2, getDeviceId());
+      }
+      if (endpointUuid_ != null) {
+        output.writeMessage(3, getEndpointUuid());
+      }
+      unknownFields.writeTo(output);
+    }
+
+    @java.lang.Override
+    public int getSerializedSize() {
+      int size = memoizedSize;
+      if (size != -1) return size;
+
+      size = 0;
+      if (topologyId_ != null) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeMessageSize(1, getTopologyId());
+      }
+      if (deviceId_ != null) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeMessageSize(2, getDeviceId());
+      }
+      if (endpointUuid_ != null) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeMessageSize(3, getEndpointUuid());
+      }
+      size += unknownFields.getSerializedSize();
+      memoizedSize = size;
+      return size;
+    }
+
+    @java.lang.Override
+    public boolean equals(final java.lang.Object obj) {
+      if (obj == this) {
+       return true;
+      }
+      if (!(obj instanceof context.ContextOuterClass.EndPointId)) {
+        return super.equals(obj);
+      }
+      context.ContextOuterClass.EndPointId other = (context.ContextOuterClass.EndPointId) obj;
+
+      if (hasTopologyId() != other.hasTopologyId()) return false;
+      if (hasTopologyId()) {
+        if (!getTopologyId()
+            .equals(other.getTopologyId())) return false;
+      }
+      if (hasDeviceId() != other.hasDeviceId()) return false;
+      if (hasDeviceId()) {
+        if (!getDeviceId()
+            .equals(other.getDeviceId())) return false;
+      }
+      if (hasEndpointUuid() != other.hasEndpointUuid()) return false;
+      if (hasEndpointUuid()) {
+        if (!getEndpointUuid()
+            .equals(other.getEndpointUuid())) return false;
+      }
+      if (!unknownFields.equals(other.unknownFields)) return false;
+      return true;
+    }
+
+    @java.lang.Override
+    public int hashCode() {
+      if (memoizedHashCode != 0) {
+        return memoizedHashCode;
+      }
+      int hash = 41;
+      hash = (19 * hash) + getDescriptor().hashCode();
+      if (hasTopologyId()) {
+        hash = (37 * hash) + TOPOLOGY_ID_FIELD_NUMBER;
+        hash = (53 * hash) + getTopologyId().hashCode();
+      }
+      if (hasDeviceId()) {
+        hash = (37 * hash) + DEVICE_ID_FIELD_NUMBER;
+        hash = (53 * hash) + getDeviceId().hashCode();
+      }
+      if (hasEndpointUuid()) {
+        hash = (37 * hash) + ENDPOINT_UUID_FIELD_NUMBER;
+        hash = (53 * hash) + getEndpointUuid().hashCode();
+      }
+      hash = (29 * hash) + unknownFields.hashCode();
+      memoizedHashCode = hash;
+      return hash;
+    }
+
+    public static context.ContextOuterClass.EndPointId parseFrom(
+        java.nio.ByteBuffer data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data);
+    }
+    public static context.ContextOuterClass.EndPointId parseFrom(
+        java.nio.ByteBuffer data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data, extensionRegistry);
+    }
+    public static context.ContextOuterClass.EndPointId parseFrom(
+        com.google.protobuf.ByteString data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data);
+    }
+    public static context.ContextOuterClass.EndPointId parseFrom(
+        com.google.protobuf.ByteString data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data, extensionRegistry);
+    }
+    public static context.ContextOuterClass.EndPointId parseFrom(byte[] data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data);
+    }
+    public static context.ContextOuterClass.EndPointId parseFrom(
+        byte[] data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data, extensionRegistry);
+    }
+    public static context.ContextOuterClass.EndPointId parseFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      return com.google.protobuf.GeneratedMessageV3
+          .parseWithIOException(PARSER, input);
+    }
+    public static context.ContextOuterClass.EndPointId parseFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return com.google.protobuf.GeneratedMessageV3
+          .parseWithIOException(PARSER, input, extensionRegistry);
+    }
+    public static context.ContextOuterClass.EndPointId parseDelimitedFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      return com.google.protobuf.GeneratedMessageV3
+          .parseDelimitedWithIOException(PARSER, input);
+    }
+    public static context.ContextOuterClass.EndPointId parseDelimitedFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return com.google.protobuf.GeneratedMessageV3
+          .parseDelimitedWithIOException(PARSER, input, extensionRegistry);
+    }
+    public static context.ContextOuterClass.EndPointId parseFrom(
+        com.google.protobuf.CodedInputStream input)
+        throws java.io.IOException {
+      return com.google.protobuf.GeneratedMessageV3
+          .parseWithIOException(PARSER, input);
+    }
+    public static context.ContextOuterClass.EndPointId parseFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return com.google.protobuf.GeneratedMessageV3
+          .parseWithIOException(PARSER, input, extensionRegistry);
     }
 
     @java.lang.Override
-    protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
-        internalGetFieldAccessorTable() {
-      return context.ContextOuterClass.internal_static_context_ConnectionId_fieldAccessorTable
-          .ensureFieldAccessorsInitialized(
-              context.ContextOuterClass.ConnectionId.class, context.ContextOuterClass.ConnectionId.Builder.class);
+    public Builder newBuilderForType() { return newBuilder(); }
+    public static Builder newBuilder() {
+      return DEFAULT_INSTANCE.toBuilder();
+    }
+    public static Builder newBuilder(context.ContextOuterClass.EndPointId prototype) {
+      return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
     }
-
-    public static final int CONNECTION_UUID_FIELD_NUMBER = 1;
-    private context.ContextOuterClass.Uuid connectionUuid_;
-    /**
-     * <code>.context.Uuid connection_uuid = 1;</code>
-     * @return Whether the connectionUuid field is set.
-     */
     @java.lang.Override
-    public boolean hasConnectionUuid() {
-      return connectionUuid_ != null;
+    public Builder toBuilder() {
+      return this == DEFAULT_INSTANCE
+          ? new Builder() : new Builder().mergeFrom(this);
     }
-    /**
-     * <code>.context.Uuid connection_uuid = 1;</code>
-     * @return The connectionUuid.
-     */
+
     @java.lang.Override
-    public context.ContextOuterClass.Uuid getConnectionUuid() {
-      return connectionUuid_ == null ? context.ContextOuterClass.Uuid.getDefaultInstance() : connectionUuid_;
+    protected Builder newBuilderForType(
+        com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+      Builder builder = new Builder(parent);
+      return builder;
     }
     /**
-     * <code>.context.Uuid connection_uuid = 1;</code>
+     * <pre>
+     * ----- Endpoint ------------------------------------------------------------------------------------------------------
+     * </pre>
+     *
+     * Protobuf type {@code context.EndPointId}
      */
-    @java.lang.Override
-    public context.ContextOuterClass.UuidOrBuilder getConnectionUuidOrBuilder() {
-      return getConnectionUuid();
-    }
+    public static final class Builder extends
+        com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
+        // @@protoc_insertion_point(builder_implements:context.EndPointId)
+        context.ContextOuterClass.EndPointIdOrBuilder {
+      public static final com.google.protobuf.Descriptors.Descriptor
+          getDescriptor() {
+        return context.ContextOuterClass.internal_static_context_EndPointId_descriptor;
+      }
 
-    private byte memoizedIsInitialized = -1;
-    @java.lang.Override
-    public final boolean isInitialized() {
-      byte isInitialized = memoizedIsInitialized;
-      if (isInitialized == 1) return true;
-      if (isInitialized == 0) return false;
+      @java.lang.Override
+      protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+          internalGetFieldAccessorTable() {
+        return context.ContextOuterClass.internal_static_context_EndPointId_fieldAccessorTable
+            .ensureFieldAccessorsInitialized(
+                context.ContextOuterClass.EndPointId.class, context.ContextOuterClass.EndPointId.Builder.class);
+      }
 
-      memoizedIsInitialized = 1;
-      return true;
-    }
+      // Construct using context.ContextOuterClass.EndPointId.newBuilder()
+      private Builder() {
+        maybeForceBuilderInitialization();
+      }
 
-    @java.lang.Override
-    public void writeTo(com.google.protobuf.CodedOutputStream output)
-                        throws java.io.IOException {
-      if (connectionUuid_ != null) {
-        output.writeMessage(1, getConnectionUuid());
+      private Builder(
+          com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+        super(parent);
+        maybeForceBuilderInitialization();
+      }
+      private void maybeForceBuilderInitialization() {
+        if (com.google.protobuf.GeneratedMessageV3
+                .alwaysUseFieldBuilders) {
+        }
+      }
+      @java.lang.Override
+      public Builder clear() {
+        super.clear();
+        if (topologyIdBuilder_ == null) {
+          topologyId_ = null;
+        } else {
+          topologyId_ = null;
+          topologyIdBuilder_ = null;
+        }
+        if (deviceIdBuilder_ == null) {
+          deviceId_ = null;
+        } else {
+          deviceId_ = null;
+          deviceIdBuilder_ = null;
+        }
+        if (endpointUuidBuilder_ == null) {
+          endpointUuid_ = null;
+        } else {
+          endpointUuid_ = null;
+          endpointUuidBuilder_ = null;
+        }
+        return this;
       }
-      unknownFields.writeTo(output);
-    }
 
-    @java.lang.Override
-    public int getSerializedSize() {
-      int size = memoizedSize;
-      if (size != -1) return size;
+      @java.lang.Override
+      public com.google.protobuf.Descriptors.Descriptor
+          getDescriptorForType() {
+        return context.ContextOuterClass.internal_static_context_EndPointId_descriptor;
+      }
 
-      size = 0;
-      if (connectionUuid_ != null) {
-        size += com.google.protobuf.CodedOutputStream
-          .computeMessageSize(1, getConnectionUuid());
+      @java.lang.Override
+      public context.ContextOuterClass.EndPointId getDefaultInstanceForType() {
+        return context.ContextOuterClass.EndPointId.getDefaultInstance();
       }
-      size += unknownFields.getSerializedSize();
-      memoizedSize = size;
-      return size;
-    }
 
-    @java.lang.Override
-    public boolean equals(final java.lang.Object obj) {
-      if (obj == this) {
-       return true;
+      @java.lang.Override
+      public context.ContextOuterClass.EndPointId build() {
+        context.ContextOuterClass.EndPointId result = buildPartial();
+        if (!result.isInitialized()) {
+          throw newUninitializedMessageException(result);
+        }
+        return result;
       }
-      if (!(obj instanceof context.ContextOuterClass.ConnectionId)) {
-        return super.equals(obj);
+
+      @java.lang.Override
+      public context.ContextOuterClass.EndPointId buildPartial() {
+        context.ContextOuterClass.EndPointId result = new context.ContextOuterClass.EndPointId(this);
+        if (topologyIdBuilder_ == null) {
+          result.topologyId_ = topologyId_;
+        } else {
+          result.topologyId_ = topologyIdBuilder_.build();
+        }
+        if (deviceIdBuilder_ == null) {
+          result.deviceId_ = deviceId_;
+        } else {
+          result.deviceId_ = deviceIdBuilder_.build();
+        }
+        if (endpointUuidBuilder_ == null) {
+          result.endpointUuid_ = endpointUuid_;
+        } else {
+          result.endpointUuid_ = endpointUuidBuilder_.build();
+        }
+        onBuilt();
+        return result;
+      }
+
+      @java.lang.Override
+      public Builder clone() {
+        return super.clone();
+      }
+      @java.lang.Override
+      public Builder setField(
+          com.google.protobuf.Descriptors.FieldDescriptor field,
+          java.lang.Object value) {
+        return super.setField(field, value);
+      }
+      @java.lang.Override
+      public Builder clearField(
+          com.google.protobuf.Descriptors.FieldDescriptor field) {
+        return super.clearField(field);
+      }
+      @java.lang.Override
+      public Builder clearOneof(
+          com.google.protobuf.Descriptors.OneofDescriptor oneof) {
+        return super.clearOneof(oneof);
+      }
+      @java.lang.Override
+      public Builder setRepeatedField(
+          com.google.protobuf.Descriptors.FieldDescriptor field,
+          int index, java.lang.Object value) {
+        return super.setRepeatedField(field, index, value);
+      }
+      @java.lang.Override
+      public Builder addRepeatedField(
+          com.google.protobuf.Descriptors.FieldDescriptor field,
+          java.lang.Object value) {
+        return super.addRepeatedField(field, value);
+      }
+      @java.lang.Override
+      public Builder mergeFrom(com.google.protobuf.Message other) {
+        if (other instanceof context.ContextOuterClass.EndPointId) {
+          return mergeFrom((context.ContextOuterClass.EndPointId)other);
+        } else {
+          super.mergeFrom(other);
+          return this;
+        }
+      }
+
+      public Builder mergeFrom(context.ContextOuterClass.EndPointId other) {
+        if (other == context.ContextOuterClass.EndPointId.getDefaultInstance()) return this;
+        if (other.hasTopologyId()) {
+          mergeTopologyId(other.getTopologyId());
+        }
+        if (other.hasDeviceId()) {
+          mergeDeviceId(other.getDeviceId());
+        }
+        if (other.hasEndpointUuid()) {
+          mergeEndpointUuid(other.getEndpointUuid());
+        }
+        this.mergeUnknownFields(other.unknownFields);
+        onChanged();
+        return this;
       }
-      context.ContextOuterClass.ConnectionId other = (context.ContextOuterClass.ConnectionId) obj;
 
-      if (hasConnectionUuid() != other.hasConnectionUuid()) return false;
-      if (hasConnectionUuid()) {
-        if (!getConnectionUuid()
-            .equals(other.getConnectionUuid())) return false;
+      @java.lang.Override
+      public final boolean isInitialized() {
+        return true;
       }
-      if (!unknownFields.equals(other.unknownFields)) return false;
-      return true;
-    }
 
-    @java.lang.Override
-    public int hashCode() {
-      if (memoizedHashCode != 0) {
-        return memoizedHashCode;
-      }
-      int hash = 41;
-      hash = (19 * hash) + getDescriptor().hashCode();
-      if (hasConnectionUuid()) {
-        hash = (37 * hash) + CONNECTION_UUID_FIELD_NUMBER;
-        hash = (53 * hash) + getConnectionUuid().hashCode();
+      @java.lang.Override
+      public Builder mergeFrom(
+          com.google.protobuf.CodedInputStream input,
+          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+          throws java.io.IOException {
+        context.ContextOuterClass.EndPointId parsedMessage = null;
+        try {
+          parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
+        } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+          parsedMessage = (context.ContextOuterClass.EndPointId) e.getUnfinishedMessage();
+          throw e.unwrapIOException();
+        } finally {
+          if (parsedMessage != null) {
+            mergeFrom(parsedMessage);
+          }
+        }
+        return this;
       }
-      hash = (29 * hash) + unknownFields.hashCode();
-      memoizedHashCode = hash;
-      return hash;
-    }
 
-    public static context.ContextOuterClass.ConnectionId parseFrom(
-        java.nio.ByteBuffer data)
-        throws com.google.protobuf.InvalidProtocolBufferException {
-      return PARSER.parseFrom(data);
-    }
-    public static context.ContextOuterClass.ConnectionId parseFrom(
-        java.nio.ByteBuffer data,
-        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
-        throws com.google.protobuf.InvalidProtocolBufferException {
-      return PARSER.parseFrom(data, extensionRegistry);
-    }
-    public static context.ContextOuterClass.ConnectionId parseFrom(
-        com.google.protobuf.ByteString data)
-        throws com.google.protobuf.InvalidProtocolBufferException {
-      return PARSER.parseFrom(data);
-    }
-    public static context.ContextOuterClass.ConnectionId parseFrom(
-        com.google.protobuf.ByteString data,
-        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
-        throws com.google.protobuf.InvalidProtocolBufferException {
-      return PARSER.parseFrom(data, extensionRegistry);
-    }
-    public static context.ContextOuterClass.ConnectionId parseFrom(byte[] data)
-        throws com.google.protobuf.InvalidProtocolBufferException {
-      return PARSER.parseFrom(data);
-    }
-    public static context.ContextOuterClass.ConnectionId parseFrom(
-        byte[] data,
-        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
-        throws com.google.protobuf.InvalidProtocolBufferException {
-      return PARSER.parseFrom(data, extensionRegistry);
-    }
-    public static context.ContextOuterClass.ConnectionId parseFrom(java.io.InputStream input)
-        throws java.io.IOException {
-      return com.google.protobuf.GeneratedMessageV3
-          .parseWithIOException(PARSER, input);
-    }
-    public static context.ContextOuterClass.ConnectionId parseFrom(
-        java.io.InputStream input,
-        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
-        throws java.io.IOException {
-      return com.google.protobuf.GeneratedMessageV3
-          .parseWithIOException(PARSER, input, extensionRegistry);
-    }
-    public static context.ContextOuterClass.ConnectionId parseDelimitedFrom(java.io.InputStream input)
-        throws java.io.IOException {
-      return com.google.protobuf.GeneratedMessageV3
-          .parseDelimitedWithIOException(PARSER, input);
-    }
-    public static context.ContextOuterClass.ConnectionId parseDelimitedFrom(
-        java.io.InputStream input,
-        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
-        throws java.io.IOException {
-      return com.google.protobuf.GeneratedMessageV3
-          .parseDelimitedWithIOException(PARSER, input, extensionRegistry);
-    }
-    public static context.ContextOuterClass.ConnectionId parseFrom(
-        com.google.protobuf.CodedInputStream input)
-        throws java.io.IOException {
-      return com.google.protobuf.GeneratedMessageV3
-          .parseWithIOException(PARSER, input);
-    }
-    public static context.ContextOuterClass.ConnectionId parseFrom(
-        com.google.protobuf.CodedInputStream input,
-        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
-        throws java.io.IOException {
-      return com.google.protobuf.GeneratedMessageV3
-          .parseWithIOException(PARSER, input, extensionRegistry);
-    }
+      private context.ContextOuterClass.TopologyId topologyId_;
+      private com.google.protobuf.SingleFieldBuilderV3<
+          context.ContextOuterClass.TopologyId, context.ContextOuterClass.TopologyId.Builder, context.ContextOuterClass.TopologyIdOrBuilder> topologyIdBuilder_;
+      /**
+       * <code>.context.TopologyId topology_id = 1;</code>
+       * @return Whether the topologyId field is set.
+       */
+      public boolean hasTopologyId() {
+        return topologyIdBuilder_ != null || topologyId_ != null;
+      }
+      /**
+       * <code>.context.TopologyId topology_id = 1;</code>
+       * @return The topologyId.
+       */
+      public context.ContextOuterClass.TopologyId getTopologyId() {
+        if (topologyIdBuilder_ == null) {
+          return topologyId_ == null ? context.ContextOuterClass.TopologyId.getDefaultInstance() : topologyId_;
+        } else {
+          return topologyIdBuilder_.getMessage();
+        }
+      }
+      /**
+       * <code>.context.TopologyId topology_id = 1;</code>
+       */
+      public Builder setTopologyId(context.ContextOuterClass.TopologyId value) {
+        if (topologyIdBuilder_ == null) {
+          if (value == null) {
+            throw new NullPointerException();
+          }
+          topologyId_ = value;
+          onChanged();
+        } else {
+          topologyIdBuilder_.setMessage(value);
+        }
 
-    @java.lang.Override
-    public Builder newBuilderForType() { return newBuilder(); }
-    public static Builder newBuilder() {
-      return DEFAULT_INSTANCE.toBuilder();
-    }
-    public static Builder newBuilder(context.ContextOuterClass.ConnectionId prototype) {
-      return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
-    }
-    @java.lang.Override
-    public Builder toBuilder() {
-      return this == DEFAULT_INSTANCE
-          ? new Builder() : new Builder().mergeFrom(this);
-    }
+        return this;
+      }
+      /**
+       * <code>.context.TopologyId topology_id = 1;</code>
+       */
+      public Builder setTopologyId(
+          context.ContextOuterClass.TopologyId.Builder builderForValue) {
+        if (topologyIdBuilder_ == null) {
+          topologyId_ = builderForValue.build();
+          onChanged();
+        } else {
+          topologyIdBuilder_.setMessage(builderForValue.build());
+        }
 
-    @java.lang.Override
-    protected Builder newBuilderForType(
-        com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
-      Builder builder = new Builder(parent);
-      return builder;
-    }
-    /**
-     * <pre>
-     * ----- Connection ----------------------------------------------------------------------------------------------------
-     * </pre>
-     *
-     * Protobuf type {@code context.ConnectionId}
-     */
-    public static final class Builder extends
-        com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
-        // @@protoc_insertion_point(builder_implements:context.ConnectionId)
-        context.ContextOuterClass.ConnectionIdOrBuilder {
-      public static final com.google.protobuf.Descriptors.Descriptor
-          getDescriptor() {
-        return context.ContextOuterClass.internal_static_context_ConnectionId_descriptor;
+        return this;
       }
+      /**
+       * <code>.context.TopologyId topology_id = 1;</code>
+       */
+      public Builder mergeTopologyId(context.ContextOuterClass.TopologyId value) {
+        if (topologyIdBuilder_ == null) {
+          if (topologyId_ != null) {
+            topologyId_ =
+              context.ContextOuterClass.TopologyId.newBuilder(topologyId_).mergeFrom(value).buildPartial();
+          } else {
+            topologyId_ = value;
+          }
+          onChanged();
+        } else {
+          topologyIdBuilder_.mergeFrom(value);
+        }
 
-      @java.lang.Override
-      protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
-          internalGetFieldAccessorTable() {
-        return context.ContextOuterClass.internal_static_context_ConnectionId_fieldAccessorTable
-            .ensureFieldAccessorsInitialized(
-                context.ContextOuterClass.ConnectionId.class, context.ContextOuterClass.ConnectionId.Builder.class);
+        return this;
       }
+      /**
+       * <code>.context.TopologyId topology_id = 1;</code>
+       */
+      public Builder clearTopologyId() {
+        if (topologyIdBuilder_ == null) {
+          topologyId_ = null;
+          onChanged();
+        } else {
+          topologyId_ = null;
+          topologyIdBuilder_ = null;
+        }
 
-      // Construct using context.ContextOuterClass.ConnectionId.newBuilder()
-      private Builder() {
-        maybeForceBuilderInitialization();
+        return this;
+      }
+      /**
+       * <code>.context.TopologyId topology_id = 1;</code>
+       */
+      public context.ContextOuterClass.TopologyId.Builder getTopologyIdBuilder() {
+        
+        onChanged();
+        return getTopologyIdFieldBuilder().getBuilder();
+      }
+      /**
+       * <code>.context.TopologyId topology_id = 1;</code>
+       */
+      public context.ContextOuterClass.TopologyIdOrBuilder getTopologyIdOrBuilder() {
+        if (topologyIdBuilder_ != null) {
+          return topologyIdBuilder_.getMessageOrBuilder();
+        } else {
+          return topologyId_ == null ?
+              context.ContextOuterClass.TopologyId.getDefaultInstance() : topologyId_;
+        }
+      }
+      /**
+       * <code>.context.TopologyId topology_id = 1;</code>
+       */
+      private com.google.protobuf.SingleFieldBuilderV3<
+          context.ContextOuterClass.TopologyId, context.ContextOuterClass.TopologyId.Builder, context.ContextOuterClass.TopologyIdOrBuilder> 
+          getTopologyIdFieldBuilder() {
+        if (topologyIdBuilder_ == null) {
+          topologyIdBuilder_ = new com.google.protobuf.SingleFieldBuilderV3<
+              context.ContextOuterClass.TopologyId, context.ContextOuterClass.TopologyId.Builder, context.ContextOuterClass.TopologyIdOrBuilder>(
+                  getTopologyId(),
+                  getParentForChildren(),
+                  isClean());
+          topologyId_ = null;
+        }
+        return topologyIdBuilder_;
       }
 
-      private Builder(
-          com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
-        super(parent);
-        maybeForceBuilderInitialization();
+      private context.ContextOuterClass.DeviceId deviceId_;
+      private com.google.protobuf.SingleFieldBuilderV3<
+          context.ContextOuterClass.DeviceId, context.ContextOuterClass.DeviceId.Builder, context.ContextOuterClass.DeviceIdOrBuilder> deviceIdBuilder_;
+      /**
+       * <code>.context.DeviceId device_id = 2;</code>
+       * @return Whether the deviceId field is set.
+       */
+      public boolean hasDeviceId() {
+        return deviceIdBuilder_ != null || deviceId_ != null;
       }
-      private void maybeForceBuilderInitialization() {
-        if (com.google.protobuf.GeneratedMessageV3
-                .alwaysUseFieldBuilders) {
+      /**
+       * <code>.context.DeviceId device_id = 2;</code>
+       * @return The deviceId.
+       */
+      public context.ContextOuterClass.DeviceId getDeviceId() {
+        if (deviceIdBuilder_ == null) {
+          return deviceId_ == null ? context.ContextOuterClass.DeviceId.getDefaultInstance() : deviceId_;
+        } else {
+          return deviceIdBuilder_.getMessage();
         }
       }
-      @java.lang.Override
-      public Builder clear() {
-        super.clear();
-        if (connectionUuidBuilder_ == null) {
-          connectionUuid_ = null;
+      /**
+       * <code>.context.DeviceId device_id = 2;</code>
+       */
+      public Builder setDeviceId(context.ContextOuterClass.DeviceId value) {
+        if (deviceIdBuilder_ == null) {
+          if (value == null) {
+            throw new NullPointerException();
+          }
+          deviceId_ = value;
+          onChanged();
         } else {
-          connectionUuid_ = null;
-          connectionUuidBuilder_ = null;
+          deviceIdBuilder_.setMessage(value);
         }
-        return this;
-      }
-
-      @java.lang.Override
-      public com.google.protobuf.Descriptors.Descriptor
-          getDescriptorForType() {
-        return context.ContextOuterClass.internal_static_context_ConnectionId_descriptor;
-      }
 
-      @java.lang.Override
-      public context.ContextOuterClass.ConnectionId getDefaultInstanceForType() {
-        return context.ContextOuterClass.ConnectionId.getDefaultInstance();
+        return this;
       }
-
-      @java.lang.Override
-      public context.ContextOuterClass.ConnectionId build() {
-        context.ContextOuterClass.ConnectionId result = buildPartial();
-        if (!result.isInitialized()) {
-          throw newUninitializedMessageException(result);
+      /**
+       * <code>.context.DeviceId device_id = 2;</code>
+       */
+      public Builder setDeviceId(
+          context.ContextOuterClass.DeviceId.Builder builderForValue) {
+        if (deviceIdBuilder_ == null) {
+          deviceId_ = builderForValue.build();
+          onChanged();
+        } else {
+          deviceIdBuilder_.setMessage(builderForValue.build());
         }
-        return result;
-      }
 
-      @java.lang.Override
-      public context.ContextOuterClass.ConnectionId buildPartial() {
-        context.ContextOuterClass.ConnectionId result = new context.ContextOuterClass.ConnectionId(this);
-        if (connectionUuidBuilder_ == null) {
-          result.connectionUuid_ = connectionUuid_;
+        return this;
+      }
+      /**
+       * <code>.context.DeviceId device_id = 2;</code>
+       */
+      public Builder mergeDeviceId(context.ContextOuterClass.DeviceId value) {
+        if (deviceIdBuilder_ == null) {
+          if (deviceId_ != null) {
+            deviceId_ =
+              context.ContextOuterClass.DeviceId.newBuilder(deviceId_).mergeFrom(value).buildPartial();
+          } else {
+            deviceId_ = value;
+          }
+          onChanged();
         } else {
-          result.connectionUuid_ = connectionUuidBuilder_.build();
+          deviceIdBuilder_.mergeFrom(value);
         }
-        onBuilt();
-        return result;
-      }
 
-      @java.lang.Override
-      public Builder clone() {
-        return super.clone();
-      }
-      @java.lang.Override
-      public Builder setField(
-          com.google.protobuf.Descriptors.FieldDescriptor field,
-          java.lang.Object value) {
-        return super.setField(field, value);
-      }
-      @java.lang.Override
-      public Builder clearField(
-          com.google.protobuf.Descriptors.FieldDescriptor field) {
-        return super.clearField(field);
-      }
-      @java.lang.Override
-      public Builder clearOneof(
-          com.google.protobuf.Descriptors.OneofDescriptor oneof) {
-        return super.clearOneof(oneof);
-      }
-      @java.lang.Override
-      public Builder setRepeatedField(
-          com.google.protobuf.Descriptors.FieldDescriptor field,
-          int index, java.lang.Object value) {
-        return super.setRepeatedField(field, index, value);
-      }
-      @java.lang.Override
-      public Builder addRepeatedField(
-          com.google.protobuf.Descriptors.FieldDescriptor field,
-          java.lang.Object value) {
-        return super.addRepeatedField(field, value);
+        return this;
       }
-      @java.lang.Override
-      public Builder mergeFrom(com.google.protobuf.Message other) {
-        if (other instanceof context.ContextOuterClass.ConnectionId) {
-          return mergeFrom((context.ContextOuterClass.ConnectionId)other);
+      /**
+       * <code>.context.DeviceId device_id = 2;</code>
+       */
+      public Builder clearDeviceId() {
+        if (deviceIdBuilder_ == null) {
+          deviceId_ = null;
+          onChanged();
         } else {
-          super.mergeFrom(other);
-          return this;
+          deviceId_ = null;
+          deviceIdBuilder_ = null;
         }
-      }
 
-      public Builder mergeFrom(context.ContextOuterClass.ConnectionId other) {
-        if (other == context.ContextOuterClass.ConnectionId.getDefaultInstance()) return this;
-        if (other.hasConnectionUuid()) {
-          mergeConnectionUuid(other.getConnectionUuid());
-        }
-        this.mergeUnknownFields(other.unknownFields);
-        onChanged();
         return this;
       }
-
-      @java.lang.Override
-      public final boolean isInitialized() {
-        return true;
+      /**
+       * <code>.context.DeviceId device_id = 2;</code>
+       */
+      public context.ContextOuterClass.DeviceId.Builder getDeviceIdBuilder() {
+        
+        onChanged();
+        return getDeviceIdFieldBuilder().getBuilder();
       }
-
-      @java.lang.Override
-      public Builder mergeFrom(
-          com.google.protobuf.CodedInputStream input,
-          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
-          throws java.io.IOException {
-        context.ContextOuterClass.ConnectionId parsedMessage = null;
-        try {
-          parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
-        } catch (com.google.protobuf.InvalidProtocolBufferException e) {
-          parsedMessage = (context.ContextOuterClass.ConnectionId) e.getUnfinishedMessage();
-          throw e.unwrapIOException();
-        } finally {
-          if (parsedMessage != null) {
-            mergeFrom(parsedMessage);
-          }
+      /**
+       * <code>.context.DeviceId device_id = 2;</code>
+       */
+      public context.ContextOuterClass.DeviceIdOrBuilder getDeviceIdOrBuilder() {
+        if (deviceIdBuilder_ != null) {
+          return deviceIdBuilder_.getMessageOrBuilder();
+        } else {
+          return deviceId_ == null ?
+              context.ContextOuterClass.DeviceId.getDefaultInstance() : deviceId_;
         }
-        return this;
+      }
+      /**
+       * <code>.context.DeviceId device_id = 2;</code>
+       */
+      private com.google.protobuf.SingleFieldBuilderV3<
+          context.ContextOuterClass.DeviceId, context.ContextOuterClass.DeviceId.Builder, context.ContextOuterClass.DeviceIdOrBuilder> 
+          getDeviceIdFieldBuilder() {
+        if (deviceIdBuilder_ == null) {
+          deviceIdBuilder_ = new com.google.protobuf.SingleFieldBuilderV3<
+              context.ContextOuterClass.DeviceId, context.ContextOuterClass.DeviceId.Builder, context.ContextOuterClass.DeviceIdOrBuilder>(
+                  getDeviceId(),
+                  getParentForChildren(),
+                  isClean());
+          deviceId_ = null;
+        }
+        return deviceIdBuilder_;
       }
 
-      private context.ContextOuterClass.Uuid connectionUuid_;
+      private context.ContextOuterClass.Uuid endpointUuid_;
       private com.google.protobuf.SingleFieldBuilderV3<
-          context.ContextOuterClass.Uuid, context.ContextOuterClass.Uuid.Builder, context.ContextOuterClass.UuidOrBuilder> connectionUuidBuilder_;
+          context.ContextOuterClass.Uuid, context.ContextOuterClass.Uuid.Builder, context.ContextOuterClass.UuidOrBuilder> endpointUuidBuilder_;
       /**
-       * <code>.context.Uuid connection_uuid = 1;</code>
-       * @return Whether the connectionUuid field is set.
+       * <code>.context.Uuid endpoint_uuid = 3;</code>
+       * @return Whether the endpointUuid field is set.
        */
-      public boolean hasConnectionUuid() {
-        return connectionUuidBuilder_ != null || connectionUuid_ != null;
+      public boolean hasEndpointUuid() {
+        return endpointUuidBuilder_ != null || endpointUuid_ != null;
       }
       /**
-       * <code>.context.Uuid connection_uuid = 1;</code>
-       * @return The connectionUuid.
+       * <code>.context.Uuid endpoint_uuid = 3;</code>
+       * @return The endpointUuid.
        */
-      public context.ContextOuterClass.Uuid getConnectionUuid() {
-        if (connectionUuidBuilder_ == null) {
-          return connectionUuid_ == null ? context.ContextOuterClass.Uuid.getDefaultInstance() : connectionUuid_;
+      public context.ContextOuterClass.Uuid getEndpointUuid() {
+        if (endpointUuidBuilder_ == null) {
+          return endpointUuid_ == null ? context.ContextOuterClass.Uuid.getDefaultInstance() : endpointUuid_;
         } else {
-          return connectionUuidBuilder_.getMessage();
+          return endpointUuidBuilder_.getMessage();
         }
       }
       /**
-       * <code>.context.Uuid connection_uuid = 1;</code>
+       * <code>.context.Uuid endpoint_uuid = 3;</code>
        */
-      public Builder setConnectionUuid(context.ContextOuterClass.Uuid value) {
-        if (connectionUuidBuilder_ == null) {
+      public Builder setEndpointUuid(context.ContextOuterClass.Uuid value) {
+        if (endpointUuidBuilder_ == null) {
           if (value == null) {
             throw new NullPointerException();
           }
-          connectionUuid_ = value;
+          endpointUuid_ = value;
           onChanged();
         } else {
-          connectionUuidBuilder_.setMessage(value);
+          endpointUuidBuilder_.setMessage(value);
         }
 
         return this;
       }
       /**
-       * <code>.context.Uuid connection_uuid = 1;</code>
+       * <code>.context.Uuid endpoint_uuid = 3;</code>
        */
-      public Builder setConnectionUuid(
+      public Builder setEndpointUuid(
           context.ContextOuterClass.Uuid.Builder builderForValue) {
-        if (connectionUuidBuilder_ == null) {
-          connectionUuid_ = builderForValue.build();
+        if (endpointUuidBuilder_ == null) {
+          endpointUuid_ = builderForValue.build();
           onChanged();
         } else {
-          connectionUuidBuilder_.setMessage(builderForValue.build());
+          endpointUuidBuilder_.setMessage(builderForValue.build());
         }
 
         return this;
       }
       /**
-       * <code>.context.Uuid connection_uuid = 1;</code>
+       * <code>.context.Uuid endpoint_uuid = 3;</code>
        */
-      public Builder mergeConnectionUuid(context.ContextOuterClass.Uuid value) {
-        if (connectionUuidBuilder_ == null) {
-          if (connectionUuid_ != null) {
-            connectionUuid_ =
-              context.ContextOuterClass.Uuid.newBuilder(connectionUuid_).mergeFrom(value).buildPartial();
+      public Builder mergeEndpointUuid(context.ContextOuterClass.Uuid value) {
+        if (endpointUuidBuilder_ == null) {
+          if (endpointUuid_ != null) {
+            endpointUuid_ =
+              context.ContextOuterClass.Uuid.newBuilder(endpointUuid_).mergeFrom(value).buildPartial();
           } else {
-            connectionUuid_ = value;
+            endpointUuid_ = value;
           }
           onChanged();
         } else {
-          connectionUuidBuilder_.mergeFrom(value);
+          endpointUuidBuilder_.mergeFrom(value);
         }
 
         return this;
       }
       /**
-       * <code>.context.Uuid connection_uuid = 1;</code>
+       * <code>.context.Uuid endpoint_uuid = 3;</code>
        */
-      public Builder clearConnectionUuid() {
-        if (connectionUuidBuilder_ == null) {
-          connectionUuid_ = null;
+      public Builder clearEndpointUuid() {
+        if (endpointUuidBuilder_ == null) {
+          endpointUuid_ = null;
           onChanged();
         } else {
-          connectionUuid_ = null;
-          connectionUuidBuilder_ = null;
+          endpointUuid_ = null;
+          endpointUuidBuilder_ = null;
         }
 
         return this;
       }
       /**
-       * <code>.context.Uuid connection_uuid = 1;</code>
+       * <code>.context.Uuid endpoint_uuid = 3;</code>
        */
-      public context.ContextOuterClass.Uuid.Builder getConnectionUuidBuilder() {
+      public context.ContextOuterClass.Uuid.Builder getEndpointUuidBuilder() {
         
         onChanged();
-        return getConnectionUuidFieldBuilder().getBuilder();
+        return getEndpointUuidFieldBuilder().getBuilder();
       }
       /**
-       * <code>.context.Uuid connection_uuid = 1;</code>
+       * <code>.context.Uuid endpoint_uuid = 3;</code>
        */
-      public context.ContextOuterClass.UuidOrBuilder getConnectionUuidOrBuilder() {
-        if (connectionUuidBuilder_ != null) {
-          return connectionUuidBuilder_.getMessageOrBuilder();
+      public context.ContextOuterClass.UuidOrBuilder getEndpointUuidOrBuilder() {
+        if (endpointUuidBuilder_ != null) {
+          return endpointUuidBuilder_.getMessageOrBuilder();
         } else {
-          return connectionUuid_ == null ?
-              context.ContextOuterClass.Uuid.getDefaultInstance() : connectionUuid_;
+          return endpointUuid_ == null ?
+              context.ContextOuterClass.Uuid.getDefaultInstance() : endpointUuid_;
         }
       }
       /**
-       * <code>.context.Uuid connection_uuid = 1;</code>
+       * <code>.context.Uuid endpoint_uuid = 3;</code>
        */
       private com.google.protobuf.SingleFieldBuilderV3<
           context.ContextOuterClass.Uuid, context.ContextOuterClass.Uuid.Builder, context.ContextOuterClass.UuidOrBuilder> 
-          getConnectionUuidFieldBuilder() {
-        if (connectionUuidBuilder_ == null) {
-          connectionUuidBuilder_ = new com.google.protobuf.SingleFieldBuilderV3<
+          getEndpointUuidFieldBuilder() {
+        if (endpointUuidBuilder_ == null) {
+          endpointUuidBuilder_ = new com.google.protobuf.SingleFieldBuilderV3<
               context.ContextOuterClass.Uuid, context.ContextOuterClass.Uuid.Builder, context.ContextOuterClass.UuidOrBuilder>(
-                  getConnectionUuid(),
+                  getEndpointUuid(),
                   getParentForChildren(),
                   isClean());
-          connectionUuid_ = null;
+          endpointUuid_ = null;
         }
-        return connectionUuidBuilder_;
+        return endpointUuidBuilder_;
       }
       @java.lang.Override
       public final Builder setUnknownFields(
@@ -31599,125 +33258,128 @@ public final class ContextOuterClass {
       }
 
 
-      // @@protoc_insertion_point(builder_scope:context.ConnectionId)
+      // @@protoc_insertion_point(builder_scope:context.EndPointId)
     }
 
-    // @@protoc_insertion_point(class_scope:context.ConnectionId)
-    private static final context.ContextOuterClass.ConnectionId DEFAULT_INSTANCE;
+    // @@protoc_insertion_point(class_scope:context.EndPointId)
+    private static final context.ContextOuterClass.EndPointId DEFAULT_INSTANCE;
     static {
-      DEFAULT_INSTANCE = new context.ContextOuterClass.ConnectionId();
+      DEFAULT_INSTANCE = new context.ContextOuterClass.EndPointId();
     }
 
-    public static context.ContextOuterClass.ConnectionId getDefaultInstance() {
+    public static context.ContextOuterClass.EndPointId getDefaultInstance() {
       return DEFAULT_INSTANCE;
     }
 
-    private static final com.google.protobuf.Parser<ConnectionId>
-        PARSER = new com.google.protobuf.AbstractParser<ConnectionId>() {
+    private static final com.google.protobuf.Parser<EndPointId>
+        PARSER = new com.google.protobuf.AbstractParser<EndPointId>() {
       @java.lang.Override
-      public ConnectionId parsePartialFrom(
+      public EndPointId parsePartialFrom(
           com.google.protobuf.CodedInputStream input,
           com.google.protobuf.ExtensionRegistryLite extensionRegistry)
           throws com.google.protobuf.InvalidProtocolBufferException {
-        return new ConnectionId(input, extensionRegistry);
+        return new EndPointId(input, extensionRegistry);
       }
     };
 
-    public static com.google.protobuf.Parser<ConnectionId> parser() {
+    public static com.google.protobuf.Parser<EndPointId> parser() {
       return PARSER;
     }
 
     @java.lang.Override
-    public com.google.protobuf.Parser<ConnectionId> getParserForType() {
+    public com.google.protobuf.Parser<EndPointId> getParserForType() {
       return PARSER;
     }
 
     @java.lang.Override
-    public context.ContextOuterClass.ConnectionId getDefaultInstanceForType() {
+    public context.ContextOuterClass.EndPointId getDefaultInstanceForType() {
       return DEFAULT_INSTANCE;
     }
 
   }
 
-  public interface ConnectionOrBuilder extends
-      // @@protoc_insertion_point(interface_extends:context.Connection)
+  public interface EndPointOrBuilder extends
+      // @@protoc_insertion_point(interface_extends:context.EndPoint)
       com.google.protobuf.MessageOrBuilder {
 
     /**
-     * <code>.context.ConnectionId connection_id = 1;</code>
-     * @return Whether the connectionId field is set.
+     * <code>.context.EndPointId endpoint_id = 1;</code>
+     * @return Whether the endpointId field is set.
      */
-    boolean hasConnectionId();
+    boolean hasEndpointId();
     /**
-     * <code>.context.ConnectionId connection_id = 1;</code>
-     * @return The connectionId.
+     * <code>.context.EndPointId endpoint_id = 1;</code>
+     * @return The endpointId.
      */
-    context.ContextOuterClass.ConnectionId getConnectionId();
+    context.ContextOuterClass.EndPointId getEndpointId();
     /**
-     * <code>.context.ConnectionId connection_id = 1;</code>
+     * <code>.context.EndPointId endpoint_id = 1;</code>
      */
-    context.ContextOuterClass.ConnectionIdOrBuilder getConnectionIdOrBuilder();
+    context.ContextOuterClass.EndPointIdOrBuilder getEndpointIdOrBuilder();
 
     /**
-     * <code>.context.ServiceId related_service_id = 2;</code>
-     * @return Whether the relatedServiceId field is set.
-     */
-    boolean hasRelatedServiceId();
-    /**
-     * <code>.context.ServiceId related_service_id = 2;</code>
-     * @return The relatedServiceId.
+     * <code>string endpoint_type = 2;</code>
+     * @return The endpointType.
      */
-    context.ContextOuterClass.ServiceId getRelatedServiceId();
+    java.lang.String getEndpointType();
     /**
-     * <code>.context.ServiceId related_service_id = 2;</code>
+     * <code>string endpoint_type = 2;</code>
+     * @return The bytes for endpointType.
      */
-    context.ContextOuterClass.ServiceIdOrBuilder getRelatedServiceIdOrBuilder();
+    com.google.protobuf.ByteString
+        getEndpointTypeBytes();
 
     /**
-     * <code>repeated .context.EndPointId path = 3;</code>
+     * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
+     * @return A list containing the kpiSampleTypes.
      */
-    java.util.List<context.ContextOuterClass.EndPointId> 
-        getPathList();
+    java.util.List<kpi_sample_types.KpiSampleTypes.KpiSampleType> getKpiSampleTypesList();
     /**
-     * <code>repeated .context.EndPointId path = 3;</code>
+     * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
+     * @return The count of kpiSampleTypes.
      */
-    context.ContextOuterClass.EndPointId getPath(int index);
+    int getKpiSampleTypesCount();
     /**
-     * <code>repeated .context.EndPointId path = 3;</code>
+     * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
+     * @param index The index of the element to return.
+     * @return The kpiSampleTypes at the given index.
      */
-    int getPathCount();
+    kpi_sample_types.KpiSampleTypes.KpiSampleType getKpiSampleTypes(int index);
     /**
-     * <code>repeated .context.EndPointId path = 3;</code>
+     * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
+     * @return A list containing the enum numeric values on the wire for kpiSampleTypes.
      */
-    java.util.List<? extends context.ContextOuterClass.EndPointIdOrBuilder> 
-        getPathOrBuilderList();
+    java.util.List<java.lang.Integer>
+    getKpiSampleTypesValueList();
     /**
-     * <code>repeated .context.EndPointId path = 3;</code>
+     * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
+     * @param index The index of the value to return.
+     * @return The enum numeric value on the wire of kpiSampleTypes at the given index.
      */
-    context.ContextOuterClass.EndPointIdOrBuilder getPathOrBuilder(
-        int index);
+    int getKpiSampleTypesValue(int index);
   }
   /**
-   * Protobuf type {@code context.Connection}
+   * Protobuf type {@code context.EndPoint}
    */
-  public static final class Connection extends
+  public static final class EndPoint extends
       com.google.protobuf.GeneratedMessageV3 implements
-      // @@protoc_insertion_point(message_implements:context.Connection)
-      ConnectionOrBuilder {
+      // @@protoc_insertion_point(message_implements:context.EndPoint)
+      EndPointOrBuilder {
   private static final long serialVersionUID = 0L;
-    // Use Connection.newBuilder() to construct.
-    private Connection(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
+    // Use EndPoint.newBuilder() to construct.
+    private EndPoint(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
       super(builder);
     }
-    private Connection() {
-      path_ = java.util.Collections.emptyList();
+    private EndPoint() {
+      endpointType_ = "";
+      kpiSampleTypes_ = java.util.Collections.emptyList();
     }
 
     @java.lang.Override
     @SuppressWarnings({"unused"})
     protected java.lang.Object newInstance(
         UnusedPrivateParameter unused) {
-      return new Connection();
+      return new EndPoint();
     }
 
     @java.lang.Override
@@ -31725,7 +33387,7 @@ public final class ContextOuterClass {
     getUnknownFields() {
       return this.unknownFields;
     }
-    private Connection(
+    private EndPoint(
         com.google.protobuf.CodedInputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
@@ -31745,38 +33407,45 @@ public final class ContextOuterClass {
               done = true;
               break;
             case 10: {
-              context.ContextOuterClass.ConnectionId.Builder subBuilder = null;
-              if (connectionId_ != null) {
-                subBuilder = connectionId_.toBuilder();
+              context.ContextOuterClass.EndPointId.Builder subBuilder = null;
+              if (endpointId_ != null) {
+                subBuilder = endpointId_.toBuilder();
               }
-              connectionId_ = input.readMessage(context.ContextOuterClass.ConnectionId.parser(), extensionRegistry);
+              endpointId_ = input.readMessage(context.ContextOuterClass.EndPointId.parser(), extensionRegistry);
               if (subBuilder != null) {
-                subBuilder.mergeFrom(connectionId_);
-                connectionId_ = subBuilder.buildPartial();
+                subBuilder.mergeFrom(endpointId_);
+                endpointId_ = subBuilder.buildPartial();
               }
 
               break;
             }
             case 18: {
-              context.ContextOuterClass.ServiceId.Builder subBuilder = null;
-              if (relatedServiceId_ != null) {
-                subBuilder = relatedServiceId_.toBuilder();
-              }
-              relatedServiceId_ = input.readMessage(context.ContextOuterClass.ServiceId.parser(), extensionRegistry);
-              if (subBuilder != null) {
-                subBuilder.mergeFrom(relatedServiceId_);
-                relatedServiceId_ = subBuilder.buildPartial();
-              }
+              java.lang.String s = input.readStringRequireUtf8();
 
+              endpointType_ = s;
               break;
             }
-            case 26: {
+            case 24: {
+              int rawValue = input.readEnum();
               if (!((mutable_bitField0_ & 0x00000001) != 0)) {
-                path_ = new java.util.ArrayList<context.ContextOuterClass.EndPointId>();
+                kpiSampleTypes_ = new java.util.ArrayList<java.lang.Integer>();
                 mutable_bitField0_ |= 0x00000001;
               }
-              path_.add(
-                  input.readMessage(context.ContextOuterClass.EndPointId.parser(), extensionRegistry));
+              kpiSampleTypes_.add(rawValue);
+              break;
+            }
+            case 26: {
+              int length = input.readRawVarint32();
+              int oldLimit = input.pushLimit(length);
+              while(input.getBytesUntilLimit() > 0) {
+                int rawValue = input.readEnum();
+                if (!((mutable_bitField0_ & 0x00000001) != 0)) {
+                  kpiSampleTypes_ = new java.util.ArrayList<java.lang.Integer>();
+                  mutable_bitField0_ |= 0x00000001;
+                }
+                kpiSampleTypes_.add(rawValue);
+              }
+              input.popLimit(oldLimit);
               break;
             }
             default: {
@@ -31795,7 +33464,7 @@ public final class ContextOuterClass {
             e).setUnfinishedMessage(this);
       } finally {
         if (((mutable_bitField0_ & 0x00000001) != 0)) {
-          path_ = java.util.Collections.unmodifiableList(path_);
+          kpiSampleTypes_ = java.util.Collections.unmodifiableList(kpiSampleTypes_);
         }
         this.unknownFields = unknownFields.build();
         makeExtensionsImmutable();
@@ -31803,108 +33472,138 @@ public final class ContextOuterClass {
     }
     public static final com.google.protobuf.Descriptors.Descriptor
         getDescriptor() {
-      return context.ContextOuterClass.internal_static_context_Connection_descriptor;
+      return context.ContextOuterClass.internal_static_context_EndPoint_descriptor;
     }
 
     @java.lang.Override
     protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
         internalGetFieldAccessorTable() {
-      return context.ContextOuterClass.internal_static_context_Connection_fieldAccessorTable
+      return context.ContextOuterClass.internal_static_context_EndPoint_fieldAccessorTable
           .ensureFieldAccessorsInitialized(
-              context.ContextOuterClass.Connection.class, context.ContextOuterClass.Connection.Builder.class);
+              context.ContextOuterClass.EndPoint.class, context.ContextOuterClass.EndPoint.Builder.class);
     }
 
-    public static final int CONNECTION_ID_FIELD_NUMBER = 1;
-    private context.ContextOuterClass.ConnectionId connectionId_;
+    public static final int ENDPOINT_ID_FIELD_NUMBER = 1;
+    private context.ContextOuterClass.EndPointId endpointId_;
     /**
-     * <code>.context.ConnectionId connection_id = 1;</code>
-     * @return Whether the connectionId field is set.
+     * <code>.context.EndPointId endpoint_id = 1;</code>
+     * @return Whether the endpointId field is set.
      */
     @java.lang.Override
-    public boolean hasConnectionId() {
-      return connectionId_ != null;
+    public boolean hasEndpointId() {
+      return endpointId_ != null;
     }
     /**
-     * <code>.context.ConnectionId connection_id = 1;</code>
-     * @return The connectionId.
+     * <code>.context.EndPointId endpoint_id = 1;</code>
+     * @return The endpointId.
      */
     @java.lang.Override
-    public context.ContextOuterClass.ConnectionId getConnectionId() {
-      return connectionId_ == null ? context.ContextOuterClass.ConnectionId.getDefaultInstance() : connectionId_;
+    public context.ContextOuterClass.EndPointId getEndpointId() {
+      return endpointId_ == null ? context.ContextOuterClass.EndPointId.getDefaultInstance() : endpointId_;
     }
     /**
-     * <code>.context.ConnectionId connection_id = 1;</code>
+     * <code>.context.EndPointId endpoint_id = 1;</code>
      */
     @java.lang.Override
-    public context.ContextOuterClass.ConnectionIdOrBuilder getConnectionIdOrBuilder() {
-      return getConnectionId();
+    public context.ContextOuterClass.EndPointIdOrBuilder getEndpointIdOrBuilder() {
+      return getEndpointId();
     }
 
-    public static final int RELATED_SERVICE_ID_FIELD_NUMBER = 2;
-    private context.ContextOuterClass.ServiceId relatedServiceId_;
-    /**
-     * <code>.context.ServiceId related_service_id = 2;</code>
-     * @return Whether the relatedServiceId field is set.
-     */
-    @java.lang.Override
-    public boolean hasRelatedServiceId() {
-      return relatedServiceId_ != null;
-    }
+    public static final int ENDPOINT_TYPE_FIELD_NUMBER = 2;
+    private volatile java.lang.Object endpointType_;
     /**
-     * <code>.context.ServiceId related_service_id = 2;</code>
-     * @return The relatedServiceId.
+     * <code>string endpoint_type = 2;</code>
+     * @return The endpointType.
      */
     @java.lang.Override
-    public context.ContextOuterClass.ServiceId getRelatedServiceId() {
-      return relatedServiceId_ == null ? context.ContextOuterClass.ServiceId.getDefaultInstance() : relatedServiceId_;
+    public java.lang.String getEndpointType() {
+      java.lang.Object ref = endpointType_;
+      if (ref instanceof java.lang.String) {
+        return (java.lang.String) ref;
+      } else {
+        com.google.protobuf.ByteString bs = 
+            (com.google.protobuf.ByteString) ref;
+        java.lang.String s = bs.toStringUtf8();
+        endpointType_ = s;
+        return s;
+      }
     }
     /**
-     * <code>.context.ServiceId related_service_id = 2;</code>
+     * <code>string endpoint_type = 2;</code>
+     * @return The bytes for endpointType.
      */
     @java.lang.Override
-    public context.ContextOuterClass.ServiceIdOrBuilder getRelatedServiceIdOrBuilder() {
-      return getRelatedServiceId();
+    public com.google.protobuf.ByteString
+        getEndpointTypeBytes() {
+      java.lang.Object ref = endpointType_;
+      if (ref instanceof java.lang.String) {
+        com.google.protobuf.ByteString b = 
+            com.google.protobuf.ByteString.copyFromUtf8(
+                (java.lang.String) ref);
+        endpointType_ = b;
+        return b;
+      } else {
+        return (com.google.protobuf.ByteString) ref;
+      }
     }
 
-    public static final int PATH_FIELD_NUMBER = 3;
-    private java.util.List<context.ContextOuterClass.EndPointId> path_;
+    public static final int KPI_SAMPLE_TYPES_FIELD_NUMBER = 3;
+    private java.util.List<java.lang.Integer> kpiSampleTypes_;
+    private static final com.google.protobuf.Internal.ListAdapter.Converter<
+        java.lang.Integer, kpi_sample_types.KpiSampleTypes.KpiSampleType> kpiSampleTypes_converter_ =
+            new com.google.protobuf.Internal.ListAdapter.Converter<
+                java.lang.Integer, kpi_sample_types.KpiSampleTypes.KpiSampleType>() {
+              public kpi_sample_types.KpiSampleTypes.KpiSampleType convert(java.lang.Integer from) {
+                @SuppressWarnings("deprecation")
+                kpi_sample_types.KpiSampleTypes.KpiSampleType result = kpi_sample_types.KpiSampleTypes.KpiSampleType.valueOf(from);
+                return result == null ? kpi_sample_types.KpiSampleTypes.KpiSampleType.UNRECOGNIZED : result;
+              }
+            };
     /**
-     * <code>repeated .context.EndPointId path = 3;</code>
+     * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
+     * @return A list containing the kpiSampleTypes.
      */
     @java.lang.Override
-    public java.util.List<context.ContextOuterClass.EndPointId> getPathList() {
-      return path_;
+    public java.util.List<kpi_sample_types.KpiSampleTypes.KpiSampleType> getKpiSampleTypesList() {
+      return new com.google.protobuf.Internal.ListAdapter<
+          java.lang.Integer, kpi_sample_types.KpiSampleTypes.KpiSampleType>(kpiSampleTypes_, kpiSampleTypes_converter_);
     }
     /**
-     * <code>repeated .context.EndPointId path = 3;</code>
+     * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
+     * @return The count of kpiSampleTypes.
      */
     @java.lang.Override
-    public java.util.List<? extends context.ContextOuterClass.EndPointIdOrBuilder> 
-        getPathOrBuilderList() {
-      return path_;
+    public int getKpiSampleTypesCount() {
+      return kpiSampleTypes_.size();
     }
     /**
-     * <code>repeated .context.EndPointId path = 3;</code>
+     * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
+     * @param index The index of the element to return.
+     * @return The kpiSampleTypes at the given index.
      */
     @java.lang.Override
-    public int getPathCount() {
-      return path_.size();
+    public kpi_sample_types.KpiSampleTypes.KpiSampleType getKpiSampleTypes(int index) {
+      return kpiSampleTypes_converter_.convert(kpiSampleTypes_.get(index));
     }
     /**
-     * <code>repeated .context.EndPointId path = 3;</code>
+     * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
+     * @return A list containing the enum numeric values on the wire for kpiSampleTypes.
      */
     @java.lang.Override
-    public context.ContextOuterClass.EndPointId getPath(int index) {
-      return path_.get(index);
+    public java.util.List<java.lang.Integer>
+    getKpiSampleTypesValueList() {
+      return kpiSampleTypes_;
     }
     /**
-     * <code>repeated .context.EndPointId path = 3;</code>
+     * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
+     * @param index The index of the value to return.
+     * @return The enum numeric value on the wire of kpiSampleTypes at the given index.
      */
     @java.lang.Override
-    public context.ContextOuterClass.EndPointIdOrBuilder getPathOrBuilder(
-        int index) {
-      return path_.get(index);
+    public int getKpiSampleTypesValue(int index) {
+      return kpiSampleTypes_.get(index);
     }
+    private int kpiSampleTypesMemoizedSerializedSize;
 
     private byte memoizedIsInitialized = -1;
     @java.lang.Override
@@ -31920,14 +33619,19 @@ public final class ContextOuterClass {
     @java.lang.Override
     public void writeTo(com.google.protobuf.CodedOutputStream output)
                         throws java.io.IOException {
-      if (connectionId_ != null) {
-        output.writeMessage(1, getConnectionId());
+      getSerializedSize();
+      if (endpointId_ != null) {
+        output.writeMessage(1, getEndpointId());
+      }
+      if (!getEndpointTypeBytes().isEmpty()) {
+        com.google.protobuf.GeneratedMessageV3.writeString(output, 2, endpointType_);
       }
-      if (relatedServiceId_ != null) {
-        output.writeMessage(2, getRelatedServiceId());
+      if (getKpiSampleTypesList().size() > 0) {
+        output.writeUInt32NoTag(26);
+        output.writeUInt32NoTag(kpiSampleTypesMemoizedSerializedSize);
       }
-      for (int i = 0; i < path_.size(); i++) {
-        output.writeMessage(3, path_.get(i));
+      for (int i = 0; i < kpiSampleTypes_.size(); i++) {
+        output.writeEnumNoTag(kpiSampleTypes_.get(i));
       }
       unknownFields.writeTo(output);
     }
@@ -31938,17 +33642,24 @@ public final class ContextOuterClass {
       if (size != -1) return size;
 
       size = 0;
-      if (connectionId_ != null) {
+      if (endpointId_ != null) {
         size += com.google.protobuf.CodedOutputStream
-          .computeMessageSize(1, getConnectionId());
+          .computeMessageSize(1, getEndpointId());
       }
-      if (relatedServiceId_ != null) {
-        size += com.google.protobuf.CodedOutputStream
-          .computeMessageSize(2, getRelatedServiceId());
+      if (!getEndpointTypeBytes().isEmpty()) {
+        size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, endpointType_);
       }
-      for (int i = 0; i < path_.size(); i++) {
-        size += com.google.protobuf.CodedOutputStream
-          .computeMessageSize(3, path_.get(i));
+      {
+        int dataSize = 0;
+        for (int i = 0; i < kpiSampleTypes_.size(); i++) {
+          dataSize += com.google.protobuf.CodedOutputStream
+            .computeEnumSizeNoTag(kpiSampleTypes_.get(i));
+        }
+        size += dataSize;
+        if (!getKpiSampleTypesList().isEmpty()) {  size += 1;
+          size += com.google.protobuf.CodedOutputStream
+            .computeUInt32SizeNoTag(dataSize);
+        }kpiSampleTypesMemoizedSerializedSize = dataSize;
       }
       size += unknownFields.getSerializedSize();
       memoizedSize = size;
@@ -31960,23 +33671,19 @@ public final class ContextOuterClass {
       if (obj == this) {
        return true;
       }
-      if (!(obj instanceof context.ContextOuterClass.Connection)) {
+      if (!(obj instanceof context.ContextOuterClass.EndPoint)) {
         return super.equals(obj);
       }
-      context.ContextOuterClass.Connection other = (context.ContextOuterClass.Connection) obj;
+      context.ContextOuterClass.EndPoint other = (context.ContextOuterClass.EndPoint) obj;
 
-      if (hasConnectionId() != other.hasConnectionId()) return false;
-      if (hasConnectionId()) {
-        if (!getConnectionId()
-            .equals(other.getConnectionId())) return false;
-      }
-      if (hasRelatedServiceId() != other.hasRelatedServiceId()) return false;
-      if (hasRelatedServiceId()) {
-        if (!getRelatedServiceId()
-            .equals(other.getRelatedServiceId())) return false;
+      if (hasEndpointId() != other.hasEndpointId()) return false;
+      if (hasEndpointId()) {
+        if (!getEndpointId()
+            .equals(other.getEndpointId())) return false;
       }
-      if (!getPathList()
-          .equals(other.getPathList())) return false;
+      if (!getEndpointType()
+          .equals(other.getEndpointType())) return false;
+      if (!kpiSampleTypes_.equals(other.kpiSampleTypes_)) return false;
       if (!unknownFields.equals(other.unknownFields)) return false;
       return true;
     }
@@ -31988,86 +33695,84 @@ public final class ContextOuterClass {
       }
       int hash = 41;
       hash = (19 * hash) + getDescriptor().hashCode();
-      if (hasConnectionId()) {
-        hash = (37 * hash) + CONNECTION_ID_FIELD_NUMBER;
-        hash = (53 * hash) + getConnectionId().hashCode();
-      }
-      if (hasRelatedServiceId()) {
-        hash = (37 * hash) + RELATED_SERVICE_ID_FIELD_NUMBER;
-        hash = (53 * hash) + getRelatedServiceId().hashCode();
+      if (hasEndpointId()) {
+        hash = (37 * hash) + ENDPOINT_ID_FIELD_NUMBER;
+        hash = (53 * hash) + getEndpointId().hashCode();
       }
-      if (getPathCount() > 0) {
-        hash = (37 * hash) + PATH_FIELD_NUMBER;
-        hash = (53 * hash) + getPathList().hashCode();
+      hash = (37 * hash) + ENDPOINT_TYPE_FIELD_NUMBER;
+      hash = (53 * hash) + getEndpointType().hashCode();
+      if (getKpiSampleTypesCount() > 0) {
+        hash = (37 * hash) + KPI_SAMPLE_TYPES_FIELD_NUMBER;
+        hash = (53 * hash) + kpiSampleTypes_.hashCode();
       }
       hash = (29 * hash) + unknownFields.hashCode();
       memoizedHashCode = hash;
       return hash;
     }
 
-    public static context.ContextOuterClass.Connection parseFrom(
+    public static context.ContextOuterClass.EndPoint parseFrom(
         java.nio.ByteBuffer data)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data);
     }
-    public static context.ContextOuterClass.Connection parseFrom(
+    public static context.ContextOuterClass.EndPoint parseFrom(
         java.nio.ByteBuffer data,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data, extensionRegistry);
     }
-    public static context.ContextOuterClass.Connection parseFrom(
+    public static context.ContextOuterClass.EndPoint parseFrom(
         com.google.protobuf.ByteString data)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data);
     }
-    public static context.ContextOuterClass.Connection parseFrom(
+    public static context.ContextOuterClass.EndPoint parseFrom(
         com.google.protobuf.ByteString data,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data, extensionRegistry);
     }
-    public static context.ContextOuterClass.Connection parseFrom(byte[] data)
+    public static context.ContextOuterClass.EndPoint parseFrom(byte[] data)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data);
     }
-    public static context.ContextOuterClass.Connection parseFrom(
+    public static context.ContextOuterClass.EndPoint parseFrom(
         byte[] data,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data, extensionRegistry);
     }
-    public static context.ContextOuterClass.Connection parseFrom(java.io.InputStream input)
+    public static context.ContextOuterClass.EndPoint parseFrom(java.io.InputStream input)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseWithIOException(PARSER, input);
     }
-    public static context.ContextOuterClass.Connection parseFrom(
+    public static context.ContextOuterClass.EndPoint parseFrom(
         java.io.InputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseWithIOException(PARSER, input, extensionRegistry);
     }
-    public static context.ContextOuterClass.Connection parseDelimitedFrom(java.io.InputStream input)
+    public static context.ContextOuterClass.EndPoint parseDelimitedFrom(java.io.InputStream input)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseDelimitedWithIOException(PARSER, input);
     }
-    public static context.ContextOuterClass.Connection parseDelimitedFrom(
+    public static context.ContextOuterClass.EndPoint parseDelimitedFrom(
         java.io.InputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseDelimitedWithIOException(PARSER, input, extensionRegistry);
     }
-    public static context.ContextOuterClass.Connection parseFrom(
+    public static context.ContextOuterClass.EndPoint parseFrom(
         com.google.protobuf.CodedInputStream input)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseWithIOException(PARSER, input);
     }
-    public static context.ContextOuterClass.Connection parseFrom(
+    public static context.ContextOuterClass.EndPoint parseFrom(
         com.google.protobuf.CodedInputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws java.io.IOException {
@@ -32080,7 +33785,7 @@ public final class ContextOuterClass {
     public static Builder newBuilder() {
       return DEFAULT_INSTANCE.toBuilder();
     }
-    public static Builder newBuilder(context.ContextOuterClass.Connection prototype) {
+    public static Builder newBuilder(context.ContextOuterClass.EndPoint prototype) {
       return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
     }
     @java.lang.Override
@@ -32096,26 +33801,26 @@ public final class ContextOuterClass {
       return builder;
     }
     /**
-     * Protobuf type {@code context.Connection}
+     * Protobuf type {@code context.EndPoint}
      */
     public static final class Builder extends
         com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
-        // @@protoc_insertion_point(builder_implements:context.Connection)
-        context.ContextOuterClass.ConnectionOrBuilder {
+        // @@protoc_insertion_point(builder_implements:context.EndPoint)
+        context.ContextOuterClass.EndPointOrBuilder {
       public static final com.google.protobuf.Descriptors.Descriptor
           getDescriptor() {
-        return context.ContextOuterClass.internal_static_context_Connection_descriptor;
+        return context.ContextOuterClass.internal_static_context_EndPoint_descriptor;
       }
 
       @java.lang.Override
       protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
           internalGetFieldAccessorTable() {
-        return context.ContextOuterClass.internal_static_context_Connection_fieldAccessorTable
+        return context.ContextOuterClass.internal_static_context_EndPoint_fieldAccessorTable
             .ensureFieldAccessorsInitialized(
-                context.ContextOuterClass.Connection.class, context.ContextOuterClass.Connection.Builder.class);
+                context.ContextOuterClass.EndPoint.class, context.ContextOuterClass.EndPoint.Builder.class);
       }
 
-      // Construct using context.ContextOuterClass.Connection.newBuilder()
+      // Construct using context.ContextOuterClass.EndPoint.newBuilder()
       private Builder() {
         maybeForceBuilderInitialization();
       }
@@ -32128,47 +33833,38 @@ public final class ContextOuterClass {
       private void maybeForceBuilderInitialization() {
         if (com.google.protobuf.GeneratedMessageV3
                 .alwaysUseFieldBuilders) {
-          getPathFieldBuilder();
         }
       }
       @java.lang.Override
       public Builder clear() {
         super.clear();
-        if (connectionIdBuilder_ == null) {
-          connectionId_ = null;
-        } else {
-          connectionId_ = null;
-          connectionIdBuilder_ = null;
-        }
-        if (relatedServiceIdBuilder_ == null) {
-          relatedServiceId_ = null;
-        } else {
-          relatedServiceId_ = null;
-          relatedServiceIdBuilder_ = null;
-        }
-        if (pathBuilder_ == null) {
-          path_ = java.util.Collections.emptyList();
-          bitField0_ = (bitField0_ & ~0x00000001);
+        if (endpointIdBuilder_ == null) {
+          endpointId_ = null;
         } else {
-          pathBuilder_.clear();
+          endpointId_ = null;
+          endpointIdBuilder_ = null;
         }
+        endpointType_ = "";
+
+        kpiSampleTypes_ = java.util.Collections.emptyList();
+        bitField0_ = (bitField0_ & ~0x00000001);
         return this;
       }
 
       @java.lang.Override
       public com.google.protobuf.Descriptors.Descriptor
           getDescriptorForType() {
-        return context.ContextOuterClass.internal_static_context_Connection_descriptor;
+        return context.ContextOuterClass.internal_static_context_EndPoint_descriptor;
       }
 
       @java.lang.Override
-      public context.ContextOuterClass.Connection getDefaultInstanceForType() {
-        return context.ContextOuterClass.Connection.getDefaultInstance();
+      public context.ContextOuterClass.EndPoint getDefaultInstanceForType() {
+        return context.ContextOuterClass.EndPoint.getDefaultInstance();
       }
 
       @java.lang.Override
-      public context.ContextOuterClass.Connection build() {
-        context.ContextOuterClass.Connection result = buildPartial();
+      public context.ContextOuterClass.EndPoint build() {
+        context.ContextOuterClass.EndPoint result = buildPartial();
         if (!result.isInitialized()) {
           throw newUninitializedMessageException(result);
         }
@@ -32176,28 +33872,20 @@ public final class ContextOuterClass {
       }
 
       @java.lang.Override
-      public context.ContextOuterClass.Connection buildPartial() {
-        context.ContextOuterClass.Connection result = new context.ContextOuterClass.Connection(this);
+      public context.ContextOuterClass.EndPoint buildPartial() {
+        context.ContextOuterClass.EndPoint result = new context.ContextOuterClass.EndPoint(this);
         int from_bitField0_ = bitField0_;
-        if (connectionIdBuilder_ == null) {
-          result.connectionId_ = connectionId_;
-        } else {
-          result.connectionId_ = connectionIdBuilder_.build();
-        }
-        if (relatedServiceIdBuilder_ == null) {
-          result.relatedServiceId_ = relatedServiceId_;
+        if (endpointIdBuilder_ == null) {
+          result.endpointId_ = endpointId_;
         } else {
-          result.relatedServiceId_ = relatedServiceIdBuilder_.build();
+          result.endpointId_ = endpointIdBuilder_.build();
         }
-        if (pathBuilder_ == null) {
-          if (((bitField0_ & 0x00000001) != 0)) {
-            path_ = java.util.Collections.unmodifiableList(path_);
-            bitField0_ = (bitField0_ & ~0x00000001);
-          }
-          result.path_ = path_;
-        } else {
-          result.path_ = pathBuilder_.build();
+        result.endpointType_ = endpointType_;
+        if (((bitField0_ & 0x00000001) != 0)) {
+          kpiSampleTypes_ = java.util.Collections.unmodifiableList(kpiSampleTypes_);
+          bitField0_ = (bitField0_ & ~0x00000001);
         }
+        result.kpiSampleTypes_ = kpiSampleTypes_;
         onBuilt();
         return result;
       }
@@ -32236,554 +33924,396 @@ public final class ContextOuterClass {
       }
       @java.lang.Override
       public Builder mergeFrom(com.google.protobuf.Message other) {
-        if (other instanceof context.ContextOuterClass.Connection) {
-          return mergeFrom((context.ContextOuterClass.Connection)other);
+        if (other instanceof context.ContextOuterClass.EndPoint) {
+          return mergeFrom((context.ContextOuterClass.EndPoint)other);
         } else {
           super.mergeFrom(other);
           return this;
         }
       }
 
-      public Builder mergeFrom(context.ContextOuterClass.Connection other) {
-        if (other == context.ContextOuterClass.Connection.getDefaultInstance()) return this;
-        if (other.hasConnectionId()) {
-          mergeConnectionId(other.getConnectionId());
-        }
-        if (other.hasRelatedServiceId()) {
-          mergeRelatedServiceId(other.getRelatedServiceId());
-        }
-        if (pathBuilder_ == null) {
-          if (!other.path_.isEmpty()) {
-            if (path_.isEmpty()) {
-              path_ = other.path_;
-              bitField0_ = (bitField0_ & ~0x00000001);
-            } else {
-              ensurePathIsMutable();
-              path_.addAll(other.path_);
-            }
-            onChanged();
-          }
-        } else {
-          if (!other.path_.isEmpty()) {
-            if (pathBuilder_.isEmpty()) {
-              pathBuilder_.dispose();
-              pathBuilder_ = null;
-              path_ = other.path_;
-              bitField0_ = (bitField0_ & ~0x00000001);
-              pathBuilder_ = 
-                com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders ?
-                   getPathFieldBuilder() : null;
-            } else {
-              pathBuilder_.addAllMessages(other.path_);
-            }
-          }
-        }
-        this.mergeUnknownFields(other.unknownFields);
-        onChanged();
-        return this;
-      }
-
-      @java.lang.Override
-      public final boolean isInitialized() {
-        return true;
-      }
-
-      @java.lang.Override
-      public Builder mergeFrom(
-          com.google.protobuf.CodedInputStream input,
-          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
-          throws java.io.IOException {
-        context.ContextOuterClass.Connection parsedMessage = null;
-        try {
-          parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
-        } catch (com.google.protobuf.InvalidProtocolBufferException e) {
-          parsedMessage = (context.ContextOuterClass.Connection) e.getUnfinishedMessage();
-          throw e.unwrapIOException();
-        } finally {
-          if (parsedMessage != null) {
-            mergeFrom(parsedMessage);
-          }
-        }
-        return this;
-      }
-      private int bitField0_;
-
-      private context.ContextOuterClass.ConnectionId connectionId_;
-      private com.google.protobuf.SingleFieldBuilderV3<
-          context.ContextOuterClass.ConnectionId, context.ContextOuterClass.ConnectionId.Builder, context.ContextOuterClass.ConnectionIdOrBuilder> connectionIdBuilder_;
-      /**
-       * <code>.context.ConnectionId connection_id = 1;</code>
-       * @return Whether the connectionId field is set.
-       */
-      public boolean hasConnectionId() {
-        return connectionIdBuilder_ != null || connectionId_ != null;
-      }
-      /**
-       * <code>.context.ConnectionId connection_id = 1;</code>
-       * @return The connectionId.
-       */
-      public context.ContextOuterClass.ConnectionId getConnectionId() {
-        if (connectionIdBuilder_ == null) {
-          return connectionId_ == null ? context.ContextOuterClass.ConnectionId.getDefaultInstance() : connectionId_;
-        } else {
-          return connectionIdBuilder_.getMessage();
-        }
-      }
-      /**
-       * <code>.context.ConnectionId connection_id = 1;</code>
-       */
-      public Builder setConnectionId(context.ContextOuterClass.ConnectionId value) {
-        if (connectionIdBuilder_ == null) {
-          if (value == null) {
-            throw new NullPointerException();
-          }
-          connectionId_ = value;
-          onChanged();
-        } else {
-          connectionIdBuilder_.setMessage(value);
-        }
-
-        return this;
-      }
-      /**
-       * <code>.context.ConnectionId connection_id = 1;</code>
-       */
-      public Builder setConnectionId(
-          context.ContextOuterClass.ConnectionId.Builder builderForValue) {
-        if (connectionIdBuilder_ == null) {
-          connectionId_ = builderForValue.build();
-          onChanged();
-        } else {
-          connectionIdBuilder_.setMessage(builderForValue.build());
-        }
-
-        return this;
-      }
-      /**
-       * <code>.context.ConnectionId connection_id = 1;</code>
-       */
-      public Builder mergeConnectionId(context.ContextOuterClass.ConnectionId value) {
-        if (connectionIdBuilder_ == null) {
-          if (connectionId_ != null) {
-            connectionId_ =
-              context.ContextOuterClass.ConnectionId.newBuilder(connectionId_).mergeFrom(value).buildPartial();
-          } else {
-            connectionId_ = value;
-          }
-          onChanged();
-        } else {
-          connectionIdBuilder_.mergeFrom(value);
-        }
-
-        return this;
-      }
-      /**
-       * <code>.context.ConnectionId connection_id = 1;</code>
-       */
-      public Builder clearConnectionId() {
-        if (connectionIdBuilder_ == null) {
-          connectionId_ = null;
+      public Builder mergeFrom(context.ContextOuterClass.EndPoint other) {
+        if (other == context.ContextOuterClass.EndPoint.getDefaultInstance()) return this;
+        if (other.hasEndpointId()) {
+          mergeEndpointId(other.getEndpointId());
+        }
+        if (!other.getEndpointType().isEmpty()) {
+          endpointType_ = other.endpointType_;
           onChanged();
-        } else {
-          connectionId_ = null;
-          connectionIdBuilder_ = null;
         }
-
-        return this;
-      }
-      /**
-       * <code>.context.ConnectionId connection_id = 1;</code>
-       */
-      public context.ContextOuterClass.ConnectionId.Builder getConnectionIdBuilder() {
-        
+        if (!other.kpiSampleTypes_.isEmpty()) {
+          if (kpiSampleTypes_.isEmpty()) {
+            kpiSampleTypes_ = other.kpiSampleTypes_;
+            bitField0_ = (bitField0_ & ~0x00000001);
+          } else {
+            ensureKpiSampleTypesIsMutable();
+            kpiSampleTypes_.addAll(other.kpiSampleTypes_);
+          }
+          onChanged();
+        }
+        this.mergeUnknownFields(other.unknownFields);
         onChanged();
-        return getConnectionIdFieldBuilder().getBuilder();
+        return this;
       }
-      /**
-       * <code>.context.ConnectionId connection_id = 1;</code>
-       */
-      public context.ContextOuterClass.ConnectionIdOrBuilder getConnectionIdOrBuilder() {
-        if (connectionIdBuilder_ != null) {
-          return connectionIdBuilder_.getMessageOrBuilder();
-        } else {
-          return connectionId_ == null ?
-              context.ContextOuterClass.ConnectionId.getDefaultInstance() : connectionId_;
-        }
+
+      @java.lang.Override
+      public final boolean isInitialized() {
+        return true;
       }
-      /**
-       * <code>.context.ConnectionId connection_id = 1;</code>
-       */
-      private com.google.protobuf.SingleFieldBuilderV3<
-          context.ContextOuterClass.ConnectionId, context.ContextOuterClass.ConnectionId.Builder, context.ContextOuterClass.ConnectionIdOrBuilder> 
-          getConnectionIdFieldBuilder() {
-        if (connectionIdBuilder_ == null) {
-          connectionIdBuilder_ = new com.google.protobuf.SingleFieldBuilderV3<
-              context.ContextOuterClass.ConnectionId, context.ContextOuterClass.ConnectionId.Builder, context.ContextOuterClass.ConnectionIdOrBuilder>(
-                  getConnectionId(),
-                  getParentForChildren(),
-                  isClean());
-          connectionId_ = null;
+
+      @java.lang.Override
+      public Builder mergeFrom(
+          com.google.protobuf.CodedInputStream input,
+          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+          throws java.io.IOException {
+        context.ContextOuterClass.EndPoint parsedMessage = null;
+        try {
+          parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
+        } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+          parsedMessage = (context.ContextOuterClass.EndPoint) e.getUnfinishedMessage();
+          throw e.unwrapIOException();
+        } finally {
+          if (parsedMessage != null) {
+            mergeFrom(parsedMessage);
+          }
         }
-        return connectionIdBuilder_;
+        return this;
       }
+      private int bitField0_;
 
-      private context.ContextOuterClass.ServiceId relatedServiceId_;
+      private context.ContextOuterClass.EndPointId endpointId_;
       private com.google.protobuf.SingleFieldBuilderV3<
-          context.ContextOuterClass.ServiceId, context.ContextOuterClass.ServiceId.Builder, context.ContextOuterClass.ServiceIdOrBuilder> relatedServiceIdBuilder_;
+          context.ContextOuterClass.EndPointId, context.ContextOuterClass.EndPointId.Builder, context.ContextOuterClass.EndPointIdOrBuilder> endpointIdBuilder_;
       /**
-       * <code>.context.ServiceId related_service_id = 2;</code>
-       * @return Whether the relatedServiceId field is set.
+       * <code>.context.EndPointId endpoint_id = 1;</code>
+       * @return Whether the endpointId field is set.
        */
-      public boolean hasRelatedServiceId() {
-        return relatedServiceIdBuilder_ != null || relatedServiceId_ != null;
+      public boolean hasEndpointId() {
+        return endpointIdBuilder_ != null || endpointId_ != null;
       }
       /**
-       * <code>.context.ServiceId related_service_id = 2;</code>
-       * @return The relatedServiceId.
+       * <code>.context.EndPointId endpoint_id = 1;</code>
+       * @return The endpointId.
        */
-      public context.ContextOuterClass.ServiceId getRelatedServiceId() {
-        if (relatedServiceIdBuilder_ == null) {
-          return relatedServiceId_ == null ? context.ContextOuterClass.ServiceId.getDefaultInstance() : relatedServiceId_;
+      public context.ContextOuterClass.EndPointId getEndpointId() {
+        if (endpointIdBuilder_ == null) {
+          return endpointId_ == null ? context.ContextOuterClass.EndPointId.getDefaultInstance() : endpointId_;
         } else {
-          return relatedServiceIdBuilder_.getMessage();
+          return endpointIdBuilder_.getMessage();
         }
       }
       /**
-       * <code>.context.ServiceId related_service_id = 2;</code>
+       * <code>.context.EndPointId endpoint_id = 1;</code>
        */
-      public Builder setRelatedServiceId(context.ContextOuterClass.ServiceId value) {
-        if (relatedServiceIdBuilder_ == null) {
+      public Builder setEndpointId(context.ContextOuterClass.EndPointId value) {
+        if (endpointIdBuilder_ == null) {
           if (value == null) {
             throw new NullPointerException();
           }
-          relatedServiceId_ = value;
+          endpointId_ = value;
           onChanged();
         } else {
-          relatedServiceIdBuilder_.setMessage(value);
+          endpointIdBuilder_.setMessage(value);
         }
 
         return this;
       }
       /**
-       * <code>.context.ServiceId related_service_id = 2;</code>
+       * <code>.context.EndPointId endpoint_id = 1;</code>
        */
-      public Builder setRelatedServiceId(
-          context.ContextOuterClass.ServiceId.Builder builderForValue) {
-        if (relatedServiceIdBuilder_ == null) {
-          relatedServiceId_ = builderForValue.build();
+      public Builder setEndpointId(
+          context.ContextOuterClass.EndPointId.Builder builderForValue) {
+        if (endpointIdBuilder_ == null) {
+          endpointId_ = builderForValue.build();
           onChanged();
         } else {
-          relatedServiceIdBuilder_.setMessage(builderForValue.build());
+          endpointIdBuilder_.setMessage(builderForValue.build());
         }
 
         return this;
       }
       /**
-       * <code>.context.ServiceId related_service_id = 2;</code>
+       * <code>.context.EndPointId endpoint_id = 1;</code>
        */
-      public Builder mergeRelatedServiceId(context.ContextOuterClass.ServiceId value) {
-        if (relatedServiceIdBuilder_ == null) {
-          if (relatedServiceId_ != null) {
-            relatedServiceId_ =
-              context.ContextOuterClass.ServiceId.newBuilder(relatedServiceId_).mergeFrom(value).buildPartial();
+      public Builder mergeEndpointId(context.ContextOuterClass.EndPointId value) {
+        if (endpointIdBuilder_ == null) {
+          if (endpointId_ != null) {
+            endpointId_ =
+              context.ContextOuterClass.EndPointId.newBuilder(endpointId_).mergeFrom(value).buildPartial();
           } else {
-            relatedServiceId_ = value;
+            endpointId_ = value;
           }
           onChanged();
         } else {
-          relatedServiceIdBuilder_.mergeFrom(value);
+          endpointIdBuilder_.mergeFrom(value);
         }
 
         return this;
       }
       /**
-       * <code>.context.ServiceId related_service_id = 2;</code>
+       * <code>.context.EndPointId endpoint_id = 1;</code>
        */
-      public Builder clearRelatedServiceId() {
-        if (relatedServiceIdBuilder_ == null) {
-          relatedServiceId_ = null;
+      public Builder clearEndpointId() {
+        if (endpointIdBuilder_ == null) {
+          endpointId_ = null;
           onChanged();
         } else {
-          relatedServiceId_ = null;
-          relatedServiceIdBuilder_ = null;
+          endpointId_ = null;
+          endpointIdBuilder_ = null;
         }
 
         return this;
       }
       /**
-       * <code>.context.ServiceId related_service_id = 2;</code>
+       * <code>.context.EndPointId endpoint_id = 1;</code>
        */
-      public context.ContextOuterClass.ServiceId.Builder getRelatedServiceIdBuilder() {
+      public context.ContextOuterClass.EndPointId.Builder getEndpointIdBuilder() {
         
         onChanged();
-        return getRelatedServiceIdFieldBuilder().getBuilder();
+        return getEndpointIdFieldBuilder().getBuilder();
       }
       /**
-       * <code>.context.ServiceId related_service_id = 2;</code>
+       * <code>.context.EndPointId endpoint_id = 1;</code>
        */
-      public context.ContextOuterClass.ServiceIdOrBuilder getRelatedServiceIdOrBuilder() {
-        if (relatedServiceIdBuilder_ != null) {
-          return relatedServiceIdBuilder_.getMessageOrBuilder();
+      public context.ContextOuterClass.EndPointIdOrBuilder getEndpointIdOrBuilder() {
+        if (endpointIdBuilder_ != null) {
+          return endpointIdBuilder_.getMessageOrBuilder();
         } else {
-          return relatedServiceId_ == null ?
-              context.ContextOuterClass.ServiceId.getDefaultInstance() : relatedServiceId_;
+          return endpointId_ == null ?
+              context.ContextOuterClass.EndPointId.getDefaultInstance() : endpointId_;
         }
       }
       /**
-       * <code>.context.ServiceId related_service_id = 2;</code>
+       * <code>.context.EndPointId endpoint_id = 1;</code>
        */
       private com.google.protobuf.SingleFieldBuilderV3<
-          context.ContextOuterClass.ServiceId, context.ContextOuterClass.ServiceId.Builder, context.ContextOuterClass.ServiceIdOrBuilder> 
-          getRelatedServiceIdFieldBuilder() {
-        if (relatedServiceIdBuilder_ == null) {
-          relatedServiceIdBuilder_ = new com.google.protobuf.SingleFieldBuilderV3<
-              context.ContextOuterClass.ServiceId, context.ContextOuterClass.ServiceId.Builder, context.ContextOuterClass.ServiceIdOrBuilder>(
-                  getRelatedServiceId(),
+          context.ContextOuterClass.EndPointId, context.ContextOuterClass.EndPointId.Builder, context.ContextOuterClass.EndPointIdOrBuilder> 
+          getEndpointIdFieldBuilder() {
+        if (endpointIdBuilder_ == null) {
+          endpointIdBuilder_ = new com.google.protobuf.SingleFieldBuilderV3<
+              context.ContextOuterClass.EndPointId, context.ContextOuterClass.EndPointId.Builder, context.ContextOuterClass.EndPointIdOrBuilder>(
+                  getEndpointId(),
                   getParentForChildren(),
                   isClean());
-          relatedServiceId_ = null;
+          endpointId_ = null;
         }
-        return relatedServiceIdBuilder_;
-      }
-
-      private java.util.List<context.ContextOuterClass.EndPointId> path_ =
-        java.util.Collections.emptyList();
-      private void ensurePathIsMutable() {
-        if (!((bitField0_ & 0x00000001) != 0)) {
-          path_ = new java.util.ArrayList<context.ContextOuterClass.EndPointId>(path_);
-          bitField0_ |= 0x00000001;
-         }
+        return endpointIdBuilder_;
       }
 
-      private com.google.protobuf.RepeatedFieldBuilderV3<
-          context.ContextOuterClass.EndPointId, context.ContextOuterClass.EndPointId.Builder, context.ContextOuterClass.EndPointIdOrBuilder> pathBuilder_;
-
+      private java.lang.Object endpointType_ = "";
       /**
-       * <code>repeated .context.EndPointId path = 3;</code>
+       * <code>string endpoint_type = 2;</code>
+       * @return The endpointType.
        */
-      public java.util.List<context.ContextOuterClass.EndPointId> getPathList() {
-        if (pathBuilder_ == null) {
-          return java.util.Collections.unmodifiableList(path_);
+      public java.lang.String getEndpointType() {
+        java.lang.Object ref = endpointType_;
+        if (!(ref instanceof java.lang.String)) {
+          com.google.protobuf.ByteString bs =
+              (com.google.protobuf.ByteString) ref;
+          java.lang.String s = bs.toStringUtf8();
+          endpointType_ = s;
+          return s;
         } else {
-          return pathBuilder_.getMessageList();
+          return (java.lang.String) ref;
         }
       }
       /**
-       * <code>repeated .context.EndPointId path = 3;</code>
+       * <code>string endpoint_type = 2;</code>
+       * @return The bytes for endpointType.
        */
-      public int getPathCount() {
-        if (pathBuilder_ == null) {
-          return path_.size();
+      public com.google.protobuf.ByteString
+          getEndpointTypeBytes() {
+        java.lang.Object ref = endpointType_;
+        if (ref instanceof String) {
+          com.google.protobuf.ByteString b = 
+              com.google.protobuf.ByteString.copyFromUtf8(
+                  (java.lang.String) ref);
+          endpointType_ = b;
+          return b;
         } else {
-          return pathBuilder_.getCount();
+          return (com.google.protobuf.ByteString) ref;
         }
       }
       /**
-       * <code>repeated .context.EndPointId path = 3;</code>
+       * <code>string endpoint_type = 2;</code>
+       * @param value The endpointType to set.
+       * @return This builder for chaining.
        */
-      public context.ContextOuterClass.EndPointId getPath(int index) {
-        if (pathBuilder_ == null) {
-          return path_.get(index);
-        } else {
-          return pathBuilder_.getMessage(index);
-        }
+      public Builder setEndpointType(
+          java.lang.String value) {
+        if (value == null) {
+    throw new NullPointerException();
+  }
+  
+        endpointType_ = value;
+        onChanged();
+        return this;
       }
       /**
-       * <code>repeated .context.EndPointId path = 3;</code>
+       * <code>string endpoint_type = 2;</code>
+       * @return This builder for chaining.
        */
-      public Builder setPath(
-          int index, context.ContextOuterClass.EndPointId value) {
-        if (pathBuilder_ == null) {
-          if (value == null) {
-            throw new NullPointerException();
-          }
-          ensurePathIsMutable();
-          path_.set(index, value);
-          onChanged();
-        } else {
-          pathBuilder_.setMessage(index, value);
-        }
+      public Builder clearEndpointType() {
+        
+        endpointType_ = getDefaultInstance().getEndpointType();
+        onChanged();
         return this;
       }
       /**
-       * <code>repeated .context.EndPointId path = 3;</code>
+       * <code>string endpoint_type = 2;</code>
+       * @param value The bytes for endpointType to set.
+       * @return This builder for chaining.
        */
-      public Builder setPath(
-          int index, context.ContextOuterClass.EndPointId.Builder builderForValue) {
-        if (pathBuilder_ == null) {
-          ensurePathIsMutable();
-          path_.set(index, builderForValue.build());
-          onChanged();
-        } else {
-          pathBuilder_.setMessage(index, builderForValue.build());
-        }
+      public Builder setEndpointTypeBytes(
+          com.google.protobuf.ByteString value) {
+        if (value == null) {
+    throw new NullPointerException();
+  }
+  checkByteStringIsUtf8(value);
+        
+        endpointType_ = value;
+        onChanged();
         return this;
       }
-      /**
-       * <code>repeated .context.EndPointId path = 3;</code>
-       */
-      public Builder addPath(context.ContextOuterClass.EndPointId value) {
-        if (pathBuilder_ == null) {
-          if (value == null) {
-            throw new NullPointerException();
-          }
-          ensurePathIsMutable();
-          path_.add(value);
-          onChanged();
-        } else {
-          pathBuilder_.addMessage(value);
+
+      private java.util.List<java.lang.Integer> kpiSampleTypes_ =
+        java.util.Collections.emptyList();
+      private void ensureKpiSampleTypesIsMutable() {
+        if (!((bitField0_ & 0x00000001) != 0)) {
+          kpiSampleTypes_ = new java.util.ArrayList<java.lang.Integer>(kpiSampleTypes_);
+          bitField0_ |= 0x00000001;
         }
-        return this;
       }
       /**
-       * <code>repeated .context.EndPointId path = 3;</code>
+       * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
+       * @return A list containing the kpiSampleTypes.
        */
-      public Builder addPath(
-          int index, context.ContextOuterClass.EndPointId value) {
-        if (pathBuilder_ == null) {
-          if (value == null) {
-            throw new NullPointerException();
-          }
-          ensurePathIsMutable();
-          path_.add(index, value);
-          onChanged();
-        } else {
-          pathBuilder_.addMessage(index, value);
-        }
-        return this;
+      public java.util.List<kpi_sample_types.KpiSampleTypes.KpiSampleType> getKpiSampleTypesList() {
+        return new com.google.protobuf.Internal.ListAdapter<
+            java.lang.Integer, kpi_sample_types.KpiSampleTypes.KpiSampleType>(kpiSampleTypes_, kpiSampleTypes_converter_);
       }
       /**
-       * <code>repeated .context.EndPointId path = 3;</code>
+       * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
+       * @return The count of kpiSampleTypes.
        */
-      public Builder addPath(
-          context.ContextOuterClass.EndPointId.Builder builderForValue) {
-        if (pathBuilder_ == null) {
-          ensurePathIsMutable();
-          path_.add(builderForValue.build());
-          onChanged();
-        } else {
-          pathBuilder_.addMessage(builderForValue.build());
-        }
-        return this;
+      public int getKpiSampleTypesCount() {
+        return kpiSampleTypes_.size();
       }
       /**
-       * <code>repeated .context.EndPointId path = 3;</code>
+       * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
+       * @param index The index of the element to return.
+       * @return The kpiSampleTypes at the given index.
        */
-      public Builder addPath(
-          int index, context.ContextOuterClass.EndPointId.Builder builderForValue) {
-        if (pathBuilder_ == null) {
-          ensurePathIsMutable();
-          path_.add(index, builderForValue.build());
-          onChanged();
-        } else {
-          pathBuilder_.addMessage(index, builderForValue.build());
-        }
-        return this;
+      public kpi_sample_types.KpiSampleTypes.KpiSampleType getKpiSampleTypes(int index) {
+        return kpiSampleTypes_converter_.convert(kpiSampleTypes_.get(index));
       }
       /**
-       * <code>repeated .context.EndPointId path = 3;</code>
+       * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
+       * @param index The index to set the value at.
+       * @param value The kpiSampleTypes to set.
+       * @return This builder for chaining.
        */
-      public Builder addAllPath(
-          java.lang.Iterable<? extends context.ContextOuterClass.EndPointId> values) {
-        if (pathBuilder_ == null) {
-          ensurePathIsMutable();
-          com.google.protobuf.AbstractMessageLite.Builder.addAll(
-              values, path_);
-          onChanged();
-        } else {
-          pathBuilder_.addAllMessages(values);
+      public Builder setKpiSampleTypes(
+          int index, kpi_sample_types.KpiSampleTypes.KpiSampleType value) {
+        if (value == null) {
+          throw new NullPointerException();
         }
+        ensureKpiSampleTypesIsMutable();
+        kpiSampleTypes_.set(index, value.getNumber());
+        onChanged();
         return this;
       }
       /**
-       * <code>repeated .context.EndPointId path = 3;</code>
+       * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
+       * @param value The kpiSampleTypes to add.
+       * @return This builder for chaining.
        */
-      public Builder clearPath() {
-        if (pathBuilder_ == null) {
-          path_ = java.util.Collections.emptyList();
-          bitField0_ = (bitField0_ & ~0x00000001);
-          onChanged();
-        } else {
-          pathBuilder_.clear();
+      public Builder addKpiSampleTypes(kpi_sample_types.KpiSampleTypes.KpiSampleType value) {
+        if (value == null) {
+          throw new NullPointerException();
         }
+        ensureKpiSampleTypesIsMutable();
+        kpiSampleTypes_.add(value.getNumber());
+        onChanged();
         return this;
       }
       /**
-       * <code>repeated .context.EndPointId path = 3;</code>
+       * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
+       * @param values The kpiSampleTypes to add.
+       * @return This builder for chaining.
        */
-      public Builder removePath(int index) {
-        if (pathBuilder_ == null) {
-          ensurePathIsMutable();
-          path_.remove(index);
-          onChanged();
-        } else {
-          pathBuilder_.remove(index);
+      public Builder addAllKpiSampleTypes(
+          java.lang.Iterable<? extends kpi_sample_types.KpiSampleTypes.KpiSampleType> values) {
+        ensureKpiSampleTypesIsMutable();
+        for (kpi_sample_types.KpiSampleTypes.KpiSampleType value : values) {
+          kpiSampleTypes_.add(value.getNumber());
         }
+        onChanged();
         return this;
       }
       /**
-       * <code>repeated .context.EndPointId path = 3;</code>
+       * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
+       * @return This builder for chaining.
        */
-      public context.ContextOuterClass.EndPointId.Builder getPathBuilder(
-          int index) {
-        return getPathFieldBuilder().getBuilder(index);
+      public Builder clearKpiSampleTypes() {
+        kpiSampleTypes_ = java.util.Collections.emptyList();
+        bitField0_ = (bitField0_ & ~0x00000001);
+        onChanged();
+        return this;
       }
       /**
-       * <code>repeated .context.EndPointId path = 3;</code>
+       * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
+       * @return A list containing the enum numeric values on the wire for kpiSampleTypes.
        */
-      public context.ContextOuterClass.EndPointIdOrBuilder getPathOrBuilder(
-          int index) {
-        if (pathBuilder_ == null) {
-          return path_.get(index);  } else {
-          return pathBuilder_.getMessageOrBuilder(index);
-        }
+      public java.util.List<java.lang.Integer>
+      getKpiSampleTypesValueList() {
+        return java.util.Collections.unmodifiableList(kpiSampleTypes_);
       }
       /**
-       * <code>repeated .context.EndPointId path = 3;</code>
+       * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
+       * @param index The index of the value to return.
+       * @return The enum numeric value on the wire of kpiSampleTypes at the given index.
        */
-      public java.util.List<? extends context.ContextOuterClass.EndPointIdOrBuilder> 
-           getPathOrBuilderList() {
-        if (pathBuilder_ != null) {
-          return pathBuilder_.getMessageOrBuilderList();
-        } else {
-          return java.util.Collections.unmodifiableList(path_);
-        }
+      public int getKpiSampleTypesValue(int index) {
+        return kpiSampleTypes_.get(index);
       }
       /**
-       * <code>repeated .context.EndPointId path = 3;</code>
+       * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
+       * @param index The index of the value to return.
+       * @return The enum numeric value on the wire of kpiSampleTypes at the given index.
+       * @return This builder for chaining.
        */
-      public context.ContextOuterClass.EndPointId.Builder addPathBuilder() {
-        return getPathFieldBuilder().addBuilder(
-            context.ContextOuterClass.EndPointId.getDefaultInstance());
+      public Builder setKpiSampleTypesValue(
+          int index, int value) {
+        ensureKpiSampleTypesIsMutable();
+        kpiSampleTypes_.set(index, value);
+        onChanged();
+        return this;
       }
       /**
-       * <code>repeated .context.EndPointId path = 3;</code>
+       * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
+       * @param value The enum numeric value on the wire for kpiSampleTypes to add.
+       * @return This builder for chaining.
        */
-      public context.ContextOuterClass.EndPointId.Builder addPathBuilder(
-          int index) {
-        return getPathFieldBuilder().addBuilder(
-            index, context.ContextOuterClass.EndPointId.getDefaultInstance());
+      public Builder addKpiSampleTypesValue(int value) {
+        ensureKpiSampleTypesIsMutable();
+        kpiSampleTypes_.add(value);
+        onChanged();
+        return this;
       }
       /**
-       * <code>repeated .context.EndPointId path = 3;</code>
+       * <code>repeated .kpi_sample_types.KpiSampleType kpi_sample_types = 3;</code>
+       * @param values The enum numeric values on the wire for kpiSampleTypes to add.
+       * @return This builder for chaining.
        */
-      public java.util.List<context.ContextOuterClass.EndPointId.Builder> 
-           getPathBuilderList() {
-        return getPathFieldBuilder().getBuilderList();
-      }
-      private com.google.protobuf.RepeatedFieldBuilderV3<
-          context.ContextOuterClass.EndPointId, context.ContextOuterClass.EndPointId.Builder, context.ContextOuterClass.EndPointIdOrBuilder> 
-          getPathFieldBuilder() {
-        if (pathBuilder_ == null) {
-          pathBuilder_ = new com.google.protobuf.RepeatedFieldBuilderV3<
-              context.ContextOuterClass.EndPointId, context.ContextOuterClass.EndPointId.Builder, context.ContextOuterClass.EndPointIdOrBuilder>(
-                  path_,
-                  ((bitField0_ & 0x00000001) != 0),
-                  getParentForChildren(),
-                  isClean());
-          path_ = null;
+      public Builder addAllKpiSampleTypesValue(
+          java.lang.Iterable<java.lang.Integer> values) {
+        ensureKpiSampleTypesIsMutable();
+        for (int value : values) {
+          kpiSampleTypes_.add(value);
         }
-        return pathBuilder_;
+        onChanged();
+        return this;
       }
       @java.lang.Override
       public final Builder setUnknownFields(
@@ -32798,95 +34328,108 @@ public final class ContextOuterClass {
       }
 
 
-      // @@protoc_insertion_point(builder_scope:context.Connection)
+      // @@protoc_insertion_point(builder_scope:context.EndPoint)
     }
 
-    // @@protoc_insertion_point(class_scope:context.Connection)
-    private static final context.ContextOuterClass.Connection DEFAULT_INSTANCE;
+    // @@protoc_insertion_point(class_scope:context.EndPoint)
+    private static final context.ContextOuterClass.EndPoint DEFAULT_INSTANCE;
     static {
-      DEFAULT_INSTANCE = new context.ContextOuterClass.Connection();
+      DEFAULT_INSTANCE = new context.ContextOuterClass.EndPoint();
     }
 
-    public static context.ContextOuterClass.Connection getDefaultInstance() {
+    public static context.ContextOuterClass.EndPoint getDefaultInstance() {
       return DEFAULT_INSTANCE;
     }
 
-    private static final com.google.protobuf.Parser<Connection>
-        PARSER = new com.google.protobuf.AbstractParser<Connection>() {
+    private static final com.google.protobuf.Parser<EndPoint>
+        PARSER = new com.google.protobuf.AbstractParser<EndPoint>() {
       @java.lang.Override
-      public Connection parsePartialFrom(
+      public EndPoint parsePartialFrom(
           com.google.protobuf.CodedInputStream input,
           com.google.protobuf.ExtensionRegistryLite extensionRegistry)
           throws com.google.protobuf.InvalidProtocolBufferException {
-        return new Connection(input, extensionRegistry);
+        return new EndPoint(input, extensionRegistry);
       }
     };
 
-    public static com.google.protobuf.Parser<Connection> parser() {
+    public static com.google.protobuf.Parser<EndPoint> parser() {
       return PARSER;
     }
 
     @java.lang.Override
-    public com.google.protobuf.Parser<Connection> getParserForType() {
+    public com.google.protobuf.Parser<EndPoint> getParserForType() {
       return PARSER;
     }
 
     @java.lang.Override
-    public context.ContextOuterClass.Connection getDefaultInstanceForType() {
+    public context.ContextOuterClass.EndPoint getDefaultInstanceForType() {
       return DEFAULT_INSTANCE;
     }
 
   }
 
-  public interface ConnectionIdListOrBuilder extends
-      // @@protoc_insertion_point(interface_extends:context.ConnectionIdList)
+  public interface ConfigRuleOrBuilder extends
+      // @@protoc_insertion_point(interface_extends:context.ConfigRule)
       com.google.protobuf.MessageOrBuilder {
 
     /**
-     * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+     * <code>.context.ConfigActionEnum action = 1;</code>
+     * @return The enum numeric value on the wire for action.
      */
-    java.util.List<context.ContextOuterClass.ConnectionId> 
-        getConnectionIdsList();
+    int getActionValue();
     /**
-     * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+     * <code>.context.ConfigActionEnum action = 1;</code>
+     * @return The action.
      */
-    context.ContextOuterClass.ConnectionId getConnectionIds(int index);
+    context.ContextOuterClass.ConfigActionEnum getAction();
+
     /**
-     * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+     * <code>string resource_key = 2;</code>
+     * @return The resourceKey.
      */
-    int getConnectionIdsCount();
+    java.lang.String getResourceKey();
     /**
-     * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+     * <code>string resource_key = 2;</code>
+     * @return The bytes for resourceKey.
      */
-    java.util.List<? extends context.ContextOuterClass.ConnectionIdOrBuilder> 
-        getConnectionIdsOrBuilderList();
+    com.google.protobuf.ByteString
+        getResourceKeyBytes();
+
     /**
-     * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+     * <code>string resource_value = 3;</code>
+     * @return The resourceValue.
      */
-    context.ContextOuterClass.ConnectionIdOrBuilder getConnectionIdsOrBuilder(
-        int index);
+    java.lang.String getResourceValue();
+    /**
+     * <code>string resource_value = 3;</code>
+     * @return The bytes for resourceValue.
+     */
+    com.google.protobuf.ByteString
+        getResourceValueBytes();
   }
   /**
-   * Protobuf type {@code context.ConnectionIdList}
+   * Protobuf type {@code context.ConfigRule}
    */
-  public static final class ConnectionIdList extends
+  public static final class ConfigRule extends
       com.google.protobuf.GeneratedMessageV3 implements
-      // @@protoc_insertion_point(message_implements:context.ConnectionIdList)
-      ConnectionIdListOrBuilder {
+      // @@protoc_insertion_point(message_implements:context.ConfigRule)
+      ConfigRuleOrBuilder {
   private static final long serialVersionUID = 0L;
-    // Use ConnectionIdList.newBuilder() to construct.
-    private ConnectionIdList(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
+    // Use ConfigRule.newBuilder() to construct.
+    private ConfigRule(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
       super(builder);
     }
-    private ConnectionIdList() {
-      connectionIds_ = java.util.Collections.emptyList();
+    private ConfigRule() {
+      action_ = 0;
+      resourceKey_ = "";
+      resourceValue_ = "";
     }
 
     @java.lang.Override
     @SuppressWarnings({"unused"})
     protected java.lang.Object newInstance(
         UnusedPrivateParameter unused) {
-      return new ConnectionIdList();
+      return new ConfigRule();
     }
 
     @java.lang.Override
@@ -32894,7 +34437,7 @@ public final class ContextOuterClass {
     getUnknownFields() {
       return this.unknownFields;
     }
-    private ConnectionIdList(
+    private ConfigRule(
         com.google.protobuf.CodedInputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
@@ -32902,7 +34445,6 @@ public final class ContextOuterClass {
       if (extensionRegistry == null) {
         throw new java.lang.NullPointerException();
       }
-      int mutable_bitField0_ = 0;
       com.google.protobuf.UnknownFieldSet.Builder unknownFields =
           com.google.protobuf.UnknownFieldSet.newBuilder();
       try {
@@ -32913,13 +34455,22 @@ public final class ContextOuterClass {
             case 0:
               done = true;
               break;
-            case 10: {
-              if (!((mutable_bitField0_ & 0x00000001) != 0)) {
-                connectionIds_ = new java.util.ArrayList<context.ContextOuterClass.ConnectionId>();
-                mutable_bitField0_ |= 0x00000001;
-              }
-              connectionIds_.add(
-                  input.readMessage(context.ContextOuterClass.ConnectionId.parser(), extensionRegistry));
+            case 8: {
+              int rawValue = input.readEnum();
+
+              action_ = rawValue;
+              break;
+            }
+            case 18: {
+              java.lang.String s = input.readStringRequireUtf8();
+
+              resourceKey_ = s;
+              break;
+            }
+            case 26: {
+              java.lang.String s = input.readStringRequireUtf8();
+
+              resourceValue_ = s;
               break;
             }
             default: {
@@ -32937,64 +34488,116 @@ public final class ContextOuterClass {
         throw new com.google.protobuf.InvalidProtocolBufferException(
             e).setUnfinishedMessage(this);
       } finally {
-        if (((mutable_bitField0_ & 0x00000001) != 0)) {
-          connectionIds_ = java.util.Collections.unmodifiableList(connectionIds_);
-        }
         this.unknownFields = unknownFields.build();
         makeExtensionsImmutable();
       }
     }
     public static final com.google.protobuf.Descriptors.Descriptor
         getDescriptor() {
-      return context.ContextOuterClass.internal_static_context_ConnectionIdList_descriptor;
+      return context.ContextOuterClass.internal_static_context_ConfigRule_descriptor;
     }
 
     @java.lang.Override
     protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
         internalGetFieldAccessorTable() {
-      return context.ContextOuterClass.internal_static_context_ConnectionIdList_fieldAccessorTable
+      return context.ContextOuterClass.internal_static_context_ConfigRule_fieldAccessorTable
           .ensureFieldAccessorsInitialized(
-              context.ContextOuterClass.ConnectionIdList.class, context.ContextOuterClass.ConnectionIdList.Builder.class);
+              context.ContextOuterClass.ConfigRule.class, context.ContextOuterClass.ConfigRule.Builder.class);
     }
 
-    public static final int CONNECTION_IDS_FIELD_NUMBER = 1;
-    private java.util.List<context.ContextOuterClass.ConnectionId> connectionIds_;
+    public static final int ACTION_FIELD_NUMBER = 1;
+    private int action_;
     /**
-     * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+     * <code>.context.ConfigActionEnum action = 1;</code>
+     * @return The enum numeric value on the wire for action.
      */
-    @java.lang.Override
-    public java.util.List<context.ContextOuterClass.ConnectionId> getConnectionIdsList() {
-      return connectionIds_;
+    @java.lang.Override public int getActionValue() {
+      return action_;
     }
     /**
-     * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+     * <code>.context.ConfigActionEnum action = 1;</code>
+     * @return The action.
+     */
+    @java.lang.Override public context.ContextOuterClass.ConfigActionEnum getAction() {
+      @SuppressWarnings("deprecation")
+      context.ContextOuterClass.ConfigActionEnum result = context.ContextOuterClass.ConfigActionEnum.valueOf(action_);
+      return result == null ? context.ContextOuterClass.ConfigActionEnum.UNRECOGNIZED : result;
+    }
+
+    public static final int RESOURCE_KEY_FIELD_NUMBER = 2;
+    private volatile java.lang.Object resourceKey_;
+    /**
+     * <code>string resource_key = 2;</code>
+     * @return The resourceKey.
      */
     @java.lang.Override
-    public java.util.List<? extends context.ContextOuterClass.ConnectionIdOrBuilder> 
-        getConnectionIdsOrBuilderList() {
-      return connectionIds_;
+    public java.lang.String getResourceKey() {
+      java.lang.Object ref = resourceKey_;
+      if (ref instanceof java.lang.String) {
+        return (java.lang.String) ref;
+      } else {
+        com.google.protobuf.ByteString bs = 
+            (com.google.protobuf.ByteString) ref;
+        java.lang.String s = bs.toStringUtf8();
+        resourceKey_ = s;
+        return s;
+      }
     }
     /**
-     * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+     * <code>string resource_key = 2;</code>
+     * @return The bytes for resourceKey.
      */
     @java.lang.Override
-    public int getConnectionIdsCount() {
-      return connectionIds_.size();
+    public com.google.protobuf.ByteString
+        getResourceKeyBytes() {
+      java.lang.Object ref = resourceKey_;
+      if (ref instanceof java.lang.String) {
+        com.google.protobuf.ByteString b = 
+            com.google.protobuf.ByteString.copyFromUtf8(
+                (java.lang.String) ref);
+        resourceKey_ = b;
+        return b;
+      } else {
+        return (com.google.protobuf.ByteString) ref;
+      }
     }
+
+    public static final int RESOURCE_VALUE_FIELD_NUMBER = 3;
+    private volatile java.lang.Object resourceValue_;
     /**
-     * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+     * <code>string resource_value = 3;</code>
+     * @return The resourceValue.
      */
     @java.lang.Override
-    public context.ContextOuterClass.ConnectionId getConnectionIds(int index) {
-      return connectionIds_.get(index);
+    public java.lang.String getResourceValue() {
+      java.lang.Object ref = resourceValue_;
+      if (ref instanceof java.lang.String) {
+        return (java.lang.String) ref;
+      } else {
+        com.google.protobuf.ByteString bs = 
+            (com.google.protobuf.ByteString) ref;
+        java.lang.String s = bs.toStringUtf8();
+        resourceValue_ = s;
+        return s;
+      }
     }
     /**
-     * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+     * <code>string resource_value = 3;</code>
+     * @return The bytes for resourceValue.
      */
     @java.lang.Override
-    public context.ContextOuterClass.ConnectionIdOrBuilder getConnectionIdsOrBuilder(
-        int index) {
-      return connectionIds_.get(index);
+    public com.google.protobuf.ByteString
+        getResourceValueBytes() {
+      java.lang.Object ref = resourceValue_;
+      if (ref instanceof java.lang.String) {
+        com.google.protobuf.ByteString b = 
+            com.google.protobuf.ByteString.copyFromUtf8(
+                (java.lang.String) ref);
+        resourceValue_ = b;
+        return b;
+      } else {
+        return (com.google.protobuf.ByteString) ref;
+      }
     }
 
     private byte memoizedIsInitialized = -1;
@@ -33011,8 +34614,14 @@ public final class ContextOuterClass {
     @java.lang.Override
     public void writeTo(com.google.protobuf.CodedOutputStream output)
                         throws java.io.IOException {
-      for (int i = 0; i < connectionIds_.size(); i++) {
-        output.writeMessage(1, connectionIds_.get(i));
+      if (action_ != context.ContextOuterClass.ConfigActionEnum.CONFIGACTION_UNDEFINED.getNumber()) {
+        output.writeEnum(1, action_);
+      }
+      if (!getResourceKeyBytes().isEmpty()) {
+        com.google.protobuf.GeneratedMessageV3.writeString(output, 2, resourceKey_);
+      }
+      if (!getResourceValueBytes().isEmpty()) {
+        com.google.protobuf.GeneratedMessageV3.writeString(output, 3, resourceValue_);
       }
       unknownFields.writeTo(output);
     }
@@ -33023,9 +34632,15 @@ public final class ContextOuterClass {
       if (size != -1) return size;
 
       size = 0;
-      for (int i = 0; i < connectionIds_.size(); i++) {
+      if (action_ != context.ContextOuterClass.ConfigActionEnum.CONFIGACTION_UNDEFINED.getNumber()) {
         size += com.google.protobuf.CodedOutputStream
-          .computeMessageSize(1, connectionIds_.get(i));
+          .computeEnumSize(1, action_);
+      }
+      if (!getResourceKeyBytes().isEmpty()) {
+        size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, resourceKey_);
+      }
+      if (!getResourceValueBytes().isEmpty()) {
+        size += com.google.protobuf.GeneratedMessageV3.computeStringSize(3, resourceValue_);
       }
       size += unknownFields.getSerializedSize();
       memoizedSize = size;
@@ -33037,13 +34652,16 @@ public final class ContextOuterClass {
       if (obj == this) {
        return true;
       }
-      if (!(obj instanceof context.ContextOuterClass.ConnectionIdList)) {
+      if (!(obj instanceof context.ContextOuterClass.ConfigRule)) {
         return super.equals(obj);
       }
-      context.ContextOuterClass.ConnectionIdList other = (context.ContextOuterClass.ConnectionIdList) obj;
+      context.ContextOuterClass.ConfigRule other = (context.ContextOuterClass.ConfigRule) obj;
 
-      if (!getConnectionIdsList()
-          .equals(other.getConnectionIdsList())) return false;
+      if (action_ != other.action_) return false;
+      if (!getResourceKey()
+          .equals(other.getResourceKey())) return false;
+      if (!getResourceValue()
+          .equals(other.getResourceValue())) return false;
       if (!unknownFields.equals(other.unknownFields)) return false;
       return true;
     }
@@ -33055,78 +34673,80 @@ public final class ContextOuterClass {
       }
       int hash = 41;
       hash = (19 * hash) + getDescriptor().hashCode();
-      if (getConnectionIdsCount() > 0) {
-        hash = (37 * hash) + CONNECTION_IDS_FIELD_NUMBER;
-        hash = (53 * hash) + getConnectionIdsList().hashCode();
-      }
+      hash = (37 * hash) + ACTION_FIELD_NUMBER;
+      hash = (53 * hash) + action_;
+      hash = (37 * hash) + RESOURCE_KEY_FIELD_NUMBER;
+      hash = (53 * hash) + getResourceKey().hashCode();
+      hash = (37 * hash) + RESOURCE_VALUE_FIELD_NUMBER;
+      hash = (53 * hash) + getResourceValue().hashCode();
       hash = (29 * hash) + unknownFields.hashCode();
       memoizedHashCode = hash;
       return hash;
     }
 
-    public static context.ContextOuterClass.ConnectionIdList parseFrom(
+    public static context.ContextOuterClass.ConfigRule parseFrom(
         java.nio.ByteBuffer data)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data);
     }
-    public static context.ContextOuterClass.ConnectionIdList parseFrom(
+    public static context.ContextOuterClass.ConfigRule parseFrom(
         java.nio.ByteBuffer data,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data, extensionRegistry);
     }
-    public static context.ContextOuterClass.ConnectionIdList parseFrom(
+    public static context.ContextOuterClass.ConfigRule parseFrom(
         com.google.protobuf.ByteString data)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data);
     }
-    public static context.ContextOuterClass.ConnectionIdList parseFrom(
+    public static context.ContextOuterClass.ConfigRule parseFrom(
         com.google.protobuf.ByteString data,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data, extensionRegistry);
     }
-    public static context.ContextOuterClass.ConnectionIdList parseFrom(byte[] data)
+    public static context.ContextOuterClass.ConfigRule parseFrom(byte[] data)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data);
     }
-    public static context.ContextOuterClass.ConnectionIdList parseFrom(
+    public static context.ContextOuterClass.ConfigRule parseFrom(
         byte[] data,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data, extensionRegistry);
     }
-    public static context.ContextOuterClass.ConnectionIdList parseFrom(java.io.InputStream input)
+    public static context.ContextOuterClass.ConfigRule parseFrom(java.io.InputStream input)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseWithIOException(PARSER, input);
     }
-    public static context.ContextOuterClass.ConnectionIdList parseFrom(
+    public static context.ContextOuterClass.ConfigRule parseFrom(
         java.io.InputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseWithIOException(PARSER, input, extensionRegistry);
     }
-    public static context.ContextOuterClass.ConnectionIdList parseDelimitedFrom(java.io.InputStream input)
+    public static context.ContextOuterClass.ConfigRule parseDelimitedFrom(java.io.InputStream input)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseDelimitedWithIOException(PARSER, input);
     }
-    public static context.ContextOuterClass.ConnectionIdList parseDelimitedFrom(
+    public static context.ContextOuterClass.ConfigRule parseDelimitedFrom(
         java.io.InputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseDelimitedWithIOException(PARSER, input, extensionRegistry);
     }
-    public static context.ContextOuterClass.ConnectionIdList parseFrom(
+    public static context.ContextOuterClass.ConfigRule parseFrom(
         com.google.protobuf.CodedInputStream input)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseWithIOException(PARSER, input);
     }
-    public static context.ContextOuterClass.ConnectionIdList parseFrom(
+    public static context.ContextOuterClass.ConfigRule parseFrom(
         com.google.protobuf.CodedInputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws java.io.IOException {
@@ -33139,7 +34759,7 @@ public final class ContextOuterClass {
     public static Builder newBuilder() {
       return DEFAULT_INSTANCE.toBuilder();
     }
-    public static Builder newBuilder(context.ContextOuterClass.ConnectionIdList prototype) {
+    public static Builder newBuilder(context.ContextOuterClass.ConfigRule prototype) {
       return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
     }
     @java.lang.Override
@@ -33155,26 +34775,26 @@ public final class ContextOuterClass {
       return builder;
     }
     /**
-     * Protobuf type {@code context.ConnectionIdList}
+     * Protobuf type {@code context.ConfigRule}
      */
     public static final class Builder extends
         com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
-        // @@protoc_insertion_point(builder_implements:context.ConnectionIdList)
-        context.ContextOuterClass.ConnectionIdListOrBuilder {
+        // @@protoc_insertion_point(builder_implements:context.ConfigRule)
+        context.ContextOuterClass.ConfigRuleOrBuilder {
       public static final com.google.protobuf.Descriptors.Descriptor
           getDescriptor() {
-        return context.ContextOuterClass.internal_static_context_ConnectionIdList_descriptor;
+        return context.ContextOuterClass.internal_static_context_ConfigRule_descriptor;
       }
 
       @java.lang.Override
       protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
           internalGetFieldAccessorTable() {
-        return context.ContextOuterClass.internal_static_context_ConnectionIdList_fieldAccessorTable
+        return context.ContextOuterClass.internal_static_context_ConfigRule_fieldAccessorTable
             .ensureFieldAccessorsInitialized(
-                context.ContextOuterClass.ConnectionIdList.class, context.ContextOuterClass.ConnectionIdList.Builder.class);
+                context.ContextOuterClass.ConfigRule.class, context.ContextOuterClass.ConfigRule.Builder.class);
       }
 
-      // Construct using context.ContextOuterClass.ConnectionIdList.newBuilder()
+      // Construct using context.ContextOuterClass.ConfigRule.newBuilder()
       private Builder() {
         maybeForceBuilderInitialization();
       }
@@ -33187,35 +34807,34 @@ public final class ContextOuterClass {
       private void maybeForceBuilderInitialization() {
         if (com.google.protobuf.GeneratedMessageV3
                 .alwaysUseFieldBuilders) {
-          getConnectionIdsFieldBuilder();
         }
       }
       @java.lang.Override
       public Builder clear() {
         super.clear();
-        if (connectionIdsBuilder_ == null) {
-          connectionIds_ = java.util.Collections.emptyList();
-          bitField0_ = (bitField0_ & ~0x00000001);
-        } else {
-          connectionIdsBuilder_.clear();
-        }
+        action_ = 0;
+
+        resourceKey_ = "";
+
+        resourceValue_ = "";
+
         return this;
       }
 
       @java.lang.Override
       public com.google.protobuf.Descriptors.Descriptor
           getDescriptorForType() {
-        return context.ContextOuterClass.internal_static_context_ConnectionIdList_descriptor;
+        return context.ContextOuterClass.internal_static_context_ConfigRule_descriptor;
       }
 
       @java.lang.Override
-      public context.ContextOuterClass.ConnectionIdList getDefaultInstanceForType() {
-        return context.ContextOuterClass.ConnectionIdList.getDefaultInstance();
+      public context.ContextOuterClass.ConfigRule getDefaultInstanceForType() {
+        return context.ContextOuterClass.ConfigRule.getDefaultInstance();
       }
 
       @java.lang.Override
-      public context.ContextOuterClass.ConnectionIdList build() {
-        context.ContextOuterClass.ConnectionIdList result = buildPartial();
+      public context.ContextOuterClass.ConfigRule build() {
+        context.ContextOuterClass.ConfigRule result = buildPartial();
         if (!result.isInitialized()) {
           throw newUninitializedMessageException(result);
         }
@@ -33223,18 +34842,11 @@ public final class ContextOuterClass {
       }
 
       @java.lang.Override
-      public context.ContextOuterClass.ConnectionIdList buildPartial() {
-        context.ContextOuterClass.ConnectionIdList result = new context.ContextOuterClass.ConnectionIdList(this);
-        int from_bitField0_ = bitField0_;
-        if (connectionIdsBuilder_ == null) {
-          if (((bitField0_ & 0x00000001) != 0)) {
-            connectionIds_ = java.util.Collections.unmodifiableList(connectionIds_);
-            bitField0_ = (bitField0_ & ~0x00000001);
-          }
-          result.connectionIds_ = connectionIds_;
-        } else {
-          result.connectionIds_ = connectionIdsBuilder_.build();
-        }
+      public context.ContextOuterClass.ConfigRule buildPartial() {
+        context.ContextOuterClass.ConfigRule result = new context.ContextOuterClass.ConfigRule(this);
+        result.action_ = action_;
+        result.resourceKey_ = resourceKey_;
+        result.resourceValue_ = resourceValue_;
         onBuilt();
         return result;
       }
@@ -33273,41 +34885,26 @@ public final class ContextOuterClass {
       }
       @java.lang.Override
       public Builder mergeFrom(com.google.protobuf.Message other) {
-        if (other instanceof context.ContextOuterClass.ConnectionIdList) {
-          return mergeFrom((context.ContextOuterClass.ConnectionIdList)other);
+        if (other instanceof context.ContextOuterClass.ConfigRule) {
+          return mergeFrom((context.ContextOuterClass.ConfigRule)other);
         } else {
           super.mergeFrom(other);
           return this;
         }
       }
 
-      public Builder mergeFrom(context.ContextOuterClass.ConnectionIdList other) {
-        if (other == context.ContextOuterClass.ConnectionIdList.getDefaultInstance()) return this;
-        if (connectionIdsBuilder_ == null) {
-          if (!other.connectionIds_.isEmpty()) {
-            if (connectionIds_.isEmpty()) {
-              connectionIds_ = other.connectionIds_;
-              bitField0_ = (bitField0_ & ~0x00000001);
-            } else {
-              ensureConnectionIdsIsMutable();
-              connectionIds_.addAll(other.connectionIds_);
-            }
-            onChanged();
-          }
-        } else {
-          if (!other.connectionIds_.isEmpty()) {
-            if (connectionIdsBuilder_.isEmpty()) {
-              connectionIdsBuilder_.dispose();
-              connectionIdsBuilder_ = null;
-              connectionIds_ = other.connectionIds_;
-              bitField0_ = (bitField0_ & ~0x00000001);
-              connectionIdsBuilder_ = 
-                com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders ?
-                   getConnectionIdsFieldBuilder() : null;
-            } else {
-              connectionIdsBuilder_.addAllMessages(other.connectionIds_);
-            }
-          }
+      public Builder mergeFrom(context.ContextOuterClass.ConfigRule other) {
+        if (other == context.ContextOuterClass.ConfigRule.getDefaultInstance()) return this;
+        if (other.action_ != 0) {
+          setActionValue(other.getActionValue());
+        }
+        if (!other.getResourceKey().isEmpty()) {
+          resourceKey_ = other.resourceKey_;
+          onChanged();
+        }
+        if (!other.getResourceValue().isEmpty()) {
+          resourceValue_ = other.resourceValue_;
+          onChanged();
         }
         this.mergeUnknownFields(other.unknownFields);
         onChanged();
@@ -33324,11 +34921,11 @@ public final class ContextOuterClass {
           com.google.protobuf.CodedInputStream input,
           com.google.protobuf.ExtensionRegistryLite extensionRegistry)
           throws java.io.IOException {
-        context.ContextOuterClass.ConnectionIdList parsedMessage = null;
+        context.ContextOuterClass.ConfigRule parsedMessage = null;
         try {
           parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
         } catch (com.google.protobuf.InvalidProtocolBufferException e) {
-          parsedMessage = (context.ContextOuterClass.ConnectionIdList) e.getUnfinishedMessage();
+          parsedMessage = (context.ContextOuterClass.ConfigRule) e.getUnfinishedMessage();
           throw e.unwrapIOException();
         } finally {
           if (parsedMessage != null) {
@@ -33337,246 +34934,211 @@ public final class ContextOuterClass {
         }
         return this;
       }
-      private int bitField0_;
-
-      private java.util.List<context.ContextOuterClass.ConnectionId> connectionIds_ =
-        java.util.Collections.emptyList();
-      private void ensureConnectionIdsIsMutable() {
-        if (!((bitField0_ & 0x00000001) != 0)) {
-          connectionIds_ = new java.util.ArrayList<context.ContextOuterClass.ConnectionId>(connectionIds_);
-          bitField0_ |= 0x00000001;
-         }
-      }
-
-      private com.google.protobuf.RepeatedFieldBuilderV3<
-          context.ContextOuterClass.ConnectionId, context.ContextOuterClass.ConnectionId.Builder, context.ContextOuterClass.ConnectionIdOrBuilder> connectionIdsBuilder_;
 
+      private int action_ = 0;
       /**
-       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
-       */
-      public java.util.List<context.ContextOuterClass.ConnectionId> getConnectionIdsList() {
-        if (connectionIdsBuilder_ == null) {
-          return java.util.Collections.unmodifiableList(connectionIds_);
-        } else {
-          return connectionIdsBuilder_.getMessageList();
-        }
-      }
-      /**
-       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
-       */
-      public int getConnectionIdsCount() {
-        if (connectionIdsBuilder_ == null) {
-          return connectionIds_.size();
-        } else {
-          return connectionIdsBuilder_.getCount();
-        }
-      }
-      /**
-       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
-       */
-      public context.ContextOuterClass.ConnectionId getConnectionIds(int index) {
-        if (connectionIdsBuilder_ == null) {
-          return connectionIds_.get(index);
-        } else {
-          return connectionIdsBuilder_.getMessage(index);
-        }
-      }
-      /**
-       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+       * <code>.context.ConfigActionEnum action = 1;</code>
+       * @return The enum numeric value on the wire for action.
        */
-      public Builder setConnectionIds(
-          int index, context.ContextOuterClass.ConnectionId value) {
-        if (connectionIdsBuilder_ == null) {
-          if (value == null) {
-            throw new NullPointerException();
-          }
-          ensureConnectionIdsIsMutable();
-          connectionIds_.set(index, value);
-          onChanged();
-        } else {
-          connectionIdsBuilder_.setMessage(index, value);
-        }
-        return this;
+      @java.lang.Override public int getActionValue() {
+        return action_;
       }
       /**
-       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+       * <code>.context.ConfigActionEnum action = 1;</code>
+       * @param value The enum numeric value on the wire for action to set.
+       * @return This builder for chaining.
        */
-      public Builder setConnectionIds(
-          int index, context.ContextOuterClass.ConnectionId.Builder builderForValue) {
-        if (connectionIdsBuilder_ == null) {
-          ensureConnectionIdsIsMutable();
-          connectionIds_.set(index, builderForValue.build());
-          onChanged();
-        } else {
-          connectionIdsBuilder_.setMessage(index, builderForValue.build());
-        }
+      public Builder setActionValue(int value) {
+        
+        action_ = value;
+        onChanged();
         return this;
       }
       /**
-       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+       * <code>.context.ConfigActionEnum action = 1;</code>
+       * @return The action.
        */
-      public Builder addConnectionIds(context.ContextOuterClass.ConnectionId value) {
-        if (connectionIdsBuilder_ == null) {
-          if (value == null) {
-            throw new NullPointerException();
-          }
-          ensureConnectionIdsIsMutable();
-          connectionIds_.add(value);
-          onChanged();
-        } else {
-          connectionIdsBuilder_.addMessage(value);
-        }
-        return this;
+      @java.lang.Override
+      public context.ContextOuterClass.ConfigActionEnum getAction() {
+        @SuppressWarnings("deprecation")
+        context.ContextOuterClass.ConfigActionEnum result = context.ContextOuterClass.ConfigActionEnum.valueOf(action_);
+        return result == null ? context.ContextOuterClass.ConfigActionEnum.UNRECOGNIZED : result;
       }
       /**
-       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+       * <code>.context.ConfigActionEnum action = 1;</code>
+       * @param value The action to set.
+       * @return This builder for chaining.
        */
-      public Builder addConnectionIds(
-          int index, context.ContextOuterClass.ConnectionId value) {
-        if (connectionIdsBuilder_ == null) {
-          if (value == null) {
-            throw new NullPointerException();
-          }
-          ensureConnectionIdsIsMutable();
-          connectionIds_.add(index, value);
-          onChanged();
-        } else {
-          connectionIdsBuilder_.addMessage(index, value);
+      public Builder setAction(context.ContextOuterClass.ConfigActionEnum value) {
+        if (value == null) {
+          throw new NullPointerException();
         }
+        
+        action_ = value.getNumber();
+        onChanged();
         return this;
       }
       /**
-       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+       * <code>.context.ConfigActionEnum action = 1;</code>
+       * @return This builder for chaining.
        */
-      public Builder addConnectionIds(
-          context.ContextOuterClass.ConnectionId.Builder builderForValue) {
-        if (connectionIdsBuilder_ == null) {
-          ensureConnectionIdsIsMutable();
-          connectionIds_.add(builderForValue.build());
-          onChanged();
-        } else {
-          connectionIdsBuilder_.addMessage(builderForValue.build());
-        }
+      public Builder clearAction() {
+        
+        action_ = 0;
+        onChanged();
         return this;
       }
+
+      private java.lang.Object resourceKey_ = "";
       /**
-       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+       * <code>string resource_key = 2;</code>
+       * @return The resourceKey.
        */
-      public Builder addConnectionIds(
-          int index, context.ContextOuterClass.ConnectionId.Builder builderForValue) {
-        if (connectionIdsBuilder_ == null) {
-          ensureConnectionIdsIsMutable();
-          connectionIds_.add(index, builderForValue.build());
-          onChanged();
+      public java.lang.String getResourceKey() {
+        java.lang.Object ref = resourceKey_;
+        if (!(ref instanceof java.lang.String)) {
+          com.google.protobuf.ByteString bs =
+              (com.google.protobuf.ByteString) ref;
+          java.lang.String s = bs.toStringUtf8();
+          resourceKey_ = s;
+          return s;
         } else {
-          connectionIdsBuilder_.addMessage(index, builderForValue.build());
+          return (java.lang.String) ref;
         }
-        return this;
       }
       /**
-       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+       * <code>string resource_key = 2;</code>
+       * @return The bytes for resourceKey.
        */
-      public Builder addAllConnectionIds(
-          java.lang.Iterable<? extends context.ContextOuterClass.ConnectionId> values) {
-        if (connectionIdsBuilder_ == null) {
-          ensureConnectionIdsIsMutable();
-          com.google.protobuf.AbstractMessageLite.Builder.addAll(
-              values, connectionIds_);
-          onChanged();
+      public com.google.protobuf.ByteString
+          getResourceKeyBytes() {
+        java.lang.Object ref = resourceKey_;
+        if (ref instanceof String) {
+          com.google.protobuf.ByteString b = 
+              com.google.protobuf.ByteString.copyFromUtf8(
+                  (java.lang.String) ref);
+          resourceKey_ = b;
+          return b;
         } else {
-          connectionIdsBuilder_.addAllMessages(values);
+          return (com.google.protobuf.ByteString) ref;
         }
-        return this;
       }
       /**
-       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+       * <code>string resource_key = 2;</code>
+       * @param value The resourceKey to set.
+       * @return This builder for chaining.
        */
-      public Builder clearConnectionIds() {
-        if (connectionIdsBuilder_ == null) {
-          connectionIds_ = java.util.Collections.emptyList();
-          bitField0_ = (bitField0_ & ~0x00000001);
-          onChanged();
-        } else {
-          connectionIdsBuilder_.clear();
-        }
+      public Builder setResourceKey(
+          java.lang.String value) {
+        if (value == null) {
+    throw new NullPointerException();
+  }
+  
+        resourceKey_ = value;
+        onChanged();
         return this;
       }
       /**
-       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+       * <code>string resource_key = 2;</code>
+       * @return This builder for chaining.
        */
-      public Builder removeConnectionIds(int index) {
-        if (connectionIdsBuilder_ == null) {
-          ensureConnectionIdsIsMutable();
-          connectionIds_.remove(index);
-          onChanged();
-        } else {
-          connectionIdsBuilder_.remove(index);
-        }
+      public Builder clearResourceKey() {
+        
+        resourceKey_ = getDefaultInstance().getResourceKey();
+        onChanged();
         return this;
       }
       /**
-       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+       * <code>string resource_key = 2;</code>
+       * @param value The bytes for resourceKey to set.
+       * @return This builder for chaining.
        */
-      public context.ContextOuterClass.ConnectionId.Builder getConnectionIdsBuilder(
-          int index) {
-        return getConnectionIdsFieldBuilder().getBuilder(index);
+      public Builder setResourceKeyBytes(
+          com.google.protobuf.ByteString value) {
+        if (value == null) {
+    throw new NullPointerException();
+  }
+  checkByteStringIsUtf8(value);
+        
+        resourceKey_ = value;
+        onChanged();
+        return this;
       }
+
+      private java.lang.Object resourceValue_ = "";
       /**
-       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+       * <code>string resource_value = 3;</code>
+       * @return The resourceValue.
        */
-      public context.ContextOuterClass.ConnectionIdOrBuilder getConnectionIdsOrBuilder(
-          int index) {
-        if (connectionIdsBuilder_ == null) {
-          return connectionIds_.get(index);  } else {
-          return connectionIdsBuilder_.getMessageOrBuilder(index);
+      public java.lang.String getResourceValue() {
+        java.lang.Object ref = resourceValue_;
+        if (!(ref instanceof java.lang.String)) {
+          com.google.protobuf.ByteString bs =
+              (com.google.protobuf.ByteString) ref;
+          java.lang.String s = bs.toStringUtf8();
+          resourceValue_ = s;
+          return s;
+        } else {
+          return (java.lang.String) ref;
         }
       }
       /**
-       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+       * <code>string resource_value = 3;</code>
+       * @return The bytes for resourceValue.
        */
-      public java.util.List<? extends context.ContextOuterClass.ConnectionIdOrBuilder> 
-           getConnectionIdsOrBuilderList() {
-        if (connectionIdsBuilder_ != null) {
-          return connectionIdsBuilder_.getMessageOrBuilderList();
+      public com.google.protobuf.ByteString
+          getResourceValueBytes() {
+        java.lang.Object ref = resourceValue_;
+        if (ref instanceof String) {
+          com.google.protobuf.ByteString b = 
+              com.google.protobuf.ByteString.copyFromUtf8(
+                  (java.lang.String) ref);
+          resourceValue_ = b;
+          return b;
         } else {
-          return java.util.Collections.unmodifiableList(connectionIds_);
+          return (com.google.protobuf.ByteString) ref;
         }
       }
       /**
-       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+       * <code>string resource_value = 3;</code>
+       * @param value The resourceValue to set.
+       * @return This builder for chaining.
        */
-      public context.ContextOuterClass.ConnectionId.Builder addConnectionIdsBuilder() {
-        return getConnectionIdsFieldBuilder().addBuilder(
-            context.ContextOuterClass.ConnectionId.getDefaultInstance());
+      public Builder setResourceValue(
+          java.lang.String value) {
+        if (value == null) {
+    throw new NullPointerException();
+  }
+  
+        resourceValue_ = value;
+        onChanged();
+        return this;
       }
       /**
-       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+       * <code>string resource_value = 3;</code>
+       * @return This builder for chaining.
        */
-      public context.ContextOuterClass.ConnectionId.Builder addConnectionIdsBuilder(
-          int index) {
-        return getConnectionIdsFieldBuilder().addBuilder(
-            index, context.ContextOuterClass.ConnectionId.getDefaultInstance());
+      public Builder clearResourceValue() {
+        
+        resourceValue_ = getDefaultInstance().getResourceValue();
+        onChanged();
+        return this;
       }
       /**
-       * <code>repeated .context.ConnectionId connection_ids = 1;</code>
+       * <code>string resource_value = 3;</code>
+       * @param value The bytes for resourceValue to set.
+       * @return This builder for chaining.
        */
-      public java.util.List<context.ContextOuterClass.ConnectionId.Builder> 
-           getConnectionIdsBuilderList() {
-        return getConnectionIdsFieldBuilder().getBuilderList();
-      }
-      private com.google.protobuf.RepeatedFieldBuilderV3<
-          context.ContextOuterClass.ConnectionId, context.ContextOuterClass.ConnectionId.Builder, context.ContextOuterClass.ConnectionIdOrBuilder> 
-          getConnectionIdsFieldBuilder() {
-        if (connectionIdsBuilder_ == null) {
-          connectionIdsBuilder_ = new com.google.protobuf.RepeatedFieldBuilderV3<
-              context.ContextOuterClass.ConnectionId, context.ContextOuterClass.ConnectionId.Builder, context.ContextOuterClass.ConnectionIdOrBuilder>(
-                  connectionIds_,
-                  ((bitField0_ & 0x00000001) != 0),
-                  getParentForChildren(),
-                  isClean());
-          connectionIds_ = null;
-        }
-        return connectionIdsBuilder_;
+      public Builder setResourceValueBytes(
+          com.google.protobuf.ByteString value) {
+        if (value == null) {
+    throw new NullPointerException();
+  }
+  checkByteStringIsUtf8(value);
+        
+        resourceValue_ = value;
+        onChanged();
+        return this;
       }
       @java.lang.Override
       public final Builder setUnknownFields(
@@ -33591,95 +35153,100 @@ public final class ContextOuterClass {
       }
 
 
-      // @@protoc_insertion_point(builder_scope:context.ConnectionIdList)
+      // @@protoc_insertion_point(builder_scope:context.ConfigRule)
     }
 
-    // @@protoc_insertion_point(class_scope:context.ConnectionIdList)
-    private static final context.ContextOuterClass.ConnectionIdList DEFAULT_INSTANCE;
+    // @@protoc_insertion_point(class_scope:context.ConfigRule)
+    private static final context.ContextOuterClass.ConfigRule DEFAULT_INSTANCE;
     static {
-      DEFAULT_INSTANCE = new context.ContextOuterClass.ConnectionIdList();
+      DEFAULT_INSTANCE = new context.ContextOuterClass.ConfigRule();
     }
 
-    public static context.ContextOuterClass.ConnectionIdList getDefaultInstance() {
+    public static context.ContextOuterClass.ConfigRule getDefaultInstance() {
       return DEFAULT_INSTANCE;
     }
 
-    private static final com.google.protobuf.Parser<ConnectionIdList>
-        PARSER = new com.google.protobuf.AbstractParser<ConnectionIdList>() {
+    private static final com.google.protobuf.Parser<ConfigRule>
+        PARSER = new com.google.protobuf.AbstractParser<ConfigRule>() {
       @java.lang.Override
-      public ConnectionIdList parsePartialFrom(
+      public ConfigRule parsePartialFrom(
           com.google.protobuf.CodedInputStream input,
           com.google.protobuf.ExtensionRegistryLite extensionRegistry)
           throws com.google.protobuf.InvalidProtocolBufferException {
-        return new ConnectionIdList(input, extensionRegistry);
+        return new ConfigRule(input, extensionRegistry);
       }
     };
 
-    public static com.google.protobuf.Parser<ConnectionIdList> parser() {
+    public static com.google.protobuf.Parser<ConfigRule> parser() {
       return PARSER;
     }
 
     @java.lang.Override
-    public com.google.protobuf.Parser<ConnectionIdList> getParserForType() {
+    public com.google.protobuf.Parser<ConfigRule> getParserForType() {
       return PARSER;
     }
 
     @java.lang.Override
-    public context.ContextOuterClass.ConnectionIdList getDefaultInstanceForType() {
+    public context.ContextOuterClass.ConfigRule getDefaultInstanceForType() {
       return DEFAULT_INSTANCE;
     }
 
   }
 
-  public interface ConnectionListOrBuilder extends
-      // @@protoc_insertion_point(interface_extends:context.ConnectionList)
+  public interface ConstraintOrBuilder extends
+      // @@protoc_insertion_point(interface_extends:context.Constraint)
       com.google.protobuf.MessageOrBuilder {
 
     /**
-     * <code>repeated .context.Connection connections = 1;</code>
-     */
-    java.util.List<context.ContextOuterClass.Connection> 
-        getConnectionsList();
-    /**
-     * <code>repeated .context.Connection connections = 1;</code>
+     * <code>string constraint_type = 1;</code>
+     * @return The constraintType.
      */
-    context.ContextOuterClass.Connection getConnections(int index);
+    java.lang.String getConstraintType();
     /**
-     * <code>repeated .context.Connection connections = 1;</code>
+     * <code>string constraint_type = 1;</code>
+     * @return The bytes for constraintType.
      */
-    int getConnectionsCount();
+    com.google.protobuf.ByteString
+        getConstraintTypeBytes();
+
     /**
-     * <code>repeated .context.Connection connections = 1;</code>
+     * <code>string constraint_value = 2;</code>
+     * @return The constraintValue.
      */
-    java.util.List<? extends context.ContextOuterClass.ConnectionOrBuilder> 
-        getConnectionsOrBuilderList();
+    java.lang.String getConstraintValue();
     /**
-     * <code>repeated .context.Connection connections = 1;</code>
+     * <code>string constraint_value = 2;</code>
+     * @return The bytes for constraintValue.
      */
-    context.ContextOuterClass.ConnectionOrBuilder getConnectionsOrBuilder(
-        int index);
+    com.google.protobuf.ByteString
+        getConstraintValueBytes();
   }
   /**
-   * Protobuf type {@code context.ConnectionList}
+   * <pre>
+   * ----- Constraint ----------------------------------------------------------------------------------------------------
+   * </pre>
+   *
+   * Protobuf type {@code context.Constraint}
    */
-  public static final class ConnectionList extends
+  public static final class Constraint extends
       com.google.protobuf.GeneratedMessageV3 implements
-      // @@protoc_insertion_point(message_implements:context.ConnectionList)
-      ConnectionListOrBuilder {
+      // @@protoc_insertion_point(message_implements:context.Constraint)
+      ConstraintOrBuilder {
   private static final long serialVersionUID = 0L;
-    // Use ConnectionList.newBuilder() to construct.
-    private ConnectionList(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
+    // Use Constraint.newBuilder() to construct.
+    private Constraint(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
       super(builder);
     }
-    private ConnectionList() {
-      connections_ = java.util.Collections.emptyList();
+    private Constraint() {
+      constraintType_ = "";
+      constraintValue_ = "";
     }
 
     @java.lang.Override
     @SuppressWarnings({"unused"})
     protected java.lang.Object newInstance(
         UnusedPrivateParameter unused) {
-      return new ConnectionList();
+      return new Constraint();
     }
 
     @java.lang.Override
@@ -33687,7 +35254,7 @@ public final class ContextOuterClass {
     getUnknownFields() {
       return this.unknownFields;
     }
-    private ConnectionList(
+    private Constraint(
         com.google.protobuf.CodedInputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
@@ -33695,7 +35262,6 @@ public final class ContextOuterClass {
       if (extensionRegistry == null) {
         throw new java.lang.NullPointerException();
       }
-      int mutable_bitField0_ = 0;
       com.google.protobuf.UnknownFieldSet.Builder unknownFields =
           com.google.protobuf.UnknownFieldSet.newBuilder();
       try {
@@ -33707,12 +35273,15 @@ public final class ContextOuterClass {
               done = true;
               break;
             case 10: {
-              if (!((mutable_bitField0_ & 0x00000001) != 0)) {
-                connections_ = new java.util.ArrayList<context.ContextOuterClass.Connection>();
-                mutable_bitField0_ |= 0x00000001;
-              }
-              connections_.add(
-                  input.readMessage(context.ContextOuterClass.Connection.parser(), extensionRegistry));
+              java.lang.String s = input.readStringRequireUtf8();
+
+              constraintType_ = s;
+              break;
+            }
+            case 18: {
+              java.lang.String s = input.readStringRequireUtf8();
+
+              constraintValue_ = s;
               break;
             }
             default: {
@@ -33730,64 +35299,97 @@ public final class ContextOuterClass {
         throw new com.google.protobuf.InvalidProtocolBufferException(
             e).setUnfinishedMessage(this);
       } finally {
-        if (((mutable_bitField0_ & 0x00000001) != 0)) {
-          connections_ = java.util.Collections.unmodifiableList(connections_);
-        }
         this.unknownFields = unknownFields.build();
         makeExtensionsImmutable();
       }
     }
     public static final com.google.protobuf.Descriptors.Descriptor
         getDescriptor() {
-      return context.ContextOuterClass.internal_static_context_ConnectionList_descriptor;
+      return context.ContextOuterClass.internal_static_context_Constraint_descriptor;
     }
 
     @java.lang.Override
     protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
         internalGetFieldAccessorTable() {
-      return context.ContextOuterClass.internal_static_context_ConnectionList_fieldAccessorTable
+      return context.ContextOuterClass.internal_static_context_Constraint_fieldAccessorTable
           .ensureFieldAccessorsInitialized(
-              context.ContextOuterClass.ConnectionList.class, context.ContextOuterClass.ConnectionList.Builder.class);
+              context.ContextOuterClass.Constraint.class, context.ContextOuterClass.Constraint.Builder.class);
     }
 
-    public static final int CONNECTIONS_FIELD_NUMBER = 1;
-    private java.util.List<context.ContextOuterClass.Connection> connections_;
-    /**
-     * <code>repeated .context.Connection connections = 1;</code>
-     */
-    @java.lang.Override
-    public java.util.List<context.ContextOuterClass.Connection> getConnectionsList() {
-      return connections_;
-    }
+    public static final int CONSTRAINT_TYPE_FIELD_NUMBER = 1;
+    private volatile java.lang.Object constraintType_;
     /**
-     * <code>repeated .context.Connection connections = 1;</code>
+     * <code>string constraint_type = 1;</code>
+     * @return The constraintType.
      */
     @java.lang.Override
-    public java.util.List<? extends context.ContextOuterClass.ConnectionOrBuilder> 
-        getConnectionsOrBuilderList() {
-      return connections_;
+    public java.lang.String getConstraintType() {
+      java.lang.Object ref = constraintType_;
+      if (ref instanceof java.lang.String) {
+        return (java.lang.String) ref;
+      } else {
+        com.google.protobuf.ByteString bs = 
+            (com.google.protobuf.ByteString) ref;
+        java.lang.String s = bs.toStringUtf8();
+        constraintType_ = s;
+        return s;
+      }
     }
     /**
-     * <code>repeated .context.Connection connections = 1;</code>
+     * <code>string constraint_type = 1;</code>
+     * @return The bytes for constraintType.
      */
     @java.lang.Override
-    public int getConnectionsCount() {
-      return connections_.size();
+    public com.google.protobuf.ByteString
+        getConstraintTypeBytes() {
+      java.lang.Object ref = constraintType_;
+      if (ref instanceof java.lang.String) {
+        com.google.protobuf.ByteString b = 
+            com.google.protobuf.ByteString.copyFromUtf8(
+                (java.lang.String) ref);
+        constraintType_ = b;
+        return b;
+      } else {
+        return (com.google.protobuf.ByteString) ref;
+      }
     }
+
+    public static final int CONSTRAINT_VALUE_FIELD_NUMBER = 2;
+    private volatile java.lang.Object constraintValue_;
     /**
-     * <code>repeated .context.Connection connections = 1;</code>
+     * <code>string constraint_value = 2;</code>
+     * @return The constraintValue.
      */
     @java.lang.Override
-    public context.ContextOuterClass.Connection getConnections(int index) {
-      return connections_.get(index);
+    public java.lang.String getConstraintValue() {
+      java.lang.Object ref = constraintValue_;
+      if (ref instanceof java.lang.String) {
+        return (java.lang.String) ref;
+      } else {
+        com.google.protobuf.ByteString bs = 
+            (com.google.protobuf.ByteString) ref;
+        java.lang.String s = bs.toStringUtf8();
+        constraintValue_ = s;
+        return s;
+      }
     }
     /**
-     * <code>repeated .context.Connection connections = 1;</code>
+     * <code>string constraint_value = 2;</code>
+     * @return The bytes for constraintValue.
      */
     @java.lang.Override
-    public context.ContextOuterClass.ConnectionOrBuilder getConnectionsOrBuilder(
-        int index) {
-      return connections_.get(index);
+    public com.google.protobuf.ByteString
+        getConstraintValueBytes() {
+      java.lang.Object ref = constraintValue_;
+      if (ref instanceof java.lang.String) {
+        com.google.protobuf.ByteString b = 
+            com.google.protobuf.ByteString.copyFromUtf8(
+                (java.lang.String) ref);
+        constraintValue_ = b;
+        return b;
+      } else {
+        return (com.google.protobuf.ByteString) ref;
+      }
     }
 
     private byte memoizedIsInitialized = -1;
@@ -33804,8 +35406,11 @@ public final class ContextOuterClass {
     @java.lang.Override
     public void writeTo(com.google.protobuf.CodedOutputStream output)
                         throws java.io.IOException {
-      for (int i = 0; i < connections_.size(); i++) {
-        output.writeMessage(1, connections_.get(i));
+      if (!getConstraintTypeBytes().isEmpty()) {
+        com.google.protobuf.GeneratedMessageV3.writeString(output, 1, constraintType_);
+      }
+      if (!getConstraintValueBytes().isEmpty()) {
+        com.google.protobuf.GeneratedMessageV3.writeString(output, 2, constraintValue_);
       }
       unknownFields.writeTo(output);
     }
@@ -33816,9 +35421,11 @@ public final class ContextOuterClass {
       if (size != -1) return size;
 
       size = 0;
-      for (int i = 0; i < connections_.size(); i++) {
-        size += com.google.protobuf.CodedOutputStream
-          .computeMessageSize(1, connections_.get(i));
+      if (!getConstraintTypeBytes().isEmpty()) {
+        size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, constraintType_);
+      }
+      if (!getConstraintValueBytes().isEmpty()) {
+        size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, constraintValue_);
       }
       size += unknownFields.getSerializedSize();
       memoizedSize = size;
@@ -33830,13 +35437,15 @@ public final class ContextOuterClass {
       if (obj == this) {
        return true;
       }
-      if (!(obj instanceof context.ContextOuterClass.ConnectionList)) {
+      if (!(obj instanceof context.ContextOuterClass.Constraint)) {
         return super.equals(obj);
       }
-      context.ContextOuterClass.ConnectionList other = (context.ContextOuterClass.ConnectionList) obj;
+      context.ContextOuterClass.Constraint other = (context.ContextOuterClass.Constraint) obj;
 
-      if (!getConnectionsList()
-          .equals(other.getConnectionsList())) return false;
+      if (!getConstraintType()
+          .equals(other.getConstraintType())) return false;
+      if (!getConstraintValue()
+          .equals(other.getConstraintValue())) return false;
       if (!unknownFields.equals(other.unknownFields)) return false;
       return true;
     }
@@ -33848,78 +35457,78 @@ public final class ContextOuterClass {
       }
       int hash = 41;
       hash = (19 * hash) + getDescriptor().hashCode();
-      if (getConnectionsCount() > 0) {
-        hash = (37 * hash) + CONNECTIONS_FIELD_NUMBER;
-        hash = (53 * hash) + getConnectionsList().hashCode();
-      }
+      hash = (37 * hash) + CONSTRAINT_TYPE_FIELD_NUMBER;
+      hash = (53 * hash) + getConstraintType().hashCode();
+      hash = (37 * hash) + CONSTRAINT_VALUE_FIELD_NUMBER;
+      hash = (53 * hash) + getConstraintValue().hashCode();
       hash = (29 * hash) + unknownFields.hashCode();
       memoizedHashCode = hash;
       return hash;
     }
 
-    public static context.ContextOuterClass.ConnectionList parseFrom(
+    public static context.ContextOuterClass.Constraint parseFrom(
         java.nio.ByteBuffer data)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data);
     }
-    public static context.ContextOuterClass.ConnectionList parseFrom(
+    public static context.ContextOuterClass.Constraint parseFrom(
         java.nio.ByteBuffer data,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data, extensionRegistry);
     }
-    public static context.ContextOuterClass.ConnectionList parseFrom(
+    public static context.ContextOuterClass.Constraint parseFrom(
         com.google.protobuf.ByteString data)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data);
     }
-    public static context.ContextOuterClass.ConnectionList parseFrom(
+    public static context.ContextOuterClass.Constraint parseFrom(
         com.google.protobuf.ByteString data,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data, extensionRegistry);
     }
-    public static context.ContextOuterClass.ConnectionList parseFrom(byte[] data)
+    public static context.ContextOuterClass.Constraint parseFrom(byte[] data)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data);
     }
-    public static context.ContextOuterClass.ConnectionList parseFrom(
+    public static context.ContextOuterClass.Constraint parseFrom(
         byte[] data,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data, extensionRegistry);
     }
-    public static context.ContextOuterClass.ConnectionList parseFrom(java.io.InputStream input)
+    public static context.ContextOuterClass.Constraint parseFrom(java.io.InputStream input)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseWithIOException(PARSER, input);
     }
-    public static context.ContextOuterClass.ConnectionList parseFrom(
+    public static context.ContextOuterClass.Constraint parseFrom(
         java.io.InputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseWithIOException(PARSER, input, extensionRegistry);
     }
-    public static context.ContextOuterClass.ConnectionList parseDelimitedFrom(java.io.InputStream input)
+    public static context.ContextOuterClass.Constraint parseDelimitedFrom(java.io.InputStream input)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseDelimitedWithIOException(PARSER, input);
     }
-    public static context.ContextOuterClass.ConnectionList parseDelimitedFrom(
+    public static context.ContextOuterClass.Constraint parseDelimitedFrom(
         java.io.InputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseDelimitedWithIOException(PARSER, input, extensionRegistry);
     }
-    public static context.ContextOuterClass.ConnectionList parseFrom(
+    public static context.ContextOuterClass.Constraint parseFrom(
         com.google.protobuf.CodedInputStream input)
         throws java.io.IOException {
       return com.google.protobuf.GeneratedMessageV3
           .parseWithIOException(PARSER, input);
     }
-    public static context.ContextOuterClass.ConnectionList parseFrom(
+    public static context.ContextOuterClass.Constraint parseFrom(
         com.google.protobuf.CodedInputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws java.io.IOException {
@@ -33932,7 +35541,7 @@ public final class ContextOuterClass {
     public static Builder newBuilder() {
       return DEFAULT_INSTANCE.toBuilder();
     }
-    public static Builder newBuilder(context.ContextOuterClass.ConnectionList prototype) {
+    public static Builder newBuilder(context.ContextOuterClass.Constraint prototype) {
       return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
     }
     @java.lang.Override
@@ -33948,26 +35557,30 @@ public final class ContextOuterClass {
       return builder;
     }
     /**
-     * Protobuf type {@code context.ConnectionList}
+     * <pre>
+     * ----- Constraint ----------------------------------------------------------------------------------------------------
+     * </pre>
+     *
+     * Protobuf type {@code context.Constraint}
      */
     public static final class Builder extends
         com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
-        // @@protoc_insertion_point(builder_implements:context.ConnectionList)
-        context.ContextOuterClass.ConnectionListOrBuilder {
+        // @@protoc_insertion_point(builder_implements:context.Constraint)
+        context.ContextOuterClass.ConstraintOrBuilder {
       public static final com.google.protobuf.Descriptors.Descriptor
           getDescriptor() {
-        return context.ContextOuterClass.internal_static_context_ConnectionList_descriptor;
+        return context.ContextOuterClass.internal_static_context_Constraint_descriptor;
       }
 
       @java.lang.Override
       protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
           internalGetFieldAccessorTable() {
-        return context.ContextOuterClass.internal_static_context_ConnectionList_fieldAccessorTable
+        return context.ContextOuterClass.internal_static_context_Constraint_fieldAccessorTable
             .ensureFieldAccessorsInitialized(
-                context.ContextOuterClass.ConnectionList.class, context.ContextOuterClass.ConnectionList.Builder.class);
+                context.ContextOuterClass.Constraint.class, context.ContextOuterClass.Constraint.Builder.class);
       }
 
-      // Construct using context.ContextOuterClass.ConnectionList.newBuilder()
+      // Construct using context.ContextOuterClass.Constraint.newBuilder()
       private Builder() {
         maybeForceBuilderInitialization();
       }
@@ -33979,36 +35592,33 @@ public final class ContextOuterClass {
       }
       private void maybeForceBuilderInitialization() {
         if (com.google.protobuf.GeneratedMessageV3
-                .alwaysUseFieldBuilders) {
-          getConnectionsFieldBuilder();
-        }
-      }
-      @java.lang.Override
-      public Builder clear() {
-        super.clear();
-        if (connectionsBuilder_ == null) {
-          connections_ = java.util.Collections.emptyList();
-          bitField0_ = (bitField0_ & ~0x00000001);
-        } else {
-          connectionsBuilder_.clear();
+                .alwaysUseFieldBuilders) {
         }
+      }
+      @java.lang.Override
+      public Builder clear() {
+        super.clear();
+        constraintType_ = "";
+
+        constraintValue_ = "";
+
         return this;
       }
 
       @java.lang.Override
       public com.google.protobuf.Descriptors.Descriptor
           getDescriptorForType() {
-        return context.ContextOuterClass.internal_static_context_ConnectionList_descriptor;
+        return context.ContextOuterClass.internal_static_context_Constraint_descriptor;
       }
 
       @java.lang.Override
-      public context.ContextOuterClass.ConnectionList getDefaultInstanceForType() {
-        return context.ContextOuterClass.ConnectionList.getDefaultInstance();
+      public context.ContextOuterClass.Constraint getDefaultInstanceForType() {
+        return context.ContextOuterClass.Constraint.getDefaultInstance();
       }
 
       @java.lang.Override
-      public context.ContextOuterClass.ConnectionList build() {
-        context.ContextOuterClass.ConnectionList result = buildPartial();
+      public context.ContextOuterClass.Constraint build() {
+        context.ContextOuterClass.Constraint result = buildPartial();
         if (!result.isInitialized()) {
           throw newUninitializedMessageException(result);
         }
@@ -34016,18 +35626,10 @@ public final class ContextOuterClass {
       }
 
       @java.lang.Override
-      public context.ContextOuterClass.ConnectionList buildPartial() {
-        context.ContextOuterClass.ConnectionList result = new context.ContextOuterClass.ConnectionList(this);
-        int from_bitField0_ = bitField0_;
-        if (connectionsBuilder_ == null) {
-          if (((bitField0_ & 0x00000001) != 0)) {
-            connections_ = java.util.Collections.unmodifiableList(connections_);
-            bitField0_ = (bitField0_ & ~0x00000001);
-          }
-          result.connections_ = connections_;
-        } else {
-          result.connections_ = connectionsBuilder_.build();
-        }
+      public context.ContextOuterClass.Constraint buildPartial() {
+        context.ContextOuterClass.Constraint result = new context.ContextOuterClass.Constraint(this);
+        result.constraintType_ = constraintType_;
+        result.constraintValue_ = constraintValue_;
         onBuilt();
         return result;
       }
@@ -34066,41 +35668,23 @@ public final class ContextOuterClass {
       }
       @java.lang.Override
       public Builder mergeFrom(com.google.protobuf.Message other) {
-        if (other instanceof context.ContextOuterClass.ConnectionList) {
-          return mergeFrom((context.ContextOuterClass.ConnectionList)other);
+        if (other instanceof context.ContextOuterClass.Constraint) {
+          return mergeFrom((context.ContextOuterClass.Constraint)other);
         } else {
           super.mergeFrom(other);
           return this;
         }
       }
 
-      public Builder mergeFrom(context.ContextOuterClass.ConnectionList other) {
-        if (other == context.ContextOuterClass.ConnectionList.getDefaultInstance()) return this;
-        if (connectionsBuilder_ == null) {
-          if (!other.connections_.isEmpty()) {
-            if (connections_.isEmpty()) {
-              connections_ = other.connections_;
-              bitField0_ = (bitField0_ & ~0x00000001);
-            } else {
-              ensureConnectionsIsMutable();
-              connections_.addAll(other.connections_);
-            }
-            onChanged();
-          }
-        } else {
-          if (!other.connections_.isEmpty()) {
-            if (connectionsBuilder_.isEmpty()) {
-              connectionsBuilder_.dispose();
-              connectionsBuilder_ = null;
-              connections_ = other.connections_;
-              bitField0_ = (bitField0_ & ~0x00000001);
-              connectionsBuilder_ = 
-                com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders ?
-                   getConnectionsFieldBuilder() : null;
-            } else {
-              connectionsBuilder_.addAllMessages(other.connections_);
-            }
-          }
+      public Builder mergeFrom(context.ContextOuterClass.Constraint other) {
+        if (other == context.ContextOuterClass.Constraint.getDefaultInstance()) return this;
+        if (!other.getConstraintType().isEmpty()) {
+          constraintType_ = other.constraintType_;
+          onChanged();
+        }
+        if (!other.getConstraintValue().isEmpty()) {
+          constraintValue_ = other.constraintValue_;
+          onChanged();
         }
         this.mergeUnknownFields(other.unknownFields);
         onChanged();
@@ -34117,11 +35701,11 @@ public final class ContextOuterClass {
           com.google.protobuf.CodedInputStream input,
           com.google.protobuf.ExtensionRegistryLite extensionRegistry)
           throws java.io.IOException {
-        context.ContextOuterClass.ConnectionList parsedMessage = null;
+        context.ContextOuterClass.Constraint parsedMessage = null;
         try {
           parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
         } catch (com.google.protobuf.InvalidProtocolBufferException e) {
-          parsedMessage = (context.ContextOuterClass.ConnectionList) e.getUnfinishedMessage();
+          parsedMessage = (context.ContextOuterClass.Constraint) e.getUnfinishedMessage();
           throw e.unwrapIOException();
         } finally {
           if (parsedMessage != null) {
@@ -34130,246 +35714,157 @@ public final class ContextOuterClass {
         }
         return this;
       }
-      private int bitField0_;
-
-      private java.util.List<context.ContextOuterClass.Connection> connections_ =
-        java.util.Collections.emptyList();
-      private void ensureConnectionsIsMutable() {
-        if (!((bitField0_ & 0x00000001) != 0)) {
-          connections_ = new java.util.ArrayList<context.ContextOuterClass.Connection>(connections_);
-          bitField0_ |= 0x00000001;
-         }
-      }
-
-      private com.google.protobuf.RepeatedFieldBuilderV3<
-          context.ContextOuterClass.Connection, context.ContextOuterClass.Connection.Builder, context.ContextOuterClass.ConnectionOrBuilder> connectionsBuilder_;
 
+      private java.lang.Object constraintType_ = "";
       /**
-       * <code>repeated .context.Connection connections = 1;</code>
-       */
-      public java.util.List<context.ContextOuterClass.Connection> getConnectionsList() {
-        if (connectionsBuilder_ == null) {
-          return java.util.Collections.unmodifiableList(connections_);
-        } else {
-          return connectionsBuilder_.getMessageList();
-        }
-      }
-      /**
-       * <code>repeated .context.Connection connections = 1;</code>
-       */
-      public int getConnectionsCount() {
-        if (connectionsBuilder_ == null) {
-          return connections_.size();
-        } else {
-          return connectionsBuilder_.getCount();
-        }
-      }
-      /**
-       * <code>repeated .context.Connection connections = 1;</code>
-       */
-      public context.ContextOuterClass.Connection getConnections(int index) {
-        if (connectionsBuilder_ == null) {
-          return connections_.get(index);
-        } else {
-          return connectionsBuilder_.getMessage(index);
-        }
-      }
-      /**
-       * <code>repeated .context.Connection connections = 1;</code>
-       */
-      public Builder setConnections(
-          int index, context.ContextOuterClass.Connection value) {
-        if (connectionsBuilder_ == null) {
-          if (value == null) {
-            throw new NullPointerException();
-          }
-          ensureConnectionsIsMutable();
-          connections_.set(index, value);
-          onChanged();
-        } else {
-          connectionsBuilder_.setMessage(index, value);
-        }
-        return this;
-      }
-      /**
-       * <code>repeated .context.Connection connections = 1;</code>
-       */
-      public Builder setConnections(
-          int index, context.ContextOuterClass.Connection.Builder builderForValue) {
-        if (connectionsBuilder_ == null) {
-          ensureConnectionsIsMutable();
-          connections_.set(index, builderForValue.build());
-          onChanged();
-        } else {
-          connectionsBuilder_.setMessage(index, builderForValue.build());
-        }
-        return this;
-      }
-      /**
-       * <code>repeated .context.Connection connections = 1;</code>
-       */
-      public Builder addConnections(context.ContextOuterClass.Connection value) {
-        if (connectionsBuilder_ == null) {
-          if (value == null) {
-            throw new NullPointerException();
-          }
-          ensureConnectionsIsMutable();
-          connections_.add(value);
-          onChanged();
-        } else {
-          connectionsBuilder_.addMessage(value);
-        }
-        return this;
-      }
-      /**
-       * <code>repeated .context.Connection connections = 1;</code>
-       */
-      public Builder addConnections(
-          int index, context.ContextOuterClass.Connection value) {
-        if (connectionsBuilder_ == null) {
-          if (value == null) {
-            throw new NullPointerException();
-          }
-          ensureConnectionsIsMutable();
-          connections_.add(index, value);
-          onChanged();
-        } else {
-          connectionsBuilder_.addMessage(index, value);
-        }
-        return this;
-      }
-      /**
-       * <code>repeated .context.Connection connections = 1;</code>
-       */
-      public Builder addConnections(
-          context.ContextOuterClass.Connection.Builder builderForValue) {
-        if (connectionsBuilder_ == null) {
-          ensureConnectionsIsMutable();
-          connections_.add(builderForValue.build());
-          onChanged();
-        } else {
-          connectionsBuilder_.addMessage(builderForValue.build());
-        }
-        return this;
-      }
-      /**
-       * <code>repeated .context.Connection connections = 1;</code>
+       * <code>string constraint_type = 1;</code>
+       * @return The constraintType.
        */
-      public Builder addConnections(
-          int index, context.ContextOuterClass.Connection.Builder builderForValue) {
-        if (connectionsBuilder_ == null) {
-          ensureConnectionsIsMutable();
-          connections_.add(index, builderForValue.build());
-          onChanged();
+      public java.lang.String getConstraintType() {
+        java.lang.Object ref = constraintType_;
+        if (!(ref instanceof java.lang.String)) {
+          com.google.protobuf.ByteString bs =
+              (com.google.protobuf.ByteString) ref;
+          java.lang.String s = bs.toStringUtf8();
+          constraintType_ = s;
+          return s;
         } else {
-          connectionsBuilder_.addMessage(index, builderForValue.build());
+          return (java.lang.String) ref;
         }
-        return this;
       }
       /**
-       * <code>repeated .context.Connection connections = 1;</code>
+       * <code>string constraint_type = 1;</code>
+       * @return The bytes for constraintType.
        */
-      public Builder addAllConnections(
-          java.lang.Iterable<? extends context.ContextOuterClass.Connection> values) {
-        if (connectionsBuilder_ == null) {
-          ensureConnectionsIsMutable();
-          com.google.protobuf.AbstractMessageLite.Builder.addAll(
-              values, connections_);
-          onChanged();
+      public com.google.protobuf.ByteString
+          getConstraintTypeBytes() {
+        java.lang.Object ref = constraintType_;
+        if (ref instanceof String) {
+          com.google.protobuf.ByteString b = 
+              com.google.protobuf.ByteString.copyFromUtf8(
+                  (java.lang.String) ref);
+          constraintType_ = b;
+          return b;
         } else {
-          connectionsBuilder_.addAllMessages(values);
+          return (com.google.protobuf.ByteString) ref;
         }
-        return this;
       }
       /**
-       * <code>repeated .context.Connection connections = 1;</code>
-       */
-      public Builder clearConnections() {
-        if (connectionsBuilder_ == null) {
-          connections_ = java.util.Collections.emptyList();
-          bitField0_ = (bitField0_ & ~0x00000001);
-          onChanged();
-        } else {
-          connectionsBuilder_.clear();
-        }
+       * <code>string constraint_type = 1;</code>
+       * @param value The constraintType to set.
+       * @return This builder for chaining.
+       */
+      public Builder setConstraintType(
+          java.lang.String value) {
+        if (value == null) {
+    throw new NullPointerException();
+  }
+  
+        constraintType_ = value;
+        onChanged();
         return this;
       }
       /**
-       * <code>repeated .context.Connection connections = 1;</code>
+       * <code>string constraint_type = 1;</code>
+       * @return This builder for chaining.
        */
-      public Builder removeConnections(int index) {
-        if (connectionsBuilder_ == null) {
-          ensureConnectionsIsMutable();
-          connections_.remove(index);
-          onChanged();
-        } else {
-          connectionsBuilder_.remove(index);
-        }
+      public Builder clearConstraintType() {
+        
+        constraintType_ = getDefaultInstance().getConstraintType();
+        onChanged();
         return this;
       }
       /**
-       * <code>repeated .context.Connection connections = 1;</code>
+       * <code>string constraint_type = 1;</code>
+       * @param value The bytes for constraintType to set.
+       * @return This builder for chaining.
        */
-      public context.ContextOuterClass.Connection.Builder getConnectionsBuilder(
-          int index) {
-        return getConnectionsFieldBuilder().getBuilder(index);
+      public Builder setConstraintTypeBytes(
+          com.google.protobuf.ByteString value) {
+        if (value == null) {
+    throw new NullPointerException();
+  }
+  checkByteStringIsUtf8(value);
+        
+        constraintType_ = value;
+        onChanged();
+        return this;
       }
+
+      private java.lang.Object constraintValue_ = "";
       /**
-       * <code>repeated .context.Connection connections = 1;</code>
+       * <code>string constraint_value = 2;</code>
+       * @return The constraintValue.
        */
-      public context.ContextOuterClass.ConnectionOrBuilder getConnectionsOrBuilder(
-          int index) {
-        if (connectionsBuilder_ == null) {
-          return connections_.get(index);  } else {
-          return connectionsBuilder_.getMessageOrBuilder(index);
+      public java.lang.String getConstraintValue() {
+        java.lang.Object ref = constraintValue_;
+        if (!(ref instanceof java.lang.String)) {
+          com.google.protobuf.ByteString bs =
+              (com.google.protobuf.ByteString) ref;
+          java.lang.String s = bs.toStringUtf8();
+          constraintValue_ = s;
+          return s;
+        } else {
+          return (java.lang.String) ref;
         }
       }
       /**
-       * <code>repeated .context.Connection connections = 1;</code>
+       * <code>string constraint_value = 2;</code>
+       * @return The bytes for constraintValue.
        */
-      public java.util.List<? extends context.ContextOuterClass.ConnectionOrBuilder> 
-           getConnectionsOrBuilderList() {
-        if (connectionsBuilder_ != null) {
-          return connectionsBuilder_.getMessageOrBuilderList();
+      public com.google.protobuf.ByteString
+          getConstraintValueBytes() {
+        java.lang.Object ref = constraintValue_;
+        if (ref instanceof String) {
+          com.google.protobuf.ByteString b = 
+              com.google.protobuf.ByteString.copyFromUtf8(
+                  (java.lang.String) ref);
+          constraintValue_ = b;
+          return b;
         } else {
-          return java.util.Collections.unmodifiableList(connections_);
+          return (com.google.protobuf.ByteString) ref;
         }
       }
       /**
-       * <code>repeated .context.Connection connections = 1;</code>
+       * <code>string constraint_value = 2;</code>
+       * @param value The constraintValue to set.
+       * @return This builder for chaining.
        */
-      public context.ContextOuterClass.Connection.Builder addConnectionsBuilder() {
-        return getConnectionsFieldBuilder().addBuilder(
-            context.ContextOuterClass.Connection.getDefaultInstance());
+      public Builder setConstraintValue(
+          java.lang.String value) {
+        if (value == null) {
+    throw new NullPointerException();
+  }
+  
+        constraintValue_ = value;
+        onChanged();
+        return this;
       }
       /**
-       * <code>repeated .context.Connection connections = 1;</code>
+       * <code>string constraint_value = 2;</code>
+       * @return This builder for chaining.
        */
-      public context.ContextOuterClass.Connection.Builder addConnectionsBuilder(
-          int index) {
-        return getConnectionsFieldBuilder().addBuilder(
-            index, context.ContextOuterClass.Connection.getDefaultInstance());
+      public Builder clearConstraintValue() {
+        
+        constraintValue_ = getDefaultInstance().getConstraintValue();
+        onChanged();
+        return this;
       }
       /**
-       * <code>repeated .context.Connection connections = 1;</code>
+       * <code>string constraint_value = 2;</code>
+       * @param value The bytes for constraintValue to set.
+       * @return This builder for chaining.
        */
-      public java.util.List<context.ContextOuterClass.Connection.Builder> 
-           getConnectionsBuilderList() {
-        return getConnectionsFieldBuilder().getBuilderList();
-      }
-      private com.google.protobuf.RepeatedFieldBuilderV3<
-          context.ContextOuterClass.Connection, context.ContextOuterClass.Connection.Builder, context.ContextOuterClass.ConnectionOrBuilder> 
-          getConnectionsFieldBuilder() {
-        if (connectionsBuilder_ == null) {
-          connectionsBuilder_ = new com.google.protobuf.RepeatedFieldBuilderV3<
-              context.ContextOuterClass.Connection, context.ContextOuterClass.Connection.Builder, context.ContextOuterClass.ConnectionOrBuilder>(
-                  connections_,
-                  ((bitField0_ & 0x00000001) != 0),
-                  getParentForChildren(),
-                  isClean());
-          connections_ = null;
-        }
-        return connectionsBuilder_;
+      public Builder setConstraintValueBytes(
+          com.google.protobuf.ByteString value) {
+        if (value == null) {
+    throw new NullPointerException();
+  }
+  checkByteStringIsUtf8(value);
+        
+        constraintValue_ = value;
+        onChanged();
+        return this;
       }
       @java.lang.Override
       public final Builder setUnknownFields(
@@ -34384,41 +35879,41 @@ public final class ContextOuterClass {
       }
 
 
-      // @@protoc_insertion_point(builder_scope:context.ConnectionList)
+      // @@protoc_insertion_point(builder_scope:context.Constraint)
     }
 
-    // @@protoc_insertion_point(class_scope:context.ConnectionList)
-    private static final context.ContextOuterClass.ConnectionList DEFAULT_INSTANCE;
+    // @@protoc_insertion_point(class_scope:context.Constraint)
+    private static final context.ContextOuterClass.Constraint DEFAULT_INSTANCE;
     static {
-      DEFAULT_INSTANCE = new context.ContextOuterClass.ConnectionList();
+      DEFAULT_INSTANCE = new context.ContextOuterClass.Constraint();
     }
 
-    public static context.ContextOuterClass.ConnectionList getDefaultInstance() {
+    public static context.ContextOuterClass.Constraint getDefaultInstance() {
       return DEFAULT_INSTANCE;
     }
 
-    private static final com.google.protobuf.Parser<ConnectionList>
-        PARSER = new com.google.protobuf.AbstractParser<ConnectionList>() {
+    private static final com.google.protobuf.Parser<Constraint>
+        PARSER = new com.google.protobuf.AbstractParser<Constraint>() {
       @java.lang.Override
-      public ConnectionList parsePartialFrom(
+      public Constraint parsePartialFrom(
           com.google.protobuf.CodedInputStream input,
           com.google.protobuf.ExtensionRegistryLite extensionRegistry)
           throws com.google.protobuf.InvalidProtocolBufferException {
-        return new ConnectionList(input, extensionRegistry);
+        return new Constraint(input, extensionRegistry);
       }
     };
 
-    public static com.google.protobuf.Parser<ConnectionList> parser() {
+    public static com.google.protobuf.Parser<Constraint> parser() {
       return PARSER;
     }
 
     @java.lang.Override
-    public com.google.protobuf.Parser<ConnectionList> getParserForType() {
+    public com.google.protobuf.Parser<Constraint> getParserForType() {
       return PARSER;
     }
 
     @java.lang.Override
-    public context.ContextOuterClass.ConnectionList getDefaultInstanceForType() {
+    public context.ContextOuterClass.Constraint getDefaultInstanceForType() {
       return DEFAULT_INSTANCE;
     }
 
@@ -36121,45 +37616,50 @@ public final class ContextOuterClass {
     com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
       internal_static_context_ServiceEvent_fieldAccessorTable;
   private static final com.google.protobuf.Descriptors.Descriptor
-    internal_static_context_EndPointId_descriptor;
+    internal_static_context_ConnectionId_descriptor;
   private static final 
     com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
-      internal_static_context_EndPointId_fieldAccessorTable;
+      internal_static_context_ConnectionId_fieldAccessorTable;
   private static final com.google.protobuf.Descriptors.Descriptor
-    internal_static_context_EndPoint_descriptor;
+    internal_static_context_Connection_descriptor;
   private static final 
     com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
-      internal_static_context_EndPoint_fieldAccessorTable;
+      internal_static_context_Connection_fieldAccessorTable;
   private static final com.google.protobuf.Descriptors.Descriptor
-    internal_static_context_ConfigRule_descriptor;
+    internal_static_context_ConnectionIdList_descriptor;
   private static final 
     com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
-      internal_static_context_ConfigRule_fieldAccessorTable;
+      internal_static_context_ConnectionIdList_fieldAccessorTable;
   private static final com.google.protobuf.Descriptors.Descriptor
-    internal_static_context_Constraint_descriptor;
+    internal_static_context_ConnectionList_descriptor;
   private static final 
     com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
-      internal_static_context_Constraint_fieldAccessorTable;
+      internal_static_context_ConnectionList_fieldAccessorTable;
   private static final com.google.protobuf.Descriptors.Descriptor
-    internal_static_context_ConnectionId_descriptor;
+    internal_static_context_ConnectionEvent_descriptor;
   private static final 
     com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
-      internal_static_context_ConnectionId_fieldAccessorTable;
+      internal_static_context_ConnectionEvent_fieldAccessorTable;
   private static final com.google.protobuf.Descriptors.Descriptor
-    internal_static_context_Connection_descriptor;
+    internal_static_context_EndPointId_descriptor;
   private static final 
     com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
-      internal_static_context_Connection_fieldAccessorTable;
+      internal_static_context_EndPointId_fieldAccessorTable;
   private static final com.google.protobuf.Descriptors.Descriptor
-    internal_static_context_ConnectionIdList_descriptor;
+    internal_static_context_EndPoint_descriptor;
   private static final 
     com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
-      internal_static_context_ConnectionIdList_fieldAccessorTable;
+      internal_static_context_EndPoint_fieldAccessorTable;
   private static final com.google.protobuf.Descriptors.Descriptor
-    internal_static_context_ConnectionList_descriptor;
+    internal_static_context_ConfigRule_descriptor;
   private static final 
     com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
-      internal_static_context_ConnectionList_fieldAccessorTable;
+      internal_static_context_ConfigRule_fieldAccessorTable;
+  private static final com.google.protobuf.Descriptors.Descriptor
+    internal_static_context_Constraint_descriptor;
+  private static final 
+    com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+      internal_static_context_Constraint_fieldAccessorTable;
   private static final com.google.protobuf.Descriptors.Descriptor
     internal_static_context_TeraFlowController_descriptor;
   private static final 
@@ -36179,160 +37679,177 @@ public final class ContextOuterClass {
       descriptor;
   static {
     java.lang.String[] descriptorData = {
-      "\n\rcontext.proto\022\007context\"\007\n\005Empty\"\024\n\004Uui" +
-      "d\022\014\n\004uuid\030\001 \001(\t\"F\n\005Event\022\021\n\ttimestamp\030\001 " +
-      "\001(\001\022*\n\nevent_type\030\002 \001(\0162\026.context.EventT" +
-      "ypeEnum\"0\n\tContextId\022#\n\014context_uuid\030\001 \001" +
-      "(\0132\r.context.Uuid\"\266\001\n\007Context\022&\n\ncontext" +
-      "_id\030\001 \001(\0132\022.context.ContextId\022)\n\014topolog" +
-      "y_ids\030\002 \003(\0132\023.context.TopologyId\022\'\n\013serv" +
-      "ice_ids\030\003 \003(\0132\022.context.ServiceId\022/\n\ncon" +
-      "troller\030\004 \001(\0132\033.context.TeraFlowControll" +
-      "er\"8\n\rContextIdList\022\'\n\013context_ids\030\001 \003(\013" +
-      "2\022.context.ContextId\"1\n\013ContextList\022\"\n\010c" +
-      "ontexts\030\001 \003(\0132\020.context.Context\"U\n\014Conte" +
-      "xtEvent\022\035\n\005event\030\001 \001(\0132\016.context.Event\022&" +
-      "\n\ncontext_id\030\002 \001(\0132\022.context.ContextId\"Z" +
-      "\n\nTopologyId\022&\n\ncontext_id\030\001 \001(\0132\022.conte" +
-      "xt.ContextId\022$\n\rtopology_uuid\030\002 \001(\0132\r.co" +
-      "ntext.Uuid\"~\n\010Topology\022(\n\013topology_id\030\001 " +
-      "\001(\0132\023.context.TopologyId\022%\n\ndevice_ids\030\002" +
-      " \003(\0132\021.context.DeviceId\022!\n\010link_ids\030\003 \003(" +
-      "\0132\017.context.LinkId\";\n\016TopologyIdList\022)\n\014" +
-      "topology_ids\030\001 \003(\0132\023.context.TopologyId\"" +
-      "5\n\014TopologyList\022%\n\ntopologies\030\001 \003(\0132\021.co" +
-      "ntext.Topology\"X\n\rTopologyEvent\022\035\n\005event" +
-      "\030\001 \001(\0132\016.context.Event\022(\n\013topology_id\030\002 " +
-      "\001(\0132\023.context.TopologyId\".\n\010DeviceId\022\"\n\013" +
-      "device_uuid\030\001 \001(\0132\r.context.Uuid\"\232\002\n\006Dev" +
-      "ice\022$\n\tdevice_id\030\001 \001(\0132\021.context.DeviceI" +
-      "d\022\023\n\013device_type\030\002 \001(\t\022,\n\rdevice_config\030" +
-      "\003 \001(\0132\025.context.DeviceConfig\022G\n\031device_o" +
-      "perational_status\030\004 \001(\0162$.context.Device" +
-      "OperationalStatusEnum\0221\n\016device_drivers\030" +
-      "\005 \003(\0162\031.context.DeviceDriverEnum\022+\n\020devi" +
-      "ce_endpoints\030\006 \003(\0132\021.context.EndPoint\"9\n" +
-      "\014DeviceConfig\022)\n\014config_rules\030\001 \003(\0132\023.co" +
-      "ntext.ConfigRule\"5\n\014DeviceIdList\022%\n\ndevi" +
-      "ce_ids\030\001 \003(\0132\021.context.DeviceId\".\n\nDevic" +
-      "eList\022 \n\007devices\030\001 \003(\0132\017.context.Device\"" +
-      "R\n\013DeviceEvent\022\035\n\005event\030\001 \001(\0132\016.context." +
-      "Event\022$\n\tdevice_id\030\002 \001(\0132\021.context.Devic" +
-      "eId\"*\n\006LinkId\022 \n\tlink_uuid\030\001 \001(\0132\r.conte" +
-      "xt.Uuid\"X\n\004Link\022 \n\007link_id\030\001 \001(\0132\017.conte" +
-      "xt.LinkId\022.\n\021link_endpoint_ids\030\002 \003(\0132\023.c" +
-      "ontext.EndPointId\"/\n\nLinkIdList\022!\n\010link_" +
-      "ids\030\001 \003(\0132\017.context.LinkId\"(\n\010LinkList\022\034" +
-      "\n\005links\030\001 \003(\0132\r.context.Link\"L\n\tLinkEven" +
-      "t\022\035\n\005event\030\001 \001(\0132\016.context.Event\022 \n\007link" +
-      "_id\030\002 \001(\0132\017.context.LinkId\"X\n\tServiceId\022" +
-      "&\n\ncontext_id\030\001 \001(\0132\022.context.ContextId\022" +
-      "#\n\014service_uuid\030\002 \001(\0132\r.context.Uuid\"\246\002\n" +
-      "\007Service\022&\n\nservice_id\030\001 \001(\0132\022.context.S" +
-      "erviceId\022.\n\014service_type\030\002 \001(\0162\030.context" +
-      ".ServiceTypeEnum\0221\n\024service_endpoint_ids" +
-      "\030\003 \003(\0132\023.context.EndPointId\0220\n\023service_c" +
-      "onstraints\030\004 \003(\0132\023.context.Constraint\022.\n" +
-      "\016service_status\030\005 \001(\0132\026.context.ServiceS" +
-      "tatus\022.\n\016service_config\030\006 \001(\0132\026.context." +
-      "ServiceConfig\"C\n\rServiceStatus\0222\n\016servic" +
-      "e_status\030\001 \001(\0162\032.context.ServiceStatusEn" +
-      "um\":\n\rServiceConfig\022)\n\014config_rules\030\001 \003(" +
-      "\0132\023.context.ConfigRule\"8\n\rServiceIdList\022" +
-      "\'\n\013service_ids\030\001 \003(\0132\022.context.ServiceId" +
-      "\"1\n\013ServiceList\022\"\n\010services\030\001 \003(\0132\020.cont" +
-      "ext.Service\"U\n\014ServiceEvent\022\035\n\005event\030\001 \001" +
-      "(\0132\016.context.Event\022&\n\nservice_id\030\002 \001(\0132\022" +
-      ".context.ServiceId\"\202\001\n\nEndPointId\022(\n\013top" +
-      "ology_id\030\001 \001(\0132\023.context.TopologyId\022$\n\td" +
-      "evice_id\030\002 \001(\0132\021.context.DeviceId\022$\n\rend" +
-      "point_uuid\030\003 \001(\0132\r.context.Uuid\"K\n\010EndPo" +
-      "int\022(\n\013endpoint_id\030\001 \001(\0132\023.context.EndPo" +
-      "intId\022\025\n\rendpoint_type\030\002 \001(\t\"e\n\nConfigRu" +
-      "le\022)\n\006action\030\001 \001(\0162\031.context.ConfigActio" +
-      "nEnum\022\024\n\014resource_key\030\002 \001(\t\022\026\n\016resource_" +
-      "value\030\003 \001(\t\"?\n\nConstraint\022\027\n\017constraint_" +
-      "type\030\001 \001(\t\022\030\n\020constraint_value\030\002 \001(\t\"6\n\014" +
-      "ConnectionId\022&\n\017connection_uuid\030\001 \001(\0132\r." +
-      "context.Uuid\"\215\001\n\nConnection\022,\n\rconnectio" +
-      "n_id\030\001 \001(\0132\025.context.ConnectionId\022.\n\022rel" +
-      "ated_service_id\030\002 \001(\0132\022.context.ServiceI" +
-      "d\022!\n\004path\030\003 \003(\0132\023.context.EndPointId\"A\n\020" +
-      "ConnectionIdList\022-\n\016connection_ids\030\001 \003(\013" +
-      "2\025.context.ConnectionId\":\n\016ConnectionLis" +
-      "t\022(\n\013connections\030\001 \003(\0132\023.context.Connect" +
-      "ion\"^\n\022TeraFlowController\022&\n\ncontext_id\030" +
-      "\001 \001(\0132\022.context.ContextId\022\022\n\nip_address\030" +
-      "\002 \001(\t\022\014\n\004port\030\003 \001(\r\"U\n\024AuthenticationRes" +
-      "ult\022&\n\ncontext_id\030\001 \001(\0132\022.context.Contex" +
-      "tId\022\025\n\rauthenticated\030\002 \001(\010*j\n\rEventTypeE" +
-      "num\022\027\n\023EVENTTYPE_UNDEFINED\020\000\022\024\n\020EVENTTYP" +
-      "E_CREATE\020\001\022\024\n\020EVENTTYPE_UPDATE\020\002\022\024\n\020EVEN" +
-      "TTYPE_REMOVE\020\003*\305\001\n\020DeviceDriverEnum\022\032\n\026D" +
-      "EVICEDRIVER_UNDEFINED\020\000\022\033\n\027DEVICEDRIVER_" +
-      "OPENCONFIG\020\001\022\036\n\032DEVICEDRIVER_TRANSPORT_A" +
-      "PI\020\002\022\023\n\017DEVICEDRIVER_P4\020\003\022&\n\"DEVICEDRIVE" +
-      "R_IETF_NETWORK_TOPOLOGY\020\004\022\033\n\027DEVICEDRIVE" +
-      "R_ONF_TR_352\020\005*\217\001\n\033DeviceOperationalStat" +
-      "usEnum\022%\n!DEVICEOPERATIONALSTATUS_UNDEFI" +
-      "NED\020\000\022$\n DEVICEOPERATIONALSTATUS_DISABLE" +
-      "D\020\001\022#\n\037DEVICEOPERATIONALSTATUS_ENABLED\020\002" +
-      "*\201\001\n\017ServiceTypeEnum\022\027\n\023SERVICETYPE_UNKN" +
-      "OWN\020\000\022\024\n\020SERVICETYPE_L3NM\020\001\022\024\n\020SERVICETY" +
-      "PE_L2NM\020\002\022)\n%SERVICETYPE_TAPI_CONNECTIVI" +
-      "TY_SERVICE\020\003*\210\001\n\021ServiceStatusEnum\022\033\n\027SE" +
-      "RVICESTATUS_UNDEFINED\020\000\022\031\n\025SERVICESTATUS" +
-      "_PLANNED\020\001\022\030\n\024SERVICESTATUS_ACTIVE\020\002\022!\n\035" +
-      "SERVICESTATUS_PENDING_REMOVAL\020\003*]\n\020Confi" +
-      "gActionEnum\022\032\n\026CONFIGACTION_UNDEFINED\020\000\022" +
-      "\024\n\020CONFIGACTION_SET\020\001\022\027\n\023CONFIGACTION_DE" +
-      "LETE\020\0022\245\r\n\016ContextService\022:\n\016ListContext" +
-      "Ids\022\016.context.Empty\032\026.context.ContextIdL" +
-      "ist\"\000\0226\n\014ListContexts\022\016.context.Empty\032\024." +
-      "context.ContextList\"\000\0224\n\nGetContext\022\022.co" +
-      "ntext.ContextId\032\020.context.Context\"\000\0224\n\nS" +
-      "etContext\022\020.context.Context\032\022.context.Co" +
-      "ntextId\"\000\0225\n\rRemoveContext\022\022.context.Con" +
-      "textId\032\016.context.Empty\"\000\022=\n\020GetContextEv" +
-      "ents\022\016.context.Empty\032\025.context.ContextEv" +
-      "ent\"\0000\001\022@\n\017ListTopologyIds\022\022.context.Con" +
-      "textId\032\027.context.TopologyIdList\"\000\022=\n\016Lis" +
-      "tTopologies\022\022.context.ContextId\032\025.contex" +
-      "t.TopologyList\"\000\0227\n\013GetTopology\022\023.contex" +
-      "t.TopologyId\032\021.context.Topology\"\000\0227\n\013Set" +
-      "Topology\022\021.context.Topology\032\023.context.To" +
-      "pologyId\"\000\0227\n\016RemoveTopology\022\023.context.T" +
-      "opologyId\032\016.context.Empty\"\000\022?\n\021GetTopolo" +
-      "gyEvents\022\016.context.Empty\032\026.context.Topol" +
-      "ogyEvent\"\0000\001\0228\n\rListDeviceIds\022\016.context." +
-      "Empty\032\025.context.DeviceIdList\"\000\0224\n\013ListDe" +
-      "vices\022\016.context.Empty\032\023.context.DeviceLi" +
-      "st\"\000\0221\n\tGetDevice\022\021.context.DeviceId\032\017.c" +
-      "ontext.Device\"\000\0221\n\tSetDevice\022\017.context.D" +
-      "evice\032\021.context.DeviceId\"\000\0223\n\014RemoveDevi" +
-      "ce\022\021.context.DeviceId\032\016.context.Empty\"\000\022" +
-      ";\n\017GetDeviceEvents\022\016.context.Empty\032\024.con" +
-      "text.DeviceEvent\"\0000\001\0224\n\013ListLinkIds\022\016.co" +
-      "ntext.Empty\032\023.context.LinkIdList\"\000\0220\n\tLi" +
-      "stLinks\022\016.context.Empty\032\021.context.LinkLi" +
-      "st\"\000\022+\n\007GetLink\022\017.context.LinkId\032\r.conte" +
-      "xt.Link\"\000\022+\n\007SetLink\022\r.context.Link\032\017.co" +
-      "ntext.LinkId\"\000\022/\n\nRemoveLink\022\017.context.L" +
-      "inkId\032\016.context.Empty\"\000\0227\n\rGetLinkEvents" +
-      "\022\016.context.Empty\032\022.context.LinkEvent\"\0000\001" +
-      "\022>\n\016ListServiceIds\022\022.context.ContextId\032\026" +
-      ".context.ServiceIdList\"\000\022:\n\014ListServices" +
-      "\022\022.context.ContextId\032\024.context.ServiceLi" +
-      "st\"\000\0224\n\nGetService\022\022.context.ServiceId\032\020" +
-      ".context.Service\"\000\0224\n\nSetService\022\020.conte" +
-      "xt.Service\032\022.context.ServiceId\"\000\0225\n\rRemo" +
-      "veService\022\022.context.ServiceId\032\016.context." +
-      "Empty\"\000\022=\n\020GetServiceEvents\022\016.context.Em" +
-      "pty\032\025.context.ServiceEvent\"\0000\001b\006proto3"
+      "\n\rcontext.proto\022\007context\032\026kpi_sample_typ" +
+      "es.proto\"\007\n\005Empty\"\024\n\004Uuid\022\014\n\004uuid\030\001 \001(\t\"" +
+      "F\n\005Event\022\021\n\ttimestamp\030\001 \001(\001\022*\n\nevent_typ" +
+      "e\030\002 \001(\0162\026.context.EventTypeEnum\"0\n\tConte" +
+      "xtId\022#\n\014context_uuid\030\001 \001(\0132\r.context.Uui" +
+      "d\"\266\001\n\007Context\022&\n\ncontext_id\030\001 \001(\0132\022.cont" +
+      "ext.ContextId\022)\n\014topology_ids\030\002 \003(\0132\023.co" +
+      "ntext.TopologyId\022\'\n\013service_ids\030\003 \003(\0132\022." +
+      "context.ServiceId\022/\n\ncontroller\030\004 \001(\0132\033." +
+      "context.TeraFlowController\"8\n\rContextIdL" +
+      "ist\022\'\n\013context_ids\030\001 \003(\0132\022.context.Conte" +
+      "xtId\"1\n\013ContextList\022\"\n\010contexts\030\001 \003(\0132\020." +
+      "context.Context\"U\n\014ContextEvent\022\035\n\005event" +
+      "\030\001 \001(\0132\016.context.Event\022&\n\ncontext_id\030\002 \001" +
+      "(\0132\022.context.ContextId\"Z\n\nTopologyId\022&\n\n" +
+      "context_id\030\001 \001(\0132\022.context.ContextId\022$\n\r" +
+      "topology_uuid\030\002 \001(\0132\r.context.Uuid\"~\n\010To" +
+      "pology\022(\n\013topology_id\030\001 \001(\0132\023.context.To" +
+      "pologyId\022%\n\ndevice_ids\030\002 \003(\0132\021.context.D" +
+      "eviceId\022!\n\010link_ids\030\003 \003(\0132\017.context.Link" +
+      "Id\";\n\016TopologyIdList\022)\n\014topology_ids\030\001 \003" +
+      "(\0132\023.context.TopologyId\"5\n\014TopologyList\022" +
+      "%\n\ntopologies\030\001 \003(\0132\021.context.Topology\"X" +
+      "\n\rTopologyEvent\022\035\n\005event\030\001 \001(\0132\016.context" +
+      ".Event\022(\n\013topology_id\030\002 \001(\0132\023.context.To" +
+      "pologyId\".\n\010DeviceId\022\"\n\013device_uuid\030\001 \001(" +
+      "\0132\r.context.Uuid\"\232\002\n\006Device\022$\n\tdevice_id" +
+      "\030\001 \001(\0132\021.context.DeviceId\022\023\n\013device_type" +
+      "\030\002 \001(\t\022,\n\rdevice_config\030\003 \001(\0132\025.context." +
+      "DeviceConfig\022G\n\031device_operational_statu" +
+      "s\030\004 \001(\0162$.context.DeviceOperationalStatu" +
+      "sEnum\0221\n\016device_drivers\030\005 \003(\0162\031.context." +
+      "DeviceDriverEnum\022+\n\020device_endpoints\030\006 \003" +
+      "(\0132\021.context.EndPoint\"9\n\014DeviceConfig\022)\n" +
+      "\014config_rules\030\001 \003(\0132\023.context.ConfigRule" +
+      "\"5\n\014DeviceIdList\022%\n\ndevice_ids\030\001 \003(\0132\021.c" +
+      "ontext.DeviceId\".\n\nDeviceList\022 \n\007devices" +
+      "\030\001 \003(\0132\017.context.Device\"R\n\013DeviceEvent\022\035" +
+      "\n\005event\030\001 \001(\0132\016.context.Event\022$\n\tdevice_" +
+      "id\030\002 \001(\0132\021.context.DeviceId\"*\n\006LinkId\022 \n" +
+      "\tlink_uuid\030\001 \001(\0132\r.context.Uuid\"X\n\004Link\022" +
+      " \n\007link_id\030\001 \001(\0132\017.context.LinkId\022.\n\021lin" +
+      "k_endpoint_ids\030\002 \003(\0132\023.context.EndPointI" +
+      "d\"/\n\nLinkIdList\022!\n\010link_ids\030\001 \003(\0132\017.cont" +
+      "ext.LinkId\"(\n\010LinkList\022\034\n\005links\030\001 \003(\0132\r." +
+      "context.Link\"L\n\tLinkEvent\022\035\n\005event\030\001 \001(\013" +
+      "2\016.context.Event\022 \n\007link_id\030\002 \001(\0132\017.cont" +
+      "ext.LinkId\"X\n\tServiceId\022&\n\ncontext_id\030\001 " +
+      "\001(\0132\022.context.ContextId\022#\n\014service_uuid\030" +
+      "\002 \001(\0132\r.context.Uuid\"\246\002\n\007Service\022&\n\nserv" +
+      "ice_id\030\001 \001(\0132\022.context.ServiceId\022.\n\014serv" +
+      "ice_type\030\002 \001(\0162\030.context.ServiceTypeEnum" +
+      "\0221\n\024service_endpoint_ids\030\003 \003(\0132\023.context" +
+      ".EndPointId\0220\n\023service_constraints\030\004 \003(\013" +
+      "2\023.context.Constraint\022.\n\016service_status\030" +
+      "\005 \001(\0132\026.context.ServiceStatus\022.\n\016service" +
+      "_config\030\006 \001(\0132\026.context.ServiceConfig\"C\n" +
+      "\rServiceStatus\0222\n\016service_status\030\001 \001(\0162\032" +
+      ".context.ServiceStatusEnum\":\n\rServiceCon" +
+      "fig\022)\n\014config_rules\030\001 \003(\0132\023.context.Conf" +
+      "igRule\"8\n\rServiceIdList\022\'\n\013service_ids\030\001" +
+      " \003(\0132\022.context.ServiceId\"1\n\013ServiceList\022" +
+      "\"\n\010services\030\001 \003(\0132\020.context.Service\"U\n\014S" +
+      "erviceEvent\022\035\n\005event\030\001 \001(\0132\016.context.Eve" +
+      "nt\022&\n\nservice_id\030\002 \001(\0132\022.context.Service" +
+      "Id\"6\n\014ConnectionId\022&\n\017connection_uuid\030\001 " +
+      "\001(\0132\r.context.Uuid\"\304\001\n\nConnection\022,\n\rcon" +
+      "nection_id\030\001 \001(\0132\025.context.ConnectionId\022" +
+      "&\n\nservice_id\030\002 \001(\0132\022.context.ServiceId\022" +
+      "3\n\026path_hops_endpoint_ids\030\003 \003(\0132\023.contex" +
+      "t.EndPointId\022+\n\017sub_service_ids\030\004 \003(\0132\022." +
+      "context.ServiceId\"A\n\020ConnectionIdList\022-\n" +
+      "\016connection_ids\030\001 \003(\0132\025.context.Connecti" +
+      "onId\":\n\016ConnectionList\022(\n\013connections\030\001 " +
+      "\003(\0132\023.context.Connection\"^\n\017ConnectionEv" +
+      "ent\022\035\n\005event\030\001 \001(\0132\016.context.Event\022,\n\rco" +
+      "nnection_id\030\002 \001(\0132\025.context.ConnectionId" +
+      "\"\202\001\n\nEndPointId\022(\n\013topology_id\030\001 \001(\0132\023.c" +
+      "ontext.TopologyId\022$\n\tdevice_id\030\002 \001(\0132\021.c" +
+      "ontext.DeviceId\022$\n\rendpoint_uuid\030\003 \001(\0132\r" +
+      ".context.Uuid\"\206\001\n\010EndPoint\022(\n\013endpoint_i" +
+      "d\030\001 \001(\0132\023.context.EndPointId\022\025\n\rendpoint" +
+      "_type\030\002 \001(\t\0229\n\020kpi_sample_types\030\003 \003(\0162\037." +
+      "kpi_sample_types.KpiSampleType\"e\n\nConfig" +
+      "Rule\022)\n\006action\030\001 \001(\0162\031.context.ConfigAct" +
+      "ionEnum\022\024\n\014resource_key\030\002 \001(\t\022\026\n\016resourc" +
+      "e_value\030\003 \001(\t\"?\n\nConstraint\022\027\n\017constrain" +
+      "t_type\030\001 \001(\t\022\030\n\020constraint_value\030\002 \001(\t\"^" +
+      "\n\022TeraFlowController\022&\n\ncontext_id\030\001 \001(\013" +
+      "2\022.context.ContextId\022\022\n\nip_address\030\002 \001(\t" +
+      "\022\014\n\004port\030\003 \001(\r\"U\n\024AuthenticationResult\022&" +
+      "\n\ncontext_id\030\001 \001(\0132\022.context.ContextId\022\025" +
+      "\n\rauthenticated\030\002 \001(\010*j\n\rEventTypeEnum\022\027" +
+      "\n\023EVENTTYPE_UNDEFINED\020\000\022\024\n\020EVENTTYPE_CRE" +
+      "ATE\020\001\022\024\n\020EVENTTYPE_UPDATE\020\002\022\024\n\020EVENTTYPE" +
+      "_REMOVE\020\003*\305\001\n\020DeviceDriverEnum\022\032\n\026DEVICE" +
+      "DRIVER_UNDEFINED\020\000\022\033\n\027DEVICEDRIVER_OPENC" +
+      "ONFIG\020\001\022\036\n\032DEVICEDRIVER_TRANSPORT_API\020\002\022" +
+      "\023\n\017DEVICEDRIVER_P4\020\003\022&\n\"DEVICEDRIVER_IET" +
+      "F_NETWORK_TOPOLOGY\020\004\022\033\n\027DEVICEDRIVER_ONF" +
+      "_TR_352\020\005*\217\001\n\033DeviceOperationalStatusEnu" +
+      "m\022%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\020\000" +
+      "\022$\n DEVICEOPERATIONALSTATUS_DISABLED\020\001\022#" +
+      "\n\037DEVICEOPERATIONALSTATUS_ENABLED\020\002*\201\001\n\017" +
+      "ServiceTypeEnum\022\027\n\023SERVICETYPE_UNKNOWN\020\000" +
+      "\022\024\n\020SERVICETYPE_L3NM\020\001\022\024\n\020SERVICETYPE_L2" +
+      "NM\020\002\022)\n%SERVICETYPE_TAPI_CONNECTIVITY_SE" +
+      "RVICE\020\003*\210\001\n\021ServiceStatusEnum\022\033\n\027SERVICE" +
+      "STATUS_UNDEFINED\020\000\022\031\n\025SERVICESTATUS_PLAN" +
+      "NED\020\001\022\030\n\024SERVICESTATUS_ACTIVE\020\002\022!\n\035SERVI" +
+      "CESTATUS_PENDING_REMOVAL\020\003*]\n\020ConfigActi" +
+      "onEnum\022\032\n\026CONFIGACTION_UNDEFINED\020\000\022\024\n\020CO" +
+      "NFIGACTION_SET\020\001\022\027\n\023CONFIGACTION_DELETE\020" +
+      "\0022\255\020\n\016ContextService\022:\n\016ListContextIds\022\016" +
+      ".context.Empty\032\026.context.ContextIdList\"\000" +
+      "\0226\n\014ListContexts\022\016.context.Empty\032\024.conte" +
+      "xt.ContextList\"\000\0224\n\nGetContext\022\022.context" +
+      ".ContextId\032\020.context.Context\"\000\0224\n\nSetCon" +
+      "text\022\020.context.Context\032\022.context.Context" +
+      "Id\"\000\0225\n\rRemoveContext\022\022.context.ContextI" +
+      "d\032\016.context.Empty\"\000\022=\n\020GetContextEvents\022" +
+      "\016.context.Empty\032\025.context.ContextEvent\"\000" +
+      "0\001\022@\n\017ListTopologyIds\022\022.context.ContextI" +
+      "d\032\027.context.TopologyIdList\"\000\022=\n\016ListTopo" +
+      "logies\022\022.context.ContextId\032\025.context.Top" +
+      "ologyList\"\000\0227\n\013GetTopology\022\023.context.Top" +
+      "ologyId\032\021.context.Topology\"\000\0227\n\013SetTopol" +
+      "ogy\022\021.context.Topology\032\023.context.Topolog" +
+      "yId\"\000\0227\n\016RemoveTopology\022\023.context.Topolo" +
+      "gyId\032\016.context.Empty\"\000\022?\n\021GetTopologyEve" +
+      "nts\022\016.context.Empty\032\026.context.TopologyEv" +
+      "ent\"\0000\001\0228\n\rListDeviceIds\022\016.context.Empty" +
+      "\032\025.context.DeviceIdList\"\000\0224\n\013ListDevices" +
+      "\022\016.context.Empty\032\023.context.DeviceList\"\000\022" +
+      "1\n\tGetDevice\022\021.context.DeviceId\032\017.contex" +
+      "t.Device\"\000\0221\n\tSetDevice\022\017.context.Device" +
+      "\032\021.context.DeviceId\"\000\0223\n\014RemoveDevice\022\021." +
+      "context.DeviceId\032\016.context.Empty\"\000\022;\n\017Ge" +
+      "tDeviceEvents\022\016.context.Empty\032\024.context." +
+      "DeviceEvent\"\0000\001\0224\n\013ListLinkIds\022\016.context" +
+      ".Empty\032\023.context.LinkIdList\"\000\0220\n\tListLin" +
+      "ks\022\016.context.Empty\032\021.context.LinkList\"\000\022" +
+      "+\n\007GetLink\022\017.context.LinkId\032\r.context.Li" +
+      "nk\"\000\022+\n\007SetLink\022\r.context.Link\032\017.context" +
+      ".LinkId\"\000\022/\n\nRemoveLink\022\017.context.LinkId" +
+      "\032\016.context.Empty\"\000\0227\n\rGetLinkEvents\022\016.co" +
+      "ntext.Empty\032\022.context.LinkEvent\"\0000\001\022>\n\016L" +
+      "istServiceIds\022\022.context.ContextId\032\026.cont" +
+      "ext.ServiceIdList\"\000\022:\n\014ListServices\022\022.co" +
+      "ntext.ContextId\032\024.context.ServiceList\"\000\022" +
+      "4\n\nGetService\022\022.context.ServiceId\032\020.cont" +
+      "ext.Service\"\000\0224\n\nSetService\022\020.context.Se" +
+      "rvice\032\022.context.ServiceId\"\000\0225\n\rRemoveSer" +
+      "vice\022\022.context.ServiceId\032\016.context.Empty" +
+      "\"\000\022=\n\020GetServiceEvents\022\016.context.Empty\032\025" +
+      ".context.ServiceEvent\"\0000\001\022D\n\021ListConnect" +
+      "ionIds\022\022.context.ServiceId\032\031.context.Con" +
+      "nectionIdList\"\000\022@\n\017ListConnections\022\022.con" +
+      "text.ServiceId\032\027.context.ConnectionList\"" +
+      "\000\022=\n\rGetConnection\022\025.context.ConnectionI" +
+      "d\032\023.context.Connection\"\000\022=\n\rSetConnectio" +
+      "n\022\023.context.Connection\032\025.context.Connect" +
+      "ionId\"\000\022;\n\020RemoveConnection\022\025.context.Co" +
+      "nnectionId\032\016.context.Empty\"\000\022C\n\023GetConne" +
+      "ctionEvents\022\016.context.Empty\032\030.context.Co" +
+      "nnectionEvent\"\0000\001b\006proto3"
     };
     descriptor = com.google.protobuf.Descriptors.FileDescriptor
       .internalBuildGeneratedFileFrom(descriptorData,
         new com.google.protobuf.Descriptors.FileDescriptor[] {
+          kpi_sample_types.KpiSampleTypes.getDescriptor(),
         });
     internal_static_context_Empty_descriptor =
       getDescriptor().getMessageTypes().get(0);
@@ -36520,66 +38037,73 @@ public final class ContextOuterClass {
       com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
         internal_static_context_ServiceEvent_descriptor,
         new java.lang.String[] { "Event", "ServiceId", });
-    internal_static_context_EndPointId_descriptor =
-      getDescriptor().getMessageTypes().get(31);
-    internal_static_context_EndPointId_fieldAccessorTable = new
-      com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
-        internal_static_context_EndPointId_descriptor,
-        new java.lang.String[] { "TopologyId", "DeviceId", "EndpointUuid", });
-    internal_static_context_EndPoint_descriptor =
-      getDescriptor().getMessageTypes().get(32);
-    internal_static_context_EndPoint_fieldAccessorTable = new
-      com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
-        internal_static_context_EndPoint_descriptor,
-        new java.lang.String[] { "EndpointId", "EndpointType", });
-    internal_static_context_ConfigRule_descriptor =
-      getDescriptor().getMessageTypes().get(33);
-    internal_static_context_ConfigRule_fieldAccessorTable = new
-      com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
-        internal_static_context_ConfigRule_descriptor,
-        new java.lang.String[] { "Action", "ResourceKey", "ResourceValue", });
-    internal_static_context_Constraint_descriptor =
-      getDescriptor().getMessageTypes().get(34);
-    internal_static_context_Constraint_fieldAccessorTable = new
-      com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
-        internal_static_context_Constraint_descriptor,
-        new java.lang.String[] { "ConstraintType", "ConstraintValue", });
     internal_static_context_ConnectionId_descriptor =
-      getDescriptor().getMessageTypes().get(35);
+      getDescriptor().getMessageTypes().get(31);
     internal_static_context_ConnectionId_fieldAccessorTable = new
       com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
         internal_static_context_ConnectionId_descriptor,
         new java.lang.String[] { "ConnectionUuid", });
     internal_static_context_Connection_descriptor =
-      getDescriptor().getMessageTypes().get(36);
+      getDescriptor().getMessageTypes().get(32);
     internal_static_context_Connection_fieldAccessorTable = new
       com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
         internal_static_context_Connection_descriptor,
-        new java.lang.String[] { "ConnectionId", "RelatedServiceId", "Path", });
+        new java.lang.String[] { "ConnectionId", "ServiceId", "PathHopsEndpointIds", "SubServiceIds", });
     internal_static_context_ConnectionIdList_descriptor =
-      getDescriptor().getMessageTypes().get(37);
+      getDescriptor().getMessageTypes().get(33);
     internal_static_context_ConnectionIdList_fieldAccessorTable = new
       com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
         internal_static_context_ConnectionIdList_descriptor,
         new java.lang.String[] { "ConnectionIds", });
     internal_static_context_ConnectionList_descriptor =
-      getDescriptor().getMessageTypes().get(38);
+      getDescriptor().getMessageTypes().get(34);
     internal_static_context_ConnectionList_fieldAccessorTable = new
       com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
         internal_static_context_ConnectionList_descriptor,
         new java.lang.String[] { "Connections", });
-    internal_static_context_TeraFlowController_descriptor =
+    internal_static_context_ConnectionEvent_descriptor =
+      getDescriptor().getMessageTypes().get(35);
+    internal_static_context_ConnectionEvent_fieldAccessorTable = new
+      com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
+        internal_static_context_ConnectionEvent_descriptor,
+        new java.lang.String[] { "Event", "ConnectionId", });
+    internal_static_context_EndPointId_descriptor =
+      getDescriptor().getMessageTypes().get(36);
+    internal_static_context_EndPointId_fieldAccessorTable = new
+      com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
+        internal_static_context_EndPointId_descriptor,
+        new java.lang.String[] { "TopologyId", "DeviceId", "EndpointUuid", });
+    internal_static_context_EndPoint_descriptor =
+      getDescriptor().getMessageTypes().get(37);
+    internal_static_context_EndPoint_fieldAccessorTable = new
+      com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
+        internal_static_context_EndPoint_descriptor,
+        new java.lang.String[] { "EndpointId", "EndpointType", "KpiSampleTypes", });
+    internal_static_context_ConfigRule_descriptor =
+      getDescriptor().getMessageTypes().get(38);
+    internal_static_context_ConfigRule_fieldAccessorTable = new
+      com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
+        internal_static_context_ConfigRule_descriptor,
+        new java.lang.String[] { "Action", "ResourceKey", "ResourceValue", });
+    internal_static_context_Constraint_descriptor =
       getDescriptor().getMessageTypes().get(39);
+    internal_static_context_Constraint_fieldAccessorTable = new
+      com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
+        internal_static_context_Constraint_descriptor,
+        new java.lang.String[] { "ConstraintType", "ConstraintValue", });
+    internal_static_context_TeraFlowController_descriptor =
+      getDescriptor().getMessageTypes().get(40);
     internal_static_context_TeraFlowController_fieldAccessorTable = new
       com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
         internal_static_context_TeraFlowController_descriptor,
         new java.lang.String[] { "ContextId", "IpAddress", "Port", });
     internal_static_context_AuthenticationResult_descriptor =
-      getDescriptor().getMessageTypes().get(40);
+      getDescriptor().getMessageTypes().get(41);
     internal_static_context_AuthenticationResult_fieldAccessorTable = new
       com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
         internal_static_context_AuthenticationResult_descriptor,
         new java.lang.String[] { "ContextId", "Authenticated", });
+    kpi_sample_types.KpiSampleTypes.getDescriptor();
   }
 
   // @@protoc_insertion_point(outer_class_scope)
diff --git a/src/policy/target/generated-sources/grpc/context/ContextService.java b/src/policy/target/generated-sources/grpc/context/ContextService.java
index 8e5095d25896abc5ff37b7a5e7d4ac7f97825dc6..cbd51163f340212839e0eeb2ad096032fdba8109 100644
--- a/src/policy/target/generated-sources/grpc/context/ContextService.java
+++ b/src/policy/target/generated-sources/grpc/context/ContextService.java
@@ -58,6 +58,16 @@ public interface ContextService extends MutinyService {
     
     io.smallrye.mutiny.Uni<context.ContextOuterClass.Empty> removeService(context.ContextOuterClass.ServiceId request);
     
+    io.smallrye.mutiny.Uni<context.ContextOuterClass.ConnectionIdList> listConnectionIds(context.ContextOuterClass.ServiceId request);
+    
+    io.smallrye.mutiny.Uni<context.ContextOuterClass.ConnectionList> listConnections(context.ContextOuterClass.ServiceId request);
+    
+    io.smallrye.mutiny.Uni<context.ContextOuterClass.Connection> getConnection(context.ContextOuterClass.ConnectionId request);
+    
+    io.smallrye.mutiny.Uni<context.ContextOuterClass.ConnectionId> setConnection(context.ContextOuterClass.Connection request);
+    
+    io.smallrye.mutiny.Uni<context.ContextOuterClass.Empty> removeConnection(context.ContextOuterClass.ConnectionId request);
+    
     
     io.smallrye.mutiny.Multi<context.ContextOuterClass.ContextEvent> getContextEvents(context.ContextOuterClass.Empty request);
     
@@ -69,6 +79,8 @@ public interface ContextService extends MutinyService {
     
     io.smallrye.mutiny.Multi<context.ContextOuterClass.ServiceEvent> getServiceEvents(context.ContextOuterClass.Empty request);
     
+    io.smallrye.mutiny.Multi<context.ContextOuterClass.ConnectionEvent> getConnectionEvents(context.ContextOuterClass.Empty request);
+    
     
 
 }
\ No newline at end of file
diff --git a/src/policy/target/generated-sources/grpc/context/ContextServiceBean.java b/src/policy/target/generated-sources/grpc/context/ContextServiceBean.java
index fbecd5d2f3cdf04e9f47693a4aeea37753855cdf..6900cf3c8a09ad6e3df1aaf2cc403c32490d3289 100644
--- a/src/policy/target/generated-sources/grpc/context/ContextServiceBean.java
+++ b/src/policy/target/generated-sources/grpc/context/ContextServiceBean.java
@@ -215,6 +215,46 @@ public class ContextServiceBean extends MutinyContextServiceGrpc.ContextServiceI
           throw new io.grpc.StatusRuntimeException(io.grpc.Status.UNIMPLEMENTED);
        }
     }
+    @Override
+    public io.smallrye.mutiny.Uni<context.ContextOuterClass.ConnectionIdList> listConnectionIds(context.ContextOuterClass.ServiceId request) {
+       try {
+         return delegate.listConnectionIds(request);
+       } catch (UnsupportedOperationException e) {
+          throw new io.grpc.StatusRuntimeException(io.grpc.Status.UNIMPLEMENTED);
+       }
+    }
+    @Override
+    public io.smallrye.mutiny.Uni<context.ContextOuterClass.ConnectionList> listConnections(context.ContextOuterClass.ServiceId request) {
+       try {
+         return delegate.listConnections(request);
+       } catch (UnsupportedOperationException e) {
+          throw new io.grpc.StatusRuntimeException(io.grpc.Status.UNIMPLEMENTED);
+       }
+    }
+    @Override
+    public io.smallrye.mutiny.Uni<context.ContextOuterClass.Connection> getConnection(context.ContextOuterClass.ConnectionId request) {
+       try {
+         return delegate.getConnection(request);
+       } catch (UnsupportedOperationException e) {
+          throw new io.grpc.StatusRuntimeException(io.grpc.Status.UNIMPLEMENTED);
+       }
+    }
+    @Override
+    public io.smallrye.mutiny.Uni<context.ContextOuterClass.ConnectionId> setConnection(context.ContextOuterClass.Connection request) {
+       try {
+         return delegate.setConnection(request);
+       } catch (UnsupportedOperationException e) {
+          throw new io.grpc.StatusRuntimeException(io.grpc.Status.UNIMPLEMENTED);
+       }
+    }
+    @Override
+    public io.smallrye.mutiny.Uni<context.ContextOuterClass.Empty> removeConnection(context.ContextOuterClass.ConnectionId request) {
+       try {
+         return delegate.removeConnection(request);
+       } catch (UnsupportedOperationException e) {
+          throw new io.grpc.StatusRuntimeException(io.grpc.Status.UNIMPLEMENTED);
+       }
+    }
 
     @Override
     public io.smallrye.mutiny.Multi<context.ContextOuterClass.ContextEvent> getContextEvents(context.ContextOuterClass.Empty request) {
@@ -261,4 +301,13 @@ public class ContextServiceBean extends MutinyContextServiceGrpc.ContextServiceI
        }
     }
 
+    @Override
+    public io.smallrye.mutiny.Multi<context.ContextOuterClass.ConnectionEvent> getConnectionEvents(context.ContextOuterClass.Empty request) {
+       try {
+         return delegate.getConnectionEvents(request);
+       } catch (UnsupportedOperationException e) {
+          throw new io.grpc.StatusRuntimeException(io.grpc.Status.UNIMPLEMENTED);
+       }
+    }
+
 }
\ No newline at end of file
diff --git a/src/policy/target/generated-sources/grpc/context/ContextServiceClient.java b/src/policy/target/generated-sources/grpc/context/ContextServiceClient.java
index 6e815b940e668e33bf936d7d9de0930d086ff9a3..a3d74cb7dfce2b3ffd259570a6dc65699f85f0cc 100644
--- a/src/policy/target/generated-sources/grpc/context/ContextServiceClient.java
+++ b/src/policy/target/generated-sources/grpc/context/ContextServiceClient.java
@@ -120,6 +120,26 @@ public class ContextServiceClient implements ContextService, MutinyClient<Mutiny
     public io.smallrye.mutiny.Uni<context.ContextOuterClass.Empty> removeService(context.ContextOuterClass.ServiceId request) {
        return stub.removeService(request);
     }
+    @Override
+    public io.smallrye.mutiny.Uni<context.ContextOuterClass.ConnectionIdList> listConnectionIds(context.ContextOuterClass.ServiceId request) {
+       return stub.listConnectionIds(request);
+    }
+    @Override
+    public io.smallrye.mutiny.Uni<context.ContextOuterClass.ConnectionList> listConnections(context.ContextOuterClass.ServiceId request) {
+       return stub.listConnections(request);
+    }
+    @Override
+    public io.smallrye.mutiny.Uni<context.ContextOuterClass.Connection> getConnection(context.ContextOuterClass.ConnectionId request) {
+       return stub.getConnection(request);
+    }
+    @Override
+    public io.smallrye.mutiny.Uni<context.ContextOuterClass.ConnectionId> setConnection(context.ContextOuterClass.Connection request) {
+       return stub.setConnection(request);
+    }
+    @Override
+    public io.smallrye.mutiny.Uni<context.ContextOuterClass.Empty> removeConnection(context.ContextOuterClass.ConnectionId request) {
+       return stub.removeConnection(request);
+    }
 
     @Override
     public io.smallrye.mutiny.Multi<context.ContextOuterClass.ContextEvent> getContextEvents(context.ContextOuterClass.Empty request) {
@@ -146,4 +166,9 @@ public class ContextServiceClient implements ContextService, MutinyClient<Mutiny
        return stub.getServiceEvents(request);
     }
 
+    @Override
+    public io.smallrye.mutiny.Multi<context.ContextOuterClass.ConnectionEvent> getConnectionEvents(context.ContextOuterClass.Empty request) {
+       return stub.getConnectionEvents(request);
+    }
+
 }
\ No newline at end of file
diff --git a/src/policy/target/generated-sources/grpc/context/ContextServiceGrpc.java b/src/policy/target/generated-sources/grpc/context/ContextServiceGrpc.java
index 03f2a66f6f09ed54eb1c359a356ba5240b184166..be9f381ffee96febd202beb540bce862c1347378 100644
--- a/src/policy/target/generated-sources/grpc/context/ContextServiceGrpc.java
+++ b/src/policy/target/generated-sources/grpc/context/ContextServiceGrpc.java
@@ -944,6 +944,192 @@ public final class ContextServiceGrpc {
     return getGetServiceEventsMethod;
   }
 
+  private static volatile io.grpc.MethodDescriptor<context.ContextOuterClass.ServiceId,
+      context.ContextOuterClass.ConnectionIdList> getListConnectionIdsMethod;
+
+  @io.grpc.stub.annotations.RpcMethod(
+      fullMethodName = SERVICE_NAME + '/' + "ListConnectionIds",
+      requestType = context.ContextOuterClass.ServiceId.class,
+      responseType = context.ContextOuterClass.ConnectionIdList.class,
+      methodType = io.grpc.MethodDescriptor.MethodType.UNARY)
+  public static io.grpc.MethodDescriptor<context.ContextOuterClass.ServiceId,
+      context.ContextOuterClass.ConnectionIdList> getListConnectionIdsMethod() {
+    io.grpc.MethodDescriptor<context.ContextOuterClass.ServiceId, context.ContextOuterClass.ConnectionIdList> getListConnectionIdsMethod;
+    if ((getListConnectionIdsMethod = ContextServiceGrpc.getListConnectionIdsMethod) == null) {
+      synchronized (ContextServiceGrpc.class) {
+        if ((getListConnectionIdsMethod = ContextServiceGrpc.getListConnectionIdsMethod) == null) {
+          ContextServiceGrpc.getListConnectionIdsMethod = getListConnectionIdsMethod =
+              io.grpc.MethodDescriptor.<context.ContextOuterClass.ServiceId, context.ContextOuterClass.ConnectionIdList>newBuilder()
+              .setType(io.grpc.MethodDescriptor.MethodType.UNARY)
+              .setFullMethodName(generateFullMethodName(SERVICE_NAME, "ListConnectionIds"))
+              .setSampledToLocalTracing(true)
+              .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
+                  context.ContextOuterClass.ServiceId.getDefaultInstance()))
+              .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
+                  context.ContextOuterClass.ConnectionIdList.getDefaultInstance()))
+              .setSchemaDescriptor(new ContextServiceMethodDescriptorSupplier("ListConnectionIds"))
+              .build();
+        }
+      }
+    }
+    return getListConnectionIdsMethod;
+  }
+
+  private static volatile io.grpc.MethodDescriptor<context.ContextOuterClass.ServiceId,
+      context.ContextOuterClass.ConnectionList> getListConnectionsMethod;
+
+  @io.grpc.stub.annotations.RpcMethod(
+      fullMethodName = SERVICE_NAME + '/' + "ListConnections",
+      requestType = context.ContextOuterClass.ServiceId.class,
+      responseType = context.ContextOuterClass.ConnectionList.class,
+      methodType = io.grpc.MethodDescriptor.MethodType.UNARY)
+  public static io.grpc.MethodDescriptor<context.ContextOuterClass.ServiceId,
+      context.ContextOuterClass.ConnectionList> getListConnectionsMethod() {
+    io.grpc.MethodDescriptor<context.ContextOuterClass.ServiceId, context.ContextOuterClass.ConnectionList> getListConnectionsMethod;
+    if ((getListConnectionsMethod = ContextServiceGrpc.getListConnectionsMethod) == null) {
+      synchronized (ContextServiceGrpc.class) {
+        if ((getListConnectionsMethod = ContextServiceGrpc.getListConnectionsMethod) == null) {
+          ContextServiceGrpc.getListConnectionsMethod = getListConnectionsMethod =
+              io.grpc.MethodDescriptor.<context.ContextOuterClass.ServiceId, context.ContextOuterClass.ConnectionList>newBuilder()
+              .setType(io.grpc.MethodDescriptor.MethodType.UNARY)
+              .setFullMethodName(generateFullMethodName(SERVICE_NAME, "ListConnections"))
+              .setSampledToLocalTracing(true)
+              .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
+                  context.ContextOuterClass.ServiceId.getDefaultInstance()))
+              .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
+                  context.ContextOuterClass.ConnectionList.getDefaultInstance()))
+              .setSchemaDescriptor(new ContextServiceMethodDescriptorSupplier("ListConnections"))
+              .build();
+        }
+      }
+    }
+    return getListConnectionsMethod;
+  }
+
+  private static volatile io.grpc.MethodDescriptor<context.ContextOuterClass.ConnectionId,
+      context.ContextOuterClass.Connection> getGetConnectionMethod;
+
+  @io.grpc.stub.annotations.RpcMethod(
+      fullMethodName = SERVICE_NAME + '/' + "GetConnection",
+      requestType = context.ContextOuterClass.ConnectionId.class,
+      responseType = context.ContextOuterClass.Connection.class,
+      methodType = io.grpc.MethodDescriptor.MethodType.UNARY)
+  public static io.grpc.MethodDescriptor<context.ContextOuterClass.ConnectionId,
+      context.ContextOuterClass.Connection> getGetConnectionMethod() {
+    io.grpc.MethodDescriptor<context.ContextOuterClass.ConnectionId, context.ContextOuterClass.Connection> getGetConnectionMethod;
+    if ((getGetConnectionMethod = ContextServiceGrpc.getGetConnectionMethod) == null) {
+      synchronized (ContextServiceGrpc.class) {
+        if ((getGetConnectionMethod = ContextServiceGrpc.getGetConnectionMethod) == null) {
+          ContextServiceGrpc.getGetConnectionMethod = getGetConnectionMethod =
+              io.grpc.MethodDescriptor.<context.ContextOuterClass.ConnectionId, context.ContextOuterClass.Connection>newBuilder()
+              .setType(io.grpc.MethodDescriptor.MethodType.UNARY)
+              .setFullMethodName(generateFullMethodName(SERVICE_NAME, "GetConnection"))
+              .setSampledToLocalTracing(true)
+              .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
+                  context.ContextOuterClass.ConnectionId.getDefaultInstance()))
+              .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
+                  context.ContextOuterClass.Connection.getDefaultInstance()))
+              .setSchemaDescriptor(new ContextServiceMethodDescriptorSupplier("GetConnection"))
+              .build();
+        }
+      }
+    }
+    return getGetConnectionMethod;
+  }
+
+  private static volatile io.grpc.MethodDescriptor<context.ContextOuterClass.Connection,
+      context.ContextOuterClass.ConnectionId> getSetConnectionMethod;
+
+  @io.grpc.stub.annotations.RpcMethod(
+      fullMethodName = SERVICE_NAME + '/' + "SetConnection",
+      requestType = context.ContextOuterClass.Connection.class,
+      responseType = context.ContextOuterClass.ConnectionId.class,
+      methodType = io.grpc.MethodDescriptor.MethodType.UNARY)
+  public static io.grpc.MethodDescriptor<context.ContextOuterClass.Connection,
+      context.ContextOuterClass.ConnectionId> getSetConnectionMethod() {
+    io.grpc.MethodDescriptor<context.ContextOuterClass.Connection, context.ContextOuterClass.ConnectionId> getSetConnectionMethod;
+    if ((getSetConnectionMethod = ContextServiceGrpc.getSetConnectionMethod) == null) {
+      synchronized (ContextServiceGrpc.class) {
+        if ((getSetConnectionMethod = ContextServiceGrpc.getSetConnectionMethod) == null) {
+          ContextServiceGrpc.getSetConnectionMethod = getSetConnectionMethod =
+              io.grpc.MethodDescriptor.<context.ContextOuterClass.Connection, context.ContextOuterClass.ConnectionId>newBuilder()
+              .setType(io.grpc.MethodDescriptor.MethodType.UNARY)
+              .setFullMethodName(generateFullMethodName(SERVICE_NAME, "SetConnection"))
+              .setSampledToLocalTracing(true)
+              .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
+                  context.ContextOuterClass.Connection.getDefaultInstance()))
+              .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
+                  context.ContextOuterClass.ConnectionId.getDefaultInstance()))
+              .setSchemaDescriptor(new ContextServiceMethodDescriptorSupplier("SetConnection"))
+              .build();
+        }
+      }
+    }
+    return getSetConnectionMethod;
+  }
+
+  private static volatile io.grpc.MethodDescriptor<context.ContextOuterClass.ConnectionId,
+      context.ContextOuterClass.Empty> getRemoveConnectionMethod;
+
+  @io.grpc.stub.annotations.RpcMethod(
+      fullMethodName = SERVICE_NAME + '/' + "RemoveConnection",
+      requestType = context.ContextOuterClass.ConnectionId.class,
+      responseType = context.ContextOuterClass.Empty.class,
+      methodType = io.grpc.MethodDescriptor.MethodType.UNARY)
+  public static io.grpc.MethodDescriptor<context.ContextOuterClass.ConnectionId,
+      context.ContextOuterClass.Empty> getRemoveConnectionMethod() {
+    io.grpc.MethodDescriptor<context.ContextOuterClass.ConnectionId, context.ContextOuterClass.Empty> getRemoveConnectionMethod;
+    if ((getRemoveConnectionMethod = ContextServiceGrpc.getRemoveConnectionMethod) == null) {
+      synchronized (ContextServiceGrpc.class) {
+        if ((getRemoveConnectionMethod = ContextServiceGrpc.getRemoveConnectionMethod) == null) {
+          ContextServiceGrpc.getRemoveConnectionMethod = getRemoveConnectionMethod =
+              io.grpc.MethodDescriptor.<context.ContextOuterClass.ConnectionId, context.ContextOuterClass.Empty>newBuilder()
+              .setType(io.grpc.MethodDescriptor.MethodType.UNARY)
+              .setFullMethodName(generateFullMethodName(SERVICE_NAME, "RemoveConnection"))
+              .setSampledToLocalTracing(true)
+              .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
+                  context.ContextOuterClass.ConnectionId.getDefaultInstance()))
+              .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
+                  context.ContextOuterClass.Empty.getDefaultInstance()))
+              .setSchemaDescriptor(new ContextServiceMethodDescriptorSupplier("RemoveConnection"))
+              .build();
+        }
+      }
+    }
+    return getRemoveConnectionMethod;
+  }
+
+  private static volatile io.grpc.MethodDescriptor<context.ContextOuterClass.Empty,
+      context.ContextOuterClass.ConnectionEvent> getGetConnectionEventsMethod;
+
+  @io.grpc.stub.annotations.RpcMethod(
+      fullMethodName = SERVICE_NAME + '/' + "GetConnectionEvents",
+      requestType = context.ContextOuterClass.Empty.class,
+      responseType = context.ContextOuterClass.ConnectionEvent.class,
+      methodType = io.grpc.MethodDescriptor.MethodType.SERVER_STREAMING)
+  public static io.grpc.MethodDescriptor<context.ContextOuterClass.Empty,
+      context.ContextOuterClass.ConnectionEvent> getGetConnectionEventsMethod() {
+    io.grpc.MethodDescriptor<context.ContextOuterClass.Empty, context.ContextOuterClass.ConnectionEvent> getGetConnectionEventsMethod;
+    if ((getGetConnectionEventsMethod = ContextServiceGrpc.getGetConnectionEventsMethod) == null) {
+      synchronized (ContextServiceGrpc.class) {
+        if ((getGetConnectionEventsMethod = ContextServiceGrpc.getGetConnectionEventsMethod) == null) {
+          ContextServiceGrpc.getGetConnectionEventsMethod = getGetConnectionEventsMethod =
+              io.grpc.MethodDescriptor.<context.ContextOuterClass.Empty, context.ContextOuterClass.ConnectionEvent>newBuilder()
+              .setType(io.grpc.MethodDescriptor.MethodType.SERVER_STREAMING)
+              .setFullMethodName(generateFullMethodName(SERVICE_NAME, "GetConnectionEvents"))
+              .setSampledToLocalTracing(true)
+              .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
+                  context.ContextOuterClass.Empty.getDefaultInstance()))
+              .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
+                  context.ContextOuterClass.ConnectionEvent.getDefaultInstance()))
+              .setSchemaDescriptor(new ContextServiceMethodDescriptorSupplier("GetConnectionEvents"))
+              .build();
+        }
+      }
+    }
+    return getGetConnectionEventsMethod;
+  }
+
   /**
    * Creates a new async stub that supports all call types for the service
    */
@@ -1202,6 +1388,48 @@ public final class ContextServiceGrpc {
       io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getGetServiceEventsMethod(), responseObserver);
     }
 
+    /**
+     */
+    public void listConnectionIds(context.ContextOuterClass.ServiceId request,
+        io.grpc.stub.StreamObserver<context.ContextOuterClass.ConnectionIdList> responseObserver) {
+      io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getListConnectionIdsMethod(), responseObserver);
+    }
+
+    /**
+     */
+    public void listConnections(context.ContextOuterClass.ServiceId request,
+        io.grpc.stub.StreamObserver<context.ContextOuterClass.ConnectionList> responseObserver) {
+      io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getListConnectionsMethod(), responseObserver);
+    }
+
+    /**
+     */
+    public void getConnection(context.ContextOuterClass.ConnectionId request,
+        io.grpc.stub.StreamObserver<context.ContextOuterClass.Connection> responseObserver) {
+      io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getGetConnectionMethod(), responseObserver);
+    }
+
+    /**
+     */
+    public void setConnection(context.ContextOuterClass.Connection request,
+        io.grpc.stub.StreamObserver<context.ContextOuterClass.ConnectionId> responseObserver) {
+      io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getSetConnectionMethod(), responseObserver);
+    }
+
+    /**
+     */
+    public void removeConnection(context.ContextOuterClass.ConnectionId request,
+        io.grpc.stub.StreamObserver<context.ContextOuterClass.Empty> responseObserver) {
+      io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getRemoveConnectionMethod(), responseObserver);
+    }
+
+    /**
+     */
+    public void getConnectionEvents(context.ContextOuterClass.Empty request,
+        io.grpc.stub.StreamObserver<context.ContextOuterClass.ConnectionEvent> responseObserver) {
+      io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getGetConnectionEventsMethod(), responseObserver);
+    }
+
     @java.lang.Override public final io.grpc.ServerServiceDefinition bindService() {
       return io.grpc.ServerServiceDefinition.builder(getServiceDescriptor())
           .addMethod(
@@ -1414,6 +1642,48 @@ public final class ContextServiceGrpc {
                 context.ContextOuterClass.Empty,
                 context.ContextOuterClass.ServiceEvent>(
                   this, METHODID_GET_SERVICE_EVENTS)))
+          .addMethod(
+            getListConnectionIdsMethod(),
+            io.grpc.stub.ServerCalls.asyncUnaryCall(
+              new MethodHandlers<
+                context.ContextOuterClass.ServiceId,
+                context.ContextOuterClass.ConnectionIdList>(
+                  this, METHODID_LIST_CONNECTION_IDS)))
+          .addMethod(
+            getListConnectionsMethod(),
+            io.grpc.stub.ServerCalls.asyncUnaryCall(
+              new MethodHandlers<
+                context.ContextOuterClass.ServiceId,
+                context.ContextOuterClass.ConnectionList>(
+                  this, METHODID_LIST_CONNECTIONS)))
+          .addMethod(
+            getGetConnectionMethod(),
+            io.grpc.stub.ServerCalls.asyncUnaryCall(
+              new MethodHandlers<
+                context.ContextOuterClass.ConnectionId,
+                context.ContextOuterClass.Connection>(
+                  this, METHODID_GET_CONNECTION)))
+          .addMethod(
+            getSetConnectionMethod(),
+            io.grpc.stub.ServerCalls.asyncUnaryCall(
+              new MethodHandlers<
+                context.ContextOuterClass.Connection,
+                context.ContextOuterClass.ConnectionId>(
+                  this, METHODID_SET_CONNECTION)))
+          .addMethod(
+            getRemoveConnectionMethod(),
+            io.grpc.stub.ServerCalls.asyncUnaryCall(
+              new MethodHandlers<
+                context.ContextOuterClass.ConnectionId,
+                context.ContextOuterClass.Empty>(
+                  this, METHODID_REMOVE_CONNECTION)))
+          .addMethod(
+            getGetConnectionEventsMethod(),
+            io.grpc.stub.ServerCalls.asyncServerStreamingCall(
+              new MethodHandlers<
+                context.ContextOuterClass.Empty,
+                context.ContextOuterClass.ConnectionEvent>(
+                  this, METHODID_GET_CONNECTION_EVENTS)))
           .build();
     }
   }
@@ -1671,6 +1941,54 @@ public final class ContextServiceGrpc {
       io.grpc.stub.ClientCalls.asyncServerStreamingCall(
           getChannel().newCall(getGetServiceEventsMethod(), getCallOptions()), request, responseObserver);
     }
+
+    /**
+     */
+    public void listConnectionIds(context.ContextOuterClass.ServiceId request,
+        io.grpc.stub.StreamObserver<context.ContextOuterClass.ConnectionIdList> responseObserver) {
+      io.grpc.stub.ClientCalls.asyncUnaryCall(
+          getChannel().newCall(getListConnectionIdsMethod(), getCallOptions()), request, responseObserver);
+    }
+
+    /**
+     */
+    public void listConnections(context.ContextOuterClass.ServiceId request,
+        io.grpc.stub.StreamObserver<context.ContextOuterClass.ConnectionList> responseObserver) {
+      io.grpc.stub.ClientCalls.asyncUnaryCall(
+          getChannel().newCall(getListConnectionsMethod(), getCallOptions()), request, responseObserver);
+    }
+
+    /**
+     */
+    public void getConnection(context.ContextOuterClass.ConnectionId request,
+        io.grpc.stub.StreamObserver<context.ContextOuterClass.Connection> responseObserver) {
+      io.grpc.stub.ClientCalls.asyncUnaryCall(
+          getChannel().newCall(getGetConnectionMethod(), getCallOptions()), request, responseObserver);
+    }
+
+    /**
+     */
+    public void setConnection(context.ContextOuterClass.Connection request,
+        io.grpc.stub.StreamObserver<context.ContextOuterClass.ConnectionId> responseObserver) {
+      io.grpc.stub.ClientCalls.asyncUnaryCall(
+          getChannel().newCall(getSetConnectionMethod(), getCallOptions()), request, responseObserver);
+    }
+
+    /**
+     */
+    public void removeConnection(context.ContextOuterClass.ConnectionId request,
+        io.grpc.stub.StreamObserver<context.ContextOuterClass.Empty> responseObserver) {
+      io.grpc.stub.ClientCalls.asyncUnaryCall(
+          getChannel().newCall(getRemoveConnectionMethod(), getCallOptions()), request, responseObserver);
+    }
+
+    /**
+     */
+    public void getConnectionEvents(context.ContextOuterClass.Empty request,
+        io.grpc.stub.StreamObserver<context.ContextOuterClass.ConnectionEvent> responseObserver) {
+      io.grpc.stub.ClientCalls.asyncServerStreamingCall(
+          getChannel().newCall(getGetConnectionEventsMethod(), getCallOptions()), request, responseObserver);
+    }
   }
 
   /**
@@ -1901,6 +2219,49 @@ public final class ContextServiceGrpc {
       return io.grpc.stub.ClientCalls.blockingServerStreamingCall(
           getChannel(), getGetServiceEventsMethod(), getCallOptions(), request);
     }
+
+    /**
+     */
+    public context.ContextOuterClass.ConnectionIdList listConnectionIds(context.ContextOuterClass.ServiceId request) {
+      return io.grpc.stub.ClientCalls.blockingUnaryCall(
+          getChannel(), getListConnectionIdsMethod(), getCallOptions(), request);
+    }
+
+    /**
+     */
+    public context.ContextOuterClass.ConnectionList listConnections(context.ContextOuterClass.ServiceId request) {
+      return io.grpc.stub.ClientCalls.blockingUnaryCall(
+          getChannel(), getListConnectionsMethod(), getCallOptions(), request);
+    }
+
+    /**
+     */
+    public context.ContextOuterClass.Connection getConnection(context.ContextOuterClass.ConnectionId request) {
+      return io.grpc.stub.ClientCalls.blockingUnaryCall(
+          getChannel(), getGetConnectionMethod(), getCallOptions(), request);
+    }
+
+    /**
+     */
+    public context.ContextOuterClass.ConnectionId setConnection(context.ContextOuterClass.Connection request) {
+      return io.grpc.stub.ClientCalls.blockingUnaryCall(
+          getChannel(), getSetConnectionMethod(), getCallOptions(), request);
+    }
+
+    /**
+     */
+    public context.ContextOuterClass.Empty removeConnection(context.ContextOuterClass.ConnectionId request) {
+      return io.grpc.stub.ClientCalls.blockingUnaryCall(
+          getChannel(), getRemoveConnectionMethod(), getCallOptions(), request);
+    }
+
+    /**
+     */
+    public java.util.Iterator<context.ContextOuterClass.ConnectionEvent> getConnectionEvents(
+        context.ContextOuterClass.Empty request) {
+      return io.grpc.stub.ClientCalls.blockingServerStreamingCall(
+          getChannel(), getGetConnectionEventsMethod(), getCallOptions(), request);
+    }
   }
 
   /**
@@ -2116,6 +2477,46 @@ public final class ContextServiceGrpc {
       return io.grpc.stub.ClientCalls.futureUnaryCall(
           getChannel().newCall(getRemoveServiceMethod(), getCallOptions()), request);
     }
+
+    /**
+     */
+    public com.google.common.util.concurrent.ListenableFuture<context.ContextOuterClass.ConnectionIdList> listConnectionIds(
+        context.ContextOuterClass.ServiceId request) {
+      return io.grpc.stub.ClientCalls.futureUnaryCall(
+          getChannel().newCall(getListConnectionIdsMethod(), getCallOptions()), request);
+    }
+
+    /**
+     */
+    public com.google.common.util.concurrent.ListenableFuture<context.ContextOuterClass.ConnectionList> listConnections(
+        context.ContextOuterClass.ServiceId request) {
+      return io.grpc.stub.ClientCalls.futureUnaryCall(
+          getChannel().newCall(getListConnectionsMethod(), getCallOptions()), request);
+    }
+
+    /**
+     */
+    public com.google.common.util.concurrent.ListenableFuture<context.ContextOuterClass.Connection> getConnection(
+        context.ContextOuterClass.ConnectionId request) {
+      return io.grpc.stub.ClientCalls.futureUnaryCall(
+          getChannel().newCall(getGetConnectionMethod(), getCallOptions()), request);
+    }
+
+    /**
+     */
+    public com.google.common.util.concurrent.ListenableFuture<context.ContextOuterClass.ConnectionId> setConnection(
+        context.ContextOuterClass.Connection request) {
+      return io.grpc.stub.ClientCalls.futureUnaryCall(
+          getChannel().newCall(getSetConnectionMethod(), getCallOptions()), request);
+    }
+
+    /**
+     */
+    public com.google.common.util.concurrent.ListenableFuture<context.ContextOuterClass.Empty> removeConnection(
+        context.ContextOuterClass.ConnectionId request) {
+      return io.grpc.stub.ClientCalls.futureUnaryCall(
+          getChannel().newCall(getRemoveConnectionMethod(), getCallOptions()), request);
+    }
   }
 
   private static final int METHODID_LIST_CONTEXT_IDS = 0;
@@ -2148,6 +2549,12 @@ public final class ContextServiceGrpc {
   private static final int METHODID_SET_SERVICE = 27;
   private static final int METHODID_REMOVE_SERVICE = 28;
   private static final int METHODID_GET_SERVICE_EVENTS = 29;
+  private static final int METHODID_LIST_CONNECTION_IDS = 30;
+  private static final int METHODID_LIST_CONNECTIONS = 31;
+  private static final int METHODID_GET_CONNECTION = 32;
+  private static final int METHODID_SET_CONNECTION = 33;
+  private static final int METHODID_REMOVE_CONNECTION = 34;
+  private static final int METHODID_GET_CONNECTION_EVENTS = 35;
 
   private static final class MethodHandlers<Req, Resp> implements
       io.grpc.stub.ServerCalls.UnaryMethod<Req, Resp>,
@@ -2286,6 +2693,30 @@ public final class ContextServiceGrpc {
           serviceImpl.getServiceEvents((context.ContextOuterClass.Empty) request,
               (io.grpc.stub.StreamObserver<context.ContextOuterClass.ServiceEvent>) responseObserver);
           break;
+        case METHODID_LIST_CONNECTION_IDS:
+          serviceImpl.listConnectionIds((context.ContextOuterClass.ServiceId) request,
+              (io.grpc.stub.StreamObserver<context.ContextOuterClass.ConnectionIdList>) responseObserver);
+          break;
+        case METHODID_LIST_CONNECTIONS:
+          serviceImpl.listConnections((context.ContextOuterClass.ServiceId) request,
+              (io.grpc.stub.StreamObserver<context.ContextOuterClass.ConnectionList>) responseObserver);
+          break;
+        case METHODID_GET_CONNECTION:
+          serviceImpl.getConnection((context.ContextOuterClass.ConnectionId) request,
+              (io.grpc.stub.StreamObserver<context.ContextOuterClass.Connection>) responseObserver);
+          break;
+        case METHODID_SET_CONNECTION:
+          serviceImpl.setConnection((context.ContextOuterClass.Connection) request,
+              (io.grpc.stub.StreamObserver<context.ContextOuterClass.ConnectionId>) responseObserver);
+          break;
+        case METHODID_REMOVE_CONNECTION:
+          serviceImpl.removeConnection((context.ContextOuterClass.ConnectionId) request,
+              (io.grpc.stub.StreamObserver<context.ContextOuterClass.Empty>) responseObserver);
+          break;
+        case METHODID_GET_CONNECTION_EVENTS:
+          serviceImpl.getConnectionEvents((context.ContextOuterClass.Empty) request,
+              (io.grpc.stub.StreamObserver<context.ContextOuterClass.ConnectionEvent>) responseObserver);
+          break;
         default:
           throw new AssertionError();
       }
@@ -2377,6 +2808,12 @@ public final class ContextServiceGrpc {
               .addMethod(getSetServiceMethod())
               .addMethod(getRemoveServiceMethod())
               .addMethod(getGetServiceEventsMethod())
+              .addMethod(getListConnectionIdsMethod())
+              .addMethod(getListConnectionsMethod())
+              .addMethod(getGetConnectionMethod())
+              .addMethod(getSetConnectionMethod())
+              .addMethod(getRemoveConnectionMethod())
+              .addMethod(getGetConnectionEventsMethod())
               .build();
         }
       }
diff --git a/src/policy/target/generated-sources/grpc/context/MutinyContextServiceGrpc.java b/src/policy/target/generated-sources/grpc/context/MutinyContextServiceGrpc.java
index 7642fdfd5c6d203fe0e0b042ca06448477b05bad..85abba20a9f315f684f964d16db4ad0cd7e91299 100644
--- a/src/policy/target/generated-sources/grpc/context/MutinyContextServiceGrpc.java
+++ b/src/policy/target/generated-sources/grpc/context/MutinyContextServiceGrpc.java
@@ -161,6 +161,31 @@ public final class MutinyContextServiceGrpc implements io.quarkus.grpc.runtime.M
         }
 
         
+        public io.smallrye.mutiny.Uni<context.ContextOuterClass.ConnectionIdList> listConnectionIds(context.ContextOuterClass.ServiceId request) {
+            return io.quarkus.grpc.runtime.ClientCalls.oneToOne(request, delegateStub::listConnectionIds);
+        }
+
+        
+        public io.smallrye.mutiny.Uni<context.ContextOuterClass.ConnectionList> listConnections(context.ContextOuterClass.ServiceId request) {
+            return io.quarkus.grpc.runtime.ClientCalls.oneToOne(request, delegateStub::listConnections);
+        }
+
+        
+        public io.smallrye.mutiny.Uni<context.ContextOuterClass.Connection> getConnection(context.ContextOuterClass.ConnectionId request) {
+            return io.quarkus.grpc.runtime.ClientCalls.oneToOne(request, delegateStub::getConnection);
+        }
+
+        
+        public io.smallrye.mutiny.Uni<context.ContextOuterClass.ConnectionId> setConnection(context.ContextOuterClass.Connection request) {
+            return io.quarkus.grpc.runtime.ClientCalls.oneToOne(request, delegateStub::setConnection);
+        }
+
+        
+        public io.smallrye.mutiny.Uni<context.ContextOuterClass.Empty> removeConnection(context.ContextOuterClass.ConnectionId request) {
+            return io.quarkus.grpc.runtime.ClientCalls.oneToOne(request, delegateStub::removeConnection);
+        }
+
+        
         public io.smallrye.mutiny.Multi<context.ContextOuterClass.ContextEvent> getContextEvents(context.ContextOuterClass.Empty request) {
             return io.quarkus.grpc.runtime.ClientCalls.oneToMany(request, delegateStub::getContextEvents);
         }
@@ -185,6 +210,11 @@ public final class MutinyContextServiceGrpc implements io.quarkus.grpc.runtime.M
             return io.quarkus.grpc.runtime.ClientCalls.oneToMany(request, delegateStub::getServiceEvents);
         }
 
+        
+        public io.smallrye.mutiny.Multi<context.ContextOuterClass.ConnectionEvent> getConnectionEvents(context.ContextOuterClass.Empty request) {
+            return io.quarkus.grpc.runtime.ClientCalls.oneToMany(request, delegateStub::getConnectionEvents);
+        }
+
     }
 
     
@@ -328,6 +358,31 @@ public final class MutinyContextServiceGrpc implements io.quarkus.grpc.runtime.M
         }
 
         
+        public io.smallrye.mutiny.Uni<context.ContextOuterClass.ConnectionIdList> listConnectionIds(context.ContextOuterClass.ServiceId request) {
+            throw new io.grpc.StatusRuntimeException(io.grpc.Status.UNIMPLEMENTED);
+        }
+
+        
+        public io.smallrye.mutiny.Uni<context.ContextOuterClass.ConnectionList> listConnections(context.ContextOuterClass.ServiceId request) {
+            throw new io.grpc.StatusRuntimeException(io.grpc.Status.UNIMPLEMENTED);
+        }
+
+        
+        public io.smallrye.mutiny.Uni<context.ContextOuterClass.Connection> getConnection(context.ContextOuterClass.ConnectionId request) {
+            throw new io.grpc.StatusRuntimeException(io.grpc.Status.UNIMPLEMENTED);
+        }
+
+        
+        public io.smallrye.mutiny.Uni<context.ContextOuterClass.ConnectionId> setConnection(context.ContextOuterClass.Connection request) {
+            throw new io.grpc.StatusRuntimeException(io.grpc.Status.UNIMPLEMENTED);
+        }
+
+        
+        public io.smallrye.mutiny.Uni<context.ContextOuterClass.Empty> removeConnection(context.ContextOuterClass.ConnectionId request) {
+            throw new io.grpc.StatusRuntimeException(io.grpc.Status.UNIMPLEMENTED);
+        }
+
+        
         public io.smallrye.mutiny.Multi<context.ContextOuterClass.ContextEvent> getContextEvents(context.ContextOuterClass.Empty request) {
             throw new io.grpc.StatusRuntimeException(io.grpc.Status.UNIMPLEMENTED);
         }
@@ -352,6 +407,11 @@ public final class MutinyContextServiceGrpc implements io.quarkus.grpc.runtime.M
             throw new io.grpc.StatusRuntimeException(io.grpc.Status.UNIMPLEMENTED);
         }
 
+        
+        public io.smallrye.mutiny.Multi<context.ContextOuterClass.ConnectionEvent> getConnectionEvents(context.ContextOuterClass.Empty request) {
+            throw new io.grpc.StatusRuntimeException(io.grpc.Status.UNIMPLEMENTED);
+        }
+
         @java.lang.Override public final io.grpc.ServerServiceDefinition bindService() {
             return io.grpc.ServerServiceDefinition.builder(getServiceDescriptor())
                     .addMethod(
@@ -564,6 +624,48 @@ public final class MutinyContextServiceGrpc implements io.quarkus.grpc.runtime.M
                                             context.ContextOuterClass.Empty,
                                             context.ContextOuterClass.ServiceEvent>(
                                             this, METHODID_GET_SERVICE_EVENTS, compression)))
+                    .addMethod(
+                            context.ContextServiceGrpc.getListConnectionIdsMethod(),
+                            asyncUnaryCall(
+                                    new MethodHandlers<
+                                            context.ContextOuterClass.ServiceId,
+                                            context.ContextOuterClass.ConnectionIdList>(
+                                            this, METHODID_LIST_CONNECTION_IDS, compression)))
+                    .addMethod(
+                            context.ContextServiceGrpc.getListConnectionsMethod(),
+                            asyncUnaryCall(
+                                    new MethodHandlers<
+                                            context.ContextOuterClass.ServiceId,
+                                            context.ContextOuterClass.ConnectionList>(
+                                            this, METHODID_LIST_CONNECTIONS, compression)))
+                    .addMethod(
+                            context.ContextServiceGrpc.getGetConnectionMethod(),
+                            asyncUnaryCall(
+                                    new MethodHandlers<
+                                            context.ContextOuterClass.ConnectionId,
+                                            context.ContextOuterClass.Connection>(
+                                            this, METHODID_GET_CONNECTION, compression)))
+                    .addMethod(
+                            context.ContextServiceGrpc.getSetConnectionMethod(),
+                            asyncUnaryCall(
+                                    new MethodHandlers<
+                                            context.ContextOuterClass.Connection,
+                                            context.ContextOuterClass.ConnectionId>(
+                                            this, METHODID_SET_CONNECTION, compression)))
+                    .addMethod(
+                            context.ContextServiceGrpc.getRemoveConnectionMethod(),
+                            asyncUnaryCall(
+                                    new MethodHandlers<
+                                            context.ContextOuterClass.ConnectionId,
+                                            context.ContextOuterClass.Empty>(
+                                            this, METHODID_REMOVE_CONNECTION, compression)))
+                    .addMethod(
+                            context.ContextServiceGrpc.getGetConnectionEventsMethod(),
+                            asyncServerStreamingCall(
+                                    new MethodHandlers<
+                                            context.ContextOuterClass.Empty,
+                                            context.ContextOuterClass.ConnectionEvent>(
+                                            this, METHODID_GET_CONNECTION_EVENTS, compression)))
                     .build();
         }
     }
@@ -598,6 +700,12 @@ public final class MutinyContextServiceGrpc implements io.quarkus.grpc.runtime.M
     private static final int METHODID_SET_SERVICE = 27;
     private static final int METHODID_REMOVE_SERVICE = 28;
     private static final int METHODID_GET_SERVICE_EVENTS = 29;
+    private static final int METHODID_LIST_CONNECTION_IDS = 30;
+    private static final int METHODID_LIST_CONNECTIONS = 31;
+    private static final int METHODID_GET_CONNECTION = 32;
+    private static final int METHODID_SET_CONNECTION = 33;
+    private static final int METHODID_REMOVE_CONNECTION = 34;
+    private static final int METHODID_GET_CONNECTION_EVENTS = 35;
 
     private static final class MethodHandlers<Req, Resp> implements
             io.grpc.stub.ServerCalls.UnaryMethod<Req, Resp>,
@@ -798,6 +906,42 @@ public final class MutinyContextServiceGrpc implements io.quarkus.grpc.runtime.M
                             compression,
                             serviceImpl::getServiceEvents);
                     break;
+                case METHODID_LIST_CONNECTION_IDS:
+                    io.quarkus.grpc.runtime.ServerCalls.oneToOne((context.ContextOuterClass.ServiceId) request,
+                            (io.grpc.stub.StreamObserver<context.ContextOuterClass.ConnectionIdList>) responseObserver,
+                            compression,
+                            serviceImpl::listConnectionIds);
+                    break;
+                case METHODID_LIST_CONNECTIONS:
+                    io.quarkus.grpc.runtime.ServerCalls.oneToOne((context.ContextOuterClass.ServiceId) request,
+                            (io.grpc.stub.StreamObserver<context.ContextOuterClass.ConnectionList>) responseObserver,
+                            compression,
+                            serviceImpl::listConnections);
+                    break;
+                case METHODID_GET_CONNECTION:
+                    io.quarkus.grpc.runtime.ServerCalls.oneToOne((context.ContextOuterClass.ConnectionId) request,
+                            (io.grpc.stub.StreamObserver<context.ContextOuterClass.Connection>) responseObserver,
+                            compression,
+                            serviceImpl::getConnection);
+                    break;
+                case METHODID_SET_CONNECTION:
+                    io.quarkus.grpc.runtime.ServerCalls.oneToOne((context.ContextOuterClass.Connection) request,
+                            (io.grpc.stub.StreamObserver<context.ContextOuterClass.ConnectionId>) responseObserver,
+                            compression,
+                            serviceImpl::setConnection);
+                    break;
+                case METHODID_REMOVE_CONNECTION:
+                    io.quarkus.grpc.runtime.ServerCalls.oneToOne((context.ContextOuterClass.ConnectionId) request,
+                            (io.grpc.stub.StreamObserver<context.ContextOuterClass.Empty>) responseObserver,
+                            compression,
+                            serviceImpl::removeConnection);
+                    break;
+                case METHODID_GET_CONNECTION_EVENTS:
+                    io.quarkus.grpc.runtime.ServerCalls.oneToMany((context.ContextOuterClass.Empty) request,
+                            (io.grpc.stub.StreamObserver<context.ContextOuterClass.ConnectionEvent>) responseObserver,
+                            compression,
+                            serviceImpl::getConnectionEvents);
+                    break;
                 default:
                     throw new java.lang.AssertionError();
             }
diff --git a/src/policy/target/generated-sources/grpc/kpi_sample_types/KpiSampleTypes.java b/src/policy/target/generated-sources/grpc/kpi_sample_types/KpiSampleTypes.java
new file mode 100644
index 0000000000000000000000000000000000000000..67e1ec736f9d83cbf95b419e9e61e92e82e73b88
--- /dev/null
+++ b/src/policy/target/generated-sources/grpc/kpi_sample_types/KpiSampleTypes.java
@@ -0,0 +1,176 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: kpi_sample_types.proto
+
+package kpi_sample_types;
+
+public final class KpiSampleTypes {
+  private KpiSampleTypes() {}
+  public static void registerAllExtensions(
+      com.google.protobuf.ExtensionRegistryLite registry) {
+  }
+
+  public static void registerAllExtensions(
+      com.google.protobuf.ExtensionRegistry registry) {
+    registerAllExtensions(
+        (com.google.protobuf.ExtensionRegistryLite) registry);
+  }
+  /**
+   * Protobuf enum {@code kpi_sample_types.KpiSampleType}
+   */
+  public enum KpiSampleType
+      implements com.google.protobuf.ProtocolMessageEnum {
+    /**
+     * <code>KPISAMPLETYPE_UNKNOWN = 0;</code>
+     */
+    KPISAMPLETYPE_UNKNOWN(0),
+    /**
+     * <code>KPISAMPLETYPE_PACKETS_TRANSMITTED = 101;</code>
+     */
+    KPISAMPLETYPE_PACKETS_TRANSMITTED(101),
+    /**
+     * <code>KPISAMPLETYPE_PACKETS_RECEIVED = 102;</code>
+     */
+    KPISAMPLETYPE_PACKETS_RECEIVED(102),
+    /**
+     * <code>KPISAMPLETYPE_BYTES_TRANSMITTED = 201;</code>
+     */
+    KPISAMPLETYPE_BYTES_TRANSMITTED(201),
+    /**
+     * <code>KPISAMPLETYPE_BYTES_RECEIVED = 202;</code>
+     */
+    KPISAMPLETYPE_BYTES_RECEIVED(202),
+    UNRECOGNIZED(-1),
+    ;
+
+    /**
+     * <code>KPISAMPLETYPE_UNKNOWN = 0;</code>
+     */
+    public static final int KPISAMPLETYPE_UNKNOWN_VALUE = 0;
+    /**
+     * <code>KPISAMPLETYPE_PACKETS_TRANSMITTED = 101;</code>
+     */
+    public static final int KPISAMPLETYPE_PACKETS_TRANSMITTED_VALUE = 101;
+    /**
+     * <code>KPISAMPLETYPE_PACKETS_RECEIVED = 102;</code>
+     */
+    public static final int KPISAMPLETYPE_PACKETS_RECEIVED_VALUE = 102;
+    /**
+     * <code>KPISAMPLETYPE_BYTES_TRANSMITTED = 201;</code>
+     */
+    public static final int KPISAMPLETYPE_BYTES_TRANSMITTED_VALUE = 201;
+    /**
+     * <code>KPISAMPLETYPE_BYTES_RECEIVED = 202;</code>
+     */
+    public static final int KPISAMPLETYPE_BYTES_RECEIVED_VALUE = 202;
+
+
+    public final int getNumber() {
+      if (this == UNRECOGNIZED) {
+        throw new java.lang.IllegalArgumentException(
+            "Can't get the number of an unknown enum value.");
+      }
+      return value;
+    }
+
+    /**
+     * @param value The numeric wire value of the corresponding enum entry.
+     * @return The enum associated with the given numeric wire value.
+     * @deprecated Use {@link #forNumber(int)} instead.
+     */
+    @java.lang.Deprecated
+    public static KpiSampleType valueOf(int value) {
+      return forNumber(value);
+    }
+
+    /**
+     * @param value The numeric wire value of the corresponding enum entry.
+     * @return The enum associated with the given numeric wire value.
+     */
+    public static KpiSampleType forNumber(int value) {
+      switch (value) {
+        case 0: return KPISAMPLETYPE_UNKNOWN;
+        case 101: return KPISAMPLETYPE_PACKETS_TRANSMITTED;
+        case 102: return KPISAMPLETYPE_PACKETS_RECEIVED;
+        case 201: return KPISAMPLETYPE_BYTES_TRANSMITTED;
+        case 202: return KPISAMPLETYPE_BYTES_RECEIVED;
+        default: return null;
+      }
+    }
+
+    public static com.google.protobuf.Internal.EnumLiteMap<KpiSampleType>
+        internalGetValueMap() {
+      return internalValueMap;
+    }
+    private static final com.google.protobuf.Internal.EnumLiteMap<
+        KpiSampleType> internalValueMap =
+          new com.google.protobuf.Internal.EnumLiteMap<KpiSampleType>() {
+            public KpiSampleType findValueByNumber(int number) {
+              return KpiSampleType.forNumber(number);
+            }
+          };
+
+    public final com.google.protobuf.Descriptors.EnumValueDescriptor
+        getValueDescriptor() {
+      if (this == UNRECOGNIZED) {
+        throw new java.lang.IllegalStateException(
+            "Can't get the descriptor of an unrecognized enum value.");
+      }
+      return getDescriptor().getValues().get(ordinal());
+    }
+    public final com.google.protobuf.Descriptors.EnumDescriptor
+        getDescriptorForType() {
+      return getDescriptor();
+    }
+    public static final com.google.protobuf.Descriptors.EnumDescriptor
+        getDescriptor() {
+      return kpi_sample_types.KpiSampleTypes.getDescriptor().getEnumTypes().get(0);
+    }
+
+    private static final KpiSampleType[] VALUES = values();
+
+    public static KpiSampleType valueOf(
+        com.google.protobuf.Descriptors.EnumValueDescriptor desc) {
+      if (desc.getType() != getDescriptor()) {
+        throw new java.lang.IllegalArgumentException(
+          "EnumValueDescriptor is not for this type.");
+      }
+      if (desc.getIndex() == -1) {
+        return UNRECOGNIZED;
+      }
+      return VALUES[desc.getIndex()];
+    }
+
+    private final int value;
+
+    private KpiSampleType(int value) {
+      this.value = value;
+    }
+
+    // @@protoc_insertion_point(enum_scope:kpi_sample_types.KpiSampleType)
+  }
+
+
+  public static com.google.protobuf.Descriptors.FileDescriptor
+      getDescriptor() {
+    return descriptor;
+  }
+  private static  com.google.protobuf.Descriptors.FileDescriptor
+      descriptor;
+  static {
+    java.lang.String[] descriptorData = {
+      "\n\026kpi_sample_types.proto\022\020kpi_sample_typ" +
+      "es*\276\001\n\rKpiSampleType\022\031\n\025KPISAMPLETYPE_UN" +
+      "KNOWN\020\000\022%\n!KPISAMPLETYPE_PACKETS_TRANSMI" +
+      "TTED\020e\022\"\n\036KPISAMPLETYPE_PACKETS_RECEIVED" +
+      "\020f\022$\n\037KPISAMPLETYPE_BYTES_TRANSMITTED\020\311\001" +
+      "\022!\n\034KPISAMPLETYPE_BYTES_RECEIVED\020\312\001b\006pro" +
+      "to3"
+    };
+    descriptor = com.google.protobuf.Descriptors.FileDescriptor
+      .internalBuildGeneratedFileFrom(descriptorData,
+        new com.google.protobuf.Descriptors.FileDescriptor[] {
+        });
+  }
+
+  // @@protoc_insertion_point(outer_class_scope)
+}
diff --git a/src/policy/target/kubernetes/kubernetes.yml b/src/policy/target/kubernetes/kubernetes.yml
index b82855281d2afc44e0a8f9e02637c09c0ab95d7c..269478f972d87478f02223336448174ccf03a174 100644
--- a/src/policy/target/kubernetes/kubernetes.yml
+++ b/src/policy/target/kubernetes/kubernetes.yml
@@ -1,13 +1,26 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 ---
 apiVersion: v1
 kind: Service
 metadata:
   annotations:
-    app.quarkus.io/commit-id: 46a3c5ec903563da8a3b9cf2dfefd2556ad63eaa
-    app.quarkus.io/build-timestamp: 2021-11-11 - 13:57:11 +0000
+    app.quarkus.io/commit-id: 43bda7817c976a9fb8ed7a410ad4f0ea13f62498
+    app.quarkus.io/build-timestamp: 2022-02-15 - 14:44:53 +0000
   labels:
     app.kubernetes.io/name: policyservice
-    app.kubernetes.io/version: 0.0.1
     app: policyservice
   name: policyservice
 spec:
@@ -20,34 +33,30 @@ spec:
       targetPort: 8080
   selector:
     app.kubernetes.io/name: policyservice
-    app.kubernetes.io/version: 0.0.1
-  type: NodePort
+  type: ClusterIP
 ---
 apiVersion: apps/v1
 kind: Deployment
 metadata:
   annotations:
-    app.quarkus.io/commit-id: 46a3c5ec903563da8a3b9cf2dfefd2556ad63eaa
-    app.quarkus.io/build-timestamp: 2021-11-11 - 13:57:11 +0000
+    app.quarkus.io/commit-id: 43bda7817c976a9fb8ed7a410ad4f0ea13f62498
+    app.quarkus.io/build-timestamp: 2022-02-15 - 14:44:53 +0000
   labels:
     app: policyservice
-    app.kubernetes.io/version: 0.0.1
     app.kubernetes.io/name: policyservice
   name: policyservice
 spec:
   replicas: 1
   selector:
     matchLabels:
-      app.kubernetes.io/version: 0.0.1
       app.kubernetes.io/name: policyservice
   template:
     metadata:
       annotations:
-        app.quarkus.io/commit-id: 46a3c5ec903563da8a3b9cf2dfefd2556ad63eaa
-        app.quarkus.io/build-timestamp: 2021-11-11 - 13:57:11 +0000
+        app.quarkus.io/commit-id: 43bda7817c976a9fb8ed7a410ad4f0ea13f62498
+        app.quarkus.io/build-timestamp: 2022-02-15 - 14:44:53 +0000
       labels:
         app: policyservice
-        app.kubernetes.io/version: 0.0.1
         app.kubernetes.io/name: policyservice
     spec:
       containers:
@@ -56,7 +65,7 @@ spec:
               valueFrom:
                 fieldRef:
                   fieldPath: metadata.namespace
-          image: registry.gitlab.com/teraflow-h2020/controller/policy:0.0.1
+          image: registry.gitlab.com/teraflow-h2020/controller/policy:0.1.0
           imagePullPolicy: Always
           livenessProbe:
             failureThreshold: 3
diff --git a/src/policy/util/set_version.sh b/src/policy/util/set_version.sh
new file mode 100755
index 0000000000000000000000000000000000000000..0d73a243ea9cf6eeaa77601de6eff54c09ee40a2
--- /dev/null
+++ b/src/policy/util/set_version.sh
@@ -0,0 +1,31 @@
+#!/usr/bin/env bash
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -eu
+
+if (( $# != 1 )); then
+  echo "Usage: set_version.sh <version>" >&2
+  exit 1
+fi
+
+version="$1"
+
+if [ "$(git status --untracked-files=no --porcelain)" ]; then
+    printf "Uncommitted changes in tracked files.\nPlease commit first and then run the script!\n"
+    exit 0;
+fi
+
+./mvnw versions:set versions:commit -DnewVersion="${version}"
+git commit -am "release(policy): ${version}"
diff --git a/src/service/.gitlab-ci.yml b/src/service/.gitlab-ci.yml
index e4da10db792f6782cdeb80e631c17271a657e962..3f845a9dd960b6a9dcfd140720b3b8db923f8ddf 100644
--- a/src/service/.gitlab-ci.yml
+++ b/src/service/.gitlab-ci.yml
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 # Build, tag and push the Docker image to the GitLab registry
 build service:
   variables:
@@ -40,7 +54,9 @@ unit test service:
     - sleep 5
     - docker ps -a
     - docker logs $IMAGE_NAME
-    - docker exec -i $IMAGE_NAME bash -c "coverage run -m pytest --log-level=INFO --verbose $IMAGE_NAME/tests/test_unitary.py --junitxml=/opt/results/${IMAGE_NAME}_report.xml; coverage xml -o /opt/results/${IMAGE_NAME}_coverage.xml; coverage report --include='${IMAGE_NAME}/*' --show-missing"
+    - docker exec -i $IMAGE_NAME bash -c "coverage run -m pytest --log-level=INFO --verbose $IMAGE_NAME/tests/test_unitary.py --junitxml=/opt/results/${IMAGE_NAME}_report.xml"
+    - docker exec -i $IMAGE_NAME bash -c "coverage xml -o /opt/results/${IMAGE_NAME}_coverage.xml"
+    - docker exec -i $IMAGE_NAME bash -c "coverage report --include='${IMAGE_NAME}/*' --show-missing"
   coverage: '/TOTAL\s+\d+\s+\d+\s+(\d+%)/'
   after_script:
     - docker rm -f $IMAGE_NAME
diff --git a/src/service/Config.py b/src/service/Config.py
index 79ca2232c1ee273dbda9d3c6642ded229b32ab6f..5d551b023fc21202a1411ae46781682055fdef91 100644
--- a/src/service/Config.py
+++ b/src/service/Config.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging
 
 # General settings
diff --git a/src/service/Dockerfile b/src/service/Dockerfile
index 3e8dcaa31acdc2c97f71fbd12a3ca730e93a7677..dced0fd45939f5522bb7e614dbac8faf13231c4b 100644
--- a/src/service/Dockerfile
+++ b/src/service/Dockerfile
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 FROM python:3-slim
 
 # Install dependencies
@@ -25,7 +39,7 @@ RUN mkdir -p /var/teraflow/service
 # Get Python packages per module
 COPY service/requirements.in service/requirements.in
 RUN pip-compile --output-file=service/requirements.txt service/requirements.in
-RUN python3 -m pip install -r service/requirements.in
+RUN python3 -m pip install -r service/requirements.txt
 
 # Add files into working directory
 COPY common/. common
diff --git a/src/service/__init__.py b/src/service/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/service/__init__.py
+++ b/src/service/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/service/client/ServiceClient.py b/src/service/client/ServiceClient.py
index b9d123b88b1c004192098caccedf4b67dc92ac2b..af489c0c672ac601939fe907e179cd0c83a3d140 100644
--- a/src/service/client/ServiceClient.py
+++ b/src/service/client/ServiceClient.py
@@ -1,11 +1,26 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import grpc, logging
 from common.tools.client.RetryDecorator import retry, delay_exponential
-from service.proto.context_pb2 import ConnectionList, Empty, Service, ServiceId
+from service.proto.context_pb2 import Empty, Service, ServiceId
 from service.proto.service_pb2_grpc import ServiceServiceStub
 
 LOGGER = logging.getLogger(__name__)
 MAX_RETRIES = 15
 DELAY_FUNCTION = delay_exponential(initial=0.01, increment=2.0, maximum=5.0)
+RETRY_DECORATOR = retry(max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
 
 class ServiceClient:
     def __init__(self, address, port):
@@ -25,30 +40,23 @@ class ServiceClient:
         self.channel = None
         self.stub = None
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def CreateService(self, request : Service) -> ServiceId:
         LOGGER.debug('CreateService request: {:s}'.format(str(request)))
         response = self.stub.CreateService(request)
         LOGGER.debug('CreateService result: {:s}'.format(str(response)))
         return response
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def UpdateService(self, request : Service) -> ServiceId:
         LOGGER.debug('UpdateService request: {:s}'.format(str(request)))
         response = self.stub.UpdateService(request)
         LOGGER.debug('UpdateService result: {:s}'.format(str(response)))
         return response
 
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    @RETRY_DECORATOR
     def DeleteService(self, request : ServiceId) -> Empty:
         LOGGER.debug('DeleteService request: {:s}'.format(str(request)))
         response = self.stub.DeleteService(request)
         LOGGER.debug('DeleteService result: {:s}'.format(str(response)))
         return response
-
-    @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
-    def GetConnectionList(self, request : ServiceId) -> ConnectionList:
-        LOGGER.debug('GetConnectionList request: {:s}'.format(str(request)))
-        response = self.stub.GetConnectionList(request)
-        LOGGER.debug('GetConnectionList result: {:s}'.format(str(response)))
-        return response
diff --git a/src/service/client/__init__.py b/src/service/client/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/service/client/__init__.py
+++ b/src/service/client/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/service/proto/__init__.py b/src/service/proto/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/service/proto/__init__.py
+++ b/src/service/proto/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/service/requirements.in b/src/service/requirements.in
index eb922871fa487db730696063a02083722ee7cfc3..8f2241b4710788089b9e7ddaee69ea5bccb7943b 100644
--- a/src/service/requirements.in
+++ b/src/service/requirements.in
@@ -1,18 +1,23 @@
-anytree
-apscheduler
-fastcache
-flask-restful
-grpcio-health-checking
-grpcio
-Jinja2
-netconf-client #1.7.3
-prometheus-client
-pytest
-pytest-benchmark
-python-json-logger
-pytz
-redis
-requests
-xmltodict
+anytree==2.8.0
+APScheduler==3.8.1
+fastcache==1.1.0
+Flask==2.0.2
+Flask-RESTful==0.3.9
+grpcio==1.43.0
+grpcio-health-checking==1.43.0
+Jinja2==3.0.3
+netconf-client==2.0.0 #1.7.3
+networkx==2.6.3
 p4runtime==1.3.0
-coverage
+paramiko==2.9.2
+prometheus-client==0.13.0
+protobuf==3.19.3
+pydot==1.4.2
+pytest==6.2.5
+pytest-benchmark==3.4.1
+python-json-logger==2.0.2
+pytz==2021.3
+redis==4.1.2
+requests==2.27.1
+xmltodict==0.12.0
+coverage==6.3
diff --git a/src/service/service/ServiceService.py b/src/service/service/ServiceService.py
index bff0d7b2f32bf0498adaa834a49226efb373b786..21945b7d3c2fe75e27bfa37bc7465f75e7b660f1 100644
--- a/src/service/service/ServiceService.py
+++ b/src/service/service/ServiceService.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import grpc, logging
 from concurrent import futures
 from grpc_health.v1.health import HealthServicer, OVERALL_HEALTH
diff --git a/src/service/service/ServiceServiceServicerImpl.py b/src/service/service/ServiceServiceServicerImpl.py
index ca93c098010031823fcd38059a624e7b0c62a5bb..2506a420617674be04772437e72c8c541c9f384c 100644
--- a/src/service/service/ServiceServiceServicerImpl.py
+++ b/src/service/service/ServiceServiceServicerImpl.py
@@ -1,35 +1,41 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from typing import Dict, List
 import grpc, json, logging
-from typing import Any, List, Optional, Tuple
-from google.protobuf.json_format import MessageToDict
 from common.orm.Database import Database
 from common.orm.HighLevel import get_object
 from common.orm.backend.Tools import key_to_str
 from common.rpc_method_wrapper.Decorator import create_metrics, safe_and_metered_rpc_method
-from common.rpc_method_wrapper.ServiceExceptions import (
-    InvalidArgumentException, NotFoundException, OperationFailedException)
+from common.rpc_method_wrapper.ServiceExceptions import InvalidArgumentException, NotFoundException
+from common.tools.grpc.Tools import grpc_message_to_json, grpc_message_to_json_string
 from context.client.ContextClient import ContextClient
 from device.client.DeviceClient import DeviceClient
-from service.proto.context_pb2 import ConnectionList, Empty, Service, ServiceId
+from service.proto.context_pb2 import Empty, Service, ServiceId
 from service.proto.service_pb2_grpc import ServiceServiceServicer
-from .database.ConfigModel import ConfigModel, ConfigRuleModel
-from .database.ConstraintModel import ConstraintModel, ConstraintsModel
+from service.service.database.DeviceModel import DeviceModel
 from .database.DatabaseServiceTools import (
-    delete_service_from_context, sync_service_from_context, sync_service_to_context, update_service_in_local_database)
-from .database.RelationModels import ServiceEndPointModel
+    sync_service_from_context, sync_service_to_context, update_service_in_local_database)
 from .database.ServiceModel import ServiceModel
-from .service_handler_api._ServiceHandler import _ServiceHandler
+from .path_computation_element.PathComputationElement import PathComputationElement, dump_connectivity
 from .service_handler_api.ServiceHandlerFactory import ServiceHandlerFactory
-from .service_handler_api.Tools import (
-    check_errors_deleteconfig, check_errors_deleteconstraint, check_errors_deleteendpoint, check_errors_setconfig,
-    check_errors_setconstraint, check_errors_setendpoint)
-from .Tools import (
-    classify_config_rules, classify_constraints, classify_endpointids, get_service_handler_class,
-    sync_devices_from_context)
+from .Tools import delete_service, sync_devices_from_context, update_service
 
 LOGGER = logging.getLogger(__name__)
 
 SERVICE_NAME = 'Service'
-METHOD_NAMES = ['CreateService', 'UpdateService', 'DeleteService',  'GetConnectionList']
+METHOD_NAMES = ['CreateService', 'UpdateService', 'DeleteService']
 METRICS = create_metrics(SERVICE_NAME, METHOD_NAMES)
 
 class ServiceServiceServicerImpl(ServiceServiceServicer):
@@ -53,9 +59,7 @@ class ServiceServiceServicerImpl(ServiceServiceServicer):
         if len(request.service_endpoint_ids) > 0:
             unexpected_endpoints = []
             for service_endpoint_id in request.service_endpoint_ids:
-                unexpected_endpoints.append(MessageToDict(
-                    service_endpoint_id, including_default_value_fields=True, preserving_proto_field_name=True,
-                    use_integers_for_enums=True))
+                unexpected_endpoints.append(grpc_message_to_json(service_endpoint_id))
             str_unexpected_endpoints = json.dumps(unexpected_endpoints, sort_keys=True)
             raise InvalidArgumentException(
                 'service.service_endpoint_ids', str_unexpected_endpoints,
@@ -65,9 +69,7 @@ class ServiceServiceServicerImpl(ServiceServiceServicer):
         if len(request.service_constraints) > 0:
             unexpected_constraints = []
             for service_constraint in request.service_constraints:
-                unexpected_constraints.append(MessageToDict(
-                    service_constraint, including_default_value_fields=True, preserving_proto_field_name=True,
-                    use_integers_for_enums=True))
+                unexpected_constraints.append(grpc_message_to_json(service_constraint))
             str_unexpected_constraints = json.dumps(unexpected_constraints, sort_keys=True)
             raise InvalidArgumentException(
                 'service.service_constraints', str_unexpected_constraints,
@@ -75,9 +77,7 @@ class ServiceServiceServicerImpl(ServiceServiceServicer):
                               'Constraints should be configured after creating the service.')
 
         if len(request.service_config.config_rules) > 0:
-            unexpected_config_rules = MessageToDict(
-                request.service_config, including_default_value_fields=True,
-                preserving_proto_field_name=True, use_integers_for_enums=True)
+            unexpected_config_rules = grpc_message_to_json(request.service_config)
             unexpected_config_rules = unexpected_config_rules['config_rules']
             str_unexpected_config_rules = json.dumps(unexpected_config_rules, sort_keys=True)
             raise InvalidArgumentException(
@@ -96,110 +96,85 @@ class ServiceServiceServicerImpl(ServiceServiceServicer):
 
     @safe_and_metered_rpc_method(METRICS, LOGGER)
     def UpdateService(self, request : Service, context : grpc.ServicerContext) -> ServiceId:
+        LOGGER.info('[UpdateService] begin ; request = {:s}'.format(grpc_message_to_json_string(request)))
+
         service_id = request.service_id
         service_uuid = service_id.service_uuid.uuid
         service_context_uuid = service_id.context_id.context_uuid.uuid
-        str_service_key = key_to_str([service_context_uuid, service_uuid])
 
-        # Sync before updating service to ensure we have devices, endpoints, constraints, and config rules to be
-        # set/deleted before actuallymodifying them in the local in-memory database.
+        pce = PathComputationElement()
+        pce.load_topology(self.context_client)
+        pce.load_connectivity(self.context_client, service_id)
+        #pce.dump_topology_to_file('../data/topo.dot')
+        #pce.dump_connectivity_to_file('../data/conn-before.txt')
+        connectivity = pce.route_service(request)
+        #pce.dump_connectivity_to_file('../data/conn-after.txt')
+
+        LOGGER.info('[UpdateService] connectivity = {:s}'.format(str(dump_connectivity(connectivity))))
+
+        if connectivity is None:
+            # just update local database and context
+            str_service_key = key_to_str([service_context_uuid, service_uuid])
+            db_service = get_object(self.database, ServiceModel, str_service_key, raise_if_not_found=False)
+            LOGGER.info('[UpdateService] before db_service = {:s}'.format(str(db_service.dump(
+                include_endpoint_ids=True, include_constraints=True, include_config_rules=True))))
+            db_devices : Dict[str, DeviceModel] = sync_devices_from_context(
+                self.context_client, self.database, db_service, request.service_endpoint_ids)
+            LOGGER.info('[UpdateService] db_devices[{:d}] = {:s}'.format(
+                len(db_devices), str({
+                    device_uuid:db_device.dump(include_config_rules=True, include_drivers=True, include_endpoints=True)
+                    for device_uuid,db_device in db_devices.items()
+                })))
+            sync_service_from_context(service_context_uuid, service_uuid, self.context_client, self.database)
+            db_service,_ = update_service_in_local_database(self.database, request)
+            LOGGER.info('[UpdateService] after db_service = {:s}'.format(str(db_service.dump(
+                include_endpoint_ids=True, include_constraints=True, include_config_rules=True))))
+            sync_service_to_context(db_service, self.context_client)
+        else:
+            for sub_service, sub_connections in connectivity.get('requirements', []):
+                for sub_connection in sub_connections:
+                    update_service(
+                        self.database, self.context_client, self.device_client, self.service_handler_factory,
+                        sub_service, sub_connection)
+
+            for connection in connectivity.get('connections'):
+                db_service = update_service(
+                    self.database, self.context_client, self.device_client, self.service_handler_factory,
+                    request, connection)
+
+            str_service_key = key_to_str([service_context_uuid, service_uuid])
+            db_service = get_object(self.database, ServiceModel, str_service_key, raise_if_not_found=False)
+            if db_service is None: raise NotFoundException('Service', str_service_key)
 
-        sync_service_from_context(service_context_uuid, service_uuid, self.context_client, self.database)
-        db_service = get_object(self.database, ServiceModel, str_service_key, raise_if_not_found=False)
-        if db_service is None: raise NotFoundException('Service', str_service_key)
         LOGGER.info('[UpdateService] db_service = {:s}'.format(str(db_service.dump(
             include_endpoint_ids=True, include_constraints=True, include_config_rules=True))))
 
-        db_devices = sync_devices_from_context(self.context_client, db_service, request.service_endpoint_ids)
-
-        resources_to_set    : List[Tuple[str, Any]] = [] # resource_key, resource_value
-        resources_to_delete : List[Tuple[str, Any]] = [] # resource_key, resource_value
-        classify_config_rules(db_service, request.service_config.config_rules, resources_to_set, resources_to_delete)
-
-        constraints_to_set    : List[Tuple[str, str]] = [] # constraint_type, constraint_value
-        constraints_to_delete : List[Tuple[str, str]] = [] # constraint_type, constraint_value
-        classify_constraints(db_service, request.service_constraints, constraints_to_set, constraints_to_delete)
-
-        endpointids_to_set    : List[Tuple[str, str, Optional[str]]] = [] # device_uuid, endpoint_uuid, topology_uuid
-        endpointids_to_delete : List[Tuple[str, str, Optional[str]]] = [] # device_uuid, endpoint_uuid, topology_uuid
-        classify_endpointids(db_service, request.service_endpoint_ids, endpointids_to_set, endpointids_to_delete)
-
-        service_handler_class = get_service_handler_class(self.service_handler_factory, db_service, db_devices)
-        service_handler_settings = {}
-        service_handler : _ServiceHandler = service_handler_class(
-            db_service, self.database, self.context_client, self.device_client, **service_handler_settings)
-
-        errors = []
-
-        if len(errors) == 0:
-            results_deleteendpoint = service_handler.DeleteEndpoint(endpointids_to_delete)
-            errors.extend(check_errors_deleteendpoint(endpointids_to_delete, results_deleteendpoint))
-
-        if len(errors) == 0:
-            results_deleteconstraint = service_handler.DeleteConstraint(constraints_to_delete)
-            errors.extend(check_errors_deleteconstraint(constraints_to_delete, results_deleteconstraint))
-
-        if len(errors) == 0:
-            results_deleteconfig = service_handler.DeleteConfig(resources_to_delete)
-            errors.extend(check_errors_deleteconfig(resources_to_delete, results_deleteconfig))
-
-        if len(errors) == 0:
-            results_setconfig = service_handler.SetConfig(resources_to_set)
-            errors.extend(check_errors_setconfig(resources_to_set, results_setconfig))
-
-        if len(errors) == 0:
-            results_setconstraint = service_handler.SetConstraint(constraints_to_set)
-            errors.extend(check_errors_setconstraint(constraints_to_set, results_setconstraint))
-
-        if len(errors) == 0:
-            results_setendpoint = service_handler.SetEndpoint(endpointids_to_set)
-            errors.extend(check_errors_setendpoint(endpointids_to_set, results_setendpoint))
-
-        if len(errors) > 0:
-            raise OperationFailedException('UpdateService', extra_details=errors)
-
-        db_service,_ = update_service_in_local_database(self.database, request)
-        LOGGER.info('[UpdateService] db_service = {:s}'.format(str(db_service.dump(
-            include_endpoint_ids=True, include_constraints=True, include_config_rules=True))))
-
-        #db_entries = self.database.dump()
-        #LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries)))
-        #for db_entry in db_entries:
-        #    LOGGER.info('  [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover
-        #LOGGER.info('-----------------------------------------------------------')
-
-        sync_service_to_context(db_service, self.context_client)
         return ServiceId(**db_service.dump_id())
 
     @safe_and_metered_rpc_method(METRICS, LOGGER)
     def DeleteService(self, request : ServiceId, context : grpc.ServicerContext) -> Empty:
-        service_uuid = request.service_uuid.uuid
-        service_context_uuid = request.context_id.context_uuid.uuid
-
-        sync_service_from_context(service_context_uuid, service_uuid, self.context_client, self.database)
-        str_service_key = key_to_str([service_context_uuid, service_uuid])
-        db_service : ServiceModel = get_object(self.database, ServiceModel, str_service_key, raise_if_not_found=False)
-        if db_service is None: return Empty()
-
-        delete_service_from_context(db_service, self.context_client)
-
-        for db_service_endpoint_pk,_ in db_service.references(ServiceEndPointModel):
-            ServiceEndPointModel(self.database, db_service_endpoint_pk).delete()
-
-        db_running_config = ConfigModel(self.database, db_service.service_config_fk)
-        for db_config_rule_pk,_ in db_running_config.references(ConfigRuleModel):
-            ConfigRuleModel(self.database, db_config_rule_pk).delete()
+        LOGGER.info('[DeleteService] begin ; request = {:s}'.format(grpc_message_to_json_string(request)))
+
+        pce = PathComputationElement()
+        pce.load_topology(self.context_client)
+        pce.load_connectivity(self.context_client, request)
+        #pce.dump_topology_to_file('../data/topo.dot')
+        #pce.dump_connectivity_to_file('../data/conn-before.txt')
+        connectivity = pce.get_connectivity_from_service_id(request)
+        if connectivity is None: return Empty()
+        #pce.dump_connectivity_to_file('../data/conn-after.txt')
+
+        LOGGER.info('[DeleteService] connectivity = {:s}'.format(str(dump_connectivity(connectivity))))
+
+        for connection in connectivity.get('connections'):
+            delete_service(
+                self.database, self.context_client, self.device_client, self.service_handler_factory,
+                request, connection)
+
+        for sub_service, sub_connections in connectivity.get('requirements', []):
+            for sub_connection in sub_connections:
+                delete_service(
+                    self.database, self.context_client, self.device_client, self.service_handler_factory,
+                    sub_service.service_id, sub_connection)
 
-        db_running_constraints = ConstraintsModel(self.database, db_service.service_constraints_fk)
-        for db_constraint_pk,_ in db_running_constraints.references(ConstraintModel):
-            ConstraintModel(self.database, db_constraint_pk).delete()
-
-        db_service.delete()
-        db_running_config.delete()
-        db_running_constraints.delete()
         return Empty()
-
-    @safe_and_metered_rpc_method(METRICS, LOGGER)
-    def GetConnectionList(self, request : ServiceId, context : grpc.ServicerContext) -> ConnectionList:
-        #raise ServiceException(grpc.StatusCode.UNIMPLEMENTED, 'RPC GetConnectionList() not implemented')
-        return ConnectionList()
diff --git a/src/service/service/Tools.py b/src/service/service/Tools.py
index 80084f363231ee18c1fe75559b5633f0b5b02e68..af9ab658979182cfe50d8d80dd254d1aedf62c88 100644
--- a/src/service/service/Tools.py
+++ b/src/service/service/Tools.py
@@ -1,33 +1,57 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging
 from typing import Any, Dict, List, Optional, Tuple
+from common.orm.Database import Database
 from common.orm.HighLevel import get_object, get_related_objects
-from common.rpc_method_wrapper.ServiceExceptions import NotFoundException
+from common.orm.backend.Tools import key_to_str
+from common.rpc_method_wrapper.ServiceExceptions import (
+    InvalidArgumentException, NotFoundException, OperationFailedException)
 from context.client.ContextClient import ContextClient
-from service.proto.context_pb2 import ConfigRule, Constraint, EndPointId
-from service.service.database.ConstraintModel import get_constraints, grpc_constraints_to_raw
-from service.service.database.DatabaseDeviceTools import sync_device_from_context
-from service.service.database.EndPointModel import EndPointModel, grpc_endpointids_to_raw
-from .database.ConfigModel import ORM_ConfigActionEnum, get_config_rules, grpc_config_rules_to_raw
+from context.proto.context_pb2 import ConfigRule, Connection, Constraint, EndPointId, Service, ServiceId, ServiceStatusEnum
+from device.client.DeviceClient import DeviceClient
+from .database.ConfigModel import (
+    ConfigModel, ConfigRuleModel, ORM_ConfigActionEnum, get_config_rules, grpc_config_rules_to_raw)
+from .database.ConstraintModel import ConstraintModel, ConstraintsModel, get_constraints, grpc_constraints_to_raw
+from .database.DatabaseDeviceTools import sync_device_from_context
+from .database.DatabaseServiceTools import (
+    delete_service_from_context, sync_service_from_context, sync_service_to_context, update_service_in_local_database)
 from .database.DeviceModel import DeviceModel, DriverModel
+from .database.EndPointModel import EndPointModel, grpc_endpointids_to_raw
 from .database.RelationModels import ServiceEndPointModel
 from .database.ServiceModel import ServiceModel
 from .service_handler_api._ServiceHandler import _ServiceHandler
 from .service_handler_api.FilterFields import FilterFieldEnum
 from .service_handler_api.ServiceHandlerFactory import ServiceHandlerFactory
+from .service_handler_api.Tools import (
+    check_errors_deleteconfig, check_errors_deleteconstraint, check_errors_deleteendpoint, check_errors_setconfig,
+    check_errors_setconstraint, check_errors_setendpoint)
 
 LOGGER = logging.getLogger(__name__)
 
 def sync_devices_from_context(
-    context_client : ContextClient, db_service : ServiceModel, service_endpoint_ids : List[EndPointId]
+        context_client : ContextClient, database : Database, db_service : Optional[ServiceModel],
+        service_endpoint_ids : List[EndPointId]
     ) -> Dict[str, DeviceModel]:
 
-    database = db_service.database
-
     required_device_uuids = set()
-    db_endpoints = get_related_objects(db_service, ServiceEndPointModel, 'endpoint_fk')
-    for db_endpoint in db_endpoints:
-        db_device = DeviceModel(database, db_endpoint.device_fk)
-        required_device_uuids.add(db_device.device_uuid)
+    if db_service is not None:
+        db_endpoints = get_related_objects(db_service, ServiceEndPointModel, 'endpoint_fk')
+        for db_endpoint in db_endpoints:
+            db_device = get_object(database, DeviceModel, db_endpoint.device_fk, raise_if_not_found=False)
+            required_device_uuids.add(db_device.device_uuid)
 
     for endpoint_id in service_endpoint_ids:
         required_device_uuids.add(endpoint_id.device_id.device_uuid.uuid)
@@ -67,6 +91,8 @@ def classify_config_rules(
         elif action == ORM_ConfigActionEnum.DELETE:
             if key in context_config_rules:
                 resources_to_delete.append((key, value))
+        else:
+            raise InvalidArgumentException('config_rule.action', str(action), extra_details=str(request_config_rules))
 
     #LOGGER.info('[classify_config_rules] resources_to_set = {:s}'.format(str(resources_to_set)))
     #LOGGER.info('[classify_config_rules] resources_to_delete = {:s}'.format(str(resources_to_delete)))
@@ -119,13 +145,15 @@ def classify_endpointids(
     request_endpoint_ids = grpc_endpointids_to_raw(service_endpoint_ids)
     #LOGGER.info('[classify_endpointids] request_endpoint_ids = {:s}'.format(str(request_endpoint_ids)))
 
+    if len(service_endpoint_ids) != 2: return
     for endpoint_id in request_endpoint_ids:
-        if endpoint_id not in context_endpoint_ids:
-            endpointids_to_set.append(endpoint_id)
-        context_endpoint_ids.discard(endpoint_id)
+        #if endpoint_id not in context_endpoint_ids:
+        #    endpointids_to_set.append(endpoint_id)
+        #context_endpoint_ids.discard(endpoint_id)
+        endpointids_to_set.append(endpoint_id)
 
-    for endpoint_id in context_endpoint_ids:
-        endpointids_to_delete.append(endpoint_id)
+    #for endpoint_id in context_endpoint_ids:
+    #    endpointids_to_delete.append(endpoint_id)
 
     #LOGGER.info('[classify_endpointids] endpointids_to_set = {:s}'.format(str(endpointids_to_set)))
     #LOGGER.info('[classify_endpointids] endpointids_to_delete = {:s}'.format(str(endpointids_to_delete)))
@@ -158,3 +186,156 @@ def get_service_handler_class(
     msg = 'ServiceHandler({:s}) selected for service({:s}) with filter_fields({:s})...'
     LOGGER.info(msg.format(str(service_handler_class.__name__), str(str_service_key), str(filter_fields)))
     return service_handler_class
+
+def update_service(
+        database : Database, context_client : ContextClient, device_client : DeviceClient,
+        service_handler_factory : ServiceHandlerFactory, service : Service, connection : Connection
+    ) -> ServiceModel:
+
+    service_id = service.service_id
+    service_uuid = service_id.service_uuid.uuid
+    service_context_uuid = service_id.context_id.context_uuid.uuid
+    str_service_key = key_to_str([service_context_uuid, service_uuid])
+
+    # Sync before updating service to ensure we have devices, endpoints, constraints, and config rules to be
+    # set/deleted before actuallymodifying them in the local in-memory database.
+
+    sync_service_from_context(service_context_uuid, service_uuid, context_client, database)
+    db_service = get_object(database, ServiceModel, str_service_key, raise_if_not_found=False)
+    db_devices = sync_devices_from_context(context_client, database, db_service, service.service_endpoint_ids)
+
+    if db_service is None: db_service,_ = update_service_in_local_database(database, service)
+    LOGGER.info('[update_service] db_service = {:s}'.format(str(db_service.dump(
+        include_endpoint_ids=True, include_constraints=True, include_config_rules=True))))
+
+    resources_to_set    : List[Tuple[str, Any]] = [] # resource_key, resource_value
+    resources_to_delete : List[Tuple[str, Any]] = [] # resource_key, resource_value
+    classify_config_rules(db_service, service.service_config.config_rules, resources_to_set, resources_to_delete)
+
+    constraints_to_set    : List[Tuple[str, str]] = [] # constraint_type, constraint_value
+    constraints_to_delete : List[Tuple[str, str]] = [] # constraint_type, constraint_value
+    classify_constraints(db_service, service.service_constraints, constraints_to_set, constraints_to_delete)
+
+    endpointids_to_set    : List[Tuple[str, str, Optional[str]]] = [] # device_uuid, endpoint_uuid, topology_uuid
+    endpointids_to_delete : List[Tuple[str, str, Optional[str]]] = [] # device_uuid, endpoint_uuid, topology_uuid
+    classify_endpointids(db_service, service.service_endpoint_ids, endpointids_to_set, endpointids_to_delete)
+
+    service_handler_class = get_service_handler_class(service_handler_factory, db_service, db_devices)
+    service_handler_settings = {}
+    service_handler : _ServiceHandler = service_handler_class(
+        db_service, database, context_client, device_client, **service_handler_settings)
+
+    errors = []
+
+    if len(errors) == 0:
+        results_deleteendpoint = service_handler.DeleteEndpoint(endpointids_to_delete)
+        errors.extend(check_errors_deleteendpoint(endpointids_to_delete, results_deleteendpoint))
+
+    if len(errors) == 0:
+        results_deleteconstraint = service_handler.DeleteConstraint(constraints_to_delete)
+        errors.extend(check_errors_deleteconstraint(constraints_to_delete, results_deleteconstraint))
+
+    if len(errors) == 0:
+        results_deleteconfig = service_handler.DeleteConfig(resources_to_delete)
+        errors.extend(check_errors_deleteconfig(resources_to_delete, results_deleteconfig))
+
+    if len(errors) == 0:
+        results_setconfig = service_handler.SetConfig(resources_to_set)
+        errors.extend(check_errors_setconfig(resources_to_set, results_setconfig))
+
+    if len(errors) == 0:
+        results_setconstraint = service_handler.SetConstraint(constraints_to_set)
+        errors.extend(check_errors_setconstraint(constraints_to_set, results_setconstraint))
+
+    if len(errors) == 0:
+        results_setendpoint = service_handler.SetEndpoint(endpointids_to_set)
+        errors.extend(check_errors_setendpoint(endpointids_to_set, results_setendpoint))
+
+    if len(errors) > 0:
+        raise OperationFailedException('UpdateService', extra_details=errors)
+
+    LOGGER.info('[update_service] len(service.service_endpoint_ids) = {:d}'.format(len(service.service_endpoint_ids)))
+    if len(service.service_endpoint_ids) >= 2:
+        service.service_status.service_status = ServiceStatusEnum.SERVICESTATUS_ACTIVE
+
+    db_service,_ = update_service_in_local_database(database, service)
+    LOGGER.info('[update_service] db_service = {:s}'.format(str(db_service.dump(
+        include_endpoint_ids=True, include_constraints=True, include_config_rules=True))))
+
+    sync_service_to_context(db_service, context_client)
+    context_client.SetConnection(connection)
+    return db_service
+
+def delete_service(
+        database : Database, context_client : ContextClient, device_client : DeviceClient,
+        service_handler_factory : ServiceHandlerFactory, service_id : ServiceId, connection : Connection
+    ) -> None:
+
+    context_client.RemoveConnection(connection.connection_id)
+
+    service_uuid = service_id.service_uuid.uuid
+    service_context_uuid = service_id.context_id.context_uuid.uuid
+    str_service_key = key_to_str([service_context_uuid, service_uuid])
+
+    # Sync before updating service to ensure we have devices, endpoints, constraints, and config rules to be
+    # set/deleted before actuallymodifying them in the local in-memory database.
+
+    sync_service_from_context(service_context_uuid, service_uuid, context_client, database)
+    db_service : ServiceModel = get_object(database, ServiceModel, str_service_key, raise_if_not_found=False)
+    if db_service is None: return
+    LOGGER.info('[delete_service] db_service = {:s}'.format(str(db_service.dump(
+        include_endpoint_ids=True, include_constraints=True, include_config_rules=True))))
+
+    db_devices = sync_devices_from_context(context_client, database, db_service, [])
+
+    resources_to_delete : List[Tuple[str, str]] = [     # resource_key, resource_value
+        (config_rule[1], config_rule[2])
+        for config_rule in get_config_rules(db_service.database, db_service.pk, 'running')
+    ]
+
+    constraints_to_delete : List[Tuple[str, str]] = [   # constraint_type, constraint_value
+        (constraint[0], constraint[1])
+        for constraint in get_constraints(db_service.database, db_service.pk, 'running')
+    ]
+
+    # device_uuid, endpoint_uuid, topology_uuid
+    endpointids_to_delete : List[Tuple[str, str, Optional[str]]] = list(set(get_service_endpointids(db_service)))
+
+    service_handler_class = get_service_handler_class(service_handler_factory, db_service, db_devices)
+    service_handler_settings = {}
+    service_handler : _ServiceHandler = service_handler_class(
+        db_service, database, context_client, device_client, **service_handler_settings)
+
+    errors = []
+
+    if len(errors) == 0:
+        results_deleteendpoint = service_handler.DeleteEndpoint(endpointids_to_delete)
+        errors.extend(check_errors_deleteendpoint(endpointids_to_delete, results_deleteendpoint))
+
+    if len(errors) == 0:
+        results_deleteconstraint = service_handler.DeleteConstraint(constraints_to_delete)
+        errors.extend(check_errors_deleteconstraint(constraints_to_delete, results_deleteconstraint))
+
+    if len(errors) == 0:
+        results_deleteconfig = service_handler.DeleteConfig(resources_to_delete)
+        errors.extend(check_errors_deleteconfig(resources_to_delete, results_deleteconfig))
+
+    if len(errors) > 0:
+        raise OperationFailedException('DeleteService', extra_details=errors)
+
+    delete_service_from_context(db_service, context_client)
+
+    for db_service_endpoint_pk,_ in db_service.references(ServiceEndPointModel):
+        ServiceEndPointModel(database, db_service_endpoint_pk).delete()
+
+    db_running_config = ConfigModel(database, db_service.service_config_fk)
+    for db_config_rule_pk,_ in db_running_config.references(ConfigRuleModel):
+        ConfigRuleModel(database, db_config_rule_pk).delete()
+
+    db_running_constraints = ConstraintsModel(database, db_service.service_constraints_fk)
+    for db_constraint_pk,_ in db_running_constraints.references(ConstraintModel):
+        ConstraintModel(database, db_constraint_pk).delete()
+
+    db_service.delete()
+    db_running_config.delete()
+    db_running_constraints.delete()
diff --git a/src/service/service/__init__.py b/src/service/service/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/service/service/__init__.py
+++ b/src/service/service/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/service/service/__main__.py b/src/service/service/__main__.py
index d6a0e9fd32c4e707dd731e5185aa7751e8bd65ee..cc1b008958572020ebbe54fdfcda27ed4218f80e 100644
--- a/src/service/service/__main__.py
+++ b/src/service/service/__main__.py
@@ -1,6 +1,20 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging, signal, sys, threading
 from prometheus_client import start_http_server
-from common.Settings import get_setting
+from common.Settings import get_setting, wait_for_environment_variables
 from context.client.ContextClient import ContextClient
 from device.client.DeviceClient import DeviceClient
 from service.Config import (
@@ -11,7 +25,7 @@ from .service_handler_api.ServiceHandlerFactory import ServiceHandlerFactory
 from .service_handlers import SERVICE_HANDLERS
 
 terminate = threading.Event()
-LOGGER = None
+LOGGER : logging.Logger = None
 
 def signal_handler(signal, frame): # pylint: disable=redefined-outer-name
     LOGGER.warning('Terminate signal received')
@@ -25,14 +39,20 @@ def main():
     grace_period         = get_setting('GRACE_PERIOD',                     default=GRPC_GRACE_PERIOD   )
     log_level            = get_setting('LOG_LEVEL',                        default=LOG_LEVEL           )
     metrics_port         = get_setting('METRICS_PORT',                     default=METRICS_PORT        )
+
+    logging.basicConfig(level=log_level)
+    LOGGER = logging.getLogger(__name__)
+
+    wait_for_environment_variables([
+        'CONTEXTSERVICE_SERVICE_HOST', 'CONTEXTSERVICE_SERVICE_PORT_GRPC',
+        'DEVICESERVICE_SERVICE_HOST', 'DEVICESERVICE_SERVICE_PORT_GRPC'
+    ])
+
     context_service_host = get_setting('CONTEXTSERVICE_SERVICE_HOST',      default=CONTEXT_SERVICE_HOST)
     context_service_port = get_setting('CONTEXTSERVICE_SERVICE_PORT_GRPC', default=CONTEXT_SERVICE_PORT)
     device_service_host  = get_setting('DEVICESERVICE_SERVICE_HOST',       default=DEVICE_SERVICE_HOST )
     device_service_port  = get_setting('DEVICESERVICE_SERVICE_PORT_GRPC',  default=DEVICE_SERVICE_PORT )
 
-    logging.basicConfig(level=log_level)
-    LOGGER = logging.getLogger(__name__)
-
     signal.signal(signal.SIGINT,  signal_handler)
     signal.signal(signal.SIGTERM, signal_handler)
 
diff --git a/src/service/service/database/ConfigModel.py b/src/service/service/database/ConfigModel.py
index 39a2b5a794fa88d2700603975f36a3fa4ef90450..a71bb18d13335d45b8c5d4a9783fb1c759e9dea8 100644
--- a/src/service/service/database/ConfigModel.py
+++ b/src/service/service/database/ConfigModel.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import functools, logging, operator
 from enum import Enum
 from typing import Dict, List, Tuple, Union
diff --git a/src/service/service/database/ConstraintModel.py b/src/service/service/database/ConstraintModel.py
index b2a4933e4caa9697a41947e4a9266b1a0d6ee5a7..4c58076accdb3e5e01d3e197148c60e8dee5ba77 100644
--- a/src/service/service/database/ConstraintModel.py
+++ b/src/service/service/database/ConstraintModel.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging, operator
 from typing import Dict, List, Tuple, Union
 from common.orm.Database import Database
diff --git a/src/service/service/database/ContextModel.py b/src/service/service/database/ContextModel.py
index 74e577e5ba5ab366cd7c3ca07c8730d21d5e8ec9..b094961ca5ae7468505414ac164ebaa7b0f8f6f4 100644
--- a/src/service/service/database/ContextModel.py
+++ b/src/service/service/database/ContextModel.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging
 from typing import Dict #, List
 from common.orm.fields.PrimaryKeyField import PrimaryKeyField
diff --git a/src/service/service/database/DatabaseDeviceTools.py b/src/service/service/database/DatabaseDeviceTools.py
index 9cf0252831921b07abeba629bf154dc2c8b475da..081e9eead4e6c9e6ea25d5a38c76e65c16b9b478 100644
--- a/src/service/service/database/DatabaseDeviceTools.py
+++ b/src/service/service/database/DatabaseDeviceTools.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import grpc
 from typing import Tuple
 from common.orm.Database import Database
diff --git a/src/service/service/database/DatabaseServiceTools.py b/src/service/service/database/DatabaseServiceTools.py
index 0b538f82a62b73aaa194628b986810204dcbc46b..f00c1ff2d1379ffab69d3eb042b3adf18c4d61c8 100644
--- a/src/service/service/database/DatabaseServiceTools.py
+++ b/src/service/service/database/DatabaseServiceTools.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import grpc
 from typing import Tuple
 from common.Constants import DEFAULT_CONTEXT_UUID, DEFAULT_TOPOLOGY_UUID
@@ -59,7 +73,8 @@ def update_service_in_local_database(database : Database, service : Service) ->
     if len(topology_uuid) > 0:
         str_topology_key = key_to_str([service_context_uuid, topology_uuid])
         result : Tuple[TopologyModel, bool] = get_or_create_object(
-            database, TopologyModel, str_topology_key, defaults={'context_fk': db_context, 'topology_uuid': topology_uuid})
+            database, TopologyModel, str_topology_key, defaults={
+                'context_fk': db_context, 'topology_uuid': topology_uuid})
         #db_topology, _ = result
 
     str_service_key = key_to_str([service_context_uuid, service_uuid])
diff --git a/src/service/service/database/DeviceModel.py b/src/service/service/database/DeviceModel.py
index d005292acfd4cf20619548dbfa5b48a08780ec2f..b5faaf77d083e2cb6ef140368a383dd6389dfa18 100644
--- a/src/service/service/database/DeviceModel.py
+++ b/src/service/service/database/DeviceModel.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import functools, logging
 from enum import Enum
 from typing import Dict, List
diff --git a/src/service/service/database/EndPointModel.py b/src/service/service/database/EndPointModel.py
index 78b00db8628a841c8deae89e3db8ca1056fa1db5..95287a4ce5599f38703c4ff7f4c19fbc236924e5 100644
--- a/src/service/service/database/EndPointModel.py
+++ b/src/service/service/database/EndPointModel.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging
 from typing import Dict, List, Optional, Tuple
 from common.orm.fields.ForeignKeyField import ForeignKeyField
diff --git a/src/service/service/database/LinkModel.py b/src/service/service/database/LinkModel.py
new file mode 100644
index 0000000000000000000000000000000000000000..742044b9758df297413ad2d0318520c825e8b738
--- /dev/null
+++ b/src/service/service/database/LinkModel.py
@@ -0,0 +1,40 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging, operator
+from typing import Dict, List
+from common.orm.fields.PrimaryKeyField import PrimaryKeyField
+from common.orm.fields.StringField import StringField
+from common.orm.model.Model import Model
+from common.orm.HighLevel import get_related_objects
+
+LOGGER = logging.getLogger(__name__)
+
+class LinkModel(Model):
+    pk = PrimaryKeyField()
+    link_uuid = StringField(required=True, allow_empty=False)
+
+    def dump_id(self) -> Dict:
+        return {'link_uuid': {'uuid': self.link_uuid}}
+
+    def dump_endpoint_ids(self) -> List[Dict]:
+        from .RelationModels import LinkEndPointModel # pylint: disable=import-outside-toplevel
+        db_endpoints = get_related_objects(self, LinkEndPointModel, 'endpoint_fk')
+        return [db_endpoint.dump_id() for db_endpoint in sorted(db_endpoints, key=operator.attrgetter('pk'))]
+
+    def dump(self) -> Dict:
+        return {
+            'link_id': self.dump_id(),
+            'link_endpoint_ids': self.dump_endpoint_ids(),
+        }
diff --git a/src/service/service/database/RelationModels.py b/src/service/service/database/RelationModels.py
index fbf93feff84a99aa20acb43a31b9abb30ae14a20..ce596893f756c66a84cb6e516871f2cb3d5d181b 100644
--- a/src/service/service/database/RelationModels.py
+++ b/src/service/service/database/RelationModels.py
@@ -1,19 +1,33 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging
 from common.orm.fields.ForeignKeyField import ForeignKeyField
 from common.orm.fields.PrimaryKeyField import PrimaryKeyField
 from common.orm.model.Model import Model
 from .DeviceModel import DeviceModel
 from .EndPointModel import EndPointModel
-#from .LinkModel import LinkModel
+from .LinkModel import LinkModel
 from .ServiceModel import ServiceModel
 from .TopologyModel import TopologyModel
 
 LOGGER = logging.getLogger(__name__)
 
-#class LinkEndPointModel(Model): # pylint: disable=abstract-method
-#    pk = PrimaryKeyField()
-#    link_fk = ForeignKeyField(LinkModel)
-#    endpoint_fk = ForeignKeyField(EndPointModel)
+class LinkEndPointModel(Model): # pylint: disable=abstract-method
+    pk = PrimaryKeyField()
+    link_fk = ForeignKeyField(LinkModel)
+    endpoint_fk = ForeignKeyField(EndPointModel)
 
 class ServiceEndPointModel(Model): # pylint: disable=abstract-method
     pk = PrimaryKeyField()
@@ -25,7 +39,7 @@ class TopologyDeviceModel(Model): # pylint: disable=abstract-method
     topology_fk = ForeignKeyField(TopologyModel)
     device_fk = ForeignKeyField(DeviceModel)
 
-#class TopologyLinkModel(Model): # pylint: disable=abstract-method
-#    pk = PrimaryKeyField()
-#    topology_fk = ForeignKeyField(TopologyModel)
-#    link_fk = ForeignKeyField(LinkModel)
+class TopologyLinkModel(Model): # pylint: disable=abstract-method
+    pk = PrimaryKeyField()
+    topology_fk = ForeignKeyField(TopologyModel)
+    link_fk = ForeignKeyField(LinkModel)
diff --git a/src/service/service/database/ServiceModel.py b/src/service/service/database/ServiceModel.py
index f6bd2e9c47f77538fac6d65aa0e02ca720126abb..86376f775c54280d06e059aceadc23515d3589e5 100644
--- a/src/service/service/database/ServiceModel.py
+++ b/src/service/service/database/ServiceModel.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import functools, logging, operator
 from enum import Enum
 from typing import Dict, List
diff --git a/src/service/service/database/Tools.py b/src/service/service/database/Tools.py
index 36ffbcd46fcf686371b0799445ce4f9ce5b75838..43bb71bd90582644c67d3ca528611eae937b6460 100644
--- a/src/service/service/database/Tools.py
+++ b/src/service/service/database/Tools.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import hashlib, re
 from enum import Enum
 from typing import Dict, List, Tuple, Union
diff --git a/src/service/service/database/TopologyModel.py b/src/service/service/database/TopologyModel.py
index 54a7e75e5059e0f1f605fe6235740c0094ab73c5..5909c7a2c63d05f2cbde7f0d8555e63587e96682 100644
--- a/src/service/service/database/TopologyModel.py
+++ b/src/service/service/database/TopologyModel.py
@@ -1,10 +1,24 @@
-import logging #, operator
-#from typing import Dict, List
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging, operator
+from typing import Dict, List
 from common.orm.fields.ForeignKeyField import ForeignKeyField
 from common.orm.fields.PrimaryKeyField import PrimaryKeyField
 from common.orm.fields.StringField import StringField
 from common.orm.model.Model import Model
-#from common.orm.HighLevel import get_related_objects
+from common.orm.HighLevel import get_related_objects
 from .ContextModel import ContextModel
 
 LOGGER = logging.getLogger(__name__)
@@ -14,27 +28,27 @@ class TopologyModel(Model):
     context_fk = ForeignKeyField(ContextModel)
     topology_uuid = StringField(required=True, allow_empty=False)
 
-#    def dump_id(self) -> Dict:
-#        context_id = ContextModel(self.database, self.context_fk).dump_id()
-#        return {
-#            'context_id': context_id,
-#            'topology_uuid': {'uuid': self.topology_uuid},
-#        }
-#
-#    def dump_device_ids(self) -> List[Dict]:
-#        from .RelationModels import TopologyDeviceModel # pylint: disable=import-outside-toplevel
-#        db_devices = get_related_objects(self, TopologyDeviceModel, 'device_fk')
-#        return [db_device.dump_id() for db_device in sorted(db_devices, key=operator.attrgetter('pk'))]
-#
-#    def dump_link_ids(self) -> List[Dict]:
-#        from .RelationModels import TopologyLinkModel # pylint: disable=import-outside-toplevel
-#        db_links = get_related_objects(self, TopologyLinkModel, 'link_fk')
-#        return [db_link.dump_id() for db_link in sorted(db_links, key=operator.attrgetter('pk'))]
-#
-#    def dump(   # pylint: disable=arguments-differ
-#            self, include_devices=True, include_links=True
-#        ) -> Dict:
-#        result = {'topology_id': self.dump_id()}
-#        if include_devices: result['device_ids'] = self.dump_device_ids()
-#        if include_links: result['link_ids'] = self.dump_link_ids()
-#        return result
+    def dump_id(self) -> Dict:
+        context_id = ContextModel(self.database, self.context_fk).dump_id()
+        return {
+            'context_id': context_id,
+            'topology_uuid': {'uuid': self.topology_uuid},
+        }
+
+    def dump_device_ids(self) -> List[Dict]:
+        from .RelationModels import TopologyDeviceModel # pylint: disable=import-outside-toplevel
+        db_devices = get_related_objects(self, TopologyDeviceModel, 'device_fk')
+        return [db_device.dump_id() for db_device in sorted(db_devices, key=operator.attrgetter('pk'))]
+
+    def dump_link_ids(self) -> List[Dict]:
+        from .RelationModels import TopologyLinkModel # pylint: disable=import-outside-toplevel
+        db_links = get_related_objects(self, TopologyLinkModel, 'link_fk')
+        return [db_link.dump_id() for db_link in sorted(db_links, key=operator.attrgetter('pk'))]
+
+    def dump(   # pylint: disable=arguments-differ
+            self, include_devices=True, include_links=True
+        ) -> Dict:
+        result = {'topology_id': self.dump_id()}
+        if include_devices: result['device_ids'] = self.dump_device_ids()
+        if include_links: result['link_ids'] = self.dump_link_ids()
+        return result
diff --git a/src/service/service/database/__init__.py b/src/service/service/database/__init__.py
index 5d2c65947e2775c4c9dc5dda8e7737bb04edb8cd..926623031b92b7dc7ad545b5f5da369b7ab79856 100644
--- a/src/service/service/database/__init__.py
+++ b/src/service/service/database/__init__.py
@@ -1,2 +1,16 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 # In-Memory database with a simplified representation of Context Database focused on the Service model.
 # Used as an internal data cache, for message validation, and message formatting purposes.
diff --git a/src/service/service/path_computation_element/Enums.py b/src/service/service/path_computation_element/Enums.py
new file mode 100644
index 0000000000000000000000000000000000000000..f55d545b54abf1749e1757200b509562dd4d0455
--- /dev/null
+++ b/src/service/service/path_computation_element/Enums.py
@@ -0,0 +1,37 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from enum import Enum
+
+class NodeTypeEnum(Enum):
+    DEVICE   = 'device'
+    ENDPOINT = 'endpoint'
+
+class EndPointTypeEnum(Enum):
+    COPPER    = 'copper'
+    OPTICAL   = 'optical'
+    MICROWAVE = 'microwave'
+
+class EdgeTypeEnum(Enum):
+    INTERNAL  = 'internal'
+    COPPER    = 'copper'
+    OPTICAL   = 'optical'
+    MICROWAVE = 'microwave'
+    VIRTUAL   = 'virtual'
+    OTHER     = 'other'
+
+class LayerTypeEnum(Enum):
+    COPPER    = 'copper'
+    OPTICAL   = 'optical'
+    MICROWAVE = 'microwave'
diff --git a/src/service/service/path_computation_element/PathComputationElement.py b/src/service/service/path_computation_element/PathComputationElement.py
new file mode 100644
index 0000000000000000000000000000000000000000..763c93a725af92ecfb41b5d0f95cd2b9fdc22d8a
--- /dev/null
+++ b/src/service/service/path_computation_element/PathComputationElement.py
@@ -0,0 +1,371 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import grpc, json, logging, networkx, uuid
+from queue import Queue
+from typing import Dict, List, Tuple
+from networkx.drawing.nx_pydot import write_dot
+from common.Constants import DEFAULT_CONTEXT_UUID
+from common.DeviceTypes import DeviceTypeEnum
+from common.tools.grpc.Tools import (
+    grpc_message_list_to_json, grpc_message_list_to_json_string, grpc_message_to_json, grpc_message_to_json_string)
+from context.client.ContextClient import ContextClient
+from context.proto.context_pb2 import (
+    ConfigActionEnum, Connection, Device, Empty, EndPoint, EndPointId, Service, ServiceId, ServiceStatusEnum,
+    ServiceTypeEnum)
+from .Enums import EdgeTypeEnum, NodeTypeEnum
+from .Tools import get_device, get_device_key, get_edge_type, get_endpoint, get_endpoint_key, get_link_key
+
+LOGGER = logging.getLogger(__name__)
+LOGGER.setLevel(logging.INFO)
+
+SUB_SERVICE_TYPES = {
+    DeviceTypeEnum.EMULATED_PACKET_ROUTER.value      : ServiceTypeEnum.SERVICETYPE_L3NM,
+    DeviceTypeEnum.EMULATED_OPTICAL_LINE_SYSTEM.value: ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE,
+    DeviceTypeEnum.PACKET_ROUTER.value               : ServiceTypeEnum.SERVICETYPE_L3NM,
+    DeviceTypeEnum.OPTICAL_LINE_SYSTEM.value         : ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE,
+}
+DEFAULT_SUB_SERVICE_TYPE = ServiceTypeEnum.SERVICETYPE_UNKNOWN
+
+def dump_requirements(requirements):
+    if requirements is None: return None
+    return [
+        {
+            'sub_service': grpc_message_to_json(sub_service),
+            'sub_connections': grpc_message_list_to_json(sub_connections),
+        }
+        for sub_service,sub_connections in requirements
+    ]
+
+def dump_connectivity(connectivity):
+    if connectivity is None: return None
+    return {
+        'service': grpc_message_to_json(connectivity.get('service')),
+        'connections': grpc_message_list_to_json(connectivity.get('connections', [])),
+        'requirements': dump_requirements(connectivity.get('requirements', [])),
+    }
+
+def dump_hops(hops):
+    if hops is None: return None
+    return [
+        'in_endpoint={:s}, device={:s}, out_endpoint={:s}'.format(
+            grpc_message_to_json_string(in_endpoint), grpc_message_to_json_string(device),
+            grpc_message_to_json_string(out_endpoint))
+        for in_endpoint,device,out_endpoint in hops
+    ]
+
+class PathComputationElement:
+    def __init__(self) -> None:
+        self.__topology = networkx.Graph()
+        self.__connectivity = {} # (a_ep_key, z_ep_key) => {service, connection, sub_services: [], sub_connections: []}
+        self.__service_endpoints = {} # (context_uuid, service_uuid) => (a_ep_key, z_ep_key)
+
+    def dump_topology_to_file(self, dot_filepath : str):
+        write_dot(self.__topology, dot_filepath)
+
+    def dump_connectivity_to_file(self, filepath : str):
+        with open(filepath, 'w', encoding='UTF-8') as f:
+            f.write(str(self.__connectivity)) # do not use json; it contains protobuf objects
+
+    def load_topology(self, context_client : ContextClient):
+        response_devices = context_client.ListDevices(Empty())
+        devices = response_devices.devices
+        LOGGER.debug('Devices[{:d}] = {:s}'.format(len(devices), grpc_message_to_json_string(response_devices)))
+        assert len(devices) > 0
+
+        response_links = context_client.ListLinks(Empty())
+        links = response_links.links
+        LOGGER.debug('Links[{:d}] = {:s}'.format(len(links), grpc_message_to_json_string(response_links)))
+        assert len(links) > 0
+
+        for device in response_devices.devices:
+            device_uuid = get_device_key(device.device_id)
+            self.__topology.add_node(device_uuid, node_type=NodeTypeEnum.DEVICE, device=device)
+            for endpoint in device.device_endpoints:
+                endpoint_key = get_endpoint_key(endpoint.endpoint_id, device_uuid=device_uuid)
+                self.__topology.add_node(endpoint_key, node_type=NodeTypeEnum.ENDPOINT, endpoint=endpoint)
+                self.__topology.add_edge(device_uuid, endpoint_key, edge_type=EdgeTypeEnum.INTERNAL)
+
+        for link in response_links.links:
+            link_uuid = get_link_key(link.link_id)
+            if len(link.link_endpoint_ids) != 2:
+                msg = 'Link({:s}) with {:d} != 2 endpoints'
+                raise NotImplementedError(msg.format(link_uuid, len(link.link_endpoint_ids)))
+            endpoint_keys = [get_endpoint_key(endpoint_id) for endpoint_id in link.link_endpoint_ids]
+            edge_type = get_edge_type(self.__topology, endpoint_keys)
+            self.__topology.add_edge(endpoint_keys[0], endpoint_keys[1], link=link, edge_type=edge_type)
+
+    def load_connectivity(self, context_client : ContextClient, service_id : ServiceId):
+        pending_service_ids = Queue()
+        pending_service_ids.put((service_id, True))
+
+        connectivity = {}
+        requirements : List[Tuple[Service, List[Connection]]] = connectivity.setdefault('requirements', [])
+        connections : List[Connection] = connectivity.setdefault('connections', [])
+
+        while not pending_service_ids.empty():
+            service_id,is_main = pending_service_ids.get()
+
+            try:
+                service = context_client.GetService(service_id)
+                LOGGER.debug('[load_connectivity] GetService({:s}) = {:s}'.format(
+                    grpc_message_to_json_string(service_id), grpc_message_to_json_string(service)))
+            except grpc.RpcError as e:
+                if is_main and e.code() == grpc.StatusCode.NOT_FOUND: continue # pylint: disable=no-member
+                raise
+
+            # TODO: implement support for services with more than 2 endpoints;
+            # Right now, services with less than 2 endpoints are ignored; with more than 2 endpoints throws exception.
+            # e.g., compute pairs such as:
+            #   list(filter(lambda ep: ep[0] < ep[1], itertools.product(service_endpoint_ids, service_endpoint_ids)))
+            service_endpoint_ids = service.service_endpoint_ids
+            if len(service_endpoint_ids) < 2: continue
+            if len(service_endpoint_ids) > 2: raise NotImplementedError('Service with more than 2 endpoints')
+
+            service_connections = context_client.ListConnections(service_id)
+            LOGGER.debug('[load_connectivity] ListConnections({:s}) = {:s}'.format(
+                grpc_message_to_json_string(service_id), grpc_message_to_json_string(service_connections)))
+
+            if is_main:
+                connectivity['service'] = service
+                a_endpoint_key = get_endpoint_key(service_endpoint_ids[0])
+                z_endpoint_key = get_endpoint_key(service_endpoint_ids[-1])
+                self.__connectivity[(a_endpoint_key, z_endpoint_key)] = connectivity
+                self.__connectivity[(z_endpoint_key, a_endpoint_key)] = connectivity
+                context_service_id = (service_id.context_id.context_uuid.uuid, service_id.service_uuid.uuid)
+                self.__service_endpoints[context_service_id] = (a_endpoint_key, z_endpoint_key)
+                connections.extend(service_connections.connections)
+            else:
+                requirements.append((service, service_connections.connections))
+
+            for connection in service_connections.connections:
+                for service_id in connection.sub_service_ids:
+                    pending_service_ids.put((service_id, False))
+
+    def get_connectivity_from_service_id(self, service_id : ServiceId) -> Dict:
+        LOGGER.debug('[get_connectivity_from_service_id] service_id={:s}'.format(
+            grpc_message_to_json_string(service_id)))
+
+        context_uuid = service_id.context_id.context_uuid.uuid
+        service_uuid = service_id.service_uuid.uuid
+        context_service_id = (context_uuid, service_uuid)
+        if context_service_id in self.__service_endpoints:
+            a_endpoint_key, z_endpoint_key = self.__service_endpoints[context_service_id]
+        else:
+            return None
+
+        if (a_endpoint_key, z_endpoint_key) in self.__connectivity:
+            connectivity = self.__connectivity.get((a_endpoint_key, z_endpoint_key))
+        elif (z_endpoint_key, a_endpoint_key) in self.__connectivity:
+            connectivity = self.__connectivity.get((z_endpoint_key, a_endpoint_key))
+        else:
+            connectivity = None
+
+        if connectivity is None or connectivity.get('connections') is None: return None
+        LOGGER.debug('[get_connectivity_from_service_id] Connectivity: {:s}'.format(
+            str(dump_connectivity(connectivity))))
+        return connectivity
+
+    def get_connectivity(self, service : Service) -> Tuple[Dict, str, str]:
+        LOGGER.debug('[get_connectivity] service.service_id = {:s}'.format(
+            grpc_message_to_json_string(service.service_id)))
+
+        context_uuid = service.service_id.context_id.context_uuid.uuid
+        service_uuid = service.service_id.service_uuid.uuid
+        context_service_id = (context_uuid, service_uuid)
+
+        if context_service_id in self.__service_endpoints:
+            LOGGER.debug('[get_connectivity] exists')
+            a_endpoint_key, z_endpoint_key = self.__service_endpoints[context_service_id]
+
+            LOGGER.debug('[get_connectivity] a_endpoint_key={:s}'.format(str(a_endpoint_key)))
+            LOGGER.debug('[get_connectivity] z_endpoint_key={:s}'.format(str(z_endpoint_key)))
+        else:
+            LOGGER.debug('[get_connectivity] new')
+            # TODO: implement support for services with more than 2 endpoints;
+            # Right now, less than 2 reports None, more than 2 endpoints throws an exception.
+            # e.g., compute pairs such as:
+            #   list(filter(lambda ep: ep[0] < ep[1], itertools.product(service_endpoint_ids, service_endpoint_ids)))
+            service_endpoint_ids = service.service_endpoint_ids
+
+            LOGGER.debug('[get_connectivity] service_endpoint_ids[{:d}] = {:s}'.format(
+                len(service_endpoint_ids), grpc_message_list_to_json_string(service_endpoint_ids)))
+
+            if len(service_endpoint_ids) < 2: return None
+            if len(service_endpoint_ids) > 2: raise NotImplementedError('Service with more than 2 endpoints')
+
+            a_endpoint_key = get_endpoint_key(service_endpoint_ids[0])
+            z_endpoint_key = get_endpoint_key(service_endpoint_ids[-1])
+            LOGGER.debug('[get_connectivity] a_endpoint_key={:s}'.format(str(a_endpoint_key)))
+            LOGGER.debug('[get_connectivity] z_endpoint_key={:s}'.format(str(z_endpoint_key)))
+
+        if (a_endpoint_key, z_endpoint_key) in self.__connectivity:
+            connectivity = self.__connectivity.get((a_endpoint_key, z_endpoint_key))
+        elif (z_endpoint_key, a_endpoint_key) in self.__connectivity:
+            connectivity = self.__connectivity.get((z_endpoint_key, a_endpoint_key))
+        else:
+            connectivity = None
+
+        LOGGER.debug('[get_connectivity] connectivity = {:s}'.format(str(dump_connectivity(connectivity))))
+
+        if connectivity is None or connectivity.get('connections') is None: return None, a_endpoint_key, z_endpoint_key
+        return connectivity, a_endpoint_key, z_endpoint_key
+
+    def route_service(self, service : Service):
+        if self.__topology is None: raise Exception('Topology has not been loaded')
+
+        connectivity = self.get_connectivity(service)
+        if connectivity is None:
+            LOGGER.debug('[route_service] connectivity = None')
+            return None
+        _, a_endpoint_key, z_endpoint_key = connectivity
+
+        LOGGER.debug('[route_service] a_endpoint_key={:s}'.format(str(a_endpoint_key)))
+        LOGGER.debug('[route_service] z_endpoint_key={:s}'.format(str(z_endpoint_key)))
+
+        # TODO: consider implementing something like a K-shortest path instead of a simple shortest path
+        # pylint: disable=no-value-for-parameter,unexpected-keyword-arg
+        #paths = networkx.all_shortest_paths(self.__topology, source=a_endpoint_key, target=z_endpoint_key)
+        path_node_keys = networkx.shortest_path(
+            self.__topology, source=a_endpoint_key, target=z_endpoint_key)
+        LOGGER.debug('[route_service] Path[{:d}] = {:s}'.format(len(path_node_keys), str(path_node_keys)))
+
+        if len(path_node_keys) % 3 != 0:
+            msg = 'Weird path: length({:d}) mod 3 != 0. Path should be a sequence of endpoint-device-endpoint, '\
+                  ' so it must be multiple of 3'
+            raise Exception(msg.format(len(path_node_keys)))
+
+        hops : List[Tuple[EndPoint, Device, EndPoint]] = []
+        device_type__to__hops : Dict[str, List[int]] = {}
+        for i in range(0, len(path_node_keys), 3):
+            hop_device = get_device(self.__topology, path_node_keys[i+1])
+            hop_a_endpoint = get_endpoint(self.__topology, path_node_keys[i+0])
+            hop_z_endpoint = get_endpoint(self.__topology, path_node_keys[i+2])
+
+            hop_device_key = get_device_key(hop_device.device_id)
+            hop_a_endpoint_device_key = get_device_key(hop_a_endpoint.endpoint_id.device_id)
+            hop_z_endpoint_device_key = get_device_key(hop_z_endpoint.endpoint_id.device_id)
+
+            if hop_a_endpoint_device_key != hop_device_key:
+                msg = 'Weird path: Hop[{:d}] a_endpoint.device({:s}) != device({:s})'
+                raise Exception(msg.format(i/3, str(hop_a_endpoint_device_key), str(hop_device_key)))
+            if hop_z_endpoint_device_key != hop_device_key:
+                msg = 'Weird path: Hop[{:d}] z_endpoint.device({:s}) != device({:s})'
+                raise Exception(msg.format(i/3, str(hop_z_endpoint_device_key), str(hop_device_key)))
+
+            hops.append((hop_a_endpoint, hop_device, hop_z_endpoint))
+            device_type__to__hops.setdefault(hop_device.device_type, []).append(len(hops) - 1)
+
+        LOGGER.debug('[route_service] hops[{:d}] = {:s}'.format(
+            len(hops), str(dump_hops(hops))))
+        LOGGER.debug('[route_service] device_type__to__hops = {:s}'.format(str(device_type__to__hops)))
+
+        context_uuid = service.service_id.context_id.context_uuid.uuid
+        service_uuid = service.service_id.service_uuid.uuid
+
+        # create main service's connection
+        main_service_device_type = hops[0][1].device_type
+        main_service_hop_indexes = device_type__to__hops.pop(main_service_device_type)
+
+        # create sub-service and sub-services' connections
+        sub_service_ids = []
+        requirements : List[Tuple[Service, List[Connection]]] = []
+        for sub_service_device_type, sub_service_hop_indexes in device_type__to__hops.items():
+            LOGGER.debug('[route_service] sub_service_device_type = {:s}'.format(str(sub_service_device_type)))
+            LOGGER.debug('[route_service] sub_service_hop_indexes = {:s}'.format(str(sub_service_hop_indexes)))
+
+            # create sub-service
+            sub_service_uuid = '{:s}:optical'.format(service_uuid)
+            sub_service_id = {
+                'context_id': {'context_uuid': {'uuid': context_uuid}},
+                'service_uuid': {'uuid': sub_service_uuid},
+            }
+            sub_service = Service(**{
+                'service_id': sub_service_id,
+                'service_type' : SUB_SERVICE_TYPES.get(sub_service_device_type, DEFAULT_SUB_SERVICE_TYPE),
+                'service_status': {'service_status': ServiceStatusEnum.SERVICESTATUS_PLANNED},
+                'service_endpoint_ids': [
+                    hops[sub_service_hop_indexes[ 0]][ 0].endpoint_id,
+                    hops[sub_service_hop_indexes[-1]][-1].endpoint_id,
+                ],
+                'service_config': {'config_rules': [
+                    {
+                        'action': ConfigActionEnum.CONFIGACTION_SET,
+                        'resource_key': 'settings',
+                        'resource_value': json.dumps({
+                            'capacity_value':   1,
+                            'capacity_unit':    'GHz',
+                            'layer_proto_name': 'PHOTONIC_MEDIA',
+                            'layer_proto_qual': 'tapi-photonic-media:PHOTONIC_LAYER_QUALIFIER_NMC',
+                            'direction':        'UNIDIRECTIONAL',
+                        }, sort_keys=True)
+                    }
+                ]},
+            })
+            LOGGER.debug('[route_service] sub_service = {:s}'.format(grpc_message_to_json_string(sub_service)))
+
+            # create sub-service's connection
+            sub_connection_uuid = '{:s}:{:s}'.format(sub_service_uuid, sub_service_device_type)
+            sub_conn_path_hops = []
+            for i in sub_service_hop_indexes:
+                sub_conn_path_hops.extend([hops[i][0].endpoint_id, hops[i][2].endpoint_id])
+            sub_connection = Connection(**{
+                'connection_id': {'connection_uuid': {'uuid': sub_connection_uuid}},
+                'service_id': sub_service_id,
+                'path_hops_endpoint_ids': sub_conn_path_hops,
+            })
+
+            LOGGER.debug('[route_service] sub_connection = {:s}'.format(grpc_message_to_json_string(sub_connection)))
+
+            sub_service_ids.append(sub_service_id)
+            requirements.append((sub_service, [sub_connection]))
+
+        LOGGER.debug('[route_service] sub_service_ids = {:s}'.format(str(sub_service_ids)))
+        LOGGER.debug('[route_service] requirements = {:s}'.format(str(dump_requirements(requirements))))
+        LOGGER.debug('[route_service] requirements[{:d}] = {:s}'.format(
+            len(requirements), str(dump_requirements(requirements))))
+
+        connections : List[Connection] = []
+
+        connection_uuid = '{:s}:{:s}'.format(service_uuid, main_service_device_type)
+        connection_path_hops : List[EndPointId] = []
+        for i in main_service_hop_indexes:
+            connection_path_hops.extend([hops[i][0].endpoint_id, hops[i][2].endpoint_id])
+        connection = Connection(**{
+            'connection_id': {'connection_uuid': {'uuid': connection_uuid}},
+            'service_id': grpc_message_to_json(service.service_id),
+            'path_hops_endpoint_ids': connection_path_hops,
+            'sub_service_ids': sub_service_ids,
+        })
+        LOGGER.debug('[route_service] connection = {:s}'.format(grpc_message_to_json_string(connection)))
+        connections.append(connection)
+            
+        LOGGER.debug('[route_service] connections[{:d}] = {:s}'.format(
+            len(connections), grpc_message_list_to_json_string(connections)))
+
+        connectivity = {
+            'service': service,
+            'connections': connections,
+            'requirements': requirements,
+        }
+
+        LOGGER.debug('[route_service] connectivity = {:s}'.format(str(dump_connectivity(connectivity))))
+
+        self.__connectivity[(a_endpoint_key, z_endpoint_key)] = connectivity
+        self.__connectivity[(z_endpoint_key, a_endpoint_key)] = connectivity
+
+        context_service_id = (context_uuid, service_uuid)
+        self.__service_endpoints[context_service_id] = (a_endpoint_key, z_endpoint_key)
+
+        return connectivity
diff --git a/src/service/service/path_computation_element/Tools.py b/src/service/service/path_computation_element/Tools.py
new file mode 100644
index 0000000000000000000000000000000000000000..a3c7285e2c15d5c2efecabaaa1cfc5ed2842c451
--- /dev/null
+++ b/src/service/service/path_computation_element/Tools.py
@@ -0,0 +1,43 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import networkx
+from typing import List, Optional
+from context.proto.context_pb2 import Device, DeviceId, EndPoint, EndPointId, LinkId
+from .Enums import EdgeTypeEnum
+
+def get_device_key(device_id : DeviceId) -> str:
+    return device_id.device_uuid.uuid # pylint: disable=no-member
+
+def get_endpoint_key(endpoint_id : EndPointId, device_uuid : Optional[str] = None) -> str:
+    if device_uuid is None: device_uuid = endpoint_id.device_id.device_uuid.uuid # pylint: disable=no-member
+    endpoint_uuid = endpoint_id.endpoint_uuid.uuid # pylint: disable=no-member
+    return '{:s}/{:s}'.format(device_uuid, endpoint_uuid)
+
+def get_link_key(link_id : LinkId) -> str:
+    return link_id.link_uuid.uuid # pylint: disable=no-member
+
+def get_device(topology : networkx.Graph, device_key : str) -> Device:
+    return topology.nodes[device_key]['device']
+
+def get_endpoint(topology : networkx.Graph, endpoint_key : str) -> EndPoint:
+    return topology.nodes[endpoint_key]['endpoint']
+
+def get_edge_type(topology : networkx.Graph, endpoint_keys : List[str]) -> EdgeTypeEnum:
+    # pylint: disable=no-member,protected-access
+    endpoint_types = {get_endpoint(topology, endpoint_key).endpoint_type for endpoint_key in endpoint_keys}
+    edge_type = None if len(endpoint_types) > 1 else \
+        EdgeTypeEnum._value2member_map_.get(endpoint_types.pop())
+    if edge_type is None: edge_type = EdgeTypeEnum.OTHER
+    return edge_type
diff --git a/src/service/service/path_computation_element/TopologyViews.py b/src/service/service/path_computation_element/TopologyViews.py
new file mode 100644
index 0000000000000000000000000000000000000000..6947f89e0296101342a8a47c9b82399829ceb5a4
--- /dev/null
+++ b/src/service/service/path_computation_element/TopologyViews.py
@@ -0,0 +1,57 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import functools, networkx
+from typing import List, Optional, Union
+from context.proto.context_pb2 import EndPoint
+from context.service.database.EndPointModel import EndPointModel
+from .Enums import EdgeTypeEnum, LayerTypeEnum
+#from .Tools import get_endpoint
+
+def select_copper_edges(topology, n1, n2):
+    selected_edges = {EdgeTypeEnum.COPPER, EdgeTypeEnum.INTERNAL, EdgeTypeEnum.OTHER}
+    return topology[n1][n2].get('edge_type', EdgeTypeEnum.OTHER) in selected_edges
+
+def select_optical_edges(topology, n1, n2):
+    selected_edges = {EdgeTypeEnum.OPTICAL, EdgeTypeEnum.INTERNAL, EdgeTypeEnum.OTHER}
+    return topology[n1][n2].get('edge_type', EdgeTypeEnum.OTHER) in selected_edges
+
+def select_microwave_edges(topology, n1, n2):
+    selected_edges = {EdgeTypeEnum.MICROWAVE, EdgeTypeEnum.INTERNAL, EdgeTypeEnum.OTHER}
+    return topology[n1][n2].get('edge_type', EdgeTypeEnum.OTHER) in selected_edges
+
+SELECT_LAYER = {
+    LayerTypeEnum.COPPER    : select_copper_edges,
+    LayerTypeEnum.OPTICAL   : select_optical_edges,
+    LayerTypeEnum.MICROWAVE : select_microwave_edges,
+}
+
+def get_layer(topology : networkx.Graph, layer : LayerTypeEnum):
+    filter_edge = functools.partial(SELECT_LAYER[layer], topology)
+    return networkx.subgraph_view(topology, filter_edge=filter_edge)
+
+def select_layer_from_endpoints(endpoints : List[Union[EndPointModel, EndPoint]]) -> Optional[LayerTypeEnum]:
+    endpoint_types = set()
+    for endpoint in endpoints: endpoint_types.add(endpoint.endpoint_type)
+    if len(endpoint_types) != 1: return None
+    # pylint: disable=no-member,protected-access
+    return LayerTypeEnum._value2member_map_.get(endpoint_types.pop())
+
+#a_endpoint = get_endpoint(self.__topology, a_endpoint_key)
+#z_endpoint = get_endpoint(self.__topology, z_endpoint_key)
+#endpoints = [a_endpoint, z_endpoint]
+#layer_type = select_layer_from_endpoints(endpoints)
+#topology = self.__topology if layer_type is None else get_layer(self.__topology, layer_type)
+#write_dot(topology, '../data/layer-{:s}.dot'.format('all' if layer_type is None else str(layer_type.value)))
+
diff --git a/src/service/service/path_computation_element/__init__.py b/src/service/service/path_computation_element/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..70a33251242c51f49140e596b8208a19dd5245f7
--- /dev/null
+++ b/src/service/service/path_computation_element/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/service/service/service_handler_api/AnyTreeTools.py b/src/service/service/service_handler_api/AnyTreeTools.py
index 435fccf56b5166000fde2b6f4e555af29c550531..24aaa24249b41aa9a227ce73da0e263cbce15511 100644
--- a/src/service/service/service_handler_api/AnyTreeTools.py
+++ b/src/service/service/service_handler_api/AnyTreeTools.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import anytree
 from typing import Any, List, Optional, Union
 
diff --git a/src/service/service/service_handler_api/Exceptions.py b/src/service/service/service_handler_api/Exceptions.py
index d52df91786ddd8a08068be792a6e6f36bb1b3ebb..f3344c97add64dafcdd63334667ebc3ead0d6d7e 100644
--- a/src/service/service/service_handler_api/Exceptions.py
+++ b/src/service/service/service_handler_api/Exceptions.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 class UnsatisfiedFilterException(Exception):
     def __init__(self, filter_fields):
         msg = 'No ServiceHandler satisfies FilterFields({:s})'
diff --git a/src/service/service/service_handler_api/FilterFields.py b/src/service/service/service_handler_api/FilterFields.py
index fc1a3d5afee4468bca344e18a356728807367bbf..9d8f9ad28f69ef606bcf2e06e3a6a17514f016b4 100644
--- a/src/service/service/service_handler_api/FilterFields.py
+++ b/src/service/service/service_handler_api/FilterFields.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 from enum import Enum
 from service.service.database.ServiceModel import ORM_ServiceTypeEnum
 from service.service.database.DeviceModel import ORM_DeviceDriverEnum
diff --git a/src/service/service/service_handler_api/ServiceHandlerFactory.py b/src/service/service/service_handler_api/ServiceHandlerFactory.py
index 3f04b449de84b4fd566b4e10cec0b46839183dfb..8b7223a95613a8b490862bb3dad091baf3b38388 100644
--- a/src/service/service/service_handler_api/ServiceHandlerFactory.py
+++ b/src/service/service/service_handler_api/ServiceHandlerFactory.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import logging, operator
 from enum import Enum
 from typing import Any, Dict, Iterable, List, Set, Tuple
diff --git a/src/service/service/service_handler_api/Tools.py b/src/service/service/service_handler_api/Tools.py
index af900690ed5bffec77434bb73c320d1df5a02ed8..61ad7976132d2175319fffdeb5199f459c16d14f 100644
--- a/src/service/service/service_handler_api/Tools.py
+++ b/src/service/service/service_handler_api/Tools.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import functools
 from typing import Any, List, Union
 
diff --git a/src/service/service/service_handler_api/_ServiceHandler.py b/src/service/service/service_handler_api/_ServiceHandler.py
index 55de4ce7a470c0b5bf87e3883f42d60f5c2d3ece..e724ebcc986a1c5d205c2b77d9cb944d6faeb359 100644
--- a/src/service/service/service_handler_api/_ServiceHandler.py
+++ b/src/service/service/service_handler_api/_ServiceHandler.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 from typing import Any, List, Optional, Tuple, Union
 from common.orm.Database import Database
 from context.client.ContextClient import ContextClient
diff --git a/src/service/service/service_handler_api/__init__.py b/src/service/service/service_handler_api/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/service/service/service_handler_api/__init__.py
+++ b/src/service/service/service_handler_api/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/service/service/service_handlers/Tools.py b/src/service/service/service_handlers/Tools.py
index 1bd4b8f1f6643e621feff84aceeb401dac120348..5ca913645ceaae6eaf79788f9427afaef27d1dc6 100644
--- a/src/service/service/service_handlers/Tools.py
+++ b/src/service/service/service_handlers/Tools.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import json
 from typing import Any, Dict, Union
 from service.proto.context_pb2 import ConfigActionEnum
diff --git a/src/service/service/service_handlers/__init__.py b/src/service/service/service_handlers/__init__.py
index 73777c52ec1459adc398b9e2a6e1ddb53339a911..6abe4048fb6771efc0a44f11aa40fc7739a87648 100644
--- a/src/service/service/service_handlers/__init__.py
+++ b/src/service/service/service_handlers/__init__.py
@@ -1,6 +1,21 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 from ..service_handler_api.FilterFields import FilterFieldEnum, ORM_DeviceDriverEnum, ORM_ServiceTypeEnum
 from .l3nm_emulated.L3NMEmulatedServiceHandler import L3NMEmulatedServiceHandler
 from .l3nm_openconfig.L3NMOpenConfigServiceHandler import L3NMOpenConfigServiceHandler
+from .tapi_tapi.TapiServiceHandler import TapiServiceHandler
 
 SERVICE_HANDLERS = [
     (L3NMEmulatedServiceHandler, [
@@ -15,4 +30,10 @@ SERVICE_HANDLERS = [
             FilterFieldEnum.DEVICE_DRIVER : ORM_DeviceDriverEnum.OPENCONFIG,
         }
     ]),
+    (TapiServiceHandler, [
+        {
+            FilterFieldEnum.SERVICE_TYPE  : ORM_ServiceTypeEnum.TAPI_CONNECTIVITY_SERVICE,
+            FilterFieldEnum.DEVICE_DRIVER : ORM_DeviceDriverEnum.TRANSPORT_API,
+        }
+    ]),
 ]
diff --git a/src/service/service/service_handlers/l3nm_emulated/L3NMEmulatedServiceHandler.py b/src/service/service/service_handlers/l3nm_emulated/L3NMEmulatedServiceHandler.py
index 4b5636c6b1284189c65f1c8589e0966ac6d57860..5c471a3982d59b0f5d9c5650705c9ef68e1c0b57 100644
--- a/src/service/service/service_handlers/l3nm_emulated/L3NMEmulatedServiceHandler.py
+++ b/src/service/service/service_handlers/l3nm_emulated/L3NMEmulatedServiceHandler.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import anytree, json, logging
 from typing import Any, Dict, List, Optional, Tuple, Union
 from common.orm.Database import Database
@@ -47,16 +61,18 @@ class L3NMEmulatedServiceHandler(_ServiceHandler):
         if len(endpoints) == 0: return []
 
         service_uuid              = self.__db_service.service_uuid
-        network_instance_name     = '{:s}-NetInst'.format(service_uuid)
-        network_interface_name    = '{:s}-NetIf'.format(service_uuid)
-        network_subinterface_name = '{:s}-NetSubIf'.format(service_uuid)
+        service_short_uuid        = service_uuid.split('-')[-1]
+        network_instance_name     = '{:s}-NetInst'.format(service_short_uuid)
+        network_interface_desc    = '{:s}-NetIf'.format(service_uuid)
+        network_subinterface_desc = '{:s}-NetSubIf'.format(service_uuid)
 
-        settings : TreeNode = get_subnode(self.__resolver, self.__config, 'settings', None)
+        settings : TreeNode = get_subnode(self.__resolver, self.__config, '/settings', None)
         if settings is None: raise Exception('Unable to retrieve service settings')
         json_settings : Dict = settings.value
-        route_distinguisher = json_settings.get('route_distinguisher', '0:0')    # '60001:801'
         mtu                 = json_settings.get('mtu',                 1450 )    # 1512
-        address_families    = json_settings.get('address_families',    []   )    # ['IPV4']
+        #address_families    = json_settings.get('address_families',    []   )    # ['IPV4']
+        bgp_as              = json_settings.get('bgp_as',              0    )    # 65000
+        bgp_route_target    = json_settings.get('bgp_route_target',    '0:0')    # 65000:333
 
         results = []
         for endpoint in endpoints:
@@ -68,14 +84,19 @@ class L3NMEmulatedServiceHandler(_ServiceHandler):
                 else:
                     device_uuid, endpoint_uuid, _ = endpoint # ignore topology_uuid by now
 
-                endpoint_settings_uri = 'device[{:s}]/endpoint[{:s}]/settings'.format(device_uuid, endpoint_uuid)
+                endpoint_settings_uri = '/device[{:s}]/endpoint[{:s}]/settings'.format(device_uuid, endpoint_uuid)
                 endpoint_settings : TreeNode = get_subnode(self.__resolver, self.__config, endpoint_settings_uri, None)
                 if endpoint_settings is None:
                     raise Exception('Unable to retrieve service settings for endpoint({:s})'.format(
                         str(endpoint_settings_uri)))
                 json_endpoint_settings : Dict = endpoint_settings.value
-                router_id           = json_endpoint_settings.get('router_id',           '0.0.0.0')  # '10.95.0.10'
+                #router_id           = json_endpoint_settings.get('router_id',           '0.0.0.0')  # '10.95.0.10'
+                route_distinguisher = json_endpoint_settings.get('route_distinguisher', '0:0'    )  # '60001:801'
                 sub_interface_index = json_endpoint_settings.get('sub_interface_index', 0        )  # 1
+                vlan_id             = json_endpoint_settings.get('vlan_id',             1        )  # 400
+                address_ip          = json_endpoint_settings.get('address_ip',          '0.0.0.0')  # '2.2.2.1'
+                address_prefix      = json_endpoint_settings.get('address_prefix',      24       )  # 30
+                if_subif_name       = '{:s}.{:d}'.format(endpoint_uuid, vlan_id)
 
                 db_device : DeviceModel = get_object(self.__database, DeviceModel, device_uuid, raise_if_not_found=True)
                 json_device = db_device.dump(include_config_rules=False, include_drivers=True, include_endpoints=True)
@@ -84,21 +105,93 @@ class L3NMEmulatedServiceHandler(_ServiceHandler):
                 json_device_config_rules.extend([
                     config_rule_set(
                         '/network_instance[{:s}]'.format(network_instance_name), {
-                            'name': network_instance_name, 'type': 'L3VRF', 'router_id': router_id,
-                            'route_distinguisher': route_distinguisher, 'address_families': address_families,
+                            'name': network_instance_name, 'description': network_interface_desc, 'type': 'L3VRF',
+                            'route_distinguisher': route_distinguisher,
+                            #'router_id': router_id, 'address_families': address_families,
                     }),
                     config_rule_set(
                         '/interface[{:s}]'.format(endpoint_uuid), {
-                            'name': endpoint_uuid, 'description': network_interface_name, 'mtu': mtu,
+                            'name': endpoint_uuid, 'description': network_interface_desc, 'mtu': mtu,
                     }),
                     config_rule_set(
                         '/interface[{:s}]/subinterface[{:d}]'.format(endpoint_uuid, sub_interface_index), {
                             'name': endpoint_uuid, 'index': sub_interface_index,
-                            'description': network_subinterface_name, 'mtu': mtu,
+                            'description': network_subinterface_desc, 'vlan_id': vlan_id,
+                            'address_ip': address_ip, 'address_prefix': address_prefix,
+                    }),
+                    config_rule_set(
+                        '/network_instance[{:s}]/interface[{:s}]'.format(network_instance_name, if_subif_name), {
+                            'name': network_instance_name, 'id': if_subif_name, 'interface': endpoint_uuid,
+                            'subinterface': sub_interface_index,
+                    }),
+                    config_rule_set(
+                        '/network_instance[{:s}]/protocols[BGP]'.format(network_instance_name), {
+                            'name': network_instance_name, 'identifier': 'BGP', 'protocol_name': 'BGP', 'as': bgp_as,
+                    }),
+                    config_rule_set(
+                        '/network_instance[{:s}]/table_connections[STATIC][BGP][IPV4]'.format(network_instance_name), {
+                            'name': network_instance_name, 'src_protocol': 'STATIC', 'dst_protocol': 'BGP',
+                            'address_family': 'IPV4', #'default_import_policy': 'REJECT_ROUTE',
+                    }),
+                    config_rule_set(
+                        '/network_instance[{:s}]/table_connections[DIRECTLY_CONNECTED][BGP][IPV4]'.format(
+                            network_instance_name), {
+                            'name': network_instance_name, 'src_protocol': 'DIRECTLY_CONNECTED', 'dst_protocol': 'BGP',
+                            'address_family': 'IPV4', #'default_import_policy': 'REJECT_ROUTE',
+                    }),
+                    config_rule_set(
+                        '/routing_policy/bgp_defined_set[{:s}_rt_import]'.format(network_instance_name), {
+                            'ext_community_set_name': '{:s}_rt_import'.format(network_instance_name),
+                    }),
+                    config_rule_set(
+                        '/routing_policy/bgp_defined_set[{:s}_rt_import][route-target:{:s}]'.format(
+                            network_instance_name, bgp_route_target), {
+                            'ext_community_set_name': '{:s}_rt_import'.format(network_instance_name),
+                            'ext_community_member'  : 'route-target:{:s}'.format(bgp_route_target),
+                    }),
+                    config_rule_set(
+                        '/routing_policy/policy_definition[{:s}_import]'.format(network_instance_name), {
+                            'policy_name': '{:s}_import'.format(network_instance_name),
                     }),
                     config_rule_set(
-                        '/network_instance[{:s}]/interface[{:s}]'.format(network_instance_name, endpoint_uuid), {
-                            'name': network_instance_name, 'id': endpoint_uuid,
+                        '/routing_policy/policy_definition[{:s}_import]/statement[{:s}]'.format(
+                            network_instance_name, '3'), {
+                            'policy_name': '{:s}_import'.format(network_instance_name), 'statement_name': '3',
+                            'ext_community_set_name': '{:s}_rt_import'.format(network_instance_name),
+                            'match_set_options': 'ANY', 'policy_result': 'ACCEPT_ROUTE',
+                    }),
+                    config_rule_set(
+                        # pylint: disable=duplicate-string-formatting-argument
+                        '/network_instance[{:s}]/inter_instance_policies[{:s}_import]'.format(
+                            network_instance_name, network_instance_name), {
+                            'name': network_instance_name, 'import_policy': '{:s}_import'.format(network_instance_name),
+                    }),
+                    config_rule_set(
+                        '/routing_policy/bgp_defined_set[{:s}_rt_export]'.format(network_instance_name), {
+                            'ext_community_set_name': '{:s}_rt_export'.format(network_instance_name),
+                    }),
+                    config_rule_set(
+                        '/routing_policy/bgp_defined_set[{:s}_rt_export][route-target:{:s}]'.format(
+                            network_instance_name, bgp_route_target), {
+                            'ext_community_set_name': '{:s}_rt_export'.format(network_instance_name),
+                            'ext_community_member'  : 'route-target:{:s}'.format(bgp_route_target),
+                    }),
+                    config_rule_set(
+                        '/routing_policy/policy_definition[{:s}_export]'.format(network_instance_name), {
+                            'policy_name': '{:s}_export'.format(network_instance_name),
+                    }),
+                    config_rule_set(
+                        '/routing_policy/policy_definition[{:s}_export]/statement[{:s}]'.format(
+                            network_instance_name, '3'), {
+                            'policy_name': '{:s}_export'.format(network_instance_name), 'statement_name': '3',
+                            'ext_community_set_name': '{:s}_rt_export'.format(network_instance_name),
+                            'match_set_options': 'ANY', 'policy_result': 'ACCEPT_ROUTE',
+                    }),
+                    config_rule_set(
+                        # pylint: disable=duplicate-string-formatting-argument
+                        '/network_instance[{:s}]/inter_instance_policies[{:s}_export]'.format(
+                            network_instance_name, network_instance_name), {
+                            'name': network_instance_name, 'export_policy': '{:s}_export'.format(network_instance_name),
                     }),
                 ])
                 self.__device_client.ConfigureDevice(Device(**json_device))
@@ -114,7 +207,13 @@ class L3NMEmulatedServiceHandler(_ServiceHandler):
         if len(endpoints) == 0: return []
 
         service_uuid              = self.__db_service.service_uuid
-        network_instance_name     = '{:s}-NetInst'.format(service_uuid)
+        service_short_uuid        = service_uuid.split('-')[-1]
+        network_instance_name     = '{:s}-NetInst'.format(service_short_uuid)
+
+        settings : TreeNode = get_subnode(self.__resolver, self.__config, '/settings', None)
+        if settings is None: raise Exception('Unable to retrieve service settings')
+        json_settings : Dict = settings.value
+        bgp_route_target    = json_settings.get('bgp_route_target',    '0:0')    # 65000:333
 
         results = []
         for endpoint in endpoints:
@@ -126,13 +225,15 @@ class L3NMEmulatedServiceHandler(_ServiceHandler):
                 else:
                     device_uuid, endpoint_uuid, _ = endpoint # ignore topology_uuid by now
 
-                endpoint_settings_uri = 'device[{:s}]/endpoint[{:s}]/settings'.format(device_uuid, endpoint_uuid)
+                endpoint_settings_uri = '/device[{:s}]/endpoint[{:s}]/settings'.format(device_uuid, endpoint_uuid)
                 endpoint_settings : TreeNode = get_subnode(self.__resolver, self.__config, endpoint_settings_uri, None)
                 if endpoint_settings is None:
                     raise Exception('Unable to retrieve service settings for endpoint({:s})'.format(
                         str(endpoint_settings_uri)))
                 json_endpoint_settings : Dict = endpoint_settings.value
                 sub_interface_index = json_endpoint_settings.get('sub_interface_index', 0        )  # 1
+                vlan_id             = json_endpoint_settings.get('vlan_id',             1        )  # 400
+                if_subif_name       = '{:s}.{:d}'.format(endpoint_uuid, vlan_id)
 
                 db_device : DeviceModel = get_object(self.__database, DeviceModel, device_uuid, raise_if_not_found=True)
                 json_device = db_device.dump(include_config_rules=False, include_drivers=True, include_endpoints=True)
@@ -140,13 +241,82 @@ class L3NMEmulatedServiceHandler(_ServiceHandler):
                 json_device_config_rules : List = json_device_config.setdefault('config_rules', [])
                 json_device_config_rules.extend([
                     config_rule_delete(
-                        '/network_instance[{:s}]/interface[{:s}]'.format(network_instance_name, endpoint_uuid), {
-                            'name': network_instance_name, 'id': endpoint_uuid
+                        '/network_instance[{:s}]/interface[{:s}]'.format(network_instance_name, if_subif_name), {
+                            'name': network_instance_name, 'id': if_subif_name,
                     }),
                     config_rule_delete(
                         '/interface[{:s}]/subinterface[{:d}]'.format(endpoint_uuid, sub_interface_index), {
                             'name': endpoint_uuid, 'index': sub_interface_index,
                     }),
+                    config_rule_delete(
+                        '/interface[{:s}]'.format(endpoint_uuid), {
+                            'name': endpoint_uuid,
+                    }),
+                    config_rule_delete(
+                        '/network_instance[{:s}]/table_connections[DIRECTLY_CONNECTED][BGP][IPV4]'.format(
+                            network_instance_name), {
+                            'name': network_instance_name, 'src_protocol': 'DIRECTLY_CONNECTED', 'dst_protocol': 'BGP',
+                            'address_family': 'IPV4',
+                    }),
+                    config_rule_delete(
+                        '/network_instance[{:s}]/table_connections[STATIC][BGP][IPV4]'.format(network_instance_name), {
+                            'name': network_instance_name, 'src_protocol': 'STATIC', 'dst_protocol': 'BGP',
+                            'address_family': 'IPV4',
+                    }),
+                    config_rule_delete(
+                        '/network_instance[{:s}]/protocols[BGP]'.format(network_instance_name), {
+                            'name': network_instance_name, 'identifier': 'BGP', 'protocol_name': 'BGP',
+                    }),
+                    config_rule_delete(
+                        # pylint: disable=duplicate-string-formatting-argument
+                        '/network_instance[{:s}]/inter_instance_policies[{:s}_import]'.format(
+                            network_instance_name, network_instance_name), {
+                        'name': network_instance_name,
+                    }),
+                    config_rule_delete(
+                        '/routing_policy/policy_definition[{:s}_import]/statement[{:s}]'.format(
+                            network_instance_name, '3'), {
+                            'policy_name': '{:s}_import'.format(network_instance_name), 'statement_name': '3',
+                    }),
+                    config_rule_delete(
+                        '/routing_policy/policy_definition[{:s}_import]'.format(network_instance_name), {
+                            'policy_name': '{:s}_import'.format(network_instance_name),
+                    }),
+                    config_rule_delete(
+                        '/routing_policy/bgp_defined_set[{:s}_rt_import][route-target:{:s}]'.format(
+                            network_instance_name, bgp_route_target), {
+                            'ext_community_set_name': '{:s}_rt_import'.format(network_instance_name),
+                            'ext_community_member'  : 'route-target:{:s}'.format(bgp_route_target),
+                    }),
+                    config_rule_delete(
+                        '/routing_policy/bgp_defined_set[{:s}_rt_import]'.format(network_instance_name), {
+                            'ext_community_set_name': '{:s}_rt_import'.format(network_instance_name),
+                    }),
+                    config_rule_delete(
+                        # pylint: disable=duplicate-string-formatting-argument
+                        '/network_instance[{:s}]/inter_instance_policies[{:s}_export]'.format(
+                            network_instance_name, network_instance_name), {
+                            'name': network_instance_name,
+                    }),
+                    config_rule_delete(
+                        '/routing_policy/policy_definition[{:s}_export]/statement[{:s}]'.format(
+                            network_instance_name, '3'), {
+                            'policy_name': '{:s}_export'.format(network_instance_name), 'statement_name': '3',
+                    }),
+                    config_rule_delete(
+                        '/routing_policy/policy_definition[{:s}_export]'.format(network_instance_name), {
+                            'policy_name': '{:s}_export'.format(network_instance_name),
+                    }),
+                    config_rule_delete(
+                        '/routing_policy/bgp_defined_set[{:s}_rt_export][route-target:{:s}]'.format(
+                            network_instance_name, bgp_route_target), {
+                            'ext_community_set_name': '{:s}_rt_export'.format(network_instance_name),
+                            'ext_community_member'  : 'route-target:{:s}'.format(bgp_route_target),
+                    }),
+                    config_rule_delete(
+                        '/routing_policy/bgp_defined_set[{:s}_rt_export]'.format(network_instance_name), {
+                            'ext_community_set_name': '{:s}_rt_export'.format(network_instance_name),
+                    }),
                     config_rule_delete(
                         '/network_instance[{:s}]'.format(network_instance_name), {
                             'name': network_instance_name
diff --git a/src/service/service/service_handlers/l3nm_emulated/__init__.py b/src/service/service/service_handlers/l3nm_emulated/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/service/service/service_handlers/l3nm_emulated/__init__.py
+++ b/src/service/service/service_handlers/l3nm_emulated/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/service/service/service_handlers/l3nm_openconfig/L3NMOpenConfigServiceHandler.py b/src/service/service/service_handlers/l3nm_openconfig/L3NMOpenConfigServiceHandler.py
index eb1914fc301cfd759cf55910634f8023eec442ce..6f0f296699fab3b359d677ecb435ca017bba87fe 100644
--- a/src/service/service/service_handlers/l3nm_openconfig/L3NMOpenConfigServiceHandler.py
+++ b/src/service/service/service_handlers/l3nm_openconfig/L3NMOpenConfigServiceHandler.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import anytree, json, logging
 from typing import Any, Dict, List, Optional, Tuple, Union
 from common.orm.Database import Database
@@ -47,16 +61,18 @@ class L3NMOpenConfigServiceHandler(_ServiceHandler):
         if len(endpoints) == 0: return []
 
         service_uuid              = self.__db_service.service_uuid
-        network_instance_name     = '{:s}-NetInst'.format(service_uuid)
-        network_interface_name    = '{:s}-NetIf'.format(service_uuid)
-        network_subinterface_name = '{:s}-NetSubIf'.format(service_uuid)
+        service_short_uuid        = service_uuid.split('-')[-1]
+        network_instance_name     = '{:s}-NetInst'.format(service_short_uuid)
+        network_interface_desc    = '{:s}-NetIf'.format(service_uuid)
+        network_subinterface_desc = '{:s}-NetSubIf'.format(service_uuid)
 
-        settings : TreeNode = get_subnode(self.__resolver, self.__config, 'settings', None)
+        settings : TreeNode = get_subnode(self.__resolver, self.__config, '/settings', None)
         if settings is None: raise Exception('Unable to retrieve service settings')
         json_settings : Dict = settings.value
-        route_distinguisher = json_settings.get('route_distinguisher', '0:0')    # '60001:801'
         mtu                 = json_settings.get('mtu',                 1450 )    # 1512
-        address_families    = json_settings.get('address_families',    []   )    # ['IPV4']
+        #address_families    = json_settings.get('address_families',    []   )    # ['IPV4']
+        bgp_as              = json_settings.get('bgp_as',              0    )    # 65000
+        bgp_route_target    = json_settings.get('bgp_route_target',    '0:0')    # 65000:333
 
         results = []
         for endpoint in endpoints:
@@ -68,14 +84,19 @@ class L3NMOpenConfigServiceHandler(_ServiceHandler):
                 else:
                     device_uuid, endpoint_uuid, _ = endpoint # ignore topology_uuid by now
 
-                endpoint_settings_uri = 'device[{:s}]/endpoint[{:s}]/settings'.format(device_uuid, endpoint_uuid)
+                endpoint_settings_uri = '/device[{:s}]/endpoint[{:s}]/settings'.format(device_uuid, endpoint_uuid)
                 endpoint_settings : TreeNode = get_subnode(self.__resolver, self.__config, endpoint_settings_uri, None)
                 if endpoint_settings is None:
                     raise Exception('Unable to retrieve service settings for endpoint({:s})'.format(
                         str(endpoint_settings_uri)))
                 json_endpoint_settings : Dict = endpoint_settings.value
-                router_id           = json_endpoint_settings.get('router_id',           '0.0.0.0')  # '10.95.0.10'
+                #router_id           = json_endpoint_settings.get('router_id',           '0.0.0.0')  # '10.95.0.10'
+                route_distinguisher = json_endpoint_settings.get('route_distinguisher', '0:0'    )  # '60001:801'
                 sub_interface_index = json_endpoint_settings.get('sub_interface_index', 0        )  # 1
+                vlan_id             = json_endpoint_settings.get('vlan_id',             1        )  # 400
+                address_ip          = json_endpoint_settings.get('address_ip',          '0.0.0.0')  # '2.2.2.1'
+                address_prefix      = json_endpoint_settings.get('address_prefix',      24       )  # 30
+                if_subif_name       = '{:s}.{:d}'.format(endpoint_uuid, vlan_id)
 
                 db_device : DeviceModel = get_object(self.__database, DeviceModel, device_uuid, raise_if_not_found=True)
                 json_device = db_device.dump(include_config_rules=False, include_drivers=True, include_endpoints=True)
@@ -84,21 +105,93 @@ class L3NMOpenConfigServiceHandler(_ServiceHandler):
                 json_device_config_rules.extend([
                     config_rule_set(
                         '/network_instance[{:s}]'.format(network_instance_name), {
-                            'name': network_instance_name, 'type': 'L3VRF', 'router_id': router_id,
-                            'route_distinguisher': route_distinguisher, 'address_families': address_families,
+                            'name': network_instance_name, 'description': network_interface_desc, 'type': 'L3VRF',
+                            'route_distinguisher': route_distinguisher,
+                            #'router_id': router_id, 'address_families': address_families,
                     }),
                     config_rule_set(
                         '/interface[{:s}]'.format(endpoint_uuid), {
-                            'name': endpoint_uuid, 'description': network_interface_name, 'mtu': mtu,
+                            'name': endpoint_uuid, 'description': network_interface_desc, 'mtu': mtu,
                     }),
                     config_rule_set(
                         '/interface[{:s}]/subinterface[{:d}]'.format(endpoint_uuid, sub_interface_index), {
                             'name': endpoint_uuid, 'index': sub_interface_index,
-                            'description': network_subinterface_name, 'mtu': mtu,
+                            'description': network_subinterface_desc, 'vlan_id': vlan_id,
+                            'address_ip': address_ip, 'address_prefix': address_prefix,
+                    }),
+                    config_rule_set(
+                        '/network_instance[{:s}]/interface[{:s}]'.format(network_instance_name, if_subif_name), {
+                            'name': network_instance_name, 'id': if_subif_name, 'interface': endpoint_uuid,
+                            'subinterface': sub_interface_index,
+                    }),
+                    config_rule_set(
+                        '/network_instance[{:s}]/protocols[BGP]'.format(network_instance_name), {
+                            'name': network_instance_name, 'identifier': 'BGP', 'protocol_name': 'BGP', 'as': bgp_as,
+                    }),
+                    config_rule_set(
+                        '/network_instance[{:s}]/table_connections[STATIC][BGP][IPV4]'.format(network_instance_name), {
+                            'name': network_instance_name, 'src_protocol': 'STATIC', 'dst_protocol': 'BGP',
+                            'address_family': 'IPV4', #'default_import_policy': 'REJECT_ROUTE',
+                    }),
+                    config_rule_set(
+                        '/network_instance[{:s}]/table_connections[DIRECTLY_CONNECTED][BGP][IPV4]'.format(
+                            network_instance_name), {
+                            'name': network_instance_name, 'src_protocol': 'DIRECTLY_CONNECTED', 'dst_protocol': 'BGP',
+                            'address_family': 'IPV4', #'default_import_policy': 'REJECT_ROUTE',
+                    }),
+                    config_rule_set(
+                        '/routing_policy/bgp_defined_set[{:s}_rt_import]'.format(network_instance_name), {
+                            'ext_community_set_name': '{:s}_rt_import'.format(network_instance_name),
+                    }),
+                    config_rule_set(
+                        '/routing_policy/bgp_defined_set[{:s}_rt_import][route-target:{:s}]'.format(
+                            network_instance_name, bgp_route_target), {
+                            'ext_community_set_name': '{:s}_rt_import'.format(network_instance_name),
+                            'ext_community_member'  : 'route-target:{:s}'.format(bgp_route_target),
+                    }),
+                    config_rule_set(
+                        '/routing_policy/policy_definition[{:s}_import]'.format(network_instance_name), {
+                            'policy_name': '{:s}_import'.format(network_instance_name),
                     }),
                     config_rule_set(
-                        '/network_instance[{:s}]/interface[{:s}]'.format(network_instance_name, endpoint_uuid), {
-                            'name': network_instance_name, 'id': endpoint_uuid,
+                        '/routing_policy/policy_definition[{:s}_import]/statement[{:s}]'.format(
+                            network_instance_name, '3'), {
+                            'policy_name': '{:s}_import'.format(network_instance_name), 'statement_name': '3',
+                            'ext_community_set_name': '{:s}_rt_import'.format(network_instance_name),
+                            'match_set_options': 'ANY', 'policy_result': 'ACCEPT_ROUTE',
+                    }),
+                    config_rule_set(
+                        # pylint: disable=duplicate-string-formatting-argument
+                        '/network_instance[{:s}]/inter_instance_policies[{:s}_import]'.format(
+                            network_instance_name, network_instance_name), {
+                            'name': network_instance_name, 'import_policy': '{:s}_import'.format(network_instance_name),
+                    }),
+                    config_rule_set(
+                        '/routing_policy/bgp_defined_set[{:s}_rt_export]'.format(network_instance_name), {
+                            'ext_community_set_name': '{:s}_rt_export'.format(network_instance_name),
+                    }),
+                    config_rule_set(
+                        '/routing_policy/bgp_defined_set[{:s}_rt_export][route-target:{:s}]'.format(
+                            network_instance_name, bgp_route_target), {
+                            'ext_community_set_name': '{:s}_rt_export'.format(network_instance_name),
+                            'ext_community_member'  : 'route-target:{:s}'.format(bgp_route_target),
+                    }),
+                    config_rule_set(
+                        '/routing_policy/policy_definition[{:s}_export]'.format(network_instance_name), {
+                            'policy_name': '{:s}_export'.format(network_instance_name),
+                    }),
+                    config_rule_set(
+                        '/routing_policy/policy_definition[{:s}_export]/statement[{:s}]'.format(
+                            network_instance_name, '3'), {
+                            'policy_name': '{:s}_export'.format(network_instance_name), 'statement_name': '3',
+                            'ext_community_set_name': '{:s}_rt_export'.format(network_instance_name),
+                            'match_set_options': 'ANY', 'policy_result': 'ACCEPT_ROUTE',
+                    }),
+                    config_rule_set(
+                        # pylint: disable=duplicate-string-formatting-argument
+                        '/network_instance[{:s}]/inter_instance_policies[{:s}_export]'.format(
+                            network_instance_name, network_instance_name), {
+                            'name': network_instance_name, 'export_policy': '{:s}_export'.format(network_instance_name),
                     }),
                 ])
                 self.__device_client.ConfigureDevice(Device(**json_device))
@@ -114,7 +207,13 @@ class L3NMOpenConfigServiceHandler(_ServiceHandler):
         if len(endpoints) == 0: return []
 
         service_uuid              = self.__db_service.service_uuid
-        network_instance_name     = '{:s}-NetInst'.format(service_uuid)
+        service_short_uuid        = service_uuid.split('-')[-1]
+        network_instance_name     = '{:s}-NetInst'.format(service_short_uuid)
+
+        settings : TreeNode = get_subnode(self.__resolver, self.__config, '/settings', None)
+        if settings is None: raise Exception('Unable to retrieve service settings')
+        json_settings : Dict = settings.value
+        bgp_route_target    = json_settings.get('bgp_route_target',    '0:0')    # 65000:333
 
         results = []
         for endpoint in endpoints:
@@ -126,13 +225,15 @@ class L3NMOpenConfigServiceHandler(_ServiceHandler):
                 else:
                     device_uuid, endpoint_uuid, _ = endpoint # ignore topology_uuid by now
 
-                endpoint_settings_uri = 'device[{:s}]/endpoint[{:s}]/settings'.format(device_uuid, endpoint_uuid)
+                endpoint_settings_uri = '/device[{:s}]/endpoint[{:s}]/settings'.format(device_uuid, endpoint_uuid)
                 endpoint_settings : TreeNode = get_subnode(self.__resolver, self.__config, endpoint_settings_uri, None)
                 if endpoint_settings is None:
                     raise Exception('Unable to retrieve service settings for endpoint({:s})'.format(
                         str(endpoint_settings_uri)))
                 json_endpoint_settings : Dict = endpoint_settings.value
                 sub_interface_index = json_endpoint_settings.get('sub_interface_index', 0        )  # 1
+                vlan_id             = json_endpoint_settings.get('vlan_id',             1        )  # 400
+                if_subif_name       = '{:s}.{:d}'.format(endpoint_uuid, vlan_id)
 
                 db_device : DeviceModel = get_object(self.__database, DeviceModel, device_uuid, raise_if_not_found=True)
                 json_device = db_device.dump(include_config_rules=False, include_drivers=True, include_endpoints=True)
@@ -140,13 +241,82 @@ class L3NMOpenConfigServiceHandler(_ServiceHandler):
                 json_device_config_rules : List = json_device_config.setdefault('config_rules', [])
                 json_device_config_rules.extend([
                     config_rule_delete(
-                        '/network_instance[{:s}]/interface[{:s}]'.format(network_instance_name, endpoint_uuid), {
-                            'name': network_instance_name, 'id': endpoint_uuid
+                        '/network_instance[{:s}]/interface[{:s}]'.format(network_instance_name, if_subif_name), {
+                            'name': network_instance_name, 'id': if_subif_name,
                     }),
                     config_rule_delete(
                         '/interface[{:s}]/subinterface[{:d}]'.format(endpoint_uuid, sub_interface_index), {
                             'name': endpoint_uuid, 'index': sub_interface_index,
                     }),
+                    config_rule_delete(
+                        '/interface[{:s}]'.format(endpoint_uuid), {
+                            'name': endpoint_uuid,
+                    }),
+                    config_rule_delete(
+                        '/network_instance[{:s}]/table_connections[DIRECTLY_CONNECTED][BGP][IPV4]'.format(
+                            network_instance_name), {
+                            'name': network_instance_name, 'src_protocol': 'DIRECTLY_CONNECTED', 'dst_protocol': 'BGP',
+                            'address_family': 'IPV4',
+                    }),
+                    config_rule_delete(
+                        '/network_instance[{:s}]/table_connections[STATIC][BGP][IPV4]'.format(network_instance_name), {
+                            'name': network_instance_name, 'src_protocol': 'STATIC', 'dst_protocol': 'BGP',
+                            'address_family': 'IPV4',
+                    }),
+                    config_rule_delete(
+                        '/network_instance[{:s}]/protocols[BGP]'.format(network_instance_name), {
+                            'name': network_instance_name, 'identifier': 'BGP', 'protocol_name': 'BGP',
+                    }),
+                    config_rule_delete(
+                        # pylint: disable=duplicate-string-formatting-argument
+                        '/network_instance[{:s}]/inter_instance_policies[{:s}_import]'.format(
+                            network_instance_name, network_instance_name), {
+                        'name': network_instance_name,
+                    }),
+                    config_rule_delete(
+                        '/routing_policy/policy_definition[{:s}_import]/statement[{:s}]'.format(
+                            network_instance_name, '3'), {
+                            'policy_name': '{:s}_import'.format(network_instance_name), 'statement_name': '3',
+                    }),
+                    config_rule_delete(
+                        '/routing_policy/policy_definition[{:s}_import]'.format(network_instance_name), {
+                            'policy_name': '{:s}_import'.format(network_instance_name),
+                    }),
+                    config_rule_delete(
+                        '/routing_policy/bgp_defined_set[{:s}_rt_import][route-target:{:s}]'.format(
+                            network_instance_name, bgp_route_target), {
+                            'ext_community_set_name': '{:s}_rt_import'.format(network_instance_name),
+                            'ext_community_member'  : 'route-target:{:s}'.format(bgp_route_target),
+                    }),
+                    config_rule_delete(
+                        '/routing_policy/bgp_defined_set[{:s}_rt_import]'.format(network_instance_name), {
+                            'ext_community_set_name': '{:s}_rt_import'.format(network_instance_name),
+                    }),
+                    config_rule_delete(
+                        # pylint: disable=duplicate-string-formatting-argument
+                        '/network_instance[{:s}]/inter_instance_policies[{:s}_export]'.format(
+                            network_instance_name, network_instance_name), {
+                            'name': network_instance_name,
+                    }),
+                    config_rule_delete(
+                        '/routing_policy/policy_definition[{:s}_export]/statement[{:s}]'.format(
+                            network_instance_name, '3'), {
+                            'policy_name': '{:s}_export'.format(network_instance_name), 'statement_name': '3',
+                    }),
+                    config_rule_delete(
+                        '/routing_policy/policy_definition[{:s}_export]'.format(network_instance_name), {
+                            'policy_name': '{:s}_export'.format(network_instance_name),
+                    }),
+                    config_rule_delete(
+                        '/routing_policy/bgp_defined_set[{:s}_rt_export][route-target:{:s}]'.format(
+                            network_instance_name, bgp_route_target), {
+                            'ext_community_set_name': '{:s}_rt_export'.format(network_instance_name),
+                            'ext_community_member'  : 'route-target:{:s}'.format(bgp_route_target),
+                    }),
+                    config_rule_delete(
+                        '/routing_policy/bgp_defined_set[{:s}_rt_export]'.format(network_instance_name), {
+                            'ext_community_set_name': '{:s}_rt_export'.format(network_instance_name),
+                    }),
                     config_rule_delete(
                         '/network_instance[{:s}]'.format(network_instance_name), {
                             'name': network_instance_name
diff --git a/src/service/service/service_handlers/l3nm_openconfig/__init__.py b/src/service/service/service_handlers/l3nm_openconfig/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/service/service/service_handlers/l3nm_openconfig/__init__.py
+++ b/src/service/service/service_handlers/l3nm_openconfig/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/service/service/service_handlers/tapi_tapi/TapiServiceHandler.py b/src/service/service/service_handlers/tapi_tapi/TapiServiceHandler.py
new file mode 100644
index 0000000000000000000000000000000000000000..8406f8f01712cbbd944f2db8c95ac4cd9d48bafe
--- /dev/null
+++ b/src/service/service/service_handlers/tapi_tapi/TapiServiceHandler.py
@@ -0,0 +1,170 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import anytree, json, logging
+from typing import Any, Dict, List, Optional, Tuple, Union
+from common.orm.Database import Database
+from common.orm.HighLevel import get_object
+from common.orm.backend.Tools import key_to_str
+from common.type_checkers.Checkers import chk_type
+from context.client.ContextClient import ContextClient
+from device.client.DeviceClient import DeviceClient
+from device.proto.context_pb2 import Device
+from service.service.database.ConfigModel import ORM_ConfigActionEnum, get_config_rules
+from service.service.database.ContextModel import ContextModel
+from service.service.database.DeviceModel import DeviceModel
+from service.service.database.ServiceModel import ServiceModel
+from service.service.service_handler_api._ServiceHandler import _ServiceHandler
+from service.service.service_handler_api.AnyTreeTools import TreeNode, delete_subnode, get_subnode, set_subnode_value
+from service.service.service_handlers.Tools import config_rule_set, config_rule_delete
+
+LOGGER = logging.getLogger(__name__)
+
+class TapiServiceHandler(_ServiceHandler):
+    def __init__(   # pylint: disable=super-init-not-called
+        self, db_service : ServiceModel, database : Database, context_client : ContextClient,
+        device_client : DeviceClient, **settings
+    ) -> None:
+        self.__db_service = db_service
+        self.__database = database
+        self.__context_client = context_client # pylint: disable=unused-private-member
+        self.__device_client = device_client
+
+        self.__db_context : ContextModel = get_object(self.__database, ContextModel, self.__db_service.context_fk)
+        str_service_key = key_to_str([self.__db_context.context_uuid, self.__db_service.service_uuid])
+        db_config = get_config_rules(self.__database, str_service_key, 'running')
+        self.__resolver = anytree.Resolver(pathattr='name')
+        self.__config = TreeNode('.')
+        for action, resource_key, resource_value in db_config:
+            if action == ORM_ConfigActionEnum.SET:
+                try:
+                    resource_value = json.loads(resource_value)
+                except: # pylint: disable=bare-except
+                    pass
+                set_subnode_value(self.__resolver, self.__config, resource_key, resource_value)
+            elif action == ORM_ConfigActionEnum.DELETE:
+                delete_subnode(self.__resolver, self.__config, resource_key)
+
+    def SetEndpoint(self, endpoints : List[Tuple[str, str, Optional[str]]]) -> List[Union[bool, Exception]]:
+        chk_type('endpoints', endpoints, list)
+        if len(endpoints) != 2: return []
+
+        service_uuid = self.__db_service.service_uuid
+        service_settings : TreeNode = get_subnode(self.__resolver, self.__config, 'settings', None)
+        if service_settings is None: raise Exception('Unable to settings for Service({:s})'.format(str(service_uuid)))
+
+        json_settings : Dict = service_settings.value
+        capacity_value   = json_settings.get('capacity_value',   1)
+        capacity_unit    = json_settings.get('capacity_unit',    'GHz')
+        layer_proto_name = json_settings.get('layer_proto_name', 'PHOTONIC_MEDIA')
+        layer_proto_qual = json_settings.get('layer_proto_qual', 'tapi-photonic-media:PHOTONIC_LAYER_QUALIFIER_NMC')
+        direction        = json_settings.get('direction',        'UNIDIRECTIONAL')
+
+        results = []
+        try:
+            device_uuid = endpoints[0][0]
+            db_device : DeviceModel = get_object(self.__database, DeviceModel, device_uuid, raise_if_not_found=True)
+            json_device = db_device.dump(include_config_rules=False, include_drivers=True, include_endpoints=True)
+            json_device_config : Dict = json_device.setdefault('device_config', {})
+            json_device_config_rules : List = json_device_config.setdefault('config_rules', [])
+            json_device_config_rules.extend([
+                config_rule_set('/service[{:s}]'.format(service_uuid), {
+                    'uuid'                    : service_uuid,
+                    'input_sip'               : endpoints[0][1],
+                    'output_sip'              : endpoints[1][1],
+                    'capacity_unit'           : capacity_unit,
+                    'capacity_value'          : capacity_value,
+                    'layer_protocol_name'     : layer_proto_name,
+                    'layer_protocol_qualifier': layer_proto_qual,
+                    'direction'               : direction,
+                }),
+            ])
+            self.__device_client.ConfigureDevice(Device(**json_device))
+            results.append(True)
+        except Exception as e: # pylint: disable=broad-except
+            LOGGER.exception('Unable to SetEndpoint for Service({:s})'.format(str(service_uuid)))
+            results.append(e)
+
+        return results
+
+    def DeleteEndpoint(self, endpoints : List[Tuple[str, str, Optional[str]]]) -> List[Union[bool, Exception]]:
+        chk_type('endpoints', endpoints, list)
+        if len(endpoints) != 2: return []
+
+        service_uuid = self.__db_service.service_uuid
+        results = []
+        try:
+            device_uuid = endpoints[0][0]
+            db_device : DeviceModel = get_object(self.__database, DeviceModel, device_uuid, raise_if_not_found=True)
+            json_device = db_device.dump(include_config_rules=False, include_drivers=True, include_endpoints=True)
+            json_device_config : Dict = json_device.setdefault('device_config', {})
+            json_device_config_rules : List = json_device_config.setdefault('config_rules', [])
+            json_device_config_rules.extend([
+                config_rule_delete('/service[{:s}]'.format(service_uuid), {'uuid': service_uuid})
+            ])
+            self.__device_client.ConfigureDevice(Device(**json_device))
+            results.append(True)
+        except Exception as e: # pylint: disable=broad-except
+            LOGGER.exception('Unable to DeleteEndpoint for Service({:s})'.format(str(service_uuid)))
+            results.append(e)
+
+        return results
+
+    def SetConstraint(self, constraints : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]:
+        chk_type('constraints', constraints, list)
+        if len(constraints) == 0: return []
+
+        msg = '[SetConstraint] Method not implemented. Constraints({:s}) are being ignored.'
+        LOGGER.warning(msg.format(str(constraints)))
+        return [True for _ in range(len(constraints))]
+
+    def DeleteConstraint(self, constraints : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]:
+        chk_type('constraints', constraints, list)
+        if len(constraints) == 0: return []
+
+        msg = '[DeleteConstraint] Method not implemented. Constraints({:s}) are being ignored.'
+        LOGGER.warning(msg.format(str(constraints)))
+        return [True for _ in range(len(constraints))]
+
+    def SetConfig(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]:
+        chk_type('resources', resources, list)
+        if len(resources) == 0: return []
+
+        results = []
+        for resource in resources:
+            try:
+                resource_key, resource_value = resource
+                resource_value = json.loads(resource_value)
+                set_subnode_value(self.__resolver, self.__config, resource_key, resource_value)
+                results.append(True)
+            except Exception as e: # pylint: disable=broad-except
+                LOGGER.exception('Unable to SetConfig({:s})'.format(str(resource)))
+                results.append(e)
+
+        return results
+
+    def DeleteConfig(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]:
+        chk_type('resources', resources, list)
+        if len(resources) == 0: return []
+
+        results = []
+        for resource in resources:
+            try:
+                resource_key, _ = resource
+                delete_subnode(self.__resolver, self.__config, resource_key)
+            except Exception as e: # pylint: disable=broad-except
+                LOGGER.exception('Unable to DeleteConfig({:s})'.format(str(resource)))
+                results.append(e)
+
+        return results
diff --git a/src/service/service/service_handlers/tapi_tapi/__init__.py b/src/service/service/service_handlers/tapi_tapi/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..70a33251242c51f49140e596b8208a19dd5245f7
--- /dev/null
+++ b/src/service/service/service_handlers/tapi_tapi/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/service/tests/.gitignore b/src/service/tests/.gitignore
index f2f83148dc3aa48945967df7297805c06296dde9..6b97d6fe3ad32f39097745229ab7f547f26ecb12 100644
--- a/src/service/tests/.gitignore
+++ b/src/service/tests/.gitignore
@@ -1,2 +1 @@
 # Add here your files containing confidential testbed details such as IP addresses, ports, usernames, passwords, etc.
-ServiceHandler_L3NM_OC.py
diff --git a/src/service/tests/CommonObjects.py b/src/service/tests/CommonObjects.py
index 2b51dcd2322e70dff9a5229e65e6a220708a834f..0ad6695dff56247eaad59700e4e908313b955857 100644
--- a/src/service/tests/CommonObjects.py
+++ b/src/service/tests/CommonObjects.py
@@ -1,21 +1,34 @@
-from copy import deepcopy
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 from common.Constants import DEFAULT_CONTEXT_UUID, DEFAULT_TOPOLOGY_UUID
+from common.tools.object_factory.Context import json_context, json_context_id
+from common.tools.object_factory.Topology import json_topology, json_topology_id
+from context.proto.kpi_sample_types_pb2 import KpiSampleType
 
-# use "deepcopy" to prevent propagating forced changes during tests
+# ----- Context --------------------------------------------------------------------------------------------------------
+CONTEXT_ID = json_context_id(DEFAULT_CONTEXT_UUID)
+CONTEXT    = json_context(DEFAULT_CONTEXT_UUID)
 
-CONTEXT_ID = {'context_uuid': {'uuid': DEFAULT_CONTEXT_UUID}}
-CONTEXT = {
-    'context_id': deepcopy(CONTEXT_ID),
-    'topology_ids': [],
-    'service_ids': [],
-}
+# ----- Topology -------------------------------------------------------------------------------------------------------
+TOPOLOGY_ID = json_topology_id(DEFAULT_TOPOLOGY_UUID, context_id=CONTEXT_ID)
+TOPOLOGY    = json_topology(DEFAULT_TOPOLOGY_UUID, context_id=CONTEXT_ID)
 
-TOPOLOGY_ID = {
-    'context_id': deepcopy(CONTEXT_ID),
-    'topology_uuid': {'uuid': DEFAULT_TOPOLOGY_UUID},
-}
-TOPOLOGY = {
-    'topology_id': deepcopy(TOPOLOGY_ID),
-    'device_ids': [],
-    'link_ids': [],
-}
+# ----- Monitoring Samples ---------------------------------------------------------------------------------------------
+PACKET_PORT_SAMPLE_TYPES = [
+    KpiSampleType.KPISAMPLETYPE_PACKETS_TRANSMITTED,
+    KpiSampleType.KPISAMPLETYPE_PACKETS_RECEIVED,
+    KpiSampleType.KPISAMPLETYPE_BYTES_TRANSMITTED,
+    KpiSampleType.KPISAMPLETYPE_BYTES_RECEIVED,
+]
diff --git a/src/service/tests/ServiceHandler_L3NM_EMU.py b/src/service/tests/ServiceHandler_L3NM_EMU.py
index cfaf49d084448b0aee88c18823a8af91040831d0..0ac5fbf24cf1937104646374f60ab9487ee1c84d 100644
--- a/src/service/tests/ServiceHandler_L3NM_EMU.py
+++ b/src/service/tests/ServiceHandler_L3NM_EMU.py
@@ -1,102 +1,136 @@
-from copy import deepcopy
-from service.proto.context_pb2 import DeviceDriverEnum, DeviceOperationalStatusEnum, ServiceStatusEnum, ServiceTypeEnum
-from service.service.service_handlers.Tools import config_rule_set, constraint, device_id, endpoint_id
-from .CommonObjects import CONTEXT_ID
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
 
-# use "deepcopy" to prevent propagating forced changes during tests
+from typing import Dict, List, Tuple
+from common.tools.object_factory.ConfigRule import json_config_rule_set
+from common.tools.object_factory.Constraint import json_constraint
+from common.tools.object_factory.Device import (
+    json_device_emulated_packet_router_disabled, json_device_emulated_tapi_disabled, json_device_id)
+from common.tools.object_factory.EndPoint import json_endpoint, json_endpoint_id
+from common.tools.object_factory.Link import json_link, json_link_id
+from common.tools.object_factory.Service import json_service_id, json_service_l3nm_planned
+from .CommonObjects import CONTEXT, CONTEXT_ID, PACKET_PORT_SAMPLE_TYPES, TOPOLOGY, TOPOLOGY_ID
 
 SERVICE_HANDLER_NAME = 'l3nm_emulated'
 
-SERVICE_UUID = 'SVC_L3NM_EMU'
-
-SERVICE_ID = {
-    'context_id': deepcopy(CONTEXT_ID),
-    'service_uuid': {'uuid': SERVICE_UUID}
-}
-
-SERVICE_DESCRIPTOR = {
-    'service_id': deepcopy(SERVICE_ID),
-    'service_type': ServiceTypeEnum.SERVICETYPE_L3NM,
-    'service_endpoint_ids' : [],
-    'service_constraints': [],
-    'service_status': {'service_status': ServiceStatusEnum.SERVICESTATUS_PLANNED},
-    'service_config': {'config_rules': []},
-}
-
-DEVICE1_UUID = 'EMULATED-ROUTER-1'
-DEVICE2_UUID = 'EMULATED-ROUTER-2'
-
-DEVICE_ATTRIBUTES = { # device_uuid => {device_settings}
-    DEVICE1_UUID: {
-        'type'    : 'emulated',
-        'drivers' : [DeviceDriverEnum.DEVICEDRIVER_UNDEFINED],
-        'endpoint': 'EP100',
-    },
-    DEVICE2_UUID: {
-        'type'    : 'emulated',
-        'drivers' : [DeviceDriverEnum.DEVICEDRIVER_UNDEFINED],
-        'endpoint': 'EP100',
-    },
-}
-
-SERVICE_DEVICE_UUIDS = [DEVICE1_UUID, DEVICE2_UUID]
-
-SERVICE_ENDPOINT_IDS = [
-    endpoint_id(device_uuid, DEVICE_ATTRIBUTES[device_uuid]['endpoint'])
-    for device_uuid in SERVICE_DEVICE_UUIDS
-]
+def json_endpoint_ids(device_id : Dict, endpoint_descriptors : List[Tuple[str, str]]):
+    return [
+        json_endpoint_id(device_id, ep_uuid)
+        for ep_uuid, _ in endpoint_descriptors
+    ]
 
-SERVICE_CONFIG_RULES = [
-    config_rule_set(
-        'settings', {
-            'route_distinguisher': '60001:801', 'mtu': 1512, 'address_families': ['IPV4']
-        }),
-    config_rule_set(
-        'device[{:s}]/endpoint[{:s}]/settings'.format(DEVICE1_UUID, DEVICE_ATTRIBUTES[DEVICE1_UUID]['endpoint']), {
-            'router_id': '10.0.0.1', 'sub_interface_index': 1,
-        }),
-    config_rule_set(
-        'device[{:s}]/endpoint[{:s}]/settings'.format(DEVICE2_UUID, DEVICE_ATTRIBUTES[DEVICE2_UUID]['endpoint']), {
-            'router_id': '10.0.0.2', 'sub_interface_index': 1,
-        }),
-]
+def json_endpoints(device_id : Dict, endpoint_descriptors : List[Tuple[str, str]]):
+    return [
+        json_endpoint(device_id, ep_uuid, ep_type, kpi_sample_types=PACKET_PORT_SAMPLE_TYPES)
+        for ep_uuid, ep_type in endpoint_descriptors
+    ]
+
+# ----- Devices --------------------------------------------------------------------------------------------------------
+DEVICE_R1_UUID          = 'R1'
+DEVICE_R1_ENDPOINT_DEFS = [('EP1', 'optical'), ('EP100', 'copper')]
+DEVICE_R1_ID            = json_device_id(DEVICE_R1_UUID)
+DEVICE_R1_ENDPOINTS     = json_endpoints(DEVICE_R1_ID, DEVICE_R1_ENDPOINT_DEFS)
+DEVICE_R1_ENDPOINT_IDS  = json_endpoint_ids(DEVICE_R1_ID, DEVICE_R1_ENDPOINT_DEFS)
+DEVICE_R1               = json_device_emulated_packet_router_disabled(DEVICE_R1_UUID, endpoints=DEVICE_R1_ENDPOINTS)
+ENDPOINT_ID_R1_EP1      = DEVICE_R1_ENDPOINT_IDS[0]
+ENDPOINT_ID_R1_EP100    = DEVICE_R1_ENDPOINT_IDS[1]
+
+DEVICE_R2_UUID          = 'R2'
+DEVICE_R2_ENDPOINT_DEFS = [('EP1', 'optical'), ('EP100', 'copper')]
+DEVICE_R2_ID            = json_device_id(DEVICE_R2_UUID)
+DEVICE_R2_ENDPOINTS     = json_endpoints(DEVICE_R2_ID, DEVICE_R2_ENDPOINT_DEFS)
+DEVICE_R2_ENDPOINT_IDS  = json_endpoint_ids(DEVICE_R2_ID, DEVICE_R2_ENDPOINT_DEFS)
+DEVICE_R2               = json_device_emulated_packet_router_disabled(DEVICE_R2_UUID, endpoints=DEVICE_R2_ENDPOINTS)
+ENDPOINT_ID_R2_EP1      = DEVICE_R2_ENDPOINT_IDS[0]
+ENDPOINT_ID_R2_EP100    = DEVICE_R2_ENDPOINT_IDS[1]
+
+DEVICE_R3_UUID          = 'R3'
+DEVICE_R3_ENDPOINT_DEFS = [('EP1', 'optical'), ('EP100', 'copper')]
+DEVICE_R3_ID            = json_device_id(DEVICE_R3_UUID)
+DEVICE_R3_ENDPOINTS     = json_endpoints(DEVICE_R3_ID, DEVICE_R3_ENDPOINT_DEFS)
+DEVICE_R3_ENDPOINT_IDS  = json_endpoint_ids(DEVICE_R3_ID, DEVICE_R3_ENDPOINT_DEFS)
+DEVICE_R3               = json_device_emulated_packet_router_disabled(DEVICE_R3_UUID, endpoints=DEVICE_R3_ENDPOINTS)
+ENDPOINT_ID_R3_EP1      = DEVICE_R3_ENDPOINT_IDS[0]
+ENDPOINT_ID_R3_EP100    = DEVICE_R3_ENDPOINT_IDS[1]
+
+DEVICE_O1_UUID          = 'O1'
+DEVICE_O1_ENDPOINT_DEFS = [('EP1', 'optical'), ('EP2', 'optical'), ('EP3', 'optical')]
+DEVICE_O1_ID            = json_device_id(DEVICE_O1_UUID)
+DEVICE_O1_ENDPOINTS     = json_endpoints(DEVICE_O1_ID, DEVICE_O1_ENDPOINT_DEFS)
+DEVICE_O1_ENDPOINT_IDS  = json_endpoint_ids(DEVICE_O1_ID, DEVICE_O1_ENDPOINT_DEFS)
+DEVICE_O1               = json_device_emulated_tapi_disabled(DEVICE_O1_UUID, endpoints=DEVICE_O1_ENDPOINTS)
+ENDPOINT_ID_O1_EP1      = DEVICE_O1_ENDPOINT_IDS[0]
+ENDPOINT_ID_O1_EP2      = DEVICE_O1_ENDPOINT_IDS[1]
+ENDPOINT_ID_O1_EP3      = DEVICE_O1_ENDPOINT_IDS[2]
 
-SERVICE_CONSTRAINTS = [
-    constraint('latency_ms', 15.2),
-    constraint('jitter_us', 1.2),
+
+# ----- Links ----------------------------------------------------------------------------------------------------------
+LINK_R1_O1_UUID = '{:s}/{:s}-{:s}/{:s}'.format(
+    DEVICE_R1_UUID, ENDPOINT_ID_R1_EP1['endpoint_uuid']['uuid'],
+    DEVICE_O1_UUID, ENDPOINT_ID_O1_EP1['endpoint_uuid']['uuid'])
+LINK_R1_O1_ID   = json_link_id(LINK_R1_O1_UUID)
+LINK_R1_O1      = json_link(LINK_R1_O1_UUID, [ENDPOINT_ID_R1_EP1, ENDPOINT_ID_O1_EP1])
+
+LINK_R2_O1_UUID = '{:s}/{:s}-{:s}/{:s}'.format(
+    DEVICE_R2_UUID, ENDPOINT_ID_R2_EP1['endpoint_uuid']['uuid'],
+    DEVICE_O1_UUID, ENDPOINT_ID_O1_EP2['endpoint_uuid']['uuid'])
+LINK_R2_O1_ID   = json_link_id(LINK_R2_O1_UUID)
+LINK_R2_O1      = json_link(LINK_R2_O1_UUID, [ENDPOINT_ID_R2_EP1, ENDPOINT_ID_O1_EP2])
+
+LINK_R3_O1_UUID = '{:s}/{:s}-{:s}/{:s}'.format(
+    DEVICE_R3_UUID, ENDPOINT_ID_R3_EP1['endpoint_uuid']['uuid'],
+    DEVICE_O1_UUID, ENDPOINT_ID_O1_EP3['endpoint_uuid']['uuid'])
+LINK_R3_O1_ID   = json_link_id(LINK_R3_O1_UUID)
+LINK_R3_O1      = json_link(LINK_R3_O1_UUID, [ENDPOINT_ID_R3_EP1, ENDPOINT_ID_O1_EP3])
+
+
+# ----- Service --------------------------------------------------------------------------------------------------------
+SERVICE_R1_R3_UUID         = 'SVC:{:s}/{:s}-{:s}/{:s}'.format(
+    DEVICE_R1_UUID, ENDPOINT_ID_R1_EP100['endpoint_uuid']['uuid'],
+    DEVICE_R3_UUID, ENDPOINT_ID_R3_EP100['endpoint_uuid']['uuid'])
+SERVICE_R1_R3_ENDPOINT_IDS = [ENDPOINT_ID_R1_EP100, ENDPOINT_ID_R3_EP100]
+SERVICE_R1_R3_CONSTRAINTS  = [
+    json_constraint('latency_ms', 15.2),
+    json_constraint('jitter_us', 1.2),
 ]
+SERVICE_R1_R3_CONFIG_RULES = [
+    json_config_rule_set(
+        '/settings',
+        {'mtu': 1512, 'address_families': ['IPV4'], 'bgp_as': 65000, 'bgp_route_target': '65000:333'}),
+    json_config_rule_set(
+        '/device[{:s}]/endpoint[{:s}]/settings'.format(DEVICE_R1_UUID, ENDPOINT_ID_R1_EP100['endpoint_uuid']['uuid']),
+        {'router_id': '10.10.10.1', 'route_distinguisher': '65000:123', 'sub_interface_index': 400, 'vlan_id': 400,
+        'address_ip': '3.3.2.1', 'address_prefix': 24}),
+    json_config_rule_set(
+        '/device[{:s}]/endpoint[{:s}]/settings'.format(DEVICE_R3_UUID, ENDPOINT_ID_R3_EP100['endpoint_uuid']['uuid']),
+        {'router_id': '20.20.20.1', 'route_distinguisher': '65000:321', 'sub_interface_index': 400, 'vlan_id': 500,
+        'address_ip': '3.3.1.1', 'address_prefix': 24}),
+]
+SERVICE_R1_R3_ID           = json_service_id(SERVICE_R1_R3_UUID, context_id=CONTEXT_ID)
+SERVICE_R1_R3_DESCRIPTOR   = json_service_l3nm_planned(SERVICE_R1_R3_UUID)
 
-def get_device_descriptor(device_uuid, enabled=True):
-    device_operational_status = (
-        DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_ENABLED \
-        if enabled else \
-        DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_DISABLED)
-    return {
-        'device_id': device_id(device_uuid),
-        'device_type': DEVICE_ATTRIBUTES[device_uuid]['type'],
-        'device_config': {'config_rules': []},
-        'device_operational_status': device_operational_status,
-        'device_drivers': DEVICE_ATTRIBUTES[device_uuid]['drivers'],
-        'device_endpoints': [],
-    }
-
-def get_connect_rules(device_uuid):
-    return [
-        config_rule_set('_connect/address', '127.0.0.1'),
-        config_rule_set('_connect/port',    '0'),
-        config_rule_set('_connect/settings', {'endpoints': [
-            {'uuid': endpoint_uuid, 'type': '10Gbps', 'sample_types': []}
-            for endpoint_uuid in ['EP1', 'EP2', 'EP3', 'EP100']
-        ]}),
-    ]
 
+# ----- Test Descriptor ------------------------------------------------------------------------------------------------
 TEST_SERVICE_HANDLER = (SERVICE_HANDLER_NAME, {
-    'service_id'            : SERVICE_ID,
-    'service_descriptor'    : SERVICE_DESCRIPTOR,
-    'service_endpoint_ids'  : SERVICE_ENDPOINT_IDS,
-    'service_config_rules'  : SERVICE_CONFIG_RULES,
-    'service_constraints'   : SERVICE_CONSTRAINTS,
-    'service_device_uuids'  : SERVICE_DEVICE_UUIDS,
-    'get_device_descriptor' : get_device_descriptor,
-    'get_connect_rules'     : get_connect_rules,
+    'contexts'              : [CONTEXT],
+    'topologies'            : [TOPOLOGY],
+    'devices'               : [DEVICE_R1, DEVICE_R2, DEVICE_R3, DEVICE_O1],
+    'links'                 : [LINK_R1_O1, LINK_R2_O1, LINK_R3_O1],
+
+    'service_id'            : SERVICE_R1_R3_ID,
+    'service_descriptor'    : SERVICE_R1_R3_DESCRIPTOR,
+    'service_endpoint_ids'  : SERVICE_R1_R3_ENDPOINT_IDS,
+    'service_config_rules'  : SERVICE_R1_R3_CONFIG_RULES,
+    'service_constraints'   : SERVICE_R1_R3_CONSTRAINTS,
 })
diff --git a/src/service/tests/ServiceHandler_L3NM_OC.py b/src/service/tests/ServiceHandler_L3NM_OC.py
new file mode 100644
index 0000000000000000000000000000000000000000..0797a4af5505e78e2af49cefc29970f9c8ff11e7
--- /dev/null
+++ b/src/service/tests/ServiceHandler_L3NM_OC.py
@@ -0,0 +1,150 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import uuid
+from typing import Dict, List, Tuple
+from common.tools.object_factory.ConfigRule import json_config_rule_set
+from common.tools.object_factory.Constraint import json_constraint
+from common.tools.object_factory.Device import (
+    json_device_connect_rules, json_device_emulated_packet_router_disabled, json_device_emulated_tapi_disabled,
+    json_device_id)
+from common.tools.object_factory.EndPoint import json_endpoint, json_endpoint_id
+from common.tools.object_factory.Link import json_link, json_link_id
+from common.tools.object_factory.Service import json_service_id, json_service_l3nm_planned
+from .CommonObjects import CONTEXT, CONTEXT_ID, PACKET_PORT_SAMPLE_TYPES, TOPOLOGY, TOPOLOGY_ID
+
+SERVICE_HANDLER_NAME = 'l3nm_openconfig'
+
+def json_endpoint_ids(device_id : Dict, endpoint_descriptors : List[Tuple[str, str]]):
+    return [
+        json_endpoint_id(device_id, ep_uuid)
+        for ep_uuid, _ in endpoint_descriptors
+    ]
+
+def json_endpoints(device_id : Dict, endpoint_descriptors : List[Tuple[str, str]]):
+    return [
+        json_endpoint(device_id, ep_uuid, ep_type, kpi_sample_types=PACKET_PORT_SAMPLE_TYPES)
+        for ep_uuid, ep_type in endpoint_descriptors
+    ]
+
+# ----- Devices --------------------------------------------------------------------------------------------------------
+DEVICE_R1_UUID          = 'R1'
+DEVICE_R1_ENDPOINT_DEFS = [('EP1', 'optical'), ('EP100', 'copper')]
+DEVICE_R1_ADDRESS       = '10.0.0.1'
+DEVICE_R1_PORT          = 830
+DEVICE_R1_USERNAME      = 'admin'
+DEVICE_R1_PASSWORD      = 'admin'
+DEVICE_R1_TIMEOUT       = 120
+DEVICE_R1_ID            = json_device_id(DEVICE_R1_UUID)
+DEVICE_R1_ENDPOINTS     = json_endpoints(DEVICE_R1_ID, DEVICE_R1_ENDPOINT_DEFS)
+DEVICE_R1_ENDPOINT_IDS  = json_endpoint_ids(DEVICE_R1_ID, DEVICE_R1_ENDPOINT_DEFS)
+DEVICE_R1               = json_device_emulated_packet_router_disabled(DEVICE_R1_UUID, endpoints=DEVICE_R1_ENDPOINTS)
+ENDPOINT_ID_R1_EP1      = DEVICE_R1_ENDPOINT_IDS[0]
+ENDPOINT_ID_R1_EP100    = DEVICE_R1_ENDPOINT_IDS[1]
+DEVICE_R1_CONNECT_RULES = json_device_connect_rules(DEVICE_R1_ADDRESS, DEVICE_R1_PORT, {
+    'username': DEVICE_R1_USERNAME,
+    'password': DEVICE_R1_PASSWORD,
+    'timeout' : DEVICE_R1_TIMEOUT,
+})
+
+
+DEVICE_R2_UUID          = 'R2'
+DEVICE_R2_ENDPOINT_DEFS = [('EP1', 'optical'), ('EP100', 'copper')]
+DEVICE_R2_ADDRESS       = '10.0.0.2'
+DEVICE_R2_PORT          = 830
+DEVICE_R2_USERNAME      = 'admin'
+DEVICE_R2_PASSWORD      = 'admin'
+DEVICE_R2_TIMEOUT       = 120
+DEVICE_R2_ID            = json_device_id(DEVICE_R2_UUID)
+DEVICE_R2_ENDPOINTS     = json_endpoints(DEVICE_R2_ID, DEVICE_R2_ENDPOINT_DEFS)
+DEVICE_R2_ENDPOINT_IDS  = json_endpoint_ids(DEVICE_R2_ID, DEVICE_R2_ENDPOINT_DEFS)
+DEVICE_R2               = json_device_emulated_packet_router_disabled(DEVICE_R2_UUID, endpoints=DEVICE_R2_ENDPOINTS)
+ENDPOINT_ID_R2_EP1      = DEVICE_R2_ENDPOINT_IDS[0]
+ENDPOINT_ID_R2_EP100    = DEVICE_R2_ENDPOINT_IDS[1]
+DEVICE_R2_CONNECT_RULES = json_device_connect_rules(DEVICE_R2_ADDRESS, DEVICE_R2_PORT, {
+    'username': DEVICE_R2_USERNAME,
+    'password': DEVICE_R2_PASSWORD,
+    'timeout' : DEVICE_R2_TIMEOUT,
+})
+
+
+DEVICE_O1_UUID          = 'O1'
+DEVICE_O1_ENDPOINT_DEFS = [(str(uuid.uuid4()), 'optical'), (str(uuid.uuid4()), 'optical')]
+DEVICE_O1_ADDRESS       = '10.0.0.3'
+DEVICE_O1_PORT          = 4900
+DEVICE_O1_TIMEOUT       = 120
+DEVICE_O1_ID            = json_device_id(DEVICE_O1_UUID)
+DEVICE_O1_ENDPOINTS     = json_endpoints(DEVICE_O1_ID, DEVICE_O1_ENDPOINT_DEFS)
+DEVICE_O1_ENDPOINT_IDS  = json_endpoint_ids(DEVICE_O1_ID, DEVICE_O1_ENDPOINT_DEFS)
+DEVICE_O1               = json_device_emulated_tapi_disabled(DEVICE_O1_UUID, endpoints=DEVICE_O1_ENDPOINTS)
+ENDPOINT_ID_O1_EP1      = DEVICE_O1_ENDPOINT_IDS[0]
+ENDPOINT_ID_O1_EP2      = DEVICE_O1_ENDPOINT_IDS[1]
+DEVICE_O1_CONNECT_RULES = json_device_connect_rules(DEVICE_O1_ADDRESS, DEVICE_O1_PORT, {
+    'timeout' : DEVICE_O1_TIMEOUT,
+})
+
+
+# ----- Links ----------------------------------------------------------------------------------------------------------
+LINK_R1_O1_UUID = '{:s}/{:s}-{:s}/{:s}'.format(
+    DEVICE_R1_UUID, ENDPOINT_ID_R1_EP1['endpoint_uuid']['uuid'],
+    DEVICE_O1_UUID, ENDPOINT_ID_O1_EP1['endpoint_uuid']['uuid'])
+LINK_R1_O1_ID   = json_link_id(LINK_R1_O1_UUID)
+LINK_R1_O1      = json_link(LINK_R1_O1_UUID, [ENDPOINT_ID_R1_EP1, ENDPOINT_ID_O1_EP1])
+
+LINK_R2_O1_UUID = '{:s}/{:s}-{:s}/{:s}'.format(
+    DEVICE_R2_UUID, ENDPOINT_ID_R2_EP1['endpoint_uuid']['uuid'],
+    DEVICE_O1_UUID, ENDPOINT_ID_O1_EP2['endpoint_uuid']['uuid'])
+LINK_R2_O1_ID   = json_link_id(LINK_R2_O1_UUID)
+LINK_R2_O1      = json_link(LINK_R2_O1_UUID, [ENDPOINT_ID_R2_EP1, ENDPOINT_ID_O1_EP2])
+
+
+# ----- Service --------------------------------------------------------------------------------------------------------
+SERVICE_R1_R2_UUID         = 'SVC:{:s}/{:s}-{:s}/{:s}'.format(
+    DEVICE_R1_UUID, ENDPOINT_ID_R1_EP100['endpoint_uuid']['uuid'],
+    DEVICE_R2_UUID, ENDPOINT_ID_R2_EP100['endpoint_uuid']['uuid'])
+SERVICE_R1_R2_ENDPOINT_IDS = [ENDPOINT_ID_R1_EP100, ENDPOINT_ID_R2_EP100]
+SERVICE_R1_R2_CONSTRAINTS  = [
+    json_constraint('latency_ms', 15.2),
+    json_constraint('jitter_us', 1.2),
+]
+SERVICE_R1_R2_CONFIG_RULES = [
+    json_config_rule_set(
+        '/settings',
+        {'mtu': 1512, 'address_families': ['IPV4'], 'bgp_as': 65000, 'bgp_route_target': '65000:333'}),
+    json_config_rule_set(
+        '/device[{:s}]/endpoint[{:s}]/settings'.format(DEVICE_R1_UUID, ENDPOINT_ID_R1_EP100['endpoint_uuid']['uuid']),
+        {'router_id': '10.10.10.1', 'route_distinguisher': '65000:123', 'sub_interface_index': 400, 'vlan_id': 400,
+        'address_ip': '3.3.2.1', 'address_prefix': 24}),
+    json_config_rule_set(
+        '/device[{:s}]/endpoint[{:s}]/settings'.format(DEVICE_R2_UUID, ENDPOINT_ID_R2_EP100['endpoint_uuid']['uuid']),
+        {'router_id': '20.20.20.1', 'route_distinguisher': '65000:321', 'sub_interface_index': 400, 'vlan_id': 500,
+        'address_ip': '3.3.1.1', 'address_prefix': 24}),
+]
+SERVICE_R1_R2_ID           = json_service_id(SERVICE_R1_R2_UUID, context_id=CONTEXT_ID)
+SERVICE_R1_R2_DESCRIPTOR   = json_service_l3nm_planned(SERVICE_R1_R2_UUID)
+
+
+# ----- Test Descriptor ------------------------------------------------------------------------------------------------
+TEST_SERVICE_HANDLER = (SERVICE_HANDLER_NAME, {
+    'contexts'              : [CONTEXT],
+    'topologies'            : [TOPOLOGY],
+    'devices'               : [DEVICE_R1, DEVICE_R2, DEVICE_O1],
+    'links'                 : [LINK_R1_O1, LINK_R2_O1],
+
+    'service_id'            : SERVICE_R1_R2_ID,
+    'service_descriptor'    : SERVICE_R1_R2_DESCRIPTOR,
+    'service_endpoint_ids'  : SERVICE_R1_R2_ENDPOINT_IDS,
+    'service_config_rules'  : SERVICE_R1_R2_CONFIG_RULES,
+    'service_constraints'   : SERVICE_R1_R2_CONSTRAINTS,
+})
diff --git a/src/service/tests/ServiceHandlersToTest.py b/src/service/tests/ServiceHandlersToTest.py
index 484ae3e45491f31a065ad27b49f86e6338af7a51..43f2a2eb2e720eea5b9f690e374ec3b5830eac1d 100644
--- a/src/service/tests/ServiceHandlersToTest.py
+++ b/src/service/tests/ServiceHandlersToTest.py
@@ -1,3 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 # Add/comment in this file the service handlers to be tested by the unitry tests.
 
 SERVICE_HANDLERS_TO_TEST = []
diff --git a/src/service/tests/__init__.py b/src/service/tests/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..70a33251242c51f49140e596b8208a19dd5245f7 100644
--- a/src/service/tests/__init__.py
+++ b/src/service/tests/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/service/tests/test_unitary.py b/src/service/tests/test_unitary.py
index e03f2017ad2acaf91f6e4186fc7c502f08b7ec0c..812a65c5c4353925dcf0b186a0ebb5401a4ecfa1 100644
--- a/src/service/tests/test_unitary.py
+++ b/src/service/tests/test_unitary.py
@@ -1,30 +1,28 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import copy, grpc, logging, os, pytest
-from typing import Tuple
-from google.protobuf.json_format import MessageToDict
-from common.orm.Database import Database
-from common.orm.Factory import get_database_backend, BackendEnum as DatabaseBackendEnum
-from common.message_broker.Factory import get_messagebroker_backend, BackendEnum as MessageBrokerBackendEnum
-from common.message_broker.MessageBroker import MessageBroker
+from common.tests.MockService import MockService
+from common.tests.MockServicerImpl_Context import MockServicerImpl_Context
+from common.tests.MockServicerImpl_Device import MockServicerImpl_Device
 from common.tests.PytestGenerateTests import pytest_generate_tests # (required) pylint: disable=unused-import
-from context.Config import (
-    GRPC_SERVICE_PORT as CONTEXT_GRPC_SERVICE_PORT, GRPC_MAX_WORKERS as CONTEXT_GRPC_MAX_WORKERS,
-    GRPC_GRACE_PERIOD as CONTEXT_GRPC_GRACE_PERIOD)
+from common.tools.grpc.Tools import grpc_message_to_json_string
 from context.client.ContextClient import ContextClient
-from context.proto.context_pb2 import Context, DeviceId, Topology, Device
-from context.service.grpc_server.ContextService import ContextService
-from device.Config import (
-    GRPC_SERVICE_PORT as DEVICE_GRPC_SERVICE_PORT, GRPC_MAX_WORKERS as DEVICE_GRPC_MAX_WORKERS,
-    GRPC_GRACE_PERIOD as DEVICE_GRPC_GRACE_PERIOD)
+from context.proto.context_pb2 import Context, ContextId, DeviceId, Link, LinkId, Topology, Device, TopologyId
+from context.proto.context_pb2_grpc import add_ContextServiceServicer_to_server
 from device.client.DeviceClient import DeviceClient
-from device.service.DeviceService import DeviceService
-from device.service.driver_api.DriverFactory import DriverFactory
-from device.service.driver_api.DriverInstanceCache import DriverInstanceCache
-from device.service.drivers import DRIVERS
-from device.tests.MockMonitoringService import MockMonitoringService
-from monitoring.Config import (
-    GRPC_SERVICE_PORT as MONITORING_GRPC_SERVICE_PORT, GRPC_MAX_WORKERS as MONITORING_GRPC_MAX_WORKERS,
-    GRPC_GRACE_PERIOD as MONITORING_GRPC_GRACE_PERIOD)
-from monitoring.client.monitoring_client import MonitoringClient
+from device.proto.device_pb2_grpc import add_DeviceServiceServicer_to_server
 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)
@@ -33,88 +31,49 @@ from service.proto.context_pb2 import Service, ServiceId
 from service.service.ServiceService import ServiceService
 from service.service.service_handler_api.ServiceHandlerFactory import ServiceHandlerFactory
 from service.service.service_handlers import SERVICE_HANDLERS
-from .CommonObjects import CONTEXT, TOPOLOGY
 
 LOGGER = logging.getLogger(__name__)
 LOGGER.setLevel(logging.DEBUG)
 
-CONTEXT_GRPC_SERVICE_PORT = 10000 + CONTEXT_GRPC_SERVICE_PORT # avoid privileged ports
-DEVICE_GRPC_SERVICE_PORT = 10000 + DEVICE_GRPC_SERVICE_PORT # avoid privileged ports
 SERVICE_GRPC_SERVICE_PORT = 10000 + SERVICE_GRPC_SERVICE_PORT # avoid privileged ports
-MONITORING_GRPC_SERVICE_PORT = 10000 + MONITORING_GRPC_SERVICE_PORT # avoid privileged ports
-
-DEFAULT_REDIS_SERVICE_HOST = '127.0.0.1'
-DEFAULT_REDIS_SERVICE_PORT = 6379
-DEFAULT_REDIS_DATABASE_ID  = 0
-
-REDIS_CONFIG = {
-    'REDIS_SERVICE_HOST': os.environ.get('REDIS_SERVICE_HOST', DEFAULT_REDIS_SERVICE_HOST),
-    'REDIS_SERVICE_PORT': os.environ.get('REDIS_SERVICE_PORT', DEFAULT_REDIS_SERVICE_PORT),
-    'REDIS_DATABASE_ID' : os.environ.get('REDIS_DATABASE_ID',  DEFAULT_REDIS_DATABASE_ID ),
-}
-
-SCENARIOS = [
-    ('all_inmemory', DatabaseBackendEnum.INMEMORY, {},           MessageBrokerBackendEnum.INMEMORY, {}          ),
-    #('all_redis',    DatabaseBackendEnum.REDIS,    REDIS_CONFIG, MessageBrokerBackendEnum.REDIS,    REDIS_CONFIG),
-]
-
-@pytest.fixture(scope='session', ids=[str(scenario[0]) for scenario in SCENARIOS], params=SCENARIOS)
-def context_db_mb(request) -> Tuple[Database, MessageBroker]:
-    name,db_backend,db_settings,mb_backend,mb_settings = request.param
-    msg = 'Running scenario {:s} db_backend={:s}, db_settings={:s}, mb_backend={:s}, mb_settings={:s}...'
-    LOGGER.info(msg.format(str(name), str(db_backend.value), str(db_settings), str(mb_backend.value), str(mb_settings)))
-    _database = Database(get_database_backend(backend=db_backend, **db_settings))
-    _message_broker = MessageBroker(get_messagebroker_backend(backend=mb_backend, **mb_settings))
-    yield _database, _message_broker
-    _message_broker.terminate()
 
-@pytest.fixture(scope='session')
-def context_service(context_db_mb : Tuple[Database, MessageBroker]): # pylint: disable=redefined-outer-name
-    _service = ContextService(
-        context_db_mb[0], context_db_mb[1], port=CONTEXT_GRPC_SERVICE_PORT, max_workers=CONTEXT_GRPC_MAX_WORKERS,
-        grace_period=CONTEXT_GRPC_GRACE_PERIOD)
-    _service.start()
-    yield _service
-    _service.stop()
+LOCALHOST = '127.0.0.1'
+MOCKSERVER_GRPC_PORT = 10000
 
-@pytest.fixture(scope='session')
-def context_client(context_service : ContextService): # pylint: disable=redefined-outer-name
-    _client = ContextClient(address='127.0.0.1', port=CONTEXT_GRPC_SERVICE_PORT)
-    yield _client
-    _client.close()
+class MockService_Combined(MockService):
+    # Mock Server implementing Context and Service to simplify unitary tests of Compute
 
-@pytest.fixture(scope='session')
-def monitoring_service():
-    _service = MockMonitoringService(port=MONITORING_GRPC_SERVICE_PORT, max_workers=MONITORING_GRPC_MAX_WORKERS,
-        grace_period=MONITORING_GRPC_GRACE_PERIOD)
-    _service.start()
-    yield _service
-    _service.stop()
+    def __init__(self, cls_name='MockService_Service'):
+        super().__init__(LOCALHOST, MOCKSERVER_GRPC_PORT, cls_name=cls_name)
 
-@pytest.fixture(scope='session')
-def monitoring_client(monitoring_service : MockMonitoringService): # pylint: disable=redefined-outer-name
-    _client = MonitoringClient(server='127.0.0.1', port=MONITORING_GRPC_SERVICE_PORT)
-    #yield _client
-    #_client.close()
-    return _client
+    # 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.device_servicer = MockServicerImpl_Device()
+        add_DeviceServiceServicer_to_server(self.device_servicer, self.server)
+
+os.environ['CONTEXTSERVICE_SERVICE_HOST'] = LOCALHOST
+os.environ['CONTEXTSERVICE_SERVICE_PORT_GRPC'] = str(MOCKSERVER_GRPC_PORT)
+os.environ['DEVICESERVICE_SERVICE_HOST'] = LOCALHOST
+os.environ['DEVICESERVICE_SERVICE_PORT_GRPC'] = str(MOCKSERVER_GRPC_PORT)
 
 @pytest.fixture(scope='session')
-def device_service(
-    context_client : ContextClient,         # pylint: disable=redefined-outer-name
-    monitoring_client : MonitoringClient):  # pylint: disable=redefined-outer-name
-
-    _driver_factory = DriverFactory(DRIVERS)
-    _driver_instance_cache = DriverInstanceCache(_driver_factory)
-    _service = DeviceService(
-        context_client, monitoring_client, _driver_instance_cache, port=DEVICE_GRPC_SERVICE_PORT,
-        max_workers=DEVICE_GRPC_MAX_WORKERS, grace_period=DEVICE_GRPC_GRACE_PERIOD)
+def mockservice():
+    _service = MockService_Combined()
     _service.start()
     yield _service
     _service.stop()
 
 @pytest.fixture(scope='session')
-def device_client(device_service : DeviceService): # pylint: disable=redefined-outer-name
-    _client = DeviceClient(address='127.0.0.1', port=DEVICE_GRPC_SERVICE_PORT)
+def context_client(mockservice : MockService_Combined): # pylint: disable=redefined-outer-name
+    _client = ContextClient(address=LOCALHOST, port=MOCKSERVER_GRPC_PORT)
+    yield _client
+    _client.close()
+
+@pytest.fixture(scope='session')
+def device_client(mockservice : MockService_Combined): # pylint: disable=redefined-outer-name
+    _client = DeviceClient(address=LOCALHOST, port=MOCKSERVER_GRPC_PORT)
     yield _client
     _client.close()
 
@@ -133,14 +92,10 @@ def service_service(
 
 @pytest.fixture(scope='session')
 def service_client(service_service : ServiceService): # pylint: disable=redefined-outer-name
-    _client = ServiceClient(address='127.0.0.1', port=SERVICE_GRPC_SERVICE_PORT)
+    _client = ServiceClient(address=LOCALHOST, port=SERVICE_GRPC_SERVICE_PORT)
     yield _client
     _client.close()
 
-def grpc_message_to_json_string(message):
-    return str(MessageToDict(
-        message, including_default_value_fields=True, preserving_proto_field_name=True, use_integers_for_enums=False))
-
 try:
     from .ServiceHandlersToTest import SERVICE_HANDLERS_TO_TEST
 except ImportError:
@@ -152,21 +107,22 @@ class TestServiceHandlers:
 
     def test_prepare_environment(
         self, service_id, service_descriptor, service_endpoint_ids, service_config_rules, service_constraints,
-        service_device_uuids, get_device_descriptor, get_connect_rules,
-        context_client : ContextClient, # pylint: disable=redefined-outer-name
-        device_client : DeviceClient):  # pylint: disable=redefined-outer-name
+        contexts, topologies, devices, links,
+        context_client : ContextClient,     # pylint: disable=redefined-outer-name
+        device_client : DeviceClient,       # pylint: disable=redefined-outer-name
+        service_client : ServiceClient):    # pylint: disable=redefined-outer-name
 
-        context_client.SetContext(Context(**CONTEXT))
-        context_client.SetTopology(Topology(**TOPOLOGY))
+        for context in contexts: context_client.SetContext(Context(**context))
+        for topology in topologies: context_client.SetTopology(Topology(**topology))
+        for device in devices: device_client.AddDevice(Device(**device))
+        for link in links: context_client.SetLink(Link(**link))
 
-        for device_uuid in service_device_uuids:
-            device_with_connect_rules = copy.deepcopy(get_device_descriptor(device_uuid))
-            device_with_connect_rules['device_config']['config_rules'].extend(get_connect_rules(device_uuid))
-            device_client.AddDevice(Device(**device_with_connect_rules))
 
     def test_service_create_error_cases(
         self, service_id, service_descriptor, service_endpoint_ids, service_config_rules, service_constraints,
-        service_device_uuids, get_device_descriptor, get_connect_rules,
+        contexts, topologies, devices, links,
+        context_client : ContextClient,     # pylint: disable=redefined-outer-name
+        device_client : DeviceClient,       # pylint: disable=redefined-outer-name
         service_client : ServiceClient):    # pylint: disable=redefined-outer-name
 
         with pytest.raises(grpc.RpcError) as e:
@@ -205,7 +161,9 @@ class TestServiceHandlers:
 
     def test_service_create_correct(
         self, service_id, service_descriptor, service_endpoint_ids, service_config_rules, service_constraints,
-        service_device_uuids, get_device_descriptor, get_connect_rules,
+        contexts, topologies, devices, links,
+        context_client : ContextClient,     # pylint: disable=redefined-outer-name
+        device_client : DeviceClient,       # pylint: disable=redefined-outer-name
         service_client : ServiceClient):    # pylint: disable=redefined-outer-name
 
         service_client.CreateService(Service(**service_descriptor))
@@ -213,20 +171,22 @@ class TestServiceHandlers:
 
     def test_service_get_created(
         self, service_id, service_descriptor, service_endpoint_ids, service_config_rules, service_constraints,
-        service_device_uuids, get_device_descriptor, get_connect_rules,
-        context_client : ContextClient):    # pylint: disable=redefined-outer-name
+        contexts, topologies, devices, links,
+        context_client : ContextClient,     # pylint: disable=redefined-outer-name
+        device_client : DeviceClient,       # pylint: disable=redefined-outer-name
+        service_client : ServiceClient):    # pylint: disable=redefined-outer-name
 
         service_data = context_client.GetService(ServiceId(**service_id))
         LOGGER.info('service_data = {:s}'.format(grpc_message_to_json_string(service_data)))
 
 
-    def test_service_update(
+    def test_service_update_configure(
         self, service_id, service_descriptor, service_endpoint_ids, service_config_rules, service_constraints,
-        service_device_uuids, get_device_descriptor, get_connect_rules,
+        contexts, topologies, devices, links,
         context_client : ContextClient,     # pylint: disable=redefined-outer-name
+        device_client : DeviceClient,       # pylint: disable=redefined-outer-name
         service_client : ServiceClient):    # pylint: disable=redefined-outer-name
 
-        # Configure
         service_with_settings = copy.deepcopy(service_descriptor)
         service_with_settings['service_endpoint_ids'].extend(service_endpoint_ids)
         service_with_settings['service_config']['config_rules'].extend(service_config_rules)
@@ -240,7 +200,14 @@ class TestServiceHandlers:
                 LOGGER.info('device_data[{:s}][#{:d}] => {:s}'.format(
                     str(device_id), i, grpc_message_to_json_string(config_rule)))
 
-        # Deconfigure
+
+    def test_service_update_deconfigure(
+        self, service_id, service_descriptor, service_endpoint_ids, service_config_rules, service_constraints,
+        contexts, topologies, devices, links,
+        context_client : ContextClient,     # pylint: disable=redefined-outer-name
+        device_client : DeviceClient,       # pylint: disable=redefined-outer-name
+        service_client : ServiceClient):    # pylint: disable=redefined-outer-name
+
         service_with_settings = copy.deepcopy(service_descriptor)
         service_with_settings['service_endpoint_ids'].extend([]) # remove endpoints
         service_client.UpdateService(Service(**service_with_settings))
@@ -255,8 +222,10 @@ class TestServiceHandlers:
 
     def test_service_get_updated(
         self, service_id, service_descriptor, service_endpoint_ids, service_config_rules, service_constraints,
-        service_device_uuids, get_device_descriptor, get_connect_rules,
-        context_client : ContextClient):    # pylint: disable=redefined-outer-name
+        contexts, topologies, devices, links,
+        context_client : ContextClient,     # pylint: disable=redefined-outer-name
+        device_client : DeviceClient,       # pylint: disable=redefined-outer-name
+        service_client : ServiceClient):    # pylint: disable=redefined-outer-name
 
         service_data = context_client.GetService(ServiceId(**service_id))
         LOGGER.info('service_data = {:s}'.format(grpc_message_to_json_string(service_data)))
@@ -264,7 +233,22 @@ class TestServiceHandlers:
 
     def test_service_delete(
         self, service_id, service_descriptor, service_endpoint_ids, service_config_rules, service_constraints,
-        service_device_uuids, get_device_descriptor, get_connect_rules,
+        contexts, topologies, devices, links,
+        context_client : ContextClient,     # pylint: disable=redefined-outer-name
+        device_client : DeviceClient,       # pylint: disable=redefined-outer-name
         service_client : ServiceClient):    # pylint: disable=redefined-outer-name
 
         service_client.DeleteService(ServiceId(**service_id))
+
+
+    def test_cleanup_environment(
+        self, service_id, service_descriptor, service_endpoint_ids, service_config_rules, service_constraints,
+        contexts, topologies, devices, links,
+        context_client : ContextClient,     # pylint: disable=redefined-outer-name
+        device_client : DeviceClient,       # pylint: disable=redefined-outer-name
+        service_client : ServiceClient):    # pylint: disable=redefined-outer-name
+
+        for link in links: context_client.RemoveLink(LinkId(**link['link_id']))
+        for device in devices: device_client.DeleteDevice(DeviceId(**device['device_id']))
+        for topology in topologies: context_client.RemoveTopology(TopologyId(**topology['topology_id']))
+        for context in contexts: context_client.RemoveContext(ContextId(**context['context_id']))
diff --git a/src/start.sh b/src/start.sh
index da0e7789d92a37f4bd0f429a1d01c7bac468deec..dc9db79d27d4d1b459092bf2f9a8f43bb5581511 100755
--- a/src/start.sh
+++ b/src/start.sh
@@ -1,4 +1,18 @@
 #!/usr/bin/env bash
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 docker network create -d bridge teraflowbridge
 
 docker run -d -p 7070:7070 --name monitoring --network=teraflowbridge monitoring:dockerfile
diff --git a/src/start_webui_dev_mode.sh b/src/start_webui_dev_mode.sh
new file mode 100755
index 0000000000000000000000000000000000000000..74540bcb36115dc175f371acbb3f80930404eac9
--- /dev/null
+++ b/src/start_webui_dev_mode.sh
@@ -0,0 +1,33 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# for development purposes only
+
+K8S_NAMESPACE=${K8S_NAMESPACE:-'tf-dev'}
+
+export CONTEXTSERVICE_SERVICE_HOST=`kubectl get service/contextservice -n ${K8S_NAMESPACE} -o jsonpath='{.spec.clusterIP}'`
+
+echo Context IP: $CONTEXTSERVICE_SERVICE_HOST
+
+export DEVICESERVICE_SERVICE_HOST=`kubectl get service/deviceservice -n ${K8S_NAMESPACE} -o jsonpath='{.spec.clusterIP}'`
+
+echo Device IP: $DEVICESERVICE_SERVICE_HOST
+
+export PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION='python'
+export HOST="127.0.0.1"
+export FLASK_ENV="development"
+
+# python3 -m webbrowser http://${HOST}:8004
+
+python -m webui.service
diff --git a/src/tester_functional/.gitlab-ci.yml b/src/tester_functional/.gitlab-ci.yml
deleted file mode 100644
index 15f2cd434174031ee8e461ec33c39a1cf5ff58e9..0000000000000000000000000000000000000000
--- a/src/tester_functional/.gitlab-ci.yml
+++ /dev/null
@@ -1,67 +0,0 @@
-# Build, tag, and push the Docker images to the GitLab Docker registry
-build funct_test:
-  variables:
-    IMAGE_NAME: 'tester_functional' # name of the microservice
-    IMAGE_TAG: 'latest' # tag of the container image (production, development, etc)
-  stage: build
-  before_script:
-    - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
-  script:
-    - 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"
-  rules:
-    - changes:
-      - src/common/**
-      - src/context/**
-      - src/device/**
-      - src/service/**
-      - src/$IMAGE_NAME/**
-      - .gitlab-ci.yml
-
-# Pull, execute, and run unitary tests for the Docker image from the GitLab registry
-unit_test funct_test:
-  variables:
-    IMAGE_NAME: 'tester_functional' # name of the microservice
-    IMAGE_TAG: 'latest' # tag of the container image (production, development, etc)
-  stage: test
-  needs:
-    - build funct_test
-  before_script:
-    - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
-  script:
-    - docker pull "$CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG"
-  rules:
-    - changes:
-      - src/common/**
-      - src/context/**
-      - src/device/**
-      - src/service/**
-      - src/$IMAGE_NAME/**
-      - .gitlab-ci.yml
-
-# Run functional tests in Kubernetes Cluster
-funct_test execute:
-  variables:
-    IMAGE_NAME: 'tester_functional' # name of the microservice
-    IMAGE_TAG: 'latest' # tag of the container image (production, development, etc)
-  stage: funct_test
-  needs:
-    - build funct_test
-    - unit_test funct_test
-    - deploy context
-    - deploy device
-    - deploy service
-    - dependencies all
-    - integ_test execute
-  before_script:
-    - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
-  script:
-    - kubectl version
-    - kubectl get all
-    - kubectl delete pod $(echo $IMAGE_NAME | sed -r 's/[^a-zA-Z0-9\.\-]/-/g') --wait=true --ignore-not-found=true
-    - kubectl get all
-    - kubectl run $(echo $IMAGE_NAME | sed -r 's/[^a-zA-Z0-9\.\-]/-/g') --image "$CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG" --restart=Never -i --rm
-    - kubectl get all
-  #when: manual
-  #allow_failure: false
diff --git a/src/tester_functional/Dockerfile b/src/tester_functional/Dockerfile
deleted file mode 100644
index 36883f30bf76fbee7094ee9c5dbe626c6fdec071..0000000000000000000000000000000000000000
--- a/src/tester_functional/Dockerfile
+++ /dev/null
@@ -1,35 +0,0 @@
-FROM python:3-slim
-
-# Install dependencies
-RUN apt-get --yes --quiet --quiet update && \
-    apt-get --yes --quiet --quiet install wget g++ && \
-    rm -rf /var/lib/apt/lists/*
-
-# Set Python to show logs as they occur
-ENV PYTHONUNBUFFERED=0
-
-# Get generic Python packages
-RUN python3 -m pip install --upgrade pip setuptools wheel pip-tools
-
-# Set working directory
-WORKDIR /var/teraflow
-
-# Create module sub-folders
-RUN mkdir -p /var/teraflow/tester_functional
-
-# Get Python packages per module
-COPY tester_functional/requirements.in tester_functional/requirements.in
-RUN pip-compile --output-file=tester_functional/requirements.txt tester_functional/requirements.in
-RUN python3 -m pip install -r tester_functional/requirements.in
-
-# Add files into working directory
-COPY common/. common
-COPY context/. context
-COPY device/. device
-COPY service/. service
-COPY tester_functional/. tester_functional
-
-# Run integration tester
-ENTRYPOINT ["pytest", "-v", "--log-level=DEBUG", \
-            "tester_functional/test_context_device_service.py" \
-]
diff --git a/src/tester_functional/definitions.py b/src/tester_functional/definitions.py
deleted file mode 100644
index 2b38e74bc9d25199e4f192dbb3f093ec033fe80b..0000000000000000000000000000000000000000
--- a/src/tester_functional/definitions.py
+++ /dev/null
@@ -1,165 +0,0 @@
-import copy
-from common.database.api.context.Constants import DEFAULT_CONTEXT_ID, DEFAULT_TOPOLOGY_ID
-from common.database.api.context.service.ServiceState import ServiceState
-from common.database.api.context.service.ServiceType import ServiceType
-from common.database.api.context.topology.device.OperationalStatus import OperationalStatus
-
-# use "copy.deepcopy" to prevent propagating forced changes during tests
-dc = copy.deepcopy
-
-CONTEXT_ID = {'contextUuid': {'uuid': DEFAULT_CONTEXT_ID}}
-
-TOPOLOGY_ID = {'contextId': dc(CONTEXT_ID), 'topoId': {'uuid': DEFAULT_TOPOLOGY_ID}}
-
-DEVICE_ID_DEV1 = {'device_id': {'uuid': 'dev1'}}
-DEVICE_DEV1 = {
-    'device_id': dc(DEVICE_ID_DEV1), 'device_type': 'ROADM', 'device_config': {'device_config': '<config/>'},
-    'devOperationalStatus': OperationalStatus.ENABLED.value,
-    'endpointList' : [
-        {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV1), 'port_id': {'uuid' : 'port2'}},
-         'port_type': 'WDM'},
-        {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV1), 'port_id': {'uuid' : 'port3'}},
-         'port_type': 'WDM'},
-        {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV1), 'port_id': {'uuid' : 'port101'}},
-         'port_type': 'OCH'},
-        {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV1), 'port_id': {'uuid' : 'port102'}},
-         'port_type': 'OCH'},
-        {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV1), 'port_id': {'uuid' : 'port103'}},
-         'port_type': 'OCH'},
-    ]
-}
-
-DEVICE_ID_DEV2 = {'device_id': {'uuid': 'dev2'}}
-DEVICE_DEV2 = {
-    'device_id': dc(DEVICE_ID_DEV2), 'device_type': 'ROADM', 'device_config': {'device_config': '<config/>'},
-    'devOperationalStatus': OperationalStatus.ENABLED.value,
-    'endpointList' : [
-        {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV2), 'port_id': {'uuid' : 'port1'}},
-         'port_type': 'WDM'},
-        {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV2), 'port_id': {'uuid' : 'port3'}},
-         'port_type': 'WDM'},
-        {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV2), 'port_id': {'uuid' : 'port101'}},
-         'port_type': 'OCH'},
-        {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV2), 'port_id': {'uuid' : 'port102'}},
-         'port_type': 'OCH'},
-        {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV2), 'port_id': {'uuid' : 'port103'}},
-         'port_type': 'OCH'},
-    ]
-}
-
-DEVICE_ID_DEV3 = {'device_id': {'uuid': 'dev3'}}
-DEVICE_DEV3 = {
-    'device_id': dc(DEVICE_ID_DEV3), 'device_type': 'ROADM', 'device_config': {'device_config': '<config/>'},
-    'devOperationalStatus': OperationalStatus.ENABLED.value,
-    'endpointList' : [
-        {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV3), 'port_id': {'uuid' : 'port1'}},
-         'port_type': 'WDM'},
-        {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV3), 'port_id': {'uuid' : 'port2'}},
-         'port_type': 'WDM'},
-        {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV3), 'port_id': {'uuid' : 'port101'}},
-         'port_type': 'OCH'},
-        {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV3), 'port_id': {'uuid' : 'port102'}},
-         'port_type': 'OCH'},
-        {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV3), 'port_id': {'uuid' : 'port103'}},
-         'port_type': 'OCH'},
-    ]
-}
-
-LINK_ID_DEV1_DEV2 = {'link_id': {'uuid': 'dev1/port2 ==> dev2/port1'}}
-LINK_DEV1_DEV2 = {
-    'link_id': dc(LINK_ID_DEV1_DEV2),
-    'endpointList' : [
-        {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV1), 'port_id': {'uuid' : 'port2'}},
-        {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV2), 'port_id': {'uuid' : 'port1'}},
-    ]
-}
-
-LINK_ID_DEV1_DEV3 = {'link_id': {'uuid': 'dev1/port3 ==> dev3/port1'}}
-LINK_DEV1_DEV3 = {
-    'link_id': dc(LINK_ID_DEV1_DEV3),
-    'endpointList' : [
-        {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV1), 'port_id': {'uuid' : 'port3'}},
-        {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV3), 'port_id': {'uuid' : 'port1'}},
-    ]
-}
-
-LINK_ID_DEV2_DEV1 = {'link_id': {'uuid': 'dev2/port1 ==> dev1/port2'}}
-LINK_DEV2_DEV1 = {
-    'link_id': dc(LINK_ID_DEV2_DEV1),
-    'endpointList' : [
-        {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV2), 'port_id': {'uuid' : 'port1'}},
-        {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV1), 'port_id': {'uuid' : 'port2'}},
-    ]
-}
-
-LINK_ID_DEV2_DEV3 = {'link_id': {'uuid': 'dev2/port3 ==> dev3/port2'}}
-LINK_DEV2_DEV3 = {
-    'link_id': dc(LINK_ID_DEV2_DEV3),
-    'endpointList' : [
-        {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV2), 'port_id': {'uuid' : 'port3'}},
-        {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV3), 'port_id': {'uuid' : 'port2'}},
-    ]
-}
-
-LINK_ID_DEV3_DEV1 = {'link_id': {'uuid': 'dev3/port1 ==> dev1/port3'}}
-LINK_DEV3_DEV1 = {
-    'link_id': dc(LINK_ID_DEV3_DEV1),
-    'endpointList' : [
-        {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV3), 'port_id': {'uuid' : 'port1'}},
-        {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV1), 'port_id': {'uuid' : 'port3'}},
-    ]
-}
-
-LINK_ID_DEV3_DEV2 = {'link_id': {'uuid': 'dev3/port2 ==> dev2/port3'}}
-LINK_DEV3_DEV2 = {
-    'link_id': dc(LINK_ID_DEV3_DEV2),
-    'endpointList' : [
-        {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV3), 'port_id': {'uuid' : 'port2'}},
-        {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV2), 'port_id': {'uuid' : 'port3'}},
-    ]
-}
-
-SERVICE_ID_SVC1 = {'contextId': dc(CONTEXT_ID), 'cs_id': {'uuid': 'svc1'}}
-SERVICE_SVC1 = {
-    'cs_id': dc(SERVICE_ID_SVC1), 'serviceType': ServiceType.L3NM.value,
-    'serviceConfig': {'serviceConfig': '<config/>'}, 'serviceState': {'serviceState': ServiceState.PLANNED.value},
-    'constraint': [
-        {'constraint_type': 'latency_ms', 'constraint_value': '100'},
-        {'constraint_type': 'hops', 'constraint_value': '5'},
-    ],
-    'endpointList' : [
-        {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV1), 'port_id': {'uuid' : 'port101'}},
-        {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV2), 'port_id': {'uuid' : 'port101'}},
-        {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV3), 'port_id': {'uuid' : 'port101'}},
-    ]
-}
-
-SERVICE_ID_SVC2 = {'contextId': dc(CONTEXT_ID), 'cs_id': {'uuid': 'svc2'}}
-SERVICE_SVC2 = {
-    'cs_id': dc(SERVICE_ID_SVC2), 'serviceType': ServiceType.L3NM.value,
-    'serviceConfig': {'serviceConfig': '<config/>'}, 'serviceState': {'serviceState': ServiceState.PLANNED.value},
-    'constraint': [
-        {'constraint_type': 'latency_ms', 'constraint_value': '100'},
-        {'constraint_type': 'hops', 'constraint_value': '5'},
-    ],
-    'endpointList' : [
-        {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV1), 'port_id': {'uuid' : 'port102'}},
-        {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV2), 'port_id': {'uuid' : 'port102'}},
-        {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV3), 'port_id': {'uuid' : 'port102'}},
-    ]
-}
-
-SERVICE_ID_SVC3 = {'contextId': dc(CONTEXT_ID), 'cs_id': {'uuid': 'svc3'}}
-SERVICE_SVC3 = {
-    'cs_id': dc(SERVICE_ID_SVC3), 'serviceType': ServiceType.L3NM.value,
-    'serviceConfig': {'serviceConfig': '<config/>'}, 'serviceState': {'serviceState': ServiceState.PLANNED.value},
-    'constraint': [
-        {'constraint_type': 'latency_ms', 'constraint_value': '100'},
-        {'constraint_type': 'hops', 'constraint_value': '5'},
-    ],
-    'endpointList' : [
-        {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV1), 'port_id': {'uuid' : 'port103'}},
-        {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV2), 'port_id': {'uuid' : 'port103'}},
-        {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV3), 'port_id': {'uuid' : 'port103'}},
-    ]
-}
diff --git a/src/tester_functional/requirements.in b/src/tester_functional/requirements.in
deleted file mode 100644
index 25abdad1b5767117956a88b816399635348884c7..0000000000000000000000000000000000000000
--- a/src/tester_functional/requirements.in
+++ /dev/null
@@ -1,6 +0,0 @@
-grpcio-health-checking
-grpcio
-prometheus-client
-pytest
-pytest-benchmark
-redis
diff --git a/src/tester_functional/test_context_device_service.py b/src/tester_functional/test_context_device_service.py
deleted file mode 100644
index 6483569de44cb3cdecafe3316a1ac83ce82ea1d3..0000000000000000000000000000000000000000
--- a/src/tester_functional/test_context_device_service.py
+++ /dev/null
@@ -1,102 +0,0 @@
-import logging, pytest
-from google.protobuf.json_format import MessageToDict
-from common.Settings import get_setting
-from common.database.Factory import get_database, DatabaseEngineEnum
-from common.database.api.Database import Database
-from common.tests.Assertions import validate_device_id, validate_link_id, validate_service_id, \
-    validate_service_list_is_not_empty, validate_topology_has_devices, validate_topology_has_links, \
-    validate_topology_is_empty
-from context.client.ContextClient import ContextClient
-from context.proto.context_pb2 import Device, Empty, Link
-from device.client.DeviceClient import DeviceClient
-from tester_functional.definitions import DEVICE_DEV1, DEVICE_DEV2, DEVICE_DEV3
-from tester_functional.definitions import LINK_DEV1_DEV2, LINK_DEV1_DEV3, LINK_DEV2_DEV1, LINK_DEV2_DEV3, \
-    LINK_DEV3_DEV1, LINK_DEV3_DEV2
-from tester_functional.definitions import SERVICE_SVC1, SERVICE_SVC2, SERVICE_SVC3
-from service.client.ServiceClient import ServiceClient
-from service.proto.service_pb2 import Service
-
-LOGGER = logging.getLogger(__name__)
-LOGGER.setLevel(logging.DEBUG)
-
-@pytest.fixture(scope='session')
-def redis_database():
-    _database = get_database(engine=DatabaseEngineEnum.REDIS, REDIS_DATABASE_ID=0)
-    return _database
-
-@pytest.fixture(scope='session')
-def context_client():
-    service_host = get_setting('CONTEXTSERVICE_SERVICE_HOST')
-    service_port = get_setting('CONTEXTSERVICE_SERVICE_PORT_GRPC')
-    _client = ContextClient(address=service_host, port=service_port)
-    yield _client
-    _client.close()
-
-@pytest.fixture(scope='session')
-def device_client():
-    service_host = get_setting('DEVICESERVICE_SERVICE_HOST')
-    service_port = get_setting('DEVICESERVICE_SERVICE_PORT_GRPC')
-    _client = DeviceClient(address=service_host, port=service_port)
-    yield _client
-    _client.close()
-
-@pytest.fixture(scope='session')
-def service_client():
-    service_host = get_setting('SERVICESERVICE_SERVICE_HOST')
-    service_port = get_setting('SERVICESERVICE_SERVICE_PORT_GRPC')
-    _client = ServiceClient(address=service_host, port=service_port)
-    yield _client
-    _client.close()
-
-def test_clean_database(redis_database : Database):
-    # should work
-    redis_database.clear_all()
-
-def test_get_topology_empty(context_client : ContextClient):
-    # should work
-    validate_topology_is_empty(MessageToDict(
-        context_client.GetTopology(Empty()),
-        including_default_value_fields=True, preserving_proto_field_name=True,
-        use_integers_for_enums=False))
-
-def test_add_devices(context_client : ContextClient, device_client : DeviceClient):
-    # should work
-    for device in [DEVICE_DEV1, DEVICE_DEV2, DEVICE_DEV3]:
-        validate_device_id(MessageToDict(
-            device_client.AddDevice(Device(**device)),
-            including_default_value_fields=True, preserving_proto_field_name=True,
-            use_integers_for_enums=False))
-
-    # should work
-    validate_topology_has_devices(MessageToDict(
-        context_client.GetTopology(Empty()),
-        including_default_value_fields=True, preserving_proto_field_name=True,
-        use_integers_for_enums=False))
-
-def test_add_links(context_client : ContextClient):
-    # should work
-    for link in [LINK_DEV1_DEV2, LINK_DEV1_DEV3, LINK_DEV2_DEV1, LINK_DEV2_DEV3, LINK_DEV3_DEV1, LINK_DEV3_DEV2]:
-        validate_link_id(MessageToDict(
-            context_client.AddLink(Link(**link)),
-            including_default_value_fields=True, preserving_proto_field_name=True,
-            use_integers_for_enums=False))
-
-    # should work
-    validate_topology_has_links(MessageToDict(
-        context_client.GetTopology(Empty()),
-        including_default_value_fields=True, preserving_proto_field_name=True,
-        use_integers_for_enums=False))
-
-def test_add_services(service_client : ServiceClient):
-    # should work
-    for service in [SERVICE_SVC1, SERVICE_SVC2, SERVICE_SVC3]:
-        validate_service_id(MessageToDict(
-            service_client.CreateService(Service(**service)),
-            including_default_value_fields=True, preserving_proto_field_name=True,
-            use_integers_for_enums=False))
-
-    # should work
-    validate_service_list_is_not_empty(MessageToDict(
-        service_client.GetServiceList(Empty()),
-        including_default_value_fields=True, preserving_proto_field_name=True,
-        use_integers_for_enums=False))
diff --git a/src/tester_integration/.gitlab-ci.yml b/src/tester_integration/.gitlab-ci.yml
deleted file mode 100644
index 02eccbc9bf9fc1103b373b4d062333c42f52b87e..0000000000000000000000000000000000000000
--- a/src/tester_integration/.gitlab-ci.yml
+++ /dev/null
@@ -1,78 +0,0 @@
-# Build, tag, and push the Docker images to the GitLab Docker registry
-build integ_test:
-  variables:
-    IMAGE_NAME: 'tester_integration' # name of the microservice
-    IMAGE_NAME_TEST: 'tester_integration-test' # name of the microservice
-    IMAGE_TAG: 'latest' # tag of the container image (production, development, etc)
-  stage: build
-  before_script:
-    - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
-  script:
-    - 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"
-  rules:
-    - changes:
-      - src/common/**
-      - src/context/**
-      - src/device/**
-      - src/service/**
-      - src/$IMAGE_NAME/**
-      - .gitlab-ci.yml
-
-# Pull, execute, and run unitary tests for the Docker image from the GitLab registry
-unit_test integ_test:
-  variables:
-    IMAGE_NAME: 'tester_functional' # name of the microservice
-    IMAGE_TAG: 'latest' # tag of the container image (production, development, etc)
-  stage: test
-  needs:
-    - build integ_test
-  before_script:
-    - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
-  script:
-    - docker pull "$CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG"
-  rules:
-    - changes:
-      - src/common/**
-      - src/context/**
-      - src/device/**
-      - src/service/**
-      - src/$IMAGE_NAME/**
-      - .gitlab-ci.yml
-
-# Pull, execute, and run integration tests for the Docker image from the GitLab registry
-integ_test execute:
-  variables:
-    IMAGE_NAME: 'tester_integration' # name of the microservice
-    IMAGE_NAME_TEST: 'tester_integration-test' # name of the microservice
-    IMAGE_TAG: 'latest' # tag of the container image (production, development, etc)
-  stage: integ_test
-  needs:
-    - build integ_test
-    - unit_test context
-    - unit_test device
-    - unit_test service
-    - unit_test integ_test
-    - dependencies all
-  before_script:
-    - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
-    - if docker network list | grep teraflowbridge; then echo "teraflowbridge is already created"; else docker network create -d bridge teraflowbridge; fi  
-  script:
-    - docker pull "$CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG"
-    - docker run -d -p 6379:6379 --name redis --network=teraflowbridge "redis:6.2"
-    - docker ps -a
-    - sleep 5
-    - docker ps -a
-    - docker run -d --name $IMAGE_NAME --network=teraflowbridge --env "DB_ENGINE=redis" --env "REDIS_SERVICE_HOST=$(docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' redis)" --env "REDIS_SERVICE_PORT=6379" --env "REDIS_DATABASE_ID=0" "$IMAGE_NAME:$IMAGE_TAG" bash -c "pytest --log-level=INFO --verbose common/database/tests/test_engine_redis.py tester_integration/test_context_device_service.py"
-  after_script:
-    - docker stop $IMAGE_NAME redis
-    - docker rm $IMAGE_NAME redis
-  rules:
-    - changes:
-      - src/common/**
-      - src/context/**
-      - src/device/**
-      - src/service/**
-      - src/$IMAGE_NAME/**
-      - .gitlab-ci.yml
diff --git a/src/tester_integration/Dockerfile b/src/tester_integration/Dockerfile
deleted file mode 100644
index 31ab8884b62814d43fbadaf624754e10681c8e7c..0000000000000000000000000000000000000000
--- a/src/tester_integration/Dockerfile
+++ /dev/null
@@ -1,35 +0,0 @@
-FROM python:3-slim
-
-# Install dependencies
-RUN apt-get --yes --quiet --quiet update && \
-    apt-get --yes --quiet --quiet install wget g++ && \
-    rm -rf /var/lib/apt/lists/*
-
-# Set Python to show logs as they occur
-ENV PYTHONUNBUFFERED=0
-
-# Get generic Python packages
-RUN python3 -m pip install --upgrade pip setuptools wheel pip-tools
-
-# Set working directory
-WORKDIR /var/teraflow
-
-# Create module sub-folders
-RUN mkdir -p /var/teraflow/tester_integration
-
-# Get Python packages per module
-COPY tester_integration/requirements.in tester_integration/requirements.in
-RUN pip-compile --output-file=tester_integration/requirements.txt tester_integration/requirements.in
-RUN python3 -m pip install -r tester_integration/requirements.in
-
-# Add files into working directory
-COPY common/. common
-COPY context/. context
-COPY device/. device
-COPY service/. service
-COPY tester_integration/. tester_integration
-
-# Run integration tester
-ENTRYPOINT ["pytest", "-v", "--log-level=DEBUG", \
-            "tester_integration/test_context_device_service.py" \
-]
diff --git a/src/tester_integration/__init__.py b/src/tester_integration/__init__.py
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/src/tester_integration/definitions.py b/src/tester_integration/definitions.py
deleted file mode 100644
index 2b38e74bc9d25199e4f192dbb3f093ec033fe80b..0000000000000000000000000000000000000000
--- a/src/tester_integration/definitions.py
+++ /dev/null
@@ -1,165 +0,0 @@
-import copy
-from common.database.api.context.Constants import DEFAULT_CONTEXT_ID, DEFAULT_TOPOLOGY_ID
-from common.database.api.context.service.ServiceState import ServiceState
-from common.database.api.context.service.ServiceType import ServiceType
-from common.database.api.context.topology.device.OperationalStatus import OperationalStatus
-
-# use "copy.deepcopy" to prevent propagating forced changes during tests
-dc = copy.deepcopy
-
-CONTEXT_ID = {'contextUuid': {'uuid': DEFAULT_CONTEXT_ID}}
-
-TOPOLOGY_ID = {'contextId': dc(CONTEXT_ID), 'topoId': {'uuid': DEFAULT_TOPOLOGY_ID}}
-
-DEVICE_ID_DEV1 = {'device_id': {'uuid': 'dev1'}}
-DEVICE_DEV1 = {
-    'device_id': dc(DEVICE_ID_DEV1), 'device_type': 'ROADM', 'device_config': {'device_config': '<config/>'},
-    'devOperationalStatus': OperationalStatus.ENABLED.value,
-    'endpointList' : [
-        {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV1), 'port_id': {'uuid' : 'port2'}},
-         'port_type': 'WDM'},
-        {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV1), 'port_id': {'uuid' : 'port3'}},
-         'port_type': 'WDM'},
-        {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV1), 'port_id': {'uuid' : 'port101'}},
-         'port_type': 'OCH'},
-        {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV1), 'port_id': {'uuid' : 'port102'}},
-         'port_type': 'OCH'},
-        {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV1), 'port_id': {'uuid' : 'port103'}},
-         'port_type': 'OCH'},
-    ]
-}
-
-DEVICE_ID_DEV2 = {'device_id': {'uuid': 'dev2'}}
-DEVICE_DEV2 = {
-    'device_id': dc(DEVICE_ID_DEV2), 'device_type': 'ROADM', 'device_config': {'device_config': '<config/>'},
-    'devOperationalStatus': OperationalStatus.ENABLED.value,
-    'endpointList' : [
-        {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV2), 'port_id': {'uuid' : 'port1'}},
-         'port_type': 'WDM'},
-        {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV2), 'port_id': {'uuid' : 'port3'}},
-         'port_type': 'WDM'},
-        {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV2), 'port_id': {'uuid' : 'port101'}},
-         'port_type': 'OCH'},
-        {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV2), 'port_id': {'uuid' : 'port102'}},
-         'port_type': 'OCH'},
-        {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV2), 'port_id': {'uuid' : 'port103'}},
-         'port_type': 'OCH'},
-    ]
-}
-
-DEVICE_ID_DEV3 = {'device_id': {'uuid': 'dev3'}}
-DEVICE_DEV3 = {
-    'device_id': dc(DEVICE_ID_DEV3), 'device_type': 'ROADM', 'device_config': {'device_config': '<config/>'},
-    'devOperationalStatus': OperationalStatus.ENABLED.value,
-    'endpointList' : [
-        {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV3), 'port_id': {'uuid' : 'port1'}},
-         'port_type': 'WDM'},
-        {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV3), 'port_id': {'uuid' : 'port2'}},
-         'port_type': 'WDM'},
-        {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV3), 'port_id': {'uuid' : 'port101'}},
-         'port_type': 'OCH'},
-        {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV3), 'port_id': {'uuid' : 'port102'}},
-         'port_type': 'OCH'},
-        {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV3), 'port_id': {'uuid' : 'port103'}},
-         'port_type': 'OCH'},
-    ]
-}
-
-LINK_ID_DEV1_DEV2 = {'link_id': {'uuid': 'dev1/port2 ==> dev2/port1'}}
-LINK_DEV1_DEV2 = {
-    'link_id': dc(LINK_ID_DEV1_DEV2),
-    'endpointList' : [
-        {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV1), 'port_id': {'uuid' : 'port2'}},
-        {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV2), 'port_id': {'uuid' : 'port1'}},
-    ]
-}
-
-LINK_ID_DEV1_DEV3 = {'link_id': {'uuid': 'dev1/port3 ==> dev3/port1'}}
-LINK_DEV1_DEV3 = {
-    'link_id': dc(LINK_ID_DEV1_DEV3),
-    'endpointList' : [
-        {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV1), 'port_id': {'uuid' : 'port3'}},
-        {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV3), 'port_id': {'uuid' : 'port1'}},
-    ]
-}
-
-LINK_ID_DEV2_DEV1 = {'link_id': {'uuid': 'dev2/port1 ==> dev1/port2'}}
-LINK_DEV2_DEV1 = {
-    'link_id': dc(LINK_ID_DEV2_DEV1),
-    'endpointList' : [
-        {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV2), 'port_id': {'uuid' : 'port1'}},
-        {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV1), 'port_id': {'uuid' : 'port2'}},
-    ]
-}
-
-LINK_ID_DEV2_DEV3 = {'link_id': {'uuid': 'dev2/port3 ==> dev3/port2'}}
-LINK_DEV2_DEV3 = {
-    'link_id': dc(LINK_ID_DEV2_DEV3),
-    'endpointList' : [
-        {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV2), 'port_id': {'uuid' : 'port3'}},
-        {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV3), 'port_id': {'uuid' : 'port2'}},
-    ]
-}
-
-LINK_ID_DEV3_DEV1 = {'link_id': {'uuid': 'dev3/port1 ==> dev1/port3'}}
-LINK_DEV3_DEV1 = {
-    'link_id': dc(LINK_ID_DEV3_DEV1),
-    'endpointList' : [
-        {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV3), 'port_id': {'uuid' : 'port1'}},
-        {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV1), 'port_id': {'uuid' : 'port3'}},
-    ]
-}
-
-LINK_ID_DEV3_DEV2 = {'link_id': {'uuid': 'dev3/port2 ==> dev2/port3'}}
-LINK_DEV3_DEV2 = {
-    'link_id': dc(LINK_ID_DEV3_DEV2),
-    'endpointList' : [
-        {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV3), 'port_id': {'uuid' : 'port2'}},
-        {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV2), 'port_id': {'uuid' : 'port3'}},
-    ]
-}
-
-SERVICE_ID_SVC1 = {'contextId': dc(CONTEXT_ID), 'cs_id': {'uuid': 'svc1'}}
-SERVICE_SVC1 = {
-    'cs_id': dc(SERVICE_ID_SVC1), 'serviceType': ServiceType.L3NM.value,
-    'serviceConfig': {'serviceConfig': '<config/>'}, 'serviceState': {'serviceState': ServiceState.PLANNED.value},
-    'constraint': [
-        {'constraint_type': 'latency_ms', 'constraint_value': '100'},
-        {'constraint_type': 'hops', 'constraint_value': '5'},
-    ],
-    'endpointList' : [
-        {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV1), 'port_id': {'uuid' : 'port101'}},
-        {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV2), 'port_id': {'uuid' : 'port101'}},
-        {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV3), 'port_id': {'uuid' : 'port101'}},
-    ]
-}
-
-SERVICE_ID_SVC2 = {'contextId': dc(CONTEXT_ID), 'cs_id': {'uuid': 'svc2'}}
-SERVICE_SVC2 = {
-    'cs_id': dc(SERVICE_ID_SVC2), 'serviceType': ServiceType.L3NM.value,
-    'serviceConfig': {'serviceConfig': '<config/>'}, 'serviceState': {'serviceState': ServiceState.PLANNED.value},
-    'constraint': [
-        {'constraint_type': 'latency_ms', 'constraint_value': '100'},
-        {'constraint_type': 'hops', 'constraint_value': '5'},
-    ],
-    'endpointList' : [
-        {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV1), 'port_id': {'uuid' : 'port102'}},
-        {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV2), 'port_id': {'uuid' : 'port102'}},
-        {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV3), 'port_id': {'uuid' : 'port102'}},
-    ]
-}
-
-SERVICE_ID_SVC3 = {'contextId': dc(CONTEXT_ID), 'cs_id': {'uuid': 'svc3'}}
-SERVICE_SVC3 = {
-    'cs_id': dc(SERVICE_ID_SVC3), 'serviceType': ServiceType.L3NM.value,
-    'serviceConfig': {'serviceConfig': '<config/>'}, 'serviceState': {'serviceState': ServiceState.PLANNED.value},
-    'constraint': [
-        {'constraint_type': 'latency_ms', 'constraint_value': '100'},
-        {'constraint_type': 'hops', 'constraint_value': '5'},
-    ],
-    'endpointList' : [
-        {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV1), 'port_id': {'uuid' : 'port103'}},
-        {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV2), 'port_id': {'uuid' : 'port103'}},
-        {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV3), 'port_id': {'uuid' : 'port103'}},
-    ]
-}
diff --git a/src/tester_integration/requirements.in b/src/tester_integration/requirements.in
deleted file mode 100644
index 25abdad1b5767117956a88b816399635348884c7..0000000000000000000000000000000000000000
--- a/src/tester_integration/requirements.in
+++ /dev/null
@@ -1,6 +0,0 @@
-grpcio-health-checking
-grpcio
-prometheus-client
-pytest
-pytest-benchmark
-redis
diff --git a/src/tester_integration/test_context_device_service.py b/src/tester_integration/test_context_device_service.py
deleted file mode 100644
index 2d128800c2e714de242de328c2d00a6134c378d7..0000000000000000000000000000000000000000
--- a/src/tester_integration/test_context_device_service.py
+++ /dev/null
@@ -1,138 +0,0 @@
-import logging, pytest
-from google.protobuf.json_format import MessageToDict
-from common.database.Factory import get_database, DatabaseEngineEnum
-from common.database.api.Database import Database
-from common.tests.Assertions import validate_device_id, validate_link_id, validate_service_id, \
-    validate_service_list_is_not_empty, validate_topology_has_devices, validate_topology_has_links, \
-    validate_topology_is_empty
-from context.Config import GRPC_SERVICE_PORT as GRPC_CONTEXT_SERVICE_PORT, \
-    GRPC_MAX_WORKERS as GRPC_CONTEXT_MAX_WORKERS, GRPC_GRACE_PERIOD as GRPC_CONTEXT_GRACE_PERIOD
-from context.client.ContextClient import ContextClient
-from context.proto.context_pb2 import Device, Empty, Link
-from context.service.ContextService import ContextService
-from device.Config import GRPC_SERVICE_PORT as GRPC_DEVICE_SERVICE_PORT, \
-    GRPC_MAX_WORKERS as GRPC_DEVICE_MAX_WORKERS, GRPC_GRACE_PERIOD as GRPC_DEVICE_GRACE_PERIOD
-from device.client.DeviceClient import DeviceClient
-from device.service.DeviceService import DeviceService
-from service.service.ServiceService import ServiceService
-from tester_integration.definitions import DEVICE_DEV1, DEVICE_DEV2, DEVICE_DEV3
-from tester_integration.definitions import LINK_DEV1_DEV2, LINK_DEV1_DEV3, LINK_DEV2_DEV1, LINK_DEV2_DEV3, \
-    LINK_DEV3_DEV1, LINK_DEV3_DEV2
-from tester_integration.definitions import SERVICE_SVC1, SERVICE_SVC2, SERVICE_SVC3
-from service.Config import GRPC_SERVICE_PORT as GRPC_SERVICE_SERVICE_PORT, \
-    GRPC_MAX_WORKERS as GRPC_SERVICE_MAX_WORKERS, GRPC_GRACE_PERIOD as GRPC_SERVICE_GRACE_PERIOD
-from service.client.ServiceClient import ServiceClient
-from service.proto.service_pb2 import Service
-
-PORT_CONTEXT = 10000 + GRPC_CONTEXT_SERVICE_PORT # avoid privileged ports
-PORT_DEVICE  = 10000 + GRPC_DEVICE_SERVICE_PORT  # avoid privileged ports
-PORT_SERVICE = 10000 + GRPC_SERVICE_SERVICE_PORT # avoid privileged ports
-
-LOGGER = logging.getLogger(__name__)
-LOGGER.setLevel(logging.DEBUG)
-
-@pytest.fixture(scope='session')
-def redis_database():
-    _database = get_database(engine=DatabaseEngineEnum.REDIS, REDIS_DATABASE_ID=0)
-    return _database
-
-@pytest.fixture(scope='session')
-def context_service(redis_database : Database):
-    context_database = get_database(engine=DatabaseEngineEnum.REDIS, REDIS_DATABASE_ID=0)
-    _service = ContextService(
-        context_database, port=PORT_CONTEXT, max_workers=GRPC_CONTEXT_MAX_WORKERS,
-        grace_period=GRPC_CONTEXT_GRACE_PERIOD)
-    _service.start()
-    yield _service
-    _service.stop()
-
-@pytest.fixture(scope='session')
-def context_client(context_service):
-    _client = ContextClient(address='127.0.0.1', port=PORT_CONTEXT)
-    yield _client
-    _client.close()
-
-@pytest.fixture(scope='session')
-def device_service(redis_database : Database):
-    device_database = get_database(engine=DatabaseEngineEnum.REDIS, REDIS_DATABASE_ID=0)
-    _service = DeviceService(
-        device_database, port=PORT_DEVICE, max_workers=GRPC_DEVICE_MAX_WORKERS,
-        grace_period=GRPC_DEVICE_GRACE_PERIOD)
-    _service.start()
-    yield _service
-    _service.stop()
-
-@pytest.fixture(scope='session')
-def device_client(device_service):
-    _client = DeviceClient(address='127.0.0.1', port=PORT_DEVICE)
-    yield _client
-    _client.close()
-
-@pytest.fixture(scope='session')
-def service_service(redis_database : Database):
-    service_database = get_database(engine=DatabaseEngineEnum.REDIS, REDIS_DATABASE_ID=0)
-    _service = ServiceService(
-        service_database, port=PORT_SERVICE, max_workers=GRPC_SERVICE_MAX_WORKERS,
-        grace_period=GRPC_SERVICE_GRACE_PERIOD)
-    _service.start()
-    yield _service
-    _service.stop()
-
-@pytest.fixture(scope='session')
-def service_client(service_service):
-    _client = ServiceClient(address='127.0.0.1', port=PORT_SERVICE)
-    yield _client
-    _client.close()
-
-def test_clean_database(redis_database : Database):
-    # should work
-    redis_database.clear_all()
-
-def test_get_topology_empty(context_client : ContextClient):
-    # should work
-    validate_topology_is_empty(MessageToDict(
-        context_client.GetTopology(Empty()),
-        including_default_value_fields=True, preserving_proto_field_name=True,
-        use_integers_for_enums=False))
-
-def test_add_devices(context_client : ContextClient, device_client : DeviceClient):
-    # should work
-    for device in [DEVICE_DEV1, DEVICE_DEV2, DEVICE_DEV3]:
-        validate_device_id(MessageToDict(
-            device_client.AddDevice(Device(**device)),
-            including_default_value_fields=True, preserving_proto_field_name=True,
-            use_integers_for_enums=False))
-
-    # should work
-    validate_topology_has_devices(MessageToDict(
-        context_client.GetTopology(Empty()),
-        including_default_value_fields=True, preserving_proto_field_name=True,
-        use_integers_for_enums=False))
-
-def test_add_links(context_client : ContextClient):
-    # should work
-    for link in [LINK_DEV1_DEV2, LINK_DEV1_DEV3, LINK_DEV2_DEV1, LINK_DEV2_DEV3, LINK_DEV3_DEV1, LINK_DEV3_DEV2]:
-        validate_link_id(MessageToDict(
-            context_client.AddLink(Link(**link)),
-            including_default_value_fields=True, preserving_proto_field_name=True,
-            use_integers_for_enums=False))
-
-    # should work
-    validate_topology_has_links(MessageToDict(
-        context_client.GetTopology(Empty()),
-        including_default_value_fields=True, preserving_proto_field_name=True,
-        use_integers_for_enums=False))
-
-def test_add_services(service_client : ServiceClient):
-    # should work
-    for service in [SERVICE_SVC1, SERVICE_SVC2, SERVICE_SVC3]:
-        validate_service_id(MessageToDict(
-            service_client.CreateService(Service(**service)),
-            including_default_value_fields=True, preserving_proto_field_name=True,
-            use_integers_for_enums=False))
-
-    # should work
-    validate_service_list_is_not_empty(MessageToDict(
-        service_client.GetServiceList(Empty()),
-        including_default_value_fields=True, preserving_proto_field_name=True,
-        use_integers_for_enums=False))
diff --git a/src/tests/.gitlab-ci.yml b/src/tests/.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..e663b09ec1d79a14f76d37a4ac906e534667ac26
--- /dev/null
+++ b/src/tests/.gitlab-ci.yml
@@ -0,0 +1,17 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# include the individual .gitlab-ci.yml of each integration test
+include:
+  - local: '/src/tests/ofc22_bootstrap_monitor_l3vpn/.gitlab-ci.yml'
diff --git a/src/tests/__init__.py b/src/tests/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..70a33251242c51f49140e596b8208a19dd5245f7
--- /dev/null
+++ b/src/tests/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/tests/ofc22/.gitignore b/src/tests/ofc22/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..0a3f4400d5c88b1af32c7667d69d2fdc12d5424e
--- /dev/null
+++ b/src/tests/ofc22/.gitignore
@@ -0,0 +1,2 @@
+# Add here your files containing confidential testbed details such as IP addresses, ports, usernames, passwords, etc.
+descriptors_real.json
diff --git a/src/tests/ofc22/README.md b/src/tests/ofc22/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..07fd4f72f494638744e7bc35eb207157bbb8cc1d
--- /dev/null
+++ b/src/tests/ofc22/README.md
@@ -0,0 +1,97 @@
+# OFC'22 Demo - Bootstrap devices, Monitor device Endpoints, Manage L3VPN Services
+This functional test reproduces the live demonstration "Demonstration of Zero-touch Device and L3-VPN Service
+Management Using the TeraFlow Cloud-native SDN Controller" carried out at
+[OFC'22](https://www.ofcconference.org/en-us/home/program-speakers/demo/).
+
+## Functional test folder
+This functional test can be found in folder `./src/tests/ofc22/`. A convenience alias `./ofc22/` pointing to that folder has been defined.
+
+## Execute with real devices
+This functional test is designed to operate both with real and emulated devices.
+By default, emulated devices are used; however, if you have access to real devices, you can create/modify the files `./ofc22/tests/Objects.py` and `./ofc22/tests/Credentials.py` to point to your devices, and map to your network topology.
+Note that the default scenario assumes devices R2 and R4 are always emulated, while devices R1, R3, and O1 can be configured as emulated or real devices.
+
+__Important:__ The OpenConfigDriver, the P4Driver, and the TrandportApiDriver have to be considered as experimental. The configuration and monitoring capabilities they support are limited or partially implemented. Use them with care.
+
+## Deployment
+To run this functional test, it is assumed you have deployed a Kubernetes-based environment as described in [Wiki: Installing Kubernetes on your Linux machine](https://gitlab.com/teraflow-h2020/controller/-/wikis/Installing-Kubernetes-on-your-Linux-machine).
+
+After installing Kubernetes, you can run it to deploy the appropriate components. Feel free to adapt it your particular case following the instructions described in [Wiki: Deploying a TeraFlow OS test instance](https://gitlab.com/teraflow-h2020/controller/-/wikis/Deploying-a-TeraFlow-OS-test-instance).
+
+__Important:__
+- The `./ofc22/deploy_in_kubernetes.sh` assumes you have installed the appropriate development dependencies using the `install_development_dependencies.sh` script.
+- Before running the scripts in this folder, remember to update the environment variable K8S_HOSTNAME to point to the Kubernetes node you will be using as described in [Wiki: Deploying a TeraFlow OS test instance](https://gitlab.com/teraflow-h2020/controller/-/wikis/Deploying-a-TeraFlow-OS-test-instance).
+
+For your convenience, the configuration s sript `./ofc22/deploy_in_kubernetes.sh` has been already defined. The script will take some minutes to download the dependencies, build the micro-services, deploy them, and leave them ready for operation. The deployment will finish with a report of the items that have been created.
+
+## Access to the WebUI and Dashboard
+When the deployment completes, you can connect to the TeraFlow OS WebUI and Dashboards as described in [Wiki: Using the WebUI](https://gitlab.com/teraflow-h2020/controller/-/wikis/Using-the-WebUI), or directly navigating to `http://[your-node-ip]:30800` for the WebUI and `http://[your-node-ip]:30300` for the Grafana Dashboard.
+
+Notes:
+- the default credentials for the Grafana Dashboiard is user/pass: `admin`/`admin123+`.
+- in Grafana, you can find the "L3-Monitorng" in the "Starred dashboards" section.
+
+## Test execution
+To execute this functional test, four main steps needs to be carried out:
+1. Device bootstrapping
+2. L3VPN Service creation
+3. L3VPN Service removal
+4. Cleanup
+
+Upon the execution of each test progresses, a report will be generated indicating PASSED / FAILED / SKIPPED. If there is some error during the execution, you should see a detailed report on the error. See the troubleshooting section in that case.
+
+Feel free to check the logs of the different components using the appropriate `ofc22/show_logs_[component].sh` scripts after you execute each step.
+
+### 1. Device bootstrapping
+
+This step configures some basic entities (Context and Topology), the devices, and the links in the topology. The expected results are:
+- The devices to be incorporated into the Topology.
+- The devices to be pre-configured and initialized as ENABLED by the Automation component.
+- The monitoring for the device ports (named as endpoints in TeraFlow OS) to be activated and data collection to automatically start.
+- The links to be added to the topology.
+
+To run this step, execute the following script:
+`./ofc22/run_test_01_bootstrap.sh`
+
+When the script finishes, check in the Grafana L3-Monitoring Dashboard and you should see the monitoring data being plotted and updated every 5 seconds (by default). Given that there is no service configured, you should see a 0-valued flat plot.
+
+In the WebUI, select the "admin" Context. In the "Devices" tab you should see that 5 different emulated devices have been created and activated: 4 packet routers, and 1 optical line system controller. Besides, in the "Services" tab you should see that there is no service created. Note here that the emulated devices produce synthetic randomly-generated data and do not care about the services configured.
+
+### 2. L3VPN Service creation
+
+This step configures a new service emulating the request an OSM WIM would make by means of a Mock OSM instance.
+
+To run this step, execute the following script:
+`./ofc22/run_test_02_create_service.sh`
+
+When the script finishes, check the WebUI "Services" tab. You should see that two services have been created, one for the optical layer and another for the packet layer. Besides, you can check the "Devices" tab to see the configuration rules that have been configured in each device. In the Grafana Dashboard, given that there is now a service configured, you should see the plots with the monitored data for the device. By default, device R1-INF is selected.
+
+### 3. L3VPN Service removal
+
+This step deconfigures the previously created services emulating the request an OSM WIM would make by means of a Mock OSM instance.
+
+To run this step, execute the following script:
+`./ofc22/run_test_03_delete_service.sh`
+
+When the script finishes, check the WebUI "Services" tab. You should see that the two services have been removed. Besides, in the "Devices" tab you can see that the appropriate configuration rules have been deconfigured. In the Grafana Dashboard, given that there is no service configured, you should see a 0-valued flat plot again.
+
+### 4. Cleanup
+
+This last step just performs a cleanup of the scenario removing all the TeraFlow OS entities for completeness.
+
+To run this step, execute the following script:
+`./ofc22/run_test_04_cleanup.sh`
+
+When the script finishes, check the WebUI "Devices" tab, you should see that the devices have been removed. Besides, in the "Services" tab you can see that the "admin" Context has no services given that that context has been removed.
+
+## Troubleshooting
+
+Different scripts are provided to help in troubleshooting issues in the execution of the test. These scripts are:
+- `./ofc22/show_deployment.sh`: this script reports the items belonging to this deployment. Use it to validate that all the pods, deployments and replica sets are ready and have a state of "running"; and the services are deployed and have appropriate IP addresses and ports.
+- `ofc22/show_logs_automation.sh`: this script reports the logs for the automation component.
+- `ofc22/show_logs_compute.sh`: this script reports the logs for the compute component.
+- `ofc22/show_logs_context.sh`: this script reports the logs for the context component.
+- `ofc22/show_logs_device.sh`: this script reports the logs for the device component.
+- `ofc22/show_logs_monitoring.sh`: this script reports the logs for the monitoring component.
+- `ofc22/show_logs_service.sh`: this script reports the logs for the service component.
+- `ofc22/show_logs_webui.sh`: this script reports the logs for the webui component.
diff --git a/src/tests/ofc22/__init__.py b/src/tests/ofc22/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..70a33251242c51f49140e596b8208a19dd5245f7
--- /dev/null
+++ b/src/tests/ofc22/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/tests/ofc22/deploy_in_kubernetes.sh b/src/tests/ofc22/deploy_in_kubernetes.sh
new file mode 100755
index 0000000000000000000000000000000000000000..1b725e5d629831d3c80be8ce8a09925e4bcc9c8e
--- /dev/null
+++ b/src/tests/ofc22/deploy_in_kubernetes.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+# OFC 22 deployment settings
+
+export REGISTRY_IMAGE=""
+export COMPONENTS="context device service compute webui automation monitoring"
+export IMAGE_TAG="ofc22"
+export K8S_NAMESPACE="ofc22"
+export K8S_HOSTNAME="kubernetes-master"
+export EXTRA_MANIFESTS="./ofc22/expose_services.yaml"
+export GRAFANA_PASSWORD="admin123+"
+
+./deploy_in_kubernetes.sh
diff --git a/src/tests/ofc22/descriptors_emulated.json b/src/tests/ofc22/descriptors_emulated.json
new file mode 100644
index 0000000000000000000000000000000000000000..3905fbc59a538185c1baa7d899e48a838864790d
--- /dev/null
+++ b/src/tests/ofc22/descriptors_emulated.json
@@ -0,0 +1,108 @@
+{
+    "contexts": [
+        {
+            "context_id": {"context_uuid": {"uuid": "admin"}},
+            "topology_ids": [],
+            "service_ids": []
+        }
+    ],
+    "topologies": [
+        {
+            "topology_id": {"topology_uuid": {"uuid": "admin"}, "context_id": {"context_uuid": {"uuid": "admin"}}},
+            "device_ids": [],
+            "link_ids": []
+        }
+    ],
+    "devices": [
+        {
+            "device_id": {"device_uuid": {"uuid": "R1-INF"}},
+            "device_type": "emu-packet-router",
+            "device_config": {"config_rules": [
+                {"action": 1, "resource_key": "_connect/address", "resource_value": "127.0.0.1"},
+                {"action": 1, "resource_key": "_connect/port", "resource_value": "0"},
+                {"action": 1, "resource_key": "_connect/settings", "resource_value": "{\"endpoints\": [{\"sample_types\": [], \"type\": \"optical\", \"uuid\": \"13/0/0\"}, {\"sample_types\": [101, 102, 201, 202], \"type\": \"copper\", \"uuid\": \"13/1/2\"}]}"}
+            ]},
+            "device_operational_status": 1,
+            "device_drivers": [0],
+            "device_endpoints": []
+        },
+        {
+            "device_id": {"device_uuid": {"uuid": "R2-EMU"}},
+            "device_type": "emu-packet-router",
+            "device_config": {"config_rules": [
+                {"action": 1, "resource_key": "_connect/address", "resource_value": "127.0.0.1"},
+                {"action": 1, "resource_key": "_connect/port", "resource_value": "0"},
+                {"action": 1, "resource_key": "_connect/settings", "resource_value": "{\"endpoints\": [{\"sample_types\": [], \"type\": \"optical\", \"uuid\": \"13/0/0\"}, {\"sample_types\": [101, 102, 201, 202], \"type\": \"copper\", \"uuid\": \"13/1/2\"}]}"}
+            ]},
+            "device_operational_status": 1,
+            "device_drivers": [0],
+            "device_endpoints": []
+        },
+        {
+            "device_id": {"device_uuid": {"uuid": "R3-INF"}},
+            "device_type": "emu-packet-router",
+            "device_config": {"config_rules": [
+                {"action": 1, "resource_key": "_connect/address", "resource_value": "127.0.0.1"},
+                {"action": 1, "resource_key": "_connect/port", "resource_value": "0"},
+                {"action": 1, "resource_key": "_connect/settings", "resource_value": "{\"endpoints\": [{\"sample_types\": [], \"type\": \"optical\", \"uuid\": \"13/0/0\"}, {\"sample_types\": [101, 102, 201, 202], \"type\": \"copper\", \"uuid\": \"13/1/2\"}]}"}
+            ]},
+            "device_operational_status": 1,
+            "device_drivers": [0],
+            "device_endpoints": []
+        },
+        {
+            "device_id": {"device_uuid": {"uuid": "R4-EMU"}},
+            "device_type": "emu-packet-router",
+            "device_config": {"config_rules": [
+                {"action": 1, "resource_key": "_connect/address", "resource_value": "127.0.0.1"},
+                {"action": 1, "resource_key": "_connect/port", "resource_value": "0"},
+                {"action": 1, "resource_key": "_connect/settings", "resource_value": "{\"endpoints\": [{\"sample_types\": [], \"type\": \"optical\", \"uuid\": \"13/0/0\"}, {\"sample_types\": [101, 102, 201, 202], \"type\": \"copper\", \"uuid\": \"13/1/2\"}]}"}
+            ]},
+            "device_operational_status": 1,
+            "device_drivers": [0],
+            "device_endpoints": []
+        },
+        {
+            "device_id": {"device_uuid": {"uuid": "O1-OLS"}},
+            "device_type": "emu-optical-line-system",
+            "device_config": {"config_rules": [
+                {"action": 1, "resource_key": "_connect/address", "resource_value": "127.0.0.1"},
+                {"action": 1, "resource_key": "_connect/port", "resource_value": "0"},
+                {"action": 1, "resource_key": "_connect/settings", "resource_value": "{\"endpoints\": [{\"sample_types\": [], \"type\": \"optical\", \"uuid\": \"aade6001-f00b-5e2f-a357-6a0a9d3de870\"}, {\"sample_types\": [], \"type\": \"optical\", \"uuid\": \"eb287d83-f05e-53ec-ab5a-adf6bd2b5418\"}, {\"sample_types\": [], \"type\": \"optical\", \"uuid\": \"0ef74f99-1acc-57bd-ab9d-4b958b06c513\"}, {\"sample_types\": [], \"type\": \"optical\", \"uuid\": \"50296d99-58cc-5ce7-82f5-fc8ee4eec2ec\"}]}"}
+            ]},
+            "device_operational_status": 1,
+            "device_drivers": [0],
+            "device_endpoints": []
+        }
+    ],
+    "links": [
+        {
+            "link_id": {"link_uuid": {"uuid": "R1-INF/13/0/0==O1-OLS/aade6001-f00b-5e2f-a357-6a0a9d3de870"}},
+            "link_endpoint_ids": [
+                {"device_id": {"device_uuid": {"uuid": "R1-INF"}}, "endpoint_uuid": {"uuid": "13/0/0"}},
+                {"device_id": {"device_uuid": {"uuid": "O1-OLS"}}, "endpoint_uuid": {"uuid": "aade6001-f00b-5e2f-a357-6a0a9d3de870"}}
+            ]
+        },
+        {
+            "link_id": {"link_uuid": {"uuid": "R2-EMU/13/0/0==O1-OLS/eb287d83-f05e-53ec-ab5a-adf6bd2b5418"}},
+            "link_endpoint_ids": [
+                {"device_id": {"device_uuid": {"uuid": "R2-EMU"}}, "endpoint_uuid": {"uuid": "13/0/0"}},
+                {"device_id": {"device_uuid": {"uuid": "O1-OLS"}}, "endpoint_uuid": {"uuid": "eb287d83-f05e-53ec-ab5a-adf6bd2b5418"}}
+            ]
+        },
+        {
+            "link_id": {"link_uuid": {"uuid": "R3-INF/13/0/0==O1-OLS/0ef74f99-1acc-57bd-ab9d-4b958b06c513"}},
+            "link_endpoint_ids": [
+                {"device_id": {"device_uuid": {"uuid": "R3-INF"}}, "endpoint_uuid": {"uuid": "13/0/0"}},
+                {"device_id": {"device_uuid": {"uuid": "O1-OLS"}}, "endpoint_uuid": {"uuid": "0ef74f99-1acc-57bd-ab9d-4b958b06c513"}}
+            ]
+        },
+        {
+            "link_id": {"link_uuid": {"uuid": "R4-EMU/13/0/0==O1-OLS/50296d99-58cc-5ce7-82f5-fc8ee4eec2ec"}},
+            "link_endpoint_ids": [
+                {"device_id": {"device_uuid": {"uuid": "R4-EMU"}}, "endpoint_uuid": {"uuid": "13/0/0"}},
+                {"device_id": {"device_uuid": {"uuid": "O1-OLS"}}, "endpoint_uuid": {"uuid": "50296d99-58cc-5ce7-82f5-fc8ee4eec2ec"}}
+            ]
+        }
+    ]
+}
diff --git a/src/tests/ofc22/expose_services.yaml b/src/tests/ofc22/expose_services.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..d514383615e7b9dca20f22dbb6ef3438457953cc
--- /dev/null
+++ b/src/tests/ofc22/expose_services.yaml
@@ -0,0 +1,112 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+---
+apiVersion: v1
+kind: Service
+metadata:
+  name: contextservice-public
+  labels:
+    app: contextservice
+spec:
+  type: NodePort
+  selector:
+    app: contextservice
+  ports:
+  - name: grpc
+    protocol: TCP
+    port: 1010
+    targetPort: 1010
+    nodePort: 30101
+  - name: redis
+    protocol: TCP
+    port: 6379
+    targetPort: 6379
+    nodePort: 30637
+  - name: http
+    protocol: TCP
+    port: 8080
+    targetPort: 8080
+    nodePort: 31808
+---
+apiVersion: v1
+kind: Service
+metadata:
+  name: deviceservice-public
+  labels:
+    app: deviceservice
+spec:
+  type: NodePort
+  selector:
+    app: deviceservice
+  ports:
+  - name: grpc
+    protocol: TCP
+    port: 2020
+    targetPort: 2020
+    nodePort: 30202
+---
+apiVersion: v1
+kind: Service
+metadata:
+  name: monitoringservice-public
+  labels:
+    app: monitoringservice
+spec:
+  type: NodePort
+  selector:
+    app: monitoringservice
+  ports:
+  - name: influx
+    protocol: TCP
+    port: 8086
+    targetPort: 8086
+    nodePort: 30886
+---
+apiVersion: v1
+kind: Service
+metadata:
+  name: computeservice-public
+spec:
+  type: NodePort
+  selector:
+    app: computeservice
+  ports:
+  - name: http
+    protocol: TCP
+    port: 8080
+    targetPort: 8080
+    nodePort: 30808
+---
+apiVersion: v1
+kind: Service
+metadata:
+  name: webuiservice-public
+  labels:
+    app: webuiservice
+spec:
+  type: NodePort
+  selector:
+    app: webuiservice
+  ports:
+  - name: http
+    protocol: TCP
+    port: 8004
+    targetPort: 8004
+    nodePort: 30800
+  - name: grafana
+    protocol: TCP
+    port: 3000
+    targetPort: 3000
+    nodePort: 30300
diff --git a/src/tests/ofc22/redeploy_webui.sh b/src/tests/ofc22/redeploy_webui.sh
new file mode 100755
index 0000000000000000000000000000000000000000..975f84a9d3b75e00a809acd336d844973cb26897
--- /dev/null
+++ b/src/tests/ofc22/redeploy_webui.sh
@@ -0,0 +1,59 @@
+#!/bin/bash
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+export COMPONENT="webui"
+export IMAGE_TAG="ofc22"
+export K8S_NAMESPACE="ofc22"
+export K8S_HOSTNAME="kubernetes-master"
+export GRAFANA_PASSWORD="admin123+"
+
+# Constants
+TMP_FOLDER="./tmp"
+
+# Create a tmp folder for files modified during the deployment
+TMP_MANIFESTS_FOLDER="$TMP_FOLDER/manifests"
+mkdir -p $TMP_MANIFESTS_FOLDER
+TMP_LOGS_FOLDER="$TMP_FOLDER/logs"
+mkdir -p $TMP_LOGS_FOLDER
+
+echo "Processing '$COMPONENT' component..."
+IMAGE_NAME="$COMPONENT:$IMAGE_TAG"
+
+echo "  Building Docker image..."
+BUILD_LOG="$TMP_LOGS_FOLDER/build_${COMPONENT}.log"
+docker build -t "$IMAGE_NAME" -f ./src/"$COMPONENT"/Dockerfile ./src/ > "$BUILD_LOG"
+
+sleep 1
+
+echo "  Deploying '$COMPONENT' component to Kubernetes..."
+kubectl --namespace $K8S_NAMESPACE scale deployment --replicas=0 ${COMPONENT}service
+kubectl --namespace $K8S_NAMESPACE scale deployment --replicas=1 ${COMPONENT}service
+printf "\n"
+
+sleep 1
+
+echo "Waiting for '$COMPONENT' component..."
+kubectl wait --namespace $K8S_NAMESPACE --for='condition=available' --timeout=300s deployment/${COMPONENT}service
+printf "\n"
+
+echo "Configuring DataStores and Dashboards..."
+./configure_dashboards.sh
+printf "\n\n"
+
+echo "Reporting Deployment..."
+kubectl --namespace $K8S_NAMESPACE get all
+printf "\n"
+
+echo "Done!"
diff --git a/src/tests/ofc22/run_test_01_bootstrap.sh b/src/tests/ofc22/run_test_01_bootstrap.sh
new file mode 100755
index 0000000000000000000000000000000000000000..634fed02dd71464c6878f0c96fb67cf3067148e2
--- /dev/null
+++ b/src/tests/ofc22/run_test_01_bootstrap.sh
@@ -0,0 +1,51 @@
+#!/bin/bash
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+PROJECTDIR=`pwd`
+
+cd $PROJECTDIR/src
+RCFILE=$PROJECTDIR/coverage/.coveragerc
+COVERAGEFILE=$PROJECTDIR/coverage/.coverage
+
+# Configure the correct folder on the .coveragerc file
+cat $PROJECTDIR/coverage/.coveragerc.template | sed s+~/teraflow/controller+$PROJECTDIR+g > $RCFILE
+
+# Destroy old coverage file
+rm -f $COVERAGEFILE
+
+# Set the name of the Kubernetes namespace and hostname to use.
+K8S_NAMESPACE="ofc22"
+# K8S_HOSTNAME="kubernetes-master"
+# dynamically gets the name of the K8s master node
+K8S_HOSTNAME=`kubectl get nodes --selector=node-role.kubernetes.io/master | tr -s " " | cut -f1 -d" " | sed -n '2 p'`
+
+# Flush Context database
+kubectl --namespace $K8S_NAMESPACE exec -it deployment/contextservice --container redis -- redis-cli FLUSHALL
+
+export CONTEXTSERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}')
+export CONTEXTSERVICE_SERVICE_PORT_GRPC=$(kubectl get service contextservice-public --namespace $K8S_NAMESPACE -o 'jsonpath={.spec.ports[?(@.port==1010)].nodePort}')
+export DEVICESERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}')
+export DEVICESERVICE_SERVICE_PORT_GRPC=$(kubectl get service deviceservice-public --namespace $K8S_NAMESPACE -o 'jsonpath={.spec.ports[?(@.port==2020)].nodePort}')
+export COMPUTESERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}')
+export COMPUTESERVICE_SERVICE_PORT_HTTP=$(kubectl get service computeservice-public --namespace $K8S_NAMESPACE -o 'jsonpath={.spec.ports[?(@.port==8080)].nodePort}')
+
+# Useful flags for pytest:
+#-o log_cli=true -o log_file=device.log -o log_file_level=DEBUG
+
+# Run functional test and analyze coverage of code at same time
+
+coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \
+    tests/ofc22/tests/test_functional_bootstrap.py
diff --git a/src/tests/ofc22/run_test_02_create_service.sh b/src/tests/ofc22/run_test_02_create_service.sh
new file mode 100755
index 0000000000000000000000000000000000000000..5498f91f2a3186ca694443dfc047760464ad2663
--- /dev/null
+++ b/src/tests/ofc22/run_test_02_create_service.sh
@@ -0,0 +1,41 @@
+#!/bin/bash
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+PROJECTDIR=`pwd`
+
+cd $PROJECTDIR/src
+RCFILE=$PROJECTDIR/coverage/.coveragerc
+COVERAGEFILE=$PROJECTDIR/coverage/.coverage
+
+# Set the name of the Kubernetes namespace and hostname to use.
+K8S_NAMESPACE="ofc22"
+# dynamically gets the name of the K8s master node
+K8S_HOSTNAME=`kubectl get nodes --selector=node-role.kubernetes.io/master | tr -s " " | cut -f1 -d" " | sed -n '2 p'`
+
+export CONTEXTSERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}')
+export CONTEXTSERVICE_SERVICE_PORT_GRPC=$(kubectl get service contextservice-public --namespace $K8S_NAMESPACE -o 'jsonpath={.spec.ports[?(@.port==1010)].nodePort}')
+export DEVICESERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}')
+export DEVICESERVICE_SERVICE_PORT_GRPC=$(kubectl get service deviceservice-public --namespace $K8S_NAMESPACE -o 'jsonpath={.spec.ports[?(@.port==2020)].nodePort}')
+export COMPUTESERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}')
+export COMPUTESERVICE_SERVICE_PORT_HTTP=$(kubectl get service computeservice-public --namespace $K8S_NAMESPACE -o 'jsonpath={.spec.ports[?(@.port==8080)].nodePort}')
+
+# Useful flags for pytest:
+#-o log_cli=true -o log_file=device.log -o log_file_level=DEBUG
+
+# Run functional test and analyze coverage of code at same time
+
+coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \
+    tests/ofc22/tests/test_functional_create_service.py
diff --git a/src/tests/ofc22/run_test_03_delete_service.sh b/src/tests/ofc22/run_test_03_delete_service.sh
new file mode 100755
index 0000000000000000000000000000000000000000..7a8e3a662610042fc3aaf603f8944e48d5573dd2
--- /dev/null
+++ b/src/tests/ofc22/run_test_03_delete_service.sh
@@ -0,0 +1,41 @@
+#!/bin/bash
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+PROJECTDIR=`pwd`
+
+cd $PROJECTDIR/src
+RCFILE=$PROJECTDIR/coverage/.coveragerc
+COVERAGEFILE=$PROJECTDIR/coverage/.coverage
+
+# Set the name of the Kubernetes namespace and hostname to use.
+K8S_NAMESPACE="ofc22"
+# dynamically gets the name of the K8s master node
+K8S_HOSTNAME=`kubectl get nodes --selector=node-role.kubernetes.io/master | tr -s " " | cut -f1 -d" " | sed -n '2 p'`
+
+export CONTEXTSERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}')
+export CONTEXTSERVICE_SERVICE_PORT_GRPC=$(kubectl get service contextservice-public --namespace $K8S_NAMESPACE -o 'jsonpath={.spec.ports[?(@.port==1010)].nodePort}')
+export DEVICESERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}')
+export DEVICESERVICE_SERVICE_PORT_GRPC=$(kubectl get service deviceservice-public --namespace $K8S_NAMESPACE -o 'jsonpath={.spec.ports[?(@.port==2020)].nodePort}')
+export COMPUTESERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}')
+export COMPUTESERVICE_SERVICE_PORT_HTTP=$(kubectl get service computeservice-public --namespace $K8S_NAMESPACE -o 'jsonpath={.spec.ports[?(@.port==8080)].nodePort}')
+
+# Useful flags for pytest:
+#-o log_cli=true -o log_file=device.log -o log_file_level=DEBUG
+
+# Run functional test and analyze coverage of code at same time
+
+coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \
+    tests/ofc22/tests/test_functional_delete_service.py
diff --git a/src/tests/ofc22/run_test_04_cleanup.sh b/src/tests/ofc22/run_test_04_cleanup.sh
new file mode 100755
index 0000000000000000000000000000000000000000..5995a804f84db1d18f7e1ed18676bc575af7e80b
--- /dev/null
+++ b/src/tests/ofc22/run_test_04_cleanup.sh
@@ -0,0 +1,41 @@
+#!/bin/bash
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+PROJECTDIR=`pwd`
+
+cd $PROJECTDIR/src
+RCFILE=$PROJECTDIR/coverage/.coveragerc
+COVERAGEFILE=$PROJECTDIR/coverage/.coverage
+
+# Set the name of the Kubernetes namespace and hostname to use.
+K8S_NAMESPACE="ofc22"
+# dynamically gets the name of the K8s master node
+K8S_HOSTNAME=`kubectl get nodes --selector=node-role.kubernetes.io/master | tr -s " " | cut -f1 -d" " | sed -n '2 p'`
+
+export CONTEXTSERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}')
+export CONTEXTSERVICE_SERVICE_PORT_GRPC=$(kubectl get service contextservice-public --namespace $K8S_NAMESPACE -o 'jsonpath={.spec.ports[?(@.port==1010)].nodePort}')
+export DEVICESERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}')
+export DEVICESERVICE_SERVICE_PORT_GRPC=$(kubectl get service deviceservice-public --namespace $K8S_NAMESPACE -o 'jsonpath={.spec.ports[?(@.port==2020)].nodePort}')
+export COMPUTESERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}')
+export COMPUTESERVICE_SERVICE_PORT_HTTP=$(kubectl get service computeservice-public --namespace $K8S_NAMESPACE -o 'jsonpath={.spec.ports[?(@.port==8080)].nodePort}')
+
+# Useful flags for pytest:
+#-o log_cli=true -o log_file=device.log -o log_file_level=DEBUG
+
+# Run functional test and analyze coverage of code at same time
+
+coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \
+    tests/ofc22/tests/test_functional_cleanup.py
diff --git a/src/tests/ofc22/show_deploy.sh b/src/tests/ofc22/show_deploy.sh
new file mode 100755
index 0000000000000000000000000000000000000000..58fce79e32819478627c87b8a5fb8ea7701db2d7
--- /dev/null
+++ b/src/tests/ofc22/show_deploy.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+K8S_NAMESPACE="ofc22"
+kubectl --namespace $K8S_NAMESPACE get all
diff --git a/src/tests/ofc22/show_logs_automation.sh b/src/tests/ofc22/show_logs_automation.sh
new file mode 100755
index 0000000000000000000000000000000000000000..778cfaa942bcb36a81ccd571afe0f024c32d373d
--- /dev/null
+++ b/src/tests/ofc22/show_logs_automation.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+K8S_NAMESPACE="ofc22"
+kubectl --namespace $K8S_NAMESPACE logs deployment/automationservice
diff --git a/src/tests/ofc22/show_logs_compute.sh b/src/tests/ofc22/show_logs_compute.sh
new file mode 100755
index 0000000000000000000000000000000000000000..cafde447ace44cc71fc75d27af2a50100f155681
--- /dev/null
+++ b/src/tests/ofc22/show_logs_compute.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+K8S_NAMESPACE="ofc22"
+kubectl --namespace $K8S_NAMESPACE logs deployment/computeservice
diff --git a/src/tests/ofc22/show_logs_context.sh b/src/tests/ofc22/show_logs_context.sh
new file mode 100755
index 0000000000000000000000000000000000000000..6d5b77fa9e0565e6df66856829644f31f55a4197
--- /dev/null
+++ b/src/tests/ofc22/show_logs_context.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+K8S_NAMESPACE="ofc22"
+kubectl --namespace $K8S_NAMESPACE logs deployment/contextservice -c server
diff --git a/src/tests/ofc22/show_logs_device.sh b/src/tests/ofc22/show_logs_device.sh
new file mode 100755
index 0000000000000000000000000000000000000000..9d976755a959dd8674a5cfe4fffb7104c27e8521
--- /dev/null
+++ b/src/tests/ofc22/show_logs_device.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+K8S_NAMESPACE="ofc22"
+kubectl --namespace $K8S_NAMESPACE logs deployment/deviceservice
diff --git a/src/tests/ofc22/show_logs_monitoring.sh b/src/tests/ofc22/show_logs_monitoring.sh
new file mode 100755
index 0000000000000000000000000000000000000000..3dd7522fa57eca242225d29571956923075e14d8
--- /dev/null
+++ b/src/tests/ofc22/show_logs_monitoring.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+K8S_NAMESPACE="ofc22"
+kubectl --namespace $K8S_NAMESPACE logs deployment/monitoringservice -c server
diff --git a/src/tests/ofc22/show_logs_service.sh b/src/tests/ofc22/show_logs_service.sh
new file mode 100755
index 0000000000000000000000000000000000000000..2589a3cfe16f4383904c342366f3efc01c42d470
--- /dev/null
+++ b/src/tests/ofc22/show_logs_service.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+K8S_NAMESPACE="ofc22"
+kubectl --namespace $K8S_NAMESPACE logs deployment/serviceservice
diff --git a/src/tests/ofc22/show_logs_webui.sh b/src/tests/ofc22/show_logs_webui.sh
new file mode 100755
index 0000000000000000000000000000000000000000..ecf4f3f6fc22dd71eef2a6db2b5ac18f54ccca35
--- /dev/null
+++ b/src/tests/ofc22/show_logs_webui.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+K8S_NAMESPACE="ofc22"
+kubectl --namespace $K8S_NAMESPACE logs deployment/webuiservice -c server
diff --git a/src/tests/ofc22/tests/.gitignore b/src/tests/ofc22/tests/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..76cb708d1b532c9b69166e55f36bcb912fd5e370
--- /dev/null
+++ b/src/tests/ofc22/tests/.gitignore
@@ -0,0 +1,2 @@
+# Add here your files containing confidential testbed details such as IP addresses, ports, usernames, passwords, etc.
+Credentials.py
diff --git a/src/tests/ofc22/tests/BuildDescriptors.py b/src/tests/ofc22/tests/BuildDescriptors.py
new file mode 100644
index 0000000000000000000000000000000000000000..5c5419190487eb5089e4a30f523dca43fa3870f2
--- /dev/null
+++ b/src/tests/ofc22/tests/BuildDescriptors.py
@@ -0,0 +1,35 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import copy, json, sys
+from .Objects import CONTEXTS, DEVICES, LINKS, TOPOLOGIES
+
+def main():
+    with open('tests/ofc22/descriptors_emulated.json', 'w', encoding='UTF-8') as f:
+        devices = []
+        for device,connect_rules in DEVICES:
+            device = copy.deepcopy(device)
+            device['device_config']['config_rules'].extend(connect_rules)
+            devices.append(device)
+
+        f.write(json.dumps({
+            'contexts': CONTEXTS,
+            'topologies': TOPOLOGIES,
+            'devices': devices,
+            'links': LINKS
+        }))
+    return 0
+
+if __name__ == '__main__':
+    sys.exit(main())
diff --git a/src/tests/ofc22/tests/LoadDescriptors.py b/src/tests/ofc22/tests/LoadDescriptors.py
new file mode 100644
index 0000000000000000000000000000000000000000..4d3af78f5c9a3fd9b09d94f24bb8aaec48af6b7a
--- /dev/null
+++ b/src/tests/ofc22/tests/LoadDescriptors.py
@@ -0,0 +1,40 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import json, logging, sys
+from common.Settings import get_setting
+from context.client.ContextClient import ContextClient
+from context.proto.context_pb2 import Context, Device, Link, Topology
+from device.client.DeviceClient import DeviceClient
+
+LOGGER = logging.getLogger(__name__)
+LOGGER.setLevel(logging.DEBUG)
+
+def main():
+    context_client = ContextClient(
+        get_setting('CONTEXTSERVICE_SERVICE_HOST'), get_setting('CONTEXTSERVICE_SERVICE_PORT_GRPC'))
+    device_client  = DeviceClient(
+        get_setting('DEVICESERVICE_SERVICE_HOST'), get_setting('DEVICESERVICE_SERVICE_PORT_GRPC'))
+
+    with open('tests/ofc22/descriptors.json', 'r', encoding='UTF-8') as f:
+        descriptors = json.loads(f.read())
+
+    for context  in descriptors['contexts'  ]: context_client.SetContext (Context (**context ))
+    for topology in descriptors['topologies']: context_client.SetTopology(Topology(**topology))
+    for device   in descriptors['devices'   ]: device_client .AddDevice  (Device  (**device  ))
+    for link     in descriptors['links'     ]: context_client.SetLink    (Link    (**link    ))
+    return 0
+
+if __name__ == '__main__':
+    sys.exit(main())
diff --git a/src/tests/ofc22/tests/Objects.py b/src/tests/ofc22/tests/Objects.py
new file mode 100644
index 0000000000000000000000000000000000000000..fd48210fa4c4ed507e090c8d225aa3755805446f
--- /dev/null
+++ b/src/tests/ofc22/tests/Objects.py
@@ -0,0 +1,240 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from typing import Dict, List, Tuple
+from common.Constants import DEFAULT_CONTEXT_UUID, DEFAULT_TOPOLOGY_UUID
+from common.tools.object_factory.Context import json_context, json_context_id
+from common.tools.object_factory.Device import (
+    json_device_connect_rules, json_device_emulated_connect_rules, json_device_emulated_packet_router_disabled,
+    json_device_emulated_tapi_disabled, json_device_id, json_device_packetrouter_disabled, json_device_tapi_disabled)
+from common.tools.object_factory.EndPoint import json_endpoint, json_endpoint_id
+from common.tools.object_factory.Link import json_link, json_link_id
+from common.tools.object_factory.Topology import json_topology, json_topology_id
+from context.proto.kpi_sample_types_pb2 import KpiSampleType
+
+# ----- Context --------------------------------------------------------------------------------------------------------
+CONTEXT_ID = json_context_id(DEFAULT_CONTEXT_UUID)
+CONTEXT    = json_context(DEFAULT_CONTEXT_UUID)
+
+# ----- Topology -------------------------------------------------------------------------------------------------------
+TOPOLOGY_ID = json_topology_id(DEFAULT_TOPOLOGY_UUID, context_id=CONTEXT_ID)
+TOPOLOGY    = json_topology(DEFAULT_TOPOLOGY_UUID, context_id=CONTEXT_ID)
+
+# ----- Monitoring Samples ---------------------------------------------------------------------------------------------
+PACKET_PORT_SAMPLE_TYPES = [
+    KpiSampleType.KPISAMPLETYPE_PACKETS_TRANSMITTED,
+    KpiSampleType.KPISAMPLETYPE_PACKETS_RECEIVED,
+    KpiSampleType.KPISAMPLETYPE_BYTES_TRANSMITTED,
+    KpiSampleType.KPISAMPLETYPE_BYTES_RECEIVED,
+]
+
+# ----- Device Credentials and Settings --------------------------------------------------------------------------------
+try:
+    from .Credentials import DEVICE_R1_ADDRESS, DEVICE_R1_PORT, DEVICE_R1_USERNAME, DEVICE_R1_PASSWORD
+    from .Credentials import DEVICE_R3_ADDRESS, DEVICE_R3_PORT, DEVICE_R3_USERNAME, DEVICE_R3_PASSWORD
+    from .Credentials import DEVICE_O1_ADDRESS, DEVICE_O1_PORT
+    USE_REAL_DEVICES = True     # Use real devices
+except ImportError:
+    USE_REAL_DEVICES = False    # Use emulated devices
+
+    DEVICE_R1_ADDRESS  = '0.0.0.0'
+    DEVICE_R1_PORT     = 830
+    DEVICE_R1_USERNAME = 'admin'
+    DEVICE_R1_PASSWORD = 'admin'
+
+    DEVICE_R3_ADDRESS  = '0.0.0.0'
+    DEVICE_R3_PORT     = 830
+    DEVICE_R3_USERNAME = 'admin'
+    DEVICE_R3_PASSWORD = 'admin'
+
+    DEVICE_O1_ADDRESS  = '0.0.0.0'
+    DEVICE_O1_PORT     = 4900
+
+#USE_REAL_DEVICES = False     # Uncomment to force to use emulated devices
+
+def json_endpoint_ids(device_id : Dict, endpoint_descriptors : List[Tuple[str, str, List[int]]]):
+    return [
+        json_endpoint_id(device_id, ep_uuid, topology_id=None)
+        for ep_uuid, _, _ in endpoint_descriptors
+    ]
+
+def json_endpoints(device_id : Dict, endpoint_descriptors : List[Tuple[str, str, List[int]]]):
+    return [
+        json_endpoint(device_id, ep_uuid, ep_type, topology_id=None, kpi_sample_types=ep_sample_types)
+        for ep_uuid, ep_type, ep_sample_types in endpoint_descriptors
+    ]
+
+def get_link_uuid(a_device_id : Dict, a_endpoint_id : Dict, z_device_id : Dict, z_endpoint_id : Dict) -> str:
+    return '{:s}/{:s}=={:s}/{:s}'.format(
+        a_device_id['device_uuid']['uuid'], a_endpoint_id['endpoint_uuid']['uuid'],
+        z_device_id['device_uuid']['uuid'], z_endpoint_id['endpoint_uuid']['uuid'])
+
+
+# ----- Devices --------------------------------------------------------------------------------------------------------
+if not USE_REAL_DEVICES:
+    json_device_packetrouter_disabled = json_device_emulated_packet_router_disabled
+    json_device_tapi_disabled         = json_device_emulated_tapi_disabled
+
+DEVICE_R1_UUID          = 'R1-INF'
+DEVICE_R1_TIMEOUT       = 120
+DEVICE_R1_ENDPOINT_DEFS = [('13/0/0', 'optical', []), ('13/1/2', 'copper', PACKET_PORT_SAMPLE_TYPES)]
+DEVICE_R1_ID            = json_device_id(DEVICE_R1_UUID)
+#DEVICE_R1_ENDPOINTS     = json_endpoints(DEVICE_R1_ID, DEVICE_R1_ENDPOINT_DEFS)
+DEVICE_R1_ENDPOINT_IDS  = json_endpoint_ids(DEVICE_R1_ID, DEVICE_R1_ENDPOINT_DEFS)
+DEVICE_R1               = json_device_packetrouter_disabled(DEVICE_R1_UUID)
+ENDPOINT_ID_R1_13_0_0   = DEVICE_R1_ENDPOINT_IDS[0]
+ENDPOINT_ID_R1_13_1_2   = DEVICE_R1_ENDPOINT_IDS[1]
+DEVICE_R1_CONNECT_RULES = json_device_connect_rules(DEVICE_R1_ADDRESS, DEVICE_R1_PORT, {
+    'username': DEVICE_R1_USERNAME,
+    'password': DEVICE_R1_PASSWORD,
+    'timeout' : DEVICE_R1_TIMEOUT,
+}) if USE_REAL_DEVICES else json_device_emulated_connect_rules(DEVICE_R1_ENDPOINT_DEFS)
+
+
+DEVICE_R2_UUID          = 'R2-EMU'
+DEVICE_R2_ENDPOINT_DEFS = [('13/0/0', 'optical', []), ('13/1/2', 'copper', PACKET_PORT_SAMPLE_TYPES)]
+DEVICE_R2_ID            = json_device_id(DEVICE_R2_UUID)
+#DEVICE_R2_ENDPOINTS     = json_endpoints(DEVICE_R2_ID, DEVICE_R2_ENDPOINT_DEFS)
+DEVICE_R2_ENDPOINT_IDS  = json_endpoint_ids(DEVICE_R2_ID, DEVICE_R2_ENDPOINT_DEFS)
+DEVICE_R2               = json_device_emulated_packet_router_disabled(DEVICE_R2_UUID)
+ENDPOINT_ID_R2_13_0_0   = DEVICE_R2_ENDPOINT_IDS[0]
+ENDPOINT_ID_R2_13_1_2   = DEVICE_R2_ENDPOINT_IDS[1]
+DEVICE_R2_CONNECT_RULES = json_device_emulated_connect_rules(DEVICE_R2_ENDPOINT_DEFS)
+
+
+DEVICE_R3_UUID          = 'R3-INF'
+DEVICE_R3_TIMEOUT       = 120
+DEVICE_R3_ENDPOINT_DEFS = [('13/0/0', 'optical', []), ('13/1/2', 'copper', PACKET_PORT_SAMPLE_TYPES)]
+DEVICE_R3_ID            = json_device_id(DEVICE_R3_UUID)
+#DEVICE_R3_ENDPOINTS     = json_endpoints(DEVICE_R3_ID, DEVICE_R3_ENDPOINT_DEFS)
+DEVICE_R3_ENDPOINT_IDS  = json_endpoint_ids(DEVICE_R3_ID, DEVICE_R3_ENDPOINT_DEFS)
+DEVICE_R3               = json_device_packetrouter_disabled(DEVICE_R3_UUID)
+ENDPOINT_ID_R3_13_0_0   = DEVICE_R3_ENDPOINT_IDS[0]
+ENDPOINT_ID_R3_13_1_2   = DEVICE_R3_ENDPOINT_IDS[1]
+DEVICE_R3_CONNECT_RULES = json_device_connect_rules(DEVICE_R3_ADDRESS, DEVICE_R3_PORT, {
+    'username': DEVICE_R3_USERNAME,
+    'password': DEVICE_R3_PASSWORD,
+    'timeout' : DEVICE_R3_TIMEOUT,
+}) if USE_REAL_DEVICES else json_device_emulated_connect_rules(DEVICE_R3_ENDPOINT_DEFS)
+
+
+DEVICE_R4_UUID          = 'R4-EMU'
+DEVICE_R4_ENDPOINT_DEFS = [('13/0/0', 'optical', []), ('13/1/2', 'copper', PACKET_PORT_SAMPLE_TYPES)]
+DEVICE_R4_ID            = json_device_id(DEVICE_R4_UUID)
+#DEVICE_R4_ENDPOINTS     = json_endpoints(DEVICE_R4_ID, DEVICE_R4_ENDPOINT_DEFS)
+DEVICE_R4_ENDPOINT_IDS  = json_endpoint_ids(DEVICE_R4_ID, DEVICE_R4_ENDPOINT_DEFS)
+DEVICE_R4               = json_device_emulated_packet_router_disabled(DEVICE_R4_UUID)
+ENDPOINT_ID_R4_13_0_0   = DEVICE_R4_ENDPOINT_IDS[0]
+ENDPOINT_ID_R4_13_1_2   = DEVICE_R4_ENDPOINT_IDS[1]
+DEVICE_R4_CONNECT_RULES = json_device_emulated_connect_rules(DEVICE_R4_ENDPOINT_DEFS)
+
+
+DEVICE_O1_UUID          = 'O1-OLS'
+DEVICE_O1_TIMEOUT       = 120
+DEVICE_O1_ENDPOINT_DEFS = [
+    ('aade6001-f00b-5e2f-a357-6a0a9d3de870', 'optical', []), # node_1_port_13
+    ('eb287d83-f05e-53ec-ab5a-adf6bd2b5418', 'optical', []), # node_2_port_13
+    ('0ef74f99-1acc-57bd-ab9d-4b958b06c513', 'optical', []), # node_3_port_13
+    ('50296d99-58cc-5ce7-82f5-fc8ee4eec2ec', 'optical', []), # node_4_port_13
+]
+DEVICE_O1_ID            = json_device_id(DEVICE_O1_UUID)
+DEVICE_O1               = json_device_tapi_disabled(DEVICE_O1_UUID)
+#DEVICE_O1_ENDPOINTS     = json_endpoints(DEVICE_O1_ID, DEVICE_O1_ENDPOINT_DEFS)
+DEVICE_O1_ENDPOINT_IDS  = json_endpoint_ids(DEVICE_O1_ID, DEVICE_O1_ENDPOINT_DEFS)
+ENDPOINT_ID_O1_EP1      = DEVICE_O1_ENDPOINT_IDS[0]
+ENDPOINT_ID_O1_EP2      = DEVICE_O1_ENDPOINT_IDS[1]
+ENDPOINT_ID_O1_EP3      = DEVICE_O1_ENDPOINT_IDS[2]
+ENDPOINT_ID_O1_EP4      = DEVICE_O1_ENDPOINT_IDS[3]
+DEVICE_O1_CONNECT_RULES = json_device_connect_rules(DEVICE_O1_ADDRESS, DEVICE_O1_PORT, {
+    'timeout' : DEVICE_O1_TIMEOUT,
+}) if USE_REAL_DEVICES else json_device_emulated_connect_rules(DEVICE_O1_ENDPOINT_DEFS)
+
+
+# ----- Links ----------------------------------------------------------------------------------------------------------
+LINK_R1_O1_UUID = get_link_uuid(DEVICE_R1_ID, ENDPOINT_ID_R1_13_0_0, DEVICE_O1_ID, ENDPOINT_ID_O1_EP1)
+LINK_R1_O1_ID   = json_link_id(LINK_R1_O1_UUID)
+LINK_R1_O1      = json_link(LINK_R1_O1_UUID, [ENDPOINT_ID_R1_13_0_0, ENDPOINT_ID_O1_EP1])
+
+LINK_R2_O1_UUID = get_link_uuid(DEVICE_R2_ID, ENDPOINT_ID_R2_13_0_0, DEVICE_O1_ID, ENDPOINT_ID_O1_EP2)
+LINK_R2_O1_ID   = json_link_id(LINK_R2_O1_UUID)
+LINK_R2_O1      = json_link(LINK_R2_O1_UUID, [ENDPOINT_ID_R2_13_0_0, ENDPOINT_ID_O1_EP2])
+
+LINK_R3_O1_UUID = get_link_uuid(DEVICE_R3_ID, ENDPOINT_ID_R3_13_0_0, DEVICE_O1_ID, ENDPOINT_ID_O1_EP3)
+LINK_R3_O1_ID   = json_link_id(LINK_R3_O1_UUID)
+LINK_R3_O1      = json_link(LINK_R3_O1_UUID, [ENDPOINT_ID_R3_13_0_0, ENDPOINT_ID_O1_EP3])
+
+LINK_R4_O1_UUID = get_link_uuid(DEVICE_R4_ID, ENDPOINT_ID_R4_13_0_0, DEVICE_O1_ID, ENDPOINT_ID_O1_EP4)
+LINK_R4_O1_ID   = json_link_id(LINK_R4_O1_UUID)
+LINK_R4_O1      = json_link(LINK_R4_O1_UUID, [ENDPOINT_ID_R4_13_0_0, ENDPOINT_ID_O1_EP4])
+
+
+# ----- WIM Service Settings -------------------------------------------------------------------------------------------
+
+def compose_service_endpoint_id(endpoint_id):
+    device_uuid = endpoint_id['device_id']['device_uuid']['uuid']
+    endpoint_uuid = endpoint_id['endpoint_uuid']['uuid']
+    return ':'.join([device_uuid, endpoint_uuid])
+
+def compose_bearer(endpoint_id, router_id, route_distinguisher):
+    device_uuid = endpoint_id['device_id']['device_uuid']['uuid']
+    endpoint_uuid = endpoint_id['endpoint_uuid']['uuid']
+    return '#'.join([device_uuid, endpoint_uuid, router_id, route_distinguisher])
+
+WIM_SEP_R1_ID          = compose_service_endpoint_id(ENDPOINT_ID_R1_13_1_2)
+WIM_SEP_R1_ROUTER_ID   = '10.10.10.1'
+WIM_SEP_R1_ROUTER_DIST = '65000:111'
+WIM_SEP_R1_SITE_ID     = '1'
+WIM_SEP_R1_BEARER      = compose_bearer(ENDPOINT_ID_R1_13_1_2, WIM_SEP_R1_ROUTER_ID, WIM_SEP_R1_ROUTER_DIST)
+WIM_SRV_R1_VLAN_ID     = 400
+
+WIM_SEP_R3_ID          = compose_service_endpoint_id(ENDPOINT_ID_R3_13_1_2)
+WIM_SEP_R3_ROUTER_ID   = '20.20.20.1'
+WIM_SEP_R3_ROUTER_DIST = '65000:222'
+WIM_SEP_R3_SITE_ID     = '2'
+WIM_SEP_R3_BEARER      = compose_bearer(ENDPOINT_ID_R3_13_1_2, WIM_SEP_R3_ROUTER_ID, WIM_SEP_R3_ROUTER_DIST)
+WIM_SRV_R3_VLAN_ID     = 500
+
+WIM_USERNAME = 'admin'
+WIM_PASSWORD = 'admin'
+
+WIM_MAPPING  = [
+    {'device-id': DEVICE_R1_UUID, 'service_endpoint_id': WIM_SEP_R1_ID,
+     'service_mapping_info': {'bearer': {'bearer-reference': WIM_SEP_R1_BEARER}, 'site-id': WIM_SEP_R1_SITE_ID}},
+    {'device-id': DEVICE_R3_UUID, 'service_endpoint_id': WIM_SEP_R3_ID,
+     'service_mapping_info': {'bearer': {'bearer-reference': WIM_SEP_R3_BEARER}, 'site-id': WIM_SEP_R3_SITE_ID}},
+]
+WIM_SERVICE_TYPE = 'ELINE'
+WIM_SERVICE_CONNECTION_POINTS = [
+    {'service_endpoint_id': WIM_SEP_R1_ID,
+        'service_endpoint_encapsulation_type': 'dot1q',
+        'service_endpoint_encapsulation_info': {'vlan': WIM_SRV_R1_VLAN_ID}},
+    {'service_endpoint_id': WIM_SEP_R3_ID,
+        'service_endpoint_encapsulation_type': 'dot1q',
+        'service_endpoint_encapsulation_info': {'vlan': WIM_SRV_R3_VLAN_ID}},
+]
+
+# ----- Object Collections ---------------------------------------------------------------------------------------------
+
+CONTEXTS = [CONTEXT]
+TOPOLOGIES = [TOPOLOGY]
+
+DEVICES = [
+    (DEVICE_R1, DEVICE_R1_CONNECT_RULES),
+    (DEVICE_R2, DEVICE_R2_CONNECT_RULES),
+    (DEVICE_R3, DEVICE_R3_CONNECT_RULES),
+    (DEVICE_R4, DEVICE_R4_CONNECT_RULES),
+    (DEVICE_O1, DEVICE_O1_CONNECT_RULES),
+]
+
+LINKS = [LINK_R1_O1, LINK_R2_O1, LINK_R3_O1, LINK_R4_O1]
diff --git a/src/tests/ofc22/tests/__init__.py b/src/tests/ofc22/tests/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..70a33251242c51f49140e596b8208a19dd5245f7
--- /dev/null
+++ b/src/tests/ofc22/tests/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/tests/ofc22/tests/test_functional_bootstrap.py b/src/tests/ofc22/tests/test_functional_bootstrap.py
new file mode 100644
index 0000000000000000000000000000000000000000..334d7894babedfed2ffb30e4682a1d56e4261cb9
--- /dev/null
+++ b/src/tests/ofc22/tests/test_functional_bootstrap.py
@@ -0,0 +1,204 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import copy, logging, pytest
+from common.Settings import get_setting
+from common.tests.EventTools import EVENT_CREATE, EVENT_UPDATE, check_events
+from common.tools.object_factory.Context import json_context_id
+from common.tools.object_factory.Device import json_device_id
+from common.tools.object_factory.Link import json_link_id
+from common.tools.object_factory.Topology import json_topology_id
+from context.client.ContextClient import ContextClient
+from context.client.EventsCollector import EventsCollector
+from context.proto.context_pb2 import Context, ContextId, Device, Empty, Link, Topology
+from device.client.DeviceClient import DeviceClient
+from .Objects import CONTEXT_ID, CONTEXTS, DEVICES, LINKS, TOPOLOGIES
+
+
+LOGGER = logging.getLogger(__name__)
+LOGGER.setLevel(logging.DEBUG)
+
+
+@pytest.fixture(scope='session')
+def context_client():
+    _client = ContextClient(get_setting('CONTEXTSERVICE_SERVICE_HOST'), get_setting('CONTEXTSERVICE_SERVICE_PORT_GRPC'))
+    yield _client
+    _client.close()
+
+
+@pytest.fixture(scope='session')
+def device_client():
+    _client = DeviceClient(get_setting('DEVICESERVICE_SERVICE_HOST'), get_setting('DEVICESERVICE_SERVICE_PORT_GRPC'))
+    yield _client
+    _client.close()
+
+
+def test_scenario_empty(context_client : ContextClient):  # pylint: disable=redefined-outer-name
+    # ----- List entities - Ensure database is empty -------------------------------------------------------------------
+    response = context_client.ListContexts(Empty())
+    assert len(response.contexts) == 0
+
+    response = context_client.ListDevices(Empty())
+    assert len(response.devices) == 0
+
+    response = context_client.ListLinks(Empty())
+    assert len(response.links) == 0
+
+
+def test_prepare_scenario(context_client : ContextClient):  # pylint: disable=redefined-outer-name
+
+    # ----- Start the EventsCollector ----------------------------------------------------------------------------------
+    events_collector = EventsCollector(context_client)
+    events_collector.start()
+
+    expected_events = []
+
+    # ----- Create Contexts and Topologies -----------------------------------------------------------------------------
+    for context in CONTEXTS:
+        context_uuid = context['context_id']['context_uuid']['uuid']
+        LOGGER.info('Adding Context {:s}'.format(context_uuid))
+        response = context_client.SetContext(Context(**context))
+        assert response.context_uuid.uuid == context_uuid
+        expected_events.append(('ContextEvent', EVENT_CREATE, json_context_id(context_uuid)))
+
+    for topology in TOPOLOGIES:
+        context_uuid = topology['topology_id']['context_id']['context_uuid']['uuid']
+        topology_uuid = topology['topology_id']['topology_uuid']['uuid']
+        LOGGER.info('Adding Topology {:s}/{:s}'.format(context_uuid, topology_uuid))
+        response = context_client.SetTopology(Topology(**topology))
+        assert response.context_id.context_uuid.uuid == context_uuid
+        assert response.topology_uuid.uuid == topology_uuid
+        context_id = json_context_id(context_uuid)
+        expected_events.append(('TopologyEvent', EVENT_CREATE, json_topology_id(topology_uuid, context_id=context_id)))
+
+    # ----- Validate Collected Events ----------------------------------------------------------------------------------
+    check_events(events_collector, expected_events)
+
+    # ----- Stop the EventsCollector -----------------------------------------------------------------------------------
+    events_collector.stop()
+
+
+def test_scenario_ready(context_client : ContextClient):  # pylint: disable=redefined-outer-name
+    # ----- List entities - Ensure scenario is ready -------------------------------------------------------------------
+    response = context_client.ListContexts(Empty())
+    assert len(response.contexts) == len(CONTEXTS)
+
+    response = context_client.ListTopologies(ContextId(**CONTEXT_ID))
+    assert len(response.topologies) == len(TOPOLOGIES)
+
+    response = context_client.ListDevices(Empty())
+    assert len(response.devices) == 0
+
+    response = context_client.ListLinks(Empty())
+    assert len(response.links) == 0
+
+    response = context_client.ListServices(ContextId(**CONTEXT_ID))
+    assert len(response.services) == 0
+
+
+def test_devices_bootstraping(
+    context_client : ContextClient, device_client : DeviceClient):  # pylint: disable=redefined-outer-name
+
+    # ----- Start the EventsCollector ----------------------------------------------------------------------------------
+    events_collector = EventsCollector(context_client, log_events_received=True)
+    events_collector.start()
+
+    expected_events = []
+
+    # ----- Create Devices and Validate Collected Events ---------------------------------------------------------------
+    for device, connect_rules in DEVICES:
+        device_uuid = device['device_id']['device_uuid']['uuid']
+        LOGGER.info('Adding Device {:s}'.format(device_uuid))
+
+        device_with_connect_rules = copy.deepcopy(device)
+        device_with_connect_rules['device_config']['config_rules'].extend(connect_rules)
+        response = device_client.AddDevice(Device(**device_with_connect_rules))
+        assert response.device_uuid.uuid == device_uuid
+
+        expected_events.extend([
+            # Device creation, update for automation to start the device
+            ('DeviceEvent', EVENT_CREATE, json_device_id(device_uuid)),
+            #('DeviceEvent', EVENT_UPDATE, json_device_id(device_uuid)),
+        ])
+
+        #response = context_client.GetDevice(response)
+        #for endpoint in response.device_endpoints:
+        #    for _ in endpoint.kpi_sample_types:
+        #        # Monitoring configures monitoring for endpoint
+        #        expected_events.append(('DeviceEvent', EVENT_UPDATE, json_device_id(device_uuid)))
+
+    # ----- Validate Collected Events ----------------------------------------------------------------------------------
+    check_events(events_collector, expected_events)
+
+    # ----- Stop the EventsCollector -----------------------------------------------------------------------------------
+    events_collector.stop()
+
+
+def test_devices_bootstrapped(context_client : ContextClient):  # pylint: disable=redefined-outer-name
+    # ----- List entities - Ensure bevices are created -----------------------------------------------------------------
+    response = context_client.ListContexts(Empty())
+    assert len(response.contexts) == len(CONTEXTS)
+
+    response = context_client.ListTopologies(ContextId(**CONTEXT_ID))
+    assert len(response.topologies) == len(TOPOLOGIES)
+
+    response = context_client.ListDevices(Empty())
+    assert len(response.devices) == len(DEVICES)
+
+    response = context_client.ListLinks(Empty())
+    assert len(response.links) == 0
+
+    response = context_client.ListServices(ContextId(**CONTEXT_ID))
+    assert len(response.services) == 0
+
+
+def test_links_creation(context_client : ContextClient):  # pylint: disable=redefined-outer-name
+
+    # ----- Start the EventsCollector ----------------------------------------------------------------------------------
+    events_collector = EventsCollector(context_client)
+    events_collector.start()
+
+    expected_events = []
+
+    # ----- Create Links and Validate Collected Events -----------------------------------------------------------------
+    for link in LINKS:
+        link_uuid = link['link_id']['link_uuid']['uuid']
+        LOGGER.info('Adding Link {:s}'.format(link_uuid))
+        response = context_client.SetLink(Link(**link))
+        assert response.link_uuid.uuid == link_uuid
+        expected_events.append(('LinkEvent', EVENT_CREATE, json_link_id(link_uuid)))
+
+    # ----- Validate Collected Events ----------------------------------------------------------------------------------
+    check_events(events_collector, expected_events)
+
+    # ----- Stop the EventsCollector -----------------------------------------------------------------------------------
+    events_collector.stop()
+
+
+def test_links_created(context_client : ContextClient):  # pylint: disable=redefined-outer-name
+    # ----- List entities - Ensure links are created -------------------------------------------------------------------
+    response = context_client.ListContexts(Empty())
+    assert len(response.contexts) == len(CONTEXTS)
+
+    response = context_client.ListTopologies(ContextId(**CONTEXT_ID))
+    assert len(response.topologies) == len(TOPOLOGIES)
+
+    response = context_client.ListDevices(Empty())
+    assert len(response.devices) == len(DEVICES)
+
+    response = context_client.ListLinks(Empty())
+    assert len(response.links) == len(LINKS)
+
+    response = context_client.ListServices(ContextId(**CONTEXT_ID))
+    assert len(response.services) == 0
diff --git a/src/tests/ofc22/tests/test_functional_cleanup.py b/src/tests/ofc22/tests/test_functional_cleanup.py
new file mode 100644
index 0000000000000000000000000000000000000000..eb78a585079e3ee757a836433bf23423a3ad899d
--- /dev/null
+++ b/src/tests/ofc22/tests/test_functional_cleanup.py
@@ -0,0 +1,123 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging, pytest
+from common.Settings import get_setting
+from common.tests.EventTools import EVENT_REMOVE, check_events
+from common.tools.object_factory.Context import json_context_id
+from common.tools.object_factory.Device import json_device_id
+from common.tools.object_factory.Link import json_link_id
+from common.tools.object_factory.Topology import json_topology_id
+from context.client.ContextClient import ContextClient
+from context.client.EventsCollector import EventsCollector
+from context.proto.context_pb2 import ContextId, DeviceId, Empty, LinkId, TopologyId
+from device.client.DeviceClient import DeviceClient
+from .Objects import CONTEXT_ID, CONTEXTS, DEVICES, LINKS, TOPOLOGIES
+
+LOGGER = logging.getLogger(__name__)
+LOGGER.setLevel(logging.DEBUG)
+
+
+@pytest.fixture(scope='session')
+def context_client():
+    _client = ContextClient(get_setting('CONTEXTSERVICE_SERVICE_HOST'), get_setting('CONTEXTSERVICE_SERVICE_PORT_GRPC'))
+    yield _client
+    _client.close()
+
+
+@pytest.fixture(scope='session')
+def device_client():
+    _client = DeviceClient(get_setting('DEVICESERVICE_SERVICE_HOST'), get_setting('DEVICESERVICE_SERVICE_PORT_GRPC'))
+    yield _client
+    _client.close()
+
+
+def test_services_removed(context_client : ContextClient):  # pylint: disable=redefined-outer-name
+    # ----- List entities - Ensure service is removed ------------------------------------------------------------------
+    response = context_client.ListContexts(Empty())
+    assert len(response.contexts) == len(CONTEXTS)
+
+    response = context_client.ListTopologies(ContextId(**CONTEXT_ID))
+    assert len(response.topologies) == len(TOPOLOGIES)
+
+    response = context_client.ListDevices(Empty())
+    assert len(response.devices) == len(DEVICES)
+
+    response = context_client.ListLinks(Empty())
+    assert len(response.links) == len(LINKS)
+
+    response = context_client.ListServices(ContextId(**CONTEXT_ID))
+    assert len(response.services) == 0
+
+
+def test_scenario_cleanup(
+    context_client : ContextClient, device_client : DeviceClient):  # pylint: disable=redefined-outer-name
+
+    # ----- Start the EventsCollector ----------------------------------------------------------------------------------
+    events_collector = EventsCollector(context_client)
+    events_collector.start()
+
+    expected_events = []
+
+    # ----- Delete Links and Validate Collected Events -----------------------------------------------------------------
+    for link in LINKS:
+        link_id = link['link_id']
+        link_uuid = link_id['link_uuid']['uuid']
+        LOGGER.info('Deleting Link {:s}'.format(link_uuid))
+        context_client.RemoveLink(LinkId(**link_id))
+        expected_events.append(('LinkEvent', EVENT_REMOVE, json_link_id(link_uuid)))
+
+    # ----- Delete Devices and Validate Collected Events ---------------------------------------------------------------
+    for device, _ in DEVICES:
+        device_id = device['device_id']
+        device_uuid = device_id['device_uuid']['uuid']
+        LOGGER.info('Deleting Device {:s}'.format(device_uuid))
+        device_client.DeleteDevice(DeviceId(**device_id))
+        expected_events.append(('DeviceEvent', EVENT_REMOVE, json_device_id(device_uuid)))
+
+    # ----- Delete Topologies and Validate Collected Events ------------------------------------------------------------
+    for topology in TOPOLOGIES:
+        topology_id = topology['topology_id']
+        context_uuid = topology_id['context_id']['context_uuid']['uuid']
+        topology_uuid = topology_id['topology_uuid']['uuid']
+        LOGGER.info('Deleting Topology {:s}/{:s}'.format(context_uuid, topology_uuid))
+        context_client.RemoveTopology(TopologyId(**topology_id))
+        context_id = json_context_id(context_uuid)
+        expected_events.append(('TopologyEvent', EVENT_REMOVE, json_topology_id(topology_uuid, context_id=context_id)))
+
+    # ----- Delete Contexts and Validate Collected Events --------------------------------------------------------------
+    for context in CONTEXTS:
+        context_id = context['context_id']
+        context_uuid = context_id['context_uuid']['uuid']
+        LOGGER.info('Deleting Context {:s}'.format(context_uuid))
+        context_client.RemoveContext(ContextId(**context_id))
+        expected_events.append(('ContextEvent', EVENT_REMOVE, json_context_id(context_uuid)))
+
+    # ----- Validate Collected Events ----------------------------------------------------------------------------------
+    check_events(events_collector, expected_events)
+
+    # ----- Stop the EventsCollector -----------------------------------------------------------------------------------
+    events_collector.stop()
+
+
+def test_scenario_empty_again(context_client : ContextClient):  # pylint: disable=redefined-outer-name
+    # ----- List entities - Ensure database is empty again -------------------------------------------------------------
+    response = context_client.ListContexts(Empty())
+    assert len(response.contexts) == 0
+
+    response = context_client.ListDevices(Empty())
+    assert len(response.devices) == 0
+
+    response = context_client.ListLinks(Empty())
+    assert len(response.links) == 0
diff --git a/src/tests/ofc22/tests/test_functional_create_service.py b/src/tests/ofc22/tests/test_functional_create_service.py
new file mode 100644
index 0000000000000000000000000000000000000000..f3389fdbfce4e9262ffddbad876bb86f9b300551
--- /dev/null
+++ b/src/tests/ofc22/tests/test_functional_create_service.py
@@ -0,0 +1,129 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging, pytest
+from common.DeviceTypes import DeviceTypeEnum
+from common.Settings import get_setting
+from common.tests.EventTools import EVENT_CREATE, EVENT_UPDATE, check_events
+from common.tools.object_factory.Connection import json_connection_id
+from common.tools.object_factory.Device import json_device_id
+from common.tools.object_factory.Service import json_service_id
+from common.tools.grpc.Tools import grpc_message_to_json_string
+from compute.tests.mock_osm.MockOSM import MockOSM
+from context.client.ContextClient import ContextClient
+from context.client.EventsCollector import EventsCollector
+from context.proto.context_pb2 import ContextId, Empty
+from .Objects import (
+    CONTEXT_ID, CONTEXTS, DEVICE_O1_UUID, DEVICE_R1_UUID, DEVICE_R3_UUID, DEVICES, LINKS, TOPOLOGIES,
+    WIM_MAPPING, WIM_PASSWORD, WIM_SERVICE_CONNECTION_POINTS, WIM_SERVICE_TYPE, WIM_USERNAME)
+
+LOGGER = logging.getLogger(__name__)
+LOGGER.setLevel(logging.DEBUG)
+
+DEVTYPE_EMU_PR  = DeviceTypeEnum.EMULATED_PACKET_ROUTER.value
+DEVTYPE_EMU_OLS = DeviceTypeEnum.EMULATED_OPTICAL_LINE_SYSTEM.value
+
+
+@pytest.fixture(scope='session')
+def context_client():
+    _client = ContextClient(get_setting('CONTEXTSERVICE_SERVICE_HOST'), get_setting('CONTEXTSERVICE_SERVICE_PORT_GRPC'))
+    yield _client
+    _client.close()
+
+
+@pytest.fixture(scope='session')
+def osm_wim():
+    wim_url = 'http://{:s}:{:s}'.format(
+        get_setting('COMPUTESERVICE_SERVICE_HOST'), str(get_setting('COMPUTESERVICE_SERVICE_PORT_HTTP')))
+    return MockOSM(wim_url, WIM_MAPPING, WIM_USERNAME, WIM_PASSWORD)
+
+
+def test_scenario_is_correct(context_client : ContextClient):  # pylint: disable=redefined-outer-name
+    # ----- List entities - Ensure links are created -------------------------------------------------------------------
+    response = context_client.ListContexts(Empty())
+    assert len(response.contexts) == len(CONTEXTS)
+
+    response = context_client.ListTopologies(ContextId(**CONTEXT_ID))
+    assert len(response.topologies) == len(TOPOLOGIES)
+
+    response = context_client.ListDevices(Empty())
+    assert len(response.devices) == len(DEVICES)
+
+    response = context_client.ListLinks(Empty())
+    assert len(response.links) == len(LINKS)
+
+    response = context_client.ListServices(ContextId(**CONTEXT_ID))
+    assert len(response.services) == 0
+
+
+def test_service_creation(context_client : ContextClient, osm_wim : MockOSM): # pylint: disable=redefined-outer-name
+    # ----- Start the EventsCollector ----------------------------------------------------------------------------------
+    events_collector = EventsCollector(context_client, log_events_received=True)
+    events_collector.start()
+
+    # ----- Create Service ---------------------------------------------------------------------------------------------
+    service_uuid = osm_wim.create_connectivity_service(WIM_SERVICE_TYPE, WIM_SERVICE_CONNECTION_POINTS)
+    osm_wim.get_connectivity_service_status(service_uuid)
+
+    # ----- Validate collected events ----------------------------------------------------------------------------------
+
+    packet_connection_uuid = '{:s}:{:s}'.format(service_uuid, DEVTYPE_EMU_PR)
+    optical_connection_uuid = '{:s}:optical:{:s}'.format(service_uuid, DEVTYPE_EMU_OLS)
+    optical_service_uuid = '{:s}:optical'.format(service_uuid)
+
+    expected_events = [
+        # Create packet service and add first endpoint
+        ('ServiceEvent',    EVENT_CREATE, json_service_id(service_uuid, context_id=CONTEXT_ID)),
+        ('ServiceEvent',    EVENT_UPDATE, json_service_id(service_uuid, context_id=CONTEXT_ID)),
+
+        # Configure OLS controller, create optical service, create optical connection
+        ('DeviceEvent',     EVENT_UPDATE, json_device_id(DEVICE_O1_UUID)),
+        ('ServiceEvent',    EVENT_CREATE, json_service_id(optical_service_uuid, context_id=CONTEXT_ID)),
+        ('ConnectionEvent', EVENT_CREATE, json_connection_id(optical_connection_uuid)),
+
+        # Configure endpoint packet devices, add second endpoint to service, create connection
+        ('DeviceEvent',     EVENT_UPDATE, json_device_id(DEVICE_R1_UUID)),
+        ('DeviceEvent',     EVENT_UPDATE, json_device_id(DEVICE_R3_UUID)),
+        ('ServiceEvent',    EVENT_UPDATE, json_service_id(service_uuid, context_id=CONTEXT_ID)),
+        ('ConnectionEvent', EVENT_CREATE, json_connection_id(packet_connection_uuid)),
+    ]
+    check_events(events_collector, expected_events)
+
+    # ----- Stop the EventsCollector -----------------------------------------------------------------------------------
+    events_collector.stop()
+
+
+def test_scenario_service_created(context_client : ContextClient):  # pylint: disable=redefined-outer-name
+    # ----- List entities - Ensure service is created ------------------------------------------------------------------
+    response = context_client.ListContexts(Empty())
+    assert len(response.contexts) == len(CONTEXTS)
+
+    response = context_client.ListTopologies(ContextId(**CONTEXT_ID))
+    assert len(response.topologies) == len(TOPOLOGIES)
+
+    response = context_client.ListDevices(Empty())
+    assert len(response.devices) == len(DEVICES)
+
+    response = context_client.ListLinks(Empty())
+    assert len(response.links) == len(LINKS)
+
+    response = context_client.ListServices(ContextId(**CONTEXT_ID))
+    LOGGER.info('Services[{:d}] = {:s}'.format(len(response.services), grpc_message_to_json_string(response)))
+    assert len(response.services) == 2 # L3NM + TAPI
+    for service in response.services:
+        service_id = service.service_id
+        response = context_client.ListConnections(service_id)
+        LOGGER.info('  ServiceId[{:s}] => Connections[{:d}] = {:s}'.format(
+            grpc_message_to_json_string(service_id), len(response.connections), grpc_message_to_json_string(response)))
+        assert len(response.connections) == 1 # one connection per service
diff --git a/src/tests/ofc22/tests/test_functional_delete_service.py b/src/tests/ofc22/tests/test_functional_delete_service.py
new file mode 100644
index 0000000000000000000000000000000000000000..51e91a5967e1696fa2fdfe7dd06d2efb46642248
--- /dev/null
+++ b/src/tests/ofc22/tests/test_functional_delete_service.py
@@ -0,0 +1,134 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging, pytest
+from common.DeviceTypes import DeviceTypeEnum
+from common.Settings import get_setting
+from common.tests.EventTools import EVENT_REMOVE, EVENT_UPDATE, check_events
+from common.tools.object_factory.Connection import json_connection_id
+from common.tools.object_factory.Device import json_device_id
+from common.tools.object_factory.Service import json_service_id
+from common.tools.grpc.Tools import grpc_message_to_json_string
+from compute.tests.mock_osm.MockOSM import MockOSM
+from context.client.ContextClient import ContextClient
+from context.client.EventsCollector import EventsCollector
+from context.proto.context_pb2 import ContextId, Empty
+from .Objects import (
+    CONTEXT_ID, CONTEXTS, DEVICE_O1_UUID, DEVICE_R1_UUID, DEVICE_R3_UUID, DEVICES, LINKS, TOPOLOGIES, WIM_MAPPING,
+    WIM_PASSWORD, WIM_USERNAME)
+
+
+LOGGER = logging.getLogger(__name__)
+LOGGER.setLevel(logging.DEBUG)
+
+DEVTYPE_EMU_PR  = DeviceTypeEnum.EMULATED_PACKET_ROUTER.value
+DEVTYPE_EMU_OLS = DeviceTypeEnum.EMULATED_OPTICAL_LINE_SYSTEM.value
+
+
+@pytest.fixture(scope='session')
+def context_client():
+    _client = ContextClient(get_setting('CONTEXTSERVICE_SERVICE_HOST'), get_setting('CONTEXTSERVICE_SERVICE_PORT_GRPC'))
+    yield _client
+    _client.close()
+
+
+@pytest.fixture(scope='session')
+def osm_wim():
+    wim_url = 'http://{:s}:{:s}'.format(
+        get_setting('COMPUTESERVICE_SERVICE_HOST'), str(get_setting('COMPUTESERVICE_SERVICE_PORT_HTTP')))
+    return MockOSM(wim_url, WIM_MAPPING, WIM_USERNAME, WIM_PASSWORD)
+
+
+def test_scenario_is_correct(context_client : ContextClient):  # pylint: disable=redefined-outer-name
+    # ----- List entities - Ensure service is created ------------------------------------------------------------------
+    response = context_client.ListContexts(Empty())
+    assert len(response.contexts) == len(CONTEXTS)
+
+    response = context_client.ListTopologies(ContextId(**CONTEXT_ID))
+    assert len(response.topologies) == len(TOPOLOGIES)
+
+    response = context_client.ListDevices(Empty())
+    assert len(response.devices) == len(DEVICES)
+
+    response = context_client.ListLinks(Empty())
+    assert len(response.links) == len(LINKS)
+
+    response = context_client.ListServices(ContextId(**CONTEXT_ID))
+    LOGGER.info('Services[{:d}] = {:s}'.format(len(response.services), grpc_message_to_json_string(response)))
+    assert len(response.services) == 2 # L3NM + TAPI
+    for service in response.services:
+        service_id = service.service_id
+        response = context_client.ListConnections(service_id)
+        LOGGER.info('  ServiceId[{:s}] => Connections[{:d}] = {:s}'.format(
+            grpc_message_to_json_string(service_id), len(response.connections), grpc_message_to_json_string(response)))
+        assert len(response.connections) == 1 # one connection per service
+
+
+def test_service_removal(context_client : ContextClient, osm_wim : MockOSM): # pylint: disable=redefined-outer-name
+    # ----- Start the EventsCollector ----------------------------------------------------------------------------------
+    events_collector = EventsCollector(context_client, log_events_received=True)
+    events_collector.start()
+
+    # ----- Delete Service ---------------------------------------------------------------------------------------------
+    response = context_client.ListServiceIds(ContextId(**CONTEXT_ID))
+    LOGGER.info('Services[{:d}] = {:s}'.format(len(response.service_ids), grpc_message_to_json_string(response)))
+    assert len(response.service_ids) == 2 # L3NM + TAPI
+    service_uuids = set()
+    for service_id in response.service_ids:
+        service_uuid = service_id.service_uuid.uuid
+        if service_uuid.endswith(':optical'): continue
+        service_uuids.add(service_uuid)
+        osm_wim.conn_info[service_uuid] = {}
+
+    assert len(service_uuids) == 1  # assume a single service has been created
+    service_uuid = set(service_uuids).pop()
+
+    osm_wim.delete_connectivity_service(service_uuid)
+
+    # ----- Validate collected events ----------------------------------------------------------------------------------
+    packet_connection_uuid = '{:s}:{:s}'.format(service_uuid, DEVTYPE_EMU_PR)
+    optical_connection_uuid = '{:s}:optical:{:s}'.format(service_uuid, DEVTYPE_EMU_OLS)
+    optical_service_uuid = '{:s}:optical'.format(service_uuid)
+
+    expected_events = [
+        ('ConnectionEvent', EVENT_REMOVE, json_connection_id(packet_connection_uuid)),
+        ('DeviceEvent',     EVENT_UPDATE, json_device_id(DEVICE_R1_UUID)),
+        ('DeviceEvent',     EVENT_UPDATE, json_device_id(DEVICE_R3_UUID)),
+        ('ServiceEvent',    EVENT_REMOVE, json_service_id(service_uuid, context_id=CONTEXT_ID)),
+        ('ConnectionEvent', EVENT_REMOVE, json_connection_id(optical_connection_uuid)),
+        ('DeviceEvent',     EVENT_UPDATE, json_device_id(DEVICE_O1_UUID)),
+        ('ServiceEvent',    EVENT_REMOVE, json_service_id(optical_service_uuid, context_id=CONTEXT_ID)),
+    ]
+    check_events(events_collector, expected_events)
+
+    # ----- Stop the EventsCollector -----------------------------------------------------------------------------------
+    events_collector.stop()
+
+
+def test_services_removed(context_client : ContextClient):  # pylint: disable=redefined-outer-name
+    # ----- List entities - Ensure service is removed ------------------------------------------------------------------
+    response = context_client.ListContexts(Empty())
+    assert len(response.contexts) == len(CONTEXTS)
+
+    response = context_client.ListTopologies(ContextId(**CONTEXT_ID))
+    assert len(response.topologies) == len(TOPOLOGIES)
+
+    response = context_client.ListDevices(Empty())
+    assert len(response.devices) == len(DEVICES)
+
+    response = context_client.ListLinks(Empty())
+    assert len(response.links) == len(LINKS)
+
+    response = context_client.ListServices(ContextId(**CONTEXT_ID))
+    assert len(response.services) == 0
diff --git a/src/webui/.gitlab-ci.yml b/src/webui/.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..65fd7127848224d2afb7c45280a3d15ffbcab6c5
--- /dev/null
+++ b/src/webui/.gitlab-ci.yml
@@ -0,0 +1,103 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# build, tag and push the Docker image to the gitlab registry
+build webui:
+  variables:
+    IMAGE_NAME: 'webui' # name of the microservice
+    IMAGE_TAG: 'latest' # tag of the container image (production, development, etc)
+  stage: build
+  before_script:
+    - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
+  script:
+    - 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 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"' 
+    - changes:
+      - src/$IMAGE_NAME/**/*.{py,in,yml}
+      - src/$IMAGE_NAME/Dockerfile
+      - src/$IMAGE_NAME/tests/*.py
+      - src/$IMAGE_NAME/tests/Dockerfile
+      - manifests/${IMAGE_NAME}service.yaml
+      - .gitlab-ci.yml
+
+# apply unit test to the webui component
+unit test webui:
+  variables:
+    IMAGE_NAME: 'webui' # name of the microservice
+    IMAGE_TAG: 'latest' # tag of the container image (production, development, etc)
+  stage: unit_test
+  needs:
+    - build webui
+  before_script:
+    - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
+    - if docker network list | grep teraflowbridge; then echo "teraflowbridge is already created"; else docker network create -d bridge teraflowbridge; fi
+    - 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 8004:8004 -v "$PWD/src/$IMAGE_NAME/tests:/opt/results" --network=teraflowbridge  --rm $CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG
+    - sleep 5
+    - docker ps -a
+    - docker logs $IMAGE_NAME
+    - docker exec --user root -i $IMAGE_NAME bash -c "coverage run -m pytest --log-level=DEBUG --verbose ${IMAGE_NAME}/tests/test_unitary.py --junitxml=/opt/results/${IMAGE_NAME}_report.xml; coverage xml -o /opt/results/${IMAGE_NAME}_coverage.xml; ls -la /opt/results; coverage report --include='${IMAGE_NAME}/*' --show-missing"
+  coverage: '/TOTAL\s+\d+\s+\d+\s+(\d+%)/'
+  after_script:
+    - docker rm -f $IMAGE_NAME
+    - docker network rm teraflowbridge
+  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"' 
+    - changes:
+      - src/$IMAGE_NAME/**/*.{py,in,yml}
+      - src/$IMAGE_NAME/Dockerfile
+      - src/$IMAGE_NAME/tests/*.py
+      - src/$IMAGE_NAME/tests/Dockerfile
+      - manifests/${IMAGE_NAME}service.yaml
+      - .gitlab-ci.yml
+  artifacts:
+      when: always
+      reports:
+        junit: src/$IMAGE_NAME/tests/${IMAGE_NAME}_report.xml
+        cobertura: src/$IMAGE_NAME/tests/${IMAGE_NAME}_coverage.xml
+
+# Deployment of the webui service in Kubernetes Cluster
+deploy webui:
+  variables:
+    IMAGE_NAME: 'webui' # name of the microservice
+    IMAGE_TAG: 'latest' # tag of the container image (production, development, etc)
+  stage: deploy
+  needs:
+    - unit test webui
+    # - integ_test execute
+  script:
+    - 'sed -i "s/$IMAGE_NAME:.*/$IMAGE_NAME:$IMAGE_TAG/" manifests/${IMAGE_NAME}service.yaml'
+    - kubectl version
+    - kubectl get all
+    - kubectl apply -f "manifests/${IMAGE_NAME}service.yaml"
+    - kubectl get all
+  # environment:
+  #   name: test
+  #   url: https://example.com
+  #   kubernetes:
+  #     namespace: test
+  rules:
+    - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && ($CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "develop" || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == $CI_DEFAULT_BRANCH)'
+      when: manual    
+    - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "develop"'
+      when: manual
\ No newline at end of file
diff --git a/src/webui/Config.py b/src/webui/Config.py
new file mode 100644
index 0000000000000000000000000000000000000000..e7720a405e2874c3679f9f507df2fdffc610f84a
--- /dev/null
+++ b/src/webui/Config.py
@@ -0,0 +1,38 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+import logging
+
+# General settings
+LOG_LEVEL = logging.DEBUG
+
+# gRPC settings
+WEBUI_SERVICE_PORT = 8004
+
+# Prometheus settings
+METRICS_PORT = 9192
+
+SECRET_KEY = '>s&}24@{]]#k3&^5$f3#?6?h3{W@[}/7z}2pa]>{3&5%RP<)[('
+MAX_CONTENT_LENGTH = 1024*1024
+
+HOST = '0.0.0.0'  # accepts connections coming from any ADDRESS
+
+DEBUG=False
+
+CONTEXT_SERVICE_ADDRESS = os.environ.get('CONTEXTSERVICE_SERVICE_HOST', 'contextservice')
+CONTEXT_SERVICE_PORT = int(os.environ.get('CONTEXTSERVICE_SERVICE_PORT_GRPC', 1010))
+
+DEVICE_SERVICE_ADDRESS = os.environ.get('DEVICESERVICE_SERVICE_HOST', 'deviceservice')
+DEVICE_SERVICE_PORT = int(os.environ.get('DEVICESERVICE_SERVICE_PORT_GRPC', 2020))
diff --git a/src/webui/Dockerfile b/src/webui/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..c22ba2e8b4fe895a93ee944a740a4108ed1c0e4c
--- /dev/null
+++ b/src/webui/Dockerfile
@@ -0,0 +1,60 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+FROM python:3-slim
+
+# Ref: https://pythonspeed.com/articles/activate-virtualenv-dockerfile/
+
+# Install dependencies
+RUN apt-get --yes --quiet --quiet update && \
+    apt-get --yes --quiet --quiet install wget g++ && \
+    rm -rf /var/lib/apt/lists/*
+
+# Set Python to show logs as they occur
+ENV PYTHONUNBUFFERED=0
+ENV PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION='python'
+
+# Download the gRPC health probe -- not needed here... health will be asserted using HTTP
+# RUN GRPC_HEALTH_PROBE_VERSION=v0.2.0 && \
+#     wget -qO/bin/grpc_health_probe https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/${GRPC_HEALTH_PROBE_VERSION}/grpc_health_probe-linux-amd64 && \
+#     chmod +x /bin/grpc_health_probe
+
+# creating a user for security reasons
+RUN groupadd -r webui && useradd --no-log-init -r -m -g webui webui
+USER webui
+
+# set working directory
+RUN mkdir -p /home/webui/teraflow
+WORKDIR /home/webui/teraflow
+
+# Get Python packages per module
+ENV VIRTUAL_ENV=/home/webui/venv
+RUN python3 -m venv ${VIRTUAL_ENV}
+ENV PATH="${VIRTUAL_ENV}/bin:${PATH}"
+COPY --chown=webui:webui webui/requirements.in /home/webui/teraflow/webui/requirements.in
+RUN pip install --upgrade pip setuptools wheel pip-tools && pip-compile --output-file=webui/requirements.txt webui/requirements.in
+RUN pip install -r webui/requirements.txt
+
+# Add files into working directory
+COPY --chown=webui:webui common/. common
+COPY --chown=webui:webui context/__init__.py context/__init__.py
+COPY --chown=webui:webui context/proto/. context/proto
+COPY --chown=webui:webui context/client/. context/client
+COPY --chown=webui:webui device/__init__.py device/__init__.py
+COPY --chown=webui:webui device/proto/. device/proto
+COPY --chown=webui:webui device/client/. device/client
+COPY --chown=webui:webui webui/. webui
+
+# Start webui service
+ENTRYPOINT ["python", "-m", "webui.service"]
diff --git a/src/webui/__init__.py b/src/webui/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..70a33251242c51f49140e596b8208a19dd5245f7
--- /dev/null
+++ b/src/webui/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/webui/genproto.sh b/src/webui/genproto.sh
new file mode 100755
index 0000000000000000000000000000000000000000..18a0d4f921c3bca9b838a47e06ddb02e344e957c
--- /dev/null
+++ b/src/webui/genproto.sh
@@ -0,0 +1,46 @@
+#!/bin/bash -eu
+#
+# Copyright 2018 Google LLC
+#
+# 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.
+
+#!/bin/bash -e
+
+# Make folder containing the script the root folder for its execution
+cd $(dirname $0)
+
+rm -rf proto/*.py
+rm -rf proto/__pycache__
+touch proto/__init__.py
+
+# building protos of services used
+# python -m grpc_tools.protoc -I../../proto --python_out=proto --grpc_python_out=proto compute.proto
+python -m grpc_tools.protoc -I../../proto --python_out=proto --grpc_python_out=proto context.proto
+python -m grpc_tools.protoc -I../../proto --python_out=proto --grpc_python_out=proto device.proto
+python -m grpc_tools.protoc -I../../proto --python_out=proto --grpc_python_out=proto service.proto
+python -m grpc_tools.protoc -I../../proto --python_out=proto --grpc_python_out=proto monitoring.proto
+python -m grpc_tools.protoc -I../../proto --python_out=proto --grpc_python_out=proto kpi_sample_types.proto
+
+# rm proto/compute_pb2_grpc.py
+rm proto/context_pb2_grpc.py
+rm proto/device_pb2_grpc.py
+rm proto/service_pb2_grpc.py
+rm proto/monitoring_pb2_grpc.py
+rm proto/kpi_sample_types_pb2_grpc.py
+
+# sed -i -E 's/(import\ .*)_pb2/from . \1_pb2/g' proto/compute_pb2.py
+sed -i -E 's/(import\ .*)_pb2/from . \1_pb2/g' proto/context_pb2.py
+sed -i -E 's/(import\ .*)_pb2/from . \1_pb2/g' proto/device_pb2.py
+sed -i -E 's/(import\ .*)_pb2/from . \1_pb2/g' proto/service_pb2.py
+sed -i -E 's/(import\ .*)_pb2/from . \1_pb2/g' proto/monitoring_pb2.py
+sed -i -E 's/(import\ .*)_pb2/from . \1_pb2/g' proto/kpi_sample_types_pb2.py
diff --git a/src/webui/grafana_dashboard.json b/src/webui/grafana_dashboard.json
new file mode 100644
index 0000000000000000000000000000000000000000..afe3ea1260cae8781fea1e29e30e2d1651ab7c2e
--- /dev/null
+++ b/src/webui/grafana_dashboard.json
@@ -0,0 +1,325 @@
+{"overwrite": true, "folderId": 0, "dashboard":
+  {
+    "id": null,
+    "annotations": {
+      "list": [
+        {
+          "builtIn": 1,
+          "datasource": "-- Grafana --",
+          "enable": true,
+          "hide": true,
+          "iconColor": "rgba(0, 211, 255, 1)",
+          "name": "Annotations & Alerts",
+          "target": {
+            "limit": 100,
+            "matchAny": false,
+            "tags": [],
+            "type": "dashboard"
+          },
+          "type": "dashboard"
+        }
+      ]
+    },
+    "editable": true,
+    "fiscalYearStartMonth": 0,
+    "gnetId": null,
+    "graphTooltip": 0,
+    "iteration": 1646406031197,
+    "links": [],
+    "liveNow": false,
+    "panels": [
+      {
+        "datasource": null,
+        "fieldConfig": {
+          "defaults": {
+            "color": {
+              "mode": "palette-classic"
+            },
+            "custom": {
+              "axisLabel": "",
+              "axisPlacement": "auto",
+              "barAlignment": 0,
+              "drawStyle": "line",
+              "fillOpacity": 0,
+              "gradientMode": "none",
+              "hideFrom": {
+                "legend": false,
+                "tooltip": false,
+                "viz": false
+              },
+              "lineInterpolation": "smooth",
+              "lineWidth": 1,
+              "pointSize": 5,
+              "scaleDistribution": {
+                "type": "linear"
+              },
+              "showPoints": "never",
+              "spanNulls": false,
+              "stacking": {
+                "group": "A",
+                "mode": "none"
+              },
+              "thresholdsStyle": {
+                "mode": "off"
+              }
+            },
+            "mappings": [],
+            "thresholds": {
+              "mode": "absolute",
+              "steps": [
+                {
+                  "color": "green",
+                  "value": null
+                },
+                {
+                  "color": "red",
+                  "value": 80
+                }
+              ]
+            }
+          },
+          "overrides": [
+            {
+              "matcher": {
+                "id": "byRegexp",
+                "options": ".* PACKETS_.*"
+              },
+              "properties": [
+                {
+                  "id": "custom.axisPlacement",
+                  "value": "left"
+                },
+                {
+                  "id": "unit",
+                  "value": "pps"
+                },
+                {
+                  "id": "custom.axisLabel",
+                  "value": "Packets / sec"
+                },
+                {
+                  "id": "custom.axisSoftMin",
+                  "value": 0
+                }
+              ]
+            },
+            {
+              "matcher": {
+                "id": "byRegexp",
+                "options": ".* BYTES_.*"
+              },
+              "properties": [
+                {
+                  "id": "custom.axisPlacement",
+                  "value": "right"
+                },
+                {
+                  "id": "unit",
+                  "value": "Bps"
+                },
+                {
+                  "id": "custom.axisLabel",
+                  "value": "Bytes / sec"
+                },
+                {
+                  "id": "custom.axisSoftMin",
+                  "value": 0
+                }
+              ]
+            }
+          ]
+        },
+        "gridPos": {
+          "h": 19,
+          "w": 24,
+          "x": 0,
+          "y": 0
+        },
+        "id": 2,
+        "options": {
+          "legend": {
+            "calcs": [
+              "first",
+              "min",
+              "mean",
+              "max",
+              "lastNotNull"
+            ],
+            "displayMode": "table",
+            "placement": "right"
+          },
+          "tooltip": {
+            "mode": "multi"
+          }
+        },
+        "targets": [
+          {
+            "alias": "$tag_device_id $tag_endpoint_id $tag_kpi_sample_type",
+            "groupBy": [
+              {
+                "params": [
+                  "device_id"
+                ],
+                "type": "tag"
+              },
+              {
+                "params": [
+                  "endpoint_id"
+                ],
+                "type": "tag"
+              },
+              {
+                "params": [
+                  "kpi_sample_type"
+                ],
+                "type": "tag"
+              }
+            ],
+            "measurement": "samples",
+            "orderByTime": "ASC",
+            "policy": "autogen",
+            "refId": "A",
+            "resultFormat": "time_series",
+            "select": [
+              [
+                {
+                  "params": [
+                    "kpi_value"
+                  ],
+                  "type": "field"
+                }
+              ]
+            ],
+            "tags": [
+              {
+                "key": "device_id",
+                "operator": "=~",
+                "value": "/^$device_id$/"
+              },
+              {
+                "condition": "AND",
+                "key": "endpoint_id",
+                "operator": "=~",
+                "value": "/^$endpoint_id$/"
+              },
+              {
+                "condition": "AND",
+                "key": "kpi_sample_type",
+                "operator": "=~",
+                "value": "/^$kpi_sample_type$/"
+              }
+            ]
+          }
+        ],
+        "title": "L3 Monitoring Packets/Bytes Received/Sent",
+        "transformations": [],
+        "type": "timeseries"
+      }
+    ],
+    "refresh": "5s",
+    "schemaVersion": 32,
+    "style": "dark",
+    "tags": [],
+    "templating": {
+      "list": [
+        {
+          "allValue": null,
+          "current": {
+            "selected": true,
+            "text": [
+              "R1-INF",
+              "R3-INF"
+            ],
+            "value": [
+              "R1-INF",
+              "R3-INF"
+            ]
+          },
+          "datasource": null,
+          "definition": "SHOW TAG VALUES FROM samples WITH KEY=\"device_id\"",
+          "description": null,
+          "error": null,
+          "hide": 0,
+          "includeAll": true,
+          "label": "Device",
+          "multi": true,
+          "name": "device_id",
+          "options": [],
+          "query": "SHOW TAG VALUES FROM samples WITH KEY=\"device_id\"",
+          "refresh": 2,
+          "regex": "",
+          "skipUrlSync": false,
+          "sort": 0,
+          "type": "query"
+        },
+        {
+          "allValue": null,
+          "current": {
+            "selected": true,
+            "text": [
+              "13/2/0",
+              "13/2/1"
+            ],
+            "value": [
+              "13/2/0",
+              "13/2/1"
+            ]
+          },
+          "datasource": null,
+          "definition": "SHOW TAG VALUES FROM samples WITH KEY=\"endpoint_id\" WHERE \"device_id\"=~/^$device_id$/",
+          "description": null,
+          "error": null,
+          "hide": 0,
+          "includeAll": true,
+          "label": "EndPoint",
+          "multi": true,
+          "name": "endpoint_id",
+          "options": [],
+          "query": "SHOW TAG VALUES FROM samples WITH KEY=\"endpoint_id\" WHERE \"device_id\"=~/^$device_id$/",
+          "refresh": 2,
+          "regex": "",
+          "skipUrlSync": false,
+          "sort": 0,
+          "type": "query"
+        },
+        {
+          "allValue": null,
+          "current": {
+            "selected": true,
+            "text": [
+              "All"
+            ],
+            "value": [
+              "$__all"
+            ]
+          },
+          "datasource": null,
+          "definition": "SHOW TAG VALUES FROM samples WITH KEY=\"kpi_sample_type\"",
+          "description": null,
+          "error": null,
+          "hide": 0,
+          "includeAll": true,
+          "label": "Kpi Sample Type",
+          "multi": true,
+          "name": "kpi_sample_type",
+          "options": [],
+          "query": "SHOW TAG VALUES FROM samples WITH KEY=\"kpi_sample_type\"",
+          "refresh": 2,
+          "regex": "",
+          "skipUrlSync": false,
+          "sort": 0,
+          "type": "query"
+        }
+      ]
+    },
+    "time": {
+      "from": "now-15m",
+      "to": "now"
+    },
+    "timepicker": {},
+    "timezone": "",
+    "title": "L3 Monitoring",
+    "uid": "tf-l3-monit",
+    "version": 2
+  }
+}
diff --git a/src/webui/proto/__init__.py b/src/webui/proto/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..70a33251242c51f49140e596b8208a19dd5245f7
--- /dev/null
+++ b/src/webui/proto/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/webui/proto/context_pb2.py b/src/webui/proto/context_pb2.py
new file mode 100644
index 0000000000000000000000000000000000000000..68602b16f264ceac9acc3ef6669b09d5984e72c2
--- /dev/null
+++ b/src/webui/proto/context_pb2.py
@@ -0,0 +1,2662 @@
+# -*- coding: utf-8 -*-
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: context.proto
+"""Generated protocol buffer code."""
+from google.protobuf.internal import enum_type_wrapper
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import message as _message
+from google.protobuf import reflection as _reflection
+from google.protobuf import symbol_database as _symbol_database
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+from . import kpi_sample_types_pb2 as kpi__sample__types__pb2
+
+
+DESCRIPTOR = _descriptor.FileDescriptor(
+  name='context.proto',
+  package='context',
+  syntax='proto3',
+  serialized_options=None,
+  create_key=_descriptor._internal_create_key,
+  serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xad\x10\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3'
+  ,
+  dependencies=[kpi__sample__types__pb2.DESCRIPTOR,])
+
+_EVENTTYPEENUM = _descriptor.EnumDescriptor(
+  name='EventTypeEnum',
+  full_name='context.EventTypeEnum',
+  filename=None,
+  file=DESCRIPTOR,
+  create_key=_descriptor._internal_create_key,
+  values=[
+    _descriptor.EnumValueDescriptor(
+      name='EVENTTYPE_UNDEFINED', index=0, number=0,
+      serialized_options=None,
+      type=None,
+      create_key=_descriptor._internal_create_key),
+    _descriptor.EnumValueDescriptor(
+      name='EVENTTYPE_CREATE', index=1, number=1,
+      serialized_options=None,
+      type=None,
+      create_key=_descriptor._internal_create_key),
+    _descriptor.EnumValueDescriptor(
+      name='EVENTTYPE_UPDATE', index=2, number=2,
+      serialized_options=None,
+      type=None,
+      create_key=_descriptor._internal_create_key),
+    _descriptor.EnumValueDescriptor(
+      name='EVENTTYPE_REMOVE', index=3, number=3,
+      serialized_options=None,
+      type=None,
+      create_key=_descriptor._internal_create_key),
+  ],
+  containing_type=None,
+  serialized_options=None,
+  serialized_start=3703,
+  serialized_end=3809,
+)
+_sym_db.RegisterEnumDescriptor(_EVENTTYPEENUM)
+
+EventTypeEnum = enum_type_wrapper.EnumTypeWrapper(_EVENTTYPEENUM)
+_DEVICEDRIVERENUM = _descriptor.EnumDescriptor(
+  name='DeviceDriverEnum',
+  full_name='context.DeviceDriverEnum',
+  filename=None,
+  file=DESCRIPTOR,
+  create_key=_descriptor._internal_create_key,
+  values=[
+    _descriptor.EnumValueDescriptor(
+      name='DEVICEDRIVER_UNDEFINED', index=0, number=0,
+      serialized_options=None,
+      type=None,
+      create_key=_descriptor._internal_create_key),
+    _descriptor.EnumValueDescriptor(
+      name='DEVICEDRIVER_OPENCONFIG', index=1, number=1,
+      serialized_options=None,
+      type=None,
+      create_key=_descriptor._internal_create_key),
+    _descriptor.EnumValueDescriptor(
+      name='DEVICEDRIVER_TRANSPORT_API', index=2, number=2,
+      serialized_options=None,
+      type=None,
+      create_key=_descriptor._internal_create_key),
+    _descriptor.EnumValueDescriptor(
+      name='DEVICEDRIVER_P4', index=3, number=3,
+      serialized_options=None,
+      type=None,
+      create_key=_descriptor._internal_create_key),
+    _descriptor.EnumValueDescriptor(
+      name='DEVICEDRIVER_IETF_NETWORK_TOPOLOGY', index=4, number=4,
+      serialized_options=None,
+      type=None,
+      create_key=_descriptor._internal_create_key),
+    _descriptor.EnumValueDescriptor(
+      name='DEVICEDRIVER_ONF_TR_352', index=5, number=5,
+      serialized_options=None,
+      type=None,
+      create_key=_descriptor._internal_create_key),
+  ],
+  containing_type=None,
+  serialized_options=None,
+  serialized_start=3812,
+  serialized_end=4009,
+)
+_sym_db.RegisterEnumDescriptor(_DEVICEDRIVERENUM)
+
+DeviceDriverEnum = enum_type_wrapper.EnumTypeWrapper(_DEVICEDRIVERENUM)
+_DEVICEOPERATIONALSTATUSENUM = _descriptor.EnumDescriptor(
+  name='DeviceOperationalStatusEnum',
+  full_name='context.DeviceOperationalStatusEnum',
+  filename=None,
+  file=DESCRIPTOR,
+  create_key=_descriptor._internal_create_key,
+  values=[
+    _descriptor.EnumValueDescriptor(
+      name='DEVICEOPERATIONALSTATUS_UNDEFINED', index=0, number=0,
+      serialized_options=None,
+      type=None,
+      create_key=_descriptor._internal_create_key),
+    _descriptor.EnumValueDescriptor(
+      name='DEVICEOPERATIONALSTATUS_DISABLED', index=1, number=1,
+      serialized_options=None,
+      type=None,
+      create_key=_descriptor._internal_create_key),
+    _descriptor.EnumValueDescriptor(
+      name='DEVICEOPERATIONALSTATUS_ENABLED', index=2, number=2,
+      serialized_options=None,
+      type=None,
+      create_key=_descriptor._internal_create_key),
+  ],
+  containing_type=None,
+  serialized_options=None,
+  serialized_start=4012,
+  serialized_end=4155,
+)
+_sym_db.RegisterEnumDescriptor(_DEVICEOPERATIONALSTATUSENUM)
+
+DeviceOperationalStatusEnum = enum_type_wrapper.EnumTypeWrapper(_DEVICEOPERATIONALSTATUSENUM)
+_SERVICETYPEENUM = _descriptor.EnumDescriptor(
+  name='ServiceTypeEnum',
+  full_name='context.ServiceTypeEnum',
+  filename=None,
+  file=DESCRIPTOR,
+  create_key=_descriptor._internal_create_key,
+  values=[
+    _descriptor.EnumValueDescriptor(
+      name='SERVICETYPE_UNKNOWN', index=0, number=0,
+      serialized_options=None,
+      type=None,
+      create_key=_descriptor._internal_create_key),
+    _descriptor.EnumValueDescriptor(
+      name='SERVICETYPE_L3NM', index=1, number=1,
+      serialized_options=None,
+      type=None,
+      create_key=_descriptor._internal_create_key),
+    _descriptor.EnumValueDescriptor(
+      name='SERVICETYPE_L2NM', index=2, number=2,
+      serialized_options=None,
+      type=None,
+      create_key=_descriptor._internal_create_key),
+    _descriptor.EnumValueDescriptor(
+      name='SERVICETYPE_TAPI_CONNECTIVITY_SERVICE', index=3, number=3,
+      serialized_options=None,
+      type=None,
+      create_key=_descriptor._internal_create_key),
+  ],
+  containing_type=None,
+  serialized_options=None,
+  serialized_start=4158,
+  serialized_end=4287,
+)
+_sym_db.RegisterEnumDescriptor(_SERVICETYPEENUM)
+
+ServiceTypeEnum = enum_type_wrapper.EnumTypeWrapper(_SERVICETYPEENUM)
+_SERVICESTATUSENUM = _descriptor.EnumDescriptor(
+  name='ServiceStatusEnum',
+  full_name='context.ServiceStatusEnum',
+  filename=None,
+  file=DESCRIPTOR,
+  create_key=_descriptor._internal_create_key,
+  values=[
+    _descriptor.EnumValueDescriptor(
+      name='SERVICESTATUS_UNDEFINED', index=0, number=0,
+      serialized_options=None,
+      type=None,
+      create_key=_descriptor._internal_create_key),
+    _descriptor.EnumValueDescriptor(
+      name='SERVICESTATUS_PLANNED', index=1, number=1,
+      serialized_options=None,
+      type=None,
+      create_key=_descriptor._internal_create_key),
+    _descriptor.EnumValueDescriptor(
+      name='SERVICESTATUS_ACTIVE', index=2, number=2,
+      serialized_options=None,
+      type=None,
+      create_key=_descriptor._internal_create_key),
+    _descriptor.EnumValueDescriptor(
+      name='SERVICESTATUS_PENDING_REMOVAL', index=3, number=3,
+      serialized_options=None,
+      type=None,
+      create_key=_descriptor._internal_create_key),
+  ],
+  containing_type=None,
+  serialized_options=None,
+  serialized_start=4290,
+  serialized_end=4426,
+)
+_sym_db.RegisterEnumDescriptor(_SERVICESTATUSENUM)
+
+ServiceStatusEnum = enum_type_wrapper.EnumTypeWrapper(_SERVICESTATUSENUM)
+_CONFIGACTIONENUM = _descriptor.EnumDescriptor(
+  name='ConfigActionEnum',
+  full_name='context.ConfigActionEnum',
+  filename=None,
+  file=DESCRIPTOR,
+  create_key=_descriptor._internal_create_key,
+  values=[
+    _descriptor.EnumValueDescriptor(
+      name='CONFIGACTION_UNDEFINED', index=0, number=0,
+      serialized_options=None,
+      type=None,
+      create_key=_descriptor._internal_create_key),
+    _descriptor.EnumValueDescriptor(
+      name='CONFIGACTION_SET', index=1, number=1,
+      serialized_options=None,
+      type=None,
+      create_key=_descriptor._internal_create_key),
+    _descriptor.EnumValueDescriptor(
+      name='CONFIGACTION_DELETE', index=2, number=2,
+      serialized_options=None,
+      type=None,
+      create_key=_descriptor._internal_create_key),
+  ],
+  containing_type=None,
+  serialized_options=None,
+  serialized_start=4428,
+  serialized_end=4521,
+)
+_sym_db.RegisterEnumDescriptor(_CONFIGACTIONENUM)
+
+ConfigActionEnum = enum_type_wrapper.EnumTypeWrapper(_CONFIGACTIONENUM)
+EVENTTYPE_UNDEFINED = 0
+EVENTTYPE_CREATE = 1
+EVENTTYPE_UPDATE = 2
+EVENTTYPE_REMOVE = 3
+DEVICEDRIVER_UNDEFINED = 0
+DEVICEDRIVER_OPENCONFIG = 1
+DEVICEDRIVER_TRANSPORT_API = 2
+DEVICEDRIVER_P4 = 3
+DEVICEDRIVER_IETF_NETWORK_TOPOLOGY = 4
+DEVICEDRIVER_ONF_TR_352 = 5
+DEVICEOPERATIONALSTATUS_UNDEFINED = 0
+DEVICEOPERATIONALSTATUS_DISABLED = 1
+DEVICEOPERATIONALSTATUS_ENABLED = 2
+SERVICETYPE_UNKNOWN = 0
+SERVICETYPE_L3NM = 1
+SERVICETYPE_L2NM = 2
+SERVICETYPE_TAPI_CONNECTIVITY_SERVICE = 3
+SERVICESTATUS_UNDEFINED = 0
+SERVICESTATUS_PLANNED = 1
+SERVICESTATUS_ACTIVE = 2
+SERVICESTATUS_PENDING_REMOVAL = 3
+CONFIGACTION_UNDEFINED = 0
+CONFIGACTION_SET = 1
+CONFIGACTION_DELETE = 2
+
+
+
+_EMPTY = _descriptor.Descriptor(
+  name='Empty',
+  full_name='context.Empty',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  create_key=_descriptor._internal_create_key,
+  fields=[
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=50,
+  serialized_end=57,
+)
+
+
+_UUID = _descriptor.Descriptor(
+  name='Uuid',
+  full_name='context.Uuid',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  create_key=_descriptor._internal_create_key,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='uuid', full_name='context.Uuid.uuid', index=0,
+      number=1, type=9, cpp_type=9, label=1,
+      has_default_value=False, default_value=b"".decode('utf-8'),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=59,
+  serialized_end=79,
+)
+
+
+_EVENT = _descriptor.Descriptor(
+  name='Event',
+  full_name='context.Event',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  create_key=_descriptor._internal_create_key,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='timestamp', full_name='context.Event.timestamp', index=0,
+      number=1, type=1, cpp_type=5, label=1,
+      has_default_value=False, default_value=float(0),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='event_type', full_name='context.Event.event_type', index=1,
+      number=2, type=14, cpp_type=8, label=1,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=81,
+  serialized_end=151,
+)
+
+
+_CONTEXTID = _descriptor.Descriptor(
+  name='ContextId',
+  full_name='context.ContextId',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  create_key=_descriptor._internal_create_key,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='context_uuid', full_name='context.ContextId.context_uuid', index=0,
+      number=1, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=153,
+  serialized_end=201,
+)
+
+
+_CONTEXT = _descriptor.Descriptor(
+  name='Context',
+  full_name='context.Context',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  create_key=_descriptor._internal_create_key,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='context_id', full_name='context.Context.context_id', index=0,
+      number=1, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='topology_ids', full_name='context.Context.topology_ids', index=1,
+      number=2, type=11, cpp_type=10, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='service_ids', full_name='context.Context.service_ids', index=2,
+      number=3, type=11, cpp_type=10, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='controller', full_name='context.Context.controller', index=3,
+      number=4, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=204,
+  serialized_end=386,
+)
+
+
+_CONTEXTIDLIST = _descriptor.Descriptor(
+  name='ContextIdList',
+  full_name='context.ContextIdList',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  create_key=_descriptor._internal_create_key,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='context_ids', full_name='context.ContextIdList.context_ids', index=0,
+      number=1, type=11, cpp_type=10, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=388,
+  serialized_end=444,
+)
+
+
+_CONTEXTLIST = _descriptor.Descriptor(
+  name='ContextList',
+  full_name='context.ContextList',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  create_key=_descriptor._internal_create_key,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='contexts', full_name='context.ContextList.contexts', index=0,
+      number=1, type=11, cpp_type=10, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=446,
+  serialized_end=495,
+)
+
+
+_CONTEXTEVENT = _descriptor.Descriptor(
+  name='ContextEvent',
+  full_name='context.ContextEvent',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  create_key=_descriptor._internal_create_key,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='event', full_name='context.ContextEvent.event', index=0,
+      number=1, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='context_id', full_name='context.ContextEvent.context_id', index=1,
+      number=2, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=497,
+  serialized_end=582,
+)
+
+
+_TOPOLOGYID = _descriptor.Descriptor(
+  name='TopologyId',
+  full_name='context.TopologyId',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  create_key=_descriptor._internal_create_key,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='context_id', full_name='context.TopologyId.context_id', index=0,
+      number=1, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='topology_uuid', full_name='context.TopologyId.topology_uuid', index=1,
+      number=2, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=584,
+  serialized_end=674,
+)
+
+
+_TOPOLOGY = _descriptor.Descriptor(
+  name='Topology',
+  full_name='context.Topology',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  create_key=_descriptor._internal_create_key,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='topology_id', full_name='context.Topology.topology_id', index=0,
+      number=1, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='device_ids', full_name='context.Topology.device_ids', index=1,
+      number=2, type=11, cpp_type=10, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='link_ids', full_name='context.Topology.link_ids', index=2,
+      number=3, type=11, cpp_type=10, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=676,
+  serialized_end=802,
+)
+
+
+_TOPOLOGYIDLIST = _descriptor.Descriptor(
+  name='TopologyIdList',
+  full_name='context.TopologyIdList',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  create_key=_descriptor._internal_create_key,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='topology_ids', full_name='context.TopologyIdList.topology_ids', index=0,
+      number=1, type=11, cpp_type=10, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=804,
+  serialized_end=863,
+)
+
+
+_TOPOLOGYLIST = _descriptor.Descriptor(
+  name='TopologyList',
+  full_name='context.TopologyList',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  create_key=_descriptor._internal_create_key,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='topologies', full_name='context.TopologyList.topologies', index=0,
+      number=1, type=11, cpp_type=10, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=865,
+  serialized_end=918,
+)
+
+
+_TOPOLOGYEVENT = _descriptor.Descriptor(
+  name='TopologyEvent',
+  full_name='context.TopologyEvent',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  create_key=_descriptor._internal_create_key,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='event', full_name='context.TopologyEvent.event', index=0,
+      number=1, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='topology_id', full_name='context.TopologyEvent.topology_id', index=1,
+      number=2, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=920,
+  serialized_end=1008,
+)
+
+
+_DEVICEID = _descriptor.Descriptor(
+  name='DeviceId',
+  full_name='context.DeviceId',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  create_key=_descriptor._internal_create_key,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='device_uuid', full_name='context.DeviceId.device_uuid', index=0,
+      number=1, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=1010,
+  serialized_end=1056,
+)
+
+
+_DEVICE = _descriptor.Descriptor(
+  name='Device',
+  full_name='context.Device',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  create_key=_descriptor._internal_create_key,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='device_id', full_name='context.Device.device_id', index=0,
+      number=1, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='device_type', full_name='context.Device.device_type', index=1,
+      number=2, type=9, cpp_type=9, label=1,
+      has_default_value=False, default_value=b"".decode('utf-8'),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='device_config', full_name='context.Device.device_config', index=2,
+      number=3, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='device_operational_status', full_name='context.Device.device_operational_status', index=3,
+      number=4, type=14, cpp_type=8, label=1,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='device_drivers', full_name='context.Device.device_drivers', index=4,
+      number=5, type=14, cpp_type=8, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='device_endpoints', full_name='context.Device.device_endpoints', index=5,
+      number=6, type=11, cpp_type=10, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=1059,
+  serialized_end=1341,
+)
+
+
+_DEVICECONFIG = _descriptor.Descriptor(
+  name='DeviceConfig',
+  full_name='context.DeviceConfig',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  create_key=_descriptor._internal_create_key,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='config_rules', full_name='context.DeviceConfig.config_rules', index=0,
+      number=1, type=11, cpp_type=10, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=1343,
+  serialized_end=1400,
+)
+
+
+_DEVICEIDLIST = _descriptor.Descriptor(
+  name='DeviceIdList',
+  full_name='context.DeviceIdList',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  create_key=_descriptor._internal_create_key,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='device_ids', full_name='context.DeviceIdList.device_ids', index=0,
+      number=1, type=11, cpp_type=10, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=1402,
+  serialized_end=1455,
+)
+
+
+_DEVICELIST = _descriptor.Descriptor(
+  name='DeviceList',
+  full_name='context.DeviceList',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  create_key=_descriptor._internal_create_key,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='devices', full_name='context.DeviceList.devices', index=0,
+      number=1, type=11, cpp_type=10, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=1457,
+  serialized_end=1503,
+)
+
+
+_DEVICEEVENT = _descriptor.Descriptor(
+  name='DeviceEvent',
+  full_name='context.DeviceEvent',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  create_key=_descriptor._internal_create_key,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='event', full_name='context.DeviceEvent.event', index=0,
+      number=1, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='device_id', full_name='context.DeviceEvent.device_id', index=1,
+      number=2, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=1505,
+  serialized_end=1587,
+)
+
+
+_LINKID = _descriptor.Descriptor(
+  name='LinkId',
+  full_name='context.LinkId',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  create_key=_descriptor._internal_create_key,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='link_uuid', full_name='context.LinkId.link_uuid', index=0,
+      number=1, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=1589,
+  serialized_end=1631,
+)
+
+
+_LINK = _descriptor.Descriptor(
+  name='Link',
+  full_name='context.Link',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  create_key=_descriptor._internal_create_key,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='link_id', full_name='context.Link.link_id', index=0,
+      number=1, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='link_endpoint_ids', full_name='context.Link.link_endpoint_ids', index=1,
+      number=2, type=11, cpp_type=10, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=1633,
+  serialized_end=1721,
+)
+
+
+_LINKIDLIST = _descriptor.Descriptor(
+  name='LinkIdList',
+  full_name='context.LinkIdList',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  create_key=_descriptor._internal_create_key,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='link_ids', full_name='context.LinkIdList.link_ids', index=0,
+      number=1, type=11, cpp_type=10, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=1723,
+  serialized_end=1770,
+)
+
+
+_LINKLIST = _descriptor.Descriptor(
+  name='LinkList',
+  full_name='context.LinkList',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  create_key=_descriptor._internal_create_key,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='links', full_name='context.LinkList.links', index=0,
+      number=1, type=11, cpp_type=10, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=1772,
+  serialized_end=1812,
+)
+
+
+_LINKEVENT = _descriptor.Descriptor(
+  name='LinkEvent',
+  full_name='context.LinkEvent',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  create_key=_descriptor._internal_create_key,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='event', full_name='context.LinkEvent.event', index=0,
+      number=1, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='link_id', full_name='context.LinkEvent.link_id', index=1,
+      number=2, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=1814,
+  serialized_end=1890,
+)
+
+
+_SERVICEID = _descriptor.Descriptor(
+  name='ServiceId',
+  full_name='context.ServiceId',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  create_key=_descriptor._internal_create_key,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='context_id', full_name='context.ServiceId.context_id', index=0,
+      number=1, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='service_uuid', full_name='context.ServiceId.service_uuid', index=1,
+      number=2, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=1892,
+  serialized_end=1980,
+)
+
+
+_SERVICE = _descriptor.Descriptor(
+  name='Service',
+  full_name='context.Service',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  create_key=_descriptor._internal_create_key,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='service_id', full_name='context.Service.service_id', index=0,
+      number=1, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='service_type', full_name='context.Service.service_type', index=1,
+      number=2, type=14, cpp_type=8, label=1,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='service_endpoint_ids', full_name='context.Service.service_endpoint_ids', index=2,
+      number=3, type=11, cpp_type=10, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='service_constraints', full_name='context.Service.service_constraints', index=3,
+      number=4, type=11, cpp_type=10, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='service_status', full_name='context.Service.service_status', index=4,
+      number=5, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='service_config', full_name='context.Service.service_config', index=5,
+      number=6, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=1983,
+  serialized_end=2277,
+)
+
+
+_SERVICESTATUS = _descriptor.Descriptor(
+  name='ServiceStatus',
+  full_name='context.ServiceStatus',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  create_key=_descriptor._internal_create_key,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='service_status', full_name='context.ServiceStatus.service_status', index=0,
+      number=1, type=14, cpp_type=8, label=1,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=2279,
+  serialized_end=2346,
+)
+
+
+_SERVICECONFIG = _descriptor.Descriptor(
+  name='ServiceConfig',
+  full_name='context.ServiceConfig',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  create_key=_descriptor._internal_create_key,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='config_rules', full_name='context.ServiceConfig.config_rules', index=0,
+      number=1, type=11, cpp_type=10, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=2348,
+  serialized_end=2406,
+)
+
+
+_SERVICEIDLIST = _descriptor.Descriptor(
+  name='ServiceIdList',
+  full_name='context.ServiceIdList',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  create_key=_descriptor._internal_create_key,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='service_ids', full_name='context.ServiceIdList.service_ids', index=0,
+      number=1, type=11, cpp_type=10, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=2408,
+  serialized_end=2464,
+)
+
+
+_SERVICELIST = _descriptor.Descriptor(
+  name='ServiceList',
+  full_name='context.ServiceList',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  create_key=_descriptor._internal_create_key,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='services', full_name='context.ServiceList.services', index=0,
+      number=1, type=11, cpp_type=10, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=2466,
+  serialized_end=2515,
+)
+
+
+_SERVICEEVENT = _descriptor.Descriptor(
+  name='ServiceEvent',
+  full_name='context.ServiceEvent',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  create_key=_descriptor._internal_create_key,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='event', full_name='context.ServiceEvent.event', index=0,
+      number=1, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='service_id', full_name='context.ServiceEvent.service_id', index=1,
+      number=2, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=2517,
+  serialized_end=2602,
+)
+
+
+_CONNECTIONID = _descriptor.Descriptor(
+  name='ConnectionId',
+  full_name='context.ConnectionId',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  create_key=_descriptor._internal_create_key,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='connection_uuid', full_name='context.ConnectionId.connection_uuid', index=0,
+      number=1, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=2604,
+  serialized_end=2658,
+)
+
+
+_CONNECTION = _descriptor.Descriptor(
+  name='Connection',
+  full_name='context.Connection',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  create_key=_descriptor._internal_create_key,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='connection_id', full_name='context.Connection.connection_id', index=0,
+      number=1, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='service_id', full_name='context.Connection.service_id', index=1,
+      number=2, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='path_hops_endpoint_ids', full_name='context.Connection.path_hops_endpoint_ids', index=2,
+      number=3, type=11, cpp_type=10, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='sub_service_ids', full_name='context.Connection.sub_service_ids', index=3,
+      number=4, type=11, cpp_type=10, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=2661,
+  serialized_end=2857,
+)
+
+
+_CONNECTIONIDLIST = _descriptor.Descriptor(
+  name='ConnectionIdList',
+  full_name='context.ConnectionIdList',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  create_key=_descriptor._internal_create_key,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='connection_ids', full_name='context.ConnectionIdList.connection_ids', index=0,
+      number=1, type=11, cpp_type=10, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=2859,
+  serialized_end=2924,
+)
+
+
+_CONNECTIONLIST = _descriptor.Descriptor(
+  name='ConnectionList',
+  full_name='context.ConnectionList',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  create_key=_descriptor._internal_create_key,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='connections', full_name='context.ConnectionList.connections', index=0,
+      number=1, type=11, cpp_type=10, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=2926,
+  serialized_end=2984,
+)
+
+
+_CONNECTIONEVENT = _descriptor.Descriptor(
+  name='ConnectionEvent',
+  full_name='context.ConnectionEvent',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  create_key=_descriptor._internal_create_key,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='event', full_name='context.ConnectionEvent.event', index=0,
+      number=1, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='connection_id', full_name='context.ConnectionEvent.connection_id', index=1,
+      number=2, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=2986,
+  serialized_end=3080,
+)
+
+
+_ENDPOINTID = _descriptor.Descriptor(
+  name='EndPointId',
+  full_name='context.EndPointId',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  create_key=_descriptor._internal_create_key,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='topology_id', full_name='context.EndPointId.topology_id', index=0,
+      number=1, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='device_id', full_name='context.EndPointId.device_id', index=1,
+      number=2, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='endpoint_uuid', full_name='context.EndPointId.endpoint_uuid', index=2,
+      number=3, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=3083,
+  serialized_end=3213,
+)
+
+
+_ENDPOINT = _descriptor.Descriptor(
+  name='EndPoint',
+  full_name='context.EndPoint',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  create_key=_descriptor._internal_create_key,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='endpoint_id', full_name='context.EndPoint.endpoint_id', index=0,
+      number=1, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='endpoint_type', full_name='context.EndPoint.endpoint_type', index=1,
+      number=2, type=9, cpp_type=9, label=1,
+      has_default_value=False, default_value=b"".decode('utf-8'),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='kpi_sample_types', full_name='context.EndPoint.kpi_sample_types', index=2,
+      number=3, type=14, cpp_type=8, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=3216,
+  serialized_end=3350,
+)
+
+
+_CONFIGRULE = _descriptor.Descriptor(
+  name='ConfigRule',
+  full_name='context.ConfigRule',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  create_key=_descriptor._internal_create_key,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='action', full_name='context.ConfigRule.action', index=0,
+      number=1, type=14, cpp_type=8, label=1,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='resource_key', full_name='context.ConfigRule.resource_key', index=1,
+      number=2, type=9, cpp_type=9, label=1,
+      has_default_value=False, default_value=b"".decode('utf-8'),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='resource_value', full_name='context.ConfigRule.resource_value', index=2,
+      number=3, type=9, cpp_type=9, label=1,
+      has_default_value=False, default_value=b"".decode('utf-8'),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=3352,
+  serialized_end=3453,
+)
+
+
+_CONSTRAINT = _descriptor.Descriptor(
+  name='Constraint',
+  full_name='context.Constraint',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  create_key=_descriptor._internal_create_key,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='constraint_type', full_name='context.Constraint.constraint_type', index=0,
+      number=1, type=9, cpp_type=9, label=1,
+      has_default_value=False, default_value=b"".decode('utf-8'),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='constraint_value', full_name='context.Constraint.constraint_value', index=1,
+      number=2, type=9, cpp_type=9, label=1,
+      has_default_value=False, default_value=b"".decode('utf-8'),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=3455,
+  serialized_end=3518,
+)
+
+
+_TERAFLOWCONTROLLER = _descriptor.Descriptor(
+  name='TeraFlowController',
+  full_name='context.TeraFlowController',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  create_key=_descriptor._internal_create_key,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='context_id', full_name='context.TeraFlowController.context_id', index=0,
+      number=1, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='ip_address', full_name='context.TeraFlowController.ip_address', index=1,
+      number=2, type=9, cpp_type=9, label=1,
+      has_default_value=False, default_value=b"".decode('utf-8'),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='port', full_name='context.TeraFlowController.port', index=2,
+      number=3, type=13, cpp_type=3, label=1,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=3520,
+  serialized_end=3614,
+)
+
+
+_AUTHENTICATIONRESULT = _descriptor.Descriptor(
+  name='AuthenticationResult',
+  full_name='context.AuthenticationResult',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  create_key=_descriptor._internal_create_key,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='context_id', full_name='context.AuthenticationResult.context_id', index=0,
+      number=1, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='authenticated', full_name='context.AuthenticationResult.authenticated', index=1,
+      number=2, type=8, cpp_type=7, label=1,
+      has_default_value=False, default_value=False,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=3616,
+  serialized_end=3701,
+)
+
+_EVENT.fields_by_name['event_type'].enum_type = _EVENTTYPEENUM
+_CONTEXTID.fields_by_name['context_uuid'].message_type = _UUID
+_CONTEXT.fields_by_name['context_id'].message_type = _CONTEXTID
+_CONTEXT.fields_by_name['topology_ids'].message_type = _TOPOLOGYID
+_CONTEXT.fields_by_name['service_ids'].message_type = _SERVICEID
+_CONTEXT.fields_by_name['controller'].message_type = _TERAFLOWCONTROLLER
+_CONTEXTIDLIST.fields_by_name['context_ids'].message_type = _CONTEXTID
+_CONTEXTLIST.fields_by_name['contexts'].message_type = _CONTEXT
+_CONTEXTEVENT.fields_by_name['event'].message_type = _EVENT
+_CONTEXTEVENT.fields_by_name['context_id'].message_type = _CONTEXTID
+_TOPOLOGYID.fields_by_name['context_id'].message_type = _CONTEXTID
+_TOPOLOGYID.fields_by_name['topology_uuid'].message_type = _UUID
+_TOPOLOGY.fields_by_name['topology_id'].message_type = _TOPOLOGYID
+_TOPOLOGY.fields_by_name['device_ids'].message_type = _DEVICEID
+_TOPOLOGY.fields_by_name['link_ids'].message_type = _LINKID
+_TOPOLOGYIDLIST.fields_by_name['topology_ids'].message_type = _TOPOLOGYID
+_TOPOLOGYLIST.fields_by_name['topologies'].message_type = _TOPOLOGY
+_TOPOLOGYEVENT.fields_by_name['event'].message_type = _EVENT
+_TOPOLOGYEVENT.fields_by_name['topology_id'].message_type = _TOPOLOGYID
+_DEVICEID.fields_by_name['device_uuid'].message_type = _UUID
+_DEVICE.fields_by_name['device_id'].message_type = _DEVICEID
+_DEVICE.fields_by_name['device_config'].message_type = _DEVICECONFIG
+_DEVICE.fields_by_name['device_operational_status'].enum_type = _DEVICEOPERATIONALSTATUSENUM
+_DEVICE.fields_by_name['device_drivers'].enum_type = _DEVICEDRIVERENUM
+_DEVICE.fields_by_name['device_endpoints'].message_type = _ENDPOINT
+_DEVICECONFIG.fields_by_name['config_rules'].message_type = _CONFIGRULE
+_DEVICEIDLIST.fields_by_name['device_ids'].message_type = _DEVICEID
+_DEVICELIST.fields_by_name['devices'].message_type = _DEVICE
+_DEVICEEVENT.fields_by_name['event'].message_type = _EVENT
+_DEVICEEVENT.fields_by_name['device_id'].message_type = _DEVICEID
+_LINKID.fields_by_name['link_uuid'].message_type = _UUID
+_LINK.fields_by_name['link_id'].message_type = _LINKID
+_LINK.fields_by_name['link_endpoint_ids'].message_type = _ENDPOINTID
+_LINKIDLIST.fields_by_name['link_ids'].message_type = _LINKID
+_LINKLIST.fields_by_name['links'].message_type = _LINK
+_LINKEVENT.fields_by_name['event'].message_type = _EVENT
+_LINKEVENT.fields_by_name['link_id'].message_type = _LINKID
+_SERVICEID.fields_by_name['context_id'].message_type = _CONTEXTID
+_SERVICEID.fields_by_name['service_uuid'].message_type = _UUID
+_SERVICE.fields_by_name['service_id'].message_type = _SERVICEID
+_SERVICE.fields_by_name['service_type'].enum_type = _SERVICETYPEENUM
+_SERVICE.fields_by_name['service_endpoint_ids'].message_type = _ENDPOINTID
+_SERVICE.fields_by_name['service_constraints'].message_type = _CONSTRAINT
+_SERVICE.fields_by_name['service_status'].message_type = _SERVICESTATUS
+_SERVICE.fields_by_name['service_config'].message_type = _SERVICECONFIG
+_SERVICESTATUS.fields_by_name['service_status'].enum_type = _SERVICESTATUSENUM
+_SERVICECONFIG.fields_by_name['config_rules'].message_type = _CONFIGRULE
+_SERVICEIDLIST.fields_by_name['service_ids'].message_type = _SERVICEID
+_SERVICELIST.fields_by_name['services'].message_type = _SERVICE
+_SERVICEEVENT.fields_by_name['event'].message_type = _EVENT
+_SERVICEEVENT.fields_by_name['service_id'].message_type = _SERVICEID
+_CONNECTIONID.fields_by_name['connection_uuid'].message_type = _UUID
+_CONNECTION.fields_by_name['connection_id'].message_type = _CONNECTIONID
+_CONNECTION.fields_by_name['service_id'].message_type = _SERVICEID
+_CONNECTION.fields_by_name['path_hops_endpoint_ids'].message_type = _ENDPOINTID
+_CONNECTION.fields_by_name['sub_service_ids'].message_type = _SERVICEID
+_CONNECTIONIDLIST.fields_by_name['connection_ids'].message_type = _CONNECTIONID
+_CONNECTIONLIST.fields_by_name['connections'].message_type = _CONNECTION
+_CONNECTIONEVENT.fields_by_name['event'].message_type = _EVENT
+_CONNECTIONEVENT.fields_by_name['connection_id'].message_type = _CONNECTIONID
+_ENDPOINTID.fields_by_name['topology_id'].message_type = _TOPOLOGYID
+_ENDPOINTID.fields_by_name['device_id'].message_type = _DEVICEID
+_ENDPOINTID.fields_by_name['endpoint_uuid'].message_type = _UUID
+_ENDPOINT.fields_by_name['endpoint_id'].message_type = _ENDPOINTID
+_ENDPOINT.fields_by_name['kpi_sample_types'].enum_type = kpi__sample__types__pb2._KPISAMPLETYPE
+_CONFIGRULE.fields_by_name['action'].enum_type = _CONFIGACTIONENUM
+_TERAFLOWCONTROLLER.fields_by_name['context_id'].message_type = _CONTEXTID
+_AUTHENTICATIONRESULT.fields_by_name['context_id'].message_type = _CONTEXTID
+DESCRIPTOR.message_types_by_name['Empty'] = _EMPTY
+DESCRIPTOR.message_types_by_name['Uuid'] = _UUID
+DESCRIPTOR.message_types_by_name['Event'] = _EVENT
+DESCRIPTOR.message_types_by_name['ContextId'] = _CONTEXTID
+DESCRIPTOR.message_types_by_name['Context'] = _CONTEXT
+DESCRIPTOR.message_types_by_name['ContextIdList'] = _CONTEXTIDLIST
+DESCRIPTOR.message_types_by_name['ContextList'] = _CONTEXTLIST
+DESCRIPTOR.message_types_by_name['ContextEvent'] = _CONTEXTEVENT
+DESCRIPTOR.message_types_by_name['TopologyId'] = _TOPOLOGYID
+DESCRIPTOR.message_types_by_name['Topology'] = _TOPOLOGY
+DESCRIPTOR.message_types_by_name['TopologyIdList'] = _TOPOLOGYIDLIST
+DESCRIPTOR.message_types_by_name['TopologyList'] = _TOPOLOGYLIST
+DESCRIPTOR.message_types_by_name['TopologyEvent'] = _TOPOLOGYEVENT
+DESCRIPTOR.message_types_by_name['DeviceId'] = _DEVICEID
+DESCRIPTOR.message_types_by_name['Device'] = _DEVICE
+DESCRIPTOR.message_types_by_name['DeviceConfig'] = _DEVICECONFIG
+DESCRIPTOR.message_types_by_name['DeviceIdList'] = _DEVICEIDLIST
+DESCRIPTOR.message_types_by_name['DeviceList'] = _DEVICELIST
+DESCRIPTOR.message_types_by_name['DeviceEvent'] = _DEVICEEVENT
+DESCRIPTOR.message_types_by_name['LinkId'] = _LINKID
+DESCRIPTOR.message_types_by_name['Link'] = _LINK
+DESCRIPTOR.message_types_by_name['LinkIdList'] = _LINKIDLIST
+DESCRIPTOR.message_types_by_name['LinkList'] = _LINKLIST
+DESCRIPTOR.message_types_by_name['LinkEvent'] = _LINKEVENT
+DESCRIPTOR.message_types_by_name['ServiceId'] = _SERVICEID
+DESCRIPTOR.message_types_by_name['Service'] = _SERVICE
+DESCRIPTOR.message_types_by_name['ServiceStatus'] = _SERVICESTATUS
+DESCRIPTOR.message_types_by_name['ServiceConfig'] = _SERVICECONFIG
+DESCRIPTOR.message_types_by_name['ServiceIdList'] = _SERVICEIDLIST
+DESCRIPTOR.message_types_by_name['ServiceList'] = _SERVICELIST
+DESCRIPTOR.message_types_by_name['ServiceEvent'] = _SERVICEEVENT
+DESCRIPTOR.message_types_by_name['ConnectionId'] = _CONNECTIONID
+DESCRIPTOR.message_types_by_name['Connection'] = _CONNECTION
+DESCRIPTOR.message_types_by_name['ConnectionIdList'] = _CONNECTIONIDLIST
+DESCRIPTOR.message_types_by_name['ConnectionList'] = _CONNECTIONLIST
+DESCRIPTOR.message_types_by_name['ConnectionEvent'] = _CONNECTIONEVENT
+DESCRIPTOR.message_types_by_name['EndPointId'] = _ENDPOINTID
+DESCRIPTOR.message_types_by_name['EndPoint'] = _ENDPOINT
+DESCRIPTOR.message_types_by_name['ConfigRule'] = _CONFIGRULE
+DESCRIPTOR.message_types_by_name['Constraint'] = _CONSTRAINT
+DESCRIPTOR.message_types_by_name['TeraFlowController'] = _TERAFLOWCONTROLLER
+DESCRIPTOR.message_types_by_name['AuthenticationResult'] = _AUTHENTICATIONRESULT
+DESCRIPTOR.enum_types_by_name['EventTypeEnum'] = _EVENTTYPEENUM
+DESCRIPTOR.enum_types_by_name['DeviceDriverEnum'] = _DEVICEDRIVERENUM
+DESCRIPTOR.enum_types_by_name['DeviceOperationalStatusEnum'] = _DEVICEOPERATIONALSTATUSENUM
+DESCRIPTOR.enum_types_by_name['ServiceTypeEnum'] = _SERVICETYPEENUM
+DESCRIPTOR.enum_types_by_name['ServiceStatusEnum'] = _SERVICESTATUSENUM
+DESCRIPTOR.enum_types_by_name['ConfigActionEnum'] = _CONFIGACTIONENUM
+_sym_db.RegisterFileDescriptor(DESCRIPTOR)
+
+Empty = _reflection.GeneratedProtocolMessageType('Empty', (_message.Message,), {
+  'DESCRIPTOR' : _EMPTY,
+  '__module__' : 'context_pb2'
+  # @@protoc_insertion_point(class_scope:context.Empty)
+  })
+_sym_db.RegisterMessage(Empty)
+
+Uuid = _reflection.GeneratedProtocolMessageType('Uuid', (_message.Message,), {
+  'DESCRIPTOR' : _UUID,
+  '__module__' : 'context_pb2'
+  # @@protoc_insertion_point(class_scope:context.Uuid)
+  })
+_sym_db.RegisterMessage(Uuid)
+
+Event = _reflection.GeneratedProtocolMessageType('Event', (_message.Message,), {
+  'DESCRIPTOR' : _EVENT,
+  '__module__' : 'context_pb2'
+  # @@protoc_insertion_point(class_scope:context.Event)
+  })
+_sym_db.RegisterMessage(Event)
+
+ContextId = _reflection.GeneratedProtocolMessageType('ContextId', (_message.Message,), {
+  'DESCRIPTOR' : _CONTEXTID,
+  '__module__' : 'context_pb2'
+  # @@protoc_insertion_point(class_scope:context.ContextId)
+  })
+_sym_db.RegisterMessage(ContextId)
+
+Context = _reflection.GeneratedProtocolMessageType('Context', (_message.Message,), {
+  'DESCRIPTOR' : _CONTEXT,
+  '__module__' : 'context_pb2'
+  # @@protoc_insertion_point(class_scope:context.Context)
+  })
+_sym_db.RegisterMessage(Context)
+
+ContextIdList = _reflection.GeneratedProtocolMessageType('ContextIdList', (_message.Message,), {
+  'DESCRIPTOR' : _CONTEXTIDLIST,
+  '__module__' : 'context_pb2'
+  # @@protoc_insertion_point(class_scope:context.ContextIdList)
+  })
+_sym_db.RegisterMessage(ContextIdList)
+
+ContextList = _reflection.GeneratedProtocolMessageType('ContextList', (_message.Message,), {
+  'DESCRIPTOR' : _CONTEXTLIST,
+  '__module__' : 'context_pb2'
+  # @@protoc_insertion_point(class_scope:context.ContextList)
+  })
+_sym_db.RegisterMessage(ContextList)
+
+ContextEvent = _reflection.GeneratedProtocolMessageType('ContextEvent', (_message.Message,), {
+  'DESCRIPTOR' : _CONTEXTEVENT,
+  '__module__' : 'context_pb2'
+  # @@protoc_insertion_point(class_scope:context.ContextEvent)
+  })
+_sym_db.RegisterMessage(ContextEvent)
+
+TopologyId = _reflection.GeneratedProtocolMessageType('TopologyId', (_message.Message,), {
+  'DESCRIPTOR' : _TOPOLOGYID,
+  '__module__' : 'context_pb2'
+  # @@protoc_insertion_point(class_scope:context.TopologyId)
+  })
+_sym_db.RegisterMessage(TopologyId)
+
+Topology = _reflection.GeneratedProtocolMessageType('Topology', (_message.Message,), {
+  'DESCRIPTOR' : _TOPOLOGY,
+  '__module__' : 'context_pb2'
+  # @@protoc_insertion_point(class_scope:context.Topology)
+  })
+_sym_db.RegisterMessage(Topology)
+
+TopologyIdList = _reflection.GeneratedProtocolMessageType('TopologyIdList', (_message.Message,), {
+  'DESCRIPTOR' : _TOPOLOGYIDLIST,
+  '__module__' : 'context_pb2'
+  # @@protoc_insertion_point(class_scope:context.TopologyIdList)
+  })
+_sym_db.RegisterMessage(TopologyIdList)
+
+TopologyList = _reflection.GeneratedProtocolMessageType('TopologyList', (_message.Message,), {
+  'DESCRIPTOR' : _TOPOLOGYLIST,
+  '__module__' : 'context_pb2'
+  # @@protoc_insertion_point(class_scope:context.TopologyList)
+  })
+_sym_db.RegisterMessage(TopologyList)
+
+TopologyEvent = _reflection.GeneratedProtocolMessageType('TopologyEvent', (_message.Message,), {
+  'DESCRIPTOR' : _TOPOLOGYEVENT,
+  '__module__' : 'context_pb2'
+  # @@protoc_insertion_point(class_scope:context.TopologyEvent)
+  })
+_sym_db.RegisterMessage(TopologyEvent)
+
+DeviceId = _reflection.GeneratedProtocolMessageType('DeviceId', (_message.Message,), {
+  'DESCRIPTOR' : _DEVICEID,
+  '__module__' : 'context_pb2'
+  # @@protoc_insertion_point(class_scope:context.DeviceId)
+  })
+_sym_db.RegisterMessage(DeviceId)
+
+Device = _reflection.GeneratedProtocolMessageType('Device', (_message.Message,), {
+  'DESCRIPTOR' : _DEVICE,
+  '__module__' : 'context_pb2'
+  # @@protoc_insertion_point(class_scope:context.Device)
+  })
+_sym_db.RegisterMessage(Device)
+
+DeviceConfig = _reflection.GeneratedProtocolMessageType('DeviceConfig', (_message.Message,), {
+  'DESCRIPTOR' : _DEVICECONFIG,
+  '__module__' : 'context_pb2'
+  # @@protoc_insertion_point(class_scope:context.DeviceConfig)
+  })
+_sym_db.RegisterMessage(DeviceConfig)
+
+DeviceIdList = _reflection.GeneratedProtocolMessageType('DeviceIdList', (_message.Message,), {
+  'DESCRIPTOR' : _DEVICEIDLIST,
+  '__module__' : 'context_pb2'
+  # @@protoc_insertion_point(class_scope:context.DeviceIdList)
+  })
+_sym_db.RegisterMessage(DeviceIdList)
+
+DeviceList = _reflection.GeneratedProtocolMessageType('DeviceList', (_message.Message,), {
+  'DESCRIPTOR' : _DEVICELIST,
+  '__module__' : 'context_pb2'
+  # @@protoc_insertion_point(class_scope:context.DeviceList)
+  })
+_sym_db.RegisterMessage(DeviceList)
+
+DeviceEvent = _reflection.GeneratedProtocolMessageType('DeviceEvent', (_message.Message,), {
+  'DESCRIPTOR' : _DEVICEEVENT,
+  '__module__' : 'context_pb2'
+  # @@protoc_insertion_point(class_scope:context.DeviceEvent)
+  })
+_sym_db.RegisterMessage(DeviceEvent)
+
+LinkId = _reflection.GeneratedProtocolMessageType('LinkId', (_message.Message,), {
+  'DESCRIPTOR' : _LINKID,
+  '__module__' : 'context_pb2'
+  # @@protoc_insertion_point(class_scope:context.LinkId)
+  })
+_sym_db.RegisterMessage(LinkId)
+
+Link = _reflection.GeneratedProtocolMessageType('Link', (_message.Message,), {
+  'DESCRIPTOR' : _LINK,
+  '__module__' : 'context_pb2'
+  # @@protoc_insertion_point(class_scope:context.Link)
+  })
+_sym_db.RegisterMessage(Link)
+
+LinkIdList = _reflection.GeneratedProtocolMessageType('LinkIdList', (_message.Message,), {
+  'DESCRIPTOR' : _LINKIDLIST,
+  '__module__' : 'context_pb2'
+  # @@protoc_insertion_point(class_scope:context.LinkIdList)
+  })
+_sym_db.RegisterMessage(LinkIdList)
+
+LinkList = _reflection.GeneratedProtocolMessageType('LinkList', (_message.Message,), {
+  'DESCRIPTOR' : _LINKLIST,
+  '__module__' : 'context_pb2'
+  # @@protoc_insertion_point(class_scope:context.LinkList)
+  })
+_sym_db.RegisterMessage(LinkList)
+
+LinkEvent = _reflection.GeneratedProtocolMessageType('LinkEvent', (_message.Message,), {
+  'DESCRIPTOR' : _LINKEVENT,
+  '__module__' : 'context_pb2'
+  # @@protoc_insertion_point(class_scope:context.LinkEvent)
+  })
+_sym_db.RegisterMessage(LinkEvent)
+
+ServiceId = _reflection.GeneratedProtocolMessageType('ServiceId', (_message.Message,), {
+  'DESCRIPTOR' : _SERVICEID,
+  '__module__' : 'context_pb2'
+  # @@protoc_insertion_point(class_scope:context.ServiceId)
+  })
+_sym_db.RegisterMessage(ServiceId)
+
+Service = _reflection.GeneratedProtocolMessageType('Service', (_message.Message,), {
+  'DESCRIPTOR' : _SERVICE,
+  '__module__' : 'context_pb2'
+  # @@protoc_insertion_point(class_scope:context.Service)
+  })
+_sym_db.RegisterMessage(Service)
+
+ServiceStatus = _reflection.GeneratedProtocolMessageType('ServiceStatus', (_message.Message,), {
+  'DESCRIPTOR' : _SERVICESTATUS,
+  '__module__' : 'context_pb2'
+  # @@protoc_insertion_point(class_scope:context.ServiceStatus)
+  })
+_sym_db.RegisterMessage(ServiceStatus)
+
+ServiceConfig = _reflection.GeneratedProtocolMessageType('ServiceConfig', (_message.Message,), {
+  'DESCRIPTOR' : _SERVICECONFIG,
+  '__module__' : 'context_pb2'
+  # @@protoc_insertion_point(class_scope:context.ServiceConfig)
+  })
+_sym_db.RegisterMessage(ServiceConfig)
+
+ServiceIdList = _reflection.GeneratedProtocolMessageType('ServiceIdList', (_message.Message,), {
+  'DESCRIPTOR' : _SERVICEIDLIST,
+  '__module__' : 'context_pb2'
+  # @@protoc_insertion_point(class_scope:context.ServiceIdList)
+  })
+_sym_db.RegisterMessage(ServiceIdList)
+
+ServiceList = _reflection.GeneratedProtocolMessageType('ServiceList', (_message.Message,), {
+  'DESCRIPTOR' : _SERVICELIST,
+  '__module__' : 'context_pb2'
+  # @@protoc_insertion_point(class_scope:context.ServiceList)
+  })
+_sym_db.RegisterMessage(ServiceList)
+
+ServiceEvent = _reflection.GeneratedProtocolMessageType('ServiceEvent', (_message.Message,), {
+  'DESCRIPTOR' : _SERVICEEVENT,
+  '__module__' : 'context_pb2'
+  # @@protoc_insertion_point(class_scope:context.ServiceEvent)
+  })
+_sym_db.RegisterMessage(ServiceEvent)
+
+ConnectionId = _reflection.GeneratedProtocolMessageType('ConnectionId', (_message.Message,), {
+  'DESCRIPTOR' : _CONNECTIONID,
+  '__module__' : 'context_pb2'
+  # @@protoc_insertion_point(class_scope:context.ConnectionId)
+  })
+_sym_db.RegisterMessage(ConnectionId)
+
+Connection = _reflection.GeneratedProtocolMessageType('Connection', (_message.Message,), {
+  'DESCRIPTOR' : _CONNECTION,
+  '__module__' : 'context_pb2'
+  # @@protoc_insertion_point(class_scope:context.Connection)
+  })
+_sym_db.RegisterMessage(Connection)
+
+ConnectionIdList = _reflection.GeneratedProtocolMessageType('ConnectionIdList', (_message.Message,), {
+  'DESCRIPTOR' : _CONNECTIONIDLIST,
+  '__module__' : 'context_pb2'
+  # @@protoc_insertion_point(class_scope:context.ConnectionIdList)
+  })
+_sym_db.RegisterMessage(ConnectionIdList)
+
+ConnectionList = _reflection.GeneratedProtocolMessageType('ConnectionList', (_message.Message,), {
+  'DESCRIPTOR' : _CONNECTIONLIST,
+  '__module__' : 'context_pb2'
+  # @@protoc_insertion_point(class_scope:context.ConnectionList)
+  })
+_sym_db.RegisterMessage(ConnectionList)
+
+ConnectionEvent = _reflection.GeneratedProtocolMessageType('ConnectionEvent', (_message.Message,), {
+  'DESCRIPTOR' : _CONNECTIONEVENT,
+  '__module__' : 'context_pb2'
+  # @@protoc_insertion_point(class_scope:context.ConnectionEvent)
+  })
+_sym_db.RegisterMessage(ConnectionEvent)
+
+EndPointId = _reflection.GeneratedProtocolMessageType('EndPointId', (_message.Message,), {
+  'DESCRIPTOR' : _ENDPOINTID,
+  '__module__' : 'context_pb2'
+  # @@protoc_insertion_point(class_scope:context.EndPointId)
+  })
+_sym_db.RegisterMessage(EndPointId)
+
+EndPoint = _reflection.GeneratedProtocolMessageType('EndPoint', (_message.Message,), {
+  'DESCRIPTOR' : _ENDPOINT,
+  '__module__' : 'context_pb2'
+  # @@protoc_insertion_point(class_scope:context.EndPoint)
+  })
+_sym_db.RegisterMessage(EndPoint)
+
+ConfigRule = _reflection.GeneratedProtocolMessageType('ConfigRule', (_message.Message,), {
+  'DESCRIPTOR' : _CONFIGRULE,
+  '__module__' : 'context_pb2'
+  # @@protoc_insertion_point(class_scope:context.ConfigRule)
+  })
+_sym_db.RegisterMessage(ConfigRule)
+
+Constraint = _reflection.GeneratedProtocolMessageType('Constraint', (_message.Message,), {
+  'DESCRIPTOR' : _CONSTRAINT,
+  '__module__' : 'context_pb2'
+  # @@protoc_insertion_point(class_scope:context.Constraint)
+  })
+_sym_db.RegisterMessage(Constraint)
+
+TeraFlowController = _reflection.GeneratedProtocolMessageType('TeraFlowController', (_message.Message,), {
+  'DESCRIPTOR' : _TERAFLOWCONTROLLER,
+  '__module__' : 'context_pb2'
+  # @@protoc_insertion_point(class_scope:context.TeraFlowController)
+  })
+_sym_db.RegisterMessage(TeraFlowController)
+
+AuthenticationResult = _reflection.GeneratedProtocolMessageType('AuthenticationResult', (_message.Message,), {
+  'DESCRIPTOR' : _AUTHENTICATIONRESULT,
+  '__module__' : 'context_pb2'
+  # @@protoc_insertion_point(class_scope:context.AuthenticationResult)
+  })
+_sym_db.RegisterMessage(AuthenticationResult)
+
+
+
+_CONTEXTSERVICE = _descriptor.ServiceDescriptor(
+  name='ContextService',
+  full_name='context.ContextService',
+  file=DESCRIPTOR,
+  index=0,
+  serialized_options=None,
+  create_key=_descriptor._internal_create_key,
+  serialized_start=4524,
+  serialized_end=6617,
+  methods=[
+  _descriptor.MethodDescriptor(
+    name='ListContextIds',
+    full_name='context.ContextService.ListContextIds',
+    index=0,
+    containing_service=None,
+    input_type=_EMPTY,
+    output_type=_CONTEXTIDLIST,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+  _descriptor.MethodDescriptor(
+    name='ListContexts',
+    full_name='context.ContextService.ListContexts',
+    index=1,
+    containing_service=None,
+    input_type=_EMPTY,
+    output_type=_CONTEXTLIST,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+  _descriptor.MethodDescriptor(
+    name='GetContext',
+    full_name='context.ContextService.GetContext',
+    index=2,
+    containing_service=None,
+    input_type=_CONTEXTID,
+    output_type=_CONTEXT,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+  _descriptor.MethodDescriptor(
+    name='SetContext',
+    full_name='context.ContextService.SetContext',
+    index=3,
+    containing_service=None,
+    input_type=_CONTEXT,
+    output_type=_CONTEXTID,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+  _descriptor.MethodDescriptor(
+    name='RemoveContext',
+    full_name='context.ContextService.RemoveContext',
+    index=4,
+    containing_service=None,
+    input_type=_CONTEXTID,
+    output_type=_EMPTY,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+  _descriptor.MethodDescriptor(
+    name='GetContextEvents',
+    full_name='context.ContextService.GetContextEvents',
+    index=5,
+    containing_service=None,
+    input_type=_EMPTY,
+    output_type=_CONTEXTEVENT,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+  _descriptor.MethodDescriptor(
+    name='ListTopologyIds',
+    full_name='context.ContextService.ListTopologyIds',
+    index=6,
+    containing_service=None,
+    input_type=_CONTEXTID,
+    output_type=_TOPOLOGYIDLIST,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+  _descriptor.MethodDescriptor(
+    name='ListTopologies',
+    full_name='context.ContextService.ListTopologies',
+    index=7,
+    containing_service=None,
+    input_type=_CONTEXTID,
+    output_type=_TOPOLOGYLIST,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+  _descriptor.MethodDescriptor(
+    name='GetTopology',
+    full_name='context.ContextService.GetTopology',
+    index=8,
+    containing_service=None,
+    input_type=_TOPOLOGYID,
+    output_type=_TOPOLOGY,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+  _descriptor.MethodDescriptor(
+    name='SetTopology',
+    full_name='context.ContextService.SetTopology',
+    index=9,
+    containing_service=None,
+    input_type=_TOPOLOGY,
+    output_type=_TOPOLOGYID,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+  _descriptor.MethodDescriptor(
+    name='RemoveTopology',
+    full_name='context.ContextService.RemoveTopology',
+    index=10,
+    containing_service=None,
+    input_type=_TOPOLOGYID,
+    output_type=_EMPTY,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+  _descriptor.MethodDescriptor(
+    name='GetTopologyEvents',
+    full_name='context.ContextService.GetTopologyEvents',
+    index=11,
+    containing_service=None,
+    input_type=_EMPTY,
+    output_type=_TOPOLOGYEVENT,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+  _descriptor.MethodDescriptor(
+    name='ListDeviceIds',
+    full_name='context.ContextService.ListDeviceIds',
+    index=12,
+    containing_service=None,
+    input_type=_EMPTY,
+    output_type=_DEVICEIDLIST,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+  _descriptor.MethodDescriptor(
+    name='ListDevices',
+    full_name='context.ContextService.ListDevices',
+    index=13,
+    containing_service=None,
+    input_type=_EMPTY,
+    output_type=_DEVICELIST,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+  _descriptor.MethodDescriptor(
+    name='GetDevice',
+    full_name='context.ContextService.GetDevice',
+    index=14,
+    containing_service=None,
+    input_type=_DEVICEID,
+    output_type=_DEVICE,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+  _descriptor.MethodDescriptor(
+    name='SetDevice',
+    full_name='context.ContextService.SetDevice',
+    index=15,
+    containing_service=None,
+    input_type=_DEVICE,
+    output_type=_DEVICEID,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+  _descriptor.MethodDescriptor(
+    name='RemoveDevice',
+    full_name='context.ContextService.RemoveDevice',
+    index=16,
+    containing_service=None,
+    input_type=_DEVICEID,
+    output_type=_EMPTY,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+  _descriptor.MethodDescriptor(
+    name='GetDeviceEvents',
+    full_name='context.ContextService.GetDeviceEvents',
+    index=17,
+    containing_service=None,
+    input_type=_EMPTY,
+    output_type=_DEVICEEVENT,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+  _descriptor.MethodDescriptor(
+    name='ListLinkIds',
+    full_name='context.ContextService.ListLinkIds',
+    index=18,
+    containing_service=None,
+    input_type=_EMPTY,
+    output_type=_LINKIDLIST,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+  _descriptor.MethodDescriptor(
+    name='ListLinks',
+    full_name='context.ContextService.ListLinks',
+    index=19,
+    containing_service=None,
+    input_type=_EMPTY,
+    output_type=_LINKLIST,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+  _descriptor.MethodDescriptor(
+    name='GetLink',
+    full_name='context.ContextService.GetLink',
+    index=20,
+    containing_service=None,
+    input_type=_LINKID,
+    output_type=_LINK,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+  _descriptor.MethodDescriptor(
+    name='SetLink',
+    full_name='context.ContextService.SetLink',
+    index=21,
+    containing_service=None,
+    input_type=_LINK,
+    output_type=_LINKID,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+  _descriptor.MethodDescriptor(
+    name='RemoveLink',
+    full_name='context.ContextService.RemoveLink',
+    index=22,
+    containing_service=None,
+    input_type=_LINKID,
+    output_type=_EMPTY,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+  _descriptor.MethodDescriptor(
+    name='GetLinkEvents',
+    full_name='context.ContextService.GetLinkEvents',
+    index=23,
+    containing_service=None,
+    input_type=_EMPTY,
+    output_type=_LINKEVENT,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+  _descriptor.MethodDescriptor(
+    name='ListServiceIds',
+    full_name='context.ContextService.ListServiceIds',
+    index=24,
+    containing_service=None,
+    input_type=_CONTEXTID,
+    output_type=_SERVICEIDLIST,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+  _descriptor.MethodDescriptor(
+    name='ListServices',
+    full_name='context.ContextService.ListServices',
+    index=25,
+    containing_service=None,
+    input_type=_CONTEXTID,
+    output_type=_SERVICELIST,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+  _descriptor.MethodDescriptor(
+    name='GetService',
+    full_name='context.ContextService.GetService',
+    index=26,
+    containing_service=None,
+    input_type=_SERVICEID,
+    output_type=_SERVICE,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+  _descriptor.MethodDescriptor(
+    name='SetService',
+    full_name='context.ContextService.SetService',
+    index=27,
+    containing_service=None,
+    input_type=_SERVICE,
+    output_type=_SERVICEID,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+  _descriptor.MethodDescriptor(
+    name='RemoveService',
+    full_name='context.ContextService.RemoveService',
+    index=28,
+    containing_service=None,
+    input_type=_SERVICEID,
+    output_type=_EMPTY,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+  _descriptor.MethodDescriptor(
+    name='GetServiceEvents',
+    full_name='context.ContextService.GetServiceEvents',
+    index=29,
+    containing_service=None,
+    input_type=_EMPTY,
+    output_type=_SERVICEEVENT,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+  _descriptor.MethodDescriptor(
+    name='ListConnectionIds',
+    full_name='context.ContextService.ListConnectionIds',
+    index=30,
+    containing_service=None,
+    input_type=_SERVICEID,
+    output_type=_CONNECTIONIDLIST,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+  _descriptor.MethodDescriptor(
+    name='ListConnections',
+    full_name='context.ContextService.ListConnections',
+    index=31,
+    containing_service=None,
+    input_type=_SERVICEID,
+    output_type=_CONNECTIONLIST,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+  _descriptor.MethodDescriptor(
+    name='GetConnection',
+    full_name='context.ContextService.GetConnection',
+    index=32,
+    containing_service=None,
+    input_type=_CONNECTIONID,
+    output_type=_CONNECTION,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+  _descriptor.MethodDescriptor(
+    name='SetConnection',
+    full_name='context.ContextService.SetConnection',
+    index=33,
+    containing_service=None,
+    input_type=_CONNECTION,
+    output_type=_CONNECTIONID,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+  _descriptor.MethodDescriptor(
+    name='RemoveConnection',
+    full_name='context.ContextService.RemoveConnection',
+    index=34,
+    containing_service=None,
+    input_type=_CONNECTIONID,
+    output_type=_EMPTY,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+  _descriptor.MethodDescriptor(
+    name='GetConnectionEvents',
+    full_name='context.ContextService.GetConnectionEvents',
+    index=35,
+    containing_service=None,
+    input_type=_EMPTY,
+    output_type=_CONNECTIONEVENT,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+])
+_sym_db.RegisterServiceDescriptor(_CONTEXTSERVICE)
+
+DESCRIPTOR.services_by_name['ContextService'] = _CONTEXTSERVICE
+
+# @@protoc_insertion_point(module_scope)
diff --git a/src/webui/proto/device_pb2.py b/src/webui/proto/device_pb2.py
new file mode 100644
index 0000000000000000000000000000000000000000..4d4dbb82567256dd79595884f0ed9c2f13498d31
--- /dev/null
+++ b/src/webui/proto/device_pb2.py
@@ -0,0 +1,162 @@
+# -*- coding: utf-8 -*-
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: device.proto
+"""Generated protocol buffer code."""
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import message as _message
+from google.protobuf import reflection as _reflection
+from google.protobuf import symbol_database as _symbol_database
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+from . import context_pb2 as context__pb2
+from . import monitoring_pb2 as monitoring__pb2
+
+
+DESCRIPTOR = _descriptor.FileDescriptor(
+  name='device.proto',
+  package='device',
+  syntax='proto3',
+  serialized_options=None,
+  create_key=_descriptor._internal_create_key,
+  serialized_pb=b'\n\x0c\x64\x65vice.proto\x12\x06\x64\x65vice\x1a\rcontext.proto\x1a\x10monitoring.proto\"\xa4\x01\n\x12MonitoringSettings\x12!\n\x06kpi_id\x18\x01 \x01(\x0b\x32\x11.monitoring.KpiId\x12\x31\n\x0ekpi_descriptor\x18\x02 \x01(\x0b\x32\x19.monitoring.KpiDescriptor\x12\x1b\n\x13sampling_duration_s\x18\x03 \x01(\x02\x12\x1b\n\x13sampling_interval_s\x18\x04 \x01(\x02\x32\xb2\x02\n\rDeviceService\x12\x31\n\tAddDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x37\n\x0f\x43onfigureDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0c\x44\x65leteDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12>\n\x10GetInitialConfig\x12\x11.context.DeviceId\x1a\x15.context.DeviceConfig\"\x00\x12@\n\x10MonitorDeviceKpi\x12\x1a.device.MonitoringSettings\x1a\x0e.context.Empty\"\x00\x62\x06proto3'
+  ,
+  dependencies=[context__pb2.DESCRIPTOR,monitoring__pb2.DESCRIPTOR,])
+
+
+
+
+_MONITORINGSETTINGS = _descriptor.Descriptor(
+  name='MonitoringSettings',
+  full_name='device.MonitoringSettings',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  create_key=_descriptor._internal_create_key,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='kpi_id', full_name='device.MonitoringSettings.kpi_id', index=0,
+      number=1, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='kpi_descriptor', full_name='device.MonitoringSettings.kpi_descriptor', index=1,
+      number=2, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='sampling_duration_s', full_name='device.MonitoringSettings.sampling_duration_s', index=2,
+      number=3, type=2, cpp_type=6, label=1,
+      has_default_value=False, default_value=float(0),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='sampling_interval_s', full_name='device.MonitoringSettings.sampling_interval_s', index=3,
+      number=4, type=2, cpp_type=6, label=1,
+      has_default_value=False, default_value=float(0),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=58,
+  serialized_end=222,
+)
+
+_MONITORINGSETTINGS.fields_by_name['kpi_id'].message_type = monitoring__pb2._KPIID
+_MONITORINGSETTINGS.fields_by_name['kpi_descriptor'].message_type = monitoring__pb2._KPIDESCRIPTOR
+DESCRIPTOR.message_types_by_name['MonitoringSettings'] = _MONITORINGSETTINGS
+_sym_db.RegisterFileDescriptor(DESCRIPTOR)
+
+MonitoringSettings = _reflection.GeneratedProtocolMessageType('MonitoringSettings', (_message.Message,), {
+  'DESCRIPTOR' : _MONITORINGSETTINGS,
+  '__module__' : 'device_pb2'
+  # @@protoc_insertion_point(class_scope:device.MonitoringSettings)
+  })
+_sym_db.RegisterMessage(MonitoringSettings)
+
+
+
+_DEVICESERVICE = _descriptor.ServiceDescriptor(
+  name='DeviceService',
+  full_name='device.DeviceService',
+  file=DESCRIPTOR,
+  index=0,
+  serialized_options=None,
+  create_key=_descriptor._internal_create_key,
+  serialized_start=225,
+  serialized_end=531,
+  methods=[
+  _descriptor.MethodDescriptor(
+    name='AddDevice',
+    full_name='device.DeviceService.AddDevice',
+    index=0,
+    containing_service=None,
+    input_type=context__pb2._DEVICE,
+    output_type=context__pb2._DEVICEID,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+  _descriptor.MethodDescriptor(
+    name='ConfigureDevice',
+    full_name='device.DeviceService.ConfigureDevice',
+    index=1,
+    containing_service=None,
+    input_type=context__pb2._DEVICE,
+    output_type=context__pb2._DEVICEID,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+  _descriptor.MethodDescriptor(
+    name='DeleteDevice',
+    full_name='device.DeviceService.DeleteDevice',
+    index=2,
+    containing_service=None,
+    input_type=context__pb2._DEVICEID,
+    output_type=context__pb2._EMPTY,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+  _descriptor.MethodDescriptor(
+    name='GetInitialConfig',
+    full_name='device.DeviceService.GetInitialConfig',
+    index=3,
+    containing_service=None,
+    input_type=context__pb2._DEVICEID,
+    output_type=context__pb2._DEVICECONFIG,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+  _descriptor.MethodDescriptor(
+    name='MonitorDeviceKpi',
+    full_name='device.DeviceService.MonitorDeviceKpi',
+    index=4,
+    containing_service=None,
+    input_type=_MONITORINGSETTINGS,
+    output_type=context__pb2._EMPTY,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+])
+_sym_db.RegisterServiceDescriptor(_DEVICESERVICE)
+
+DESCRIPTOR.services_by_name['DeviceService'] = _DEVICESERVICE
+
+# @@protoc_insertion_point(module_scope)
diff --git a/src/webui/proto/kpi_sample_types_pb2.py b/src/webui/proto/kpi_sample_types_pb2.py
new file mode 100644
index 0000000000000000000000000000000000000000..ea7fd2f82757d4c3db02d7e2c7817e2787b0b490
--- /dev/null
+++ b/src/webui/proto/kpi_sample_types_pb2.py
@@ -0,0 +1,78 @@
+# -*- coding: utf-8 -*-
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: kpi_sample_types.proto
+"""Generated protocol buffer code."""
+from google.protobuf.internal import enum_type_wrapper
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import message as _message
+from google.protobuf import reflection as _reflection
+from google.protobuf import symbol_database as _symbol_database
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+
+
+DESCRIPTOR = _descriptor.FileDescriptor(
+  name='kpi_sample_types.proto',
+  package='kpi_sample_types',
+  syntax='proto3',
+  serialized_options=None,
+  create_key=_descriptor._internal_create_key,
+  serialized_pb=b'\n\x16kpi_sample_types.proto\x12\x10kpi_sample_types*\xbe\x01\n\rKpiSampleType\x12\x19\n\x15KPISAMPLETYPE_UNKNOWN\x10\x00\x12%\n!KPISAMPLETYPE_PACKETS_TRANSMITTED\x10\x65\x12\"\n\x1eKPISAMPLETYPE_PACKETS_RECEIVED\x10\x66\x12$\n\x1fKPISAMPLETYPE_BYTES_TRANSMITTED\x10\xc9\x01\x12!\n\x1cKPISAMPLETYPE_BYTES_RECEIVED\x10\xca\x01\x62\x06proto3'
+)
+
+_KPISAMPLETYPE = _descriptor.EnumDescriptor(
+  name='KpiSampleType',
+  full_name='kpi_sample_types.KpiSampleType',
+  filename=None,
+  file=DESCRIPTOR,
+  create_key=_descriptor._internal_create_key,
+  values=[
+    _descriptor.EnumValueDescriptor(
+      name='KPISAMPLETYPE_UNKNOWN', index=0, number=0,
+      serialized_options=None,
+      type=None,
+      create_key=_descriptor._internal_create_key),
+    _descriptor.EnumValueDescriptor(
+      name='KPISAMPLETYPE_PACKETS_TRANSMITTED', index=1, number=101,
+      serialized_options=None,
+      type=None,
+      create_key=_descriptor._internal_create_key),
+    _descriptor.EnumValueDescriptor(
+      name='KPISAMPLETYPE_PACKETS_RECEIVED', index=2, number=102,
+      serialized_options=None,
+      type=None,
+      create_key=_descriptor._internal_create_key),
+    _descriptor.EnumValueDescriptor(
+      name='KPISAMPLETYPE_BYTES_TRANSMITTED', index=3, number=201,
+      serialized_options=None,
+      type=None,
+      create_key=_descriptor._internal_create_key),
+    _descriptor.EnumValueDescriptor(
+      name='KPISAMPLETYPE_BYTES_RECEIVED', index=4, number=202,
+      serialized_options=None,
+      type=None,
+      create_key=_descriptor._internal_create_key),
+  ],
+  containing_type=None,
+  serialized_options=None,
+  serialized_start=45,
+  serialized_end=235,
+)
+_sym_db.RegisterEnumDescriptor(_KPISAMPLETYPE)
+
+KpiSampleType = enum_type_wrapper.EnumTypeWrapper(_KPISAMPLETYPE)
+KPISAMPLETYPE_UNKNOWN = 0
+KPISAMPLETYPE_PACKETS_TRANSMITTED = 101
+KPISAMPLETYPE_PACKETS_RECEIVED = 102
+KPISAMPLETYPE_BYTES_TRANSMITTED = 201
+KPISAMPLETYPE_BYTES_RECEIVED = 202
+
+
+DESCRIPTOR.enum_types_by_name['KpiSampleType'] = _KPISAMPLETYPE
+_sym_db.RegisterFileDescriptor(DESCRIPTOR)
+
+
+# @@protoc_insertion_point(module_scope)
diff --git a/src/webui/proto/monitoring_pb2.py b/src/webui/proto/monitoring_pb2.py
new file mode 100644
index 0000000000000000000000000000000000000000..b313ebb68f0da37a540898e8c362fd204a799076
--- /dev/null
+++ b/src/webui/proto/monitoring_pb2.py
@@ -0,0 +1,452 @@
+# -*- coding: utf-8 -*-
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: monitoring.proto
+"""Generated protocol buffer code."""
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import message as _message
+from google.protobuf import reflection as _reflection
+from google.protobuf import symbol_database as _symbol_database
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+from . import context_pb2 as context__pb2
+from . import kpi_sample_types_pb2 as kpi__sample__types__pb2
+
+
+DESCRIPTOR = _descriptor.FileDescriptor(
+  name='monitoring.proto',
+  package='monitoring',
+  syntax='proto3',
+  serialized_options=None,
+  create_key=_descriptor._internal_create_key,
+  serialized_pb=b'\n\x10monitoring.proto\x12\nmonitoring\x1a\rcontext.proto\x1a\x16kpi_sample_types.proto\"\xda\x01\n\rKpiDescriptor\x12\x17\n\x0fkpi_description\x18\x01 \x01(\t\x12\x38\n\x0fkpi_sample_type\x18\x02 \x01(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\x12$\n\tdevice_id\x18\x03 \x01(\x0b\x32\x11.context.DeviceId\x12(\n\x0b\x65ndpoint_id\x18\x04 \x01(\x0b\x32\x13.context.EndPointId\x12&\n\nservice_id\x18\x05 \x01(\x0b\x32\x12.context.ServiceId\"p\n\x11MonitorKpiRequest\x12!\n\x06kpi_id\x18\x01 \x01(\x0b\x32\x11.monitoring.KpiId\x12\x1b\n\x13sampling_duration_s\x18\x02 \x01(\x02\x12\x1b\n\x13sampling_interval_s\x18\x03 \x01(\x02\"&\n\x05KpiId\x12\x1d\n\x06kpi_id\x18\x01 \x01(\x0b\x32\r.context.Uuid\"d\n\x03Kpi\x12!\n\x06kpi_id\x18\x01 \x01(\x0b\x32\x11.monitoring.KpiId\x12\x11\n\ttimestamp\x18\x02 \x01(\t\x12\'\n\tkpi_value\x18\x04 \x01(\x0b\x32\x14.monitoring.KpiValue\"a\n\x08KpiValue\x12\x10\n\x06intVal\x18\x01 \x01(\rH\x00\x12\x12\n\x08\x66loatVal\x18\x02 \x01(\x02H\x00\x12\x13\n\tstringVal\x18\x03 \x01(\tH\x00\x12\x11\n\x07\x62oolVal\x18\x04 \x01(\x08H\x00\x42\x07\n\x05value\",\n\x07KpiList\x12!\n\x08kpi_list\x18\x01 \x03(\x0b\x32\x0f.monitoring.Kpi2\xf3\x02\n\x11MonitoringService\x12;\n\tCreateKpi\x12\x19.monitoring.KpiDescriptor\x1a\x11.monitoring.KpiId\"\x00\x12\x42\n\x10GetKpiDescriptor\x12\x11.monitoring.KpiId\x1a\x19.monitoring.KpiDescriptor\"\x00\x12/\n\nIncludeKpi\x12\x0f.monitoring.Kpi\x1a\x0e.context.Empty\"\x00\x12=\n\nMonitorKpi\x12\x1d.monitoring.MonitorKpiRequest\x1a\x0e.context.Empty\"\x00\x12\x36\n\x0cGetStreamKpi\x12\x11.monitoring.KpiId\x1a\x0f.monitoring.Kpi\"\x00\x30\x01\x12\x35\n\rGetInstantKpi\x12\x11.monitoring.KpiId\x1a\x0f.monitoring.Kpi\"\x00\x62\x06proto3'
+  ,
+  dependencies=[context__pb2.DESCRIPTOR,kpi__sample__types__pb2.DESCRIPTOR,])
+
+
+
+
+_KPIDESCRIPTOR = _descriptor.Descriptor(
+  name='KpiDescriptor',
+  full_name='monitoring.KpiDescriptor',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  create_key=_descriptor._internal_create_key,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='kpi_description', full_name='monitoring.KpiDescriptor.kpi_description', index=0,
+      number=1, type=9, cpp_type=9, label=1,
+      has_default_value=False, default_value=b"".decode('utf-8'),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='kpi_sample_type', full_name='monitoring.KpiDescriptor.kpi_sample_type', index=1,
+      number=2, type=14, cpp_type=8, label=1,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='device_id', full_name='monitoring.KpiDescriptor.device_id', index=2,
+      number=3, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='endpoint_id', full_name='monitoring.KpiDescriptor.endpoint_id', index=3,
+      number=4, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='service_id', full_name='monitoring.KpiDescriptor.service_id', index=4,
+      number=5, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=72,
+  serialized_end=290,
+)
+
+
+_MONITORKPIREQUEST = _descriptor.Descriptor(
+  name='MonitorKpiRequest',
+  full_name='monitoring.MonitorKpiRequest',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  create_key=_descriptor._internal_create_key,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='kpi_id', full_name='monitoring.MonitorKpiRequest.kpi_id', index=0,
+      number=1, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='sampling_duration_s', full_name='monitoring.MonitorKpiRequest.sampling_duration_s', index=1,
+      number=2, type=2, cpp_type=6, label=1,
+      has_default_value=False, default_value=float(0),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='sampling_interval_s', full_name='monitoring.MonitorKpiRequest.sampling_interval_s', index=2,
+      number=3, type=2, cpp_type=6, label=1,
+      has_default_value=False, default_value=float(0),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=292,
+  serialized_end=404,
+)
+
+
+_KPIID = _descriptor.Descriptor(
+  name='KpiId',
+  full_name='monitoring.KpiId',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  create_key=_descriptor._internal_create_key,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='kpi_id', full_name='monitoring.KpiId.kpi_id', index=0,
+      number=1, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=406,
+  serialized_end=444,
+)
+
+
+_KPI = _descriptor.Descriptor(
+  name='Kpi',
+  full_name='monitoring.Kpi',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  create_key=_descriptor._internal_create_key,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='kpi_id', full_name='monitoring.Kpi.kpi_id', index=0,
+      number=1, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='timestamp', full_name='monitoring.Kpi.timestamp', index=1,
+      number=2, type=9, cpp_type=9, label=1,
+      has_default_value=False, default_value=b"".decode('utf-8'),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='kpi_value', full_name='monitoring.Kpi.kpi_value', index=2,
+      number=4, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=446,
+  serialized_end=546,
+)
+
+
+_KPIVALUE = _descriptor.Descriptor(
+  name='KpiValue',
+  full_name='monitoring.KpiValue',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  create_key=_descriptor._internal_create_key,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='intVal', full_name='monitoring.KpiValue.intVal', index=0,
+      number=1, type=13, cpp_type=3, label=1,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='floatVal', full_name='monitoring.KpiValue.floatVal', index=1,
+      number=2, type=2, cpp_type=6, label=1,
+      has_default_value=False, default_value=float(0),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='stringVal', full_name='monitoring.KpiValue.stringVal', index=2,
+      number=3, type=9, cpp_type=9, label=1,
+      has_default_value=False, default_value=b"".decode('utf-8'),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='boolVal', full_name='monitoring.KpiValue.boolVal', index=3,
+      number=4, type=8, cpp_type=7, label=1,
+      has_default_value=False, default_value=False,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+    _descriptor.OneofDescriptor(
+      name='value', full_name='monitoring.KpiValue.value',
+      index=0, containing_type=None,
+      create_key=_descriptor._internal_create_key,
+    fields=[]),
+  ],
+  serialized_start=548,
+  serialized_end=645,
+)
+
+
+_KPILIST = _descriptor.Descriptor(
+  name='KpiList',
+  full_name='monitoring.KpiList',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  create_key=_descriptor._internal_create_key,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='kpi_list', full_name='monitoring.KpiList.kpi_list', index=0,
+      number=1, type=11, cpp_type=10, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=647,
+  serialized_end=691,
+)
+
+_KPIDESCRIPTOR.fields_by_name['kpi_sample_type'].enum_type = kpi__sample__types__pb2._KPISAMPLETYPE
+_KPIDESCRIPTOR.fields_by_name['device_id'].message_type = context__pb2._DEVICEID
+_KPIDESCRIPTOR.fields_by_name['endpoint_id'].message_type = context__pb2._ENDPOINTID
+_KPIDESCRIPTOR.fields_by_name['service_id'].message_type = context__pb2._SERVICEID
+_MONITORKPIREQUEST.fields_by_name['kpi_id'].message_type = _KPIID
+_KPIID.fields_by_name['kpi_id'].message_type = context__pb2._UUID
+_KPI.fields_by_name['kpi_id'].message_type = _KPIID
+_KPI.fields_by_name['kpi_value'].message_type = _KPIVALUE
+_KPIVALUE.oneofs_by_name['value'].fields.append(
+  _KPIVALUE.fields_by_name['intVal'])
+_KPIVALUE.fields_by_name['intVal'].containing_oneof = _KPIVALUE.oneofs_by_name['value']
+_KPIVALUE.oneofs_by_name['value'].fields.append(
+  _KPIVALUE.fields_by_name['floatVal'])
+_KPIVALUE.fields_by_name['floatVal'].containing_oneof = _KPIVALUE.oneofs_by_name['value']
+_KPIVALUE.oneofs_by_name['value'].fields.append(
+  _KPIVALUE.fields_by_name['stringVal'])
+_KPIVALUE.fields_by_name['stringVal'].containing_oneof = _KPIVALUE.oneofs_by_name['value']
+_KPIVALUE.oneofs_by_name['value'].fields.append(
+  _KPIVALUE.fields_by_name['boolVal'])
+_KPIVALUE.fields_by_name['boolVal'].containing_oneof = _KPIVALUE.oneofs_by_name['value']
+_KPILIST.fields_by_name['kpi_list'].message_type = _KPI
+DESCRIPTOR.message_types_by_name['KpiDescriptor'] = _KPIDESCRIPTOR
+DESCRIPTOR.message_types_by_name['MonitorKpiRequest'] = _MONITORKPIREQUEST
+DESCRIPTOR.message_types_by_name['KpiId'] = _KPIID
+DESCRIPTOR.message_types_by_name['Kpi'] = _KPI
+DESCRIPTOR.message_types_by_name['KpiValue'] = _KPIVALUE
+DESCRIPTOR.message_types_by_name['KpiList'] = _KPILIST
+_sym_db.RegisterFileDescriptor(DESCRIPTOR)
+
+KpiDescriptor = _reflection.GeneratedProtocolMessageType('KpiDescriptor', (_message.Message,), {
+  'DESCRIPTOR' : _KPIDESCRIPTOR,
+  '__module__' : 'monitoring_pb2'
+  # @@protoc_insertion_point(class_scope:monitoring.KpiDescriptor)
+  })
+_sym_db.RegisterMessage(KpiDescriptor)
+
+MonitorKpiRequest = _reflection.GeneratedProtocolMessageType('MonitorKpiRequest', (_message.Message,), {
+  'DESCRIPTOR' : _MONITORKPIREQUEST,
+  '__module__' : 'monitoring_pb2'
+  # @@protoc_insertion_point(class_scope:monitoring.MonitorKpiRequest)
+  })
+_sym_db.RegisterMessage(MonitorKpiRequest)
+
+KpiId = _reflection.GeneratedProtocolMessageType('KpiId', (_message.Message,), {
+  'DESCRIPTOR' : _KPIID,
+  '__module__' : 'monitoring_pb2'
+  # @@protoc_insertion_point(class_scope:monitoring.KpiId)
+  })
+_sym_db.RegisterMessage(KpiId)
+
+Kpi = _reflection.GeneratedProtocolMessageType('Kpi', (_message.Message,), {
+  'DESCRIPTOR' : _KPI,
+  '__module__' : 'monitoring_pb2'
+  # @@protoc_insertion_point(class_scope:monitoring.Kpi)
+  })
+_sym_db.RegisterMessage(Kpi)
+
+KpiValue = _reflection.GeneratedProtocolMessageType('KpiValue', (_message.Message,), {
+  'DESCRIPTOR' : _KPIVALUE,
+  '__module__' : 'monitoring_pb2'
+  # @@protoc_insertion_point(class_scope:monitoring.KpiValue)
+  })
+_sym_db.RegisterMessage(KpiValue)
+
+KpiList = _reflection.GeneratedProtocolMessageType('KpiList', (_message.Message,), {
+  'DESCRIPTOR' : _KPILIST,
+  '__module__' : 'monitoring_pb2'
+  # @@protoc_insertion_point(class_scope:monitoring.KpiList)
+  })
+_sym_db.RegisterMessage(KpiList)
+
+
+
+_MONITORINGSERVICE = _descriptor.ServiceDescriptor(
+  name='MonitoringService',
+  full_name='monitoring.MonitoringService',
+  file=DESCRIPTOR,
+  index=0,
+  serialized_options=None,
+  create_key=_descriptor._internal_create_key,
+  serialized_start=694,
+  serialized_end=1065,
+  methods=[
+  _descriptor.MethodDescriptor(
+    name='CreateKpi',
+    full_name='monitoring.MonitoringService.CreateKpi',
+    index=0,
+    containing_service=None,
+    input_type=_KPIDESCRIPTOR,
+    output_type=_KPIID,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+  _descriptor.MethodDescriptor(
+    name='GetKpiDescriptor',
+    full_name='monitoring.MonitoringService.GetKpiDescriptor',
+    index=1,
+    containing_service=None,
+    input_type=_KPIID,
+    output_type=_KPIDESCRIPTOR,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+  _descriptor.MethodDescriptor(
+    name='IncludeKpi',
+    full_name='monitoring.MonitoringService.IncludeKpi',
+    index=2,
+    containing_service=None,
+    input_type=_KPI,
+    output_type=context__pb2._EMPTY,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+  _descriptor.MethodDescriptor(
+    name='MonitorKpi',
+    full_name='monitoring.MonitoringService.MonitorKpi',
+    index=3,
+    containing_service=None,
+    input_type=_MONITORKPIREQUEST,
+    output_type=context__pb2._EMPTY,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+  _descriptor.MethodDescriptor(
+    name='GetStreamKpi',
+    full_name='monitoring.MonitoringService.GetStreamKpi',
+    index=4,
+    containing_service=None,
+    input_type=_KPIID,
+    output_type=_KPI,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+  _descriptor.MethodDescriptor(
+    name='GetInstantKpi',
+    full_name='monitoring.MonitoringService.GetInstantKpi',
+    index=5,
+    containing_service=None,
+    input_type=_KPIID,
+    output_type=_KPI,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+])
+_sym_db.RegisterServiceDescriptor(_MONITORINGSERVICE)
+
+DESCRIPTOR.services_by_name['MonitoringService'] = _MONITORINGSERVICE
+
+# @@protoc_insertion_point(module_scope)
diff --git a/src/webui/proto/service_pb2.py b/src/webui/proto/service_pb2.py
new file mode 100644
index 0000000000000000000000000000000000000000..7a006915b8be39710a17faab075e382e322d918f
--- /dev/null
+++ b/src/webui/proto/service_pb2.py
@@ -0,0 +1,88 @@
+# -*- coding: utf-8 -*-
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: service.proto
+"""Generated protocol buffer code."""
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import message as _message
+from google.protobuf import reflection as _reflection
+from google.protobuf import symbol_database as _symbol_database
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+from . import context_pb2 as context__pb2
+
+
+DESCRIPTOR = _descriptor.FileDescriptor(
+  name='service.proto',
+  package='service',
+  syntax='proto3',
+  serialized_options=None,
+  create_key=_descriptor._internal_create_key,
+  serialized_pb=b'\n\rservice.proto\x12\x07service\x1a\rcontext.proto2\xfd\x01\n\x0eServiceService\x12\x37\n\rCreateService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x37\n\rUpdateService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rDeleteService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12\x42\n\x11GetConnectionList\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x62\x06proto3'
+  ,
+  dependencies=[context__pb2.DESCRIPTOR,])
+
+
+
+_sym_db.RegisterFileDescriptor(DESCRIPTOR)
+
+
+
+_SERVICESERVICE = _descriptor.ServiceDescriptor(
+  name='ServiceService',
+  full_name='service.ServiceService',
+  file=DESCRIPTOR,
+  index=0,
+  serialized_options=None,
+  create_key=_descriptor._internal_create_key,
+  serialized_start=42,
+  serialized_end=295,
+  methods=[
+  _descriptor.MethodDescriptor(
+    name='CreateService',
+    full_name='service.ServiceService.CreateService',
+    index=0,
+    containing_service=None,
+    input_type=context__pb2._SERVICE,
+    output_type=context__pb2._SERVICEID,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+  _descriptor.MethodDescriptor(
+    name='UpdateService',
+    full_name='service.ServiceService.UpdateService',
+    index=1,
+    containing_service=None,
+    input_type=context__pb2._SERVICE,
+    output_type=context__pb2._SERVICEID,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+  _descriptor.MethodDescriptor(
+    name='DeleteService',
+    full_name='service.ServiceService.DeleteService',
+    index=2,
+    containing_service=None,
+    input_type=context__pb2._SERVICEID,
+    output_type=context__pb2._EMPTY,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+  _descriptor.MethodDescriptor(
+    name='GetConnectionList',
+    full_name='service.ServiceService.GetConnectionList',
+    index=3,
+    containing_service=None,
+    input_type=context__pb2._SERVICEID,
+    output_type=context__pb2._CONNECTIONLIST,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+])
+_sym_db.RegisterServiceDescriptor(_SERVICESERVICE)
+
+DESCRIPTOR.services_by_name['ServiceService'] = _SERVICESERVICE
+
+# @@protoc_insertion_point(module_scope)
diff --git a/src/webui/requirements.in b/src/webui/requirements.in
new file mode 100644
index 0000000000000000000000000000000000000000..c57794403c1dee6f1764a877b83805b6d32f12f5
--- /dev/null
+++ b/src/webui/requirements.in
@@ -0,0 +1,12 @@
+Flask==2.0.2
+Flask-WTF==1.0.0
+flask-healthz==0.0.3
+flask-unittest==0.1.2
+grpcio==1.43.0
+grpcio-health-checking==1.43.0
+protobuf==3.19.3
+prometheus-client==0.13.0
+pytest==6.2.5
+pytest-benchmark==3.4.1
+lorem-text==2.1
+coverage==6.3
diff --git a/src/webui/service/__init__.py b/src/webui/service/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..d9755563ad60b2b897d96540a7ceaaa43f4d36dc
--- /dev/null
+++ b/src/webui/service/__init__.py
@@ -0,0 +1,83 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+import json
+
+from flask import Flask, request, session
+from flask_healthz import healthz, HealthError
+
+from webui.proto.context_pb2 import Empty
+from device.client.DeviceClient import DeviceClient
+from context.client.ContextClient import ContextClient
+from webui.Config import (CONTEXT_SERVICE_ADDRESS, CONTEXT_SERVICE_PORT,
+                DEVICE_SERVICE_ADDRESS, DEVICE_SERVICE_PORT)
+
+
+def get_working_context() -> str:
+    if 'context_uuid' in session:
+        return session['context_uuid']
+    else:
+        return 'Not selected'
+
+
+def liveness():
+    pass
+
+
+def readiness():
+    try:  # this component is ready when it is able to connect with the other components it depends on
+        context_client: ContextClient = ContextClient(CONTEXT_SERVICE_ADDRESS, CONTEXT_SERVICE_PORT)
+        context_client.connect()
+        context_client.close()
+        device_client: DeviceClient = DeviceClient(DEVICE_SERVICE_ADDRESS, DEVICE_SERVICE_PORT)
+        device_client.connect()
+        device_client.close()
+    except Exception as e:
+        raise HealthError('Can\'t connect with the service: ' + e.details())
+
+
+def from_json(json_str):
+    return json.loads(json_str)
+
+
+def create_app(use_config=None):
+    app = Flask(__name__)
+    if use_config:
+        app.config.from_mapping(**use_config)
+    
+    app.config.update(HEALTHZ={
+        'live': liveness,
+        'ready': readiness
+    })
+    
+    app.register_blueprint(healthz, url_prefix='/healthz')
+
+    from webui.service.main.routes import main
+    app.register_blueprint(main)
+
+    from webui.service.service.routes import service
+    app.register_blueprint(service)
+
+    from webui.service.device.routes import device
+    app.register_blueprint(device)
+
+    from webui.service.link.routes import link
+    app.register_blueprint(link)
+
+    app.jinja_env.filters['from_json'] = from_json
+    
+    app.jinja_env.globals.update(get_working_context=get_working_context)
+
+    return app
diff --git a/src/webui/service/__main__.py b/src/webui/service/__main__.py
new file mode 100644
index 0000000000000000000000000000000000000000..5dd20aab74751390a11b32e9ae2c63aadb9e364e
--- /dev/null
+++ b/src/webui/service/__main__.py
@@ -0,0 +1,50 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os, sys, logging
+from prometheus_client import start_http_server
+from common.Settings import wait_for_environment_variables
+from webui.service import create_app
+from webui.Config import MAX_CONTENT_LENGTH, WEBUI_SERVICE_PORT, LOG_LEVEL, METRICS_PORT, HOST, SECRET_KEY, DEBUG
+
+def main():
+    service_port = os.environ.get('WEBUISERVICE_SERVICE_PORT', WEBUI_SERVICE_PORT)
+    log_level    = os.environ.get('LOG_LEVEL',                 LOG_LEVEL         )
+    metrics_port = os.environ.get('METRICS_PORT',              METRICS_PORT      )
+    host         = os.environ.get('HOST',                      HOST              )
+    debug        = os.environ.get('DEBUG',                     DEBUG             )
+
+    logging.basicConfig(level=log_level)
+    logger = logging.getLogger(__name__)
+
+    wait_for_environment_variables([
+        'CONTEXTSERVICE_SERVICE_HOST', 'CONTEXTSERVICE_SERVICE_PORT_GRPC',
+        'DEVICESERVICE_SERVICE_HOST', 'DEVICESERVICE_SERVICE_PORT_GRPC'
+    ])
+
+    logger.info('Starting...')
+
+    start_http_server(metrics_port)
+
+    app = create_app(use_config={
+        'SECRET_KEY': SECRET_KEY,
+        'MAX_CONTENT_LENGTH': MAX_CONTENT_LENGTH,
+    })
+    app.run(host=host, port=service_port, debug=debug)
+
+    logger.info('Bye')
+    return 0
+
+if __name__ == '__main__':
+    sys.exit(main())
diff --git a/src/webui/service/context/__init__.py b/src/webui/service/context/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..70a33251242c51f49140e596b8208a19dd5245f7
--- /dev/null
+++ b/src/webui/service/context/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/webui/service/context/routes.py b/src/webui/service/context/routes.py
new file mode 100644
index 0000000000000000000000000000000000000000..29910cbc34509f1dc0848359fd7367a07174b6dc
--- /dev/null
+++ b/src/webui/service/context/routes.py
@@ -0,0 +1,23 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from flask import render_template, Blueprint, flash
+
+context = Blueprint('context', __name__)
+
+@context.route('/')
+def home():
+    flash('This is an info message', 'info')
+    flash('This is a danger message', 'danger')
+    return render_template('main/home.html')
diff --git a/src/webui/service/device/__init__.py b/src/webui/service/device/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..70a33251242c51f49140e596b8208a19dd5245f7
--- /dev/null
+++ b/src/webui/service/device/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/webui/service/device/forms.py b/src/webui/service/device/forms.py
new file mode 100644
index 0000000000000000000000000000000000000000..7d2ac45e1f95c44804ca54c17343b18101d3b104
--- /dev/null
+++ b/src/webui/service/device/forms.py
@@ -0,0 +1,55 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# external imports
+from flask_wtf import FlaskForm
+from wtforms import StringField, SelectField, TextAreaField, SubmitField
+from wtforms.validators import DataRequired, Length, NumberRange, Regexp, ValidationError
+
+from webui.utils.form_validators import key_value_validator
+from webui.proto.context_pb2 import (DeviceDriverEnum, DeviceOperationalStatusEnum)
+
+class AddDeviceForm(FlaskForm):
+    device_id = StringField('ID', 
+                           validators=[DataRequired(), Length(min=5)])
+    device_type = StringField('Type', 
+                           validators=[DataRequired(), Length(min=5)])
+    device_config = TextAreaField('Configurations', validators=[key_value_validator()])
+    operational_status = SelectField('Operational Status',
+                        #    choices=[(-1, 'Select...'), (0, 'Undefined'), (1, 'Disabled'), (2, 'Enabled')],
+                           coerce=int,
+                           validators=[NumberRange(min=0)])
+    device_drivers = TextAreaField('Drivers', validators=[DataRequired(), Regexp(r'^\d+(,\d+)*$')])
+    submit = SubmitField('Add')
+
+    def validate_operational_status(form, field):
+        if field.data not in DeviceOperationalStatusEnum.DESCRIPTOR.values_by_number:
+            raise ValidationError('The operational status value selected is incorrect!')
+    
+    def validate_device_drivers(form, field):
+        if ',' not in field.data:
+            data = str(field.data) + ','
+        else:
+            data = field.data
+        for value in data.split(','):
+            value = value.strip()
+            if len(value) == 0:
+                continue
+            try:
+                value_int = int(value)
+            except:
+                raise ValidationError(f'The value "{value}" is not a valid driver identified.')
+            if value_int not in DeviceDriverEnum.DESCRIPTOR.values_by_number:
+                values = ', '.join([str(x) for x in DeviceDriverEnum.DESCRIPTOR.values_by_number])
+                raise ValidationError(f'The device driver {value_int} is not correct. Allowed values are: {values}.')
diff --git a/src/webui/service/device/routes.py b/src/webui/service/device/routes.py
new file mode 100644
index 0000000000000000000000000000000000000000..1f9a6783c54ef14a72e9afb6db8e15b04fa20872
--- /dev/null
+++ b/src/webui/service/device/routes.py
@@ -0,0 +1,133 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from flask import render_template, Blueprint, flash, session, redirect, url_for
+from device.client.DeviceClient import DeviceClient
+from context.client.ContextClient import ContextClient
+from webui.Config import (CONTEXT_SERVICE_ADDRESS, CONTEXT_SERVICE_PORT,
+                DEVICE_SERVICE_ADDRESS, DEVICE_SERVICE_PORT)
+from webui.proto.context_pb2 import (ContextId, DeviceList, DeviceId, Empty,
+    Device, DeviceDriverEnum, DeviceOperationalStatusEnum,
+    ConfigActionEnum, ConfigRule, TopologyIdList, TopologyList)
+from webui.service.device.forms import AddDeviceForm
+
+device = Blueprint('device', __name__, url_prefix='/device')
+context_client: ContextClient = ContextClient(CONTEXT_SERVICE_ADDRESS, CONTEXT_SERVICE_PORT)
+device_client: DeviceClient = DeviceClient(DEVICE_SERVICE_ADDRESS, DEVICE_SERVICE_PORT)
+
+@device.get('/')
+def home():
+    context_uuid = session.get('context_uuid', '-')
+    if context_uuid == "-":
+        flash("Please select a context!", "warning")
+        return redirect(url_for("main.home"))
+    context_client.connect()
+    response: DeviceList = context_client.ListDevices(Empty())
+    context_client.close()
+    return render_template('device/home.html', devices=response.devices,
+                                               dde=DeviceDriverEnum,
+                                               dose=DeviceOperationalStatusEnum)
+
+@device.route('add', methods=['GET', 'POST'])
+def add():
+    form = AddDeviceForm()
+
+    # listing enum values
+    form.operational_status.choices = [(-1, 'Select...')]
+    for key, value in DeviceOperationalStatusEnum.DESCRIPTOR.values_by_name.items():
+        form.operational_status.choices.append((DeviceOperationalStatusEnum.Value(key), key.replace('DEVICEOPERATIONALSTATUS_', '')))
+    
+    # device driver ids
+    device_driver_ids = []
+    for key in DeviceDriverEnum.DESCRIPTOR.values_by_name:
+        device_driver_ids.append(f"{DeviceDriverEnum.Value(key)}={key.replace('DEVICEDRIVER_', '')}")
+    device_driver_ids = ', '.join(device_driver_ids)
+
+    if form.validate_on_submit():
+        device: Device = Device()
+        device.device_id.device_uuid.uuid = form.device_id.data
+        device.device_type = form.device_type.data
+        if '\n' not in form.device_config.data:
+            data = form.device_config.data.strip() + '\n'
+        else:
+            data = form.device_config.data.strip()
+        
+        for config in data.split('\n'):
+            if len(config.strip()) > 0:
+                parts = config.strip().split('=')
+                config_rule: ConfigRule = ConfigRule()
+                config_rule.action = ConfigActionEnum.CONFIGACTION_SET
+                config_rule.resource_key = parts[0].strip()
+                config_rule.resource_value = parts[1].strip()
+                device.device_config.config_rules.append(config_rule)
+
+        device.device_operational_status = form.operational_status.data
+
+        if ',' not in form.device_drivers.data:
+            data = form.device_drivers.data.strip() + ','
+        else:
+            data = form.device_drivers.data.strip()
+        
+        for driver in data.split(','):
+            driver = driver.strip()
+            if len(driver) == 0:
+                continue
+            device.device_drivers.append(int(driver))
+        try:
+            device_client.connect()
+            response: DeviceId = device_client.AddDevice(device)
+            device_client.close()
+
+            flash(f'New device was created with ID "{response.device_uuid.uuid}".', 'success')
+            return redirect(url_for('device.home'))
+        except Exception as e:
+            flash(f'Problem adding the device. {e.details()}', 'danger')
+        
+    return render_template('device/add.html', form=form,
+                    submit_text='Add New Device',
+                    device_driver_ids=device_driver_ids)
+
+@device.route('detail/<path:device_uuid>', methods=['GET', 'POST'])
+def detail(device_uuid: str):
+    request: DeviceId = DeviceId()
+    request.device_uuid.uuid = device_uuid
+    context_client.connect()
+    response: Device = context_client.GetDevice(request)
+    context_client.close()
+    return render_template('device/detail.html', device=response,
+                                                 dde=DeviceDriverEnum,
+                                                 dose=DeviceOperationalStatusEnum)
+
+@device.get('<path:device_uuid>/delete')
+def delete(device_uuid):
+    try:
+
+        # first, check if device exists!
+        # request: DeviceId = DeviceId()
+        # request.device_uuid.uuid = device_uuid
+        # response: Device = client.GetDevice(request)
+        # TODO: finalize implementation
+
+        request: DeviceId = DeviceId()
+        request.device_uuid.uuid = device_uuid
+        device_client.connect()
+        response = device_client.DeleteDevice(request)
+
+        device_client.close()
+
+        flash('Device deleted successfully!', 'success')
+    except Exception as e:
+        flash(f'Problem deleting the device. {e.details()}', 'danger')
+
+    return redirect(url_for('device.home'))
diff --git a/src/tester_functional/__init__.py b/src/webui/service/link/__init__.py
similarity index 100%
rename from src/tester_functional/__init__.py
rename to src/webui/service/link/__init__.py
diff --git a/src/webui/service/link/routes.py b/src/webui/service/link/routes.py
new file mode 100644
index 0000000000000000000000000000000000000000..91157a0b9450dcffe395c78b434875f9254caeed
--- /dev/null
+++ b/src/webui/service/link/routes.py
@@ -0,0 +1,37 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from flask import render_template, Blueprint, flash, session, redirect, url_for
+from device.client.DeviceClient import DeviceClient
+from context.client.ContextClient import ContextClient
+from webui.Config import (CONTEXT_SERVICE_ADDRESS, CONTEXT_SERVICE_PORT)
+from webui.proto.context_pb2 import (Empty, LinkList)
+
+link = Blueprint('link', __name__, url_prefix='/link')
+context_client: ContextClient = ContextClient(CONTEXT_SERVICE_ADDRESS, CONTEXT_SERVICE_PORT)
+
+@link.get('/')
+def home():
+    context_uuid = session.get('context_uuid', '-')
+    if context_uuid == "-":
+        flash("Please select a context!", "warning")
+        return redirect(url_for("main.home"))
+    request: Empty = Empty()
+    context_client.connect()
+    response: LinkList = context_client.ListLinks(request)
+    context_client.close()
+    return render_template(
+        "link/home.html",
+        links=response.links,
+    )
\ No newline at end of file
diff --git a/src/webui/service/main/__init__.py b/src/webui/service/main/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..70a33251242c51f49140e596b8208a19dd5245f7
--- /dev/null
+++ b/src/webui/service/main/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/webui/service/main/forms.py b/src/webui/service/main/forms.py
new file mode 100644
index 0000000000000000000000000000000000000000..abef11e06d6222c6bbab527f3a41ccdc5918480f
--- /dev/null
+++ b/src/webui/service/main/forms.py
@@ -0,0 +1,38 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# external imports
+from flask_wtf import FlaskForm
+from flask_wtf.file import FileAllowed
+from wtforms import SelectField, FileField, SubmitField
+from wtforms.validators import DataRequired, Length
+
+
+class ContextForm(FlaskForm):
+    context = SelectField(  'Context',
+                            choices=[],
+                            validators=[
+                                DataRequired(),
+                                Length(min=1)
+                            ])
+    
+    submit = SubmitField('Submit')
+
+
+class DescriptorForm(FlaskForm):
+    descriptors = FileField('Descriptors',
+                            validators=[
+                                FileAllowed(['json'], 'JSON Descriptors only!')
+                            ])
+    submit = SubmitField('Submit')
diff --git a/src/webui/service/main/routes.py b/src/webui/service/main/routes.py
new file mode 100644
index 0000000000000000000000000000000000000000..54004220a6fc54258f272a5537cc66600bafd44c
--- /dev/null
+++ b/src/webui/service/main/routes.py
@@ -0,0 +1,125 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import json
+import logging
+import sys
+from flask import jsonify, redirect, render_template, Blueprint, flash, session, url_for, request
+from webui.Config import (CONTEXT_SERVICE_ADDRESS, CONTEXT_SERVICE_PORT,
+                DEVICE_SERVICE_ADDRESS, DEVICE_SERVICE_PORT)
+from context.client.ContextClient import ContextClient
+from device.client.DeviceClient import DeviceClient
+from webui.proto.context_pb2 import Context, Device, Empty, Link, Topology
+from webui.service.main.forms import ContextForm, DescriptorForm
+
+main = Blueprint('main', __name__)
+
+context_client: ContextClient = ContextClient(CONTEXT_SERVICE_ADDRESS, CONTEXT_SERVICE_PORT)
+device_client: DeviceClient = DeviceClient(DEVICE_SERVICE_ADDRESS, DEVICE_SERVICE_PORT)
+
+logger = logging.getLogger(__name__)
+
+def process_descriptor(item_name_singluar, item_name_plural, grpc_method, grpc_class, items):
+    num_ok, num_err = 0, 0
+    for item in items:
+        try:
+            grpc_method(grpc_class(**item))
+            num_ok += 1
+        except Exception as e: # pylint: disable=broad-except
+            flash(f'Unable to add {item_name_singluar} {str(item)}: {str(e)}', 'error')
+            num_err += 1
+    if num_ok : flash(f'{str(num_ok)} {item_name_plural} added', 'success')
+    if num_err: flash(f'{str(num_err)} {item_name_plural} failed', 'danger')
+
+def process_descriptors(descriptors):
+    logger.warning(str(descriptors.data))
+    logger.warning(str(descriptors.name))
+    try:
+        logger.warning(str(request.files))
+        descriptors_file = request.files[descriptors.name]
+        logger.warning(str(descriptors_file))
+        descriptors_data = descriptors_file.read()
+        logger.warning(str(descriptors_data))
+        descriptors = json.loads(descriptors_data)
+        logger.warning(str(descriptors))
+    except Exception as e: # pylint: disable=broad-except
+        flash(f'Unable to load descriptor file: {str(e)}', 'danger')
+        return
+
+    process_descriptor('Context',  'Contexts',   context_client.SetContext,  Context,  descriptors['contexts'  ])
+    process_descriptor('Topology', 'Topologies', context_client.SetTopology, Topology, descriptors['topologies'])
+    process_descriptor('Device',   'Devices',    device_client .AddDevice,   Device,   descriptors['devices'   ])
+    process_descriptor('Link',     'Links',      context_client.SetLink,     Link,     descriptors['links'     ])
+
+@main.route('/', methods=['GET', 'POST'])
+def home():
+    context_client.connect()
+    device_client.connect()
+    response = context_client.ListContextIds(Empty())
+    context_form: ContextForm = ContextForm()
+    context_form.context.choices.append(('', 'Select...'))
+    for context in response.context_ids:
+        context_form.context.choices.append((context.context_uuid.uuid, context.context_uuid))
+    if context_form.validate_on_submit():
+        session['context_uuid'] = context_form.context.data
+        flash(f'The context was successfully set to `{context_form.context.data}`.', 'success')
+        return redirect(url_for("main.home"))
+    if 'context_uuid' in session:
+        context_form.context.data = session['context_uuid']
+    descriptor_form: DescriptorForm = DescriptorForm()
+    try:
+        if descriptor_form.validate_on_submit():
+            process_descriptors(descriptor_form.descriptors)
+            return redirect(url_for("main.home"))
+    except Exception as e:
+        logger.exception('Descriptor load failed')
+        flash(f'Descriptor load failed: `{str(e)}`', 'danger')
+    finally:
+        context_client.close()
+        device_client.close()
+    return render_template('main/home.html', context_form=context_form, descriptor_form=descriptor_form)
+
+@main.route('/topology', methods=['GET'])
+def topology():
+    context_client.connect()
+    try:
+        response = context_client.ListDevices(Empty())
+        devices = [{
+            'id': device.device_id.device_uuid.uuid,
+            'name': device.device_id.device_uuid.uuid,
+            'type': device.device_type,
+        } for device in response.devices]
+
+        response = context_client.ListLinks(Empty())
+        links = [{
+            'id': link.link_id.link_uuid.uuid,
+            'source': link.link_endpoint_ids[0].device_id.device_uuid.uuid,
+            'target': link.link_endpoint_ids[1].device_id.device_uuid.uuid,
+        } for link in response.links]
+
+        return jsonify({'devices': devices, 'links': links})
+    except:
+        logger.exception('Error retrieving topology')
+    finally:
+        context_client.close()
+
+@main.get('/about')
+def about():
+    return render_template('main/about.html')
+
+
+@main.get('/resetsession')
+def reset_session():
+    session.clear()
+    return redirect(url_for("main.home"))
diff --git a/src/webui/service/service/__init__.py b/src/webui/service/service/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..70a33251242c51f49140e596b8208a19dd5245f7
--- /dev/null
+++ b/src/webui/service/service/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/webui/service/service/routes.py b/src/webui/service/service/routes.py
new file mode 100644
index 0000000000000000000000000000000000000000..17efe1c0973550f6b185dc4ce0a0f88e9e3a82d8
--- /dev/null
+++ b/src/webui/service/service/routes.py
@@ -0,0 +1,87 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import grpc
+from flask import current_app, redirect, render_template, Blueprint, flash, session, url_for
+from context.proto.context_pb2 import Service, ServiceId
+from webui.Config import CONTEXT_SERVICE_ADDRESS, CONTEXT_SERVICE_PORT
+from context.client.ContextClient import ContextClient
+from webui.proto.context_pb2 import ContextId, ServiceList, ServiceTypeEnum, ServiceStatusEnum, ConfigActionEnum
+
+
+service = Blueprint('service', __name__, url_prefix='/service')
+
+context_client: ContextClient = ContextClient(CONTEXT_SERVICE_ADDRESS, CONTEXT_SERVICE_PORT)
+
+@service.get('/')
+def home():
+    # flash('This is an info message', 'info')
+    # flash('This is a danger message', 'danger')
+
+    context_uuid = session.get('context_uuid', '-')
+    if context_uuid == "-":
+        flash("Please select a context!", "warning")
+        return redirect(url_for("main.home"))
+    request: ContextId = ContextId()
+    request.context_uuid.uuid = context_uuid
+    context_client.connect()
+    try:
+        service_list: ServiceList = context_client.ListServices(request)
+        # print(service_list)
+        services = service_list.services
+        context_not_found = False
+    except grpc.RpcError as e:
+        if e.code() != grpc.StatusCode.NOT_FOUND: raise
+        if e.details() != 'Context({:s}) not found'.format(context_uuid): raise
+        services = []
+        context_not_found = True
+
+    context_client.close()
+    return render_template('service/home.html', services=services,
+                                                context_not_found=context_not_found,
+                                                ste=ServiceTypeEnum,
+                                                sse=ServiceStatusEnum)
+
+
+@service.route('add', methods=['GET', 'POST'])
+def add():
+    flash('Add service route called', 'danger')
+    raise NotImplementedError()
+    return render_template('service/home.html')
+
+
+@service.get('detail/<path:service_uuid>')
+def detail(service_uuid: str):
+    context_uuid = session.get('context_uuid', '-')
+    if context_uuid == "-":
+        flash("Please select a context!", "warning")
+        return redirect(url_for("main.home"))
+    
+    request: ServiceId = ServiceId()
+    request.service_uuid.uuid = service_uuid
+    request.context_id.context_uuid.uuid = context_uuid
+    try:
+        context_client.connect()
+        response: Service = context_client.GetService(request)
+        context_client.close()
+    except Exception as e:
+        flash('The system encountered an error and cannot show the details of this service.', 'warning')
+        current_app.logger.exception(e)
+        return redirect(url_for('service.home'))
+    return render_template('service/detail.html', service=response)
+
+
+@service.get('delete/<path:service_uuid>')
+def delete(service_uuid: str):
+    pass
diff --git a/src/webui/service/static/partners.png b/src/webui/service/static/partners.png
new file mode 100644
index 0000000000000000000000000000000000000000..0c2b89eb9321caf8d1f1d63e0e20fd3e4b6ddeb7
Binary files /dev/null and b/src/webui/service/static/partners.png differ
diff --git a/src/webui/service/static/site.js b/src/webui/service/static/site.js
new file mode 100644
index 0000000000000000000000000000000000000000..1cd015f17cf87a6efe319d8d7090d81889e03062
--- /dev/null
+++ b/src/webui/service/static/site.js
@@ -0,0 +1,20 @@
+/**
+ * Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'))
+var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
+  return new bootstrap.Tooltip(tooltipTriggerEl)
+})
diff --git a/src/webui/service/static/topology.js b/src/webui/service/static/topology.js
new file mode 100644
index 0000000000000000000000000000000000000000..dd58388cd2b7253f328b2ba7f38e007cdcc1007f
--- /dev/null
+++ b/src/webui/service/static/topology.js
@@ -0,0 +1,148 @@
+// Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Based on:
+//   https://www.d3-graph-gallery.com/graph/network_basic.html
+//   https://bl.ocks.org/steveharoz/8c3e2524079a8c440df60c1ab72b5d03
+
+// set the dimensions and margins of the graph
+const margin = {top: 5, right: 5, bottom: 5, left: 5};
+
+const icon_width  = 40;
+const icon_height = 40;
+
+width = 800 - margin.left - margin.right;
+height = 500 - margin.top - margin.bottom;
+
+// append the svg object to the body of the page
+const svg = d3.select('#topology')
+    .append('svg')
+        .attr('width', width + margin.left + margin.right)
+        .attr('height', height + margin.top + margin.bottom)
+    .append('g')
+        .attr('transform', `translate(${margin.left}, ${margin.top})`);
+
+// svg objects
+var link, node;
+
+// values for all forces
+forceProperties = {
+    center: {x: 0.5, y: 0.5},
+    charge: {enabled: true, strength: -500, distanceMin: 10, distanceMax: 2000},
+    collide: {enabled: true, strength: 0.7, iterations: 1, radius: 5},
+    forceX: {enabled: false, strength: 0.1, x: 0.5},
+    forceY: {enabled: false, strength: 0.1, y: 0.5},
+    link: {enabled: true, distance: 100, iterations: 1}
+}
+
+/**************** FORCE SIMULATION *****************/
+
+var simulation = d3.forceSimulation();
+
+// load the data
+d3.json('/topology', function(data) {
+    // set the data and properties of link lines and node circles
+    link = svg.append("g").attr("class", "links").style('stroke', '#aaa')
+        .selectAll("line")
+        .data(data.links)
+        .enter()
+        .append("line");
+    node = svg.append("g").attr("class", "devices").attr('r', 20).style('fill', '#69b3a2')
+        .selectAll("circle")
+        .data(data.devices)
+        .enter()
+        .append("image")
+        .attr('xlink:href', function(d) {return '/static/topology_icons/' + d.type + '.png';})
+        .attr('width',  icon_width)
+        .attr('height', icon_height)
+        .call(d3.drag().on("start", dragstarted).on("drag", dragged).on("end", dragended));
+
+    // node tooltip
+    node.append("title").text(function(d) { return d.id; });
+
+    // link style
+    link
+        .attr("stroke-width", forceProperties.link.enabled ? 2 : 1)
+        .attr("opacity", forceProperties.link.enabled ? 1 : 0);
+    
+    // set up the simulation and event to update locations after each tick
+    simulation.nodes(data.devices);
+
+    // add forces, associate each with a name, and set their properties
+    simulation
+        .force("link", d3.forceLink()
+            .id(function(d) {return d.id;})
+            .distance(forceProperties.link.distance)
+            .iterations(forceProperties.link.iterations)
+            .links(forceProperties.link.enabled ? data.links : []))
+        .force("charge", d3.forceManyBody()
+            .strength(forceProperties.charge.strength * forceProperties.charge.enabled)
+            .distanceMin(forceProperties.charge.distanceMin)
+            .distanceMax(forceProperties.charge.distanceMax))
+        .force("collide", d3.forceCollide()
+            .strength(forceProperties.collide.strength * forceProperties.collide.enabled)
+            .radius(forceProperties.collide.radius)
+            .iterations(forceProperties.collide.iterations))
+        .force("center", d3.forceCenter()
+            .x(width * forceProperties.center.x)
+            .y(height * forceProperties.center.y))
+        .force("forceX", d3.forceX()
+            .strength(forceProperties.forceX.strength * forceProperties.forceX.enabled)
+            .x(width * forceProperties.forceX.x))
+        .force("forceY", d3.forceY()
+            .strength(forceProperties.forceY.strength * forceProperties.forceY.enabled)
+            .y(height * forceProperties.forceY.y));
+    
+    // after each simulation tick, update the display positions
+    simulation.on("tick", ticked);
+});
+
+// update the display positions
+function ticked() {
+    link
+        .attr('x1', function(d) { return d.source.x; })
+        .attr('y1', function(d) { return d.source.y; })
+        .attr('x2', function(d) { return d.target.x; })
+        .attr('y2', function(d) { return d.target.y; });
+
+    node
+        .attr('x', function(d) { return d.x-icon_width/2; })
+        .attr('y', function(d) { return d.y-icon_height/2; });
+}
+
+/******************** UI EVENTS ********************/
+
+function dragstarted(d) {
+    if (!d3.event.active) simulation.alphaTarget(0.3).restart();
+    d.fx = d.x;
+    d.fy = d.y;
+}
+
+function dragged(d) {
+    d.fx = d3.event.x;
+    d.fy = d3.event.y;
+}
+
+function dragended(d) {
+    if (!d3.event.active) simulation.alphaTarget(0.0001);
+    d.fx = null;
+    d.fy = null;
+}
+
+// update size-related forces
+d3.select(window).on("resize", function(){
+    width = +svg.node().getBoundingClientRect().width;
+    height = +svg.node().getBoundingClientRect().height;
+    simulation.alpha(1).restart();
+});
diff --git a/src/webui/service/static/topology_icons/Acknowledgements.txt b/src/webui/service/static/topology_icons/Acknowledgements.txt
new file mode 100644
index 0000000000000000000000000000000000000000..c646efdec0d79148f9bd066116d6ca3985f6f909
--- /dev/null
+++ b/src/webui/service/static/topology_icons/Acknowledgements.txt
@@ -0,0 +1,12 @@
+Network Topology Icons taken from https://vecta.io/symbols
+
+https://symbols.getvecta.com/stencil_240/51_cloud.4d0a827676.png => cloud.png
+
+https://symbols.getvecta.com/stencil_240/15_atm-switch.1bbf9a7cca.png => packet-switch.png
+https://symbols.getvecta.com/stencil_241/45_atm-switch.6a7362c1df.png => emu-packet-switch.png
+
+https://symbols.getvecta.com/stencil_240/204_router.7b208c1133.png => packet-router.png
+https://symbols.getvecta.com/stencil_241/224_router.be30fb87e7.png => emu-packet-router.png
+
+https://symbols.getvecta.com/stencil_240/269_virtual-layer-switch.ed10fdede6.png => optical-line-system.png
+https://symbols.getvecta.com/stencil_241/281_virtual-layer-switch.29420aff2f.png => emu-optical-line-system.png
diff --git a/src/webui/service/static/topology_icons/cloud.png b/src/webui/service/static/topology_icons/cloud.png
new file mode 100644
index 0000000000000000000000000000000000000000..0f8e9c9714edd1c11904367ef1e9c60ef7ed3295
Binary files /dev/null and b/src/webui/service/static/topology_icons/cloud.png differ
diff --git a/src/webui/service/static/topology_icons/emu-optical-line-system.png b/src/webui/service/static/topology_icons/emu-optical-line-system.png
new file mode 100644
index 0000000000000000000000000000000000000000..a5c30d679170c6e080dee3cc5239bf7ecaefe743
Binary files /dev/null and b/src/webui/service/static/topology_icons/emu-optical-line-system.png differ
diff --git a/src/webui/service/static/topology_icons/emu-packet-router.png b/src/webui/service/static/topology_icons/emu-packet-router.png
new file mode 100644
index 0000000000000000000000000000000000000000..95fc8b9f35a0cda9440a07ac0df3d0d417cdd0f2
Binary files /dev/null and b/src/webui/service/static/topology_icons/emu-packet-router.png differ
diff --git a/src/webui/service/static/topology_icons/emu-packet-switch.png b/src/webui/service/static/topology_icons/emu-packet-switch.png
new file mode 100644
index 0000000000000000000000000000000000000000..f9d431cd29b9eebc7eb6ec503ba8ed777082fa21
Binary files /dev/null and b/src/webui/service/static/topology_icons/emu-packet-switch.png differ
diff --git a/src/webui/service/static/topology_icons/optical-line-system.png b/src/webui/service/static/topology_icons/optical-line-system.png
new file mode 100644
index 0000000000000000000000000000000000000000..b51f094216755ed9fc5c7a7e8957bab88090c954
Binary files /dev/null and b/src/webui/service/static/topology_icons/optical-line-system.png differ
diff --git a/src/webui/service/static/topology_icons/packet-router.png b/src/webui/service/static/topology_icons/packet-router.png
new file mode 100644
index 0000000000000000000000000000000000000000..5d2ad4dabe730f78206afddb21886c6678e88a26
Binary files /dev/null and b/src/webui/service/static/topology_icons/packet-router.png differ
diff --git a/src/webui/service/static/topology_icons/packet-switch.png b/src/webui/service/static/topology_icons/packet-switch.png
new file mode 100644
index 0000000000000000000000000000000000000000..14f81111f9fbb4236f60f92cac1147365112bc41
Binary files /dev/null and b/src/webui/service/static/topology_icons/packet-switch.png differ
diff --git a/src/webui/service/templates/base.html b/src/webui/service/templates/base.html
new file mode 100644
index 0000000000000000000000000000000000000000..d5f748eae43531bc58951aa44b24c5272d230883
--- /dev/null
+++ b/src/webui/service/templates/base.html
@@ -0,0 +1,155 @@
+<!doctype html>
+<!--
+ Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<html lang="en">
+  <head>
+    <!-- Required meta tags -->
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+
+    <link rel="shortcut icon" href="https://teraflow-h2020.eu/sites/teraflow/files/public/favicon.png" type="image/png" />
+
+    <!-- Bootstrap CSS -->
+    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-uWxY/CJNBR+1zjPWmfnSnVxwRheevXITnMqoEIeG1LJrdI0GlVs/9cVSyPYXdcSF" crossorigin="anonymous">
+    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.5.0/font/bootstrap-icons.css">
+
+    <title>TeraFlow OFC 2022 Demo</title>
+  </head>
+  <body>
+      <div id="teraflow-branding" style="width: 260px; margin: 7px;">
+        <a href="/" title="Home" rel="home" id="main-logo" class="site-logo site-logo-pages">
+            <svg id="Capa_1" data-name="Capa 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 436.3 132.1"><defs><style>.cls-1{fill:#36a9e1;}.cls-2{fill:#1d71b8;}.cls-3{fill:none;stroke-width:2.52px;}.cls-10,.cls-3,.cls-4,.cls-5,.cls-7,.cls-8,.cls-9{stroke:#0f77b6;}.cls-3,.cls-4,.cls-8{stroke-miterlimit:10;}.cls-10,.cls-4,.cls-5,.cls-7,.cls-8,.cls-9{fill:#fff;}.cls-4{stroke-width:0.73px;}.cls-5,.cls-7{stroke-miterlimit:10;}.cls-5{stroke-width:0.75px;}.cls-6{fill:#0f77b6;}.cls-7{stroke-width:0.72px;}.cls-8{stroke-width:0.7px;}.cls-9{stroke-miterlimit:10;stroke-width:0.69px;}.cls-10{stroke-miterlimit:10;stroke-width:0.7px;}</style></defs><path class="cls-1" d="M96,57V51.3h44.1V57H121v52.3h-5.9V57Z"></path><path class="cls-1" d="M168.9,95.1l4.7,2.4a26,26,0,0,1-5.3,7.3,22.27,22.27,0,0,1-6.7,4.2,22.64,22.64,0,0,1-8.5,1.4c-7,0-12.5-2.3-16.4-6.9a23.53,23.53,0,0,1-5.9-15.6,23,23,0,0,1,5-14.5c4.2-5.4,9.9-8.1,17-8.1,7.3,0,13.2,2.8,17.5,8.3,3.1,3.9,4.7,8.8,4.7,14.7H136.4a17.48,17.48,0,0,0,4.8,12.3,15.26,15.26,0,0,0,11.4,4.8,20,20,0,0,0,6.4-1.1,19.3,19.3,0,0,0,5.3-3A33.07,33.07,0,0,0,168.9,95.1Zm0-11.6a18.66,18.66,0,0,0-3.2-7.1,15.25,15.25,0,0,0-5.6-4.3,16.87,16.87,0,0,0-7.3-1.6,16.06,16.06,0,0,0-10.9,4.1,18.15,18.15,0,0,0-5,8.9Z"></path><path class="cls-1" d="M182,66.4h5.6v6.3a20,20,0,0,1,5.3-5.5,10.67,10.67,0,0,1,5.8-1.8,9.87,9.87,0,0,1,4.9,1.5l-2.9,4.7a7.52,7.52,0,0,0-2.9-.7,8.09,8.09,0,0,0-5.3,2.3,14.64,14.64,0,0,0-3.9,7c-.7,2.4-1,7.4-1,14.8v14.5H182Z"></path><path class="cls-1" d="M246.2,66.4v42.9h-5.4V102a23.11,23.11,0,0,1-7.8,6.3,21.23,21.23,0,0,1-9.4,2.1,21,21,0,0,1-15.6-6.6,23.07,23.07,0,0,1,.1-32,21.23,21.23,0,0,1,15.7-6.6,20,20,0,0,1,17.1,8.9V66.2h5.3Zm-22.1,4.2a16.67,16.67,0,0,0-8.5,2.3,15.93,15.93,0,0,0-6.2,6.4,17.68,17.68,0,0,0-2.3,8.7,18.26,18.26,0,0,0,2.3,8.7,15.93,15.93,0,0,0,6.2,6.4,16.58,16.58,0,0,0,8.4,2.3,17.59,17.59,0,0,0,8.6-2.3,15.42,15.42,0,0,0,6.2-6.2,17.17,17.17,0,0,0,2.2-8.8,16.73,16.73,0,0,0-4.9-12.4A15.8,15.8,0,0,0,224.1,70.6Z"></path><path class="cls-2" d="M259.5,51.3h29.1V57H265.3V75.2h23.3v5.7H265.3v28.5h-5.8V51.3Z"></path><path class="cls-2" d="M296.9,49.9h5.5v59.5h-5.5Z"></path><path class="cls-2" d="M330.5,65.3a21.1,21.1,0,0,1,16.4,7.2A22.55,22.55,0,0,1,352.8,88a22.24,22.24,0,0,1-6.3,15.7c-4.2,4.5-9.5,6.7-16.1,6.7s-12-2.2-16.1-6.7A22.24,22.24,0,0,1,308,88a22.73,22.73,0,0,1,5.9-15.5A21.81,21.81,0,0,1,330.5,65.3Zm0,5.4a15.83,15.83,0,0,0-11.8,5.1,17,17,0,0,0-4.9,12.3,17.68,17.68,0,0,0,2.3,8.7,15.19,15.19,0,0,0,6.1,6.2,16.48,16.48,0,0,0,8.4,2.2A16,16,0,0,0,339,103a15.82,15.82,0,0,0,6.1-6.2,17.68,17.68,0,0,0,2.3-8.7,17.07,17.07,0,0,0-5-12.3A16.2,16.2,0,0,0,330.5,70.7Z"></path><path class="cls-2" d="M351.2,66.4h5.7L370,97.6l13.7-31.1h1l13.8,31.1,13.4-31.1h5.7L399,109.3h-1L384.3,78.6l-13.7,30.7h-1Z"></path><polyline class="cls-3" points="51 105 51 41.2 27 41.2"></polyline><polyline class="cls-3" points="38.1 33.8 56.4 33.8 56.4 93"></polyline><polyline class="cls-3" points="79.9 33.8 61.5 33.8 61.5 79.2"></polyline><polyline class="cls-3" points="90.7 41.2 66.7 41.2 66.7 105"></polyline><line class="cls-3" x1="83.1" y1="62.6" x2="66.7" y2="62.6"></line><circle class="cls-4" cx="27" cy="41.2" r="5.3"></circle><path class="cls-1" d="M23.3,41.2a3.8,3.8,0,1,0,3.8-3.8A3.8,3.8,0,0,0,23.3,41.2Z"></path><circle class="cls-5" cx="51" cy="105" r="5.4"></circle><path class="cls-1" d="M47.3,105a3.8,3.8,0,1,0,3.8-3.8A3.8,3.8,0,0,0,47.3,105Z"></path><circle class="cls-6" cx="56.36" cy="93.02" r="3.4"></circle><circle class="cls-6" cx="61.5" cy="79.2" r="2.8"></circle><circle class="cls-7" cx="66.7" cy="105.01" r="5.3"></circle><path class="cls-1" d="M63,105a3.8,3.8,0,1,0,3.8-3.8A3.8,3.8,0,0,0,63,105Z"></path><circle class="cls-8" cx="90.7" cy="41.2" r="5.1"></circle><path class="cls-1" d="M87,41.2a3.8,3.8,0,1,0,3.8-3.8A3.8,3.8,0,0,0,87,41.2Z"></path><circle class="cls-8" cx="84.7" cy="62.6" r="5.1"></circle><path class="cls-1" d="M81,62.6a3.8,3.8,0,1,0,3.8-3.8A3.8,3.8,0,0,0,81,62.6Z"></path><line class="cls-3" x1="34.8" y1="62.6" x2="51.1" y2="62.6"></line><circle class="cls-8" cx="33.1" cy="62.6" r="5.1"></circle><path class="cls-1" d="M36.9,62.6a3.8,3.8,0,1,1-3.8-3.8A3.8,3.8,0,0,1,36.9,62.6Z"></path><line class="cls-3" x1="23.7" y1="26.7" x2="94.1" y2="26.7"></line><circle class="cls-9" cx="94.09" cy="26.67" r="5"></circle><path class="cls-1" d="M90.3,26.7a3.8,3.8,0,1,0,3.8-3.8A3.8,3.8,0,0,0,90.3,26.7Z"></path><circle class="cls-6" cx="78" cy="33.8" r="3.8"></circle><circle class="cls-6" cx="40" cy="33.8" r="3.8"></circle><circle class="cls-10" cx="23.71" cy="26.71" r="5.1"></circle><path class="cls-1" d="M20,26.7a3.8,3.8,0,1,0,3.8-3.8A3.8,3.8,0,0,0,20,26.7Z"></path></svg>
+          </a>
+      </div>
+
+    <nav class="navbar navbar-expand-lg navbar-dark bg-primary" style="margin-bottom: 10px;">
+        <div class="container-fluid">
+          <a class="navbar-brand" href="{{ url_for('main.home') }}">
+            <img src="https://teraflow-h2020.eu/sites/teraflow/files/public/favicon.png" alt="" width="30" height="24" class="d-inline-block align-text-top"/>
+            TeraFlow
+          </a>
+          <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarColor02" aria-controls="navbarColor02" aria-expanded="false" aria-label="Toggle navigation">
+            <span class="navbar-toggler-icon"></span>
+          </button>
+          <div class="collapse navbar-collapse" id="navbarColor02">
+            <ul class="navbar-nav me-auto mb-2 mb-lg-0">
+              <li class="nav-item">
+                {% if request.path == '/' %}
+                <a class="nav-link active" aria-current="page" href="{{ url_for('main.home') }}">Home</a>
+                {% else %}
+                <a class="nav-link" href="{{ url_for('main.home') }}">Home</a>
+                {% endif %}
+              </li>
+              <li class="nav-item">
+                {% if '/device/' in request.path %}
+                <a class="nav-link active" aria-current="page" href="{{ url_for('device.home') }}">Device</a>
+                {% else %}
+                <a class="nav-link" href="{{ url_for('device.home') }}">Device</a>
+                {% endif %}
+              </li>
+              <li class="nav-item">
+                {% if '/link/' in request.path %}
+                <a class="nav-link active" aria-current="page" href="{{ url_for('link.home') }}">Link</a>
+                {% else %}
+                <a class="nav-link" href="{{ url_for('link.home') }}">Link</a>
+                {% endif %}
+              </li>
+              <li class="nav-item">
+                {% if '/service/' in request.path %}
+                <a class="nav-link active" aria-current="page" href="{{ url_for('service.home') }}">Service</a>
+                {% else %}
+                <a class="nav-link" href="{{ url_for('service.home') }}">Service</a>
+                {% endif %}
+              </li>
+              
+              <li class="nav-item">
+                <a class="nav-link" href="#" id="grafana_link" target="grafana">Grafana</a>
+              </li>
+              <!-- <li class="nav-item">
+                <a class="nav-link" href="#">Context</a>
+              </li>
+              
+              <li class="nav-item">
+                <a class="nav-link" href="#">Monitoring</a>
+              </li> -->
+              <li class="nav-item">
+                <a class="nav-link" href="{{ url_for('main.about') }}">About</a>
+              </li>
+            </ul>
+            <span class="navbar-text" style="color: #fff;">
+              Current context: <b>{{ get_working_context() }}</b>
+            </span>
+          </div>
+        </div>
+      </nav>
+
+      <main class="container">
+        <div class="row">
+          <div class="col-md-12">
+            {% with messages = get_flashed_messages(with_categories=true) %}
+              {% if messages %}
+                {% for category, message in messages %}
+                  <div class="alert alert-{{ category }} alert-dismissible fade show" role="alert">
+                    {{ message }}
+                    <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
+                  </div>
+      
+                {% endfor %}
+              {% endif %}
+            {% endwith %}
+          </div>
+        </div>
+        <div class="row">
+          <div class="col-xxl-12">
+          {% block content %}{% endblock %}
+          </div>
+        </div>
+      </main>
+
+      <footer class="footer" style="background-color: darkgrey; margin-top: 30px; padding-top: 20px;">
+        <div class="container">
+          <div class="row">
+            <div class="col-md-12">
+              <p class="text-center" style="color: white;">&copy; 2021-2023</p>
+            </div>
+          </div>
+          <div class="row">
+            <div class="col-md-6">
+              <p>This project has received funding from the European Union's Horizon 2020 research and innovation programme under grant agreement No 101015857.</p>
+            </div>
+            <div class="col-md-6">
+              <img src="https://teraflow-h2020.eu/sites/teraflow/files/public/content-images/media/2021/logo%205G-ppp%20eu.png" width="310" alt="5g ppp EU logo" loading="lazy" typeof="foaf:Image">
+            </div>
+          </div>
+        </div>
+      </footer>
+
+    <!-- Optional JavaScript; choose one of the two! -->
+
+    <!-- Option 1: Bootstrap Bundle with Popper -->
+    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-kQtW33rZJAHjgefvhyyzcGF3C5TFyBQBA13V1RKPf4uH+bwyzQxZ6CmMZHmNBEfJ" crossorigin="anonymous"></script>
+    <!-- <script src="{{ url_for('static', filename='site.js') }}"/> -->
+    <script>
+      document.getElementById("grafana_link").href = window.location.protocol + "//" + window.location.hostname + ":30300"
+    </script>
+    <!-- Option 2: Separate Popper and Bootstrap JS -->
+    <!--
+    <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.10.2/dist/umd/popper.min.js" integrity="sha384-7+zCNj/IqJ95wo16oMtfsKbZ9ccEh31eOz1HGyDuCQ6wgnyJNSYdrPa03rtR1zdB" crossorigin="anonymous"></script>
+    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.2/dist/js/bootstrap.min.js" integrity="sha384-PsUw7Xwds7x08Ew3exXhqzbhuEYmA2xnwc8BuD6SEr+UmEHlX8/MCltYEodzWA4u" crossorigin="anonymous"></script>
+    -->
+  </body>
+</html>
\ No newline at end of file
diff --git a/src/webui/service/templates/context/home.html b/src/webui/service/templates/context/home.html
new file mode 100644
index 0000000000000000000000000000000000000000..9e1d1cd531c5542efdfbf87dbe9d452cd1956f14
--- /dev/null
+++ b/src/webui/service/templates/context/home.html
@@ -0,0 +1,95 @@
+<!--
+ Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+{% extends 'base.html' %}
+
+{% block content %}
+    <h1>Service</h1>
+
+    <div class="row">
+        <div class="col">
+            <a href="{{ url_for('service.add') }}" class="btn btn-primary" style="margin-bottom: 10px;">
+                <i class="bi bi-plus"></i>
+                Add New Service
+            </a>
+        </div>
+        <div class="col">
+            {{ services | length }} services found
+        </div>
+        <div class="col">
+            <form>
+                <div class="input-group">
+                    <input type="text" aria-label="Search" placeholder="Search..." class="form-control"/>
+                    <button type="submit" class="btn btn-primary">Search</button>
+                  </div>
+            </form>
+        </div>
+    </div>
+    
+
+    <table class="table table-striped">
+        <thead>
+          <tr>
+            <th scope="col">#</th>
+            <th scope="col">Type</th>
+            <th scope="col">End points</th>
+            <th scope="col">Constraints</th>
+            <th scope="col">State</th>
+            <th scope="col">Configuration</th>
+            <th scope="col"></th>
+          </tr>
+        </thead>
+        <tbody>
+            {% if services %}
+                {% for service in services %}
+                <tr>
+                    <td><a href="{{ url_for('service.detail', contextUuid=service.cs_id.contextId.contextUuid.uuid, serviceUuid=service.cs_id.cs_id.uuid) }}">{{ service.cs_id.contextId.contextUuid.uuid }} / {{ service.cs_id.cs_id.uuid }}</a></td>
+                    <td>{{ service.serviceType }}</td>
+                    <td>
+                        <ul>
+                            {% for end_point in service.endpointList %}
+                            <li>{{ end_point }}</li>
+                            {% endfor %}
+                        </ul>
+                    </td>
+                    <td>
+                        <ul>
+                            {% for constraint in service.constraint %}
+                            <li>{{ constraint }}</li>
+                            {% endfor %}
+                        </ul>
+                    </td>
+                    <td>{{ service.serviceState }}</td>
+                    <td>{{ service.serviceConfig }}</td>
+                    <td>
+                        <a href="{{ url_for('service.detail', contextUuid=service.cs_id.contextId.contextUuid.uuid, serviceUuid=service.cs_id.cs_id.uuid) }}">
+                            <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-eye" viewBox="0 0 16 16">
+                                <path d="M16 8s-3-5.5-8-5.5S0 8 0 8s3 5.5 8 5.5S16 8 16 8zM1.173 8a13.133 13.133 0 0 1 1.66-2.043C4.12 4.668 5.88 3.5 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.133 13.133 0 0 1 14.828 8c-.058.087-.122.183-.195.288-.335.48-.83 1.12-1.465 1.755C11.879 11.332 10.119 12.5 8 12.5c-2.12 0-3.879-1.168-5.168-2.457A13.134 13.134 0 0 1 1.172 8z"/>
+                                <path d="M8 5.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5zM4.5 8a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0z"/>
+                            </svg>
+                        </a>
+                    </td>
+                </tr>
+                {% endfor %}
+            {% else %}
+                <tr>
+                    <td colspan="7">No services found</td>
+                </tr>
+            {% endif %}
+        </tbody>
+    </table>
+
+{% endblock %}
\ No newline at end of file
diff --git a/src/webui/service/templates/device/add.html b/src/webui/service/templates/device/add.html
new file mode 100644
index 0000000000000000000000000000000000000000..fe1ba31f26579fcf681ccf4da64a7906cae9cff5
--- /dev/null
+++ b/src/webui/service/templates/device/add.html
@@ -0,0 +1,116 @@
+<!--
+ Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+{% extends 'base.html' %}
+
+{% block content %}
+    <h1>Add New Device</h1>
+
+    <form id="add_device" method="POST">
+        {{ form.hidden_tag() }}
+        <fieldset>
+            <div class="row mb-3">
+                {{ form.device_id.label(class="col-sm-2 col-form-label") }}
+                <div class="col-sm-10">
+                  {% if form.device_id.errors %}
+                        {{ form.device_id(class="form-control is-invalid") }}
+                        <div class="invalid-feedback">
+                            {% for error in form.device_id.errors %}
+                                <span>{{ error }}</span>
+                            {% endfor %}
+                        </div>
+                    {% else %}
+                        {{ form.device_id(class="form-control") }}
+                    {% endif %}
+                </div>
+            </div>
+            <div class="row mb-3">
+                {{ form.device_type.label(class="col-sm-2 col-form-label") }}
+                <div class="col-sm-10">
+                  {% if form.device_type.errors %}
+                        {{ form.device_type(class="form-control is-invalid") }}
+                        <div class="invalid-feedback">
+                            {% for error in form.device_type.errors %}
+                                <span>{{ error }}</span>
+                            {% endfor %}
+                        </div>
+                    {% else %}
+                        {{ form.device_type(class="form-control") }}
+                    {% endif %}
+                </div>
+            </div>
+            <div class="row mb-3">
+                {{ form.operational_status.label(class="col-sm-2 col-form-label") }}
+                <div class="col-sm-10">
+                  {% if form.operational_status.errors %}
+                        {{ form.operational_status(class="form-control is-invalid") }}
+                        <div class="invalid-feedback">
+                            {% for error in form.operational_status.errors %}
+                                <span>{{ error }}</span>
+                            {% endfor %}
+                        </div>
+                    {% else %}
+                        {{ form.operational_status(class="form-control") }}
+                    {% endif %}
+                </div>
+            </div>
+            <div class="row mb-3">
+                {{ form.device_config.label(class="col-sm-2 col-form-label") }}
+                <div class="col-sm-10">
+                  {% if form.device_config.errors %}
+                        {{ form.device_config(class="form-control is-invalid", rows=5) }}
+                        <div class="invalid-feedback">
+                            {% for error in form.device_config.errors %}
+                                <span>{{ error }}</span>
+                            {% endfor %}
+                        </div>
+                    {% else %}
+                        {{ form.device_config(class="form-control", rows=5) }}
+                    {% endif %}
+                </div>
+                <div id="device_config_help" class="form-text">The device configurations should follow a <i>key=value</i> format, one configuration per line.</div>
+            </div>
+            <div class="row mb-3">
+                {{ form.device_drivers.label(class="col-sm-2 col-form-label") }}
+                <div class="col-sm-10">
+                  {% if form.device_drivers.errors %}
+                        {{ form.device_drivers(class="form-control is-invalid", rows=5) }}
+                        <div class="invalid-feedback">
+                            {% for error in form.device_drivers.errors %}
+                                <span>{{ error }}</span>
+                            {% endfor %}
+                        </div>
+                    {% else %}
+                        {{ form.device_drivers(class="form-control", rows=5) }}
+                    {% endif %}
+                </div>
+                <div id="device_drivers_help" class="form-text">
+                    List the device drivers by their numerical ID, separated by commas, without spaces between them. Numerical IDs: {{ device_driver_ids }}.
+                </div>
+            </div>
+            <div class="d-grid gap-2 d-md-flex justify-content-md-start">
+                <button type="submit" class="btn btn-primary">
+                    <i class="bi bi-plus-circle-fill"></i>
+                    {{ submit_text }}
+                </button>
+                <button type="button" class="btn btn-block btn-secondary" onclick="javascript: history.back()">
+                    <i class="bi bi-box-arrow-in-left"></i>
+                    Cancel
+                </button>
+              </div>
+        </fieldset>
+    </form>
+{% endblock %}
\ No newline at end of file
diff --git a/src/webui/service/templates/device/detail.html b/src/webui/service/templates/device/detail.html
new file mode 100644
index 0000000000000000000000000000000000000000..143bbeed70b545f6d1e123efe5f8559a3cc44355
--- /dev/null
+++ b/src/webui/service/templates/device/detail.html
@@ -0,0 +1,109 @@
+<!--
+ Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+{% extends 'base.html' %}
+
+{% block content %}
+    <h1>Device {{ device.device_id.device_uuid.uuid }}</h1>
+
+    <div class="row mb-3">
+        <div class="col-sm-3">
+            <button type="button" class="btn btn-success" onclick="window.location.href = '/device/'">
+                <i class="bi bi-box-arrow-in-left"></i>
+                Back to device list
+            </button>
+        </div>
+        <div class="col-sm-3">
+            <a id="update" class="btn btn-secondary" href="#">
+                <i class="bi bi-pencil-square"></i>
+                Update
+            </a>
+        </div>
+        <div class="col-sm-3">
+            <!-- <button type="button" class="btn btn-danger"><i class="bi bi-x-square"></i>Delete device</button> -->
+            <button type="button" class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#deleteModal">
+                <i class="bi bi-x-square"></i>Delete device
+              </button>
+        </div>
+    </div>
+
+    <div class="row mb-3">
+        <div class="col-sm-1"><b>UUID:</b></div>
+        <div class="col-sm-5">
+            {{ device.device_id.device_uuid.uuid }}
+        </div>
+        <div class="col-sm-1"><b>Type:</b></div>
+        <div class="col-sm-5">
+            {{ device.device_type }}
+        </div>
+    </div>
+    <div class="row mb-3">
+        <div class="col-sm-1"><b>Drivers:</b></div>
+        <div class="col-sm-11">
+            <ul>
+                {% for driver in device.device_drivers %}
+                <li>{{ dde.Name(driver).replace('DEVICEDRIVER_', '').replace('UNDEFINED', 'EMULATED') }}</li>
+                {% endfor %}
+            </ul>
+        </div>
+    </div>
+    <div class="row mb-3">
+        <b>Endpoints:</b>
+        <div class="col-sm-10">
+            <ul>
+            {% for endpoint in device.device_endpoints %}
+                <li>{{ endpoint.endpoint_id.endpoint_uuid.uuid }}: {{ endpoint.endpoint_type }}</li>
+            {% endfor %}
+            </ul>
+        </div>
+    </div>
+    <div class="row mb-3">
+        <b>Configurations:</b>
+        <div class="col-sm-10">
+            <ul>
+            {% for config in device.device_config.config_rules %}
+                <li>{{ config.resource_key }}:
+                    <ul>
+                        {% for key, value in (config.resource_value | from_json).items() %}
+                        <li><b>{{ key }}:</b> {{ value }}</li>
+                        {% endfor %}
+                    </ul>
+                </li>
+            {% endfor %}
+            </ul>
+        </div>
+    </div>
+
+    <!-- Modal -->
+<div class="modal fade" id="deleteModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
+    <div class="modal-dialog">
+      <div class="modal-content">
+        <div class="modal-header">
+          <h5 class="modal-title" id="staticBackdropLabel">Delete device?</h5>
+          <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
+        </div>
+        <div class="modal-body">
+          Are you sure you want to delete the device "{{ device.device_id.device_uuid.uuid }}"?
+        </div>
+        <div class="modal-footer">
+          <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">No</button>
+          <a type="button" class="btn btn-danger" href="{{ url_for('device.delete', device_uuid=device.device_id.device_uuid.uuid) }}"><i class="bi bi-exclamation-diamond"></i>Yes</a>
+        </div>
+      </div>
+    </div>
+  </div>
+
+{% endblock %}
\ No newline at end of file
diff --git a/src/webui/service/templates/device/home.html b/src/webui/service/templates/device/home.html
new file mode 100644
index 0000000000000000000000000000000000000000..2c108add96df7de413f5310d4bd9e3c3fb69a6ed
--- /dev/null
+++ b/src/webui/service/templates/device/home.html
@@ -0,0 +1,109 @@
+<!--
+ Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+{% extends 'base.html' %}
+
+{% block content %}
+    <h1>Devices</h1>
+
+    <div class="row">
+        <div class="col">
+            <a href="{{ url_for('device.add') }}" class="btn btn-primary" style="margin-bottom: 10px;">
+                <i class="bi bi-plus"></i>
+                Add New Device
+            </a>
+        </div>
+        <div class="col">
+            {{ devices | length }} devices found in context <i>{{ session['context_uuid'] }}</i>
+        </div>
+        <!-- <div class="col">
+            <form>
+                <div class="input-group">
+                    <input type="text" aria-label="Search" placeholder="Search..." class="form-control"/>
+                    <button type="submit" class="btn btn-primary">Search</button>
+                  </div>
+            </form>
+        </div> -->
+    </div>
+
+    <table class="table table-striped table-hover">
+        <thead>
+          <tr>
+            <th scope="col">#</th>
+            <th scope="col">Type</th>
+            <th scope="col">Endpoints</th>
+            <th scope="col">Drivers</th>
+            <th scope="col">Status</th>
+            <!-- <th scope="col">Configuration</th> -->
+            <th scope="col"></th>
+          </tr>
+        </thead>
+        <tbody>
+            {% if devices %}
+                {% for device in devices %}
+                <tr>
+                    <td>
+                        <!-- <a href="{{ url_for('device.detail', device_uuid=device.device_id.device_uuid.uuid) }}"> -->
+                            {{ device.device_id.device_uuid.uuid }}
+                        <!-- </a> -->
+                    </td>
+                    <td>
+                        {{ device.device_type }}
+                    </td>
+                    <td>
+                        <ul>
+                            {% for end_point in device.device_endpoints %}
+                            <li>{{ end_point.endpoint_id.endpoint_uuid.uuid }}</li>
+                            {% endfor %}
+                        </ul>
+                    </td>
+                    <td>
+                        <ul>
+                            {% for driver in device.device_drivers %}
+                            <li>{{ dde.Name(driver).replace('DEVICEDRIVER_', '').replace('UNDEFINED', 'EMULATED') }}</li>
+                            {% endfor %}
+                        </ul>
+                    </td>
+                    <td>{{ dose.Name(device.device_operational_status).replace('DEVICEOPERATIONALSTATUS_', '') }}</td>
+                    <!-- <td>
+                        <ul>
+                            {% for config in device.device_config.config_rules %}
+                            <li>
+                                Key: {{ config.resource_key }}<br/>
+                                Value: {{ config.resource_value }}
+                            </li>
+                            {% endfor %}
+                        </ul>
+                    </td> -->
+                    <td>
+                        <a href="{{ url_for('device.detail', device_uuid=device.device_id.device_uuid.uuid) }}">
+                            <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-eye" viewBox="0 0 16 16">
+                                <path d="M16 8s-3-5.5-8-5.5S0 8 0 8s3 5.5 8 5.5S16 8 16 8zM1.173 8a13.133 13.133 0 0 1 1.66-2.043C4.12 4.668 5.88 3.5 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.133 13.133 0 0 1 14.828 8c-.058.087-.122.183-.195.288-.335.48-.83 1.12-1.465 1.755C11.879 11.332 10.119 12.5 8 12.5c-2.12 0-3.879-1.168-5.168-2.457A13.134 13.134 0 0 1 1.172 8z"/>
+                                <path d="M8 5.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5zM4.5 8a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0z"/>
+                            </svg>
+                        </a>
+                    </td>
+                </tr>
+                {% endfor %}
+            {% else %}
+                <tr>
+                    <td colspan="7">No devices found</td>
+                </tr>
+            {% endif %}
+        </tbody>
+    </table>
+
+{% endblock %}
\ No newline at end of file
diff --git a/src/webui/service/templates/link/home.html b/src/webui/service/templates/link/home.html
new file mode 100644
index 0000000000000000000000000000000000000000..d0c122f6aafd0de8e2937be056d1c2e787c91710
--- /dev/null
+++ b/src/webui/service/templates/link/home.html
@@ -0,0 +1,96 @@
+<!--
+ Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+{% extends 'base.html' %}
+
+{% block content %}
+    <h1>Links</h1>
+
+    <div class="row">
+        <div class="col">
+            <!-- <a href="#" class="btn btn-primary" style="margin-bottom: 10px;">
+                <i class="bi bi-plus"></i>
+                Add New Link
+            </a> -->
+        </div>
+        <div class="col">
+            {{ links | length }} links found</i>
+        </div>
+        <!-- <div class="col">
+            <form>
+                <div class="input-group">
+                    <input type="text" aria-label="Search" placeholder="Search..." class="form-control"/>
+                    <button type="submit" class="btn btn-primary">Search</button>
+                  </div>
+            </form>
+        </div> -->
+    </div>
+
+    <table class="table table-striped table-hover">
+        <thead>
+          <tr>
+            <th scope="col">#</th>
+            <th scope="col">Endpoints</th>
+            <th scope="col"></th>
+          </tr>
+        </thead>
+        <tbody>
+            {% if links %}
+                {% for link in links %}
+                <tr>
+                    <td>
+                        <!-- <a href="#"> -->
+                            {{ link.link_id.link_uuid.uuid }}
+                        <!-- </a> -->
+                    </td>
+
+                    <td>
+                        <ul>
+                            {% for end_point in link.link_endpoint_ids %}
+                            <li>
+                                {{ end_point.endpoint_uuid.uuid }} / 
+                                Device: 
+                                <a href="{{ url_for('device.detail', device_uuid=end_point.device_id.device_uuid.uuid) }}">
+                                    {{ end_point.device_id.device_uuid.uuid }}
+                                    <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-eye" viewBox="0 0 16 16">
+                                        <path d="M16 8s-3-5.5-8-5.5S0 8 0 8s3 5.5 8 5.5S16 8 16 8zM1.173 8a13.133 13.133 0 0 1 1.66-2.043C4.12 4.668 5.88 3.5 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.133 13.133 0 0 1 14.828 8c-.058.087-.122.183-.195.288-.335.48-.83 1.12-1.465 1.755C11.879 11.332 10.119 12.5 8 12.5c-2.12 0-3.879-1.168-5.168-2.457A13.134 13.134 0 0 1 1.172 8z"/>
+                                        <path d="M8 5.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5zM4.5 8a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0z"/>
+                                    </svg>
+                                </a>
+                            </li>
+                            {% endfor %}
+                        </ul>
+                    </td>
+
+                    <td>
+                        <!-- <a href="#">
+                            <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-eye" viewBox="0 0 16 16">
+                                <path d="M16 8s-3-5.5-8-5.5S0 8 0 8s3 5.5 8 5.5S16 8 16 8zM1.173 8a13.133 13.133 0 0 1 1.66-2.043C4.12 4.668 5.88 3.5 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.133 13.133 0 0 1 14.828 8c-.058.087-.122.183-.195.288-.335.48-.83 1.12-1.465 1.755C11.879 11.332 10.119 12.5 8 12.5c-2.12 0-3.879-1.168-5.168-2.457A13.134 13.134 0 0 1 1.172 8z"/>
+                                <path d="M8 5.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5zM4.5 8a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0z"/>
+                            </svg>
+                        </a> -->
+                    </td>
+                </tr>
+                {% endfor %}
+            {% else %}
+                <tr>
+                    <td colspan="7">No links found</td>
+                </tr>
+            {% endif %}
+        </tbody>
+    </table>
+
+{% endblock %}
\ No newline at end of file
diff --git a/src/webui/service/templates/main/about.html b/src/webui/service/templates/main/about.html
new file mode 100644
index 0000000000000000000000000000000000000000..4ba3a5845b0e8e70b029d4ec459733468899698b
--- /dev/null
+++ b/src/webui/service/templates/main/about.html
@@ -0,0 +1,25 @@
+<!--
+ Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+{% extends 'base.html' %}
+{% block content %}
+    <h1>TeraFlow OS</h1>
+
+    <p>For more information, visit the <a href="https://teraflow-h2020.eu/" target="_newtf">TeraFlow H2020 webpage</a>.</p>
+
+    <img alt="Consortium" class="img-fluid" src="{{ url_for('static', filename='partners.png') }}"/>
+
+{% endblock %}
\ No newline at end of file
diff --git a/src/webui/service/templates/main/home.html b/src/webui/service/templates/main/home.html
new file mode 100644
index 0000000000000000000000000000000000000000..2134a3a87abde0d8c6af69dbc136819a4fbbf1c1
--- /dev/null
+++ b/src/webui/service/templates/main/home.html
@@ -0,0 +1,84 @@
+<!--
+ Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+{% extends 'base.html' %}
+
+{% block content %}
+    <h1>TeraFlow OS SDN Controller</h1>
+
+    {% for field, message in context_form.errors.items() %}
+        <div class="alert alert-dismissible fade show" role="alert">
+        <b>{{ field }}</b>: {{ message }}
+        <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
+        </div>
+
+    {% endfor %}
+
+    <form id="select_context" method="POST" enctype="multipart/form-data">
+        {{ context_form.hidden_tag() }}
+        <fieldset class="form-group">
+            <legend>Select the working context, or upload a JSON descriptors file</legend>
+            <div class="row mb-3">
+                {{ context_form.context.label(class="col-sm-1 col-form-label") }}
+                <div class="col-sm-5">
+                    {% if context_form.context.errors %}
+                        {{ context_form.context(class="form-select is-invalid") }}
+                        <div class="invalid-feedback">
+                            {% for error in context_form.context.errors %}
+                                <span>{{ error }}</span>
+                            {% endfor %}
+                        </div>
+                    {% else %}
+                        {{ context_form.context(class="form-select") }}
+                    {% endif %}
+                </div>
+                <div class="col-sm-2">
+                    {{ context_form.submit(class='btn btn-primary') }}
+                </div>
+            </div>
+        </fieldset>
+    </form>
+
+    <form id="select_context" method="POST" enctype="multipart/form-data">
+        {{ context_form.hidden_tag() }}
+        <fieldset class="form-group">
+            <legend>Upload a JSON descriptors file</legend>
+            <div class="row mb-3">
+                {{ descriptor_form.descriptors.label(class="col-sm-1 col-form-label") }}
+                <div class="col-sm-5">
+                    {% if descriptor_form.descriptors.errors %}
+                        {{ descriptor_form.descriptors(class="form-control is-invalid") }}
+                        <div class="invalid-feedback">
+                            {% for error in descriptor_form.descriptors.errors %}
+                                <span>{{ error }}</span>
+                            {% endfor %}
+                        </div>
+                    {% else %}
+                        {{ descriptor_form.descriptors(class="form-control") }}
+                    {% endif %}
+                </div>
+                <div class="col-sm-2">
+                    {{ descriptor_form.submit(class='btn btn-primary') }}
+                </div>
+            </div>
+        </fieldset>
+    </form>
+
+    <script src="https://d3js.org/d3.v4.min.js"></script>
+    <div id="topology"></div>
+    <script src="{{ url_for('static', filename='topology.js') }}"></script>
+
+{% endblock %}
diff --git a/src/webui/service/templates/service/detail.html b/src/webui/service/templates/service/detail.html
new file mode 100644
index 0000000000000000000000000000000000000000..77988c74c1f004fe872961ac50aabe9cede8dc65
--- /dev/null
+++ b/src/webui/service/templates/service/detail.html
@@ -0,0 +1,99 @@
+<!--
+ Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+{% extends 'base.html' %}
+
+{% block content %}
+    <h1>Service {{ service.service_id.service_uuid.uuid }}</h1>
+
+    <div class="row mb-3">
+        <div class="col-sm-3">
+            <button type="button" class="btn btn-success" onclick="window.location.href = '/service/'">
+                <i class="bi bi-box-arrow-in-left"></i>
+                Back to service list
+            </button>
+        </div>
+        <div class="col-sm-3">
+            <a id="update" class="btn btn-secondary" href="#">
+                <i class="bi bi-pencil-square"></i>
+                Update
+            </a>
+        </div>
+        <div class="col-sm-3">
+            <!-- <button type="button" class="btn btn-danger"><i class="bi bi-x-square"></i>Delete service</button> -->
+            <button type="button" class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#deleteModal">
+                <i class="bi bi-x-square"></i>Delete service
+              </button>
+        </div>
+    </div>
+
+    <div class="row mb-3">
+        <div class="col-sm-1"><b>UUID:</b></div>
+        <div class="col-sm-5">
+            {{ service.service_id.service_uuid.uuid }}
+        </div>
+        <div class="col-sm-1"><b>Type:</b></div>
+        <div class="col-sm-5">
+            {{ service.service_type }}
+        </div>
+    </div>
+    <div class="row mb-3">
+        <b>Endpoints:</b>
+        <div class="col-sm-10">
+            <ul>
+            {% for endpoint in service.service_endpoint_ids %}
+                <li>{{ endpoint.endpoint_uuid.uuid }}: {{ endpoint.endpoint_type }}</li>
+            {% endfor %}
+            </ul>
+        </div>
+    </div>
+    <div class="row mb-3">
+        <b>Configurations:</b>
+        <div class="col-sm-10">
+            <ul>
+            {% for config in service.service_config.config_rules %}
+                <li>{{ config.resource_key }}:
+                    <ul>
+                        {% for key, value in (config.resource_value | from_json).items() %}
+                        <li><b>{{ key }}:</b> {{ value }}</li>
+                        {% endfor %}
+                    </ul>
+                </li>
+            {% endfor %}
+            </ul>
+        </div>
+    </div>
+
+    <!-- Modal -->
+<div class="modal fade" id="deleteModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
+    <div class="modal-dialog">
+      <div class="modal-content">
+        <div class="modal-header">
+          <h5 class="modal-title" id="staticBackdropLabel">Delete service?</h5>
+          <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
+        </div>
+        <div class="modal-body">
+          Are you sure you want to delete the service "{{ service.service_id.service_uuid.uuid }}"?
+        </div>
+        <div class="modal-footer">
+          <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">No</button>
+          <a type="button" class="btn btn-danger" href="{{ url_for('service.delete', service_uuid=service.service_id.service_uuid.uuid) }}"><i class="bi bi-exclamation-diamond"></i>Yes</a>
+        </div>
+      </div>
+    </div>
+  </div>
+
+{% endblock %}
\ No newline at end of file
diff --git a/src/webui/service/templates/service/home.html b/src/webui/service/templates/service/home.html
new file mode 100644
index 0000000000000000000000000000000000000000..0e152006c149df35d477ecfb81bb4fcc0b562d9a
--- /dev/null
+++ b/src/webui/service/templates/service/home.html
@@ -0,0 +1,99 @@
+<!--
+ Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+{% extends 'base.html' %}
+
+{% block content %}
+    <h1>Services</h1>
+
+    <div class="row">
+        <!-- <div class="col">
+            <a href="{{ url_for('service.add') }}" class="btn btn-primary" style="margin-bottom: 10px;">
+                <i class="bi bi-plus"></i>
+                Add New Service
+            </a>
+        </div> -->
+        <div class="col">
+            {{ services | length }} services found in context <i>{{ session['context_uuid'] }}</i>
+        </div>
+        <!-- <div class="col">
+            <form>
+                <div class="input-group">
+                    <input type="text" aria-label="Search" placeholder="Search..." class="form-control"/>
+                    <button type="submit" class="btn btn-primary">Search</button>
+                  </div>
+            </form>
+        </div> -->
+    </div>
+    
+
+    <table class="table table-striped table-hover">
+        <thead>
+          <tr>
+            <th scope="col">#</th>
+            <th scope="col">Type</th>
+            <th scope="col">End points</th>
+            <th scope="col">Constraints</th>
+            <th scope="col">Status</th>
+            <th scope="col"></th>
+          </tr>
+        </thead>
+        <tbody>
+            {% if services %}
+                {% for service in services %}
+                <tr>
+                    <td>
+                        <!-- <a href="{{ url_for('service.detail', service_uuid=service.service_id.service_uuid.uuid) }}"> -->
+                            {{ service.service_id.service_uuid.uuid }}
+                        <!-- </a> -->
+                    </td>
+                    <td>
+                        {{ ste.Name(service.service_type).replace('SERVICETYPE_', '') }}
+                    </td>
+                    <td>
+                        <ul>
+                            {% for end_point in service.service_endpoint_ids %}
+                            <li>{{ end_point.device_id.device_uuid.uuid }} / {{ end_point.endpoint_uuid.uuid }}</li>
+                            {% endfor %}
+                        </ul>
+                    </td>
+                    <td>
+                        <ul>
+                            {% for constraint in service.service_constraints %}
+                            <li>{{ constraint.constraint_type }}: {{ constraint.constraint_value }}</li>
+                            {% endfor %}
+                        </ul>
+                    </td>
+                    <td>{{ sse.Name(service.service_status.service_status).replace('SERVICESTATUS_', '') }}</td>
+                    <td>
+                        <a href="{{ url_for('service.detail', service_uuid=service.service_id.service_uuid.uuid) }}">
+                            <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-eye" viewBox="0 0 16 16">
+                                <path d="M16 8s-3-5.5-8-5.5S0 8 0 8s3 5.5 8 5.5S16 8 16 8zM1.173 8a13.133 13.133 0 0 1 1.66-2.043C4.12 4.668 5.88 3.5 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.133 13.133 0 0 1 14.828 8c-.058.087-.122.183-.195.288-.335.48-.83 1.12-1.465 1.755C11.879 11.332 10.119 12.5 8 12.5c-2.12 0-3.879-1.168-5.168-2.457A13.134 13.134 0 0 1 1.172 8z"/>
+                                <path d="M8 5.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5zM4.5 8a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0z"/>
+                            </svg>
+                        </a>
+                    </td>
+                </tr>
+                {% endfor %}
+            {% else %}
+                <tr>
+                    <td colspan="7">No services found</td>
+                </tr>
+            {% endif %}
+        </tbody>
+    </table>
+
+{% endblock %}
\ No newline at end of file
diff --git a/src/webui/tests/__init__.py b/src/webui/tests/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..70a33251242c51f49140e596b8208a19dd5245f7
--- /dev/null
+++ b/src/webui/tests/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/webui/tests/test_unitary.py b/src/webui/tests/test_unitary.py
new file mode 100644
index 0000000000000000000000000000000000000000..08c996446c67f76d65105bde3263d46b28d37048
--- /dev/null
+++ b/src/webui/tests/test_unitary.py
@@ -0,0 +1,171 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# import pytest
+from flask_unittest import ClientTestCase
+from unittest import mock
+from flask.testing import FlaskClient
+from flask.app import Flask
+from flask.helpers import url_for
+from common.DeviceTypes import DeviceTypeEnum
+# from device.client.DeviceClient import DeviceClient
+from webui.service import create_app
+from webui.proto.context_pb2 import Empty, DeviceId, DeviceList, TopologyIdList
+
+class TestWebUI(ClientTestCase):
+    app = create_app(use_config={'TESTING': True, 
+                                 'SERVER_NAME': 'localhost.localdomain',
+                                 'SECRET_KEY': '>s&}24@{]]#k3&^5$f3#?6?h3{W@[}/7z}2pa]>{3&5%RP<)[(',
+                                 'WTF_CSRF_ENABLED': False})
+    
+    def setUp(self, client: FlaskClient) -> None:
+
+        self.mocker_delete_device = mock.patch('webui.service.device.routes.device_client.DeleteDevice')
+        self.mocker_delete_device.return_value = Empty()
+        self.mocker_delete_device.start()
+        self.addCleanup(self.mocker_delete_device.stop)
+
+        self.mocker_list_devices = mock.patch('webui.service.device.routes.context_client.ListDevices')
+        self.mocker_list_devices.return_value = DeviceList()  # returns an empty list
+        self.mocker_list_devices.start()
+        self.addCleanup(self.mocker_list_devices.stop)
+
+        self.mocker_add_device = mock.patch('webui.service.device.routes.device_client.AddDevice')
+        self.mocker_add_device.return_value = DeviceId()
+        self.mocker_add_device.start()
+        self.addCleanup(self.mocker_add_device.stop)
+
+        self.mocker_list_topology_ids = mock.patch('webui.service.device.routes.context_client.ListTopologyIds')
+        self.mocker_list_topology_ids.return_value = TopologyIdList()
+        self.mocker_list_topology_ids.start()
+        self.addCleanup(self.mocker_list_topology_ids.stop)
+
+        return super().setUp(client)
+    
+    def tearDown(self, client: FlaskClient) -> None:
+        mock.patch.stopall()
+        return super().tearDown(client)
+    
+    def test_routes(self, client):
+        with self.app.app_context():
+            url_for('main.home')
+            url_for('service.home')
+            url_for('device.home')
+            url_for('main.about')
+    
+    def test_device_add_action_success(self, client):
+        with client.session_transaction() as sess:
+            sess['context_uuid'] = 'admin'
+        DEVICE_EMU = {
+            'device_id': 'EMULATED',
+            'device_type': DeviceTypeEnum.EMULATED_PACKET_ROUTER.value,
+            'device_config': '',
+            'operational_status': 1,
+            'device_drivers': 0,
+            'device_endpoints': [],
+        }
+        rv = client.post('/device/add', data=DEVICE_EMU, follow_redirects=True)
+        self.assertInResponse(b'success', rv)
+    
+    def test_device_delete_action(self, client):
+        with client.session_transaction() as sess:
+            sess['context_uuid'] = 'admin'
+
+        rv = client.get('/device/EMULATED/delete', follow_redirects=True)
+        # mocked_list.assert_called()
+        # mocked_delete.assert_called()
+        self.assertInResponse(b'success', rv)
+    
+    def test_service_up(self, client):
+        pass
+
+
+
+# def test_service_up(client):
+#     rw = client.get('/')
+#     assert rw.status_code == 200, 'Service is not up!'
+
+# def test_home_page(client):
+#     rw = client.get('/')
+#     assert rw.status_code == 200, 'Error in the home page!'
+#     assert b'Select the working context' in rw.data
+
+# def test_service_home_page(client):
+#     with client.session_transaction() as sess:
+#         sess['context_uuid'] = 'admin'
+#     rw = client.get('/service/')
+#     assert rw.status_code == 200
+#     assert b'Services' in rw.data
+#     assert b'Add New Service' in rw.data
+
+# def test_device_home_page(client):
+#     with client.session_transaction() as sess:
+#         sess['context_uuid'] = 'admin'
+#     rw = client.get('/device/')
+#     assert rw.status_code == 200
+#     assert b'Devices' in rw.data
+#     assert b'Add New Device' in rw.data
+
+# @pytest.mark.parametrize('device_id', (
+#     'DEV1',
+#     'DEV2',
+#     'DEV3',
+# ))
+# def test_device_detail_page(client, device_id):
+#     with client.session_transaction() as sess:
+#         sess['context_uuid'] = 'admin'
+#     rw = client.get(f'/device/detail/{device_id}')
+#     assert rw.status_code == 200
+#     assert b'Device' in rw.data
+#     assert device_id in rw.data.decode()
+#     assert b'Endpoints' in rw.data, 'Missing endpoint information on the device detail page.'
+#     # assert b'Add New Device' in rw.data
+
+# def test_device_add_page(client):
+#     with client.session_transaction() as sess:
+#         sess['context_uuid'] = 'admin'
+#     rw = client.get('/device/add')
+#     assert rw.status_code == 200
+#     assert b'Add New Device' in rw.data
+#     assert b'Operational Status' in rw.data, 'Form is not correctly implemented.'
+#     assert b'Type' in rw.data, 'Form is not correctly implemented.'
+#     assert b'Configurations' in rw.data, 'Form is not correctly implemented.'
+#     assert b'Drivers' in rw.data, 'Form is not correctly implemented.'
+
+# def test_device_add_action(client):
+#     with client.session_transaction() as sess:
+#         sess['context_uuid'] = 'admin'
+#     DEVICE_EMU = {
+#         'device_id': 'EMULATED',
+#         'device_type': DeviceTypeEnum.EMULATED_PACKET_ROUTER.value,
+#         'device_config': '',
+#         'operational_status': 1,
+#         'device_drivers': 0,
+#         'device_endpoints': [],
+#     }
+#     with mock.patch('webui.service.device.routes.device_client.AddDevice') as mocked_add:
+#         mocked_add.return_value = DeviceId()
+#         rw = client.post('/device/add', data=DEVICE_EMU, follow_redirects=True)
+#     assert b'success' in rw.data
+
+# def test_device_delete_action(client):
+#     with client.session_transaction() as sess:
+#         sess['context_uuid'] = 'admin'
+#     with mock.patch('webui.service.device.routes.device_client.DeleteDevice') as mocked_delete,\
+#          mock.patch('webui.service.device.routes.context_client.ListDevices') as mocked_list:
+#         mocked_list.return_value = DeviceList()  # returns an empty list
+#         rw = client.get('/device/EMULATED/delete', follow_redirects=True)
+#         mocked_list.assert_called()
+#         mocked_delete.assert_called()
+#     assert b'success' in rw.data
diff --git a/src/webui/utils/__init__.py b/src/webui/utils/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..70a33251242c51f49140e596b8208a19dd5245f7
--- /dev/null
+++ b/src/webui/utils/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/webui/utils/form_validators.py b/src/webui/utils/form_validators.py
new file mode 100644
index 0000000000000000000000000000000000000000..59c5cf998847be5a5a81419264fbc09afd30f595
--- /dev/null
+++ b/src/webui/utils/form_validators.py
@@ -0,0 +1,28 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from wtforms.validators import ValidationError
+
+def key_value_validator():
+    def _validate(form, field):
+        if len(field.data) > 0:
+            if '\n' not in field.data:  # case in which there is only one configuration
+                if '=' not in field.data:
+                    raise ValidationError(f'Configuration "{field.data}" does not follow the key=value pattern.')
+            else:  # case in which there are several configurations
+                configurations = field.data.split('\n')
+                for configutation in configurations:
+                    if '=' not in configutation:
+                        raise ValidationError(f'Configuration "{configutation}" does not follow the key=value pattern.')
+    return _validate