diff --git a/.dockerignore b/.dockerignore
index 2e001ed43dd844adbf8853219d73d5cb196cbc42..96e722427c2b0250783623668ef89e3d6ac7083d 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -5,13 +5,12 @@
 coverage/
 data/
 deploy/
-manifests/
-hackfest/
-scripts/
-tmp/
-
 ecoc22/
+hackfest/
+manifests/
 nfvsdn22/
 oeccpsc22/
 ofc22/
 ofc23/
+scripts/
+tmp/
diff --git a/deploy/tfs.sh b/deploy/tfs.sh
index e6a0c0c1053b69462a0e60c6b6cebe28a7dc59af..1ecb039e3fdeb88ca04737e56df5f94e588fed0b 100755
--- a/deploy/tfs.sh
+++ b/deploy/tfs.sh
@@ -176,13 +176,14 @@ echo "# Environment variables for TeraFlowSDN deployment" > $ENV_VARS_SCRIPT
 PYTHONPATH=$(pwd)/src
 echo "export PYTHONPATH=${PYTHONPATH}" >> $ENV_VARS_SCRIPT
 
-echo "Create Redis secret..."
-# first try to delete an old one if exists
-kubectl delete secret redis-secrets --namespace=$TFS_K8S_NAMESPACE --ignore-not-found
-REDIS_PASSWORD=`uuidgen`
-kubectl create secret generic redis-secrets --namespace=$TFS_K8S_NAMESPACE \
-    --from-literal=REDIS_PASSWORD=$REDIS_PASSWORD
-echo "export REDIS_PASSWORD=${REDIS_PASSWORD}" >> $ENV_VARS_SCRIPT
+# Not needed for the Hackfest
+#echo "Create Redis secret..."
+## first try to delete an old one if exists
+#kubectl delete secret redis-secrets --namespace=$TFS_K8S_NAMESPACE --ignore-not-found
+#REDIS_PASSWORD=`uuidgen`
+#kubectl create secret generic redis-secrets --namespace=$TFS_K8S_NAMESPACE \
+#    --from-literal=REDIS_PASSWORD=$REDIS_PASSWORD
+#echo "export REDIS_PASSWORD=${REDIS_PASSWORD}" >> $ENV_VARS_SCRIPT
 
 for COMPONENT in $TFS_COMPONENTS; do
     echo "Processing '$COMPONENT' component..."
@@ -259,8 +260,9 @@ for COMPONENT in $TFS_COMPONENTS; do
 
     echo "  Adapting '$COMPONENT' manifest file..."
     MANIFEST="$TMP_MANIFESTS_FOLDER/${COMPONENT}service.yaml"
-    # cp ./manifests/"${COMPONENT}"service.yaml "$MANIFEST"
-    cat ./manifests/"${COMPONENT}"service.yaml | linkerd inject - --proxy-cpu-request "10m" --proxy-cpu-limit "1" --proxy-memory-request "64Mi" --proxy-memory-limit "256Mi" > "$MANIFEST"
+    # Deactivated linkerd for the Hackfest
+    cp ./manifests/"${COMPONENT}"service.yaml "$MANIFEST"
+    #cat ./manifests/"${COMPONENT}"service.yaml | linkerd inject - --proxy-cpu-request "10m" --proxy-cpu-limit "1" --proxy-memory-request "64Mi" --proxy-memory-limit "256Mi" > "$MANIFEST"
 
     if [ "$COMPONENT" == "pathcomp" ]; then
         IMAGE_URL=$(echo "$TFS_REGISTRY_IMAGES/$COMPONENT-frontend:$TFS_IMAGE_TAG" | sed 's,//,/,g' | sed 's,http:/,,g')
@@ -375,10 +377,11 @@ if [[ "$TFS_COMPONENTS" == *"webui"* ]]; then
     GRAFANA_URL_UPDATED="http://${GRAFANA_USERNAME}:${TFS_GRAFANA_PASSWORD}@${GRAFANA_URL}"
     echo "export GRAFANA_URL_UPDATED=${GRAFANA_URL_UPDATED}" >> $ENV_VARS_SCRIPT
 
-    echo ">> Installing Scatter Plot plugin..."
-    curl -X POST -H "Content-Type: application/json" -H "Content-Length: 0" \
-        ${GRAFANA_URL_UPDATED}/api/plugins/michaeldmoore-scatter-panel/install
-    echo
+    # Not needed for the Hackfest
+    #echo ">> Installing Scatter Plot plugin..."
+    #curl -X POST -H "Content-Type: application/json" -H "Content-Length: 0" \
+    #    ${GRAFANA_URL_UPDATED}/api/plugins/michaeldmoore-scatter-panel/install
+    #echo
 
     # Ref: https://grafana.com/docs/grafana/latest/http_api/data_source/
     QDB_HOST_PORT="${METRICSDB_HOSTNAME}:${QDB_SQL_PORT}"
@@ -408,68 +411,71 @@ if [[ "$TFS_COMPONENTS" == *"webui"* ]]; then
     }' ${GRAFANA_URL_UPDATED}/api/datasources
     echo
 
-    curl -X POST -H "Content-Type: application/json" -H "Accept: application/json" -d '{
-        "access"   : "proxy",
-        "type"     : "postgres",
-        "name"     : "questdb-slc-grp",
-        "url"      : "'${QDB_HOST_PORT}'",
-        "database" : "'${QDB_TABLE_SLICE_GROUPS}'",
-        "user"     : "'${QDB_USERNAME}'",
-        "basicAuth": false,
-        "isDefault": false,
-        "jsonData" : {
-            "sslmode"               : "disable",
-            "postgresVersion"       : 1100,
-            "maxOpenConns"          : 0,
-            "maxIdleConns"          : 2,
-            "connMaxLifetime"       : 14400,
-            "tlsAuth"               : false,
-            "tlsAuthWithCACert"     : false,
-            "timescaledb"           : false,
-            "tlsConfigurationMethod": "file-path",
-            "tlsSkipVerify"         : true
-        },
-        "secureJsonData": {"password": "'${QDB_PASSWORD}'"}
-    }' ${GRAFANA_URL_UPDATED}/api/datasources
-    echo
-
-    curl -X POST -H "Content-Type: application/json" -H "Accept: application/json" -d '{
-        "access"   : "proxy",
-        "type"     : "postgres",
-        "name"     : "cockroachdb",
-        "url"      : "'cockroachdb-public.${CRDB_NAMESPACE}.svc.cluster.local:${CRDB_SQL_PORT}'",
-        "database" : "'${CRDB_DATABASE}'",
-        "user"     : "'${CRDB_USERNAME}'",
-        "basicAuth": false,
-        "isDefault": false,
-        "jsonData" : {
-            "sslmode"               : "require",
-            "postgresVersion"       : 1100,
-            "maxOpenConns"          : 0,
-            "maxIdleConns"          : 2,
-            "connMaxLifetime"       : 14400,
-            "tlsAuth"               : false,
-            "tlsAuthWithCACert"     : false,
-            "timescaledb"           : false,
-            "tlsConfigurationMethod": "file-path",
-            "tlsSkipVerify"         : true
-        },
-        "secureJsonData": {"password": "'${CRDB_PASSWORD}'"}
-    }' ${GRAFANA_URL_UPDATED}/api/datasources
-    echo
-
-    # adding the datasource of the metrics collection framework
-    curl -X POST -H "Content-Type: application/json" -H "Accept: application/json" -d '{
-        "access"   : "proxy",
-        "type"     : "prometheus",
-        "name"     : "prometheus",
-        "url"      : "http://prometheus-k8s.monitoring.svc:9090",
-        "basicAuth": false,
-        "isDefault": false,
-        "jsonData" : {
-            "httpMethod"               : "POST"
-        }
-    }' ${GRAFANA_URL_UPDATED}/api/datasources
+    # Not needed for the Hackfest
+    #curl -X POST -H "Content-Type: application/json" -H "Accept: application/json" -d '{
+    #    "access"   : "proxy",
+    #    "type"     : "postgres",
+    #    "name"     : "questdb-slc-grp",
+    #    "url"      : "'${QDB_HOST_PORT}'",
+    #    "database" : "'${QDB_TABLE_SLICE_GROUPS}'",
+    #    "user"     : "'${QDB_USERNAME}'",
+    #    "basicAuth": false,
+    #    "isDefault": false,
+    #    "jsonData" : {
+    #        "sslmode"               : "disable",
+    #        "postgresVersion"       : 1100,
+    #        "maxOpenConns"          : 0,
+    #        "maxIdleConns"          : 2,
+    #        "connMaxLifetime"       : 14400,
+    #        "tlsAuth"               : false,
+    #        "tlsAuthWithCACert"     : false,
+    #        "timescaledb"           : false,
+    #        "tlsConfigurationMethod": "file-path",
+    #        "tlsSkipVerify"         : true
+    #    },
+    #    "secureJsonData": {"password": "'${QDB_PASSWORD}'"}
+    #}' ${GRAFANA_URL_UPDATED}/api/datasources
+    #echo
+
+    # Not needed for the Hackfest
+    #curl -X POST -H "Content-Type: application/json" -H "Accept: application/json" -d '{
+    #    "access"   : "proxy",
+    #    "type"     : "postgres",
+    #    "name"     : "cockroachdb",
+    #    "url"      : "'cockroachdb-public.${CRDB_NAMESPACE}.svc.cluster.local:${CRDB_SQL_PORT}'",
+    #    "database" : "'${CRDB_DATABASE}'",
+    #    "user"     : "'${CRDB_USERNAME}'",
+    #    "basicAuth": false,
+    #    "isDefault": false,
+    #    "jsonData" : {
+    #        "sslmode"               : "require",
+    #        "postgresVersion"       : 1100,
+    #        "maxOpenConns"          : 0,
+    #        "maxIdleConns"          : 2,
+    #        "connMaxLifetime"       : 14400,
+    #        "tlsAuth"               : false,
+    #        "tlsAuthWithCACert"     : false,
+    #        "timescaledb"           : false,
+    #        "tlsConfigurationMethod": "file-path",
+    #        "tlsSkipVerify"         : true
+    #    },
+    #    "secureJsonData": {"password": "'${CRDB_PASSWORD}'"}
+    #}' ${GRAFANA_URL_UPDATED}/api/datasources
+    #echo
+
+    # Not needed for the Hackfest
+    ## adding the datasource of the metrics collection framework
+    #curl -X POST -H "Content-Type: application/json" -H "Accept: application/json" -d '{
+    #    "access"   : "proxy",
+    #    "type"     : "prometheus",
+    #    "name"     : "prometheus",
+    #    "url"      : "http://prometheus-k8s.monitoring.svc:9090",
+    #    "basicAuth": false,
+    #    "isDefault": false,
+    #    "jsonData" : {
+    #        "httpMethod"               : "POST"
+    #    }
+    #}' ${GRAFANA_URL_UPDATED}/api/datasources
     printf "\n\n"
 
     echo ">> Creating and staring dashboards..."
@@ -484,68 +490,75 @@ if [[ "$TFS_COMPONENTS" == *"webui"* ]]; then
     curl -X POST ${GRAFANA_URL_UPDATED}/api/user/stars/dashboard/${DASHBOARD_ID}
     echo
 
-    # Dashboard: Slice Grouping
-    curl -X POST -H "Content-Type: application/json" -d '@src/webui/grafana_db_slc_grps_psql.json' \
-        ${GRAFANA_URL_UPDATED}/api/dashboards/db
-    echo
-    DASHBOARD_URL="${GRAFANA_URL_UPDATED}/api/dashboards/uid/tfs-slice-grps"
-    DASHBOARD_ID=$(curl -s "${DASHBOARD_URL}" | jq '.dashboard.id')
-    curl -X POST ${GRAFANA_URL_UPDATED}/api/user/stars/dashboard/${DASHBOARD_ID}
-    echo
-
-    # Dashboard: Component RPCs
-    curl -X POST -H "Content-Type: application/json" -d '@src/webui/grafana_prom_component_rpc.json' \
-        ${GRAFANA_URL_UPDATED}/api/dashboards/db
-    echo
-    DASHBOARD_URL="${GRAFANA_URL_UPDATED}/api/dashboards/uid/tfs-comp-rpc"
-    DASHBOARD_ID=$(curl -s "${DASHBOARD_URL}" | jq '.dashboard.id')
-    curl -X POST ${GRAFANA_URL_UPDATED}/api/user/stars/dashboard/${DASHBOARD_ID}
-    echo
-
-    # Dashboard: Device Drivers
-    curl -X POST -H "Content-Type: application/json" -d '@src/webui/grafana_prom_device_driver.json' \
-        ${GRAFANA_URL_UPDATED}/api/dashboards/db
-    echo
-    DASHBOARD_URL="${GRAFANA_URL_UPDATED}/api/dashboards/uid/tfs-dev-drv"
-    DASHBOARD_ID=$(curl -s "${DASHBOARD_URL}" | jq '.dashboard.id')
-    curl -X POST ${GRAFANA_URL_UPDATED}/api/user/stars/dashboard/${DASHBOARD_ID}
-    echo
-
-    # Dashboard: Service Handlers
-    curl -X POST -H "Content-Type: application/json" -d '@src/webui/grafana_prom_service_handler.json' \
-        ${GRAFANA_URL_UPDATED}/api/dashboards/db
-    echo
-    DASHBOARD_URL="${GRAFANA_URL_UPDATED}/api/dashboards/uid/tfs-svc-hdlr"
-    DASHBOARD_ID=$(curl -s "${DASHBOARD_URL}" | jq '.dashboard.id')
-    curl -X POST ${GRAFANA_URL_UPDATED}/api/user/stars/dashboard/${DASHBOARD_ID}
-    echo
-
-    # Dashboard: Device Execution Details
-    curl -X POST -H "Content-Type: application/json" -d '@src/webui/grafana_prom_device_exec_details.json' \
-        ${GRAFANA_URL_UPDATED}/api/dashboards/db
-    echo
-    DASHBOARD_URL="${GRAFANA_URL_UPDATED}/api/dashboards/uid/tfs-dev-exec"
-    DASHBOARD_ID=$(curl -s "${DASHBOARD_URL}" | jq '.dashboard.id')
-    curl -X POST ${GRAFANA_URL_UPDATED}/api/user/stars/dashboard/${DASHBOARD_ID}
-    echo
-
-    # Dashboard: Load Generator Status
-    curl -X POST -H "Content-Type: application/json" -d '@src/webui/grafana_prom_load_generator.json' \
-        ${GRAFANA_URL_UPDATED}/api/dashboards/db
-    echo
-    DASHBOARD_URL="${GRAFANA_URL_UPDATED}/api/dashboards/uid/tfs-loadgen-stats"
-    DASHBOARD_ID=$(curl -s "${DASHBOARD_URL}" | jq '.dashboard.id')
-    curl -X POST ${GRAFANA_URL_UPDATED}/api/user/stars/dashboard/${DASHBOARD_ID}
-    echo
-
-    # Dashboard: Load Generator Status
-    curl -X POST -H "Content-Type: application/json" -d '@src/webui/grafana_prom_tfs_num_pods.json' \
-        ${GRAFANA_URL_UPDATED}/api/dashboards/db
-    echo
-    DASHBOARD_URL="${GRAFANA_URL_UPDATED}/api/dashboards/uid/tfs-num-pods"
-    DASHBOARD_ID=$(curl -s "${DASHBOARD_URL}" | jq '.dashboard.id')
-    curl -X POST ${GRAFANA_URL_UPDATED}/api/user/stars/dashboard/${DASHBOARD_ID}
-    echo
+    # Not needed for the Hackfest
+    ## Dashboard: Slice Grouping
+    #curl -X POST -H "Content-Type: application/json" -d '@src/webui/grafana_db_slc_grps_psql.json' \
+    #    ${GRAFANA_URL_UPDATED}/api/dashboards/db
+    #echo
+    #DASHBOARD_URL="${GRAFANA_URL_UPDATED}/api/dashboards/uid/tfs-slice-grps"
+    #DASHBOARD_ID=$(curl -s "${DASHBOARD_URL}" | jq '.dashboard.id')
+    #curl -X POST ${GRAFANA_URL_UPDATED}/api/user/stars/dashboard/${DASHBOARD_ID}
+    #echo
+
+    # Not needed for the Hackfest
+    ## Dashboard: Component RPCs
+    #curl -X POST -H "Content-Type: application/json" -d '@src/webui/grafana_prom_component_rpc.json' \
+    #    ${GRAFANA_URL_UPDATED}/api/dashboards/db
+    #echo
+    #DASHBOARD_URL="${GRAFANA_URL_UPDATED}/api/dashboards/uid/tfs-comp-rpc"
+    #DASHBOARD_ID=$(curl -s "${DASHBOARD_URL}" | jq '.dashboard.id')
+    #curl -X POST ${GRAFANA_URL_UPDATED}/api/user/stars/dashboard/${DASHBOARD_ID}
+    #echo
+
+    # Not needed for the Hackfest
+    ## Dashboard: Device Drivers
+    #curl -X POST -H "Content-Type: application/json" -d '@src/webui/grafana_prom_device_driver.json' \
+    #    ${GRAFANA_URL_UPDATED}/api/dashboards/db
+    #echo
+    #DASHBOARD_URL="${GRAFANA_URL_UPDATED}/api/dashboards/uid/tfs-dev-drv"
+    #DASHBOARD_ID=$(curl -s "${DASHBOARD_URL}" | jq '.dashboard.id')
+    #curl -X POST ${GRAFANA_URL_UPDATED}/api/user/stars/dashboard/${DASHBOARD_ID}
+    #echo
+
+    # Not needed for the Hackfest
+    ## Dashboard: Service Handlers
+    #curl -X POST -H "Content-Type: application/json" -d '@src/webui/grafana_prom_service_handler.json' \
+    #    ${GRAFANA_URL_UPDATED}/api/dashboards/db
+    #echo
+    #DASHBOARD_URL="${GRAFANA_URL_UPDATED}/api/dashboards/uid/tfs-svc-hdlr"
+    #DASHBOARD_ID=$(curl -s "${DASHBOARD_URL}" | jq '.dashboard.id')
+    #curl -X POST ${GRAFANA_URL_UPDATED}/api/user/stars/dashboard/${DASHBOARD_ID}
+    #echo
+
+    # Not needed for the Hackfest
+    ## Dashboard: Device Execution Details
+    #curl -X POST -H "Content-Type: application/json" -d '@src/webui/grafana_prom_device_exec_details.json' \
+    #    ${GRAFANA_URL_UPDATED}/api/dashboards/db
+    #echo
+    #DASHBOARD_URL="${GRAFANA_URL_UPDATED}/api/dashboards/uid/tfs-dev-exec"
+    #DASHBOARD_ID=$(curl -s "${DASHBOARD_URL}" | jq '.dashboard.id')
+    #curl -X POST ${GRAFANA_URL_UPDATED}/api/user/stars/dashboard/${DASHBOARD_ID}
+    #echo
+
+    # Not needed for the Hackfest
+    ## Dashboard: Load Generator Status
+    #curl -X POST -H "Content-Type: application/json" -d '@src/webui/grafana_prom_load_generator.json' \
+    #    ${GRAFANA_URL_UPDATED}/api/dashboards/db
+    #echo
+    #DASHBOARD_URL="${GRAFANA_URL_UPDATED}/api/dashboards/uid/tfs-loadgen-stats"
+    #DASHBOARD_ID=$(curl -s "${DASHBOARD_URL}" | jq '.dashboard.id')
+    #curl -X POST ${GRAFANA_URL_UPDATED}/api/user/stars/dashboard/${DASHBOARD_ID}
+    #echo
+
+    # Not needed for the Hackfest
+    ## Dashboard: Load Generator Status
+    #curl -X POST -H "Content-Type: application/json" -d '@src/webui/grafana_prom_tfs_num_pods.json' \
+    #    ${GRAFANA_URL_UPDATED}/api/dashboards/db
+    #echo
+    #DASHBOARD_URL="${GRAFANA_URL_UPDATED}/api/dashboards/uid/tfs-num-pods"
+    #DASHBOARD_ID=$(curl -s "${DASHBOARD_URL}" | jq '.dashboard.id')
+    #curl -X POST ${GRAFANA_URL_UPDATED}/api/user/stars/dashboard/${DASHBOARD_ID}
+    #echo
 
     printf "\n\n"
 fi
diff --git a/hackfest/commands.txt b/hackfest/commands.txt
index 1b5d03dd4f8b64521401f1ab81bc5c2066eb68c4..840b83a245f6adf823b8ec097b16437c7d052826 100644
--- a/hackfest/commands.txt
+++ b/hackfest/commands.txt
@@ -337,8 +337,8 @@ $ python3 connectionServiceWithNotif_client.py
 $ sudo bash -c "$(curl -sL https://get.containerlab.dev)"
 
 ## Deploy proposed two SR node scenario
-$ cd tfs-ctrl/hackfest/gnmi
-$ sudo containerlab deploy -t srlinux.clab.yml
+$ cd ~/tfs-ctrl/hackfest/gnmi
+$ sudo containerlab deploy --topo srlinux.clab.yml
 
 ## Access SR Bash
 $ docker exec -it clab-srlinux-srl1 bash
diff --git a/hackfest/containerlab/.gitignore b/hackfest/containerlab/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..48cbf38799194b5fb3896f3a27f16bff11369fb2
--- /dev/null
+++ b/hackfest/containerlab/.gitignore
@@ -0,0 +1,2 @@
+clab-tfs-scenario
+.tfs-scenario.clab.yml.bak
diff --git a/hackfest/containerlab/commands.txt b/hackfest/containerlab/commands.txt
new file mode 100644
index 0000000000000000000000000000000000000000..18c629c0af2fe176a34f9b08a16405731c185243
--- /dev/null
+++ b/hackfest/containerlab/commands.txt
@@ -0,0 +1,99 @@
+############
+# ContainerLab
+############
+
+Refs:
+https://documentation.nokia.com/srlinux/22-6/SR_Linux_Book_Files/SysMgmt_Guide/data-models.html#openconfig-ov
+https://documentation.nokia.com/srlinux/SR_Linux_HTML_R21-11/SysMgmt_Guide/gnmi-interface.html#ai9ersv4qe
+https://github.com/openconfig/kne/blob/v0.1.9/examples/nokia/srlinux-services/srl-openconfig.cfg.json
+https://containerlab.dev/manual/kinds/srl/#default-node-configuration
+https://learn.srlinux.dev/tutorials/infrastructure/kne/srl-with-oc-services/
+https://github.com/openconfig/reference/blob/master/rpc/gnmi/gnmi-specification.md
+https://gnmic.kmrd.dev/cmd/get/
+
+
+IMPORTANT: for Nokia SR Linux, use kind "srl" and type "ixr6"
+
+## Download and install the latest release
+$ sudo bash -c "$(curl -sL https://get.containerlab.dev)" -- -v 0.42.0
+
+## Deploy proposed two SR node scenario
+$ cd ~/tfs-ctrl/hackfest/containerlab
+$ sudo containerlab deploy --topo tfs-scenario.clab.yml
+
+## Access SR Bash
+$ docker exec -it clab-tfs-scenario-srl1 bash
+
+## Access SR CLI
+$ docker exec -it clab-tfs-scenario-srl1 sr_cli
+
+## Destroy scenario
+$ sudo containerlab destroy --topo tfs-scenario.clab.yml
+
+
+## Enable OpenConfig data models and set as default:
+$ docker exec -it clab-tfs-scenario-srl1 sr_cli
+# enter candidate
+# system management openconfig admin-state enable
+# system gnmi-server network-instance mgmt yang-models openconfig
+# commit stay
+# quit
+
+
+# Configure containerlab clients
+docker exec -it clab-tfs-scenario-client1 bash
+    ip address add 172.16.1.10/24 dev eth1
+    ip route add 172.16.2.0/24 via 172.16.1.1
+
+    ping 172.16.2.1 or 172.16.2.10
+
+docker exec -it clab-tfs-scenario-client2 bash
+    ip address add 172.16.2.10/24 dev eth1
+    ip route add 172.16.1.0/24 via 172.16.2.1
+
+    ping 172.16.1.1 or 172.16.1.10
+
+
+
+
+## Install gNMIc
+$ sudo bash -c "$(curl -sL https://get-gnmic.kmrd.dev)"
+
+## gNMI Capabilities request
+$ gnmic -a clab-srlinux-srl1 -u admin -p NokiaSrl1! --skip-verify capabilities
+
+## gNMI Get request
+$ gnmic -a clab-srlinux-srl1 -u admin -p NokiaSrl1! --skip-verify -e json_ietf get --path /system/name/host-name
+$ gnmic -a clab-srlinux-srl1 -u admin -p NokiaSrl1! --skip-verify -e json_ietf get --path /interface[name=mgmt0]
+
+## gNMI Set request
+$ gnmic -a clab-srlinux-srl1 -u admin -p NokiaSrl1! --skip-verify -e json_ietf set --update-path /system/name/host-name --update-value slr11
+
+(we check the changed value) 
+$ gnmic -a clab-srlinux-srl1 -u admin -p NokiaSrl1! --skip-verify -e json_ietf get --path /system/name/host-name 
+
+## Subscribe request
+$ gnmic -a clab-srlinux-srl1 -u admin -p NokiaSrl1! --skip-verify -e json_ietf subscribe --path /interface[name=mgmt0]/statistics
+(In another terminal, you can generate traffic) 
+$ssh admin@clab-srlinux-srl1
+
+
+
+
+# Check configurations done:
+gnmic -a 172.100.100.101 -u admin -p NokiaSrl1! --skip-verify -e json_ietf get --path '/network-instances' > srl1-nis.json
+gnmic -a 172.100.100.101 -u admin -p NokiaSrl1! --skip-verify -e json_ietf get --path '/interfaces' > srl1-ifs.json
+gnmic -a 172.100.100.102 -u admin -p NokiaSrl1! --skip-verify -e json_ietf get --path '/network-instances' > srl2-nis.json
+gnmic -a 172.100.100.102 -u admin -p NokiaSrl1! --skip-verify -e json_ietf get --path '/interfaces' > srl2-ifs.json
+
+
+# Delete elements:
+gnmic -a 172.100.100.101 -u admin -p NokiaSrl1! --skip-verify -e json_ietf set --delete '/network-instances/network-instance[name=b19229e8]'
+gnmic -a 172.100.100.101 -u admin -p NokiaSrl1! --skip-verify -e json_ietf set --delete '/interfaces/interface[name=ethernet-1/1]/subinterfaces/subinterface[index=0]'
+gnmic -a 172.100.100.101 -u admin -p NokiaSrl1! --skip-verify -e json_ietf set --delete '/interfaces/interface[name=ethernet-1/2]/subinterfaces/subinterface[index=0]'
+gnmic -a 172.100.100.102 -u admin -p NokiaSrl1! --skip-verify -e json_ietf set --delete '/network-instances/network-instance[name=b19229e8]'
+gnmic -a 172.100.100.102 -u admin -p NokiaSrl1! --skip-verify -e json_ietf set --delete '/interfaces/interface[name=ethernet-1/1]/subinterfaces/subinterface[index=0]'
+gnmic -a 172.100.100.102 -u admin -p NokiaSrl1! --skip-verify -e json_ietf set --delete '/interfaces/interface[name=ethernet-1/2]/subinterfaces/subinterface[index=0]'
+
+# Run gNMI Driver in standalone mode (advanced)
+PYTHONPATH=./src python -m src.device.tests.test_gnmi
diff --git a/hackfest/containerlab/tfs-descriptors/.gitkeep b/hackfest/containerlab/tfs-descriptors/.gitkeep
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/hackfest/containerlab/tfs-descriptors/dc-2-dc-l3-service.json b/hackfest/containerlab/tfs-descriptors/dc-2-dc-l3-service.json
new file mode 100644
index 0000000000000000000000000000000000000000..cb9ef972e4c314694a1a8a78bb5e30509a2de61f
--- /dev/null
+++ b/hackfest/containerlab/tfs-descriptors/dc-2-dc-l3-service.json
@@ -0,0 +1,37 @@
+{
+    "services": [
+        {
+            "service_id": {
+                "context_id": {"context_uuid": {"uuid": "admin"}}, "service_uuid": {"uuid": "dc-2-dc-l3-svc"}
+            },
+            "service_type": 1,
+            "service_status": {"service_status": 1},
+            "service_endpoint_ids": [
+                {"device_id":{"device_uuid":{"uuid":"DC1"}},"endpoint_uuid":{"uuid":"int"}},
+                {"device_id":{"device_uuid":{"uuid":"DC2"}},"endpoint_uuid":{"uuid":"int"}}
+            ],
+            "service_constraints": [],
+            "service_config": {"config_rules": [
+                {"action": 1, "custom": {"resource_key": "/device[SRL1]/settings", "resource_value": {
+                    "static_routes": [{"prefix": "172.16.2.0/24", "next_hop": "172.0.0.2"}]
+                }}},
+                {"action": 1, "custom": {"resource_key": "/device[SRL1]/endpoint[ethernet-1/1]/settings", "resource_value": {
+                    "ipv4_address": "172.0.0.1", "ipv4_prefix": 30, "sub_interface_index": 0
+                }}},
+                {"action": 1, "custom": {"resource_key": "/device[SRL1]/endpoint[ethernet-1/2]/settings", "resource_value": {
+                    "ipv4_address": "172.16.1.1", "ipv4_prefix": 24, "sub_interface_index": 0
+                }}},
+
+                {"action": 1, "custom": {"resource_key": "/device[SRL2]/settings", "resource_value": {
+                    "static_routes": [{"prefix": "172.16.1.0/24", "next_hop": "172.0.0.1"}]
+                }}},
+                {"action": 1, "custom": {"resource_key": "/device[SRL2]/endpoint[ethernet-1/1]/settings", "resource_value": {
+                    "ipv4_address": "172.0.0.2", "ipv4_prefix": 30, "sub_interface_index": 0
+                }}},
+                {"action": 1, "custom": {"resource_key": "/device[SRL2]/endpoint[ethernet-1/2]/settings", "resource_value": {
+                    "ipv4_address": "172.16.2.1", "ipv4_prefix": 24, "sub_interface_index": 0
+                }}}
+            ]}
+        }
+    ]
+}
diff --git a/hackfest/containerlab/tfs-descriptors/topology.json b/hackfest/containerlab/tfs-descriptors/topology.json
new file mode 100644
index 0000000000000000000000000000000000000000..e4a49981f99339d77538af814365703e463d3db5
--- /dev/null
+++ b/hackfest/containerlab/tfs-descriptors/topology.json
@@ -0,0 +1,96 @@
+{
+    "contexts": [
+        {"context_id": {"context_uuid": {"uuid": "admin"}}}
+    ],
+    "topologies": [
+        {"topology_id": {"context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "admin"}}}
+    ],
+    "devices": [
+        {
+            "device_id": {"device_uuid": {"uuid": "DC1"}}, "device_type": "emu-datacenter", "device_drivers": [0],
+            "device_config": {"config_rules": [
+                {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}},
+                {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}},
+                {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [
+                    {"uuid": "eth1", "type": "copper"}, {"uuid": "eth2", "type": "copper"}, {"uuid": "int", "type": "copper"}
+                ]}}}
+            ]}
+        },
+        {
+            "device_id": {"device_uuid": {"uuid": "DC2"}}, "device_type": "emu-datacenter", "device_drivers": [0],
+            "device_config": {"config_rules": [
+                {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}},
+                {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}},
+                {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [
+                    {"uuid": "eth1", "type": "copper"}, {"uuid": "eth2", "type": "copper"}, {"uuid": "int", "type": "copper"}
+                ]}}}
+            ]}
+        },
+        {
+            "device_id": {"device_uuid": {"uuid": "SRL1"}}, "device_type": "packet-router", "device_drivers": [8],
+            "device_config": {"config_rules": [
+                {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "172.100.100.101"}},
+                {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "57400"}},
+                {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {
+                    "username": "admin", "password": "NokiaSrl1!", "use_tls": true
+                }}}
+            ]}
+        },
+        {
+            "device_id": {"device_uuid": {"uuid": "SRL2"}}, "device_type": "packet-router", "device_drivers": [8],
+            "device_config": {"config_rules": [
+                {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "172.100.100.102"}},
+                {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "57400"}},
+                {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {
+                    "username": "admin", "password": "NokiaSrl1!", "use_tls": true
+                }}}
+            ]}
+        }
+    ],
+    "links": [
+        {
+            "link_id": {"link_uuid": {"uuid": "DC1/eth1==SRL1/ethernet-1/2"}},
+            "link_endpoint_ids": [
+                {"device_id": {"device_uuid": {"uuid": "DC1"}}, "endpoint_uuid": {"uuid": "eth1"}},
+                {"device_id": {"device_uuid": {"uuid": "SRL1"}}, "endpoint_uuid": {"uuid": "ethernet-1/2"}}
+            ]
+        },
+        {
+            "link_id": {"link_uuid": {"uuid": "SRL1/ethernet-1/2==DC1/eth1"}},
+            "link_endpoint_ids": [
+                {"device_id": {"device_uuid": {"uuid": "SRL1"}}, "endpoint_uuid": {"uuid": "ethernet-1/2"}},
+                {"device_id": {"device_uuid": {"uuid": "DC1"}}, "endpoint_uuid": {"uuid": "eth1"}}
+            ]
+        },
+
+        {
+            "link_id": {"link_uuid": {"uuid": "SRL1/ethernet-1/1==SRL2/ethernet-1/1"}},
+            "link_endpoint_ids": [
+                {"device_id": {"device_uuid": {"uuid": "SRL1"}}, "endpoint_uuid": {"uuid": "ethernet-1/1"}},
+                {"device_id": {"device_uuid": {"uuid": "SRL2"}}, "endpoint_uuid": {"uuid": "ethernet-1/1"}}
+            ]
+        },
+        {
+            "link_id": {"link_uuid": {"uuid": "SRL2/ethernet-1/1==SRL1/ethernet-1/1"}},
+            "link_endpoint_ids": [
+                {"device_id": {"device_uuid": {"uuid": "SRL2"}}, "endpoint_uuid": {"uuid": "ethernet-1/1"}},
+                {"device_id": {"device_uuid": {"uuid": "SRL1"}}, "endpoint_uuid": {"uuid": "ethernet-1/1"}}
+            ]
+        },
+
+        {
+            "link_id": {"link_uuid": {"uuid": "DC2/eth1==SRL2/ethernet-1/2"}},
+            "link_endpoint_ids": [
+                {"device_id": {"device_uuid": {"uuid": "DC2"}}, "endpoint_uuid": {"uuid": "eth1"}},
+                {"device_id": {"device_uuid": {"uuid": "SRL2"}}, "endpoint_uuid": {"uuid": "ethernet-1/2"}}
+            ]
+        },
+        {
+            "link_id": {"link_uuid": {"uuid": "SRL2/ethernet-1/2==DC2/eth1"}},
+            "link_endpoint_ids": [
+                {"device_id": {"device_uuid": {"uuid": "SRL2"}}, "endpoint_uuid": {"uuid": "ethernet-1/2"}},
+                {"device_id": {"device_uuid": {"uuid": "DC2"}}, "endpoint_uuid": {"uuid": "eth1"}}
+            ]
+        }
+    ]
+}
diff --git a/hackfest/containerlab/tfs-scenario.clab.yml b/hackfest/containerlab/tfs-scenario.clab.yml
new file mode 100644
index 0000000000000000000000000000000000000000..df197ebea70906874515240f18d2f06c4c307c88
--- /dev/null
+++ b/hackfest/containerlab/tfs-scenario.clab.yml
@@ -0,0 +1,59 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Example based on clos01 example (http://containerlab.dev/lab-examples/min-clos/)
+
+# TFS 2 Nokia SR Linux nodes + 2 Linux clients
+name: tfs-scenario
+
+mgmt:
+  network: mgmt-net
+  ipv4-subnet: 172.100.100.0/24
+
+topology:
+  kinds:
+    srl:
+      image: ghcr.io/nokia/srlinux:23.3.1
+    linux:
+      image: ghcr.io/hellt/network-multitool
+  nodes:
+    srl1:
+      kind: srl
+      type: ixr6
+      cpu: 0.5
+      memory: 1GB
+      mgmt-ipv4: 172.100.100.101
+      #startup-config: srl1.cli
+    srl2:
+      kind: srl
+      type: ixr6
+      cpu: 0.5
+      memory: 1GB
+      mgmt-ipv4: 172.100.100.102
+      #startup-config: srl2.cli
+    client1:
+      kind: linux
+      cpu: 0.1
+      memory: 100MB
+      mgmt-ipv4: 172.100.100.201
+    client2:
+      kind: linux
+      cpu: 0.1
+      memory: 100MB
+      mgmt-ipv4: 172.100.100.202
+
+  links:
+    - endpoints: ["srl1:e1-1", "srl2:e1-1"]
+    - endpoints: ["client1:eth1", "srl1:e1-2"]
+    - endpoints: ["client2:eth1", "srl2:e1-2"]
diff --git a/hackfest/tfs-descriptors/context-topology.json b/hackfest/tfs-descriptors/hackfest1/context-topology.json
similarity index 100%
rename from hackfest/tfs-descriptors/context-topology.json
rename to hackfest/tfs-descriptors/hackfest1/context-topology.json
diff --git a/hackfest/tfs-descriptors/device-all.json b/hackfest/tfs-descriptors/hackfest1/device-all.json
similarity index 100%
rename from hackfest/tfs-descriptors/device-all.json
rename to hackfest/tfs-descriptors/hackfest1/device-all.json
diff --git a/hackfest/tfs-descriptors/device-netconf-openconfig.json b/hackfest/tfs-descriptors/hackfest1/device-netconf-openconfig.json
similarity index 100%
rename from hackfest/tfs-descriptors/device-netconf-openconfig.json
rename to hackfest/tfs-descriptors/hackfest1/device-netconf-openconfig.json
diff --git a/hackfest/tfs-descriptors/device-tapi-ols.json b/hackfest/tfs-descriptors/hackfest1/device-tapi-ols.json
similarity index 100%
rename from hackfest/tfs-descriptors/device-tapi-ols.json
rename to hackfest/tfs-descriptors/hackfest1/device-tapi-ols.json
diff --git a/hackfest/tfs-descriptors/links.json b/hackfest/tfs-descriptors/hackfest1/links.json
similarity index 100%
rename from hackfest/tfs-descriptors/links.json
rename to hackfest/tfs-descriptors/hackfest1/links.json
diff --git a/hackfest/tfs-descriptors/service-l3vpn.json b/hackfest/tfs-descriptors/hackfest1/service-l3vpn.json
similarity index 100%
rename from hackfest/tfs-descriptors/service-l3vpn.json
rename to hackfest/tfs-descriptors/hackfest1/service-l3vpn.json
diff --git a/hackfest/tfs-descriptors/hackfest2/emulated-topology.json b/hackfest/tfs-descriptors/hackfest2/emulated-topology.json
new file mode 100644
index 0000000000000000000000000000000000000000..6885c7d9082bfcb17447252636ea4efb0f500283
--- /dev/null
+++ b/hackfest/tfs-descriptors/hackfest2/emulated-topology.json
@@ -0,0 +1,210 @@
+{
+    "contexts": [
+        {"context_id": {"context_uuid": {"uuid": "admin"}}}
+    ],
+    "topologies": [
+        {"topology_id": {"context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "admin"}}}
+    ],
+    "devices": [
+        {
+            "device_id": {"device_uuid": {"uuid": "R1"}}, "device_type": "emu-packet-router", "device_drivers": [0],
+            "device_endpoints": [], "device_operational_status": 1, "device_config": {"config_rules": [
+                {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}},
+                {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}},
+                {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "1/1"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "1/2"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "1/3"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "1/4"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "1/5"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "1/6"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "2/1"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "2/2"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "2/3"}
+                ]}}}
+            ]}
+        },
+        {
+            "device_id": {"device_uuid": {"uuid": "R2"}}, "device_type": "emu-packet-router", "device_drivers": [0],
+            "device_endpoints": [], "device_operational_status": 1, "device_config": {"config_rules": [
+                {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}},
+                {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}},
+                {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "1/1"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "1/2"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "1/3"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "1/4"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "1/5"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "1/6"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "2/1"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "2/2"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "2/3"}
+                ]}}}
+            ]}
+        },
+        {
+            "device_id": {"device_uuid": {"uuid": "R3"}}, "device_type": "emu-packet-router", "device_drivers": [0],
+            "device_endpoints": [], "device_operational_status": 1, "device_config": {"config_rules": [
+                {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}},
+                {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}},
+                {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "1/1"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "1/2"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "1/3"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "1/4"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "1/5"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "1/6"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "2/1"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "2/2"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "2/3"}
+                ]}}}
+            ]}
+        },
+        {
+            "device_id": {"device_uuid": {"uuid": "R4"}}, "device_type": "emu-packet-router", "device_drivers": [0],
+            "device_endpoints": [], "device_operational_status": 1, "device_config": {"config_rules": [
+                {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}},
+                {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}},
+                {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "1/1"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "1/2"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "1/3"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "1/4"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "1/5"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "1/6"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "2/1"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "2/2"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "2/3"}
+                ]}}}
+            ]}
+        },
+        {
+            "device_id": {"device_uuid": {"uuid": "R5"}}, "device_type": "emu-packet-router", "device_drivers": [0],
+            "device_endpoints": [], "device_operational_status": 1, "device_config": {"config_rules": [
+                {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}},
+                {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}},
+                {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "1/1"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "1/2"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "1/3"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "1/4"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "1/5"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "1/6"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "2/1"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "2/2"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "2/3"}
+                ]}}}
+            ]}
+        },
+        {
+            "device_id": {"device_uuid": {"uuid": "R6"}}, "device_type": "emu-packet-router", "device_drivers": [0],
+            "device_endpoints": [], "device_operational_status": 1, "device_config": {"config_rules": [
+                {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}},
+                {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}},
+                {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "1/1"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "1/2"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "1/3"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "1/4"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "1/5"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "1/6"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "2/1"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "2/2"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "2/3"}
+                ]}}}
+            ]}
+        },
+        {
+            "device_id": {"device_uuid": {"uuid": "R7"}}, "device_type": "emu-packet-router", "device_drivers": [0],
+            "device_endpoints": [], "device_operational_status": 1, "device_config": {"config_rules": [
+                {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}},
+                {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}},
+                {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "1/1"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "1/2"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "1/3"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "2/1"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "2/2"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "2/3"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "2/4"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "2/5"},
+                    {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "2/6"}
+                ]}}}
+            ]}
+        }
+    ],
+    "links": [
+        {"link_id": {"link_uuid": {"uuid": "R1==R2"}}, "link_endpoint_ids": [
+            {"device_id": {"device_uuid": {"uuid": "R1"}}, "endpoint_uuid": {"uuid": "2/1"}},
+            {"device_id": {"device_uuid": {"uuid": "R2"}}, "endpoint_uuid": {"uuid": "2/2"}}
+        ]},
+        {"link_id": {"link_uuid": {"uuid": "R1==R6"}}, "link_endpoint_ids": [
+            {"device_id": {"device_uuid": {"uuid": "R1"}}, "endpoint_uuid": {"uuid": "2/2"}},
+            {"device_id": {"device_uuid": {"uuid": "R6"}}, "endpoint_uuid": {"uuid": "2/1"}}
+        ]},
+        {"link_id": {"link_uuid": {"uuid": "R1==R7"}}, "link_endpoint_ids": [
+            {"device_id": {"device_uuid": {"uuid": "R1"}}, "endpoint_uuid": {"uuid": "2/3"}},
+            {"device_id": {"device_uuid": {"uuid": "R7"}}, "endpoint_uuid": {"uuid": "2/1"}}
+        ]},
+        {"link_id": {"link_uuid": {"uuid": "R2==R1"}}, "link_endpoint_ids": [
+            {"device_id": {"device_uuid": {"uuid": "R2"}}, "endpoint_uuid": {"uuid": "2/2"}},
+            {"device_id": {"device_uuid": {"uuid": "R1"}}, "endpoint_uuid": {"uuid": "2/1"}}
+        ]},
+        {"link_id": {"link_uuid": {"uuid": "R2==R3"}}, "link_endpoint_ids": [
+            {"device_id": {"device_uuid": {"uuid": "R2"}}, "endpoint_uuid": {"uuid": "2/1"}},
+            {"device_id": {"device_uuid": {"uuid": "R3"}}, "endpoint_uuid": {"uuid": "2/2"}}
+        ]},
+        {"link_id": {"link_uuid": {"uuid": "R3==R2"}}, "link_endpoint_ids": [
+            {"device_id": {"device_uuid": {"uuid": "R3"}}, "endpoint_uuid": {"uuid": "2/2"}},
+            {"device_id": {"device_uuid": {"uuid": "R2"}}, "endpoint_uuid": {"uuid": "2/1"}}
+        ]},
+        {"link_id": {"link_uuid": {"uuid": "R3==R4"}}, "link_endpoint_ids": [
+            {"device_id": {"device_uuid": {"uuid": "R3"}}, "endpoint_uuid": {"uuid": "2/1"}},
+            {"device_id": {"device_uuid": {"uuid": "R4"}}, "endpoint_uuid": {"uuid": "2/2"}}
+        ]},
+        {"link_id": {"link_uuid": {"uuid": "R3==R7"}}, "link_endpoint_ids": [
+            {"device_id": {"device_uuid": {"uuid": "R3"}}, "endpoint_uuid": {"uuid": "2/3"}},
+            {"device_id": {"device_uuid": {"uuid": "R7"}}, "endpoint_uuid": {"uuid": "2/3"}}
+        ]},
+        {"link_id": {"link_uuid": {"uuid": "R4==R3"}}, "link_endpoint_ids": [
+            {"device_id": {"device_uuid": {"uuid": "R4"}}, "endpoint_uuid": {"uuid": "2/2"}},
+            {"device_id": {"device_uuid": {"uuid": "R3"}}, "endpoint_uuid": {"uuid": "2/1"}}
+        ]},
+        {"link_id": {"link_uuid": {"uuid": "R4==R5"}}, "link_endpoint_ids": [
+            {"device_id": {"device_uuid": {"uuid": "R4"}}, "endpoint_uuid": {"uuid": "2/1"}},
+            {"device_id": {"device_uuid": {"uuid": "R5"}}, "endpoint_uuid": {"uuid": "2/2"}}
+        ]},
+        {"link_id": {"link_uuid": {"uuid": "R5==R4"}}, "link_endpoint_ids": [
+            {"device_id": {"device_uuid": {"uuid": "R5"}}, "endpoint_uuid": {"uuid": "2/2"}},
+            {"device_id": {"device_uuid": {"uuid": "R4"}}, "endpoint_uuid": {"uuid": "2/1"}}
+        ]},
+        {"link_id": {"link_uuid": {"uuid": "R5==R6"}}, "link_endpoint_ids": [
+            {"device_id": {"device_uuid": {"uuid": "R5"}}, "endpoint_uuid": {"uuid": "2/1"}},
+            {"device_id": {"device_uuid": {"uuid": "R6"}}, "endpoint_uuid": {"uuid": "2/2"}}
+        ]},
+        {"link_id": {"link_uuid": {"uuid": "R5==R7"}}, "link_endpoint_ids": [
+            {"device_id": {"device_uuid": {"uuid": "R5"}}, "endpoint_uuid": {"uuid": "2/3"}},
+            {"device_id": {"device_uuid": {"uuid": "R7"}}, "endpoint_uuid": {"uuid": "2/5"}}
+        ]},
+        {"link_id": {"link_uuid": {"uuid": "R6==R1"}}, "link_endpoint_ids": [
+            {"device_id": {"device_uuid": {"uuid": "R6"}}, "endpoint_uuid": {"uuid": "2/1"}},
+            {"device_id": {"device_uuid": {"uuid": "R1"}}, "endpoint_uuid": {"uuid": "2/2"}}
+        ]},
+        {"link_id": {"link_uuid": {"uuid": "R6==R5"}}, "link_endpoint_ids": [
+            {"device_id": {"device_uuid": {"uuid": "R6"}}, "endpoint_uuid": {"uuid": "2/2"}},
+            {"device_id": {"device_uuid": {"uuid": "R5"}}, "endpoint_uuid": {"uuid": "2/1"}}
+        ]},
+        {"link_id": {"link_uuid": {"uuid": "R7==R1"}}, "link_endpoint_ids": [
+            {"device_id": {"device_uuid": {"uuid": "R7"}}, "endpoint_uuid": {"uuid": "2/1"}},
+            {"device_id": {"device_uuid": {"uuid": "R1"}}, "endpoint_uuid": {"uuid": "2/3"}}
+        ]},
+        {"link_id": {"link_uuid": {"uuid": "R7==R3"}}, "link_endpoint_ids": [
+            {"device_id": {"device_uuid": {"uuid": "R7"}}, "endpoint_uuid": {"uuid": "2/3"}},
+            {"device_id": {"device_uuid": {"uuid": "R3"}}, "endpoint_uuid": {"uuid": "2/3"}}
+        ]},
+        {"link_id": {"link_uuid": {"uuid": "R7==R5"}}, "link_endpoint_ids": [
+            {"device_id": {"device_uuid": {"uuid": "R7"}}, "endpoint_uuid": {"uuid": "2/5"}},
+            {"device_id": {"device_uuid": {"uuid": "R5"}}, "endpoint_uuid": {"uuid": "2/3"}}
+        ]}
+    ]
+}
diff --git a/hackfest/tfs-descriptors/hackfest2/emulated-topology.png b/hackfest/tfs-descriptors/hackfest2/emulated-topology.png
new file mode 100644
index 0000000000000000000000000000000000000000..e8684e825cd8a92d58c58c785b45203f7b0abee2
Binary files /dev/null and b/hackfest/tfs-descriptors/hackfest2/emulated-topology.png differ
diff --git a/hackfest/tfs-descriptors/hackfest2/l3-service.json b/hackfest/tfs-descriptors/hackfest2/l3-service.json
new file mode 100644
index 0000000000000000000000000000000000000000..540d3a7a6744551ba13e59cd9ec2c23258e0c762
--- /dev/null
+++ b/hackfest/tfs-descriptors/hackfest2/l3-service.json
@@ -0,0 +1,44 @@
+{
+    "services": [
+        {
+            "service_id": {
+                "context_id": {"context_uuid": {"uuid": "admin"}},
+                "service_uuid": {"uuid": "l3-service"}
+            },
+            "service_type": 1,
+            "service_status": {"service_status": 1},
+            "service_endpoint_ids": [
+                {"device_id": {"device_uuid": {"uuid": "R1"}}, "endpoint_uuid": {"uuid": "1/2"}},
+                {"device_id": {"device_uuid": {"uuid": "R4"}}, "endpoint_uuid": {"uuid": "1/2"}}
+            ],
+            "service_constraints": [
+                {"custom": {"constraint_type": "bandwidth[gbps]", "constraint_value": "10.0"}},
+                {"custom": {"constraint_type": "latency[ms]", "constraint_value": "15.2"}}
+            ],
+            "service_config": {"config_rules": [
+                {"action": 1, "custom": {"resource_key": "/settings", "resource_value": {
+                    "address_families": ["IPV4"],
+                    "bgp_as": 65000,
+                    "bgp_route_target": "65000:333",
+                    "mtu": 1512
+                }}},
+                {"action": 1, "custom": {"resource_key": "/device[R1]/endpoint[1/2]/settings", "resource_value": {
+                    "address_ip": "3.3.2.1",
+                    "address_prefix": 24,
+                    "route_distinguisher": "65000:123",
+                    "router_id": "10.10.10.1",
+                    "sub_interface_index": 400,
+                    "vlan_id": 400
+                }}},
+                {"action": 1, "custom": {"resource_key": "/device[R4]/endpoint[1/2]/settings", "resource_value": {
+                    "address_ip": "3.3.1.1",
+                    "address_prefix": 24,
+                    "route_distinguisher": "65000:123",
+                    "router_id": "20.20.20.1",
+                    "sub_interface_index": 400,
+                    "vlan_id": 500
+                }}}
+            ]}
+        }
+    ]
+}
diff --git a/manifests/contextservice.yaml b/manifests/contextservice.yaml
index 96735bf5f89f682f31131c123ee9884a1becbfdb..659ff7b8daa217476e37634b1689b9ae69c96bad 100644
--- a/manifests/contextservice.yaml
+++ b/manifests/contextservice.yaml
@@ -23,8 +23,9 @@ spec:
   #replicas: 1
   template:
     metadata:
-      annotations:
-        config.linkerd.io/skip-outbound-ports: "4222"
+      # Deactivated linkerd for the Hackfest
+      #annotations:
+      #  config.linkerd.io/skip-outbound-ports: "4222"
       labels:
         app: contextservice
     spec:
@@ -54,11 +55,11 @@ spec:
             command: ["/bin/grpc_health_probe", "-addr=:1010"]
         resources:
           requests:
-            cpu: 250m
+            cpu: 150m
             memory: 128Mi
           limits:
-            cpu: 1000m
-            memory: 1024Mi
+            cpu: 500m
+            memory: 512Mi
 ---
 apiVersion: v1
 kind: Service
@@ -79,25 +80,25 @@ spec:
     protocol: TCP
     port: 9192
     targetPort: 9192
----
-apiVersion: autoscaling/v2
-kind: HorizontalPodAutoscaler
-metadata:
-  name: contextservice-hpa
-spec:
-  scaleTargetRef:
-    apiVersion: apps/v1
-    kind: Deployment
-    name: contextservice
-  minReplicas: 1
-  maxReplicas: 20
-  metrics:
-  - type: Resource
-    resource:
-      name: cpu
-      target:
-        type: Utilization
-        averageUtilization: 80
-  #behavior:
-  #  scaleDown:
-  #    stabilizationWindowSeconds: 30
+#---
+#apiVersion: autoscaling/v2
+#kind: HorizontalPodAutoscaler
+#metadata:
+#  name: contextservice-hpa
+#spec:
+#  scaleTargetRef:
+#    apiVersion: apps/v1
+#    kind: Deployment
+#    name: contextservice
+#  minReplicas: 1
+#  maxReplicas: 20
+#  metrics:
+#  - type: Resource
+#    resource:
+#      name: cpu
+#      target:
+#        type: Utilization
+#        averageUtilization: 80
+#  #behavior:
+#  #  scaleDown:
+#  #    stabilizationWindowSeconds: 30
diff --git a/manifests/deviceservice.yaml b/manifests/deviceservice.yaml
index 22c0f5f9d124b76d0e477dce35d14811204c1496..74c58b83dabe2bd38dec08e22fc130341996c457 100644
--- a/manifests/deviceservice.yaml
+++ b/manifests/deviceservice.yaml
@@ -23,9 +23,10 @@ spec:
   replicas: 1
   template:
     metadata:
-      annotations:
-        # Required for IETF L2VPN SBI when both parent and child run in same K8s cluster with Linkerd
-        config.linkerd.io/skip-outbound-ports: "8002"
+      # Deactivated linkerd for the Hackfest
+      #annotations:
+      #  # Required for IETF L2VPN SBI when both parent and child run in same K8s cluster with Linkerd
+      #  config.linkerd.io/skip-outbound-ports: "8002"
       labels:
         app: deviceservice
     spec:
@@ -40,6 +41,11 @@ spec:
         env:
         - name: LOG_LEVEL
           value: "INFO"
+        startupProbe:
+          exec:
+            command: ["/bin/grpc_health_probe", "-addr=:2020"]
+          failureThreshold: 30
+          periodSeconds: 10
         readinessProbe:
           exec:
             command: ["/bin/grpc_health_probe", "-addr=:2020"]
@@ -48,8 +54,8 @@ spec:
             command: ["/bin/grpc_health_probe", "-addr=:2020"]
         resources:
           requests:
-            cpu: 250m
-            memory: 128Mi
+            cpu: 500m
+            memory: 512Mi
           limits:
             cpu: 1000m
             memory: 1024Mi
diff --git a/manifests/monitoringservice.yaml b/manifests/monitoringservice.yaml
index 4447a1427980be6554228087924bf8e4ca775758..06ac823a169c2b08f46a225db3fe04defe7e87f4 100644
--- a/manifests/monitoringservice.yaml
+++ b/manifests/monitoringservice.yaml
@@ -36,7 +36,7 @@ spec:
         - containerPort: 9192
         env:
         - name: LOG_LEVEL
-          value: "INFO"
+          value: "DEBUG"
         envFrom:
         - secretRef:
             name: qdb-data
diff --git a/manifests/pathcompservice.yaml b/manifests/pathcompservice.yaml
index 15dd311972bcafadcccb765f75d73aa9566c3683..8aaf99dcbda895c662049d4b0fc9df38a8b0d7d6 100644
--- a/manifests/pathcompservice.yaml
+++ b/manifests/pathcompservice.yaml
@@ -36,7 +36,7 @@ spec:
         - containerPort: 9192
         env:
         - name: LOG_LEVEL
-          value: "DEBUG"
+          value: "INFO"
         readinessProbe:
           exec:
             command: ["/bin/grpc_health_probe", "-addr=:10020"]
@@ -72,7 +72,7 @@ spec:
             cpu: 100m
             memory: 256Mi
           limits:
-            cpu: 700m
+            cpu: 500m
             memory: 1024Mi
 ---
 apiVersion: v1
@@ -98,25 +98,25 @@ spec:
     protocol: TCP
     port: 9192
     targetPort: 9192
----
-apiVersion: autoscaling/v2
-kind: HorizontalPodAutoscaler
-metadata:
-  name: pathcompservice-hpa
-spec:
-  scaleTargetRef:
-    apiVersion: apps/v1
-    kind: Deployment
-    name: pathcompservice
-  minReplicas: 1
-  maxReplicas: 20
-  metrics:
-  - type: Resource
-    resource:
-      name: cpu
-      target:
-        type: Utilization
-        averageUtilization: 80
-  #behavior:
-  #  scaleDown:
-  #    stabilizationWindowSeconds: 30
+#---
+#apiVersion: autoscaling/v2
+#kind: HorizontalPodAutoscaler
+#metadata:
+#  name: pathcompservice-hpa
+#spec:
+#  scaleTargetRef:
+#    apiVersion: apps/v1
+#    kind: Deployment
+#    name: pathcompservice
+#  minReplicas: 1
+#  maxReplicas: 20
+#  metrics:
+#  - type: Resource
+#    resource:
+#      name: cpu
+#      target:
+#        type: Utilization
+#        averageUtilization: 80
+#  #behavior:
+#  #  scaleDown:
+#  #    stabilizationWindowSeconds: 30
diff --git a/manifests/serviceservice.yaml b/manifests/serviceservice.yaml
index 7d7bdaa4ef9ad4972da6236071810c63a9faa4f8..99f8f45a6c5ff7a2d0cd8f44f5be5afbedbe1a77 100644
--- a/manifests/serviceservice.yaml
+++ b/manifests/serviceservice.yaml
@@ -45,11 +45,11 @@ spec:
             command: ["/bin/grpc_health_probe", "-addr=:3030"]
         resources:
           requests:
-            cpu: 250m
+            cpu: 150m
             memory: 128Mi
           limits:
-            cpu: 1000m
-            memory: 1024Mi
+            cpu: 500m
+            memory: 512Mi
 ---
 apiVersion: v1
 kind: Service
@@ -70,25 +70,25 @@ spec:
     protocol: TCP
     port: 9192
     targetPort: 9192
----
-apiVersion: autoscaling/v2
-kind: HorizontalPodAutoscaler
-metadata:
-  name: serviceservice-hpa
-spec:
-  scaleTargetRef:
-    apiVersion: apps/v1
-    kind: Deployment
-    name: serviceservice
-  minReplicas: 1
-  maxReplicas: 20
-  metrics:
-  - type: Resource
-    resource:
-      name: cpu
-      target:
-        type: Utilization
-        averageUtilization: 80
-  #behavior:
-  #  scaleDown:
-  #    stabilizationWindowSeconds: 30
+#---
+#apiVersion: autoscaling/v2
+#kind: HorizontalPodAutoscaler
+#metadata:
+#  name: serviceservice-hpa
+#spec:
+#  scaleTargetRef:
+#    apiVersion: apps/v1
+#    kind: Deployment
+#    name: serviceservice
+#  minReplicas: 1
+#  maxReplicas: 20
+#  metrics:
+#  - type: Resource
+#    resource:
+#      name: cpu
+#      target:
+#        type: Utilization
+#        averageUtilization: 80
+#  #behavior:
+#  #  scaleDown:
+#  #    stabilizationWindowSeconds: 30
diff --git a/manifests/sliceservice.yaml b/manifests/sliceservice.yaml
index e7e5c1604a8b971424ff5f7e5bf292c4b263cbfe..5ea63ad0cbe466ea0e9caf8bd076b8c8ec359e37 100644
--- a/manifests/sliceservice.yaml
+++ b/manifests/sliceservice.yaml
@@ -50,11 +50,11 @@ spec:
             command: ["/bin/grpc_health_probe", "-addr=:4040"]
         resources:
           requests:
-            cpu: 250m
+            cpu: 150m
             memory: 128Mi
           limits:
-            cpu: 1000m
-            memory: 1024Mi
+            cpu: 500m
+            memory: 512Mi
 ---
 apiVersion: v1
 kind: Service
@@ -75,25 +75,25 @@ spec:
     protocol: TCP
     port: 9192
     targetPort: 9192
----
-apiVersion: autoscaling/v2
-kind: HorizontalPodAutoscaler
-metadata:
-  name: sliceservice-hpa
-spec:
-  scaleTargetRef:
-    apiVersion: apps/v1
-    kind: Deployment
-    name: sliceservice
-  minReplicas: 1
-  maxReplicas: 20
-  metrics:
-  - type: Resource
-    resource:
-      name: cpu
-      target:
-        type: Utilization
-        averageUtilization: 80
-  #behavior:
-  #  scaleDown:
-  #    stabilizationWindowSeconds: 30
+#---
+#apiVersion: autoscaling/v2
+#kind: HorizontalPodAutoscaler
+#metadata:
+#  name: sliceservice-hpa
+#spec:
+#  scaleTargetRef:
+#    apiVersion: apps/v1
+#    kind: Deployment
+#    name: sliceservice
+#  minReplicas: 1
+#  maxReplicas: 20
+#  metrics:
+#  - type: Resource
+#    resource:
+#      name: cpu
+#      target:
+#        type: Utilization
+#        averageUtilization: 80
+#  #behavior:
+#  #  scaleDown:
+#  #    stabilizationWindowSeconds: 30
diff --git a/my_deploy.sh b/my_deploy.sh
index e3ad5e71ae58f8546ecf7f24bf2a3a4c0d03cb37..8fe133477e95e4a3d595edf59878330cc1e600bb 100755
--- a/my_deploy.sh
+++ b/my_deploy.sh
@@ -20,10 +20,11 @@
 export TFS_REGISTRY_IMAGES="http://localhost:32000/tfs/"
 
 # Set the list of components, separated by spaces, you want to build images for, and deploy.
-export TFS_COMPONENTS="context device pathcomp service slice compute webui load_generator"
+#export TFS_COMPONENTS="context device pathcomp service slice compute webui load_generator"
+export TFS_COMPONENTS="context device pathcomp service slice webui"
 
 # Uncoment to activate Monitoring
-#export TFS_COMPONENTS="${TFS_COMPONENTS} monitoring"
+export TFS_COMPONENTS="${TFS_COMPONENTS} monitoring"
 
 # Uncoment to activate Automation and Policy Manager
 #export TFS_COMPONENTS="${TFS_COMPONENTS} automation policy"
@@ -41,7 +42,8 @@ export TFS_IMAGE_TAG="dev"
 export TFS_K8S_NAMESPACE="tfs"
 
 # Set additional manifest files to be applied after the deployment
-export TFS_EXTRA_MANIFESTS="manifests/nginx_ingress_http.yaml manifests/servicemonitors.yaml"
+#export TFS_EXTRA_MANIFESTS="manifests/nginx_ingress_http.yaml manifests/servicemonitors.yaml"
+export TFS_EXTRA_MANIFESTS="manifests/nginx_ingress_http.yaml"
 
 # Uncoment when deploying Optical CyberSecurity
 #export TFS_EXTRA_MANIFESTS="${TFS_EXTRA_MANIFESTS} manifests/cachingservice.yaml"
diff --git a/proto/context.proto b/proto/context.proto
index 39abc081d4a652eda246f876c5184faffc7065d5..9f779d8db310a98ea05682e619b1357c27c76904 100644
--- a/proto/context.proto
+++ b/proto/context.proto
@@ -195,6 +195,7 @@ enum DeviceDriverEnum {
   DEVICEDRIVER_ONF_TR_352 = 5;
   DEVICEDRIVER_XR = 6;
   DEVICEDRIVER_IETF_L2VPN = 7;
+  DEVICEDRIVER_GNMI_OPENCONFIG = 8;
 }
 
 enum DeviceOperationalStatusEnum {
diff --git a/src/common/type_checkers/Assertions.py b/src/common/type_checkers/Assertions.py
index d5476a9534ca6e2d74ba16d3af71ed367bc5ab51..25eb42fabf9c670256e9079a060aa38deb3c0f3d 100644
--- a/src/common/type_checkers/Assertions.py
+++ b/src/common/type_checkers/Assertions.py
@@ -34,6 +34,7 @@ def validate_device_driver_enum(message):
         'DEVICEDRIVER_ONF_TR_352',
         'DEVICEDRIVER_XR',
         'DEVICEDRIVER_IETF_L2VPN',
+        'DEVICEDRIVER_GNMI_OPENCONFIG',
     ]
 
 def validate_device_operational_status_enum(message):
diff --git a/src/context/service/database/models/enums/DeviceDriver.py b/src/context/service/database/models/enums/DeviceDriver.py
index a612803e235de2c6d2d8c91052416a675a3a3085..09be94b1d7ec041f2d3f50f832f15017fb62e63c 100644
--- a/src/context/service/database/models/enums/DeviceDriver.py
+++ b/src/context/service/database/models/enums/DeviceDriver.py
@@ -25,6 +25,7 @@ class ORM_DeviceDriverEnum(enum.Enum):
     ONF_TR_352            = DeviceDriverEnum.DEVICEDRIVER_ONF_TR_352
     XR                    = DeviceDriverEnum.DEVICEDRIVER_XR
     IETF_L2VPN            = DeviceDriverEnum.DEVICEDRIVER_IETF_L2VPN
+    GNMI_OPENCONFIG       = DeviceDriverEnum.DEVICEDRIVER_GNMI_OPENCONFIG
 
 grpc_to_enum__device_driver = functools.partial(
     grpc_to_enum, DeviceDriverEnum, ORM_DeviceDriverEnum)
diff --git a/src/device/requirements.in b/src/device/requirements.in
index 0c5f5856a6f06950f185d5f7318d059575795abd..825ef8e21e6c904497e1be7f701f2a6cd53e6b38 100644
--- a/src/device/requirements.in
+++ b/src/device/requirements.in
@@ -15,6 +15,7 @@
 
 anytree==2.8.0
 APScheduler==3.8.1
+cryptography==36.0.2
 #fastcache==1.1.0
 Jinja2==3.0.3
 ncclient==0.6.13
diff --git a/src/device/service/__main__.py b/src/device/service/__main__.py
index a07a2ab90d15d99bdabe6b3fb6b0e0c9c497cf3c..401711b4e366dad5347cf771651516fe5e125d55 100644
--- a/src/device/service/__main__.py
+++ b/src/device/service/__main__.py
@@ -58,13 +58,14 @@ def main():
     driver_factory = DriverFactory(DRIVERS)
     driver_instance_cache = DriverInstanceCache(driver_factory)
 
-    # Initialize drivers with existing devices in context
-    preload_drivers(driver_instance_cache)
-
     # Starting device service
     grpc_service = DeviceService(driver_instance_cache)
     grpc_service.start()
 
+    # Initialize drivers with existing devices in context
+    LOGGER.info('Pre-loading drivers...')
+    preload_drivers(driver_instance_cache)
+
     # Wait for Ctrl+C or termination signal
     while not terminate.wait(timeout=1.0): pass
 
diff --git a/src/device/service/drivers/__init__.py b/src/device/service/drivers/__init__.py
index b3b485a471899dd96a4985fedb4bb6ede2432921..89d3bfc0138223a4dd815aa94eeb90c54ef20d7a 100644
--- a/src/device/service/drivers/__init__.py
+++ b/src/device/service/drivers/__init__.py
@@ -70,6 +70,7 @@ DRIVERS.append(
         #        DeviceDriverEnum.DEVICEDRIVER_P4,
         #        DeviceDriverEnum.DEVICEDRIVER_IETF_NETWORK_TOPOLOGY,
         #        DeviceDriverEnum.DEVICEDRIVER_ONF_TR_352,
+        #        DeviceDriverEnum.DEVICEDRIVER_GNMI_OPENCONFIG,
         #    ],
         #}
     ]))
@@ -84,13 +85,23 @@ DRIVERS.append(
     ]))
 
 if LOAD_ALL_DEVICE_DRIVERS:
-    from .openconfig.OpenConfigDriver import OpenConfigDriver # pylint: disable=wrong-import-position
+    #from .openconfig.OpenConfigDriver import OpenConfigDriver # pylint: disable=wrong-import-position
+    #DRIVERS.append(
+    #    (OpenConfigDriver, [
+    #        {
+    #            # Real Packet Router, specifying OpenConfig Driver => use OpenConfigDriver
+    #            FilterFieldEnum.DEVICE_TYPE: DeviceTypeEnum.PACKET_ROUTER,
+    #            FilterFieldEnum.DRIVER     : DeviceDriverEnum.DEVICEDRIVER_OPENCONFIG,
+    #        }
+    #    ]))
+
+    from .gnmi_openconfig.GnmiOpenConfigDriver import GnmiOpenConfigDriver # pylint: disable=wrong-import-position
     DRIVERS.append(
-        (OpenConfigDriver, [
+        (GnmiOpenConfigDriver, [
             {
-                # Real Packet Router, specifying OpenConfig Driver => use OpenConfigDriver
+                # Real Packet Router, specifying gNMI OpenConfig Driver => use GnmiOpenConfigDriver
                 FilterFieldEnum.DEVICE_TYPE: DeviceTypeEnum.PACKET_ROUTER,
-                FilterFieldEnum.DRIVER     : DeviceDriverEnum.DEVICEDRIVER_OPENCONFIG,
+                FilterFieldEnum.DRIVER     : DeviceDriverEnum.DEVICEDRIVER_GNMI_OPENCONFIG,
             }
         ]))
 
diff --git a/src/device/service/drivers/gnmi_openconfig/DeltaSampleCache.py b/src/device/service/drivers/gnmi_openconfig/DeltaSampleCache.py
new file mode 100644
index 0000000000000000000000000000000000000000..5083082fe5694a95e95d95cd8ed72563d77dc098
--- /dev/null
+++ b/src/device/service/drivers/gnmi_openconfig/DeltaSampleCache.py
@@ -0,0 +1,35 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import copy
+from typing import Any, Dict, Tuple, Union
+
+class DeltaSampleCache:
+    def __init__(self) -> None:
+        self._previous_samples : Dict[str, Tuple[float, Union[int, float]]] = dict()
+
+    def get_delta(self, path : str, current_timestamp : float, current_value : Any) -> None:
+        previous_sample = copy.deepcopy(self._previous_samples.get(path))
+        self._previous_samples[path] = current_timestamp, current_value
+
+        if not isinstance(current_value, (int, float)): return None
+        if previous_sample is None: return current_timestamp, 0
+        previous_timestamp, previous_value = previous_sample
+        if not isinstance(previous_value, (int, float)): return None
+
+        delta_value = max(0, current_value - previous_value)
+        delay = current_timestamp - previous_timestamp
+        delta_sample = current_timestamp, delta_value / delay
+
+        return delta_sample
diff --git a/src/device/service/drivers/gnmi_openconfig/GnmiOpenConfigDriver.py b/src/device/service/drivers/gnmi_openconfig/GnmiOpenConfigDriver.py
new file mode 100644
index 0000000000000000000000000000000000000000..882c0de07440c3b89c4a82f522d08155329b5d7e
--- /dev/null
+++ b/src/device/service/drivers/gnmi_openconfig/GnmiOpenConfigDriver.py
@@ -0,0 +1,100 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging, queue, threading
+from typing import Any, Iterator, List, Optional, Tuple, Union
+from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method
+from common.type_checkers.Checkers import chk_type
+from device.service.driver_api._Driver import _Driver
+from .GnmiSessionHandler import GnmiSessionHandler
+
+DRIVER_NAME = 'gnmi_openconfig'
+METRICS_POOL = MetricsPool('Device', 'Driver', labels={'driver': DRIVER_NAME})
+
+class GnmiOpenConfigDriver(_Driver):
+    def __init__(self, address : str, port : int, **settings) -> None:
+        super().__init__(DRIVER_NAME, address, port, **settings)
+        self.__logger = logging.getLogger('{:s}:[{:s}:{:s}]'.format(str(__name__), str(self.address), str(self.port)))
+        self.__lock = threading.Lock()
+        self.__started = threading.Event()
+        self.__terminate = threading.Event()
+        self.__handler = GnmiSessionHandler(self.address, self.port, settings, self.__logger)
+        self.__out_samples = self.__handler.out_samples
+
+    def Connect(self) -> bool:
+        with self.__lock:
+            if self.__started.is_set(): return True
+            self.__handler.connect()
+            self.__started.set()
+            return True
+
+    def Disconnect(self) -> bool:
+        with self.__lock:
+            # Trigger termination of loops and processes
+            self.__terminate.set()
+            # If not started, assume it is already disconnected
+            if not self.__started.is_set(): return True
+            self.__handler.disconnect()
+            return True
+
+    @metered_subclass_method(METRICS_POOL)
+    def GetInitialConfig(self) -> List[Tuple[str, Any]]:
+        with self.__lock:
+            return []
+
+    @metered_subclass_method(METRICS_POOL)
+    def GetConfig(self, resource_keys : List[str] = []) -> List[Tuple[str, Union[Any, None, Exception]]]:
+        chk_type('resources', resource_keys, list)
+        with self.__lock:
+            return self.__handler.get(resource_keys)
+
+    @metered_subclass_method(METRICS_POOL)
+    def SetConfig(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]:
+        chk_type('resources', resources, list)
+        if len(resources) == 0: return []
+        with self.__lock:
+            return self.__handler.set(resources)
+
+    @metered_subclass_method(METRICS_POOL)
+    def DeleteConfig(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]:
+        chk_type('resources', resources, list)
+        if len(resources) == 0: return []
+        with self.__lock:
+            return self.__handler.delete(resources)
+
+    @metered_subclass_method(METRICS_POOL)
+    def SubscribeState(self, subscriptions : List[Tuple[str, float, float]]) -> List[Union[bool, Exception]]:
+        chk_type('subscriptions', subscriptions, list)
+        if len(subscriptions) == 0: return []
+        with self.__lock:
+            return self.__handler.subscribe(subscriptions)
+
+    @metered_subclass_method(METRICS_POOL)
+    def UnsubscribeState(self, subscriptions : List[Tuple[str, float, float]]) -> List[Union[bool, Exception]]:
+        chk_type('subscriptions', subscriptions, list)
+        if len(subscriptions) == 0: return []
+        with self.__lock:
+            return self.__handler.unsubscribe(subscriptions)
+
+    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/gnmi_openconfig/GnmiSessionHandler.py b/src/device/service/drivers/gnmi_openconfig/GnmiSessionHandler.py
new file mode 100644
index 0000000000000000000000000000000000000000..04dae4f5fcc6427c735b528b0ab32ba1c967709a
--- /dev/null
+++ b/src/device/service/drivers/gnmi_openconfig/GnmiSessionHandler.py
@@ -0,0 +1,332 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import copy, grpc, json, logging, queue, threading
+from typing import Any, Dict, List, Optional, Tuple, Union
+from common.tools.grpc.Tools import grpc_message_to_json_string
+from common.type_checkers.Checkers import chk_float, chk_length, chk_string, chk_type
+from .gnmi.gnmi_pb2_grpc import gNMIStub
+from .gnmi.gnmi_pb2 import Encoding, GetRequest, SetRequest, UpdateResult   # pylint: disable=no-name-in-module
+from .handlers import ALL_RESOURCE_KEYS, compose, get_path, parse
+from .tools.Capabilities import get_supported_encodings
+from .tools.Channel import get_grpc_channel
+from .tools.Path import path_from_string, path_to_string #, compose_path
+from .tools.Subscriptions import Subscriptions
+from .tools.Value import decode_value #, value_exists
+from .MonitoringThread import MonitoringThread
+
+class GnmiSessionHandler:
+    def __init__(self, address : str, port : int, settings : Dict, logger : logging.Logger) -> None:
+        self._address   = address
+        self._port      = port
+        self._settings  = copy.deepcopy(settings)
+        self._logger    = logger
+        self._lock      = threading.Lock()
+        self._connected = threading.Event()
+        self._username  = settings.get('username')
+        self._password  = settings.get('password')
+        self._use_tls   = settings.get('use_tls', False)
+        self._channel : Optional[grpc.Channel] = None
+        self._stub : Optional[gNMIStub] = None
+        self._monit_thread = None
+        self._supported_encodings = None
+        self._subscriptions = Subscriptions()
+        self._in_subscriptions = queue.Queue()
+        self._out_samples = queue.Queue()
+
+    @property
+    def subscriptions(self): return self._subscriptions
+
+    @property
+    def in_subscriptions(self): return self._in_subscriptions
+
+    @property
+    def out_samples(self): return self._out_samples
+
+    def connect(self):
+        with self._lock:
+            self._channel = get_grpc_channel(self._address, self._port, self._use_tls, self._logger)
+            self._stub = gNMIStub(self._channel)
+            self._supported_encodings = get_supported_encodings(
+                self._stub, self._username, self._password, timeout=120)
+            self._monit_thread = MonitoringThread(
+                self._stub, self._logger, self._settings, self._in_subscriptions, self._out_samples)
+            self._monit_thread.start()
+            self._connected.set()
+
+    def disconnect(self):
+        if not self._connected.is_set(): return
+        with self._lock:
+            self._monit_thread.stop()
+            self._monit_thread.join()
+            self._channel.close()
+            self._connected.clear()
+
+    def get(self, resource_keys : List[str]) -> List[Tuple[str, Union[Any, None, Exception]]]:
+        if len(resource_keys) == 0: resource_keys = ALL_RESOURCE_KEYS
+        chk_type('resources', resource_keys, list)
+
+        parsing_results = []
+
+        get_request = GetRequest()
+        get_request.type = GetRequest.DataType.ALL
+        get_request.encoding = Encoding.JSON_IETF
+        #get_request.use_models.add() # kept empty: return for all models supported
+        for i,resource_key in enumerate(resource_keys):
+            str_resource_name = 'resource_key[#{:d}]'.format(i)
+            try:
+                chk_string(str_resource_name, resource_key, allow_empty=False)
+                self._logger.debug('[GnmiSessionHandler:get] resource_key = {:s}'.format(str(resource_key)))
+                str_path = get_path(resource_key)
+                self._logger.debug('[GnmiSessionHandler:get] str_path = {:s}'.format(str(str_path)))
+                get_request.path.append(path_from_string(str_path))
+            except Exception as e: # pylint: disable=broad-except
+                MSG = 'Exception parsing {:s}: {:s}'
+                self._logger.exception(MSG.format(str_resource_name, str(resource_key)))
+                parsing_results.append((resource_key, e)) # if validation fails, store the exception
+
+        if len(parsing_results) > 0:
+            return parsing_results
+
+        metadata = [('username', self._username), ('password', self._password)]
+        timeout = None # GNMI_SUBSCRIPTION_TIMEOUT = int(sampling_duration)
+        get_reply = self._stub.Get(get_request, metadata=metadata, timeout=timeout)
+        #self._logger.info('get_reply={:s}'.format(grpc_message_to_json_string(get_reply)))
+
+        results = []
+        #results[str_filter] = [i, None, False]  # (index, value, processed?)
+
+        for notification in get_reply.notification:
+            #for delete_path in notification.delete:
+            #    self._logger.info('delete_path={:s}'.format(grpc_message_to_json_string(delete_path)))
+            #    str_path = path_to_string(delete_path)
+            #    resource_key_tuple = results.get(str_path)
+            #    if resource_key_tuple is None:
+            #        # pylint: disable=broad-exception-raised
+            #        MSG = 'Unexpected Delete Path({:s}); requested resource_keys({:s})'
+            #        raise Exception(MSG.format(str(str_path), str(resource_keys)))
+            #    resource_key_tuple[2] = True
+
+            for update in notification.update:
+                #self._logger.info('update={:s}'.format(grpc_message_to_json_string(update)))
+                str_path = path_to_string(update.path)
+                #resource_key_tuple = results.get(str_path)
+                #if resource_key_tuple is None:
+                #    # pylint: disable=broad-exception-raised
+                #    MSG = 'Unexpected Update Path({:s}); requested resource_keys({:s})'
+                #    raise Exception(MSG.format(str(str_path), str(resource_keys)))
+                try:
+                    value = decode_value(update.val)
+                    #resource_key_tuple[1] = value
+                    #resource_key_tuple[2] = True
+                    results.extend(parse(str_path, value))
+                except Exception as e: # pylint: disable=broad-except
+                    MSG = 'Exception processing notification {:s}'
+                    self._logger.exception(MSG.format(grpc_message_to_json_string(notification)))
+                    results.append((str_path, e)) # if validation fails, store the exception
+
+        #_results = sorted(results.items(), key=lambda x: x[1][0])
+        #results = list()
+        #for resource_key,resource_key_tuple in _results:
+        #    _, value, processed = resource_key_tuple
+        #    value = value if processed else Exception('Not Processed')
+        #    results.append((resource_key, value))
+        return results
+
+    def set(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]:
+        #resource_keys = [key for key,_ in resources]
+        #current_values = self.get(resource_keys)
+
+        #resource_tuples = {
+        #    resource_key : [i, value, value_exists(value), None]
+        #    for i,(resource_key,value) in enumerate(current_values)
+        #}
+
+        #self._logger.info('---0')
+        #self._logger.info(str(resource_tuples))
+
+        set_request = SetRequest()
+        #for resource_key in resource_keys:
+        for resource_key, resource_value in resources:
+            self._logger.info('---1')
+            self._logger.info(str(resource_key))
+            self._logger.info(str(resource_value))
+            #resource_tuple = resource_tuples.get(resource_key)
+            #if resource_tuple is None: continue
+            #_, value, exists, operation_done = resource_tuple
+            if isinstance(resource_value, str): resource_value = json.loads(resource_value)
+            str_path, str_data = compose(resource_key, resource_value, delete=False)
+            self._logger.info('---3')
+            self._logger.info(str(str_path))
+            self._logger.info(str(str_data))
+            set_request_list = set_request.update #if exists else set_request.replace
+            set_request_entry = set_request_list.add()
+            set_request_entry.path.CopyFrom(path_from_string(str_path))
+            set_request_entry.val.json_val = str_data.encode('UTF-8')
+
+        self._logger.info('set_request={:s}'.format(grpc_message_to_json_string(set_request)))
+        metadata = [('username', self._username), ('password', self._password)]
+        timeout = None # GNMI_SUBSCRIPTION_TIMEOUT = int(sampling_duration)
+        set_reply = self._stub.Set(set_request, metadata=metadata, timeout=timeout)
+        self._logger.info('set_reply={:s}'.format(grpc_message_to_json_string(set_reply)))
+
+        results = []
+        for (resource_key, resource_value), update_result in zip(resources, set_reply.response):
+            operation = update_result.op
+            if operation == UpdateResult.UPDATE:
+                results.append((resource_key, True))
+            else:
+                results.append((resource_key, Exception('Unexpected')))
+
+            #str_path = path_to_string(update_result.path)
+            #resource_tuple = resource_tuples.get(str_path)
+            #if resource_tuple is None: continue
+            #resource_tuple[3] = operation
+
+        #resource_tuples = sorted(resource_tuples.items(), key=lambda x: x[1][0])
+        #results = list()
+        #for resource_key,resource_tuple in resource_tuples:
+        #    _, _, exists, operation_done = resource_tuple
+        #    desired_operation = 'update' if exists else 'replace'
+        #
+        #    if operation_done == UpdateResult.INVALID:
+        #        value = Exception('Invalid')
+        #    elif operation_done == UpdateResult.DELETE:
+        #        value = Exception('Unexpected Delete')
+        #    elif operation_done == UpdateResult.REPLACE:
+        #        value = True if desired_operation == 'replace' else Exception('Failed')
+        #    elif operation_done == UpdateResult.UPDATE:
+        #        value = True if desired_operation == 'update' else Exception('Failed')
+        #    else:
+        #        value = Exception('Unexpected')
+        #    results.append((resource_key, value))
+        return results
+
+    def delete(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]:
+        #resource_keys = [key for key,_ in resources]
+        #current_values = self.get(resource_keys)
+
+        #resource_tuples = {
+        #    resource_key : [i, value, value_exists(value), None]
+        #    for i,(resource_key,value) in enumerate(current_values)
+        #}
+
+        #self._logger.info('---0')
+        #self._logger.info(str(resource_tuples))
+
+        set_request = SetRequest()
+        #for resource_key in resource_keys:
+        for resource_key, resource_value in resources:
+            self._logger.info('---1')
+            self._logger.info(str(resource_key))
+            self._logger.info(str(resource_value))
+            #resource_tuple = resource_tuples.get(resource_key)
+            #if resource_tuple is None: continue
+            #_, value, exists, operation_done = resource_tuple
+            #if not exists: continue
+            if isinstance(resource_value, str): resource_value = json.loads(resource_value)
+            str_path, str_data = compose(resource_key, resource_value, delete=True)
+            self._logger.info('---3')
+            self._logger.info(str(str_path))
+            self._logger.info(str(str_data))
+            set_request_entry = set_request.delete.add()
+            set_request_entry.CopyFrom(path_from_string(str_path))
+
+        self._logger.info('set_request={:s}'.format(grpc_message_to_json_string(set_request)))
+        metadata = [('username', self._username), ('password', self._password)]
+        timeout = None # GNMI_SUBSCRIPTION_TIMEOUT = int(sampling_duration)
+        set_reply = self._stub.Set(set_request, metadata=metadata, timeout=timeout)
+        self._logger.info('set_reply={:s}'.format(grpc_message_to_json_string(set_reply)))
+
+        results = []
+        for (resource_key, resource_value), update_result in zip(resources, set_reply.response):
+            operation = update_result.op
+            if operation == UpdateResult.DELETE:
+                results.append((resource_key, True))
+            else:
+                results.append((resource_key, Exception('Unexpected')))
+
+            #str_path = path_to_string(update_result.path)
+            #resource_tuple = resource_tuples.get(str_path)
+            #if resource_tuple is None: continue
+            #resource_tuple[3] = operation
+
+        #resource_tuples = sorted(resource_tuples.items(), key=lambda x: x[1][0])
+        #results = list()
+        #for resource_key,resource_tuple in resource_tuples:
+        #    _, _, exists, operation_done = resource_tuple
+        #    if operation_done == UpdateResult.INVALID:
+        #        value = Exception('Invalid')
+        #    elif operation_done == UpdateResult.DELETE:
+        #        value = True
+        #    elif operation_done == UpdateResult.REPLACE:
+        #        value = Exception('Unexpected Replace')
+        #    elif operation_done == UpdateResult.UPDATE:
+        #        value = Exception('Unexpected Update')
+        #    else:
+        #        value = Exception('Unexpected')
+        #    results.append((resource_key, value))
+        return results
+
+    def subscribe(self, subscriptions : List[Tuple[str, float, float]]) -> List[Union[bool, Exception]]:
+        results = []
+        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)
+                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
+                MSG = 'Exception validating {:s}: {:s}'
+                self._logger.exception(MSG.format(str_subscription_name, str(resource_key)))
+                results.append(e) # if validation fails, store the exception
+                continue
+
+            #resource_path = resource_key.split('/')
+            #self._subscriptions.add(resource_path, sampling_duration, sampling_interval, reference)
+            subscription = 'subscribe', resource_key, sampling_duration, sampling_interval
+            self._in_subscriptions.put_nowait(subscription)
+            results.append(True)
+        return results
+
+    def unsubscribe(self, subscriptions : List[Tuple[str, float, float]]) -> List[Union[bool, Exception]]:
+        results = []
+        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)
+                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
+                MSG = 'Exception validating {:s}: {:s}'
+                self._logger.exception(MSG.format(str_subscription_name, str(resource_key)))
+                results.append(e) # if validation fails, store the exception
+                continue
+
+            #resource_path = resource_key.split('/')
+            #reference = self._subscriptions.get(resource_path, sampling_duration, sampling_interval)
+            #if reference is None:
+            #    results.append(False)
+            #    continue
+            #self._subscriptions.delete(reference)
+            subscription = 'unsubscribe', resource_key, sampling_duration, sampling_interval
+            self._in_subscriptions.put_nowait(subscription)
+            results.append(True)
+        return results
diff --git a/src/device/service/drivers/gnmi_openconfig/MonitoringThread.py b/src/device/service/drivers/gnmi_openconfig/MonitoringThread.py
new file mode 100644
index 0000000000000000000000000000000000000000..7cbd0da87d15b6fac0ea7f4a5de3c02259a07dc8
--- /dev/null
+++ b/src/device/service/drivers/gnmi_openconfig/MonitoringThread.py
@@ -0,0 +1,171 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Ref: https://github.com/openconfig/reference/blob/master/rpc/gnmi/gnmi-specification.md
+# Ref: https://github.com/openconfig/gnmi/blob/master/proto/gnmi/gnmi.proto
+
+from __future__ import annotations
+import grpc, logging, queue, re, threading
+from collections.abc import Iterator
+from datetime import datetime
+from typing import Dict
+from common.tools.grpc.Tools import grpc_message_to_json_string
+from .gnmi.gnmi_pb2 import ( # pylint: disable=no-name-in-module
+    QOSMarking, SubscribeRequest, Subscription, SubscriptionList, SubscriptionMode
+)
+from .gnmi.gnmi_pb2_grpc import gNMIStub
+from .tools.Path import path_from_string, path_to_string
+from .DeltaSampleCache import DeltaSampleCache
+
+
+LOGGER = logging.getLogger(__name__)
+
+# SubscriptionList Mode: Mode of the subscription.
+#  STREAM = 0: Values streamed by the target. gNMI Specification Section 3.5.1.5.2
+#  ONCE   = 1: Values sent once-off by the target. gNMI Specification Section 3.5.1.5.1
+#  POLL   = 2: Values sent in response to a poll request. gNMI Specification Section 3.5.1.5.3
+GNMI_SUBSCRIPTION_LIST_MODE = SubscriptionList.Mode.STREAM
+
+# Path Prefix: Prefix used for paths.
+GNMI_PATH_PREFIX = None
+
+# QOS MArking: DSCP marking to be used.
+GNMI_QOS_MARKING = None
+
+# Allow Aggregation: Whether elements of the schema that are marked as eligible for aggregation
+# should be aggregated or not.
+GNMI_ALLOW_AGGREGATION = False
+
+# Encoding: The encoding that the target should use within the Notifications generated
+# corresponding to the SubscriptionList.
+GNMI_ENCODING = 'JSON'
+
+#Subscription Mode: The mode of the subscription, specifying how the target must return values
+# in a subscription. gNMI Specification Section 3.5.1.3
+#  TARGET_DEFINED = 0: The target selects the relevant mode for each element.
+#  ON_CHANGE      = 1: The target sends an update on element value change.
+#  SAMPLE         = 2: The target samples values according to the interval.
+GNMI_SUBSCRIPTION_MODE = SubscriptionMode.SAMPLE
+
+# Suppress Redundant: Indicates whether values that have not changed should be sent in a SAMPLE
+# subscription. gNMI Specification Section 3.5.1.3
+GNMI_SUPPRESS_REDUNDANT = False
+
+# Heartbeat Interval: Specifies the maximum allowable silent period in nanoseconds when
+# suppress_redundant is in use. The target should send a value at least once in the period
+# specified. gNMI Specification Section 3.5.1.3
+GNMI_HEARTBEAT_INTERVAL = 10 # seconds
+
+GNMI_SUBSCRIPTION_TIMEOUT = None
+
+class MonitoringThread(threading.Thread):
+    def __init__(
+        self, stub : gNMIStub, logger : logging.Logger, settings : Dict,
+        in_subscriptions : queue.Queue, out_samples : queue.Queue
+    ) -> None:
+        super().__init__(daemon=True)
+        self._terminate = threading.Event()
+        self._stub = stub
+        self._logger = logger
+        self._username = settings.get('username')
+        self._password = settings.get('password')
+        self._in_subscriptions = in_subscriptions
+        self._out_samples = out_samples
+        self._response_iterator = None
+        self._delta_sample_cache = DeltaSampleCache()
+
+    def stop(self) -> None:
+        self._terminate.set()
+        if self._response_iterator is not None:
+            self._response_iterator.cancel()
+
+    def generate_requests(self) -> Iterator[SubscribeRequest]:
+        subscriptions = []
+        while not self._terminate.is_set():
+            try:
+                subscription = self._in_subscriptions.get(block=True, timeout=0.1)
+                operation, resource_key, sampling_duration, sampling_interval = subscription   # pylint: disable=unused-variable
+                if operation != 'subscribe': continue # Unsubscribe not supported by gNM, needs to cancel entire connection
+                # options.timeout = int(sampling_duration)
+                #_path = parse_xpath(resource_key)
+                path = path_from_string(resource_key)
+                subscription = Subscription(
+                    path=path, mode=GNMI_SUBSCRIPTION_MODE, suppress_redundant=GNMI_SUPPRESS_REDUNDANT,
+                    sample_interval=int(sampling_interval * 1000000000),
+                    heartbeat_interval=int(GNMI_HEARTBEAT_INTERVAL * 1000000000))
+                subscriptions.append(subscription)
+            except queue.Empty:
+                if len(subscriptions) == 0: continue
+                #self._logger.warning('[generate_requests] process')
+                prefix = path_from_string(GNMI_PATH_PREFIX) if GNMI_PATH_PREFIX is not None else None
+                qos = QOSMarking(marking=GNMI_QOS_MARKING) if GNMI_QOS_MARKING is not None else None
+                subscriptions_list = SubscriptionList(
+                    prefix=prefix, mode=GNMI_SUBSCRIPTION_LIST_MODE, allow_aggregation=GNMI_ALLOW_AGGREGATION,
+                    encoding=GNMI_ENCODING, subscription=subscriptions, qos=qos)
+                subscribe_request = SubscribeRequest(subscribe=subscriptions_list)
+                #str_subscribe_request = grpc_message_to_json_string(subscribe_request)
+                #self._logger.warning('[generate_requests] subscribe_request={:s}'.format(str_subscribe_request))
+                yield subscribe_request
+                subscriptions = []
+            except: # pylint: disable=bare-except
+                self._logger.exception('[generate_requests] Unhandled Exception')
+
+    def run(self) -> None:
+        # Add a dummy subscription to be used as keep-alive
+        # usable only with SRLinux native data models
+        #subscription = ('/system/name/host-name', None, 1)
+        #self._in_subscriptions.put_nowait(subscription)
+
+        try:
+            request_iterator = self.generate_requests()
+            metadata = [('username', self._username), ('password', self._password)]
+            timeout = None # GNMI_SUBSCRIPTION_TIMEOUT = int(sampling_duration)
+            self._response_iterator = self._stub.Subscribe(request_iterator, metadata=metadata, timeout=timeout)
+            for subscribe_response in self._response_iterator:
+                str_subscribe_response = grpc_message_to_json_string(subscribe_response)
+                self._logger.warning('[run] subscribe_response={:s}'.format(str_subscribe_response))
+                update = subscribe_response.update
+                timestamp_device = float(update.timestamp) / 1.e9
+                timestamp_local = datetime.timestamp(datetime.utcnow())
+                # if difference between timestamp from device and local is lower than 1 second
+                if abs(timestamp_device - timestamp_local) <= 1:
+                    # assume clocks are synchronized, use timestamp from device
+                    timestamp = timestamp_device
+                else:
+                    # might be clocks are not synchronized, use local timestamp
+                    timestamp = timestamp_local
+                for update_entry in update.update:
+                    str_path = path_to_string(update_entry.path)
+                    #if str_path != '/system/name/host-name': continue
+                    #counter_name = update_entry.path[-1].name
+                    value_type = update_entry.val.WhichOneof('value')
+                    value = getattr(update_entry.val, value_type)
+                    if re.match(r'^[0-9]+$', value) is not None:
+                        value = int(value)
+                    elif re.match(r'^[0-9]*\.[0-9]*$', value) is not None:
+                        value = float(value)
+                    else:
+                        value = str(value)
+                    delta_sample = self._delta_sample_cache.get_delta(str_path, timestamp, value)
+                    if delta_sample is None:
+                        sample = (timestamp, str_path, value)
+                    else:
+                        sample = (delta_sample[0], str_path, delta_sample[1])
+                    self._logger.warning('[run] sample={:s}'.format(str(sample)))
+                    self._out_samples.put_nowait(sample)
+        except grpc.RpcError as e:
+            if e.code() != grpc.StatusCode.CANCELLED: raise                 # pylint: disable=no-member
+            if e.details() != 'Locally cancelled by application!': raise    # pylint: disable=no-member
+        except: # pylint: disable=bare-except
+            self._logger.exception('Unhandled Exception')
diff --git a/src/device/service/drivers/gnmi_openconfig/__init__.py b/src/device/service/drivers/gnmi_openconfig/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..1549d9811aa5d1c193a44ad45d0d7773236c0612
--- /dev/null
+++ b/src/device/service/drivers/gnmi_openconfig/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/device/service/drivers/gnmi_openconfig/gnmi/Acknowledgement.txt b/src/device/service/drivers/gnmi_openconfig/gnmi/Acknowledgement.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a004e1f58bdea3ba78263a06e6e473f593e1b390
--- /dev/null
+++ b/src/device/service/drivers/gnmi_openconfig/gnmi/Acknowledgement.txt
@@ -0,0 +1,25 @@
+This code is partially based on:
+https://github.com/nokia/pygnmi/blob/master/gNMI_Subscribe.py
+
+
+MIT License
+
+Copyright (c) 2017 Nokia
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
diff --git a/src/device/service/drivers/gnmi_openconfig/gnmi/__init__.py b/src/device/service/drivers/gnmi_openconfig/gnmi/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..1549d9811aa5d1c193a44ad45d0d7773236c0612
--- /dev/null
+++ b/src/device/service/drivers/gnmi_openconfig/gnmi/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/device/service/drivers/gnmi_openconfig/gnmi/gnmi.proto b/src/device/service/drivers/gnmi_openconfig/gnmi/gnmi.proto
new file mode 100644
index 0000000000000000000000000000000000000000..292880a55586f5a5c05b0edcfc0f2f73baf7be13
--- /dev/null
+++ b/src/device/service/drivers/gnmi_openconfig/gnmi/gnmi.proto
@@ -0,0 +1,501 @@
+syntax = "proto3";
+
+import "google/protobuf/any.proto";
+import "google/protobuf/descriptor.proto";
+// import gnmi_ext.proto;
+
+// Package gNMI defines a service specification for the gRPC Network Management
+// Interface. This interface is defined to be a standard interface via which
+// a network management system ("client") can subscribe to state values,
+// retrieve snapshots of state information, and manipulate the state of a data
+// tree supported by a device ("target").
+//
+// This document references the gNMI Specification which can be found at
+// http://github.com/openconfig/reference/blob/master/rpc/gnmi
+package gnmi;
+
+// Define a protobuf FileOption that defines the gNMI service version.
+extend google.protobuf.FileOptions {
+  // The gNMI service semantic version.
+  string gnmi_service = 1001;
+}
+
+// gNMI_service is the current version of the gNMI service, returned through
+// the Capabilities RPC.
+option (gnmi_service) = "0.8.0";
+
+option go_package = "github.com/openconfig/gnmi/proto/gnmi";
+option java_multiple_files = true;
+option java_outer_classname = "GnmiProto";
+option java_package = "com.github.gnmi.proto";
+
+
+service gNMI {
+  // Capabilities allows the client to retrieve the set of capabilities that
+  // is supported by the target. This allows the target to validate the
+  // service version that is implemented and retrieve the set of models that
+  // the target supports. The models can then be specified in subsequent RPCs
+  // to restrict the set of data that is utilized.
+  // Reference: gNMI Specification Section 3.2
+  rpc Capabilities(CapabilityRequest) returns (CapabilityResponse);
+  // Retrieve a snapshot of data from the target. A Get RPC requests that the
+  // target snapshots a subset of the data tree as specified by the paths
+  // included in the message and serializes this to be returned to the
+  // client using the specified encoding.
+  // Reference: gNMI Specification Section 3.3
+  rpc Get(GetRequest) returns (GetResponse);
+  // Set allows the client to modify the state of data on the target. The
+  // paths to modified along with the new values that the client wishes
+  // to set the value to.
+  // Reference: gNMI Specification Section 3.4
+  rpc Set(SetRequest) returns (SetResponse);
+  // Subscribe allows a client to request the target to send it values
+  // of particular paths within the data tree. These values may be streamed
+  // at a particular cadence (STREAM), sent one off on a long-lived channel
+  // (POLL), or sent as a one-off retrieval (ONCE).
+  // Reference: gNMI Specification Section 3.5
+  rpc Subscribe(stream SubscribeRequest) returns (stream SubscribeResponse);
+}
+// The Extension message contains a single gNMI extension.
+message Extension {
+  oneof ext {
+    RegisteredExtension registered_ext = 1;    // A registered extension.
+    // Well known extensions.
+    MasterArbitration master_arbitration = 2;  // Master arbitration extension.
+    History history = 3;                       // History extension.
+  }
+}
+
+// The RegisteredExtension message defines an extension which is defined outside
+// of this file.
+message RegisteredExtension {
+  ExtensionID id = 1; // The unique ID assigned to this extension.
+  bytes msg = 2;      // The binary-marshalled protobuf extension payload.
+}
+
+// RegisteredExtension is an enumeration acting as a registry for extensions
+// defined by external sources.
+enum ExtensionID {
+  EID_UNSET = 0;
+  // New extensions are to be defined within this enumeration - their definition
+  // MUST link to a reference describing their implementation.
+
+  // An experimental extension that may be used during prototyping of a new
+  // extension.
+  EID_EXPERIMENTAL = 999;
+}
+
+// MasterArbitration is used to select the master among multiple gNMI clients
+// with the same Roles. The client with the largest election_id is honored as
+// the master.
+// The document about gNMI master arbitration can be found at
+// https://github.com/openconfig/reference/blob/master/rpc/gnmi/gnmi-master-arbitration.md
+message MasterArbitration {
+  Role role = 1;
+  Uint128 election_id = 2;
+}
+
+// Representation of unsigned 128-bit integer.
+message Uint128 {
+  uint64 high = 1;
+  uint64 low = 2;
+}
+
+// There can be one master for each role. The role is identified by its id.
+message Role {
+  string id = 1;
+  // More fields can be added if needed, for example, to specify what paths the
+  // role can read/write.
+}
+
+// The History extension allows clients to request historical data. Its
+// spec can be found at
+// https://github.com/openconfig/reference/blob/master/rpc/gnmi/gnmi-history.md
+message History {
+  oneof request {
+    int64 snapshot_time = 1; // Nanoseconds since the epoch
+    TimeRange range = 2;
+  }
+}
+
+message TimeRange {
+  int64 start = 1; // Nanoseconds since the epoch
+  int64 end = 2;   // Nanoseconds since the epoch
+}
+// Notification is a re-usable message that is used to encode data from the
+// target to the client. A Notification carries two types of changes to the data
+// tree:
+//  - Deleted values (delete) - a set of paths that have been removed from the
+//    data tree.
+//  - Updated values (update) - a set of path-value pairs indicating the path
+//    whose value has changed in the data tree.
+// Reference: gNMI Specification Section 2.1
+message Notification {
+  int64 timestamp = 1;          // Timestamp in nanoseconds since Epoch.
+  Path prefix = 2;              // Prefix used for paths in the message.
+  repeated Update update = 4;   // Data elements that have changed values.
+  repeated Path delete = 5;     // Data elements that have been deleted.
+  // This notification contains a set of paths that are always updated together
+  // referenced by a globally unique prefix.
+  bool atomic = 6;
+  // Reserved field numbers and identifiers.
+  reserved "alias";
+  reserved 3;
+}
+
+// Update is a re-usable message that is used to store a particular Path,
+// Value pair.
+// Reference: gNMI Specification Section 2.1
+message Update {
+  Path path = 1;                      // The path (key) for the update.
+  Value value = 2 [deprecated=true];  // The value (value) for the update.
+  TypedValue val = 3;                 // The explicitly typed update value.
+  uint32 duplicates = 4;              // Number of coalesced duplicates.
+}
+
+// TypedValue is used to encode a value being sent between the client and
+// target (originated by either entity).
+message TypedValue {
+  // One of the fields within the val oneof is populated with the value
+  // of the update. The type of the value being included in the Update
+  // determines which field should be populated. In the case that the
+  // encoding is a particular form of the base protobuf type, a specific
+  // field is used to store the value (e.g., json_val).
+  oneof value {
+    string string_val = 1;            // String value.
+    int64 int_val = 2;                // Integer value.
+    uint64 uint_val = 3;              // Unsigned integer value.
+    bool bool_val = 4;                // Bool value.
+    bytes bytes_val = 5;              // Arbitrary byte sequence value.
+    float float_val = 6 [deprecated=true]; // Deprecated - use double_val.
+    double double_val = 14;           // Floating point value.
+    Decimal64 decimal_val = 7 [deprecated=true]; // Deprecated - use double_val.
+    ScalarArray leaflist_val = 8;     // Mixed type scalar array value.
+    google.protobuf.Any any_val = 9;  // protobuf.Any encoded bytes.
+    bytes json_val = 10;              // JSON-encoded text.
+    bytes json_ietf_val = 11;         // JSON-encoded text per RFC7951.
+    string ascii_val = 12;            // Arbitrary ASCII text.
+    // Protobuf binary encoded bytes. The message type is not included.
+    // See the specification at
+    // github.com/openconfig/reference/blob/master/rpc/gnmi/protobuf-vals.md
+    // for a complete specification. [Experimental]
+    bytes proto_bytes = 13;
+  }
+}
+
+// Path encodes a data tree path as a series of repeated strings, with
+// each element of the path representing a data tree node name and the
+// associated attributes.
+// Reference: gNMI Specification Section 2.2.2.
+message Path {
+  // Elements of the path are no longer encoded as a string, but rather within
+  // the elem field as a PathElem message.
+  repeated string element = 1 [deprecated=true];
+  string origin = 2;                              // Label to disambiguate path.
+  repeated PathElem elem = 3;                     // Elements of the path.
+  string target = 4;                              // The name of the target
+                                                  // (Sec. 2.2.2.1)
+}
+
+// PathElem encodes an element of a gNMI path, along with any attributes (keys)
+// that may be associated with it.
+// Reference: gNMI Specification Section 2.2.2.
+message PathElem {
+  string name = 1;                    // The name of the element in the path.
+  map<string, string> key = 2;        // Map of key (attribute) name to value.
+}
+
+// Value encodes a data tree node's value - along with the way in which
+// the value is encoded. This message is deprecated by gNMI 0.3.0.
+// Reference: gNMI Specification Section 2.2.3.
+message Value {
+  option deprecated = true;
+  bytes value = 1;      // Value of the variable being transmitted.
+  Encoding type = 2;    // Encoding used for the value field.
+}
+
+// Encoding defines the value encoding formats that are supported by the gNMI
+// protocol. These encodings are used by both the client (when sending Set
+// messages to modify the state of the target) and the target when serializing
+// data to be returned to the client (in both Subscribe and Get RPCs).
+// Reference: gNMI Specification Section 2.3
+enum Encoding {
+  JSON = 0;           // JSON encoded text.
+  BYTES = 1;          // Arbitrarily encoded bytes.
+  PROTO = 2;          // Encoded according to scalar values of TypedValue.
+  ASCII = 3;          // ASCII text of an out-of-band agreed format.
+  JSON_IETF = 4;      // JSON encoded text as per RFC7951.
+}
+
+// Error message previously utilised to return errors to the client. Deprecated
+// in favour of using the google.golang.org/genproto/googleapis/rpc/status
+// message in the RPC response.
+// Reference: gNMI Specification Section 2.5
+message Error {
+  option deprecated = true;
+  uint32 code = 1;                // Canonical gRPC error code.
+  string message = 2;             // Human readable error.
+  google.protobuf.Any data = 3;   // Optional additional information.
+}
+
+// Decimal64 is used to encode a fixed precision decimal number. The value
+// is expressed as a set of digits with the precision specifying the
+// number of digits following the decimal point in the digit set.
+// This message is deprecated in favor of encoding all floating point types
+// as double precision.
+message Decimal64 {
+  option deprecated = true;
+  int64 digits = 1;         // Set of digits.
+  uint32 precision = 2;     // Number of digits following the decimal point.
+}
+
+// ScalarArray is used to encode a mixed-type array of values.
+message ScalarArray {
+  // The set of elements within the array. Each TypedValue message should
+  // specify only elements that have a field identifier of 1-7 (i.e., the
+  // values are scalar values).
+  repeated TypedValue element = 1;
+}
+
+// SubscribeRequest is the message sent by the client to the target when
+// initiating a subscription to a set of paths within the data tree. The
+// request field must be populated and the initial message must specify a
+// SubscriptionList to initiate a subscription.
+// Reference: gNMI Specification Section 3.5.1.1
+message SubscribeRequest {
+  oneof request {
+    SubscriptionList subscribe = 1; // Specify the paths within a subscription.
+    Poll poll = 3;                  // Trigger a polled update.
+  }
+  // Extension messages associated with the SubscribeRequest. See the
+  // gNMI extension specification for further definition.
+  repeated Extension extension = 5;
+  // Reserved field numbers and identifiers.
+  reserved 4;
+  reserved "aliases";
+}
+
+// Poll is sent within a SubscribeRequest to trigger the device to
+// send telemetry updates for the paths that are associated with the
+// subscription.
+// Reference: gNMI Specification Section Section 3.5.1.4
+message Poll {
+}
+
+// SubscribeResponse is the message used by the target within a Subscribe RPC.
+// The target includes a Notification message which is used to transmit values
+// of the path(s) that are associated with the subscription. The same message
+// is to indicate that the target has sent all data values once (is
+// synchronized).
+// Reference: gNMI Specification Section 3.5.1.4
+message SubscribeResponse {
+  oneof response {
+    Notification update = 1;          // Changed or sampled value for a path.
+    // Indicate target has sent all values associated with the subscription
+    // at least once.
+    bool sync_response = 3;
+    // Deprecated in favour of google.golang.org/genproto/googleapis/rpc/status
+    Error error = 4 [deprecated=true];
+  }
+  // Extension messages associated with the SubscribeResponse. See the
+  // gNMI extension specification for further definition.
+  repeated Extension extension = 5;
+}
+
+// SubscriptionList is used within a Subscribe message to specify the list of
+// paths that the client wishes to subscribe to. The message consists of a
+// list of (possibly prefixed) paths, and options that relate to the
+// subscription.
+// Reference: gNMI Specification Section 3.5.1.2
+message SubscriptionList {
+  Path prefix = 1;                          // Prefix used for paths.
+  repeated Subscription subscription = 2;   // Set of subscriptions to create.
+  QOSMarking qos = 4;                       // DSCP marking to be used.
+  // Mode of the subscription.
+  enum Mode {
+    STREAM = 0; // Values streamed by the target (Sec. 3.5.1.5.2).
+    ONCE = 1;   // Values sent once-off by the target (Sec. 3.5.1.5.1).
+    POLL = 2;   // Values sent in response to a poll request (Sec. 3.5.1.5.3).
+  }
+  Mode mode = 5;
+  // Whether elements of the schema that are marked as eligible for aggregation
+  // should be aggregated or not.
+  bool allow_aggregation = 6;
+  // The set of schemas that define the elements of the data tree that should
+  // be sent by the target.
+  repeated ModelData use_models = 7;
+  // The encoding that the target should use within the Notifications generated
+  // corresponding to the SubscriptionList.
+  Encoding encoding = 8;
+  // An optional field to specify that only updates to current state should be
+  // sent to a client. If set, the initial state is not sent to the client but
+  // rather only the sync message followed by any subsequent updates to the
+  // current state. For ONCE and POLL modes, this causes the server to send only
+  // the sync message (Sec. 3.5.2.3).
+  bool updates_only = 9;
+  // Reserved field numbers and identifiers.
+  reserved 3;
+  reserved "use_aliases";
+}
+
+// Subscription is a single request within a SubscriptionList. The path
+// specified is interpreted (along with the prefix) as the elements of the data
+// tree that the client is subscribing to. The mode determines how the target
+// should trigger updates to be sent.
+// Reference: gNMI Specification Section 3.5.1.3
+message Subscription {
+  Path path = 1;                    // The data tree path.
+  SubscriptionMode mode = 2;        // Subscription mode to be used.
+  uint64 sample_interval = 3;       // ns between samples in SAMPLE mode.
+  // Indicates whether values that have not changed should be sent in a SAMPLE
+  // subscription.
+  bool suppress_redundant = 4;
+  // Specifies the maximum allowable silent period in nanoseconds when
+  // suppress_redundant is in use. The target should send a value at least once
+  // in the period specified.
+  uint64 heartbeat_interval = 5;
+}
+
+// SubscriptionMode is the mode of the subscription, specifying how the
+// target must return values in a subscription.
+// Reference: gNMI Specification Section 3.5.1.3
+enum SubscriptionMode {
+  TARGET_DEFINED = 0;  // The target selects the relevant mode for each element.
+  ON_CHANGE      = 1;  // The target sends an update on element value change.
+  SAMPLE         = 2;  // The target samples values according to the interval.
+}
+
+// QOSMarking specifies the DSCP value to be set on transmitted telemetry
+// updates from the target.
+// Reference: gNMI Specification Section 3.5.1.2
+message QOSMarking {
+  uint32 marking = 1;
+}
+
+// SetRequest is sent from a client to the target to update values in the data
+// tree. Paths are either deleted by the client, or modified by means of being
+// updated, or replaced. Where a replace is used, unspecified values are
+// considered to be replaced, whereas when update is used the changes are
+// considered to be incremental. The set of changes that are specified within
+// a single SetRequest are considered to be a transaction.
+// Reference: gNMI Specification Section 3.4.1
+message SetRequest {
+  Path prefix = 1;                // Prefix used for paths in the message.
+  repeated Path delete = 2;       // Paths to be deleted from the data tree.
+  repeated Update replace = 3;    // Updates specifying elements to be replaced.
+  repeated Update update = 4;     // Updates specifying elements to updated.
+  // Extension messages associated with the SetRequest. See the
+  // gNMI extension specification for further definition.
+  repeated Extension extension = 5;
+}
+
+// SetResponse is the response to a SetRequest, sent from the target to the
+// client. It reports the result of the modifications to the data tree that were
+// specified by the client. Errors for this RPC should be reported using the
+// https://github.com/googleapis/googleapis/blob/master/google/rpc/status.proto
+// message in the RPC return. The gnmi.Error message can be used to add additional
+// details where required.
+// Reference: gNMI Specification Section 3.4.2
+message SetResponse {
+  Path prefix = 1;                      // Prefix used for paths.
+  // A set of responses specifying the result of the operations specified in
+  // the SetRequest.
+  repeated UpdateResult response = 2;
+  Error message = 3 [deprecated=true]; // The overall status of the transaction.
+  int64 timestamp = 4;                 // Timestamp of transaction (ns since epoch).
+  // Extension messages associated with the SetResponse. See the
+  // gNMI extension specification for further definition.
+  repeated Extension extension = 5;
+}
+
+// UpdateResult is used within the SetResponse message to communicate the
+// result of an operation specified within a SetRequest message.
+// Reference: gNMI Specification Section 3.4.2
+message UpdateResult {
+  // The operation that was associated with the Path specified.
+  enum Operation {
+    INVALID = 0;
+    DELETE = 1;           // The result relates to a delete of Path.
+    REPLACE = 2;          // The result relates to a replace of Path.
+    UPDATE = 3;           // The result relates to an update of Path.
+  }
+  // Deprecated timestamp for the UpdateResult, this field has been
+  // replaced by the timestamp within the SetResponse message, since
+  // all mutations effected by a set should be applied as a single
+  // transaction.
+  int64 timestamp = 1 [deprecated=true];
+  Path path = 2;                            // Path associated with the update.
+  Error message = 3 [deprecated=true];      // Status of the update operation.
+  Operation op = 4;                         // Update operation type.
+}
+
+// GetRequest is sent when a client initiates a Get RPC. It is used to specify
+// the set of data elements for which the target should return a snapshot of
+// data. The use_models field specifies the set of schema modules that are to
+// be used by the target - where use_models is not specified then the target
+// must use all schema models that it has.
+// Reference: gNMI Specification Section 3.3.1
+message GetRequest {
+  Path prefix = 1;                      // Prefix used for paths.
+  repeated Path path = 2;               // Paths requested by the client.
+  // Type of elements within the data tree.
+  enum DataType {
+    ALL = 0;                            // All data elements.
+    CONFIG = 1;                         // Config (rw) only elements.
+    STATE = 2;                          // State (ro) only elements.
+    // Data elements marked in the schema as operational. This refers to data
+    // elements whose value relates to the state of processes or interactions
+    // running on the device.
+    OPERATIONAL = 3;
+  }
+  DataType type = 3;                    // The type of data being requested.
+  Encoding encoding = 5;                // Encoding to be used.
+  repeated ModelData use_models = 6;    // The schema models to be used.
+  // Extension messages associated with the GetRequest. See the
+  // gNMI extension specification for further definition.
+  repeated Extension extension = 7;
+}
+
+// GetResponse is used by the target to respond to a GetRequest from a client.
+// The set of Notifications corresponds to the data values that are requested
+// by the client in the GetRequest.
+// Reference: gNMI Specification Section 3.3.2
+message GetResponse {
+  repeated Notification notification = 1;   // Data values.
+  Error error = 2 [deprecated=true];        // Errors that occurred in the Get.
+  // Extension messages associated with the GetResponse. See the
+  // gNMI extension specification for further definition.
+  repeated Extension extension = 3;
+}
+
+// CapabilityRequest is sent by the client in the Capabilities RPC to request
+// that the target reports its capabilities.
+// Reference: gNMI Specification Section 3.2.1
+message CapabilityRequest {
+  // Extension messages associated with the CapabilityRequest. See the
+  // gNMI extension specification for further definition.
+  repeated Extension extension = 1;
+}
+
+// CapabilityResponse is used by the target to report its capabilities to the
+// client within the Capabilities RPC.
+// Reference: gNMI Specification Section 3.2.2
+message CapabilityResponse {
+  repeated ModelData supported_models = 1;    // Supported schema models.
+  repeated Encoding supported_encodings = 2;  // Supported encodings.
+  string gNMI_version = 3;                    // Supported gNMI version.
+  // Extension messages associated with the CapabilityResponse. See the
+  // gNMI extension specification for further definition.
+  repeated Extension extension = 4;
+}
+
+// ModelData is used to describe a set of schema modules. It can be used in a
+// CapabilityResponse where a target reports the set of modules that it
+// supports, and within the SubscribeRequest and GetRequest messages to specify
+// the set of models from which data tree elements should be reported.
+// Reference: gNMI Specification Section 3.2.3
+message ModelData {
+  string name = 1;            // Name of the model.
+  string organization = 2;    // Organization publishing the model.
+  string version = 3;         // Semantic version of the model.
+}
\ No newline at end of file
diff --git a/src/device/service/drivers/gnmi_openconfig/gnmi/gnmi_ext.proto b/src/device/service/drivers/gnmi_openconfig/gnmi/gnmi_ext.proto
new file mode 100644
index 0000000000000000000000000000000000000000..9960f12afa9d19a45b3d30fd127cb27586f02607
--- /dev/null
+++ b/src/device/service/drivers/gnmi_openconfig/gnmi/gnmi_ext.proto
@@ -0,0 +1,76 @@
+syntax = "proto3";
+
+// Package gnmi_ext defines a set of extensions messages which can be optionally
+// included with the request and response messages of gNMI RPCs. A set of
+// well-known extensions are defined within this file, along with a registry for
+// extensions defined outside of this package.
+package gnmi_ext;
+
+option go_package = "github.com/openconfig/gnmi/proto/gnmi_ext";
+
+// The Extension message contains a single gNMI extension.
+message Extension {
+  oneof ext {
+    RegisteredExtension registered_ext = 1;    // A registered extension.
+    // Well known extensions.
+    MasterArbitration master_arbitration = 2;  // Master arbitration extension.
+    History history = 3;                       // History extension.
+  }
+}
+
+// The RegisteredExtension message defines an extension which is defined outside
+// of this file.
+message RegisteredExtension {
+  ExtensionID id = 1; // The unique ID assigned to this extension.
+  bytes msg = 2;      // The binary-marshalled protobuf extension payload.
+}
+
+// RegisteredExtension is an enumeration acting as a registry for extensions
+// defined by external sources.
+enum ExtensionID {
+  EID_UNSET = 0;
+  // New extensions are to be defined within this enumeration - their definition
+  // MUST link to a reference describing their implementation.
+
+  // An experimental extension that may be used during prototyping of a new
+  // extension.
+  EID_EXPERIMENTAL = 999;
+}
+
+// MasterArbitration is used to select the master among multiple gNMI clients
+// with the same Roles. The client with the largest election_id is honored as
+// the master.
+// The document about gNMI master arbitration can be found at
+// https://github.com/openconfig/reference/blob/master/rpc/gnmi/gnmi-master-arbitration.md
+message MasterArbitration {
+  Role role = 1;
+  Uint128 election_id = 2;
+}
+
+// Representation of unsigned 128-bit integer.
+message Uint128 {
+  uint64 high = 1;
+  uint64 low = 2;
+}
+
+// There can be one master for each role. The role is identified by its id.
+message Role {
+  string id = 1;
+  // More fields can be added if needed, for example, to specify what paths the
+  // role can read/write.
+}
+
+// The History extension allows clients to request historical data. Its
+// spec can be found at
+// https://github.com/openconfig/reference/blob/master/rpc/gnmi/gnmi-history.md
+message History {
+  oneof request {
+    int64 snapshot_time = 1; // Nanoseconds since the epoch
+    TimeRange range = 2;
+  }
+}
+
+message TimeRange {
+  int64 start = 1; // Nanoseconds since the epoch
+  int64 end = 2;   // Nanoseconds since the epoch
+}
\ No newline at end of file
diff --git a/src/device/service/drivers/gnmi_openconfig/gnmi/gnmi_pb2.py b/src/device/service/drivers/gnmi_openconfig/gnmi/gnmi_pb2.py
new file mode 100644
index 0000000000000000000000000000000000000000..af67f9a8ca1d4e01d1a95a0d86adbabe6d8e83ea
--- /dev/null
+++ b/src/device/service/drivers/gnmi_openconfig/gnmi/gnmi_pb2.py
@@ -0,0 +1,129 @@
+# -*- coding: utf-8 -*-
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: gnmi.proto
+"""Generated protocol buffer code."""
+from google.protobuf.internal import builder as _builder
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import descriptor_pool as _descriptor_pool
+from google.protobuf import symbol_database as _symbol_database
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+from google.protobuf import any_pb2 as google_dot_protobuf_dot_any__pb2
+from google.protobuf import descriptor_pb2 as google_dot_protobuf_dot_descriptor__pb2
+
+
+DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\ngnmi.proto\x12\x04gnmi\x1a\x19google/protobuf/any.proto\x1a google/protobuf/descriptor.proto\"\xa0\x01\n\tExtension\x12\x33\n\x0eregistered_ext\x18\x01 \x01(\x0b\x32\x19.gnmi.RegisteredExtensionH\x00\x12\x35\n\x12master_arbitration\x18\x02 \x01(\x0b\x32\x17.gnmi.MasterArbitrationH\x00\x12 \n\x07history\x18\x03 \x01(\x0b\x32\r.gnmi.HistoryH\x00\x42\x05\n\x03\x65xt\"A\n\x13RegisteredExtension\x12\x1d\n\x02id\x18\x01 \x01(\x0e\x32\x11.gnmi.ExtensionID\x12\x0b\n\x03msg\x18\x02 \x01(\x0c\"Q\n\x11MasterArbitration\x12\x18\n\x04role\x18\x01 \x01(\x0b\x32\n.gnmi.Role\x12\"\n\x0b\x65lection_id\x18\x02 \x01(\x0b\x32\r.gnmi.Uint128\"$\n\x07Uint128\x12\x0c\n\x04high\x18\x01 \x01(\x04\x12\x0b\n\x03low\x18\x02 \x01(\x04\"\x12\n\x04Role\x12\n\n\x02id\x18\x01 \x01(\t\"O\n\x07History\x12\x17\n\rsnapshot_time\x18\x01 \x01(\x03H\x00\x12 \n\x05range\x18\x02 \x01(\x0b\x32\x0f.gnmi.TimeRangeH\x00\x42\t\n\x07request\"\'\n\tTimeRange\x12\r\n\x05start\x18\x01 \x01(\x03\x12\x0b\n\x03\x65nd\x18\x02 \x01(\x03\"\x94\x01\n\x0cNotification\x12\x11\n\ttimestamp\x18\x01 \x01(\x03\x12\x1a\n\x06prefix\x18\x02 \x01(\x0b\x32\n.gnmi.Path\x12\x1c\n\x06update\x18\x04 \x03(\x0b\x32\x0c.gnmi.Update\x12\x1a\n\x06\x64\x65lete\x18\x05 \x03(\x0b\x32\n.gnmi.Path\x12\x0e\n\x06\x61tomic\x18\x06 \x01(\x08J\x04\x08\x03\x10\x04R\x05\x61lias\"u\n\x06Update\x12\x18\n\x04path\x18\x01 \x01(\x0b\x32\n.gnmi.Path\x12\x1e\n\x05value\x18\x02 \x01(\x0b\x32\x0b.gnmi.ValueB\x02\x18\x01\x12\x1d\n\x03val\x18\x03 \x01(\x0b\x32\x10.gnmi.TypedValue\x12\x12\n\nduplicates\x18\x04 \x01(\r\"\x83\x03\n\nTypedValue\x12\x14\n\nstring_val\x18\x01 \x01(\tH\x00\x12\x11\n\x07int_val\x18\x02 \x01(\x03H\x00\x12\x12\n\x08uint_val\x18\x03 \x01(\x04H\x00\x12\x12\n\x08\x62ool_val\x18\x04 \x01(\x08H\x00\x12\x13\n\tbytes_val\x18\x05 \x01(\x0cH\x00\x12\x17\n\tfloat_val\x18\x06 \x01(\x02\x42\x02\x18\x01H\x00\x12\x14\n\ndouble_val\x18\x0e \x01(\x01H\x00\x12*\n\x0b\x64\x65\x63imal_val\x18\x07 \x01(\x0b\x32\x0f.gnmi.Decimal64B\x02\x18\x01H\x00\x12)\n\x0cleaflist_val\x18\x08 \x01(\x0b\x32\x11.gnmi.ScalarArrayH\x00\x12\'\n\x07\x61ny_val\x18\t \x01(\x0b\x32\x14.google.protobuf.AnyH\x00\x12\x12\n\x08json_val\x18\n \x01(\x0cH\x00\x12\x17\n\rjson_ietf_val\x18\x0b \x01(\x0cH\x00\x12\x13\n\tascii_val\x18\x0c \x01(\tH\x00\x12\x15\n\x0bproto_bytes\x18\r \x01(\x0cH\x00\x42\x07\n\x05value\"Y\n\x04Path\x12\x13\n\x07\x65lement\x18\x01 \x03(\tB\x02\x18\x01\x12\x0e\n\x06origin\x18\x02 \x01(\t\x12\x1c\n\x04\x65lem\x18\x03 \x03(\x0b\x32\x0e.gnmi.PathElem\x12\x0e\n\x06target\x18\x04 \x01(\t\"j\n\x08PathElem\x12\x0c\n\x04name\x18\x01 \x01(\t\x12$\n\x03key\x18\x02 \x03(\x0b\x32\x17.gnmi.PathElem.KeyEntry\x1a*\n\x08KeyEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"8\n\x05Value\x12\r\n\x05value\x18\x01 \x01(\x0c\x12\x1c\n\x04type\x18\x02 \x01(\x0e\x32\x0e.gnmi.Encoding:\x02\x18\x01\"N\n\x05\x45rror\x12\x0c\n\x04\x63ode\x18\x01 \x01(\r\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\"\n\x04\x64\x61ta\x18\x03 \x01(\x0b\x32\x14.google.protobuf.Any:\x02\x18\x01\"2\n\tDecimal64\x12\x0e\n\x06\x64igits\x18\x01 \x01(\x03\x12\x11\n\tprecision\x18\x02 \x01(\r:\x02\x18\x01\"0\n\x0bScalarArray\x12!\n\x07\x65lement\x18\x01 \x03(\x0b\x32\x10.gnmi.TypedValue\"\x99\x01\n\x10SubscribeRequest\x12+\n\tsubscribe\x18\x01 \x01(\x0b\x32\x16.gnmi.SubscriptionListH\x00\x12\x1a\n\x04poll\x18\x03 \x01(\x0b\x32\n.gnmi.PollH\x00\x12\"\n\textension\x18\x05 \x03(\x0b\x32\x0f.gnmi.ExtensionB\t\n\x07requestJ\x04\x08\x04\x10\x05R\x07\x61liases\"\x06\n\x04Poll\"\xa4\x01\n\x11SubscribeResponse\x12$\n\x06update\x18\x01 \x01(\x0b\x32\x12.gnmi.NotificationH\x00\x12\x17\n\rsync_response\x18\x03 \x01(\x08H\x00\x12 \n\x05\x65rror\x18\x04 \x01(\x0b\x32\x0b.gnmi.ErrorB\x02\x18\x01H\x00\x12\"\n\textension\x18\x05 \x03(\x0b\x32\x0f.gnmi.ExtensionB\n\n\x08response\"\xd5\x02\n\x10SubscriptionList\x12\x1a\n\x06prefix\x18\x01 \x01(\x0b\x32\n.gnmi.Path\x12(\n\x0csubscription\x18\x02 \x03(\x0b\x32\x12.gnmi.Subscription\x12\x1d\n\x03qos\x18\x04 \x01(\x0b\x32\x10.gnmi.QOSMarking\x12)\n\x04mode\x18\x05 \x01(\x0e\x32\x1b.gnmi.SubscriptionList.Mode\x12\x19\n\x11\x61llow_aggregation\x18\x06 \x01(\x08\x12#\n\nuse_models\x18\x07 \x03(\x0b\x32\x0f.gnmi.ModelData\x12 \n\x08\x65ncoding\x18\x08 \x01(\x0e\x32\x0e.gnmi.Encoding\x12\x14\n\x0cupdates_only\x18\t \x01(\x08\"&\n\x04Mode\x12\n\n\x06STREAM\x10\x00\x12\x08\n\x04ONCE\x10\x01\x12\x08\n\x04POLL\x10\x02J\x04\x08\x03\x10\x04R\x0buse_aliases\"\x9f\x01\n\x0cSubscription\x12\x18\n\x04path\x18\x01 \x01(\x0b\x32\n.gnmi.Path\x12$\n\x04mode\x18\x02 \x01(\x0e\x32\x16.gnmi.SubscriptionMode\x12\x17\n\x0fsample_interval\x18\x03 \x01(\x04\x12\x1a\n\x12suppress_redundant\x18\x04 \x01(\x08\x12\x1a\n\x12heartbeat_interval\x18\x05 \x01(\x04\"\x1d\n\nQOSMarking\x12\x0f\n\x07marking\x18\x01 \x01(\r\"\xa5\x01\n\nSetRequest\x12\x1a\n\x06prefix\x18\x01 \x01(\x0b\x32\n.gnmi.Path\x12\x1a\n\x06\x64\x65lete\x18\x02 \x03(\x0b\x32\n.gnmi.Path\x12\x1d\n\x07replace\x18\x03 \x03(\x0b\x32\x0c.gnmi.Update\x12\x1c\n\x06update\x18\x04 \x03(\x0b\x32\x0c.gnmi.Update\x12\"\n\textension\x18\x05 \x03(\x0b\x32\x0f.gnmi.Extension\"\xa8\x01\n\x0bSetResponse\x12\x1a\n\x06prefix\x18\x01 \x01(\x0b\x32\n.gnmi.Path\x12$\n\x08response\x18\x02 \x03(\x0b\x32\x12.gnmi.UpdateResult\x12 \n\x07message\x18\x03 \x01(\x0b\x32\x0b.gnmi.ErrorB\x02\x18\x01\x12\x11\n\ttimestamp\x18\x04 \x01(\x03\x12\"\n\textension\x18\x05 \x03(\x0b\x32\x0f.gnmi.Extension\"\xca\x01\n\x0cUpdateResult\x12\x15\n\ttimestamp\x18\x01 \x01(\x03\x42\x02\x18\x01\x12\x18\n\x04path\x18\x02 \x01(\x0b\x32\n.gnmi.Path\x12 \n\x07message\x18\x03 \x01(\x0b\x32\x0b.gnmi.ErrorB\x02\x18\x01\x12(\n\x02op\x18\x04 \x01(\x0e\x32\x1c.gnmi.UpdateResult.Operation\"=\n\tOperation\x12\x0b\n\x07INVALID\x10\x00\x12\n\n\x06\x44\x45LETE\x10\x01\x12\x0b\n\x07REPLACE\x10\x02\x12\n\n\x06UPDATE\x10\x03\"\x93\x02\n\nGetRequest\x12\x1a\n\x06prefix\x18\x01 \x01(\x0b\x32\n.gnmi.Path\x12\x18\n\x04path\x18\x02 \x03(\x0b\x32\n.gnmi.Path\x12\'\n\x04type\x18\x03 \x01(\x0e\x32\x19.gnmi.GetRequest.DataType\x12 \n\x08\x65ncoding\x18\x05 \x01(\x0e\x32\x0e.gnmi.Encoding\x12#\n\nuse_models\x18\x06 \x03(\x0b\x32\x0f.gnmi.ModelData\x12\"\n\textension\x18\x07 \x03(\x0b\x32\x0f.gnmi.Extension\";\n\x08\x44\x61taType\x12\x07\n\x03\x41LL\x10\x00\x12\n\n\x06\x43ONFIG\x10\x01\x12\t\n\x05STATE\x10\x02\x12\x0f\n\x0bOPERATIONAL\x10\x03\"{\n\x0bGetResponse\x12(\n\x0cnotification\x18\x01 \x03(\x0b\x32\x12.gnmi.Notification\x12\x1e\n\x05\x65rror\x18\x02 \x01(\x0b\x32\x0b.gnmi.ErrorB\x02\x18\x01\x12\"\n\textension\x18\x03 \x03(\x0b\x32\x0f.gnmi.Extension\"7\n\x11\x43\x61pabilityRequest\x12\"\n\textension\x18\x01 \x03(\x0b\x32\x0f.gnmi.Extension\"\xa6\x01\n\x12\x43\x61pabilityResponse\x12)\n\x10supported_models\x18\x01 \x03(\x0b\x32\x0f.gnmi.ModelData\x12+\n\x13supported_encodings\x18\x02 \x03(\x0e\x32\x0e.gnmi.Encoding\x12\x14\n\x0cgNMI_version\x18\x03 \x01(\t\x12\"\n\textension\x18\x04 \x03(\x0b\x32\x0f.gnmi.Extension\"@\n\tModelData\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x14\n\x0corganization\x18\x02 \x01(\t\x12\x0f\n\x07version\x18\x03 \x01(\t*3\n\x0b\x45xtensionID\x12\r\n\tEID_UNSET\x10\x00\x12\x15\n\x10\x45ID_EXPERIMENTAL\x10\xe7\x07*D\n\x08\x45ncoding\x12\x08\n\x04JSON\x10\x00\x12\t\n\x05\x42YTES\x10\x01\x12\t\n\x05PROTO\x10\x02\x12\t\n\x05\x41SCII\x10\x03\x12\r\n\tJSON_IETF\x10\x04*A\n\x10SubscriptionMode\x12\x12\n\x0eTARGET_DEFINED\x10\x00\x12\r\n\tON_CHANGE\x10\x01\x12\n\n\x06SAMPLE\x10\x02\x32\xe3\x01\n\x04gNMI\x12\x41\n\x0c\x43\x61pabilities\x12\x17.gnmi.CapabilityRequest\x1a\x18.gnmi.CapabilityResponse\x12*\n\x03Get\x12\x10.gnmi.GetRequest\x1a\x11.gnmi.GetResponse\x12*\n\x03Set\x12\x10.gnmi.SetRequest\x1a\x11.gnmi.SetResponse\x12@\n\tSubscribe\x12\x16.gnmi.SubscribeRequest\x1a\x17.gnmi.SubscribeResponse(\x01\x30\x01:3\n\x0cgnmi_service\x12\x1c.google.protobuf.FileOptions\x18\xe9\x07 \x01(\tBS\n\x15\x63om.github.gnmi.protoB\tGnmiProtoP\x01Z%github.com/openconfig/gnmi/proto/gnmi\xca>\x05\x30.8.0b\x06proto3')
+
+_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
+_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'gnmi_pb2', globals())
+if _descriptor._USE_C_DESCRIPTORS == False:
+  google_dot_protobuf_dot_descriptor__pb2.FileOptions.RegisterExtension(gnmi_service)
+
+  DESCRIPTOR._options = None
+  DESCRIPTOR._serialized_options = b'\n\025com.github.gnmi.protoB\tGnmiProtoP\001Z%github.com/openconfig/gnmi/proto/gnmi\312>\0050.8.0'
+  _UPDATE.fields_by_name['value']._options = None
+  _UPDATE.fields_by_name['value']._serialized_options = b'\030\001'
+  _TYPEDVALUE.fields_by_name['float_val']._options = None
+  _TYPEDVALUE.fields_by_name['float_val']._serialized_options = b'\030\001'
+  _TYPEDVALUE.fields_by_name['decimal_val']._options = None
+  _TYPEDVALUE.fields_by_name['decimal_val']._serialized_options = b'\030\001'
+  _PATH.fields_by_name['element']._options = None
+  _PATH.fields_by_name['element']._serialized_options = b'\030\001'
+  _PATHELEM_KEYENTRY._options = None
+  _PATHELEM_KEYENTRY._serialized_options = b'8\001'
+  _VALUE._options = None
+  _VALUE._serialized_options = b'\030\001'
+  _ERROR._options = None
+  _ERROR._serialized_options = b'\030\001'
+  _DECIMAL64._options = None
+  _DECIMAL64._serialized_options = b'\030\001'
+  _SUBSCRIBERESPONSE.fields_by_name['error']._options = None
+  _SUBSCRIBERESPONSE.fields_by_name['error']._serialized_options = b'\030\001'
+  _SETRESPONSE.fields_by_name['message']._options = None
+  _SETRESPONSE.fields_by_name['message']._serialized_options = b'\030\001'
+  _UPDATERESULT.fields_by_name['timestamp']._options = None
+  _UPDATERESULT.fields_by_name['timestamp']._serialized_options = b'\030\001'
+  _UPDATERESULT.fields_by_name['message']._options = None
+  _UPDATERESULT.fields_by_name['message']._serialized_options = b'\030\001'
+  _GETRESPONSE.fields_by_name['error']._options = None
+  _GETRESPONSE.fields_by_name['error']._serialized_options = b'\030\001'
+  _EXTENSIONID._serialized_start=3780
+  _EXTENSIONID._serialized_end=3831
+  _ENCODING._serialized_start=3833
+  _ENCODING._serialized_end=3901
+  _SUBSCRIPTIONMODE._serialized_start=3903
+  _SUBSCRIPTIONMODE._serialized_end=3968
+  _EXTENSION._serialized_start=82
+  _EXTENSION._serialized_end=242
+  _REGISTEREDEXTENSION._serialized_start=244
+  _REGISTEREDEXTENSION._serialized_end=309
+  _MASTERARBITRATION._serialized_start=311
+  _MASTERARBITRATION._serialized_end=392
+  _UINT128._serialized_start=394
+  _UINT128._serialized_end=430
+  _ROLE._serialized_start=432
+  _ROLE._serialized_end=450
+  _HISTORY._serialized_start=452
+  _HISTORY._serialized_end=531
+  _TIMERANGE._serialized_start=533
+  _TIMERANGE._serialized_end=572
+  _NOTIFICATION._serialized_start=575
+  _NOTIFICATION._serialized_end=723
+  _UPDATE._serialized_start=725
+  _UPDATE._serialized_end=842
+  _TYPEDVALUE._serialized_start=845
+  _TYPEDVALUE._serialized_end=1232
+  _PATH._serialized_start=1234
+  _PATH._serialized_end=1323
+  _PATHELEM._serialized_start=1325
+  _PATHELEM._serialized_end=1431
+  _PATHELEM_KEYENTRY._serialized_start=1389
+  _PATHELEM_KEYENTRY._serialized_end=1431
+  _VALUE._serialized_start=1433
+  _VALUE._serialized_end=1489
+  _ERROR._serialized_start=1491
+  _ERROR._serialized_end=1569
+  _DECIMAL64._serialized_start=1571
+  _DECIMAL64._serialized_end=1621
+  _SCALARARRAY._serialized_start=1623
+  _SCALARARRAY._serialized_end=1671
+  _SUBSCRIBEREQUEST._serialized_start=1674
+  _SUBSCRIBEREQUEST._serialized_end=1827
+  _POLL._serialized_start=1829
+  _POLL._serialized_end=1835
+  _SUBSCRIBERESPONSE._serialized_start=1838
+  _SUBSCRIBERESPONSE._serialized_end=2002
+  _SUBSCRIPTIONLIST._serialized_start=2005
+  _SUBSCRIPTIONLIST._serialized_end=2346
+  _SUBSCRIPTIONLIST_MODE._serialized_start=2289
+  _SUBSCRIPTIONLIST_MODE._serialized_end=2327
+  _SUBSCRIPTION._serialized_start=2349
+  _SUBSCRIPTION._serialized_end=2508
+  _QOSMARKING._serialized_start=2510
+  _QOSMARKING._serialized_end=2539
+  _SETREQUEST._serialized_start=2542
+  _SETREQUEST._serialized_end=2707
+  _SETRESPONSE._serialized_start=2710
+  _SETRESPONSE._serialized_end=2878
+  _UPDATERESULT._serialized_start=2881
+  _UPDATERESULT._serialized_end=3083
+  _UPDATERESULT_OPERATION._serialized_start=3022
+  _UPDATERESULT_OPERATION._serialized_end=3083
+  _GETREQUEST._serialized_start=3086
+  _GETREQUEST._serialized_end=3361
+  _GETREQUEST_DATATYPE._serialized_start=3302
+  _GETREQUEST_DATATYPE._serialized_end=3361
+  _GETRESPONSE._serialized_start=3363
+  _GETRESPONSE._serialized_end=3486
+  _CAPABILITYREQUEST._serialized_start=3488
+  _CAPABILITYREQUEST._serialized_end=3543
+  _CAPABILITYRESPONSE._serialized_start=3546
+  _CAPABILITYRESPONSE._serialized_end=3712
+  _MODELDATA._serialized_start=3714
+  _MODELDATA._serialized_end=3778
+  _GNMI._serialized_start=3971
+  _GNMI._serialized_end=4198
+# @@protoc_insertion_point(module_scope)
diff --git a/src/device/service/drivers/gnmi_openconfig/gnmi/gnmi_pb2.py.old b/src/device/service/drivers/gnmi_openconfig/gnmi/gnmi_pb2.py.old
new file mode 100644
index 0000000000000000000000000000000000000000..313674f8ce5be1586c63df02226570f5082544ba
--- /dev/null
+++ b/src/device/service/drivers/gnmi_openconfig/gnmi/gnmi_pb2.py.old
@@ -0,0 +1,2037 @@
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: proto/gnmi/gnmi.proto
+
+import sys
+_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
+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
+from google.protobuf import descriptor_pb2
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+from google.protobuf import any_pb2 as google_dot_protobuf_dot_any__pb2
+from google.protobuf import descriptor_pb2 as google_dot_protobuf_dot_descriptor__pb2
+
+
+DESCRIPTOR = _descriptor.FileDescriptor(
+  name='proto/gnmi/gnmi.proto',
+  package='gnmi',
+  syntax='proto3',
+  serialized_pb=_b('\n\x15proto/gnmi/gnmi.proto\x12\x04gnmi\x1a\x19google/protobuf/any.proto\x1a google/protobuf/descriptor.proto\"\x86\x01\n\x0cNotification\x12\x11\n\ttimestamp\x18\x01 \x01(\x03\x12\x1a\n\x06prefix\x18\x02 \x01(\x0b\x32\n.gnmi.Path\x12\r\n\x05\x61lias\x18\x03 \x01(\t\x12\x1c\n\x06update\x18\x04 \x03(\x0b\x32\x0c.gnmi.Update\x12\x1a\n\x06\x64\x65lete\x18\x05 \x03(\x0b\x32\n.gnmi.Path\"u\n\x06Update\x12\x18\n\x04path\x18\x01 \x01(\x0b\x32\n.gnmi.Path\x12\x1e\n\x05value\x18\x02 \x01(\x0b\x32\x0b.gnmi.ValueB\x02\x18\x01\x12\x1d\n\x03val\x18\x03 \x01(\x0b\x32\x10.gnmi.TypedValue\x12\x12\n\nduplicates\x18\x04 \x01(\r\"\xce\x02\n\nTypedValue\x12\x14\n\nstring_val\x18\x01 \x01(\tH\x00\x12\x11\n\x07int_val\x18\x02 \x01(\x03H\x00\x12\x12\n\x08uint_val\x18\x03 \x01(\x04H\x00\x12\x12\n\x08\x62ool_val\x18\x04 \x01(\x08H\x00\x12\x13\n\tbytes_val\x18\x05 \x01(\x0cH\x00\x12\x13\n\tfloat_val\x18\x06 \x01(\x02H\x00\x12&\n\x0b\x64\x65\x63imal_val\x18\x07 \x01(\x0b\x32\x0f.gnmi.Decimal64H\x00\x12)\n\x0cleaflist_val\x18\x08 \x01(\x0b\x32\x11.gnmi.ScalarArrayH\x00\x12\'\n\x07\x61ny_val\x18\t \x01(\x0b\x32\x14.google.protobuf.AnyH\x00\x12\x12\n\x08json_val\x18\n \x01(\x0cH\x00\x12\x17\n\rjson_ietf_val\x18\x0b \x01(\x0cH\x00\x12\x13\n\tascii_val\x18\x0c \x01(\tH\x00\x42\x07\n\x05value\"Y\n\x04Path\x12\x13\n\x07\x65lement\x18\x01 \x03(\tB\x02\x18\x01\x12\x0e\n\x06origin\x18\x02 \x01(\t\x12\x1c\n\x04\x65lem\x18\x03 \x03(\x0b\x32\x0e.gnmi.PathElem\x12\x0e\n\x06target\x18\x04 \x01(\t\"j\n\x08PathElem\x12\x0c\n\x04name\x18\x01 \x01(\t\x12$\n\x03key\x18\x02 \x03(\x0b\x32\x17.gnmi.PathElem.KeyEntry\x1a*\n\x08KeyEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"8\n\x05Value\x12\r\n\x05value\x18\x01 \x01(\x0c\x12\x1c\n\x04type\x18\x02 \x01(\x0e\x32\x0e.gnmi.Encoding:\x02\x18\x01\"N\n\x05\x45rror\x12\x0c\n\x04\x63ode\x18\x01 \x01(\r\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\"\n\x04\x64\x61ta\x18\x03 \x01(\x0b\x32\x14.google.protobuf.Any:\x02\x18\x01\".\n\tDecimal64\x12\x0e\n\x06\x64igits\x18\x01 \x01(\x04\x12\x11\n\tprecision\x18\x02 \x01(\r\"0\n\x0bScalarArray\x12!\n\x07\x65lement\x18\x01 \x03(\x0b\x32\x10.gnmi.TypedValue\"\x8a\x01\n\x10SubscribeRequest\x12+\n\tsubscribe\x18\x01 \x01(\x0b\x32\x16.gnmi.SubscriptionListH\x00\x12\x1a\n\x04poll\x18\x03 \x01(\x0b\x32\n.gnmi.PollH\x00\x12\"\n\x07\x61liases\x18\x04 \x01(\x0b\x32\x0f.gnmi.AliasListH\x00\x42\t\n\x07request\"\x06\n\x04Poll\"\x80\x01\n\x11SubscribeResponse\x12$\n\x06update\x18\x01 \x01(\x0b\x32\x12.gnmi.NotificationH\x00\x12\x17\n\rsync_response\x18\x03 \x01(\x08H\x00\x12 \n\x05\x65rror\x18\x04 \x01(\x0b\x32\x0b.gnmi.ErrorB\x02\x18\x01H\x00\x42\n\n\x08response\"\xd7\x02\n\x10SubscriptionList\x12\x1a\n\x06prefix\x18\x01 \x01(\x0b\x32\n.gnmi.Path\x12(\n\x0csubscription\x18\x02 \x03(\x0b\x32\x12.gnmi.Subscription\x12\x13\n\x0buse_aliases\x18\x03 \x01(\x08\x12\x1d\n\x03qos\x18\x04 \x01(\x0b\x32\x10.gnmi.QOSMarking\x12)\n\x04mode\x18\x05 \x01(\x0e\x32\x1b.gnmi.SubscriptionList.Mode\x12\x19\n\x11\x61llow_aggregation\x18\x06 \x01(\x08\x12#\n\nuse_models\x18\x07 \x03(\x0b\x32\x0f.gnmi.ModelData\x12 \n\x08\x65ncoding\x18\x08 \x01(\x0e\x32\x0e.gnmi.Encoding\x12\x14\n\x0cupdates_only\x18\t \x01(\x08\"&\n\x04Mode\x12\n\n\x06STREAM\x10\x00\x12\x08\n\x04ONCE\x10\x01\x12\x08\n\x04POLL\x10\x02\"\x9f\x01\n\x0cSubscription\x12\x18\n\x04path\x18\x01 \x01(\x0b\x32\n.gnmi.Path\x12$\n\x04mode\x18\x02 \x01(\x0e\x32\x16.gnmi.SubscriptionMode\x12\x17\n\x0fsample_interval\x18\x03 \x01(\x04\x12\x1a\n\x12suppress_redundant\x18\x04 \x01(\x08\x12\x1a\n\x12heartbeat_interval\x18\x05 \x01(\x04\"\x1d\n\nQOSMarking\x12\x0f\n\x07marking\x18\x01 \x01(\r\"0\n\x05\x41lias\x12\x18\n\x04path\x18\x01 \x01(\x0b\x32\n.gnmi.Path\x12\r\n\x05\x61lias\x18\x02 \x01(\t\"\'\n\tAliasList\x12\x1a\n\x05\x61lias\x18\x01 \x03(\x0b\x32\x0b.gnmi.Alias\"\x81\x01\n\nSetRequest\x12\x1a\n\x06prefix\x18\x01 \x01(\x0b\x32\n.gnmi.Path\x12\x1a\n\x06\x64\x65lete\x18\x02 \x03(\x0b\x32\n.gnmi.Path\x12\x1d\n\x07replace\x18\x03 \x03(\x0b\x32\x0c.gnmi.Update\x12\x1c\n\x06update\x18\x04 \x03(\x0b\x32\x0c.gnmi.Update\"\x84\x01\n\x0bSetResponse\x12\x1a\n\x06prefix\x18\x01 \x01(\x0b\x32\n.gnmi.Path\x12$\n\x08response\x18\x02 \x03(\x0b\x32\x12.gnmi.UpdateResult\x12 \n\x07message\x18\x03 \x01(\x0b\x32\x0b.gnmi.ErrorB\x02\x18\x01\x12\x11\n\ttimestamp\x18\x04 \x01(\x03\"\xca\x01\n\x0cUpdateResult\x12\x15\n\ttimestamp\x18\x01 \x01(\x03\x42\x02\x18\x01\x12\x18\n\x04path\x18\x02 \x01(\x0b\x32\n.gnmi.Path\x12 \n\x07message\x18\x03 \x01(\x0b\x32\x0b.gnmi.ErrorB\x02\x18\x01\x12(\n\x02op\x18\x04 \x01(\x0e\x32\x1c.gnmi.UpdateResult.Operation\"=\n\tOperation\x12\x0b\n\x07INVALID\x10\x00\x12\n\n\x06\x44\x45LETE\x10\x01\x12\x0b\n\x07REPLACE\x10\x02\x12\n\n\x06UPDATE\x10\x03\"\xef\x01\n\nGetRequest\x12\x1a\n\x06prefix\x18\x01 \x01(\x0b\x32\n.gnmi.Path\x12\x18\n\x04path\x18\x02 \x03(\x0b\x32\n.gnmi.Path\x12\'\n\x04type\x18\x03 \x01(\x0e\x32\x19.gnmi.GetRequest.DataType\x12 \n\x08\x65ncoding\x18\x05 \x01(\x0e\x32\x0e.gnmi.Encoding\x12#\n\nuse_models\x18\x06 \x03(\x0b\x32\x0f.gnmi.ModelData\";\n\x08\x44\x61taType\x12\x07\n\x03\x41LL\x10\x00\x12\n\n\x06\x43ONFIG\x10\x01\x12\t\n\x05STATE\x10\x02\x12\x0f\n\x0bOPERATIONAL\x10\x03\"W\n\x0bGetResponse\x12(\n\x0cnotification\x18\x01 \x03(\x0b\x32\x12.gnmi.Notification\x12\x1e\n\x05\x65rror\x18\x02 \x01(\x0b\x32\x0b.gnmi.ErrorB\x02\x18\x01\"\x13\n\x11\x43\x61pabilityRequest\"\x82\x01\n\x12\x43\x61pabilityResponse\x12)\n\x10supported_models\x18\x01 \x03(\x0b\x32\x0f.gnmi.ModelData\x12+\n\x13supported_encodings\x18\x02 \x03(\x0e\x32\x0e.gnmi.Encoding\x12\x14\n\x0cgNMI_version\x18\x03 \x01(\t\"@\n\tModelData\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x14\n\x0corganization\x18\x02 \x01(\t\x12\x0f\n\x07version\x18\x03 \x01(\t*D\n\x08\x45ncoding\x12\x08\n\x04JSON\x10\x00\x12\t\n\x05\x42YTES\x10\x01\x12\t\n\x05PROTO\x10\x02\x12\t\n\x05\x41SCII\x10\x03\x12\r\n\tJSON_IETF\x10\x04*A\n\x10SubscriptionMode\x12\x12\n\x0eTARGET_DEFINED\x10\x00\x12\r\n\tON_CHANGE\x10\x01\x12\n\n\x06SAMPLE\x10\x02\x32\xe3\x01\n\x04gNMI\x12\x41\n\x0c\x43\x61pabilities\x12\x17.gnmi.CapabilityRequest\x1a\x18.gnmi.CapabilityResponse\x12*\n\x03Get\x12\x10.gnmi.GetRequest\x1a\x11.gnmi.GetResponse\x12*\n\x03Set\x12\x10.gnmi.SetRequest\x1a\x11.gnmi.SetResponse\x12@\n\tSubscribe\x12\x16.gnmi.SubscribeRequest\x1a\x17.gnmi.SubscribeResponse(\x01\x30\x01:3\n\x0cgnmi_service\x12\x1c.google.protobuf.FileOptions\x18\xe9\x07 \x01(\tB\x08\xca>\x05\x30.5.0b\x06proto3')
+  ,
+  dependencies=[google_dot_protobuf_dot_any__pb2.DESCRIPTOR,google_dot_protobuf_dot_descriptor__pb2.DESCRIPTOR,])
+
+_ENCODING = _descriptor.EnumDescriptor(
+  name='Encoding',
+  full_name='gnmi.Encoding',
+  filename=None,
+  file=DESCRIPTOR,
+  values=[
+    _descriptor.EnumValueDescriptor(
+      name='JSON', index=0, number=0,
+      options=None,
+      type=None),
+    _descriptor.EnumValueDescriptor(
+      name='BYTES', index=1, number=1,
+      options=None,
+      type=None),
+    _descriptor.EnumValueDescriptor(
+      name='PROTO', index=2, number=2,
+      options=None,
+      type=None),
+    _descriptor.EnumValueDescriptor(
+      name='ASCII', index=3, number=3,
+      options=None,
+      type=None),
+    _descriptor.EnumValueDescriptor(
+      name='JSON_IETF', index=4, number=4,
+      options=None,
+      type=None),
+  ],
+  containing_type=None,
+  options=None,
+  serialized_start=3053,
+  serialized_end=3121,
+)
+_sym_db.RegisterEnumDescriptor(_ENCODING)
+
+Encoding = enum_type_wrapper.EnumTypeWrapper(_ENCODING)
+_SUBSCRIPTIONMODE = _descriptor.EnumDescriptor(
+  name='SubscriptionMode',
+  full_name='gnmi.SubscriptionMode',
+  filename=None,
+  file=DESCRIPTOR,
+  values=[
+    _descriptor.EnumValueDescriptor(
+      name='TARGET_DEFINED', index=0, number=0,
+      options=None,
+      type=None),
+    _descriptor.EnumValueDescriptor(
+      name='ON_CHANGE', index=1, number=1,
+      options=None,
+      type=None),
+    _descriptor.EnumValueDescriptor(
+      name='SAMPLE', index=2, number=2,
+      options=None,
+      type=None),
+  ],
+  containing_type=None,
+  options=None,
+  serialized_start=3123,
+  serialized_end=3188,
+)
+_sym_db.RegisterEnumDescriptor(_SUBSCRIPTIONMODE)
+
+SubscriptionMode = enum_type_wrapper.EnumTypeWrapper(_SUBSCRIPTIONMODE)
+JSON = 0
+BYTES = 1
+PROTO = 2
+ASCII = 3
+JSON_IETF = 4
+TARGET_DEFINED = 0
+ON_CHANGE = 1
+SAMPLE = 2
+
+GNMI_SERVICE_FIELD_NUMBER = 1001
+gnmi_service = _descriptor.FieldDescriptor(
+  name='gnmi_service', full_name='gnmi.gnmi_service', index=0,
+  number=1001, 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=True, extension_scope=None,
+  options=None)
+
+_SUBSCRIPTIONLIST_MODE = _descriptor.EnumDescriptor(
+  name='Mode',
+  full_name='gnmi.SubscriptionList.Mode',
+  filename=None,
+  file=DESCRIPTOR,
+  values=[
+    _descriptor.EnumValueDescriptor(
+      name='STREAM', index=0, number=0,
+      options=None,
+      type=None),
+    _descriptor.EnumValueDescriptor(
+      name='ONCE', index=1, number=1,
+      options=None,
+      type=None),
+    _descriptor.EnumValueDescriptor(
+      name='POLL', index=2, number=2,
+      options=None,
+      type=None),
+  ],
+  containing_type=None,
+  options=None,
+  serialized_start=1706,
+  serialized_end=1744,
+)
+_sym_db.RegisterEnumDescriptor(_SUBSCRIPTIONLIST_MODE)
+
+_UPDATERESULT_OPERATION = _descriptor.EnumDescriptor(
+  name='Operation',
+  full_name='gnmi.UpdateResult.Operation',
+  filename=None,
+  file=DESCRIPTOR,
+  values=[
+    _descriptor.EnumValueDescriptor(
+      name='INVALID', index=0, number=0,
+      options=None,
+      type=None),
+    _descriptor.EnumValueDescriptor(
+      name='DELETE', index=1, number=1,
+      options=None,
+      type=None),
+    _descriptor.EnumValueDescriptor(
+      name='REPLACE', index=2, number=2,
+      options=None,
+      type=None),
+    _descriptor.EnumValueDescriptor(
+      name='UPDATE', index=3, number=3,
+      options=None,
+      type=None),
+  ],
+  containing_type=None,
+  options=None,
+  serialized_start=2439,
+  serialized_end=2500,
+)
+_sym_db.RegisterEnumDescriptor(_UPDATERESULT_OPERATION)
+
+_GETREQUEST_DATATYPE = _descriptor.EnumDescriptor(
+  name='DataType',
+  full_name='gnmi.GetRequest.DataType',
+  filename=None,
+  file=DESCRIPTOR,
+  values=[
+    _descriptor.EnumValueDescriptor(
+      name='ALL', index=0, number=0,
+      options=None,
+      type=None),
+    _descriptor.EnumValueDescriptor(
+      name='CONFIG', index=1, number=1,
+      options=None,
+      type=None),
+    _descriptor.EnumValueDescriptor(
+      name='STATE', index=2, number=2,
+      options=None,
+      type=None),
+    _descriptor.EnumValueDescriptor(
+      name='OPERATIONAL', index=3, number=3,
+      options=None,
+      type=None),
+  ],
+  containing_type=None,
+  options=None,
+  serialized_start=2683,
+  serialized_end=2742,
+)
+_sym_db.RegisterEnumDescriptor(_GETREQUEST_DATATYPE)
+
+
+_NOTIFICATION = _descriptor.Descriptor(
+  name='Notification',
+  full_name='gnmi.Notification',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='timestamp', full_name='gnmi.Notification.timestamp', index=0,
+      number=1, type=3, cpp_type=2, label=1,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='prefix', full_name='gnmi.Notification.prefix', 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,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='alias', full_name='gnmi.Notification.alias', 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,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='update', full_name='gnmi.Notification.update', 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,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='delete', full_name='gnmi.Notification.delete', index=4,
+      number=5, 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,
+      options=None),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=93,
+  serialized_end=227,
+)
+
+
+_UPDATE = _descriptor.Descriptor(
+  name='Update',
+  full_name='gnmi.Update',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='path', full_name='gnmi.Update.path', 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,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='value', full_name='gnmi.Update.value', 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,
+      options=_descriptor._ParseOptions(descriptor_pb2.FieldOptions(), _b('\030\001'))),
+    _descriptor.FieldDescriptor(
+      name='val', full_name='gnmi.Update.val', 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,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='duplicates', full_name='gnmi.Update.duplicates', index=3,
+      number=4, 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,
+      options=None),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=229,
+  serialized_end=346,
+)
+
+
+_TYPEDVALUE = _descriptor.Descriptor(
+  name='TypedValue',
+  full_name='gnmi.TypedValue',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='string_val', full_name='gnmi.TypedValue.string_val', 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,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='int_val', full_name='gnmi.TypedValue.int_val', index=1,
+      number=2, type=3, cpp_type=2, label=1,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='uint_val', full_name='gnmi.TypedValue.uint_val', index=2,
+      number=3, type=4, cpp_type=4, label=1,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='bool_val', full_name='gnmi.TypedValue.bool_val', 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,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='bytes_val', full_name='gnmi.TypedValue.bytes_val', index=4,
+      number=5, type=12, cpp_type=9, label=1,
+      has_default_value=False, default_value=_b(""),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='float_val', full_name='gnmi.TypedValue.float_val', index=5,
+      number=6, 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,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='decimal_val', full_name='gnmi.TypedValue.decimal_val', index=6,
+      number=7, 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,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='leaflist_val', full_name='gnmi.TypedValue.leaflist_val', index=7,
+      number=8, 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,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='any_val', full_name='gnmi.TypedValue.any_val', index=8,
+      number=9, 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,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='json_val', full_name='gnmi.TypedValue.json_val', index=9,
+      number=10, type=12, cpp_type=9, label=1,
+      has_default_value=False, default_value=_b(""),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='json_ietf_val', full_name='gnmi.TypedValue.json_ietf_val', index=10,
+      number=11, type=12, cpp_type=9, label=1,
+      has_default_value=False, default_value=_b(""),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='ascii_val', full_name='gnmi.TypedValue.ascii_val', index=11,
+      number=12, 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,
+      options=None),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+    _descriptor.OneofDescriptor(
+      name='value', full_name='gnmi.TypedValue.value',
+      index=0, containing_type=None, fields=[]),
+  ],
+  serialized_start=349,
+  serialized_end=683,
+)
+
+
+_PATH = _descriptor.Descriptor(
+  name='Path',
+  full_name='gnmi.Path',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='element', full_name='gnmi.Path.element', index=0,
+      number=1, type=9, cpp_type=9, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=_descriptor._ParseOptions(descriptor_pb2.FieldOptions(), _b('\030\001'))),
+    _descriptor.FieldDescriptor(
+      name='origin', full_name='gnmi.Path.origin', 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,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='elem', full_name='gnmi.Path.elem', 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,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='target', full_name='gnmi.Path.target', index=3,
+      number=4, 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,
+      options=None),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=685,
+  serialized_end=774,
+)
+
+
+_PATHELEM_KEYENTRY = _descriptor.Descriptor(
+  name='KeyEntry',
+  full_name='gnmi.PathElem.KeyEntry',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='key', full_name='gnmi.PathElem.KeyEntry.key', 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,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='value', full_name='gnmi.PathElem.KeyEntry.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,
+      options=None),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  options=_descriptor._ParseOptions(descriptor_pb2.MessageOptions(), _b('8\001')),
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=840,
+  serialized_end=882,
+)
+
+_PATHELEM = _descriptor.Descriptor(
+  name='PathElem',
+  full_name='gnmi.PathElem',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='name', full_name='gnmi.PathElem.name', 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,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='key', full_name='gnmi.PathElem.key', 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,
+      options=None),
+  ],
+  extensions=[
+  ],
+  nested_types=[_PATHELEM_KEYENTRY, ],
+  enum_types=[
+  ],
+  options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=776,
+  serialized_end=882,
+)
+
+
+_VALUE = _descriptor.Descriptor(
+  name='Value',
+  full_name='gnmi.Value',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='value', full_name='gnmi.Value.value', index=0,
+      number=1, type=12, cpp_type=9, label=1,
+      has_default_value=False, default_value=_b(""),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='type', full_name='gnmi.Value.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,
+      options=None),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  options=_descriptor._ParseOptions(descriptor_pb2.MessageOptions(), _b('\030\001')),
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=884,
+  serialized_end=940,
+)
+
+
+_ERROR = _descriptor.Descriptor(
+  name='Error',
+  full_name='gnmi.Error',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='code', full_name='gnmi.Error.code', 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,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='message', full_name='gnmi.Error.message', 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,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='data', full_name='gnmi.Error.data', 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,
+      options=None),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  options=_descriptor._ParseOptions(descriptor_pb2.MessageOptions(), _b('\030\001')),
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=942,
+  serialized_end=1020,
+)
+
+
+_DECIMAL64 = _descriptor.Descriptor(
+  name='Decimal64',
+  full_name='gnmi.Decimal64',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='digits', full_name='gnmi.Decimal64.digits', index=0,
+      number=1, type=4, cpp_type=4, label=1,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='precision', full_name='gnmi.Decimal64.precision', index=1,
+      number=2, 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,
+      options=None),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=1022,
+  serialized_end=1068,
+)
+
+
+_SCALARARRAY = _descriptor.Descriptor(
+  name='ScalarArray',
+  full_name='gnmi.ScalarArray',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='element', full_name='gnmi.ScalarArray.element', 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,
+      options=None),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=1070,
+  serialized_end=1118,
+)
+
+
+_SUBSCRIBEREQUEST = _descriptor.Descriptor(
+  name='SubscribeRequest',
+  full_name='gnmi.SubscribeRequest',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='subscribe', full_name='gnmi.SubscribeRequest.subscribe', 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,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='poll', full_name='gnmi.SubscribeRequest.poll', index=1,
+      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,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='aliases', full_name='gnmi.SubscribeRequest.aliases', 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,
+      options=None),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+    _descriptor.OneofDescriptor(
+      name='request', full_name='gnmi.SubscribeRequest.request',
+      index=0, containing_type=None, fields=[]),
+  ],
+  serialized_start=1121,
+  serialized_end=1259,
+)
+
+
+_POLL = _descriptor.Descriptor(
+  name='Poll',
+  full_name='gnmi.Poll',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=1261,
+  serialized_end=1267,
+)
+
+
+_SUBSCRIBERESPONSE = _descriptor.Descriptor(
+  name='SubscribeResponse',
+  full_name='gnmi.SubscribeResponse',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='update', full_name='gnmi.SubscribeResponse.update', 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,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='sync_response', full_name='gnmi.SubscribeResponse.sync_response', index=1,
+      number=3, 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,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='error', full_name='gnmi.SubscribeResponse.error', 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,
+      options=_descriptor._ParseOptions(descriptor_pb2.FieldOptions(), _b('\030\001'))),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+    _descriptor.OneofDescriptor(
+      name='response', full_name='gnmi.SubscribeResponse.response',
+      index=0, containing_type=None, fields=[]),
+  ],
+  serialized_start=1270,
+  serialized_end=1398,
+)
+
+
+_SUBSCRIPTIONLIST = _descriptor.Descriptor(
+  name='SubscriptionList',
+  full_name='gnmi.SubscriptionList',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='prefix', full_name='gnmi.SubscriptionList.prefix', 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,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='subscription', full_name='gnmi.SubscriptionList.subscription', 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,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='use_aliases', full_name='gnmi.SubscriptionList.use_aliases', index=2,
+      number=3, 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,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='qos', full_name='gnmi.SubscriptionList.qos', 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,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='mode', full_name='gnmi.SubscriptionList.mode', index=4,
+      number=5, 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,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='allow_aggregation', full_name='gnmi.SubscriptionList.allow_aggregation', index=5,
+      number=6, 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,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='use_models', full_name='gnmi.SubscriptionList.use_models', index=6,
+      number=7, 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,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='encoding', full_name='gnmi.SubscriptionList.encoding', index=7,
+      number=8, 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,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='updates_only', full_name='gnmi.SubscriptionList.updates_only', index=8,
+      number=9, 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,
+      options=None),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+    _SUBSCRIPTIONLIST_MODE,
+  ],
+  options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=1401,
+  serialized_end=1744,
+)
+
+
+_SUBSCRIPTION = _descriptor.Descriptor(
+  name='Subscription',
+  full_name='gnmi.Subscription',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='path', full_name='gnmi.Subscription.path', 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,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='mode', full_name='gnmi.Subscription.mode', 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,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='sample_interval', full_name='gnmi.Subscription.sample_interval', index=2,
+      number=3, type=4, cpp_type=4, label=1,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='suppress_redundant', full_name='gnmi.Subscription.suppress_redundant', 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,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='heartbeat_interval', full_name='gnmi.Subscription.heartbeat_interval', index=4,
+      number=5, type=4, cpp_type=4, label=1,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=1747,
+  serialized_end=1906,
+)
+
+
+_QOSMARKING = _descriptor.Descriptor(
+  name='QOSMarking',
+  full_name='gnmi.QOSMarking',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='marking', full_name='gnmi.QOSMarking.marking', 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,
+      options=None),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=1908,
+  serialized_end=1937,
+)
+
+
+_ALIAS = _descriptor.Descriptor(
+  name='Alias',
+  full_name='gnmi.Alias',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='path', full_name='gnmi.Alias.path', 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,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='alias', full_name='gnmi.Alias.alias', 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,
+      options=None),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=1939,
+  serialized_end=1987,
+)
+
+
+_ALIASLIST = _descriptor.Descriptor(
+  name='AliasList',
+  full_name='gnmi.AliasList',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='alias', full_name='gnmi.AliasList.alias', 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,
+      options=None),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=1989,
+  serialized_end=2028,
+)
+
+
+_SETREQUEST = _descriptor.Descriptor(
+  name='SetRequest',
+  full_name='gnmi.SetRequest',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='prefix', full_name='gnmi.SetRequest.prefix', 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,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='delete', full_name='gnmi.SetRequest.delete', 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,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='replace', full_name='gnmi.SetRequest.replace', 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,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='update', full_name='gnmi.SetRequest.update', 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,
+      options=None),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=2031,
+  serialized_end=2160,
+)
+
+
+_SETRESPONSE = _descriptor.Descriptor(
+  name='SetResponse',
+  full_name='gnmi.SetResponse',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='prefix', full_name='gnmi.SetResponse.prefix', 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,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='response', full_name='gnmi.SetResponse.response', 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,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='message', full_name='gnmi.SetResponse.message', 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,
+      options=_descriptor._ParseOptions(descriptor_pb2.FieldOptions(), _b('\030\001'))),
+    _descriptor.FieldDescriptor(
+      name='timestamp', full_name='gnmi.SetResponse.timestamp', index=3,
+      number=4, type=3, cpp_type=2, label=1,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=2163,
+  serialized_end=2295,
+)
+
+
+_UPDATERESULT = _descriptor.Descriptor(
+  name='UpdateResult',
+  full_name='gnmi.UpdateResult',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='timestamp', full_name='gnmi.UpdateResult.timestamp', index=0,
+      number=1, type=3, cpp_type=2, label=1,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=_descriptor._ParseOptions(descriptor_pb2.FieldOptions(), _b('\030\001'))),
+    _descriptor.FieldDescriptor(
+      name='path', full_name='gnmi.UpdateResult.path', 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,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='message', full_name='gnmi.UpdateResult.message', 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,
+      options=_descriptor._ParseOptions(descriptor_pb2.FieldOptions(), _b('\030\001'))),
+    _descriptor.FieldDescriptor(
+      name='op', full_name='gnmi.UpdateResult.op', 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,
+      options=None),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+    _UPDATERESULT_OPERATION,
+  ],
+  options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=2298,
+  serialized_end=2500,
+)
+
+
+_GETREQUEST = _descriptor.Descriptor(
+  name='GetRequest',
+  full_name='gnmi.GetRequest',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='prefix', full_name='gnmi.GetRequest.prefix', 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,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='path', full_name='gnmi.GetRequest.path', 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,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='type', full_name='gnmi.GetRequest.type', index=2,
+      number=3, 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,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='encoding', full_name='gnmi.GetRequest.encoding', index=3,
+      number=5, 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,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='use_models', full_name='gnmi.GetRequest.use_models', index=4,
+      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,
+      options=None),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+    _GETREQUEST_DATATYPE,
+  ],
+  options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=2503,
+  serialized_end=2742,
+)
+
+
+_GETRESPONSE = _descriptor.Descriptor(
+  name='GetResponse',
+  full_name='gnmi.GetResponse',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='notification', full_name='gnmi.GetResponse.notification', 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,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='error', full_name='gnmi.GetResponse.error', 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,
+      options=_descriptor._ParseOptions(descriptor_pb2.FieldOptions(), _b('\030\001'))),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=2744,
+  serialized_end=2831,
+)
+
+
+_CAPABILITYREQUEST = _descriptor.Descriptor(
+  name='CapabilityRequest',
+  full_name='gnmi.CapabilityRequest',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=2833,
+  serialized_end=2852,
+)
+
+
+_CAPABILITYRESPONSE = _descriptor.Descriptor(
+  name='CapabilityResponse',
+  full_name='gnmi.CapabilityResponse',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='supported_models', full_name='gnmi.CapabilityResponse.supported_models', 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,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='supported_encodings', full_name='gnmi.CapabilityResponse.supported_encodings', index=1,
+      number=2, 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,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='gNMI_version', full_name='gnmi.CapabilityResponse.gNMI_version', 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,
+      options=None),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=2855,
+  serialized_end=2985,
+)
+
+
+_MODELDATA = _descriptor.Descriptor(
+  name='ModelData',
+  full_name='gnmi.ModelData',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='name', full_name='gnmi.ModelData.name', 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,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='organization', full_name='gnmi.ModelData.organization', 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,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='version', full_name='gnmi.ModelData.version', 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,
+      options=None),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=2987,
+  serialized_end=3051,
+)
+
+_NOTIFICATION.fields_by_name['prefix'].message_type = _PATH
+_NOTIFICATION.fields_by_name['update'].message_type = _UPDATE
+_NOTIFICATION.fields_by_name['delete'].message_type = _PATH
+_UPDATE.fields_by_name['path'].message_type = _PATH
+_UPDATE.fields_by_name['value'].message_type = _VALUE
+_UPDATE.fields_by_name['val'].message_type = _TYPEDVALUE
+_TYPEDVALUE.fields_by_name['decimal_val'].message_type = _DECIMAL64
+_TYPEDVALUE.fields_by_name['leaflist_val'].message_type = _SCALARARRAY
+_TYPEDVALUE.fields_by_name['any_val'].message_type = google_dot_protobuf_dot_any__pb2._ANY
+_TYPEDVALUE.oneofs_by_name['value'].fields.append(
+  _TYPEDVALUE.fields_by_name['string_val'])
+_TYPEDVALUE.fields_by_name['string_val'].containing_oneof = _TYPEDVALUE.oneofs_by_name['value']
+_TYPEDVALUE.oneofs_by_name['value'].fields.append(
+  _TYPEDVALUE.fields_by_name['int_val'])
+_TYPEDVALUE.fields_by_name['int_val'].containing_oneof = _TYPEDVALUE.oneofs_by_name['value']
+_TYPEDVALUE.oneofs_by_name['value'].fields.append(
+  _TYPEDVALUE.fields_by_name['uint_val'])
+_TYPEDVALUE.fields_by_name['uint_val'].containing_oneof = _TYPEDVALUE.oneofs_by_name['value']
+_TYPEDVALUE.oneofs_by_name['value'].fields.append(
+  _TYPEDVALUE.fields_by_name['bool_val'])
+_TYPEDVALUE.fields_by_name['bool_val'].containing_oneof = _TYPEDVALUE.oneofs_by_name['value']
+_TYPEDVALUE.oneofs_by_name['value'].fields.append(
+  _TYPEDVALUE.fields_by_name['bytes_val'])
+_TYPEDVALUE.fields_by_name['bytes_val'].containing_oneof = _TYPEDVALUE.oneofs_by_name['value']
+_TYPEDVALUE.oneofs_by_name['value'].fields.append(
+  _TYPEDVALUE.fields_by_name['float_val'])
+_TYPEDVALUE.fields_by_name['float_val'].containing_oneof = _TYPEDVALUE.oneofs_by_name['value']
+_TYPEDVALUE.oneofs_by_name['value'].fields.append(
+  _TYPEDVALUE.fields_by_name['decimal_val'])
+_TYPEDVALUE.fields_by_name['decimal_val'].containing_oneof = _TYPEDVALUE.oneofs_by_name['value']
+_TYPEDVALUE.oneofs_by_name['value'].fields.append(
+  _TYPEDVALUE.fields_by_name['leaflist_val'])
+_TYPEDVALUE.fields_by_name['leaflist_val'].containing_oneof = _TYPEDVALUE.oneofs_by_name['value']
+_TYPEDVALUE.oneofs_by_name['value'].fields.append(
+  _TYPEDVALUE.fields_by_name['any_val'])
+_TYPEDVALUE.fields_by_name['any_val'].containing_oneof = _TYPEDVALUE.oneofs_by_name['value']
+_TYPEDVALUE.oneofs_by_name['value'].fields.append(
+  _TYPEDVALUE.fields_by_name['json_val'])
+_TYPEDVALUE.fields_by_name['json_val'].containing_oneof = _TYPEDVALUE.oneofs_by_name['value']
+_TYPEDVALUE.oneofs_by_name['value'].fields.append(
+  _TYPEDVALUE.fields_by_name['json_ietf_val'])
+_TYPEDVALUE.fields_by_name['json_ietf_val'].containing_oneof = _TYPEDVALUE.oneofs_by_name['value']
+_TYPEDVALUE.oneofs_by_name['value'].fields.append(
+  _TYPEDVALUE.fields_by_name['ascii_val'])
+_TYPEDVALUE.fields_by_name['ascii_val'].containing_oneof = _TYPEDVALUE.oneofs_by_name['value']
+_PATH.fields_by_name['elem'].message_type = _PATHELEM
+_PATHELEM_KEYENTRY.containing_type = _PATHELEM
+_PATHELEM.fields_by_name['key'].message_type = _PATHELEM_KEYENTRY
+_VALUE.fields_by_name['type'].enum_type = _ENCODING
+_ERROR.fields_by_name['data'].message_type = google_dot_protobuf_dot_any__pb2._ANY
+_SCALARARRAY.fields_by_name['element'].message_type = _TYPEDVALUE
+_SUBSCRIBEREQUEST.fields_by_name['subscribe'].message_type = _SUBSCRIPTIONLIST
+_SUBSCRIBEREQUEST.fields_by_name['poll'].message_type = _POLL
+_SUBSCRIBEREQUEST.fields_by_name['aliases'].message_type = _ALIASLIST
+_SUBSCRIBEREQUEST.oneofs_by_name['request'].fields.append(
+  _SUBSCRIBEREQUEST.fields_by_name['subscribe'])
+_SUBSCRIBEREQUEST.fields_by_name['subscribe'].containing_oneof = _SUBSCRIBEREQUEST.oneofs_by_name['request']
+_SUBSCRIBEREQUEST.oneofs_by_name['request'].fields.append(
+  _SUBSCRIBEREQUEST.fields_by_name['poll'])
+_SUBSCRIBEREQUEST.fields_by_name['poll'].containing_oneof = _SUBSCRIBEREQUEST.oneofs_by_name['request']
+_SUBSCRIBEREQUEST.oneofs_by_name['request'].fields.append(
+  _SUBSCRIBEREQUEST.fields_by_name['aliases'])
+_SUBSCRIBEREQUEST.fields_by_name['aliases'].containing_oneof = _SUBSCRIBEREQUEST.oneofs_by_name['request']
+_SUBSCRIBERESPONSE.fields_by_name['update'].message_type = _NOTIFICATION
+_SUBSCRIBERESPONSE.fields_by_name['error'].message_type = _ERROR
+_SUBSCRIBERESPONSE.oneofs_by_name['response'].fields.append(
+  _SUBSCRIBERESPONSE.fields_by_name['update'])
+_SUBSCRIBERESPONSE.fields_by_name['update'].containing_oneof = _SUBSCRIBERESPONSE.oneofs_by_name['response']
+_SUBSCRIBERESPONSE.oneofs_by_name['response'].fields.append(
+  _SUBSCRIBERESPONSE.fields_by_name['sync_response'])
+_SUBSCRIBERESPONSE.fields_by_name['sync_response'].containing_oneof = _SUBSCRIBERESPONSE.oneofs_by_name['response']
+_SUBSCRIBERESPONSE.oneofs_by_name['response'].fields.append(
+  _SUBSCRIBERESPONSE.fields_by_name['error'])
+_SUBSCRIBERESPONSE.fields_by_name['error'].containing_oneof = _SUBSCRIBERESPONSE.oneofs_by_name['response']
+_SUBSCRIPTIONLIST.fields_by_name['prefix'].message_type = _PATH
+_SUBSCRIPTIONLIST.fields_by_name['subscription'].message_type = _SUBSCRIPTION
+_SUBSCRIPTIONLIST.fields_by_name['qos'].message_type = _QOSMARKING
+_SUBSCRIPTIONLIST.fields_by_name['mode'].enum_type = _SUBSCRIPTIONLIST_MODE
+_SUBSCRIPTIONLIST.fields_by_name['use_models'].message_type = _MODELDATA
+_SUBSCRIPTIONLIST.fields_by_name['encoding'].enum_type = _ENCODING
+_SUBSCRIPTIONLIST_MODE.containing_type = _SUBSCRIPTIONLIST
+_SUBSCRIPTION.fields_by_name['path'].message_type = _PATH
+_SUBSCRIPTION.fields_by_name['mode'].enum_type = _SUBSCRIPTIONMODE
+_ALIAS.fields_by_name['path'].message_type = _PATH
+_ALIASLIST.fields_by_name['alias'].message_type = _ALIAS
+_SETREQUEST.fields_by_name['prefix'].message_type = _PATH
+_SETREQUEST.fields_by_name['delete'].message_type = _PATH
+_SETREQUEST.fields_by_name['replace'].message_type = _UPDATE
+_SETREQUEST.fields_by_name['update'].message_type = _UPDATE
+_SETRESPONSE.fields_by_name['prefix'].message_type = _PATH
+_SETRESPONSE.fields_by_name['response'].message_type = _UPDATERESULT
+_SETRESPONSE.fields_by_name['message'].message_type = _ERROR
+_UPDATERESULT.fields_by_name['path'].message_type = _PATH
+_UPDATERESULT.fields_by_name['message'].message_type = _ERROR
+_UPDATERESULT.fields_by_name['op'].enum_type = _UPDATERESULT_OPERATION
+_UPDATERESULT_OPERATION.containing_type = _UPDATERESULT
+_GETREQUEST.fields_by_name['prefix'].message_type = _PATH
+_GETREQUEST.fields_by_name['path'].message_type = _PATH
+_GETREQUEST.fields_by_name['type'].enum_type = _GETREQUEST_DATATYPE
+_GETREQUEST.fields_by_name['encoding'].enum_type = _ENCODING
+_GETREQUEST.fields_by_name['use_models'].message_type = _MODELDATA
+_GETREQUEST_DATATYPE.containing_type = _GETREQUEST
+_GETRESPONSE.fields_by_name['notification'].message_type = _NOTIFICATION
+_GETRESPONSE.fields_by_name['error'].message_type = _ERROR
+_CAPABILITYRESPONSE.fields_by_name['supported_models'].message_type = _MODELDATA
+_CAPABILITYRESPONSE.fields_by_name['supported_encodings'].enum_type = _ENCODING
+DESCRIPTOR.message_types_by_name['Notification'] = _NOTIFICATION
+DESCRIPTOR.message_types_by_name['Update'] = _UPDATE
+DESCRIPTOR.message_types_by_name['TypedValue'] = _TYPEDVALUE
+DESCRIPTOR.message_types_by_name['Path'] = _PATH
+DESCRIPTOR.message_types_by_name['PathElem'] = _PATHELEM
+DESCRIPTOR.message_types_by_name['Value'] = _VALUE
+DESCRIPTOR.message_types_by_name['Error'] = _ERROR
+DESCRIPTOR.message_types_by_name['Decimal64'] = _DECIMAL64
+DESCRIPTOR.message_types_by_name['ScalarArray'] = _SCALARARRAY
+DESCRIPTOR.message_types_by_name['SubscribeRequest'] = _SUBSCRIBEREQUEST
+DESCRIPTOR.message_types_by_name['Poll'] = _POLL
+DESCRIPTOR.message_types_by_name['SubscribeResponse'] = _SUBSCRIBERESPONSE
+DESCRIPTOR.message_types_by_name['SubscriptionList'] = _SUBSCRIPTIONLIST
+DESCRIPTOR.message_types_by_name['Subscription'] = _SUBSCRIPTION
+DESCRIPTOR.message_types_by_name['QOSMarking'] = _QOSMARKING
+DESCRIPTOR.message_types_by_name['Alias'] = _ALIAS
+DESCRIPTOR.message_types_by_name['AliasList'] = _ALIASLIST
+DESCRIPTOR.message_types_by_name['SetRequest'] = _SETREQUEST
+DESCRIPTOR.message_types_by_name['SetResponse'] = _SETRESPONSE
+DESCRIPTOR.message_types_by_name['UpdateResult'] = _UPDATERESULT
+DESCRIPTOR.message_types_by_name['GetRequest'] = _GETREQUEST
+DESCRIPTOR.message_types_by_name['GetResponse'] = _GETRESPONSE
+DESCRIPTOR.message_types_by_name['CapabilityRequest'] = _CAPABILITYREQUEST
+DESCRIPTOR.message_types_by_name['CapabilityResponse'] = _CAPABILITYRESPONSE
+DESCRIPTOR.message_types_by_name['ModelData'] = _MODELDATA
+DESCRIPTOR.enum_types_by_name['Encoding'] = _ENCODING
+DESCRIPTOR.enum_types_by_name['SubscriptionMode'] = _SUBSCRIPTIONMODE
+DESCRIPTOR.extensions_by_name['gnmi_service'] = gnmi_service
+_sym_db.RegisterFileDescriptor(DESCRIPTOR)
+
+Notification = _reflection.GeneratedProtocolMessageType('Notification', (_message.Message,), dict(
+  DESCRIPTOR = _NOTIFICATION,
+  __module__ = 'proto.gnmi.gnmi_pb2'
+  # @@protoc_insertion_point(class_scope:gnmi.Notification)
+  ))
+_sym_db.RegisterMessage(Notification)
+
+Update = _reflection.GeneratedProtocolMessageType('Update', (_message.Message,), dict(
+  DESCRIPTOR = _UPDATE,
+  __module__ = 'proto.gnmi.gnmi_pb2'
+  # @@protoc_insertion_point(class_scope:gnmi.Update)
+  ))
+_sym_db.RegisterMessage(Update)
+
+TypedValue = _reflection.GeneratedProtocolMessageType('TypedValue', (_message.Message,), dict(
+  DESCRIPTOR = _TYPEDVALUE,
+  __module__ = 'proto.gnmi.gnmi_pb2'
+  # @@protoc_insertion_point(class_scope:gnmi.TypedValue)
+  ))
+_sym_db.RegisterMessage(TypedValue)
+
+Path = _reflection.GeneratedProtocolMessageType('Path', (_message.Message,), dict(
+  DESCRIPTOR = _PATH,
+  __module__ = 'proto.gnmi.gnmi_pb2'
+  # @@protoc_insertion_point(class_scope:gnmi.Path)
+  ))
+_sym_db.RegisterMessage(Path)
+
+PathElem = _reflection.GeneratedProtocolMessageType('PathElem', (_message.Message,), dict(
+
+  KeyEntry = _reflection.GeneratedProtocolMessageType('KeyEntry', (_message.Message,), dict(
+    DESCRIPTOR = _PATHELEM_KEYENTRY,
+    __module__ = 'proto.gnmi.gnmi_pb2'
+    # @@protoc_insertion_point(class_scope:gnmi.PathElem.KeyEntry)
+    ))
+  ,
+  DESCRIPTOR = _PATHELEM,
+  __module__ = 'proto.gnmi.gnmi_pb2'
+  # @@protoc_insertion_point(class_scope:gnmi.PathElem)
+  ))
+_sym_db.RegisterMessage(PathElem)
+_sym_db.RegisterMessage(PathElem.KeyEntry)
+
+Value = _reflection.GeneratedProtocolMessageType('Value', (_message.Message,), dict(
+  DESCRIPTOR = _VALUE,
+  __module__ = 'proto.gnmi.gnmi_pb2'
+  # @@protoc_insertion_point(class_scope:gnmi.Value)
+  ))
+_sym_db.RegisterMessage(Value)
+
+Error = _reflection.GeneratedProtocolMessageType('Error', (_message.Message,), dict(
+  DESCRIPTOR = _ERROR,
+  __module__ = 'proto.gnmi.gnmi_pb2'
+  # @@protoc_insertion_point(class_scope:gnmi.Error)
+  ))
+_sym_db.RegisterMessage(Error)
+
+Decimal64 = _reflection.GeneratedProtocolMessageType('Decimal64', (_message.Message,), dict(
+  DESCRIPTOR = _DECIMAL64,
+  __module__ = 'proto.gnmi.gnmi_pb2'
+  # @@protoc_insertion_point(class_scope:gnmi.Decimal64)
+  ))
+_sym_db.RegisterMessage(Decimal64)
+
+ScalarArray = _reflection.GeneratedProtocolMessageType('ScalarArray', (_message.Message,), dict(
+  DESCRIPTOR = _SCALARARRAY,
+  __module__ = 'proto.gnmi.gnmi_pb2'
+  # @@protoc_insertion_point(class_scope:gnmi.ScalarArray)
+  ))
+_sym_db.RegisterMessage(ScalarArray)
+
+SubscribeRequest = _reflection.GeneratedProtocolMessageType('SubscribeRequest', (_message.Message,), dict(
+  DESCRIPTOR = _SUBSCRIBEREQUEST,
+  __module__ = 'proto.gnmi.gnmi_pb2'
+  # @@protoc_insertion_point(class_scope:gnmi.SubscribeRequest)
+  ))
+_sym_db.RegisterMessage(SubscribeRequest)
+
+Poll = _reflection.GeneratedProtocolMessageType('Poll', (_message.Message,), dict(
+  DESCRIPTOR = _POLL,
+  __module__ = 'proto.gnmi.gnmi_pb2'
+  # @@protoc_insertion_point(class_scope:gnmi.Poll)
+  ))
+_sym_db.RegisterMessage(Poll)
+
+SubscribeResponse = _reflection.GeneratedProtocolMessageType('SubscribeResponse', (_message.Message,), dict(
+  DESCRIPTOR = _SUBSCRIBERESPONSE,
+  __module__ = 'proto.gnmi.gnmi_pb2'
+  # @@protoc_insertion_point(class_scope:gnmi.SubscribeResponse)
+  ))
+_sym_db.RegisterMessage(SubscribeResponse)
+
+SubscriptionList = _reflection.GeneratedProtocolMessageType('SubscriptionList', (_message.Message,), dict(
+  DESCRIPTOR = _SUBSCRIPTIONLIST,
+  __module__ = 'proto.gnmi.gnmi_pb2'
+  # @@protoc_insertion_point(class_scope:gnmi.SubscriptionList)
+  ))
+_sym_db.RegisterMessage(SubscriptionList)
+
+Subscription = _reflection.GeneratedProtocolMessageType('Subscription', (_message.Message,), dict(
+  DESCRIPTOR = _SUBSCRIPTION,
+  __module__ = 'proto.gnmi.gnmi_pb2'
+  # @@protoc_insertion_point(class_scope:gnmi.Subscription)
+  ))
+_sym_db.RegisterMessage(Subscription)
+
+QOSMarking = _reflection.GeneratedProtocolMessageType('QOSMarking', (_message.Message,), dict(
+  DESCRIPTOR = _QOSMARKING,
+  __module__ = 'proto.gnmi.gnmi_pb2'
+  # @@protoc_insertion_point(class_scope:gnmi.QOSMarking)
+  ))
+_sym_db.RegisterMessage(QOSMarking)
+
+Alias = _reflection.GeneratedProtocolMessageType('Alias', (_message.Message,), dict(
+  DESCRIPTOR = _ALIAS,
+  __module__ = 'proto.gnmi.gnmi_pb2'
+  # @@protoc_insertion_point(class_scope:gnmi.Alias)
+  ))
+_sym_db.RegisterMessage(Alias)
+
+AliasList = _reflection.GeneratedProtocolMessageType('AliasList', (_message.Message,), dict(
+  DESCRIPTOR = _ALIASLIST,
+  __module__ = 'proto.gnmi.gnmi_pb2'
+  # @@protoc_insertion_point(class_scope:gnmi.AliasList)
+  ))
+_sym_db.RegisterMessage(AliasList)
+
+SetRequest = _reflection.GeneratedProtocolMessageType('SetRequest', (_message.Message,), dict(
+  DESCRIPTOR = _SETREQUEST,
+  __module__ = 'proto.gnmi.gnmi_pb2'
+  # @@protoc_insertion_point(class_scope:gnmi.SetRequest)
+  ))
+_sym_db.RegisterMessage(SetRequest)
+
+SetResponse = _reflection.GeneratedProtocolMessageType('SetResponse', (_message.Message,), dict(
+  DESCRIPTOR = _SETRESPONSE,
+  __module__ = 'proto.gnmi.gnmi_pb2'
+  # @@protoc_insertion_point(class_scope:gnmi.SetResponse)
+  ))
+_sym_db.RegisterMessage(SetResponse)
+
+UpdateResult = _reflection.GeneratedProtocolMessageType('UpdateResult', (_message.Message,), dict(
+  DESCRIPTOR = _UPDATERESULT,
+  __module__ = 'proto.gnmi.gnmi_pb2'
+  # @@protoc_insertion_point(class_scope:gnmi.UpdateResult)
+  ))
+_sym_db.RegisterMessage(UpdateResult)
+
+GetRequest = _reflection.GeneratedProtocolMessageType('GetRequest', (_message.Message,), dict(
+  DESCRIPTOR = _GETREQUEST,
+  __module__ = 'proto.gnmi.gnmi_pb2'
+  # @@protoc_insertion_point(class_scope:gnmi.GetRequest)
+  ))
+_sym_db.RegisterMessage(GetRequest)
+
+GetResponse = _reflection.GeneratedProtocolMessageType('GetResponse', (_message.Message,), dict(
+  DESCRIPTOR = _GETRESPONSE,
+  __module__ = 'proto.gnmi.gnmi_pb2'
+  # @@protoc_insertion_point(class_scope:gnmi.GetResponse)
+  ))
+_sym_db.RegisterMessage(GetResponse)
+
+CapabilityRequest = _reflection.GeneratedProtocolMessageType('CapabilityRequest', (_message.Message,), dict(
+  DESCRIPTOR = _CAPABILITYREQUEST,
+  __module__ = 'proto.gnmi.gnmi_pb2'
+  # @@protoc_insertion_point(class_scope:gnmi.CapabilityRequest)
+  ))
+_sym_db.RegisterMessage(CapabilityRequest)
+
+CapabilityResponse = _reflection.GeneratedProtocolMessageType('CapabilityResponse', (_message.Message,), dict(
+  DESCRIPTOR = _CAPABILITYRESPONSE,
+  __module__ = 'proto.gnmi.gnmi_pb2'
+  # @@protoc_insertion_point(class_scope:gnmi.CapabilityResponse)
+  ))
+_sym_db.RegisterMessage(CapabilityResponse)
+
+ModelData = _reflection.GeneratedProtocolMessageType('ModelData', (_message.Message,), dict(
+  DESCRIPTOR = _MODELDATA,
+  __module__ = 'proto.gnmi.gnmi_pb2'
+  # @@protoc_insertion_point(class_scope:gnmi.ModelData)
+  ))
+_sym_db.RegisterMessage(ModelData)
+
+google_dot_protobuf_dot_descriptor__pb2.FileOptions.RegisterExtension(gnmi_service)
+
+DESCRIPTOR.has_options = True
+DESCRIPTOR._options = _descriptor._ParseOptions(descriptor_pb2.FileOptions(), _b('\312>\0050.5.0'))
+_UPDATE.fields_by_name['value'].has_options = True
+_UPDATE.fields_by_name['value']._options = _descriptor._ParseOptions(descriptor_pb2.FieldOptions(), _b('\030\001'))
+_PATH.fields_by_name['element'].has_options = True
+_PATH.fields_by_name['element']._options = _descriptor._ParseOptions(descriptor_pb2.FieldOptions(), _b('\030\001'))
+_PATHELEM_KEYENTRY.has_options = True
+_PATHELEM_KEYENTRY._options = _descriptor._ParseOptions(descriptor_pb2.MessageOptions(), _b('8\001'))
+_VALUE.has_options = True
+_VALUE._options = _descriptor._ParseOptions(descriptor_pb2.MessageOptions(), _b('\030\001'))
+_ERROR.has_options = True
+_ERROR._options = _descriptor._ParseOptions(descriptor_pb2.MessageOptions(), _b('\030\001'))
+_SUBSCRIBERESPONSE.fields_by_name['error'].has_options = True
+_SUBSCRIBERESPONSE.fields_by_name['error']._options = _descriptor._ParseOptions(descriptor_pb2.FieldOptions(), _b('\030\001'))
+_SETRESPONSE.fields_by_name['message'].has_options = True
+_SETRESPONSE.fields_by_name['message']._options = _descriptor._ParseOptions(descriptor_pb2.FieldOptions(), _b('\030\001'))
+_UPDATERESULT.fields_by_name['timestamp'].has_options = True
+_UPDATERESULT.fields_by_name['timestamp']._options = _descriptor._ParseOptions(descriptor_pb2.FieldOptions(), _b('\030\001'))
+_UPDATERESULT.fields_by_name['message'].has_options = True
+_UPDATERESULT.fields_by_name['message']._options = _descriptor._ParseOptions(descriptor_pb2.FieldOptions(), _b('\030\001'))
+_GETRESPONSE.fields_by_name['error'].has_options = True
+_GETRESPONSE.fields_by_name['error']._options = _descriptor._ParseOptions(descriptor_pb2.FieldOptions(), _b('\030\001'))
+try:
+  # THESE ELEMENTS WILL BE DEPRECATED.
+  # Please use the generated *_pb2_grpc.py files instead.
+  import grpc
+  from grpc.beta import implementations as beta_implementations
+  from grpc.beta import interfaces as beta_interfaces
+  from grpc.framework.common import cardinality
+  from grpc.framework.interfaces.face import utilities as face_utilities
+
+
+  class gNMIStub(object):
+    # missing associated documentation comment in .proto file
+    pass
+
+    def __init__(self, channel):
+      """Constructor.
+
+      Args:
+        channel: A grpc.Channel.
+      """
+      self.Capabilities = channel.unary_unary(
+          '/gnmi.gNMI/Capabilities',
+          request_serializer=CapabilityRequest.SerializeToString,
+          response_deserializer=CapabilityResponse.FromString,
+          )
+      self.Get = channel.unary_unary(
+          '/gnmi.gNMI/Get',
+          request_serializer=GetRequest.SerializeToString,
+          response_deserializer=GetResponse.FromString,
+          )
+      self.Set = channel.unary_unary(
+          '/gnmi.gNMI/Set',
+          request_serializer=SetRequest.SerializeToString,
+          response_deserializer=SetResponse.FromString,
+          )
+      self.Subscribe = channel.stream_stream(
+          '/gnmi.gNMI/Subscribe',
+          request_serializer=SubscribeRequest.SerializeToString,
+          response_deserializer=SubscribeResponse.FromString,
+          )
+
+
+  class gNMIServicer(object):
+    # missing associated documentation comment in .proto file
+    pass
+
+    def Capabilities(self, request, context):
+      """Capabilities allows the client to retrieve the set of capabilities that
+      is supported by the target. This allows the target to validate the
+      service version that is implemented and retrieve the set of models that
+      the target supports. The models can then be specified in subsequent RPCs
+      to restrict the set of data that is utilized.
+      Reference: gNMI Specification Section 3.2
+      """
+      context.set_code(grpc.StatusCode.UNIMPLEMENTED)
+      context.set_details('Method not implemented!')
+      raise NotImplementedError('Method not implemented!')
+
+    def Get(self, request, context):
+      """Retrieve a snapshot of data from the target. A Get RPC requests that the
+      target snapshots a subset of the data tree as specified by the paths
+      included in the message and serializes this to be returned to the
+      client using the specified encoding.
+      Reference: gNMI Specification Section 3.3
+      """
+      context.set_code(grpc.StatusCode.UNIMPLEMENTED)
+      context.set_details('Method not implemented!')
+      raise NotImplementedError('Method not implemented!')
+
+    def Set(self, request, context):
+      """Set allows the client to modify the state of data on the target. The
+      paths to modified along with the new values that the client wishes
+      to set the value to.
+      Reference: gNMI Specification Section 3.4
+      """
+      context.set_code(grpc.StatusCode.UNIMPLEMENTED)
+      context.set_details('Method not implemented!')
+      raise NotImplementedError('Method not implemented!')
+
+    def Subscribe(self, request_iterator, context):
+      """Subscribe allows a client to request the target to send it values
+      of particular paths within the data tree. These values may be streamed
+      at a particular cadence (STREAM), sent one off on a long-lived channel
+      (POLL), or sent as a one-off retrieval (ONCE).
+      Reference: gNMI Specification Section 3.5
+      """
+      context.set_code(grpc.StatusCode.UNIMPLEMENTED)
+      context.set_details('Method not implemented!')
+      raise NotImplementedError('Method not implemented!')
+
+
+  def add_gNMIServicer_to_server(servicer, server):
+    rpc_method_handlers = {
+        'Capabilities': grpc.unary_unary_rpc_method_handler(
+            servicer.Capabilities,
+            request_deserializer=CapabilityRequest.FromString,
+            response_serializer=CapabilityResponse.SerializeToString,
+        ),
+        'Get': grpc.unary_unary_rpc_method_handler(
+            servicer.Get,
+            request_deserializer=GetRequest.FromString,
+            response_serializer=GetResponse.SerializeToString,
+        ),
+        'Set': grpc.unary_unary_rpc_method_handler(
+            servicer.Set,
+            request_deserializer=SetRequest.FromString,
+            response_serializer=SetResponse.SerializeToString,
+        ),
+        'Subscribe': grpc.stream_stream_rpc_method_handler(
+            servicer.Subscribe,
+            request_deserializer=SubscribeRequest.FromString,
+            response_serializer=SubscribeResponse.SerializeToString,
+        ),
+    }
+    generic_handler = grpc.method_handlers_generic_handler(
+        'gnmi.gNMI', rpc_method_handlers)
+    server.add_generic_rpc_handlers((generic_handler,))
+
+
+  class BetagNMIServicer(object):
+    """The Beta API is deprecated for 0.15.0 and later.
+
+    It is recommended to use the GA API (classes and functions in this
+    file not marked beta) for all further purposes. This class was generated
+    only to ease transition from grpcio<0.15.0 to grpcio>=0.15.0."""
+    # missing associated documentation comment in .proto file
+    pass
+    def Capabilities(self, request, context):
+      """Capabilities allows the client to retrieve the set of capabilities that
+      is supported by the target. This allows the target to validate the
+      service version that is implemented and retrieve the set of models that
+      the target supports. The models can then be specified in subsequent RPCs
+      to restrict the set of data that is utilized.
+      Reference: gNMI Specification Section 3.2
+      """
+      context.code(beta_interfaces.StatusCode.UNIMPLEMENTED)
+    def Get(self, request, context):
+      """Retrieve a snapshot of data from the target. A Get RPC requests that the
+      target snapshots a subset of the data tree as specified by the paths
+      included in the message and serializes this to be returned to the
+      client using the specified encoding.
+      Reference: gNMI Specification Section 3.3
+      """
+      context.code(beta_interfaces.StatusCode.UNIMPLEMENTED)
+    def Set(self, request, context):
+      """Set allows the client to modify the state of data on the target. The
+      paths to modified along with the new values that the client wishes
+      to set the value to.
+      Reference: gNMI Specification Section 3.4
+      """
+      context.code(beta_interfaces.StatusCode.UNIMPLEMENTED)
+    def Subscribe(self, request_iterator, context):
+      """Subscribe allows a client to request the target to send it values
+      of particular paths within the data tree. These values may be streamed
+      at a particular cadence (STREAM), sent one off on a long-lived channel
+      (POLL), or sent as a one-off retrieval (ONCE).
+      Reference: gNMI Specification Section 3.5
+      """
+      context.code(beta_interfaces.StatusCode.UNIMPLEMENTED)
+
+
+  class BetagNMIStub(object):
+    """The Beta API is deprecated for 0.15.0 and later.
+
+    It is recommended to use the GA API (classes and functions in this
+    file not marked beta) for all further purposes. This class was generated
+    only to ease transition from grpcio<0.15.0 to grpcio>=0.15.0."""
+    # missing associated documentation comment in .proto file
+    pass
+    def Capabilities(self, request, timeout, metadata=None, with_call=False, protocol_options=None):
+      """Capabilities allows the client to retrieve the set of capabilities that
+      is supported by the target. This allows the target to validate the
+      service version that is implemented and retrieve the set of models that
+      the target supports. The models can then be specified in subsequent RPCs
+      to restrict the set of data that is utilized.
+      Reference: gNMI Specification Section 3.2
+      """
+      raise NotImplementedError()
+    Capabilities.future = None
+    def Get(self, request, timeout, metadata=None, with_call=False, protocol_options=None):
+      """Retrieve a snapshot of data from the target. A Get RPC requests that the
+      target snapshots a subset of the data tree as specified by the paths
+      included in the message and serializes this to be returned to the
+      client using the specified encoding.
+      Reference: gNMI Specification Section 3.3
+      """
+      raise NotImplementedError()
+    Get.future = None
+    def Set(self, request, timeout, metadata=None, with_call=False, protocol_options=None):
+      """Set allows the client to modify the state of data on the target. The
+      paths to modified along with the new values that the client wishes
+      to set the value to.
+      Reference: gNMI Specification Section 3.4
+      """
+      raise NotImplementedError()
+    Set.future = None
+    def Subscribe(self, request_iterator, timeout, metadata=None, with_call=False, protocol_options=None):
+      """Subscribe allows a client to request the target to send it values
+      of particular paths within the data tree. These values may be streamed
+      at a particular cadence (STREAM), sent one off on a long-lived channel
+      (POLL), or sent as a one-off retrieval (ONCE).
+      Reference: gNMI Specification Section 3.5
+      """
+      raise NotImplementedError()
+
+
+  def beta_create_gNMI_server(servicer, pool=None, pool_size=None, default_timeout=None, maximum_timeout=None):
+    """The Beta API is deprecated for 0.15.0 and later.
+
+    It is recommended to use the GA API (classes and functions in this
+    file not marked beta) for all further purposes. This function was
+    generated only to ease transition from grpcio<0.15.0 to grpcio>=0.15.0"""
+    request_deserializers = {
+      ('gnmi.gNMI', 'Capabilities'): CapabilityRequest.FromString,
+      ('gnmi.gNMI', 'Get'): GetRequest.FromString,
+      ('gnmi.gNMI', 'Set'): SetRequest.FromString,
+      ('gnmi.gNMI', 'Subscribe'): SubscribeRequest.FromString,
+    }
+    response_serializers = {
+      ('gnmi.gNMI', 'Capabilities'): CapabilityResponse.SerializeToString,
+      ('gnmi.gNMI', 'Get'): GetResponse.SerializeToString,
+      ('gnmi.gNMI', 'Set'): SetResponse.SerializeToString,
+      ('gnmi.gNMI', 'Subscribe'): SubscribeResponse.SerializeToString,
+    }
+    method_implementations = {
+      ('gnmi.gNMI', 'Capabilities'): face_utilities.unary_unary_inline(servicer.Capabilities),
+      ('gnmi.gNMI', 'Get'): face_utilities.unary_unary_inline(servicer.Get),
+      ('gnmi.gNMI', 'Set'): face_utilities.unary_unary_inline(servicer.Set),
+      ('gnmi.gNMI', 'Subscribe'): face_utilities.stream_stream_inline(servicer.Subscribe),
+    }
+    server_options = beta_implementations.server_options(request_deserializers=request_deserializers, response_serializers=response_serializers, thread_pool=pool, thread_pool_size=pool_size, default_timeout=default_timeout, maximum_timeout=maximum_timeout)
+    return beta_implementations.server(method_implementations, options=server_options)
+
+
+  def beta_create_gNMI_stub(channel, host=None, metadata_transformer=None, pool=None, pool_size=None):
+    """The Beta API is deprecated for 0.15.0 and later.
+
+    It is recommended to use the GA API (classes and functions in this
+    file not marked beta) for all further purposes. This function was
+    generated only to ease transition from grpcio<0.15.0 to grpcio>=0.15.0"""
+    request_serializers = {
+      ('gnmi.gNMI', 'Capabilities'): CapabilityRequest.SerializeToString,
+      ('gnmi.gNMI', 'Get'): GetRequest.SerializeToString,
+      ('gnmi.gNMI', 'Set'): SetRequest.SerializeToString,
+      ('gnmi.gNMI', 'Subscribe'): SubscribeRequest.SerializeToString,
+    }
+    response_deserializers = {
+      ('gnmi.gNMI', 'Capabilities'): CapabilityResponse.FromString,
+      ('gnmi.gNMI', 'Get'): GetResponse.FromString,
+      ('gnmi.gNMI', 'Set'): SetResponse.FromString,
+      ('gnmi.gNMI', 'Subscribe'): SubscribeResponse.FromString,
+    }
+    cardinalities = {
+      'Capabilities': cardinality.Cardinality.UNARY_UNARY,
+      'Get': cardinality.Cardinality.UNARY_UNARY,
+      'Set': cardinality.Cardinality.UNARY_UNARY,
+      'Subscribe': cardinality.Cardinality.STREAM_STREAM,
+    }
+    stub_options = beta_implementations.stub_options(host=host, metadata_transformer=metadata_transformer, request_serializers=request_serializers, response_deserializers=response_deserializers, thread_pool=pool, thread_pool_size=pool_size)
+    return beta_implementations.dynamic_stub(channel, 'gnmi.gNMI', cardinalities, options=stub_options)
+except ImportError:
+  pass
+# @@protoc_insertion_point(module_scope)
diff --git a/src/device/service/drivers/gnmi_openconfig/gnmi/gnmi_pb2.pyi b/src/device/service/drivers/gnmi_openconfig/gnmi/gnmi_pb2.pyi
new file mode 100644
index 0000000000000000000000000000000000000000..423bcfb90e1a603c2ebcdc29092569c7695db2d0
--- /dev/null
+++ b/src/device/service/drivers/gnmi_openconfig/gnmi/gnmi_pb2.pyi
@@ -0,0 +1,380 @@
+from google.protobuf import any_pb2 as _any_pb2
+from google.protobuf import descriptor_pb2 as _descriptor_pb2
+from google.protobuf.internal import containers as _containers
+from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import message as _message
+from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union
+
+ASCII: Encoding
+BYTES: Encoding
+DESCRIPTOR: _descriptor.FileDescriptor
+EID_EXPERIMENTAL: ExtensionID
+EID_UNSET: ExtensionID
+GNMI_SERVICE_FIELD_NUMBER: _ClassVar[int]
+JSON: Encoding
+JSON_IETF: Encoding
+ON_CHANGE: SubscriptionMode
+PROTO: Encoding
+SAMPLE: SubscriptionMode
+TARGET_DEFINED: SubscriptionMode
+gnmi_service: _descriptor.FieldDescriptor
+
+class CapabilityRequest(_message.Message):
+    __slots__ = ["extension"]
+    EXTENSION_FIELD_NUMBER: _ClassVar[int]
+    extension: _containers.RepeatedCompositeFieldContainer[Extension]
+    def __init__(self, extension: _Optional[_Iterable[_Union[Extension, _Mapping]]] = ...) -> None: ...
+
+class CapabilityResponse(_message.Message):
+    __slots__ = ["extension", "gNMI_version", "supported_encodings", "supported_models"]
+    EXTENSION_FIELD_NUMBER: _ClassVar[int]
+    GNMI_VERSION_FIELD_NUMBER: _ClassVar[int]
+    SUPPORTED_ENCODINGS_FIELD_NUMBER: _ClassVar[int]
+    SUPPORTED_MODELS_FIELD_NUMBER: _ClassVar[int]
+    extension: _containers.RepeatedCompositeFieldContainer[Extension]
+    gNMI_version: str
+    supported_encodings: _containers.RepeatedScalarFieldContainer[Encoding]
+    supported_models: _containers.RepeatedCompositeFieldContainer[ModelData]
+    def __init__(self, supported_models: _Optional[_Iterable[_Union[ModelData, _Mapping]]] = ..., supported_encodings: _Optional[_Iterable[_Union[Encoding, str]]] = ..., gNMI_version: _Optional[str] = ..., extension: _Optional[_Iterable[_Union[Extension, _Mapping]]] = ...) -> None: ...
+
+class Decimal64(_message.Message):
+    __slots__ = ["digits", "precision"]
+    DIGITS_FIELD_NUMBER: _ClassVar[int]
+    PRECISION_FIELD_NUMBER: _ClassVar[int]
+    digits: int
+    precision: int
+    def __init__(self, digits: _Optional[int] = ..., precision: _Optional[int] = ...) -> None: ...
+
+class Error(_message.Message):
+    __slots__ = ["code", "data", "message"]
+    CODE_FIELD_NUMBER: _ClassVar[int]
+    DATA_FIELD_NUMBER: _ClassVar[int]
+    MESSAGE_FIELD_NUMBER: _ClassVar[int]
+    code: int
+    data: _any_pb2.Any
+    message: str
+    def __init__(self, code: _Optional[int] = ..., message: _Optional[str] = ..., data: _Optional[_Union[_any_pb2.Any, _Mapping]] = ...) -> None: ...
+
+class Extension(_message.Message):
+    __slots__ = ["history", "master_arbitration", "registered_ext"]
+    HISTORY_FIELD_NUMBER: _ClassVar[int]
+    MASTER_ARBITRATION_FIELD_NUMBER: _ClassVar[int]
+    REGISTERED_EXT_FIELD_NUMBER: _ClassVar[int]
+    history: History
+    master_arbitration: MasterArbitration
+    registered_ext: RegisteredExtension
+    def __init__(self, registered_ext: _Optional[_Union[RegisteredExtension, _Mapping]] = ..., master_arbitration: _Optional[_Union[MasterArbitration, _Mapping]] = ..., history: _Optional[_Union[History, _Mapping]] = ...) -> None: ...
+
+class GetRequest(_message.Message):
+    __slots__ = ["encoding", "extension", "path", "prefix", "type", "use_models"]
+    class DataType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
+        __slots__ = []
+    ALL: GetRequest.DataType
+    CONFIG: GetRequest.DataType
+    ENCODING_FIELD_NUMBER: _ClassVar[int]
+    EXTENSION_FIELD_NUMBER: _ClassVar[int]
+    OPERATIONAL: GetRequest.DataType
+    PATH_FIELD_NUMBER: _ClassVar[int]
+    PREFIX_FIELD_NUMBER: _ClassVar[int]
+    STATE: GetRequest.DataType
+    TYPE_FIELD_NUMBER: _ClassVar[int]
+    USE_MODELS_FIELD_NUMBER: _ClassVar[int]
+    encoding: Encoding
+    extension: _containers.RepeatedCompositeFieldContainer[Extension]
+    path: _containers.RepeatedCompositeFieldContainer[Path]
+    prefix: Path
+    type: GetRequest.DataType
+    use_models: _containers.RepeatedCompositeFieldContainer[ModelData]
+    def __init__(self, prefix: _Optional[_Union[Path, _Mapping]] = ..., path: _Optional[_Iterable[_Union[Path, _Mapping]]] = ..., type: _Optional[_Union[GetRequest.DataType, str]] = ..., encoding: _Optional[_Union[Encoding, str]] = ..., use_models: _Optional[_Iterable[_Union[ModelData, _Mapping]]] = ..., extension: _Optional[_Iterable[_Union[Extension, _Mapping]]] = ...) -> None: ...
+
+class GetResponse(_message.Message):
+    __slots__ = ["error", "extension", "notification"]
+    ERROR_FIELD_NUMBER: _ClassVar[int]
+    EXTENSION_FIELD_NUMBER: _ClassVar[int]
+    NOTIFICATION_FIELD_NUMBER: _ClassVar[int]
+    error: Error
+    extension: _containers.RepeatedCompositeFieldContainer[Extension]
+    notification: _containers.RepeatedCompositeFieldContainer[Notification]
+    def __init__(self, notification: _Optional[_Iterable[_Union[Notification, _Mapping]]] = ..., error: _Optional[_Union[Error, _Mapping]] = ..., extension: _Optional[_Iterable[_Union[Extension, _Mapping]]] = ...) -> None: ...
+
+class History(_message.Message):
+    __slots__ = ["range", "snapshot_time"]
+    RANGE_FIELD_NUMBER: _ClassVar[int]
+    SNAPSHOT_TIME_FIELD_NUMBER: _ClassVar[int]
+    range: TimeRange
+    snapshot_time: int
+    def __init__(self, snapshot_time: _Optional[int] = ..., range: _Optional[_Union[TimeRange, _Mapping]] = ...) -> None: ...
+
+class MasterArbitration(_message.Message):
+    __slots__ = ["election_id", "role"]
+    ELECTION_ID_FIELD_NUMBER: _ClassVar[int]
+    ROLE_FIELD_NUMBER: _ClassVar[int]
+    election_id: Uint128
+    role: Role
+    def __init__(self, role: _Optional[_Union[Role, _Mapping]] = ..., election_id: _Optional[_Union[Uint128, _Mapping]] = ...) -> None: ...
+
+class ModelData(_message.Message):
+    __slots__ = ["name", "organization", "version"]
+    NAME_FIELD_NUMBER: _ClassVar[int]
+    ORGANIZATION_FIELD_NUMBER: _ClassVar[int]
+    VERSION_FIELD_NUMBER: _ClassVar[int]
+    name: str
+    organization: str
+    version: str
+    def __init__(self, name: _Optional[str] = ..., organization: _Optional[str] = ..., version: _Optional[str] = ...) -> None: ...
+
+class Notification(_message.Message):
+    __slots__ = ["atomic", "delete", "prefix", "timestamp", "update"]
+    ATOMIC_FIELD_NUMBER: _ClassVar[int]
+    DELETE_FIELD_NUMBER: _ClassVar[int]
+    PREFIX_FIELD_NUMBER: _ClassVar[int]
+    TIMESTAMP_FIELD_NUMBER: _ClassVar[int]
+    UPDATE_FIELD_NUMBER: _ClassVar[int]
+    atomic: bool
+    delete: _containers.RepeatedCompositeFieldContainer[Path]
+    prefix: Path
+    timestamp: int
+    update: _containers.RepeatedCompositeFieldContainer[Update]
+    def __init__(self, timestamp: _Optional[int] = ..., prefix: _Optional[_Union[Path, _Mapping]] = ..., update: _Optional[_Iterable[_Union[Update, _Mapping]]] = ..., delete: _Optional[_Iterable[_Union[Path, _Mapping]]] = ..., atomic: bool = ...) -> None: ...
+
+class Path(_message.Message):
+    __slots__ = ["elem", "element", "origin", "target"]
+    ELEMENT_FIELD_NUMBER: _ClassVar[int]
+    ELEM_FIELD_NUMBER: _ClassVar[int]
+    ORIGIN_FIELD_NUMBER: _ClassVar[int]
+    TARGET_FIELD_NUMBER: _ClassVar[int]
+    elem: _containers.RepeatedCompositeFieldContainer[PathElem]
+    element: _containers.RepeatedScalarFieldContainer[str]
+    origin: str
+    target: str
+    def __init__(self, element: _Optional[_Iterable[str]] = ..., origin: _Optional[str] = ..., elem: _Optional[_Iterable[_Union[PathElem, _Mapping]]] = ..., target: _Optional[str] = ...) -> None: ...
+
+class PathElem(_message.Message):
+    __slots__ = ["key", "name"]
+    class KeyEntry(_message.Message):
+        __slots__ = ["key", "value"]
+        KEY_FIELD_NUMBER: _ClassVar[int]
+        VALUE_FIELD_NUMBER: _ClassVar[int]
+        key: str
+        value: str
+        def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ...
+    KEY_FIELD_NUMBER: _ClassVar[int]
+    NAME_FIELD_NUMBER: _ClassVar[int]
+    key: _containers.ScalarMap[str, str]
+    name: str
+    def __init__(self, name: _Optional[str] = ..., key: _Optional[_Mapping[str, str]] = ...) -> None: ...
+
+class Poll(_message.Message):
+    __slots__ = []
+    def __init__(self) -> None: ...
+
+class QOSMarking(_message.Message):
+    __slots__ = ["marking"]
+    MARKING_FIELD_NUMBER: _ClassVar[int]
+    marking: int
+    def __init__(self, marking: _Optional[int] = ...) -> None: ...
+
+class RegisteredExtension(_message.Message):
+    __slots__ = ["id", "msg"]
+    ID_FIELD_NUMBER: _ClassVar[int]
+    MSG_FIELD_NUMBER: _ClassVar[int]
+    id: ExtensionID
+    msg: bytes
+    def __init__(self, id: _Optional[_Union[ExtensionID, str]] = ..., msg: _Optional[bytes] = ...) -> None: ...
+
+class Role(_message.Message):
+    __slots__ = ["id"]
+    ID_FIELD_NUMBER: _ClassVar[int]
+    id: str
+    def __init__(self, id: _Optional[str] = ...) -> None: ...
+
+class ScalarArray(_message.Message):
+    __slots__ = ["element"]
+    ELEMENT_FIELD_NUMBER: _ClassVar[int]
+    element: _containers.RepeatedCompositeFieldContainer[TypedValue]
+    def __init__(self, element: _Optional[_Iterable[_Union[TypedValue, _Mapping]]] = ...) -> None: ...
+
+class SetRequest(_message.Message):
+    __slots__ = ["delete", "extension", "prefix", "replace", "update"]
+    DELETE_FIELD_NUMBER: _ClassVar[int]
+    EXTENSION_FIELD_NUMBER: _ClassVar[int]
+    PREFIX_FIELD_NUMBER: _ClassVar[int]
+    REPLACE_FIELD_NUMBER: _ClassVar[int]
+    UPDATE_FIELD_NUMBER: _ClassVar[int]
+    delete: _containers.RepeatedCompositeFieldContainer[Path]
+    extension: _containers.RepeatedCompositeFieldContainer[Extension]
+    prefix: Path
+    replace: _containers.RepeatedCompositeFieldContainer[Update]
+    update: _containers.RepeatedCompositeFieldContainer[Update]
+    def __init__(self, prefix: _Optional[_Union[Path, _Mapping]] = ..., delete: _Optional[_Iterable[_Union[Path, _Mapping]]] = ..., replace: _Optional[_Iterable[_Union[Update, _Mapping]]] = ..., update: _Optional[_Iterable[_Union[Update, _Mapping]]] = ..., extension: _Optional[_Iterable[_Union[Extension, _Mapping]]] = ...) -> None: ...
+
+class SetResponse(_message.Message):
+    __slots__ = ["extension", "message", "prefix", "response", "timestamp"]
+    EXTENSION_FIELD_NUMBER: _ClassVar[int]
+    MESSAGE_FIELD_NUMBER: _ClassVar[int]
+    PREFIX_FIELD_NUMBER: _ClassVar[int]
+    RESPONSE_FIELD_NUMBER: _ClassVar[int]
+    TIMESTAMP_FIELD_NUMBER: _ClassVar[int]
+    extension: _containers.RepeatedCompositeFieldContainer[Extension]
+    message: Error
+    prefix: Path
+    response: _containers.RepeatedCompositeFieldContainer[UpdateResult]
+    timestamp: int
+    def __init__(self, prefix: _Optional[_Union[Path, _Mapping]] = ..., response: _Optional[_Iterable[_Union[UpdateResult, _Mapping]]] = ..., message: _Optional[_Union[Error, _Mapping]] = ..., timestamp: _Optional[int] = ..., extension: _Optional[_Iterable[_Union[Extension, _Mapping]]] = ...) -> None: ...
+
+class SubscribeRequest(_message.Message):
+    __slots__ = ["extension", "poll", "subscribe"]
+    EXTENSION_FIELD_NUMBER: _ClassVar[int]
+    POLL_FIELD_NUMBER: _ClassVar[int]
+    SUBSCRIBE_FIELD_NUMBER: _ClassVar[int]
+    extension: _containers.RepeatedCompositeFieldContainer[Extension]
+    poll: Poll
+    subscribe: SubscriptionList
+    def __init__(self, subscribe: _Optional[_Union[SubscriptionList, _Mapping]] = ..., poll: _Optional[_Union[Poll, _Mapping]] = ..., extension: _Optional[_Iterable[_Union[Extension, _Mapping]]] = ...) -> None: ...
+
+class SubscribeResponse(_message.Message):
+    __slots__ = ["error", "extension", "sync_response", "update"]
+    ERROR_FIELD_NUMBER: _ClassVar[int]
+    EXTENSION_FIELD_NUMBER: _ClassVar[int]
+    SYNC_RESPONSE_FIELD_NUMBER: _ClassVar[int]
+    UPDATE_FIELD_NUMBER: _ClassVar[int]
+    error: Error
+    extension: _containers.RepeatedCompositeFieldContainer[Extension]
+    sync_response: bool
+    update: Notification
+    def __init__(self, update: _Optional[_Union[Notification, _Mapping]] = ..., sync_response: bool = ..., error: _Optional[_Union[Error, _Mapping]] = ..., extension: _Optional[_Iterable[_Union[Extension, _Mapping]]] = ...) -> None: ...
+
+class Subscription(_message.Message):
+    __slots__ = ["heartbeat_interval", "mode", "path", "sample_interval", "suppress_redundant"]
+    HEARTBEAT_INTERVAL_FIELD_NUMBER: _ClassVar[int]
+    MODE_FIELD_NUMBER: _ClassVar[int]
+    PATH_FIELD_NUMBER: _ClassVar[int]
+    SAMPLE_INTERVAL_FIELD_NUMBER: _ClassVar[int]
+    SUPPRESS_REDUNDANT_FIELD_NUMBER: _ClassVar[int]
+    heartbeat_interval: int
+    mode: SubscriptionMode
+    path: Path
+    sample_interval: int
+    suppress_redundant: bool
+    def __init__(self, path: _Optional[_Union[Path, _Mapping]] = ..., mode: _Optional[_Union[SubscriptionMode, str]] = ..., sample_interval: _Optional[int] = ..., suppress_redundant: bool = ..., heartbeat_interval: _Optional[int] = ...) -> None: ...
+
+class SubscriptionList(_message.Message):
+    __slots__ = ["allow_aggregation", "encoding", "mode", "prefix", "qos", "subscription", "updates_only", "use_models"]
+    class Mode(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
+        __slots__ = []
+    ALLOW_AGGREGATION_FIELD_NUMBER: _ClassVar[int]
+    ENCODING_FIELD_NUMBER: _ClassVar[int]
+    MODE_FIELD_NUMBER: _ClassVar[int]
+    ONCE: SubscriptionList.Mode
+    POLL: SubscriptionList.Mode
+    PREFIX_FIELD_NUMBER: _ClassVar[int]
+    QOS_FIELD_NUMBER: _ClassVar[int]
+    STREAM: SubscriptionList.Mode
+    SUBSCRIPTION_FIELD_NUMBER: _ClassVar[int]
+    UPDATES_ONLY_FIELD_NUMBER: _ClassVar[int]
+    USE_MODELS_FIELD_NUMBER: _ClassVar[int]
+    allow_aggregation: bool
+    encoding: Encoding
+    mode: SubscriptionList.Mode
+    prefix: Path
+    qos: QOSMarking
+    subscription: _containers.RepeatedCompositeFieldContainer[Subscription]
+    updates_only: bool
+    use_models: _containers.RepeatedCompositeFieldContainer[ModelData]
+    def __init__(self, prefix: _Optional[_Union[Path, _Mapping]] = ..., subscription: _Optional[_Iterable[_Union[Subscription, _Mapping]]] = ..., qos: _Optional[_Union[QOSMarking, _Mapping]] = ..., mode: _Optional[_Union[SubscriptionList.Mode, str]] = ..., allow_aggregation: bool = ..., use_models: _Optional[_Iterable[_Union[ModelData, _Mapping]]] = ..., encoding: _Optional[_Union[Encoding, str]] = ..., updates_only: bool = ...) -> None: ...
+
+class TimeRange(_message.Message):
+    __slots__ = ["end", "start"]
+    END_FIELD_NUMBER: _ClassVar[int]
+    START_FIELD_NUMBER: _ClassVar[int]
+    end: int
+    start: int
+    def __init__(self, start: _Optional[int] = ..., end: _Optional[int] = ...) -> None: ...
+
+class TypedValue(_message.Message):
+    __slots__ = ["any_val", "ascii_val", "bool_val", "bytes_val", "decimal_val", "double_val", "float_val", "int_val", "json_ietf_val", "json_val", "leaflist_val", "proto_bytes", "string_val", "uint_val"]
+    ANY_VAL_FIELD_NUMBER: _ClassVar[int]
+    ASCII_VAL_FIELD_NUMBER: _ClassVar[int]
+    BOOL_VAL_FIELD_NUMBER: _ClassVar[int]
+    BYTES_VAL_FIELD_NUMBER: _ClassVar[int]
+    DECIMAL_VAL_FIELD_NUMBER: _ClassVar[int]
+    DOUBLE_VAL_FIELD_NUMBER: _ClassVar[int]
+    FLOAT_VAL_FIELD_NUMBER: _ClassVar[int]
+    INT_VAL_FIELD_NUMBER: _ClassVar[int]
+    JSON_IETF_VAL_FIELD_NUMBER: _ClassVar[int]
+    JSON_VAL_FIELD_NUMBER: _ClassVar[int]
+    LEAFLIST_VAL_FIELD_NUMBER: _ClassVar[int]
+    PROTO_BYTES_FIELD_NUMBER: _ClassVar[int]
+    STRING_VAL_FIELD_NUMBER: _ClassVar[int]
+    UINT_VAL_FIELD_NUMBER: _ClassVar[int]
+    any_val: _any_pb2.Any
+    ascii_val: str
+    bool_val: bool
+    bytes_val: bytes
+    decimal_val: Decimal64
+    double_val: float
+    float_val: float
+    int_val: int
+    json_ietf_val: bytes
+    json_val: bytes
+    leaflist_val: ScalarArray
+    proto_bytes: bytes
+    string_val: str
+    uint_val: int
+    def __init__(self, string_val: _Optional[str] = ..., int_val: _Optional[int] = ..., uint_val: _Optional[int] = ..., bool_val: bool = ..., bytes_val: _Optional[bytes] = ..., float_val: _Optional[float] = ..., double_val: _Optional[float] = ..., decimal_val: _Optional[_Union[Decimal64, _Mapping]] = ..., leaflist_val: _Optional[_Union[ScalarArray, _Mapping]] = ..., any_val: _Optional[_Union[_any_pb2.Any, _Mapping]] = ..., json_val: _Optional[bytes] = ..., json_ietf_val: _Optional[bytes] = ..., ascii_val: _Optional[str] = ..., proto_bytes: _Optional[bytes] = ...) -> None: ...
+
+class Uint128(_message.Message):
+    __slots__ = ["high", "low"]
+    HIGH_FIELD_NUMBER: _ClassVar[int]
+    LOW_FIELD_NUMBER: _ClassVar[int]
+    high: int
+    low: int
+    def __init__(self, high: _Optional[int] = ..., low: _Optional[int] = ...) -> None: ...
+
+class Update(_message.Message):
+    __slots__ = ["duplicates", "path", "val", "value"]
+    DUPLICATES_FIELD_NUMBER: _ClassVar[int]
+    PATH_FIELD_NUMBER: _ClassVar[int]
+    VALUE_FIELD_NUMBER: _ClassVar[int]
+    VAL_FIELD_NUMBER: _ClassVar[int]
+    duplicates: int
+    path: Path
+    val: TypedValue
+    value: Value
+    def __init__(self, path: _Optional[_Union[Path, _Mapping]] = ..., value: _Optional[_Union[Value, _Mapping]] = ..., val: _Optional[_Union[TypedValue, _Mapping]] = ..., duplicates: _Optional[int] = ...) -> None: ...
+
+class UpdateResult(_message.Message):
+    __slots__ = ["message", "op", "path", "timestamp"]
+    class Operation(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
+        __slots__ = []
+    DELETE: UpdateResult.Operation
+    INVALID: UpdateResult.Operation
+    MESSAGE_FIELD_NUMBER: _ClassVar[int]
+    OP_FIELD_NUMBER: _ClassVar[int]
+    PATH_FIELD_NUMBER: _ClassVar[int]
+    REPLACE: UpdateResult.Operation
+    TIMESTAMP_FIELD_NUMBER: _ClassVar[int]
+    UPDATE: UpdateResult.Operation
+    message: Error
+    op: UpdateResult.Operation
+    path: Path
+    timestamp: int
+    def __init__(self, timestamp: _Optional[int] = ..., path: _Optional[_Union[Path, _Mapping]] = ..., message: _Optional[_Union[Error, _Mapping]] = ..., op: _Optional[_Union[UpdateResult.Operation, str]] = ...) -> None: ...
+
+class Value(_message.Message):
+    __slots__ = ["type", "value"]
+    TYPE_FIELD_NUMBER: _ClassVar[int]
+    VALUE_FIELD_NUMBER: _ClassVar[int]
+    type: Encoding
+    value: bytes
+    def __init__(self, value: _Optional[bytes] = ..., type: _Optional[_Union[Encoding, str]] = ...) -> None: ...
+
+class ExtensionID(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
+    __slots__ = []
+
+class Encoding(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
+    __slots__ = []
+
+class SubscriptionMode(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
+    __slots__ = []
\ No newline at end of file
diff --git a/src/device/service/drivers/gnmi_openconfig/gnmi/gnmi_pb2_grpc.py b/src/device/service/drivers/gnmi_openconfig/gnmi/gnmi_pb2_grpc.py
new file mode 100644
index 0000000000000000000000000000000000000000..517d3d9eb41c5833a28cc9b7f43859dde186f348
--- /dev/null
+++ b/src/device/service/drivers/gnmi_openconfig/gnmi/gnmi_pb2_grpc.py
@@ -0,0 +1,185 @@
+# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
+"""Client and server classes corresponding to protobuf-defined services."""
+import grpc
+
+from . import gnmi_pb2 as gnmi__pb2
+
+
+class gNMIStub(object):
+    """Missing associated documentation comment in .proto file."""
+
+    def __init__(self, channel):
+        """Constructor.
+
+        Args:
+            channel: A grpc.Channel.
+        """
+        self.Capabilities = channel.unary_unary(
+                '/gnmi.gNMI/Capabilities',
+                request_serializer=gnmi__pb2.CapabilityRequest.SerializeToString,
+                response_deserializer=gnmi__pb2.CapabilityResponse.FromString,
+                )
+        self.Get = channel.unary_unary(
+                '/gnmi.gNMI/Get',
+                request_serializer=gnmi__pb2.GetRequest.SerializeToString,
+                response_deserializer=gnmi__pb2.GetResponse.FromString,
+                )
+        self.Set = channel.unary_unary(
+                '/gnmi.gNMI/Set',
+                request_serializer=gnmi__pb2.SetRequest.SerializeToString,
+                response_deserializer=gnmi__pb2.SetResponse.FromString,
+                )
+        self.Subscribe = channel.stream_stream(
+                '/gnmi.gNMI/Subscribe',
+                request_serializer=gnmi__pb2.SubscribeRequest.SerializeToString,
+                response_deserializer=gnmi__pb2.SubscribeResponse.FromString,
+                )
+
+
+class gNMIServicer(object):
+    """Missing associated documentation comment in .proto file."""
+
+    def Capabilities(self, request, context):
+        """Capabilities allows the client to retrieve the set of capabilities that
+        is supported by the target. This allows the target to validate the
+        service version that is implemented and retrieve the set of models that
+        the target supports. The models can then be specified in subsequent RPCs
+        to restrict the set of data that is utilized.
+        Reference: gNMI Specification Section 3.2
+        """
+        context.set_code(grpc.StatusCode.UNIMPLEMENTED)
+        context.set_details('Method not implemented!')
+        raise NotImplementedError('Method not implemented!')
+
+    def Get(self, request, context):
+        """Retrieve a snapshot of data from the target. A Get RPC requests that the
+        target snapshots a subset of the data tree as specified by the paths
+        included in the message and serializes this to be returned to the
+        client using the specified encoding.
+        Reference: gNMI Specification Section 3.3
+        """
+        context.set_code(grpc.StatusCode.UNIMPLEMENTED)
+        context.set_details('Method not implemented!')
+        raise NotImplementedError('Method not implemented!')
+
+    def Set(self, request, context):
+        """Set allows the client to modify the state of data on the target. The
+        paths to modified along with the new values that the client wishes
+        to set the value to.
+        Reference: gNMI Specification Section 3.4
+        """
+        context.set_code(grpc.StatusCode.UNIMPLEMENTED)
+        context.set_details('Method not implemented!')
+        raise NotImplementedError('Method not implemented!')
+
+    def Subscribe(self, request_iterator, context):
+        """Subscribe allows a client to request the target to send it values
+        of particular paths within the data tree. These values may be streamed
+        at a particular cadence (STREAM), sent one off on a long-lived channel
+        (POLL), or sent as a one-off retrieval (ONCE).
+        Reference: gNMI Specification Section 3.5
+        """
+        context.set_code(grpc.StatusCode.UNIMPLEMENTED)
+        context.set_details('Method not implemented!')
+        raise NotImplementedError('Method not implemented!')
+
+
+def add_gNMIServicer_to_server(servicer, server):
+    rpc_method_handlers = {
+            'Capabilities': grpc.unary_unary_rpc_method_handler(
+                    servicer.Capabilities,
+                    request_deserializer=gnmi__pb2.CapabilityRequest.FromString,
+                    response_serializer=gnmi__pb2.CapabilityResponse.SerializeToString,
+            ),
+            'Get': grpc.unary_unary_rpc_method_handler(
+                    servicer.Get,
+                    request_deserializer=gnmi__pb2.GetRequest.FromString,
+                    response_serializer=gnmi__pb2.GetResponse.SerializeToString,
+            ),
+            'Set': grpc.unary_unary_rpc_method_handler(
+                    servicer.Set,
+                    request_deserializer=gnmi__pb2.SetRequest.FromString,
+                    response_serializer=gnmi__pb2.SetResponse.SerializeToString,
+            ),
+            'Subscribe': grpc.stream_stream_rpc_method_handler(
+                    servicer.Subscribe,
+                    request_deserializer=gnmi__pb2.SubscribeRequest.FromString,
+                    response_serializer=gnmi__pb2.SubscribeResponse.SerializeToString,
+            ),
+    }
+    generic_handler = grpc.method_handlers_generic_handler(
+            'gnmi.gNMI', rpc_method_handlers)
+    server.add_generic_rpc_handlers((generic_handler,))
+
+
+ # This class is part of an EXPERIMENTAL API.
+class gNMI(object):
+    """Missing associated documentation comment in .proto file."""
+
+    @staticmethod
+    def Capabilities(request,
+            target,
+            options=(),
+            channel_credentials=None,
+            call_credentials=None,
+            insecure=False,
+            compression=None,
+            wait_for_ready=None,
+            timeout=None,
+            metadata=None):
+        return grpc.experimental.unary_unary(request, target, '/gnmi.gNMI/Capabilities',
+            gnmi__pb2.CapabilityRequest.SerializeToString,
+            gnmi__pb2.CapabilityResponse.FromString,
+            options, channel_credentials,
+            insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
+
+    @staticmethod
+    def Get(request,
+            target,
+            options=(),
+            channel_credentials=None,
+            call_credentials=None,
+            insecure=False,
+            compression=None,
+            wait_for_ready=None,
+            timeout=None,
+            metadata=None):
+        return grpc.experimental.unary_unary(request, target, '/gnmi.gNMI/Get',
+            gnmi__pb2.GetRequest.SerializeToString,
+            gnmi__pb2.GetResponse.FromString,
+            options, channel_credentials,
+            insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
+
+    @staticmethod
+    def Set(request,
+            target,
+            options=(),
+            channel_credentials=None,
+            call_credentials=None,
+            insecure=False,
+            compression=None,
+            wait_for_ready=None,
+            timeout=None,
+            metadata=None):
+        return grpc.experimental.unary_unary(request, target, '/gnmi.gNMI/Set',
+            gnmi__pb2.SetRequest.SerializeToString,
+            gnmi__pb2.SetResponse.FromString,
+            options, channel_credentials,
+            insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
+
+    @staticmethod
+    def Subscribe(request_iterator,
+            target,
+            options=(),
+            channel_credentials=None,
+            call_credentials=None,
+            insecure=False,
+            compression=None,
+            wait_for_ready=None,
+            timeout=None,
+            metadata=None):
+        return grpc.experimental.stream_stream(request_iterator, target, '/gnmi.gNMI/Subscribe',
+            gnmi__pb2.SubscribeRequest.SerializeToString,
+            gnmi__pb2.SubscribeResponse.FromString,
+            options, channel_credentials,
+            insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
diff --git a/src/device/service/drivers/gnmi_openconfig/handlers/Component.py b/src/device/service/drivers/gnmi_openconfig/handlers/Component.py
new file mode 100644
index 0000000000000000000000000000000000000000..0b3c1f9705353548025cb4365ea31e68978c79f1
--- /dev/null
+++ b/src/device/service/drivers/gnmi_openconfig/handlers/Component.py
@@ -0,0 +1,63 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging
+from typing import Any, Dict, List, Tuple
+from common.proto.kpi_sample_types_pb2 import KpiSampleType
+from ._Handler import _Handler
+
+LOGGER = logging.getLogger(__name__)
+
+PATH_IF_CTR = "/interfaces/interface[name={:s}]/state/counters/{:s}"
+
+class ComponentHandler(_Handler):
+    def get_resource_key(self) -> str: return '/endpoints/endpoint'
+    def get_path(self) -> str: return '/components/component'
+
+    def parse(self, json_data : Dict) -> List[Tuple[str, Dict[str, Any]]]:
+        #LOGGER.info('json_data = {:s}'.format(json.dumps(json_data)))
+        json_component_list : List[Dict] = json_data.get('component', [])
+        response = []
+        for json_component in json_component_list:
+            #LOGGER.info('json_component = {:s}'.format(json.dumps(json_component)))
+
+            endpoint = {}
+
+            component_type = json_component.get('state', {}).get('type')
+            if component_type is None: continue
+            component_type = component_type.replace('oc-platform-types:', '')
+            component_type = component_type.replace('openconfig-platform-types:', '')
+            if component_type not in {'PORT'}: continue
+            endpoint['type'] = '-'
+
+            #LOGGER.info('PORT json_component = {:s}'.format(json.dumps(json_component)))
+
+            component_name = json_component.get('name')
+            if component_name is None: continue
+
+            # TODO: improve mapping between interface name and component name
+            # By now, computed by time for the sake of saving time for the Hackfest.
+            interface_name = component_name.lower().replace('-port', '')
+
+            endpoint['uuid'] = interface_name
+            endpoint['sample_types'] = {
+                KpiSampleType.KPISAMPLETYPE_BYTES_RECEIVED     : PATH_IF_CTR.format(interface_name, 'in-octets' ),
+                KpiSampleType.KPISAMPLETYPE_BYTES_TRANSMITTED  : PATH_IF_CTR.format(interface_name, 'out-octets'),
+                KpiSampleType.KPISAMPLETYPE_PACKETS_RECEIVED   : PATH_IF_CTR.format(interface_name, 'in-pkts'   ),
+                KpiSampleType.KPISAMPLETYPE_PACKETS_TRANSMITTED: PATH_IF_CTR.format(interface_name, 'out-pkts'  ),
+            }
+
+            if len(endpoint) == 0: continue
+            response.append(('/endpoints/endpoint[{:s}]'.format(endpoint['uuid']), endpoint))
+        return response
diff --git a/src/device/service/drivers/gnmi_openconfig/handlers/Interface.py b/src/device/service/drivers/gnmi_openconfig/handlers/Interface.py
new file mode 100644
index 0000000000000000000000000000000000000000..20f79b3c2e15b58ab99166a68422fd35d40fd00f
--- /dev/null
+++ b/src/device/service/drivers/gnmi_openconfig/handlers/Interface.py
@@ -0,0 +1,248 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import json, logging
+from typing import Any, Dict, List, Tuple
+from ._Handler import _Handler
+from .Tools import dict_get_first
+
+LOGGER = logging.getLogger(__name__)
+
+class InterfaceHandler(_Handler):
+    def get_resource_key(self) -> str: return '/interface'
+    def get_path(self) -> str: return '/interfaces/interface'
+
+    def compose(self, resource_key : str, resource_value : Dict, delete : bool = False) -> Tuple[str, str]:
+        if_name          = str (resource_value['name'                         ])    # ethernet-1/1
+        sif_index        = int (resource_value.get('sub_if_index'       , 0   ))    # 0
+
+        if delete:
+            PATH_TMPL = '/interfaces/interface[name={:s}]/subinterfaces/subinterface[index={:d}]'
+            str_path = PATH_TMPL.format(if_name, sif_index)
+            str_data = json.dumps({})
+            return str_path, str_data
+
+        if_enabled       = bool(resource_value.get('enabled'            , True))    # True/False
+        sif_enabled      = bool(resource_value.get('sub_if_enabled'     , True))    # True/False
+        sif_ipv4_enabled = bool(resource_value.get('sub_if_ipv4_enabled', True))    # True/False
+        sif_ipv4_address = str (resource_value['sub_if_ipv4_address'          ])    # 172.16.0.1
+        sif_ipv4_prefix  = int (resource_value['sub_if_ipv4_prefix'           ])    # 24
+
+        str_path = '/interfaces/interface[name={:s}]'.format(if_name)
+        str_data = json.dumps({
+            'name': if_name,
+            'config': {'name': if_name, 'enabled': if_enabled},
+            'subinterfaces': {
+                'subinterface': {
+                    'index': sif_index,
+                    'config': {'index': sif_index, 'enabled': sif_enabled},
+                    'ipv4': {
+                        'config': {'enabled': sif_ipv4_enabled},
+                        'addresses': {
+                            'address': {
+                                'ip': sif_ipv4_address,
+                                'config': {'ip': sif_ipv4_address, 'prefix_length': sif_ipv4_prefix},
+                            }
+                        }
+                    }
+                }
+            }
+        })
+        return str_path, str_data
+
+    def parse(self, json_data : Dict) -> List[Tuple[str, Dict[str, Any]]]:
+        #LOGGER.info('json_data = {:s}'.format(json.dumps(json_data)))
+        json_interface_list : List[Dict] = json_data.get('interface', [])
+
+        response = []
+        for json_interface in json_interface_list:
+            #LOGGER.info('json_interface = {:s}'.format(json.dumps(json_interface)))
+
+            interface = {}
+
+            interface_name = json_interface.get('name')
+            if interface_name is None:
+                LOGGER.info('DISCARDED json_interface = {:s}'.format(json.dumps(json_interface)))
+                continue
+            interface['name'] = interface_name
+
+            CONFIG_FIELDS = ('config', 'openconfig-interface:config', 'oci:config')
+            json_config : Dict = dict_get_first(json_interface, CONFIG_FIELDS, default={})
+
+            STATE_FIELDS = ('state', 'openconfig-interface:state', 'oci:state')
+            json_state : Dict = dict_get_first(json_interface, STATE_FIELDS, default={})
+
+            interface_type = json_config.get('type')
+            if interface_type is None: interface_type = json_state.get('type')
+            if interface_type is None:
+                LOGGER.info('DISCARDED json_interface = {:s}'.format(json.dumps(json_interface)))
+                continue
+            interface_type = interface_type.replace('ianaift:', '')
+            interface_type = interface_type.replace('iana-if-type:', '')
+            interface['type'] = interface_type
+
+            interface_mtu = json_config.get('mtu')
+            if interface_mtu is None: interface_mtu = json_state.get('mtu')
+            if interface_mtu is not None: interface['mtu'] = int(interface_mtu)
+
+            interface_enabled = json_config.get('enabled')
+            if interface_enabled is None: interface_enabled = json_state.get('enabled')
+            interface['enabled'] = False if interface_enabled is None else bool(interface_enabled)
+
+            interface_management = json_config.get('management')
+            if interface_management is None: interface_management = json_state.get('management')
+            interface['management'] = False if interface_management is None else bool(interface_management)
+
+            interface_descr = json_interface.get('config', {}).get('description')
+            if interface_descr is not None: interface['description'] = interface_descr
+
+            json_subinterfaces = json_interface.get('subinterfaces', {})
+            json_subinterface_list : List[Dict] = json_subinterfaces.get('subinterface', [])
+
+            for json_subinterface in json_subinterface_list:
+                #LOGGER.info('json_subinterface = {:s}'.format(json.dumps(json_subinterface)))
+
+                subinterface = {}
+
+                subinterface_index = json_subinterface.get('state', {}).get('index')
+                if subinterface_index is None: continue
+                subinterface['index'] = int(subinterface_index)
+
+                subinterface_name = json_subinterface.get('state', {}).get('name')
+                if subinterface_name is None: continue
+                subinterface['name'] = subinterface_name
+
+                subinterface_enabled = json_subinterface.get('state', {}).get('enabled', False)
+                subinterface['enabled'] = bool(subinterface_enabled)
+
+                VLAN_FIELDS = ('vlan', 'openconfig-vlan:vlan', 'ocv:vlan')
+                json_vlan = dict_get_first(json_subinterface, VLAN_FIELDS, default={})
+
+                MATCH_FIELDS = ('match', 'openconfig-vlan:match', 'ocv:match')
+                json_vlan = dict_get_first(json_vlan, MATCH_FIELDS, default={})
+
+                SIN_TAG_FIELDS = ('single-tagged', 'openconfig-vlan:single-tagged', 'ocv:single-tagged')
+                json_vlan = dict_get_first(json_vlan, SIN_TAG_FIELDS, default={})
+
+                CONFIG_FIELDS = ('config', 'openconfig-vlan:config', 'ocv:config')
+                json_vlan = dict_get_first(json_vlan, CONFIG_FIELDS, default={})
+
+                VLAN_ID_FIELDS = ('vlan-id', 'openconfig-vlan:vlan-id', 'ocv:vlan-id')
+                subinterface_vlan_id = dict_get_first(json_vlan, VLAN_ID_FIELDS)
+                if subinterface_vlan_id is not None: subinterface['vlan_id'] = subinterface_vlan_id
+
+
+                # TODO: implement support for multiple IP addresses per subinterface
+
+                IPV4_FIELDS = ('ipv4', 'openconfig-if-ip:ipv4', 'ociip:ipv4')
+                json_ipv4 = dict_get_first(json_subinterface, IPV4_FIELDS, default={})
+                
+                IPV4_ADDRESSES_FIELDS = ('addresses', 'openconfig-if-ip:addresses', 'ociip:addresses')
+                json_ipv4_addresses = dict_get_first(json_ipv4, IPV4_ADDRESSES_FIELDS, default={})
+
+                IPV4_ADDRESS_FIELDS = ('address', 'openconfig-if-ip:address', 'ociip:address')
+                json_ipv4_address_list : List[Dict] = dict_get_first(json_ipv4_addresses, IPV4_ADDRESS_FIELDS, default=[])
+
+                #ipv4_addresses = []
+                for json_ipv4_address in json_ipv4_address_list:
+                    #LOGGER.info('json_ipv4_address = {:s}'.format(json.dumps(json_ipv4_address)))
+
+                    STATE_FIELDS = ('state', 'openconfig-if-ip:state', 'ociip:state')
+                    json_ipv4_address_state = dict_get_first(json_ipv4_address, STATE_FIELDS, default={})
+
+                    #ipv4_address = {}
+
+                    #ORIGIN_FIELDS = ('origin', 'openconfig-if-ip:origin', 'ociip:origin')
+                    #ipv4_address_origin = dict_get_first(json_ipv4_address_state, ORIGIN_FIELDS, default={})
+                    #if ipv4_address_origin is not None: ipv4_address['origin'] = ipv4_address_origin
+
+                    IP_FIELDS = ('ip', 'openconfig-if-ip:ip', 'ociip:ip')
+                    ipv4_address_ip = dict_get_first(json_ipv4_address_state, IP_FIELDS)
+                    #if ipv4_address_ip is not None: ipv4_address['address_ip'] = ipv4_address_ip
+                    if ipv4_address_ip is not None: subinterface['address_ip'] = ipv4_address_ip
+
+                    PREFIX_FIELDS = ('prefix-length', 'openconfig-if-ip:prefix-length', 'ociip:prefix-length')
+                    ipv4_address_prefix = dict_get_first(json_ipv4_address_state, PREFIX_FIELDS)
+                    #if ipv4_address_prefix is not None: ipv4_address['address_prefix'] = int(ipv4_address_prefix)
+                    if ipv4_address_prefix is not None: subinterface['address_prefix'] = int(ipv4_address_prefix)
+
+                    #if len(ipv4_address) == 0: continue
+                    #ipv4_addresses.append(ipv4_address)
+
+                #subinterface['ipv4_addresses'] = ipv4_addresses
+
+                if len(subinterface) == 0: continue
+                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))
+
+        return response
+
+    def parse_counters(self, json_data : Dict) -> List[Tuple[str, Dict[str, Any]]]:
+        LOGGER.info('[parse_counters] json_data = {:s}'.format(json.dumps(json_data)))
+        json_interface_list : List[Dict] = json_data.get('interface', [])
+
+        response = []
+        for json_interface in json_interface_list:
+            LOGGER.info('[parse_counters] json_interface = {:s}'.format(json.dumps(json_interface)))
+
+            interface = {}
+
+            NAME_FIELDS = ('name', 'openconfig-interface:name', 'oci:name')
+            interface_name = dict_get_first(json_interface, NAME_FIELDS)
+            if interface_name is None: continue
+            interface['name'] = interface_name
+
+            STATE_FIELDS = ('state', 'openconfig-interface:state', 'oci:state')
+            json_state = dict_get_first(json_interface, STATE_FIELDS, default={})
+
+            COUNTERS_FIELDS = ('counters', 'openconfig-interface:counters', 'oci:counters')
+            json_counters = dict_get_first(json_state, COUNTERS_FIELDS, default={})
+
+            IN_PKTS_FIELDS = ('in-pkts', 'openconfig-interface:in-pkts', 'oci:in-pkts')
+            interface_in_pkts = dict_get_first(json_counters, IN_PKTS_FIELDS)
+            if interface_in_pkts is not None: interface['in-pkts'] = int(interface_in_pkts)
+
+            IN_OCTETS_FIELDS = ('in-octets', 'openconfig-interface:in-octets', 'oci:in-octets')
+            interface_in_octets = dict_get_first(json_counters, IN_OCTETS_FIELDS)
+            if interface_in_octets is not None: interface['in-octets'] = int(interface_in_octets)
+
+            IN_ERRORS_FIELDS = ('in-errors', 'openconfig-interface:in-errors', 'oci:in-errors')
+            interface_in_errors = dict_get_first(json_counters, IN_ERRORS_FIELDS)
+            if interface_in_errors is not None: interface['in-errors'] = int(interface_in_errors)
+
+            OUT_OCTETS_FIELDS = ('out-octets', 'openconfig-interface:out-octets', 'oci:out-octets')
+            interface_out_octets = dict_get_first(json_counters, OUT_OCTETS_FIELDS)
+            if interface_out_octets is not None: interface['out-octets'] = int(interface_out_octets)
+
+            OUT_PKTS_FIELDS = ('out-pkts', 'openconfig-interface:out-pkts', 'oci:out-pkts')
+            interface_out_pkts = dict_get_first(json_counters, OUT_PKTS_FIELDS)
+            if interface_out_pkts is not None: interface['out-pkts'] = int(interface_out_pkts)
+
+            OUT_ERRORS_FIELDS = ('out-errors', 'openconfig-interface:out-errors', 'oci:out-errors')
+            interface_out_errors = dict_get_first(json_counters, OUT_ERRORS_FIELDS)
+            if interface_out_errors is not None: interface['out-errors'] = int(interface_out_errors)
+
+            OUT_DISCARDS_FIELDS = ('out-discards', 'openconfig-interface:out-discards', 'oci:out-discards')
+            interface_out_discards = dict_get_first(json_counters, OUT_DISCARDS_FIELDS)
+            if interface_out_discards is not None: interface['out-discards'] = int(interface_out_discards)
+
+            #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/gnmi_openconfig/handlers/InterfaceCounter.py b/src/device/service/drivers/gnmi_openconfig/handlers/InterfaceCounter.py
new file mode 100644
index 0000000000000000000000000000000000000000..a45dc9e7f972445691143df15d6d56d079384fc4
--- /dev/null
+++ b/src/device/service/drivers/gnmi_openconfig/handlers/InterfaceCounter.py
@@ -0,0 +1,80 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import json, logging
+from typing import Any, Dict, List, Tuple
+from ._Handler import _Handler
+from .Tools import dict_get_first
+
+LOGGER = logging.getLogger(__name__)
+
+class InterfaceCounterHandler(_Handler):
+    def get_resource_key(self) -> str: return '/interface/counters'
+    def get_path(self) -> str: return '/interfaces/interface/state/counters'
+
+    def parse(self, json_data : Dict) -> List[Tuple[str, Dict[str, Any]]]:
+        LOGGER.info('[parse] json_data = {:s}'.format(json.dumps(json_data)))
+        json_interface_list : List[Dict] = json_data.get('interface', [])
+
+        response = []
+        for json_interface in json_interface_list:
+            LOGGER.info('[parse] json_interface = {:s}'.format(json.dumps(json_interface)))
+
+            interface = {}
+
+            NAME_FIELDS = ('name', 'openconfig-interface:name', 'oci:name')
+            interface_name = dict_get_first(json_interface, NAME_FIELDS)
+            if interface_name is None: continue
+            interface['name'] = interface_name
+
+            STATE_FIELDS = ('state', 'openconfig-interface:state', 'oci:state')
+            json_state = dict_get_first(json_interface, STATE_FIELDS, default={})
+
+            COUNTERS_FIELDS = ('counters', 'openconfig-interface:counters', 'oci:counters')
+            json_counters = dict_get_first(json_state, COUNTERS_FIELDS, default={})
+
+            IN_PKTS_FIELDS = ('in-pkts', 'openconfig-interface:in-pkts', 'oci:in-pkts')
+            interface_in_pkts = dict_get_first(json_counters, IN_PKTS_FIELDS)
+            if interface_in_pkts is not None: interface['in-pkts'] = int(interface_in_pkts)
+
+            IN_OCTETS_FIELDS = ('in-octets', 'openconfig-interface:in-octets', 'oci:in-octets')
+            interface_in_octets = dict_get_first(json_counters, IN_OCTETS_FIELDS)
+            if interface_in_octets is not None: interface['in-octets'] = int(interface_in_octets)
+
+            IN_ERRORS_FIELDS = ('in-errors', 'openconfig-interface:in-errors', 'oci:in-errors')
+            interface_in_errors = dict_get_first(json_counters, IN_ERRORS_FIELDS)
+            if interface_in_errors is not None: interface['in-errors'] = int(interface_in_errors)
+
+            OUT_OCTETS_FIELDS = ('out-octets', 'openconfig-interface:out-octets', 'oci:out-octets')
+            interface_out_octets = dict_get_first(json_counters, OUT_OCTETS_FIELDS)
+            if interface_out_octets is not None: interface['out-octets'] = int(interface_out_octets)
+
+            OUT_PKTS_FIELDS = ('out-pkts', 'openconfig-interface:out-pkts', 'oci:out-pkts')
+            interface_out_pkts = dict_get_first(json_counters, OUT_PKTS_FIELDS)
+            if interface_out_pkts is not None: interface['out-pkts'] = int(interface_out_pkts)
+
+            OUT_ERRORS_FIELDS = ('out-errors', 'openconfig-interface:out-errors', 'oci:out-errors')
+            interface_out_errors = dict_get_first(json_counters, OUT_ERRORS_FIELDS)
+            if interface_out_errors is not None: interface['out-errors'] = int(interface_out_errors)
+
+            OUT_DISCARDS_FIELDS = ('out-discards', 'openconfig-interface:out-discards', 'oci:out-discards')
+            interface_out_discards = dict_get_first(json_counters, OUT_DISCARDS_FIELDS)
+            if interface_out_discards is not None: interface['out-discards'] = int(interface_out_discards)
+
+            #LOGGER.info('[parse] 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/gnmi_openconfig/handlers/NetworkInstance.py b/src/device/service/drivers/gnmi_openconfig/handlers/NetworkInstance.py
new file mode 100644
index 0000000000000000000000000000000000000000..aed821a06fa7fcafe96a21ad5f5fa06be2902038
--- /dev/null
+++ b/src/device/service/drivers/gnmi_openconfig/handlers/NetworkInstance.py
@@ -0,0 +1,62 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import json, logging
+from typing import Any, Dict, List, Tuple
+from ._Handler import _Handler
+
+LOGGER = logging.getLogger(__name__)
+
+class NetworkInstanceHandler(_Handler):
+    def get_resource_key(self) -> str: return '/network_instance'
+    def get_path(self) -> str: return '/network-instances/network-instance'
+
+    def compose(self, resource_key : str, resource_value : Dict, delete : bool = False) -> Tuple[str, str]:
+        ni_name   = str(resource_value['name'])   # test-svc
+
+        if delete:
+            PATH_TMPL = '/network-instances/network-instance[name={:s}]'
+            str_path = PATH_TMPL.format(ni_name)
+            str_data = json.dumps({})
+            return str_path, str_data
+
+        ni_type   = str(resource_value['type'])   # L3VRF / L2VSI / ...
+
+        # not works: [FailedPrecondition] unsupported identifier 'DIRECTLY_CONNECTED'
+        #protocols = [self._compose_directly_connected()]
+
+        MAP_OC_NI_TYPE = {
+            'L3VRF': 'openconfig-network-instance-types:L3VRF',
+        }
+        ni_type = MAP_OC_NI_TYPE.get(ni_type, ni_type)
+
+        str_path = '/network-instances/network-instance[name={:s}]'.format(ni_name)
+        str_data = json.dumps({
+            'name': ni_name,
+            'config': {'name': ni_name, 'type': ni_type},
+            #'protocols': {'protocol': protocols},
+        })
+        return str_path, str_data
+
+    def _compose_directly_connected(self, name=None, enabled=True) -> Dict:
+        identifier = 'DIRECTLY_CONNECTED'
+        if name is None: name = 'DIRECTLY_CONNECTED'
+        return {
+            'identifier': identifier, 'name': name,
+            'config': {'identifier': identifier, 'name': name, 'enabled': enabled},
+        }
+
+    def parse(self, json_data : Dict) -> List[Tuple[str, Dict[str, Any]]]:
+        response = []
+        return response
diff --git a/src/device/service/drivers/gnmi_openconfig/handlers/NetworkInstanceInterface.py b/src/device/service/drivers/gnmi_openconfig/handlers/NetworkInstanceInterface.py
new file mode 100644
index 0000000000000000000000000000000000000000..205373fca870ea7338a3c9c043c60306b535c1c0
--- /dev/null
+++ b/src/device/service/drivers/gnmi_openconfig/handlers/NetworkInstanceInterface.py
@@ -0,0 +1,46 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import json, logging
+from typing import Any, Dict, List, Tuple
+from ._Handler import _Handler
+
+LOGGER = logging.getLogger(__name__)
+
+class NetworkInstanceInterfaceHandler(_Handler):
+    def get_resource_key(self) -> str: return '/network_instance/interface'
+    def get_path(self) -> str: return '/network-instances/network-instance/interfaces'
+
+    def compose(self, resource_key : str, resource_value : Dict, delete : bool = False) -> Tuple[str, str]:
+        ni_name   = str(resource_value['name'     ])    # test-svc
+        if_name   = str(resource_value['if_name'  ])    # ethernet-1/1
+        sif_index = int(resource_value['sif_index'])    # 0
+        if_id     = '{:s}.{:d}'.format(if_name, sif_index)
+
+        if delete:
+            PATH_TMPL = '/network-instances/network-instance[name={:s}]/interfaces/interface[id={:s}]'
+            str_path = PATH_TMPL.format(ni_name, if_id)
+            str_data = json.dumps({})
+            return str_path, str_data
+
+        str_path = '/network-instances/network-instance[name={:s}]/interfaces/interface[id={:s}]'.format(ni_name, if_id)
+        str_data = json.dumps({
+            'id': if_id,
+            'config': {'id': if_id, 'interface': if_name, 'subinterface': sif_index},
+        })
+        return str_path, str_data
+
+    def parse(self, json_data : Dict) -> List[Tuple[str, Dict[str, Any]]]:
+        response = []
+        return response
diff --git a/src/device/service/drivers/gnmi_openconfig/handlers/NetworkInstanceStaticRoute.py b/src/device/service/drivers/gnmi_openconfig/handlers/NetworkInstanceStaticRoute.py
new file mode 100644
index 0000000000000000000000000000000000000000..9d75e9ac66e023c8f7be44d892cb6eec647761eb
--- /dev/null
+++ b/src/device/service/drivers/gnmi_openconfig/handlers/NetworkInstanceStaticRoute.py
@@ -0,0 +1,61 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import json, logging
+from typing import Any, Dict, List, Tuple
+from ._Handler import _Handler
+
+LOGGER = logging.getLogger(__name__)
+
+class NetworkInstanceStaticRouteHandler(_Handler):
+    def get_resource_key(self) -> str: return '/network_instance/static_route'
+    def get_path(self) -> str: return '/network-instances/network-instance/static_route'
+
+    def compose(self, resource_key : str, resource_value : Dict, delete : bool = False) -> Tuple[str, str]:
+        ni_name        = str(resource_value['name'                 ]) # test-svc
+        prefix         = str(resource_value['prefix'               ]) # '172.0.1.0/24'
+
+        identifier = 'STATIC'
+        name = 'static'
+        if delete:
+            PATH_TMPL  = '/network-instances/network-instance[name={:s}]/protocols'
+            PATH_TMPL += '/protocol[identifier={:s}][name={:s}]/static-routes/static[prefix={:s}]'
+            str_path = PATH_TMPL.format(ni_name, identifier, name, prefix)
+            str_data = json.dumps({})
+            return str_path, str_data
+
+        next_hop       = str(resource_value['next_hop'             ]) # '172.0.0.1'
+        next_hop_index = int(resource_value.get('next_hop_index', 0)) # 0
+
+        PATH_TMPL = '/network-instances/network-instance[name={:s}]/protocols/protocol[identifier={:s}][name={:s}]'
+        str_path = PATH_TMPL.format(ni_name, identifier, name)
+        str_data = json.dumps({
+            'identifier': identifier, 'name': name,
+            'config': {'identifier': identifier, 'name': name, 'enabled': True},
+            'static_routes': {'static': [{
+                'prefix': prefix,
+                'config': {'prefix': prefix},
+                'next_hops': {
+                    'next-hop': [{
+                        'index': next_hop_index,
+                        'config': {'index': next_hop_index, 'next_hop': next_hop}
+                    }]
+                }
+            }]}
+        })
+        return str_path, str_data
+
+    def parse(self, json_data : Dict) -> List[Tuple[str, Dict[str, Any]]]:
+        response = []
+        return response
diff --git a/src/device/service/drivers/gnmi_openconfig/handlers/Tools.py b/src/device/service/drivers/gnmi_openconfig/handlers/Tools.py
new file mode 100644
index 0000000000000000000000000000000000000000..30343ac28a46a0c1d24bcb66d07fa03fa377f9fa
--- /dev/null
+++ b/src/device/service/drivers/gnmi_openconfig/handlers/Tools.py
@@ -0,0 +1,30 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import re
+from typing import Any, Dict, Iterable
+
+RE_REMOVE_FILTERS = re.compile(r'\[[^\]]+\]')
+RE_REMOVE_NAMESPACES = re.compile(r'\/[a-zA-Z0-9\_\-]+:')
+
+def get_schema(resource_key : str):
+    resource_key = RE_REMOVE_FILTERS.sub('', resource_key)
+    resource_key = RE_REMOVE_NAMESPACES.sub('/', resource_key)
+    return resource_key
+
+def dict_get_first(d : Dict, field_names : Iterable[str], default=None) -> Any:
+    for field_name in field_names:
+        if field_name not in d: continue
+        return d[field_name]
+    return default
diff --git a/src/device/service/drivers/gnmi_openconfig/handlers/_Handler.py b/src/device/service/drivers/gnmi_openconfig/handlers/_Handler.py
new file mode 100644
index 0000000000000000000000000000000000000000..d20c77b1165decce7ea07243beb782a6b749734b
--- /dev/null
+++ b/src/device/service/drivers/gnmi_openconfig/handlers/_Handler.py
@@ -0,0 +1,32 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from typing import Any, Dict, List, Tuple
+
+class _Handler:
+    def get_resource_key(self) -> str:
+        # Retrieve the TeraFlowSDN resource_key path schema used to point this handler
+        raise NotImplementedError()
+
+    def get_path(self) -> str:
+        # Retrieve the OpenConfig path schema used to interrogate the device
+        raise NotImplementedError()
+
+    def compose(self, resource_key : str, resource_value : Dict, delete : bool = False) -> Tuple[str, str]:
+        # Compose a Set/Delete message based on the resource_key/resource_value fields, and the delete flag
+        raise NotImplementedError()
+
+    def parse(self, json_data : Dict) -> List[Tuple[str, Dict[str, Any]]]:
+        # Parse a Reply from the device and return a list of resource_key/resource_value pairs
+        raise NotImplementedError()
diff --git a/src/device/service/drivers/gnmi_openconfig/handlers/__init__.py b/src/device/service/drivers/gnmi_openconfig/handlers/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..39cd7c66ad5e8c16e89192ad0f2ffb7c43ae6c50
--- /dev/null
+++ b/src/device/service/drivers/gnmi_openconfig/handlers/__init__.py
@@ -0,0 +1,103 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging
+from typing import Dict, List, Optional, Tuple, Union
+from device.service.driver_api._Driver import RESOURCE_ENDPOINTS, RESOURCE_INTERFACES, RESOURCE_NETWORK_INSTANCES
+from ._Handler import _Handler
+from .Component import ComponentHandler
+from .Interface import InterfaceHandler
+from .InterfaceCounter import InterfaceCounterHandler
+from .NetworkInstance import NetworkInstanceHandler
+from .NetworkInstanceInterface import NetworkInstanceInterfaceHandler
+from .NetworkInstanceStaticRoute import NetworkInstanceStaticRouteHandler
+from .Tools import get_schema
+
+LOGGER = logging.getLogger(__name__)
+
+comph  = ComponentHandler()
+ifaceh = InterfaceHandler()
+ifctrh = InterfaceCounterHandler()
+nih    = NetworkInstanceHandler()
+niifh  = NetworkInstanceInterfaceHandler()
+nisrh  = NetworkInstanceStaticRouteHandler()
+
+ALL_RESOURCE_KEYS = [
+    RESOURCE_ENDPOINTS,
+    RESOURCE_INTERFACES,
+    RESOURCE_NETWORK_INSTANCES,
+]
+
+RESOURCE_KEY_MAPPER = {
+    RESOURCE_ENDPOINTS         : comph.get_resource_key(),
+    RESOURCE_INTERFACES        : ifaceh.get_resource_key(),
+    RESOURCE_NETWORK_INSTANCES : nih.get_resource_key(),
+}
+
+PATH_MAPPER = {
+    '/components'        : comph.get_path(),
+    '/interfaces'        : ifaceh.get_path(),
+    '/network-instances' : nih.get_path(),
+}
+
+RESOURCE_KEY_TO_HANDLER = {
+    comph.get_resource_key()  : comph,
+    ifaceh.get_resource_key() : ifaceh,
+    ifctrh.get_resource_key() : ifctrh,
+    nih.get_resource_key()    : nih,
+    niifh.get_resource_key()  : niifh,
+    nisrh.get_resource_key()  : nisrh,
+}
+
+PATH_TO_HANDLER = {
+    comph.get_path()  : comph,
+    ifaceh.get_path() : ifaceh,
+    ifctrh.get_path() : ifctrh,
+    nih.get_path()    : nih,
+    niifh.get_path()  : niifh,
+    nisrh.get_path()  : nisrh,
+}
+
+def get_handler(
+    resource_key : Optional[str] = None, path : Optional[str] = None, raise_if_not_found=True
+) -> Optional[_Handler]:
+    if (resource_key is None) == (path is None):
+        MSG = 'Exactly one of resource_key({:s}) or path({:s}) must be specified'
+        raise Exception(MSG.format(str(resource_key), str(path))) # pylint: disable=broad-exception-raised
+    if resource_key is not None:
+        resource_key_schema = get_schema(resource_key)
+        resource_key_schema = RESOURCE_KEY_MAPPER.get(resource_key_schema, resource_key_schema)
+        handler = RESOURCE_KEY_TO_HANDLER.get(resource_key_schema)
+        if handler is None and raise_if_not_found:
+            MSG = 'Handler not found: resource_key={:s} resource_key_schema={:s}'
+            # pylint: disable=broad-exception-raised
+            raise Exception(MSG.format(str(resource_key), str(resource_key_schema)))
+    elif path is not None:
+        path_schema = get_schema(path)
+        path_schema = PATH_MAPPER.get(path_schema, path_schema)
+        handler = PATH_TO_HANDLER.get(path_schema)
+        if handler is None and raise_if_not_found:
+            MSG = 'Handler not found: resource_key={:s} resource_key_schema={:s}'
+            # pylint: disable=broad-exception-raised
+            raise Exception(MSG.format(str(resource_key), str(resource_key_schema)))
+    return handler
+
+def get_path(resource_key : str) -> str:
+    return get_handler(resource_key=resource_key).get_path()
+
+def parse(str_path : str, value : Union[Dict, List]):
+    return get_handler(path=str_path).parse(value)
+
+def compose(resource_key : str, resource_value : Union[Dict, List], delete : bool = False) -> Tuple[str, str]:
+    return get_handler(resource_key=resource_key).compose(resource_key, resource_value, delete=delete)
diff --git a/src/device/service/drivers/gnmi_openconfig/handlers/old_bgp_handler.txt b/src/device/service/drivers/gnmi_openconfig/handlers/old_bgp_handler.txt
new file mode 100644
index 0000000000000000000000000000000000000000..595a19788bce7ff0990346ef249ad3ce71f55efa
--- /dev/null
+++ b/src/device/service/drivers/gnmi_openconfig/handlers/old_bgp_handler.txt
@@ -0,0 +1,138 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+# WARNING: this handler is work in progress. Use with care!
+
+import logging, json
+from typing import Any, Dict, List, Tuple
+
+LOGGER = logging.getLogger(__name__)
+
+class NetworkInstanceHandler:
+    def get_resource_key(self) -> str: return '/network_instance'
+    def get_path(self) -> str: return '/network-instances/network-instance'
+
+    def compose_set(self, resource_key : str, resource_value : Dict) -> Tuple[str, str]:
+        ni_name = str(resource_value['name'])   # test-svc
+        ni_type = str(resource_value['type'])   # L3VRF / 
+
+        if_name          = str (resource_value['name'                         ])    # ethernet-1/1
+        if_enabled       = bool(resource_value.get('enabled'            , True))    # True/False
+        sif_index        = int (resource_value.get('sub_if_index'       , 0   ))    # 0
+        sif_enabled      = bool(resource_value.get('sub_if_enabled'     , True))    # True/False
+        sif_ipv4_enabled = bool(resource_value.get('sub_if_ipv4_enabled', True))    # True/False
+        sif_ipv4_address = str (resource_value['sub_if_ipv4_address'          ])    # 172.16.0.1
+        sif_ipv4_prefix  = int (resource_value['sub_if_ipv4_prefix'           ])    # 24
+
+        str_path = '/interfaces/interface[name={:s}]'.format(if_name)
+        str_data = json.dumps({
+            "name": if_name,
+            "config": {"name": if_name, "enabled": if_enabled},
+            "subinterfaces": {
+                "subinterface": {
+                    "index": sif_index,
+                    "config": {"index": sif_index, "enabled": sif_enabled},
+                    "ipv4": {
+                        "config": {"enabled": sif_ipv4_enabled},
+                        "addresses": {
+                            "address": {
+                                "ip": sif_ipv4_address,
+                                "config": {"ip": sif_ipv4_address, "prefix_length": sif_ipv4_prefix},
+                            }
+                        }
+                    }
+                }
+            }
+        })
+        return str_path, str_data
+
+
+        #oc_ni = openconfig_network_instance()
+        #ni = oc_ni.network_instances.network_instance.add(name=ni_name)
+        #ni.config.name = ni_name
+
+        #ni_desc = resource_value.get('description')
+        #if ni_desc is not None: ni.config.description = ni_desc
+
+        #if ni_type == 'L3VRF':
+        #    ni.config.type = 'L3VRF'
+        #    #ni_router_id  = resource_value.get('router_id')
+        #    #if ni_router_id is not None: ni.config.router_id = ni_router_id
+
+        #    proto_bgp = ni.protocols.protocol.add(identifier='BGP', name=ni_name)
+        #    proto_bgp.config.identifier = 'BGP'
+        #    proto_bgp.config.name = ni_name
+        #    proto_bgp.config.enabled = True
+        #    proto_bgp.bgp.global_.config.as_ = 65000
+        #    proto_bgp.bgp.global_.config.router_id = '172.0.0.1'
+
+        #    #ni.config.route_distinguisher = resource_value['route_distinguisher']
+        #elif ni_type == 'L3VRF':
+        #    pass
+        #else:
+        #    raise NotImplementedError()
+        
+        #str_path = '/network-instances/network-instance[name={:s}]'.format(ni_name)
+        #str_data = pybindJSON.dumps(ni, mode='default')
+
+        #str_path = '/network-instances/network-instance[name={:s}]/protocols/protocol[identifier=BGP][name=BGP]'.format(ni_name)
+        #str_data = json.dumps({
+        #    "identifier": "BGP",
+        #    "name": "BGP",
+        #    "config": {"identifier": "BGP", "name": "BGP", "enabled": True},
+        #    "bgp": {"global": {"config": {"as": 65000, "router-id": "5.5.5.5"}}}
+        #})
+
+        str_path = '/network-instances/network-instance[name=test-svc]'
+        str_data = json.dumps({
+            "name": "test-svc",
+            "config": {
+                "name": "test-svc",
+                "type": "openconfig-network-instance-types:L3VRF"
+            },
+            "protocols": {
+                "protocol": [
+                    {
+                        "identifier": "DIRECTLY_CONNECTED",
+                        "name": "DIRECTLY-CONNECTED",
+                        "config": {"identifier": "DIRECTLY_CONNECTED", "name": "DIRECTLY-CONNECTED", "enabled": True},
+                    },
+                    {
+                        "identifier": "STATIC",
+                        "name": "static",
+                        "config": {"identifier": "STATIC", "name": "static", "enabled": True},
+                        "static_routes": {
+                            "static": [
+                                {
+                                    "prefix": "172.0.1.0/24",
+                                    "config": {"prefix": "172.0.1.0/24"},
+                                    "next_hops": {
+                                        "next-hop": [{"index": 0, "config": {"index": 0, "next_hop": "172.0.0.1"}}]
+                                    }
+                                }
+                            ]
+                        }
+                    }
+                ]
+            },
+        })
+
+
+        #str_path = '/network-instances/network-instance[name={:s}]/protocols/protocol[identifier=DIRECTLY_CONNECTED][name=DIR]'.format(ni_name)
+        #str_data = json.dumps({
+        #    "identifier": "DIRECTLY_CONNECTED",
+        #    "name": "DIR",
+        #    "config": {"identifier": "DIRECTLY_CONNECTED", "name": "DIR", "enabled": True},
+        #})
diff --git a/src/device/service/drivers/gnmi_openconfig/tools/Capabilities.py b/src/device/service/drivers/gnmi_openconfig/tools/Capabilities.py
new file mode 100644
index 0000000000000000000000000000000000000000..b90bf3db887874d3c9015336cc105b3429c8e64e
--- /dev/null
+++ b/src/device/service/drivers/gnmi_openconfig/tools/Capabilities.py
@@ -0,0 +1,36 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from typing import Optional, Set, Union
+from common.tools.grpc.Tools import grpc_message_to_json
+from ..gnmi.gnmi_pb2 import CapabilityRequest   # pylint: disable=no-name-in-module
+from ..gnmi.gnmi_pb2_grpc import gNMIStub
+
+def get_supported_encodings(
+    stub : gNMIStub, username : str, password : str, timeout : Optional[int] = None
+) -> Set[Union[str, int]]:
+    metadata = [('username', username), ('password', password)]
+    req = CapabilityRequest()
+    reply = stub.Capabilities(req, metadata=metadata, timeout=timeout)
+
+    data = grpc_message_to_json(reply)
+    supported_encodings = {
+        supported_encoding
+        for supported_encoding in data.get('supported_encodings', [])
+        if isinstance(supported_encoding, str)
+    }
+    if len(supported_encodings) == 0:
+        # pylint: disable=broad-exception-raised
+        raise Exception('No supported encodings found')
+    return supported_encodings
diff --git a/src/device/service/drivers/gnmi_openconfig/tools/Channel.py b/src/device/service/drivers/gnmi_openconfig/tools/Channel.py
new file mode 100644
index 0000000000000000000000000000000000000000..264dd032166117873702643e17acb344b408b194
--- /dev/null
+++ b/src/device/service/drivers/gnmi_openconfig/tools/Channel.py
@@ -0,0 +1,34 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import grpc, logging, ssl
+
+def get_grpc_channel(address : str, port : int, use_tls : bool, logger : logging.Logger) -> grpc.Channel:
+    endpoint = str(address) + ':' + str(port)
+    logger.info('Connecting gNMI {:s}...'.format(endpoint))
+    if use_tls:
+        logger.debug('Getting server certificate...')
+        str_server_certificate = ssl.get_server_certificate((str(address), int(port)))
+        bytes_server_certificate = str_server_certificate.encode('UTF-8')
+        logger.debug('Using secure SSL channel...')
+        credentials = grpc.ssl_channel_credentials(
+            root_certificates=bytes_server_certificate, private_key=None, certificate_chain=None)
+        options = [
+            #('grpc.ssl_target_name_override', options.altName,)
+        ]
+        channel = grpc.secure_channel(endpoint, credentials, options)
+    else:
+        logger.debug('Using insecure channel...')
+        channel = grpc.insecure_channel(endpoint)
+    return channel
diff --git a/src/device/service/drivers/gnmi_openconfig/tools/Path.py b/src/device/service/drivers/gnmi_openconfig/tools/Path.py
new file mode 100644
index 0000000000000000000000000000000000000000..40ab28dc6bbaf8a65b667804dfe9285f36864e29
--- /dev/null
+++ b/src/device/service/drivers/gnmi_openconfig/tools/Path.py
@@ -0,0 +1,98 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import re
+from typing import List
+from ..gnmi.gnmi_pb2 import Path, PathElem
+
+RE_PATH_SPLIT = re.compile(r'/(?=(?:[^\[\]]|\[[^\[\]]+\])*$)')
+RE_PATH_KEYS = re.compile(r'\[(.*?)\]')
+
+def path_from_string(path='/'):
+    if not path: return Path(elem=[])
+
+    if path[0] == '/':
+        if path[-1] == '/':
+            path_list = RE_PATH_SPLIT.split(path)[1:-1]
+        else:
+            path_list = RE_PATH_SPLIT.split(path)[1:]
+    else:
+        if path[-1] == '/':
+            path_list = RE_PATH_SPLIT.split(path)[:-1]
+        else:
+            path_list = RE_PATH_SPLIT.split(path)
+
+    path = []
+    for elem in path_list:
+        elem_name = elem.split('[', 1)[0]
+        elem_keys = RE_PATH_KEYS.findall(elem)
+        dict_keys = dict(x.split('=', 1) for x in elem_keys)
+        path.append(PathElem(name=elem_name, key=dict_keys))
+
+    return Path(elem=path)
+
+def path_to_string(path : Path) -> str:
+    path_parts = list()
+    for elem in path.elem:
+        kv_list = list()
+        for key in elem.key:
+            value = elem.key[key]
+            kv = '{:s}={:s}'.format(key, value)
+            kv_list.append(kv)
+
+        path_part_name = elem.name
+        if len(kv_list) == 0:
+            path_parts.append(path_part_name)
+        else:
+            str_kv = ', '.join(kv_list)
+            path_part = '{:s}[{:s}]'.format(path_part_name, str_kv)
+            path_parts.append(path_part)
+
+    str_path = '/{:s}'.format('/'.join(path_parts))
+    return str_path
+
+def parse_xpath(xpath : str) -> str:
+    xpath = xpath.replace('//', '/')
+    xpath = xpath.replace('oci:interface[', 'interface[')
+    xpath = xpath.replace('/oci', '/openconfig-interfaces')
+    xpath = re.sub(r"\[oci:name='(.*?)'\]", r"[name=\1]", xpath)
+    # Eliminar el contador del final
+    xpath = '/'.join(xpath.split('/')[:-1]) + '/'
+    return xpath
+
+def split_resource_key(path):
+    pattern = r'/state/counters/(.*)'
+    match = re.search(pattern, path)
+    if match is None: return None
+    return match.group(1)
+
+def dict_to_xpath(d: dict) -> str:
+    xpath = '/'
+    for item in d['elem']:
+        name = item.get('name')
+        if name == 'interface':
+            key = item.get('key')
+            interface_name = key.get('name')
+            xpath += f"/oci:interface[oci:name='{interface_name}']"
+        else:
+            xpath += f"/{name}"
+    xpath = xpath.replace('openconfig-interfaces', 'oci')
+    return xpath
+
+def compose_path(base_path : str, path_filters : List[str] = []):
+    new_path = '' if base_path is None else str(base_path)
+    for path_filter in path_filters:
+        if path_filter == '': continue
+        new_path = '{:s}[{:s}]'.format(new_path, path_filter)
+    return new_path
diff --git a/src/device/service/drivers/gnmi_openconfig/tools/Subscriptions.py b/src/device/service/drivers/gnmi_openconfig/tools/Subscriptions.py
new file mode 100644
index 0000000000000000000000000000000000000000..18b6445ae31c00495c4f3a84922c9c8d4198b3f7
--- /dev/null
+++ b/src/device/service/drivers/gnmi_openconfig/tools/Subscriptions.py
@@ -0,0 +1,47 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# 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
+
+import anytree
+from typing import Any, List
+from device.service.driver_api.AnyTreeTools import TreeNode, get_subnode, set_subnode_value
+
+class Subscriptions:
+    def __init__(self) -> None:
+        self.__resolver = anytree.Resolver(pathattr='name')
+        self.__subscriptions = TreeNode('.')
+    
+    def add(
+        self, resource_path : List[str], sampling_duration : float, sampling_interval : float, value : Any
+    ) -> None:
+        subscription_path = resource_path + ['{:.3f}:{:.3f}'.format(sampling_duration, sampling_interval)]
+        set_subnode_value(self.__resolver, self.__subscriptions, subscription_path, value)
+
+    def get(
+        self, resource_path : List[str], sampling_duration : float, sampling_interval : float
+    ) -> TreeNode:
+        subscription_path = resource_path + ['{:.3f}:{:.3f}'.format(sampling_duration, sampling_interval)]
+        value = get_subnode(self.__resolver, self.__subscriptions, subscription_path)
+        return value
+
+    def delete(
+        self, reference : TreeNode
+    ) -> None:
+        parent : TreeNode = reference.parent
+        children = list(parent.children)
+        children.remove(reference)
+        parent.children = tuple(children)
diff --git a/src/device/service/drivers/gnmi_openconfig/tools/Value.py b/src/device/service/drivers/gnmi_openconfig/tools/Value.py
new file mode 100644
index 0000000000000000000000000000000000000000..4797930a17360d8a780e99ea9ac05c0e3a1f7abc
--- /dev/null
+++ b/src/device/service/drivers/gnmi_openconfig/tools/Value.py
@@ -0,0 +1,52 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import base64, json
+from typing import Any
+from ..gnmi.gnmi_pb2 import TypedValue
+
+def decode_value(value : TypedValue) -> Any:
+    encoding = value.WhichOneof('value')
+    if encoding == 'json_val':
+        value = value.json_val
+        #mdl, cls = self._classes[className]
+        #obj = json.loads(strObj)
+        #if isinstance(obj, (list,)):
+        #    obj = map(lambda n: pybindJSON.loads(n, mdl, cls.__name__), obj)
+        #    data = map(lambda n: json.loads(pybindJSON.dumps(n, mode='default')), obj)
+        #else:
+        #    obj = pybindJSON.loads(obj, mdl, cls.__name__)
+        #    data = json.loads(pybindJSON.dumps(obj, mode='default'))
+        raise NotImplementedError()
+        #return value
+    elif encoding == 'json_ietf_val':
+        value : str = value.json_ietf_val
+        try:
+            return json.loads(value)
+        except json.decoder.JSONDecodeError:
+            # Assume is Base64-encoded
+            b_b64_value = value.encode('UTF-8')
+            b_value = base64.b64decode(b_b64_value, validate=True)
+            value = b_value.decode('UTF-8')
+            return json.loads(value)
+    else:
+        MSG = 'Unsupported Encoding({:s}) in Value({:s})'
+        # pylint: disable=broad-exception-raised
+        raise Exception(MSG.format(str(encoding), str(value)))
+
+def value_exists(value) -> bool:
+    if value is None: return False
+    if isinstance(value, Exception): return False
+    if issubclass(type(value), Exception): return False
+    return True
diff --git a/src/device/service/drivers/gnmi_openconfig/tools/__init__.py b/src/device/service/drivers/gnmi_openconfig/tools/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..1549d9811aa5d1c193a44ad45d0d7773236c0612
--- /dev/null
+++ b/src/device/service/drivers/gnmi_openconfig/tools/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/device/service/drivers/openconfig/OpenConfigDriver.py b/src/device/service/drivers/openconfig/OpenConfigDriver.py
index c70dafe9462763310f55368b60232c3733bbd825..48a383f7ca71f23e840117e8129a3c1c76827a2f 100644
--- a/src/device/service/drivers/openconfig/OpenConfigDriver.py
+++ b/src/device/service/drivers/openconfig/OpenConfigDriver.py
@@ -32,6 +32,7 @@ from device.service.driver_api.AnyTreeTools import TreeNode, get_subnode, set_su
 from .templates import ALL_RESOURCE_KEYS, EMPTY_CONFIG, compose_config, get_filter, parse, cli_compose_config
 from .RetryDecorator import retry
 
+
 DEBUG_MODE = False
 logging.getLogger('ncclient.manager').setLevel(logging.DEBUG if DEBUG_MODE else logging.WARNING)
 logging.getLogger('ncclient.transport.ssh').setLevel(logging.DEBUG if DEBUG_MODE else logging.WARNING)
@@ -59,19 +60,19 @@ class NetconfSessionHandler:
         self.__connected = threading.Event()
         self.__address = address
         self.__port = int(port)
-        self.__username         = settings.get('username')
-        self.__password         = settings.get('password')
-        self.__vendor           = settings.get('vendor')
-        self.__version           = settings.get('version', "1")
-        self.__key_filename     = settings.get('key_filename')
-        self.__hostkey_verify   = settings.get('hostkey_verify', True)
-        self.__look_for_keys    = settings.get('look_for_keys', True)
-        self.__allow_agent      = settings.get('allow_agent', True)
-        self.__force_running    = settings.get('force_running', False)
-        self.__commit_per_rule  = settings.get('commit_per_rule', False)
-        self.__device_params    = settings.get('device_params', {})
-        self.__manager_params   = settings.get('manager_params', {})
-        self.__nc_params        = settings.get('nc_params', {})
+        self.__username        = settings.get('username')
+        self.__password        = settings.get('password')
+        self.__vendor          = settings.get('vendor')
+        self.__version         = settings.get('version', "1")
+        self.__key_filename    = settings.get('key_filename')
+        self.__hostkey_verify  = settings.get('hostkey_verify', True)
+        self.__look_for_keys   = settings.get('look_for_keys', True)
+        self.__allow_agent     = settings.get('allow_agent', True)
+        self.__force_running   = settings.get('force_running', False)
+        self.__commit_per_rule = settings.get('commit_per_rule', False)
+        self.__device_params   = settings.get('device_params', {})
+        self.__manager_params  = settings.get('manager_params', {})
+        self.__nc_params       = settings.get('nc_params', {})
         self.__message_renderer = settings.get('message_renderer','jinja')
         self.__manager : Manager   = None
         self.__candidate_supported = False
@@ -201,7 +202,7 @@ def do_sampling(
     except: # pylint: disable=bare-except
         logger.exception('Error retrieving samples')
 
-def edit_config(                                                                                                            # edit the configuration of openconfig devices
+def edit_config(
     netconf_handler : NetconfSessionHandler, logger : logging.Logger, resources : List[Tuple[str, Any]], delete=False,
     commit_per_rule=False, target='running', default_operation='merge', test_option=None, error_option=None,
     format='xml' # pylint: disable=redefined-builtin
diff --git a/src/device/service/drivers/openconfig/templates/ACL/__init__.py b/src/device/service/drivers/openconfig/templates/ACL/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..1549d9811aa5d1c193a44ad45d0d7773236c0612
--- /dev/null
+++ b/src/device/service/drivers/openconfig/templates/ACL/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/device/service/drivers/openconfig/templates/VPN/__init__.py b/src/device/service/drivers/openconfig/templates/VPN/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..1549d9811aa5d1c193a44ad45d0d7773236c0612
--- /dev/null
+++ b/src/device/service/drivers/openconfig/templates/VPN/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/device/tests/test_gnmi.py b/src/device/tests/test_gnmi.py
new file mode 100644
index 0000000000000000000000000000000000000000..50c9155822d5285fda5fc75777363c066ffb215a
--- /dev/null
+++ b/src/device/tests/test_gnmi.py
@@ -0,0 +1,115 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging, os, sys, time
+from typing import Dict, Tuple
+os.environ['DEVICE_EMULATED_ONLY'] = 'YES'
+from device.service.drivers.gnmi_openconfig.GnmiOpenConfigDriver import GnmiOpenConfigDriver # pylint: disable=wrong-import-position
+#from device.service.driver_api._Driver import (
+#    RESOURCE_ENDPOINTS, RESOURCE_INTERFACES, RESOURCE_NETWORK_INSTANCES, RESOURCE_ROUTING_POLICIES, RESOURCE_SERVICES
+#)
+
+logging.basicConfig(level=logging.DEBUG)
+LOGGER = logging.getLogger(__name__)
+LOGGER.setLevel(logging.DEBUG)
+
+# +---+---------------------------+--------------+---------------------------------+-------+---------+--------------------+--------------+
+# | # |           Name            | Container ID |              Image              | Kind  |  State  |    IPv4 Address    | IPv6 Address |
+# +---+---------------------------+--------------+---------------------------------+-------+---------+--------------------+--------------+
+# | 1 | clab-tfs-scenario-client1 | a8d48ec3265a | ghcr.io/hellt/network-multitool | linux | running | 172.100.100.201/24 | N/A          |
+# | 2 | clab-tfs-scenario-client2 | fc88436d2b32 | ghcr.io/hellt/network-multitool | linux | running | 172.100.100.202/24 | N/A          |
+# | 3 | clab-tfs-scenario-srl1    | b995b9bdadda | ghcr.io/nokia/srlinux           | srl   | running | 172.100.100.101/24 | N/A          |
+# | 4 | clab-tfs-scenario-srl2    | aacfc38cc376 | ghcr.io/nokia/srlinux           | srl   | running | 172.100.100.102/24 | N/A          |
+# +---+---------------------------+--------------+---------------------------------+-------+---------+--------------------+--------------+
+
+def interface(if_name, sif_index, ipv4_address, ipv4_prefix, enabled) -> Tuple[str, Dict]:
+    str_path = '/interface[{:s}]'.format(if_name)
+    str_data = {'name': if_name, 'enabled': enabled, 'sub_if_index': sif_index, 'sub_if_enabled': enabled,
+                'sub_if_ipv4_enabled': enabled, 'sub_if_ipv4_address': ipv4_address, 'sub_if_ipv4_prefix': ipv4_prefix}
+    return str_path, str_data
+
+def network_instance(ni_name, ni_type) -> Tuple[str, Dict]:
+    str_path = '/network_instance[{:s}]'.format(ni_name)
+    str_data = {'name': ni_name, 'type': ni_type}
+    return str_path, str_data
+
+def network_instance_static_route(ni_name, prefix, next_hop, next_hop_index=0) -> Tuple[str, Dict]:
+    str_path = '/network_instance[{:s}]/static_route[{:s}]'.format(ni_name, prefix)
+    str_data = {'name': ni_name, 'prefix': prefix, 'next_hop': next_hop, 'next_hop_index': next_hop_index}
+    return str_path, str_data
+
+def network_instance_interface(ni_name, if_name, sif_index) -> Tuple[str, Dict]:
+    str_path = '/network_instance[{:s}]/interface[{:s}.{:d}]'.format(ni_name, if_name, sif_index)
+    str_data = {'name': ni_name, 'if_name': if_name, 'sif_index': sif_index}
+    return str_path, str_data
+
+def main():
+    driver_settings = {
+        'protocol': 'gnmi',
+        'username': 'admin',
+        'password': 'NokiaSrl1!',
+        'use_tls' : True,
+    }
+    driver = GnmiOpenConfigDriver('172.100.100.102', 57400, **driver_settings)
+    driver.Connect()
+
+    #resources_to_get = []
+    #resources_to_get = [RESOURCE_ENDPOINTS]
+    #resources_to_get = [RESOURCE_INTERFACES]
+    #resources_to_get = [RESOURCE_NETWORK_INSTANCES]
+    #resources_to_get = [RESOURCE_ROUTING_POLICIES]
+    #resources_to_get = [RESOURCE_SERVICES]
+    #LOGGER.info('resources_to_get = {:s}'.format(str(resources_to_get)))
+    #results_getconfig = driver.GetConfig(resources_to_get)
+    #LOGGER.info('results_getconfig = {:s}'.format(str(results_getconfig)))
+
+    #resources_to_set = [
+    #    network_instance('test-svc', 'L3VRF'),
+    #
+    #    interface('ethernet-1/1', 0, '172.16.0.1', 24, True),
+    #    network_instance_interface('test-svc', 'ethernet-1/1', 0),
+    #
+    #    interface('ethernet-1/2', 0, '172.0.0.1', 24, True),
+    #    network_instance_interface('test-svc', 'ethernet-1/2', 0),
+    #
+    #    network_instance_static_route('test-svc', '172.0.0.0/24', '172.16.0.2'),
+    #    network_instance_static_route('test-svc', '172.2.0.0/24', '172.16.0.3'),
+    #]
+    #LOGGER.info('resources_to_set = {:s}'.format(str(resources_to_set)))
+    #results_setconfig = driver.SetConfig(resources_to_set)
+    #LOGGER.info('results_setconfig = {:s}'.format(str(results_setconfig)))
+
+    resources_to_delete = [
+        #network_instance_static_route('d35fc1d9', '172.0.0.0/24', '172.16.0.2'),
+        #network_instance_static_route('d35fc1d9', '172.2.0.0/24', '172.16.0.3'),
+    
+        #network_instance_interface('d35fc1d9', 'ethernet-1/1', 0),
+        #network_instance_interface('d35fc1d9', 'ethernet-1/2', 0),
+    
+        interface('ethernet-1/1', 0, '172.16.1.1', 24, True),
+        interface('ethernet-1/2', 0, '172.0.0.2', 24, True),
+    
+        network_instance('20f66fb5', 'L3VRF'),
+    ]
+    LOGGER.info('resources_to_delete = {:s}'.format(str(resources_to_delete)))
+    results_deleteconfig = driver.DeleteConfig(resources_to_delete)
+    LOGGER.info('results_deleteconfig = {:s}'.format(str(results_deleteconfig)))
+
+    time.sleep(1)
+
+    driver.Disconnect()
+    return 0
+
+if __name__ == '__main__':
+    sys.exit(main())
diff --git a/src/device/tests/test_netconf.py b/src/device/tests/test_netconf.py
new file mode 100644
index 0000000000000000000000000000000000000000..70551eed759867d26b46d399fba998a2d8007d21
--- /dev/null
+++ b/src/device/tests/test_netconf.py
@@ -0,0 +1,31 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from ncclient.manager import Manager, connect_ssh
+
+str_filter = '''<filter>
+    <components xmlns="http://openconfig.net/yang/platform">
+        <component/>
+    </components>
+</filter>'''
+
+_manager : Manager = connect_ssh(
+    host='10.5.32.3', port=830, username='admin', password='admin',
+    device_params={'name': 'huaweiyang'}, manager_params={'timeout': 120},
+    key_filename=None, hostkey_verify=False, allow_agent=False,
+    look_for_keys=False)
+c = _manager.get(filter=str_filter, with_defaults=None).data_xml
+with open('data.xml', 'w') as f:
+    f.write(c)
+_manager.close_session()
diff --git a/src/monitoring/service/EventTools.py b/src/monitoring/service/EventTools.py
index 0d351eee968684f18571f0da9f094a806f577efd..be3fe9b92c4867b66c562b8f0f0b35148e249cac 100644
--- a/src/monitoring/service/EventTools.py
+++ b/src/monitoring/service/EventTools.py
@@ -12,11 +12,11 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import grpc, json, logging, queue, threading
 from typing import Dict
-import grpc, logging, queue, threading
 from common.method_wrappers.ServiceExceptions import ServiceException
 from common.proto import monitoring_pb2
-from common.proto.context_pb2 import DeviceOperationalStatusEnum, Empty, EventTypeEnum
+from common.proto.context_pb2 import ConfigActionEnum, DeviceOperationalStatusEnum, Empty, EventTypeEnum
 from common.proto.kpi_sample_types_pb2 import KpiSampleType
 from context.client.ContextClient import ContextClient
 from monitoring.client.MonitoringClient import MonitoringClient
@@ -108,7 +108,21 @@ class EventsDeviceCollector:
                         # device is not ready for monitoring
                         continue
 
+                    enabled_endpoint_names = set()
+                    for config_rule in device.device_config.config_rules:
+                        if config_rule.action != ConfigActionEnum.CONFIGACTION_SET: continue
+                        if config_rule.WhichOneof('config_rule') != 'custom': continue
+                        str_resource_key = str(config_rule.custom.resource_key)
+                        if not str_resource_key.startswith('/interface['): continue
+                        json_resource_value = json.loads(config_rule.custom.resource_value)
+                        if 'name' not in json_resource_value: continue
+                        if 'enabled' not in json_resource_value: continue
+                        if not json_resource_value['enabled']: continue
+                        enabled_endpoint_names.add(json_resource_value['name'])
+
                     for endpoint in device.device_endpoints:
+                        if endpoint.name not in enabled_endpoint_names: continue
+
                         endpoint_uuid = endpoint.endpoint_id.endpoint_uuid.uuid
                         self._name_mapping.set_endpoint_name(endpoint_uuid, endpoint.name)
 
diff --git a/src/monitoring/service/ManagementDBTools.py b/src/monitoring/service/ManagementDBTools.py
index a2beddccd633a1961238a6affa4b8d069c26762e..6c0a69e0ec6ec22a9fff24a1de073f2df03e2115 100644
--- a/src/monitoring/service/ManagementDBTools.py
+++ b/src/monitoring/service/ManagementDBTools.py
@@ -261,10 +261,10 @@ class ManagementDB():
             else:
                 if data[0] == 1:
                     return True
-                elif data[0] == 0:
+                elif data[0] == 0 or data[0] is None:
                     return False
                 else:
-                    LOGGER.debug(f"KPI {kpi_id} is wrong")
+                    LOGGER.debug(f"KPI {kpi_id} is wrong: {str(data)}")
                     return None
         except sqlite3.Error as e:
             LOGGER.debug(f"KPI {kpi_id} cannot be checked from the ManagementDB: {e}")
diff --git a/src/monitoring/service/__main__.py b/src/monitoring/service/__main__.py
index d0a132c70bed2c56bc9159ec3ad284120c0eb623..d242f0fc0add7afebed32178bfafde23552a7649 100644
--- a/src/monitoring/service/__main__.py
+++ b/src/monitoring/service/__main__.py
@@ -49,7 +49,7 @@ def start_monitoring(name_mapping : NameMapping):
                 monitor_kpi_request = monitoring_pb2.MonitorKpiRequest()
                 monitor_kpi_request.kpi_id.CopyFrom(kpi_id)
                 monitor_kpi_request.monitoring_window_s = 86400
-                monitor_kpi_request.sampling_rate_s = 30
+                monitor_kpi_request.sampling_rate_s = 10
                 events_collector._monitoring_client.MonitorKpi(monitor_kpi_request)
         
         time.sleep(0.5) # let other tasks run; do not overload CPU
diff --git a/src/pathcomp/frontend/service/algorithms/tools/ComposeConfigRules.py b/src/pathcomp/frontend/service/algorithms/tools/ComposeConfigRules.py
index 91367e23f29a02aa3e9605fcd0d2864b9191d800..c6621773b8b45230358eebff2c60b95aacdda31f 100644
--- a/src/pathcomp/frontend/service/algorithms/tools/ComposeConfigRules.py
+++ b/src/pathcomp/frontend/service/algorithms/tools/ComposeConfigRules.py
@@ -19,7 +19,8 @@ from common.tools.object_factory.ConfigRule import json_config_rule_set
 
 SETTINGS_RULE_NAME = '/settings'
 
-DEV_EP_SETTINGS = re.compile(r'\/device\[([^\]]+)\]\/endpoint\[([^\]]+)\]\/settings')
+DEVICE_SETTINGS = re.compile(r'\/device\[([^\]]+)\]\/settings')
+ENDPOINT_SETTINGS = re.compile(r'\/device\[([^\]]+)\]\/endpoint\[([^\]]+)\]\/settings')
 
 L2NM_SETTINGS_FIELD_DEFAULTS = {
     'encapsulation_type': 'dot1q',
@@ -76,26 +77,37 @@ def compose_device_config_rules(
     device_name_mapping : Dict[str, str], endpoint_name_mapping : Dict[Tuple[str, str], str]
 ) -> None:
 
+    devices_traversed = set()
     endpoints_traversed = set()
     for path_hop in path_hops:
         device_uuid_or_name = path_hop['device']
+        devices_traversed.add(device_uuid_or_name)
         endpoints_traversed.add((device_uuid_or_name, path_hop['ingress_ep']))
         endpoints_traversed.add((device_uuid_or_name, path_hop['egress_ep']))
 
     for config_rule in config_rules:
         if config_rule.WhichOneof('config_rule') != 'custom': continue
-        match = DEV_EP_SETTINGS.match(config_rule.custom.resource_key)
-        if match is None: continue
 
-        device_uuid_or_name = match.group(1)
-        device_name_or_uuid = device_name_mapping[device_uuid_or_name]
-        device_keys = {device_uuid_or_name, device_name_or_uuid}
-
-        endpoint_uuid_or_name = match.group(2)
-        endpoint_name_or_uuid_1 = endpoint_name_mapping[(device_uuid_or_name, endpoint_uuid_or_name)]
-        endpoint_name_or_uuid_2 = endpoint_name_mapping[(device_name_or_uuid, endpoint_uuid_or_name)]
-        endpoint_keys = {endpoint_uuid_or_name, endpoint_name_or_uuid_1, endpoint_name_or_uuid_2}
-
-        device_endpoint_keys = set(itertools.product(device_keys, endpoint_keys))
-        if len(device_endpoint_keys.intersection(endpoints_traversed)) == 0: continue
-        subservice_config_rules.append(config_rule)
+        match = DEVICE_SETTINGS.match(config_rule.custom.resource_key)
+        if match is not None:
+            device_uuid_or_name = match.group(1)
+            device_name_or_uuid = device_name_mapping[device_uuid_or_name]
+            device_keys = {device_uuid_or_name, device_name_or_uuid}
+
+            if len(device_keys.intersection(devices_traversed)) == 0: continue
+            subservice_config_rules.append(config_rule)
+
+        match = ENDPOINT_SETTINGS.match(config_rule.custom.resource_key)
+        if match is not None:
+            device_uuid_or_name = match.group(1)
+            device_name_or_uuid = device_name_mapping[device_uuid_or_name]
+            device_keys = {device_uuid_or_name, device_name_or_uuid}
+
+            endpoint_uuid_or_name = match.group(2)
+            endpoint_name_or_uuid_1 = endpoint_name_mapping[(device_uuid_or_name, endpoint_uuid_or_name)]
+            endpoint_name_or_uuid_2 = endpoint_name_mapping[(device_name_or_uuid, endpoint_uuid_or_name)]
+            endpoint_keys = {endpoint_uuid_or_name, endpoint_name_or_uuid_1, endpoint_name_or_uuid_2}
+
+            device_endpoint_keys = set(itertools.product(device_keys, endpoint_keys))
+            if len(device_endpoint_keys.intersection(endpoints_traversed)) == 0: continue
+            subservice_config_rules.append(config_rule)
diff --git a/src/pathcomp/frontend/service/algorithms/tools/EroPathToHops.py b/src/pathcomp/frontend/service/algorithms/tools/EroPathToHops.py
index cf41f186f4f35c45cbb1ce836196272327f6048f..eb9e862753d3834f7dd7b7c6529d7d9ae5e38f1f 100644
--- a/src/pathcomp/frontend/service/algorithms/tools/EroPathToHops.py
+++ b/src/pathcomp/frontend/service/algorithms/tools/EroPathToHops.py
@@ -111,14 +111,18 @@ def eropath_to_hops(
 
             if num_ero_hops - 1 == len(path_hops): break
 
-            endpoint_key = (last_hop['device'], last_hop['egress_ep'])
-            link_tuple = endpoint_to_link_dict.get(endpoint_key)
+            endpoint_key = (last_hop['device'], last_hop['egress_ep'], 'src')
+            link_tuple = endpoint_to_link_dict[endpoint_key]
+            if link_tuple is None: raise Exception('Malformed path')
+
             ingress = next(iter([
                 ep_id for ep_id in link_tuple[0]['link_endpoint_ids']
                 if (ep_id['endpoint_id']['device_id'] == device_uuid) and\
                     (ep_id['endpoint_id']['endpoint_uuid'] != endpoint_uuid)
             ]), None)
-            if ingress['endpoint_id']['device_id'] != device_uuid: raise Exception('Malformed path')
+            if ingress['endpoint_id']['device_id'] != device_uuid:
+                raise Exception('Malformed path')
+
             ingress_ep = ingress['endpoint_id']['endpoint_uuid']
             ingress_ep = MAP_TAPI_UUIDS.get(ingress_ep, ingress_ep)
             path_hops.append({
diff --git a/src/service/service/service_handler_api/FilterFields.py b/src/service/service/service_handler_api/FilterFields.py
index 3ec71dc64536e28457c4f1adbf3679186285786d..f86412a8c736fda4d2fff2b485453cc3bf1ce0b1 100644
--- a/src/service/service/service_handler_api/FilterFields.py
+++ b/src/service/service/service_handler_api/FilterFields.py
@@ -35,6 +35,7 @@ DEVICE_DRIVER_VALUES = {
     DeviceDriverEnum.DEVICEDRIVER_ONF_TR_352,
     DeviceDriverEnum.DEVICEDRIVER_XR,
     DeviceDriverEnum.DEVICEDRIVER_IETF_L2VPN,
+    DeviceDriverEnum.DEVICEDRIVER_GNMI_OPENCONFIG,
 }
 
 # Map allowed filter fields to allowed values per Filter field. If no restriction (free text) None is specified
diff --git a/src/service/service/service_handler_api/SettingsHandler.py b/src/service/service/service_handler_api/SettingsHandler.py
index 2527347768fd70dfe68d92cf99329a67b617f6ce..b8daec7d7b5fc857aef9aa5e46bb04d33a02b47c 100644
--- a/src/service/service/service_handler_api/SettingsHandler.py
+++ b/src/service/service/service_handler_api/SettingsHandler.py
@@ -57,6 +57,16 @@ class SettingsHandler:
     def get(self, key_or_path : Union[str, List[str]], default : Optional[Any] = None) -> Optional[TreeNode]:
         return get_subnode(self.__resolver, self.__config, key_or_path, default=default)
 
+    def get_device_settings(self, device : Device) -> Optional[TreeNode]:
+        device_keys = device.device_id.device_uuid.uuid, device.name
+
+        for device_key in device_keys:
+            endpoint_settings_uri = '/device[{:s}]/settings'.format(device_key)
+            endpoint_settings = self.get(endpoint_settings_uri)
+            if endpoint_settings is not None: return endpoint_settings
+
+        return None
+
     def get_endpoint_settings(self, device : Device, endpoint : EndPoint) -> Optional[TreeNode]:
         device_keys   = device.device_id.device_uuid.uuid,       device.name
         endpoint_keys = endpoint.endpoint_id.endpoint_uuid.uuid, endpoint.name
diff --git a/src/service/service/service_handler_api/Tools.py b/src/service/service/service_handler_api/Tools.py
index 787b0f499a2d4b3ad76bfe4b7d41f072bbe6c50c..b06d128d94328b0d664e8928e1701ee327aa6d03 100644
--- a/src/service/service/service_handler_api/Tools.py
+++ b/src/service/service/service_handler_api/Tools.py
@@ -11,6 +11,7 @@
 # WITHOUT 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, re
 from typing import Any, List, Optional, Tuple, Union
 from common.method_wrappers.ServiceExceptions import NotFoundException
diff --git a/src/service/service/service_handlers/__init__.py b/src/service/service/service_handlers/__init__.py
index 257bc138fe932e7e5abee00981848248039d0b3f..7ea0d4f627b5d6010f6e40135f6005471efe8d71 100644
--- a/src/service/service/service_handlers/__init__.py
+++ b/src/service/service/service_handlers/__init__.py
@@ -19,6 +19,7 @@ from .l2nm_ietfl2vpn.L2NM_IETFL2VPN_ServiceHandler import L2NM_IETFL2VPN_Service
 from .l2nm_openconfig.L2NMOpenConfigServiceHandler import L2NMOpenConfigServiceHandler
 from .l3nm_emulated.L3NMEmulatedServiceHandler import L3NMEmulatedServiceHandler
 from .l3nm_openconfig.L3NMOpenConfigServiceHandler import L3NMOpenConfigServiceHandler
+from .l3nm_gnmi_openconfig.L3NMGnmiOpenConfigServiceHandler import L3NMGnmiOpenConfigServiceHandler
 from .microwave.MicrowaveServiceHandler import MicrowaveServiceHandler
 from .p4.p4_service_handler import P4ServiceHandler
 from .tapi_tapi.TapiServiceHandler import TapiServiceHandler
@@ -49,6 +50,12 @@ SERVICE_HANDLERS = [
             FilterFieldEnum.DEVICE_DRIVER : DeviceDriverEnum.DEVICEDRIVER_OPENCONFIG,
         }
     ]),
+    (L3NMGnmiOpenConfigServiceHandler, [
+        {
+            FilterFieldEnum.SERVICE_TYPE  : ServiceTypeEnum.SERVICETYPE_L3NM,
+            FilterFieldEnum.DEVICE_DRIVER : DeviceDriverEnum.DEVICEDRIVER_GNMI_OPENCONFIG,
+        }
+    ]),
     (TapiServiceHandler, [
         {
             FilterFieldEnum.SERVICE_TYPE  : ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE,
diff --git a/src/service/service/service_handlers/l2nm_emulated/ConfigRules.py b/src/service/service/service_handlers/l2nm_emulated/ConfigRules.py
index e68a62030fba242d40e6b1c9bf0c2c65e66639f2..ab5807a9fa5b2f9d0031851ba2cd8069dece44f0 100644
--- a/src/service/service/service_handlers/l2nm_emulated/ConfigRules.py
+++ b/src/service/service/service_handlers/l2nm_emulated/ConfigRules.py
@@ -21,12 +21,6 @@ def setup_config_rules(
     service_settings : TreeNode, endpoint_settings : TreeNode, endpoint_acls : List [Tuple]
 ) -> List[Dict]:
 
-    if service_settings  is None: return []
-    if endpoint_settings is None: return []
-
-    json_settings          : Dict = service_settings.value
-    json_endpoint_settings : Dict = endpoint_settings.value
-
     #mtu                 = json_settings.get('mtu',                 1450 )    # 1512
     #address_families    = json_settings.get('address_families',    []   )    # ['IPV4']
     #bgp_as              = json_settings.get('bgp_as',              0    )    # 65000
@@ -86,9 +80,6 @@ def teardown_config_rules(
     service_settings : TreeNode, endpoint_settings : TreeNode
 ) -> List[Dict]:
 
-    if service_settings  is None: return []
-    if endpoint_settings is None: return []
-
     #json_settings          : Dict = service_settings.value
     json_endpoint_settings : Dict = endpoint_settings.value
 
diff --git a/src/service/service/service_handlers/l3nm_gnmi_openconfig/ConfigRuleComposer.py b/src/service/service/service_handlers/l3nm_gnmi_openconfig/ConfigRuleComposer.py
new file mode 100644
index 0000000000000000000000000000000000000000..7f3593df39f88e2e3ee806dcc33f67f9e85a6b69
--- /dev/null
+++ b/src/service/service/service_handlers/l3nm_gnmi_openconfig/ConfigRuleComposer.py
@@ -0,0 +1,119 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from typing import Dict, List, Optional, Tuple
+from common.proto.context_pb2 import Device, EndPoint
+from common.tools.object_factory.ConfigRule import json_config_rule_delete, json_config_rule_set
+
+from service.service.service_handler_api.AnyTreeTools import TreeNode
+
+def _interface(if_name, sif_index, ipv4_address, ipv4_prefix, enabled) -> Tuple[str, Dict]:
+    str_path = '/interface[{:s}]'.format(if_name)
+    str_data = {'name': if_name, 'enabled': enabled, 'sub_if_index': sif_index,
+                'sub_if_enabled': enabled, 'sub_if_ipv4_enabled': enabled,
+                'sub_if_ipv4_address': ipv4_address, 'sub_if_ipv4_prefix': ipv4_prefix}
+    return str_path, str_data
+
+def _network_instance(ni_name, ni_type) -> Tuple[str, Dict]:
+    str_path = '/network_instance[{:s}]'.format(ni_name)
+    str_data = {'name': ni_name, 'type': ni_type}
+    return str_path, str_data
+
+def _network_instance_static_route(ni_name, prefix, next_hop, next_hop_index=0) -> Tuple[str, Dict]:
+    str_path = '/network_instance[{:s}]/static_route[{:s}]'.format(ni_name, prefix)
+    str_data = {'name': ni_name, 'prefix': prefix, 'next_hop': next_hop, 'next_hop_index': next_hop_index}
+    return str_path, str_data
+
+def _network_instance_interface(ni_name, if_name, sif_index) -> Tuple[str, Dict]:
+    str_path = '/network_instance[{:s}]/interface[{:s}.{:d}]'.format(ni_name, if_name, sif_index)
+    str_data = {'name': ni_name, 'if_name': if_name, 'sif_index': sif_index}
+    return str_path, str_data
+
+class EndpointComposer:
+    def __init__(self, endpoint_uuid : str) -> None:
+        self.uuid = endpoint_uuid
+        self.objekt : Optional[EndPoint] = None
+        self.sub_interface_index = 0
+        self.ipv4_address = None
+        self.ipv4_prefix = None
+
+    def configure(self, endpoint_obj : EndPoint, settings : Optional[TreeNode]) -> None:
+        self.objekt = endpoint_obj
+        if settings is None: return
+        json_settings : Dict = settings.value
+        self.ipv4_address = json_settings['ipv4_address']
+        self.ipv4_prefix = json_settings['ipv4_prefix']
+        self.sub_interface_index = json_settings['sub_interface_index']
+
+    def get_config_rules(self, network_instance_name : str, delete : bool = False) -> List[Dict]:
+        json_config_rule = json_config_rule_delete if delete else json_config_rule_set
+        return [
+            json_config_rule(*_interface(
+                self.objekt.name, self.sub_interface_index, self.ipv4_address, self.ipv4_prefix, True
+            )),
+            json_config_rule(*_network_instance_interface(
+                network_instance_name, self.objekt.name, self.sub_interface_index
+            )),
+        ]
+
+class DeviceComposer:
+    def __init__(self, device_uuid : str) -> None:
+        self.uuid = device_uuid
+        self.objekt : Optional[Device] = None
+        self.endpoints : Dict[str, EndpointComposer] = dict()
+        self.static_routes : Dict[str, str] = dict()
+    
+    def get_endpoint(self, endpoint_uuid : str) -> EndpointComposer:
+        if endpoint_uuid not in self.endpoints:
+            self.endpoints[endpoint_uuid] = EndpointComposer(endpoint_uuid)
+        return self.endpoints[endpoint_uuid]
+
+    def configure(self, device_obj : Device, settings : Optional[TreeNode]) -> None:
+        self.objekt = device_obj
+        if settings is None: return
+        json_settings : Dict = settings.value
+        static_routes = json_settings.get('static_routes', [])
+        for static_route in static_routes:
+            prefix   = static_route['prefix']
+            next_hop = static_route['next_hop']
+            self.static_routes[prefix] = next_hop
+
+    def get_config_rules(self, network_instance_name : str, delete : bool = False) -> List[Dict]:
+        json_config_rule = json_config_rule_delete if delete else json_config_rule_set
+        config_rules = [
+            json_config_rule(*_network_instance(network_instance_name, 'L3VRF'))
+        ]
+        for endpoint in self.endpoints.values():
+            config_rules.extend(endpoint.get_config_rules(network_instance_name, delete=delete))
+        for prefix, next_hop in self.static_routes.items():
+            config_rules.append(
+                json_config_rule(*_network_instance_static_route(network_instance_name, prefix, next_hop))
+            )
+        if delete: config_rules = list(reversed(config_rules))
+        return config_rules
+
+class ConfigRuleComposer:
+    def __init__(self) -> None:
+        self.devices : Dict[str, DeviceComposer] = dict()
+
+    def get_device(self, device_uuid : str) -> DeviceComposer:
+        if device_uuid not in self.devices:
+            self.devices[device_uuid] = DeviceComposer(device_uuid)
+        return self.devices[device_uuid]
+
+    def get_config_rules(self, network_instance_name : str, delete : bool = False) -> Dict[str, List[Dict]]:
+        return {
+            device_uuid : device.get_config_rules(network_instance_name, delete=delete)
+            for device_uuid, device in self.devices.items()
+        }
diff --git a/src/service/service/service_handlers/l3nm_gnmi_openconfig/L3NMGnmiOpenConfigServiceHandler.py b/src/service/service/service_handlers/l3nm_gnmi_openconfig/L3NMGnmiOpenConfigServiceHandler.py
new file mode 100644
index 0000000000000000000000000000000000000000..a9cec3f42cf75a27451e953f85342e299b642685
--- /dev/null
+++ b/src/service/service/service_handlers/l3nm_gnmi_openconfig/L3NMGnmiOpenConfigServiceHandler.py
@@ -0,0 +1,161 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import json, logging
+from typing import Any, Dict, List, Optional, Tuple, Union
+from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method
+from common.proto.context_pb2 import ConfigRule, DeviceId, Service
+from common.tools.object_factory.Device import json_device_id
+from common.type_checkers.Checkers import chk_type
+from service.service.service_handler_api.Tools import get_device_endpoint_uuids, get_endpoint_matching
+from service.service.service_handler_api._ServiceHandler import _ServiceHandler
+from service.service.service_handler_api.SettingsHandler import SettingsHandler
+from service.service.task_scheduler.TaskExecutor import TaskExecutor
+from .ConfigRuleComposer import ConfigRuleComposer
+
+LOGGER = logging.getLogger(__name__)
+
+METRICS_POOL = MetricsPool('Service', 'Handler', labels={'handler': 'l3nm_gnmi_openconfig'})
+
+class L3NMGnmiOpenConfigServiceHandler(_ServiceHandler):
+    def __init__(   # pylint: disable=super-init-not-called
+        self, service : Service, task_executor : TaskExecutor, **settings
+    ) -> None:
+        self.__service = service
+        self.__task_executor = task_executor
+        self.__settings_handler = SettingsHandler(service.service_config, **settings)
+        self.__composer = ConfigRuleComposer()
+        self.__endpoint_map : Dict[Tuple[str, str], str] = dict()
+
+    def _compose_config_rules(self, endpoints : List[Tuple[str, str, Optional[str]]]) -> None:
+        for endpoint in endpoints:
+            device_uuid, endpoint_uuid = get_device_endpoint_uuids(endpoint)
+
+            device_obj = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid)))
+            device_settings = self.__settings_handler.get_device_settings(device_obj)
+            _device = self.__composer.get_device(device_obj.name)
+            _device.configure(device_obj, device_settings)
+
+            endpoint_obj = get_endpoint_matching(device_obj, endpoint_uuid)
+            endpoint_settings = self.__settings_handler.get_endpoint_settings(device_obj, endpoint_obj)
+            _endpoint = _device.get_endpoint(endpoint_obj.name)
+            _endpoint.configure(endpoint_obj, endpoint_settings)
+
+            self.__endpoint_map[(device_uuid, endpoint_uuid)] = device_obj.name
+
+    def _do_configurations(
+        self, config_rules_per_device : Dict[str, List[Dict]], endpoints : List[Tuple[str, str, Optional[str]]],
+        delete : bool = False
+    ) -> List[Union[bool, Exception]]:
+        # Configuration is done atomically on each device, all OK / all KO per device
+        results_per_device = dict()
+        for device_name,json_config_rules in config_rules_per_device.items():
+            try:
+                device_obj = self.__composer.get_device(device_name).objekt
+                if len(json_config_rules) == 0: continue
+                del device_obj.device_config.config_rules[:]
+                for json_config_rule in json_config_rules:
+                    device_obj.device_config.config_rules.append(ConfigRule(**json_config_rule))
+                self.__task_executor.configure_device(device_obj)
+                results_per_device[device_name] = True
+            except Exception as e: # pylint: disable=broad-exception-caught
+                verb = 'deconfigure' if delete else 'configure'
+                MSG = 'Unable to {:s} Device({:s}) : ConfigRules({:s})'
+                LOGGER.exception(MSG.format(verb, str(device_name), str(json_config_rules)))
+                results_per_device[device_name] = e
+
+        results = []
+        for endpoint in endpoints:
+            device_uuid, endpoint_uuid = get_device_endpoint_uuids(endpoint)
+            device_name = self.__endpoint_map[(device_uuid, endpoint_uuid)]
+            results.append(results_per_device[device_name])
+        return results
+
+    @metered_subclass_method(METRICS_POOL)
+    def SetEndpoint(
+        self, endpoints : List[Tuple[str, str, Optional[str]]], connection_uuid : Optional[str] = None
+    ) -> List[Union[bool, Exception]]:
+        chk_type('endpoints', endpoints, list)
+        if len(endpoints) == 0: return []
+        service_uuid = self.__service.service_id.service_uuid.uuid
+        #settings = self.__settings_handler.get('/settings')
+        self._compose_config_rules(endpoints)
+        network_instance_name = service_uuid.split('-')[0]
+        config_rules_per_device = self.__composer.get_config_rules(network_instance_name, delete=False)
+        results = self._do_configurations(config_rules_per_device, endpoints)
+        return results
+
+    @metered_subclass_method(METRICS_POOL)
+    def DeleteEndpoint(
+        self, endpoints : List[Tuple[str, str, Optional[str]]], connection_uuid : Optional[str] = None
+    ) -> List[Union[bool, Exception]]:
+        chk_type('endpoints', endpoints, list)
+        if len(endpoints) == 0: return []
+        service_uuid = self.__service.service_id.service_uuid.uuid
+        #settings = self.__settings_handler.get('/settings')
+        self._compose_config_rules(endpoints)
+        network_instance_name = service_uuid.split('-')[0]
+        config_rules_per_device = self.__composer.get_config_rules(network_instance_name, delete=True)
+        results = self._do_configurations(config_rules_per_device, endpoints, delete=True)
+        return results
+
+    @metered_subclass_method(METRICS_POOL)
+    def SetConstraint(self, constraints : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]:
+        chk_type('constraints', constraints, list)
+        if len(constraints) == 0: return []
+
+        msg = '[SetConstraint] Method not implemented. Constraints({:s}) are being ignored.'
+        LOGGER.warning(msg.format(str(constraints)))
+        return [True for _ in range(len(constraints))]
+
+    @metered_subclass_method(METRICS_POOL)
+    def DeleteConstraint(self, constraints : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]:
+        chk_type('constraints', constraints, list)
+        if len(constraints) == 0: return []
+
+        msg = '[DeleteConstraint] Method not implemented. Constraints({:s}) are being ignored.'
+        LOGGER.warning(msg.format(str(constraints)))
+        return [True for _ in range(len(constraints))]
+
+    @metered_subclass_method(METRICS_POOL)
+    def SetConfig(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]:
+        chk_type('resources', resources, list)
+        if len(resources) == 0: return []
+
+        results = []
+        for resource in resources:
+            try:
+                resource_value = json.loads(resource[1])
+                self.__settings_handler.set(resource[0], resource_value)
+                results.append(True)
+            except Exception as e: # pylint: disable=broad-except
+                LOGGER.exception('Unable to SetConfig({:s})'.format(str(resource)))
+                results.append(e)
+
+        return results
+
+    @metered_subclass_method(METRICS_POOL)
+    def DeleteConfig(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]:
+        chk_type('resources', resources, list)
+        if len(resources) == 0: return []
+
+        results = []
+        for resource in resources:
+            try:
+                self.__settings_handler.delete(resource[0])
+            except Exception as e: # pylint: disable=broad-except
+                LOGGER.exception('Unable to DeleteConfig({:s})'.format(str(resource)))
+                results.append(e)
+
+        return results
diff --git a/src/service/service/service_handlers/l3nm_gnmi_openconfig/__init__.py b/src/service/service/service_handlers/l3nm_gnmi_openconfig/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..1549d9811aa5d1c193a44ad45d0d7773236c0612
--- /dev/null
+++ b/src/service/service/service_handlers/l3nm_gnmi_openconfig/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/webui/grafana_db_mon_kpis_psql.json b/src/webui/grafana_db_mon_kpis_psql.json
index 845ed4296605b6a0d15f38c9a20576a93195543e..df72f9ab052c02d5a4af7e7923a96fe968116103 100644
--- a/src/webui/grafana_db_mon_kpis_psql.json
+++ b/src/webui/grafana_db_mon_kpis_psql.json
@@ -144,7 +144,6 @@
         "options": {
           "legend": {
             "calcs": [
-              "first",
               "min",
               "mean",
               "max",
diff --git a/src/webui/service/device/forms.py b/src/webui/service/device/forms.py
index 24bc92b3a5a4aec4321c07b17830f6111be7176d..a6e07fe3cee6889163d5f22670326ef62320bd9d 100644
--- a/src/webui/service/device/forms.py
+++ b/src/webui/service/device/forms.py
@@ -30,6 +30,7 @@ class AddDeviceForm(FlaskForm):
     device_drivers_onf_tr_352 = BooleanField('ONF_TR_352')
     device_drivers_xr = BooleanField('XR')
     device_drivers_ietf_l2vpn = BooleanField('IETF L2VPN')
+    device_drivers_gnmi_openconfig = BooleanField('GNMI OPENCONFIG')
     device_config_address = StringField('connect/address',default='127.0.0.1',validators=[DataRequired(), Length(min=5)])
     device_config_port = StringField('connect/port',default='0',validators=[DataRequired(), Length(min=1)])
     device_config_settings = TextAreaField('connect/settings',default='{}',validators=[DataRequired(), Length(min=2)])
diff --git a/src/webui/service/device/routes.py b/src/webui/service/device/routes.py
index bc46847704b28fb6ef44de0aae030ccb67935928..4590c7f01a24e801ecc775ad0b22cf0dcdea3452 100644
--- a/src/webui/service/device/routes.py
+++ b/src/webui/service/device/routes.py
@@ -122,6 +122,8 @@ def add():
             device_drivers.append(DeviceDriverEnum.DEVICEDRIVER_XR)
         if form.device_drivers_ietf_l2vpn.data:
             device_drivers.append(DeviceDriverEnum.DEVICEDRIVER_IETF_L2VPN)
+        if form.device_drivers_gnmi_openconfig.data:
+            device_drivers.append(DeviceDriverEnum.DEVICEDRIVER_GNMI_OPENCONFIG)
         device_obj.device_drivers.extend(device_drivers) # pylint: disable=no-member
 
         try:
diff --git a/src/webui/service/service/routes.py b/src/webui/service/service/routes.py
index 08312e5257d13c4b55b83733ded689c7565c4790..3d3d47ab59500a8cf3d330407d9311ad17ece94b 100644
--- a/src/webui/service/service/routes.py
+++ b/src/webui/service/service/routes.py
@@ -92,13 +92,6 @@ def home():
         ste=ServiceTypeEnum, sse=ServiceStatusEnum, active_drivers=active_drivers)
 
 
-@service.route('add', methods=['GET', 'POST'])
-def add():
-    flash('Add service route called', 'danger')
-    raise NotImplementedError()
-    #return render_template('service/home.html')
-
-
 def get_hub_module_name(dev: Device) -> Optional[str]:
     for cr in dev.device_config.config_rules:
         if cr.action == ConfigActionEnum.CONFIGACTION_SET and cr.custom and cr.custom.resource_key == "_connect/settings":
@@ -110,139 +103,139 @@ def get_hub_module_name(dev: Device) -> Optional[str]:
                 pass
     return None
 
-@service.route('add-xr', methods=['GET', 'POST'])
-def add_xr():
-    ### FIXME: copypaste
-    if 'context_uuid' not in session or 'topology_uuid' not in session:
-        flash("Please select a context!", "warning")
-        return redirect(url_for("main.home"))
-
-    context_uuid = session['context_uuid']
-    topology_uuid = session['topology_uuid']
-
-    context_client.connect()
-    grpc_topology = get_topology(context_client, topology_uuid, context_uuid=context_uuid, rw_copy=False)
-    if grpc_topology is None:
-        flash('Context({:s})/Topology({:s}) not found'.format(str(context_uuid), str(topology_uuid)), 'danger')
-        return redirect(url_for("main.home"))
-    else:
-        topo_device_uuids = {device_id.device_uuid.uuid for device_id in grpc_topology.device_ids}
-        grpc_devices= context_client.ListDevices(Empty())
-        devices = [
-            device for device in grpc_devices.devices
-            if device.device_id.device_uuid.uuid in topo_device_uuids and DeviceDriverEnum.DEVICEDRIVER_XR in device.device_drivers
-        ]
-        devices.sort(key=lambda dev: dev.name)
-
-        hub_interfaces_by_device = defaultdict(list)
-        leaf_interfaces_by_device = defaultdict(list)
-        constellation_name_to_uuid = {}
-        dev_ep_to_uuid = {}
-        ep_uuid_to_name = {}
-        for d in devices:
-            constellation_name_to_uuid[d.name] = d.device_id.device_uuid.uuid
-            hm_name = get_hub_module_name(d)
-            if hm_name is not None:
-                hm_if_prefix= hm_name + "|"
-                for ep in d.device_endpoints:
-                    dev_ep_to_uuid[(d.name, ep.name)] = ep.endpoint_id.endpoint_uuid.uuid
-                    if ep.name.startswith(hm_if_prefix):
-                        hub_interfaces_by_device[d.name].append(ep.name)
-                    else:
-                        leaf_interfaces_by_device[d.name].append(ep.name)
-                    ep_uuid_to_name[ep.endpoint_id.endpoint_uuid.uuid] = (d.name, ep.name)
-                hub_interfaces_by_device[d.name].sort()
-                leaf_interfaces_by_device[d.name].sort()
-
-        # Find out what endpoints are already used so that they can be disabled
-        # in the create screen
-        context_obj = get_context(context_client, context_uuid, rw_copy=False)
-        if context_obj is None:
-            flash('Context({:s}) not found'.format(str(context_uuid)), 'danger')
-            return redirect(request.url)
-        
-        services = context_client.ListServices(context_obj.context_id)
-        ep_used_by={}
-        for service in services.services:
-            if  service.service_type == ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE:
-                for ep in service.service_endpoint_ids:
-                    ep_uuid = ep.endpoint_uuid.uuid
-                    if ep_uuid in ep_uuid_to_name:
-                        dev_name, ep_name = ep_uuid_to_name[ep_uuid]
-                        ep_used_by[f"{ep_name}@{dev_name}"] = service.name
-
-    context_client.close()
-
-    if request.method != 'POST':
-        return render_template('service/add-xr.html', devices=devices, hub_if=hub_interfaces_by_device, leaf_if=leaf_interfaces_by_device, ep_used_by=ep_used_by)
-    else:
-        service_name = request.form["service_name"]
-        if service_name == "":
-            flash(f"Service name must be specified", 'danger')
-
-        constellation = request.form["constellation"]
-        constellation_uuid = constellation_name_to_uuid.get(constellation, None)
-        if constellation_uuid is None:
-            flash(f"Invalid constellation \"{constellation}\"", 'danger')
-
-        hub_if = request.form["hubif"]
-        hub_if_uuid = dev_ep_to_uuid.get((constellation, hub_if), None)
-        if hub_if_uuid is None:
-            flash(f"Invalid hub interface \"{hub_if}\"", 'danger')
-
-        leaf_if = request.form["leafif"]
-        leaf_if_uuid = dev_ep_to_uuid.get((constellation, leaf_if), None)
-        if leaf_if_uuid is None:
-            flash(f"Invalid leaf interface \"{leaf_if}\"", 'danger')
-        
-        if service_name == "" or constellation_uuid is None or hub_if_uuid is None or leaf_if_uuid is None:
-            return redirect(request.url)
-        
-        
-        json_context_uuid=json_context_id(context_uuid)
-        sr = {
-            "name": service_name,
-            "service_id": {
-                 "context_id": {"context_uuid": {"uuid": context_uuid}},
-                 "service_uuid": {"uuid": service_name}
-            },
-            'service_type'        : ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE,
-            "service_endpoint_ids": [
-                {'device_id': {'device_uuid': {'uuid': constellation_uuid}}, 'endpoint_uuid': {'uuid': hub_if_uuid}, 'topology_id': json_topology_id("admin", context_id=json_context_uuid)},
-                {'device_id': {'device_uuid': {'uuid': constellation_uuid}}, 'endpoint_uuid': {'uuid': leaf_if_uuid}, 'topology_id': json_topology_id("admin", context_id=json_context_uuid)}
-            ],
-            'service_status'      : {'service_status': ServiceStatusEnum.SERVICESTATUS_PLANNED},
-            'service_constraints' : [],
-        }
-
-        json_tapi_settings = {
-            'capacity_value'  : 50.0,
-            'capacity_unit'   : 'GHz',
-            'layer_proto_name': 'PHOTONIC_MEDIA',
-            'layer_proto_qual': 'tapi-photonic-media:PHOTONIC_LAYER_QUALIFIER_NMC',
-            'direction'       : 'UNIDIRECTIONAL',
-        }
-        config_rule = json_config_rule_set('/settings', json_tapi_settings)
-
-        with connected_client(service_client) as sc:
-            endpoints, sr['service_endpoint_ids'] = sr['service_endpoint_ids'], []
-            try:
-                create_response = sc.CreateService(Service(**sr))
-            except Exception as e:
-                flash(f'Failure to update service name {service_name} with endpoints and configuration, exception {str(e)}', 'danger')
-                return redirect(request.url)
-            
-            sr['service_endpoint_ids'] = endpoints
-            sr['service_config'] = {'config_rules': [config_rule]}
-
-            try:
-                update_response = sc.UpdateService(Service(**sr))
-                flash(f'Created service {update_response.service_uuid.uuid}', 'success')
-            except Exception as e: 
-                flash(f'Failure to update service {create_response.service_uuid.uuid} with endpoints and configuration, exception {str(e)}', 'danger')
-                return redirect(request.url)
-
-            return redirect(url_for('service.home'))
+#@service.route('add-xr', methods=['GET', 'POST'])
+#def add_xr():
+#    ### FIXME: copypaste
+#    if 'context_uuid' not in session or 'topology_uuid' not in session:
+#        flash("Please select a context!", "warning")
+#        return redirect(url_for("main.home"))
+#
+#    context_uuid = session['context_uuid']
+#    topology_uuid = session['topology_uuid']
+#
+#    context_client.connect()
+#    grpc_topology = get_topology(context_client, topology_uuid, context_uuid=context_uuid, rw_copy=False)
+#    if grpc_topology is None:
+#        flash('Context({:s})/Topology({:s}) not found'.format(str(context_uuid), str(topology_uuid)), 'danger')
+#        return redirect(url_for("main.home"))
+#    else:
+#        topo_device_uuids = {device_id.device_uuid.uuid for device_id in grpc_topology.device_ids}
+#        grpc_devices= context_client.ListDevices(Empty())
+#        devices = [
+#            device for device in grpc_devices.devices
+#            if device.device_id.device_uuid.uuid in topo_device_uuids and DeviceDriverEnum.DEVICEDRIVER_XR in device.device_drivers
+#        ]
+#        devices.sort(key=lambda dev: dev.name)
+#
+#        hub_interfaces_by_device = defaultdict(list)
+#        leaf_interfaces_by_device = defaultdict(list)
+#        constellation_name_to_uuid = {}
+#        dev_ep_to_uuid = {}
+#        ep_uuid_to_name = {}
+#        for d in devices:
+#            constellation_name_to_uuid[d.name] = d.device_id.device_uuid.uuid
+#            hm_name = get_hub_module_name(d)
+#            if hm_name is not None:
+#                hm_if_prefix= hm_name + "|"
+#                for ep in d.device_endpoints:
+#                    dev_ep_to_uuid[(d.name, ep.name)] = ep.endpoint_id.endpoint_uuid.uuid
+#                    if ep.name.startswith(hm_if_prefix):
+#                        hub_interfaces_by_device[d.name].append(ep.name)
+#                    else:
+#                        leaf_interfaces_by_device[d.name].append(ep.name)
+#                    ep_uuid_to_name[ep.endpoint_id.endpoint_uuid.uuid] = (d.name, ep.name)
+#                hub_interfaces_by_device[d.name].sort()
+#                leaf_interfaces_by_device[d.name].sort()
+#
+#        # Find out what endpoints are already used so that they can be disabled
+#        # in the create screen
+#        context_obj = get_context(context_client, context_uuid, rw_copy=False)
+#        if context_obj is None:
+#            flash('Context({:s}) not found'.format(str(context_uuid)), 'danger')
+#            return redirect(request.url)
+#        
+#        services = context_client.ListServices(context_obj.context_id)
+#        ep_used_by={}
+#        for service in services.services:
+#            if  service.service_type == ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE:
+#                for ep in service.service_endpoint_ids:
+#                    ep_uuid = ep.endpoint_uuid.uuid
+#                    if ep_uuid in ep_uuid_to_name:
+#                        dev_name, ep_name = ep_uuid_to_name[ep_uuid]
+#                        ep_used_by[f"{ep_name}@{dev_name}"] = service.name
+#
+#    context_client.close()
+#
+#    if request.method != 'POST':
+#        return render_template('service/add-xr.html', devices=devices, hub_if=hub_interfaces_by_device, leaf_if=leaf_interfaces_by_device, ep_used_by=ep_used_by)
+#    else:
+#        service_name = request.form["service_name"]
+#        if service_name == "":
+#            flash(f"Service name must be specified", 'danger')
+#
+#        constellation = request.form["constellation"]
+#        constellation_uuid = constellation_name_to_uuid.get(constellation, None)
+#        if constellation_uuid is None:
+#            flash(f"Invalid constellation \"{constellation}\"", 'danger')
+#
+#        hub_if = request.form["hubif"]
+#        hub_if_uuid = dev_ep_to_uuid.get((constellation, hub_if), None)
+#        if hub_if_uuid is None:
+#            flash(f"Invalid hub interface \"{hub_if}\"", 'danger')
+#
+#        leaf_if = request.form["leafif"]
+#        leaf_if_uuid = dev_ep_to_uuid.get((constellation, leaf_if), None)
+#        if leaf_if_uuid is None:
+#            flash(f"Invalid leaf interface \"{leaf_if}\"", 'danger')
+#        
+#        if service_name == "" or constellation_uuid is None or hub_if_uuid is None or leaf_if_uuid is None:
+#            return redirect(request.url)
+#        
+#        
+#        json_context_uuid=json_context_id(context_uuid)
+#        sr = {
+#            "name": service_name,
+#            "service_id": {
+#                 "context_id": {"context_uuid": {"uuid": context_uuid}},
+#                 "service_uuid": {"uuid": service_name}
+#            },
+#            'service_type'        : ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE,
+#            "service_endpoint_ids": [
+#                {'device_id': {'device_uuid': {'uuid': constellation_uuid}}, 'endpoint_uuid': {'uuid': hub_if_uuid}, 'topology_id': json_topology_id("admin", context_id=json_context_uuid)},
+#                {'device_id': {'device_uuid': {'uuid': constellation_uuid}}, 'endpoint_uuid': {'uuid': leaf_if_uuid}, 'topology_id': json_topology_id("admin", context_id=json_context_uuid)}
+#            ],
+#            'service_status'      : {'service_status': ServiceStatusEnum.SERVICESTATUS_PLANNED},
+#            'service_constraints' : [],
+#        }
+#
+#        json_tapi_settings = {
+#            'capacity_value'  : 50.0,
+#            'capacity_unit'   : 'GHz',
+#            'layer_proto_name': 'PHOTONIC_MEDIA',
+#            'layer_proto_qual': 'tapi-photonic-media:PHOTONIC_LAYER_QUALIFIER_NMC',
+#            'direction'       : 'UNIDIRECTIONAL',
+#        }
+#        config_rule = json_config_rule_set('/settings', json_tapi_settings)
+#
+#        with connected_client(service_client) as sc:
+#            endpoints, sr['service_endpoint_ids'] = sr['service_endpoint_ids'], []
+#            try:
+#                create_response = sc.CreateService(Service(**sr))
+#            except Exception as e:
+#                flash(f'Failure to update service name {service_name} with endpoints and configuration, exception {str(e)}', 'danger')
+#                return redirect(request.url)
+#            
+#            sr['service_endpoint_ids'] = endpoints
+#            sr['service_config'] = {'config_rules': [config_rule]}
+#
+#            try:
+#                update_response = sc.UpdateService(Service(**sr))
+#                flash(f'Created service {update_response.service_uuid.uuid}', 'success')
+#            except Exception as e: 
+#                flash(f'Failure to update service {create_response.service_uuid.uuid} with endpoints and configuration, exception {str(e)}', 'danger')
+#                return redirect(request.url)
+#
+#            return redirect(url_for('service.home'))
 
 @service.get('<path:service_uuid>/detail')
 def detail(service_uuid: str):
diff --git a/src/webui/service/templates/device/add.html b/src/webui/service/templates/device/add.html
index 6b11a19208cc9c10daa4b7615ff46ac627162573..c9165667dd8c089e91400f652177b4ca4ec98010 100644
--- a/src/webui/service/templates/device/add.html
+++ b/src/webui/service/templates/device/add.html
@@ -81,20 +81,17 @@
                     {% endfor %}
                 </div>
                 {% else %}
-                {{ form.device_drivers_undefined }} {{ form.device_drivers_undefined.label(class="col-sm-3
-                col-form-label") }}
-                {{ form.device_drivers_openconfig }} {{ form.device_drivers_openconfig.label(class="col-sm-3
-                col-form-label") }}
-                {{ form.device_drivers_transport_api }} {{ form.device_drivers_transport_api.label(class="col-sm-3
-                col-form-label") }}
-                <br />{{ form.device_drivers_p4 }} {{ form.device_drivers_p4.label(class="col-sm-3 col-form-label") }}
-                {{ form.device_drivers_ietf_network_topology }} {{
-                form.device_drivers_ietf_network_topology.label(class="col-sm-3
-                col-form-label") }}
-                {{ form.device_drivers_onf_tr_352 }} {{ form.device_drivers_onf_tr_352.label(class="col-sm-3
-                col-form-label") }}<br />
-                {{ form.device_drivers_xr }} {{ form.device_drivers_xr.label(class="col-sm-3
-                col-form-label") }}
+                {{ form.device_drivers_undefined }} {{ form.device_drivers_undefined.label(class="col-sm-3 col-form-label") }}
+                {{ form.device_drivers_openconfig }} {{ form.device_drivers_openconfig.label(class="col-sm-3 col-form-label") }}
+                {{ form.device_drivers_transport_api }} {{ form.device_drivers_transport_api.label(class="col-sm-3 col-form-label") }}
+                <br />
+                {{ form.device_drivers_p4 }} {{ form.device_drivers_p4.label(class="col-sm-3 col-form-label") }}
+                {{ form.device_drivers_ietf_network_topology }} {{form.device_drivers_ietf_network_topology.label(class="col-sm-3 col-form-label") }}
+                {{ form.device_drivers_onf_tr_352 }} {{ form.device_drivers_onf_tr_352.label(class="col-sm-3 col-form-label") }}
+                <br />
+                {{ form.device_drivers_xr }} {{ form.device_drivers_xr.label(class="col-sm-3 col-form-label") }}
+                {{ form.device_drivers_ietf_l2vpn }} {{ form.device_drivers_ietf_l2vpn.label(class="col-sm-3 col-form-label") }}
+                {{ form.device_drivers_gnmi_openconfig }} {{ form.device_drivers_gnmi_openconfig.label(class="col-sm-3 col-form-label") }}
                 {% endif %}
             </div>
         </div>
diff --git a/src/webui/service/templates/service/home.html b/src/webui/service/templates/service/home.html
index 00feaff59128dd026ab2bdb369229a9d0aaae805..d08b78924e72fa19112f54f020f68a3e2633e63a 100644
--- a/src/webui/service/templates/service/home.html
+++ b/src/webui/service/templates/service/home.html
@@ -25,7 +25,7 @@
                 <i class="bi bi-plus"></i>
                 Add New Service
             </a>
-        </div> -->
+        </div>
 
         <!-- Only display XR service addition button if there are XR constellations. Otherwise it might confuse
              user, as other service types do not have GUI to add service yet. -->