diff --git a/src/common/message_broker/Factory.py b/src/common/message_broker/Factory.py
index 34e030b0e2bd878e75cee8af7ea1b56bc095eb23..a2ea36435c717835bf4b7c89c2522878e67074c9 100644
--- a/src/common/message_broker/Factory.py
+++ b/src/common/message_broker/Factory.py
@@ -3,13 +3,13 @@ from typing import Optional, Union
 from .backend._Backend import _Backend
 from .backend.BackendEnum import BackendEnum
 from .backend.inmemory.InMemoryBackend import InMemoryBackend
-# from .backend.redis.RedisBackend import RedisBackend
+from .backend.redis.RedisBackend import RedisBackend
 
 LOGGER = logging.getLogger(__name__)
 
 BACKENDS = {
     BackendEnum.INMEMORY.value: InMemoryBackend,
-    # BackendEnum.REDIS.value: RedisBackend,
+    BackendEnum.REDIS.value: RedisBackend,
     #BackendEnum.KAFKA.value: KafkaBackend,
     #BackendEnum.RABBITMQ.value: RabbitMQBackend,
     #BackendEnum.ZEROMQ.value: ZeroMQBackend,
diff --git a/src/common/orm/Factory.py b/src/common/orm/Factory.py
index df1df56eec900ea8d6dffce5c1dd1132e0e88eee..6ef0e11ccdd7b2f0f9e3fde62903fef522cb9f7a 100644
--- a/src/common/orm/Factory.py
+++ b/src/common/orm/Factory.py
@@ -3,13 +3,13 @@ from typing import Optional, Union
 from .backend._Backend import _Backend
 from .backend.BackendEnum import BackendEnum
 from .backend.inmemory.InMemoryBackend import InMemoryBackend
-# from .backend.redis.RedisBackend import RedisBackend
+from .backend.redis.RedisBackend import RedisBackend
 
 LOGGER = logging.getLogger(__name__)
 
 BACKENDS = {
     BackendEnum.INMEMORY.value: InMemoryBackend,
-    # BackendEnum.REDIS.value: RedisBackend,
+    BackendEnum.REDIS.value: RedisBackend,
     #BackendEnum.MONGODB.value: MongoDBBackend,
     #BackendEnum.RETHINKDB.value: RethinkDBBackend,
     #BackendEnum.ETCD.value: EtcdBackend,
diff --git a/src/context/proto/context_pb2.py b/src/context/proto/context_pb2.py
index 658c58897615b33a435c7004d05b0a291abf95b7..8b4848bc33bfb0eba76590c8a3a627b2db84ca9f 100644
--- a/src/context/proto/context_pb2.py
+++ b/src/context/proto/context_pb2.py
@@ -12,7 +12,6 @@ from google.protobuf import symbol_database as _symbol_database
 _sym_db = _symbol_database.Default()
 
 
-from . import kpi_sample_types_pb2 as kpi__sample__types__pb2
 
 
 DESCRIPTOR = _descriptor.FileDescriptor(
@@ -21,9 +20,8 @@ DESCRIPTOR = _descriptor.FileDescriptor(
   syntax='proto3',
   serialized_options=None,
   create_key=_descriptor._internal_create_key,
-  serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"K\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\"\x9f\x01\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\x12\x38\n\x0fkpi_sample_type\x18\x04 \x01(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x8d\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12.\n\x12related_service_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12!\n\x04path\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xa5\r\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x62\x06proto3'
-  ,
-  dependencies=[kpi__sample__types__pb2.DESCRIPTOR,])
+  serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"K\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x8d\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12.\n\x12related_service_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12!\n\x04path\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xa5\r\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x62\x06proto3'
+)
 
 _EVENTTYPEENUM = _descriptor.EnumDescriptor(
   name='EventTypeEnum',
@@ -55,8 +53,8 @@ _EVENTTYPEENUM = _descriptor.EnumDescriptor(
   ],
   containing_type=None,
   serialized_options=None,
-  serialized_start=3551,
-  serialized_end=3657,
+  serialized_start=3468,
+  serialized_end=3574,
 )
 _sym_db.RegisterEnumDescriptor(_EVENTTYPEENUM)
 
@@ -101,8 +99,8 @@ _DEVICEDRIVERENUM = _descriptor.EnumDescriptor(
   ],
   containing_type=None,
   serialized_options=None,
-  serialized_start=3660,
-  serialized_end=3857,
+  serialized_start=3577,
+  serialized_end=3774,
 )
 _sym_db.RegisterEnumDescriptor(_DEVICEDRIVERENUM)
 
@@ -132,8 +130,8 @@ _DEVICEOPERATIONALSTATUSENUM = _descriptor.EnumDescriptor(
   ],
   containing_type=None,
   serialized_options=None,
-  serialized_start=3860,
-  serialized_end=4003,
+  serialized_start=3777,
+  serialized_end=3920,
 )
 _sym_db.RegisterEnumDescriptor(_DEVICEOPERATIONALSTATUSENUM)
 
@@ -168,8 +166,8 @@ _SERVICETYPEENUM = _descriptor.EnumDescriptor(
   ],
   containing_type=None,
   serialized_options=None,
-  serialized_start=4006,
-  serialized_end=4135,
+  serialized_start=3923,
+  serialized_end=4052,
 )
 _sym_db.RegisterEnumDescriptor(_SERVICETYPEENUM)
 
@@ -204,8 +202,8 @@ _SERVICESTATUSENUM = _descriptor.EnumDescriptor(
   ],
   containing_type=None,
   serialized_options=None,
-  serialized_start=4138,
-  serialized_end=4274,
+  serialized_start=4055,
+  serialized_end=4191,
 )
 _sym_db.RegisterEnumDescriptor(_SERVICESTATUSENUM)
 
@@ -235,8 +233,8 @@ _CONFIGACTIONENUM = _descriptor.EnumDescriptor(
   ],
   containing_type=None,
   serialized_options=None,
-  serialized_start=4276,
-  serialized_end=4369,
+  serialized_start=4193,
+  serialized_end=4286,
 )
 _sym_db.RegisterEnumDescriptor(_CONFIGACTIONENUM)
 
@@ -288,8 +286,8 @@ _EMPTY = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=50,
-  serialized_end=57,
+  serialized_start=26,
+  serialized_end=33,
 )
 
 
@@ -320,8 +318,8 @@ _UUID = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=59,
-  serialized_end=79,
+  serialized_start=35,
+  serialized_end=55,
 )
 
 
@@ -359,8 +357,8 @@ _EVENT = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=81,
-  serialized_end=151,
+  serialized_start=57,
+  serialized_end=127,
 )
 
 
@@ -391,8 +389,8 @@ _CONTEXTID = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=153,
-  serialized_end=201,
+  serialized_start=129,
+  serialized_end=177,
 )
 
 
@@ -444,8 +442,8 @@ _CONTEXT = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=204,
-  serialized_end=386,
+  serialized_start=180,
+  serialized_end=362,
 )
 
 
@@ -476,8 +474,8 @@ _CONTEXTIDLIST = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=388,
-  serialized_end=444,
+  serialized_start=364,
+  serialized_end=420,
 )
 
 
@@ -508,8 +506,8 @@ _CONTEXTLIST = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=446,
-  serialized_end=495,
+  serialized_start=422,
+  serialized_end=471,
 )
 
 
@@ -547,8 +545,8 @@ _CONTEXTEVENT = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=497,
-  serialized_end=582,
+  serialized_start=473,
+  serialized_end=558,
 )
 
 
@@ -586,8 +584,8 @@ _TOPOLOGYID = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=584,
-  serialized_end=674,
+  serialized_start=560,
+  serialized_end=650,
 )
 
 
@@ -632,8 +630,8 @@ _TOPOLOGY = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=676,
-  serialized_end=802,
+  serialized_start=652,
+  serialized_end=778,
 )
 
 
@@ -664,8 +662,8 @@ _TOPOLOGYIDLIST = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=804,
-  serialized_end=863,
+  serialized_start=780,
+  serialized_end=839,
 )
 
 
@@ -696,8 +694,8 @@ _TOPOLOGYLIST = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=865,
-  serialized_end=918,
+  serialized_start=841,
+  serialized_end=894,
 )
 
 
@@ -735,8 +733,8 @@ _TOPOLOGYEVENT = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=920,
-  serialized_end=1008,
+  serialized_start=896,
+  serialized_end=984,
 )
 
 
@@ -767,8 +765,8 @@ _DEVICEID = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=1010,
-  serialized_end=1056,
+  serialized_start=986,
+  serialized_end=1032,
 )
 
 
@@ -834,8 +832,8 @@ _DEVICE = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=1059,
-  serialized_end=1341,
+  serialized_start=1035,
+  serialized_end=1317,
 )
 
 
@@ -866,8 +864,8 @@ _DEVICECONFIG = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=1343,
-  serialized_end=1400,
+  serialized_start=1319,
+  serialized_end=1376,
 )
 
 
@@ -898,8 +896,8 @@ _DEVICEIDLIST = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=1402,
-  serialized_end=1455,
+  serialized_start=1378,
+  serialized_end=1431,
 )
 
 
@@ -930,8 +928,8 @@ _DEVICELIST = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=1457,
-  serialized_end=1503,
+  serialized_start=1433,
+  serialized_end=1479,
 )
 
 
@@ -969,8 +967,8 @@ _DEVICEEVENT = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=1505,
-  serialized_end=1587,
+  serialized_start=1481,
+  serialized_end=1563,
 )
 
 
@@ -1001,8 +999,8 @@ _LINKID = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=1589,
-  serialized_end=1631,
+  serialized_start=1565,
+  serialized_end=1607,
 )
 
 
@@ -1040,8 +1038,8 @@ _LINK = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=1633,
-  serialized_end=1721,
+  serialized_start=1609,
+  serialized_end=1697,
 )
 
 
@@ -1072,8 +1070,8 @@ _LINKIDLIST = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=1723,
-  serialized_end=1770,
+  serialized_start=1699,
+  serialized_end=1746,
 )
 
 
@@ -1104,8 +1102,8 @@ _LINKLIST = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=1772,
-  serialized_end=1812,
+  serialized_start=1748,
+  serialized_end=1788,
 )
 
 
@@ -1143,8 +1141,8 @@ _LINKEVENT = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=1814,
-  serialized_end=1890,
+  serialized_start=1790,
+  serialized_end=1866,
 )
 
 
@@ -1182,8 +1180,8 @@ _SERVICEID = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=1892,
-  serialized_end=1980,
+  serialized_start=1868,
+  serialized_end=1956,
 )
 
 
@@ -1249,8 +1247,8 @@ _SERVICE = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=1983,
-  serialized_end=2277,
+  serialized_start=1959,
+  serialized_end=2253,
 )
 
 
@@ -1281,8 +1279,8 @@ _SERVICESTATUS = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=2279,
-  serialized_end=2346,
+  serialized_start=2255,
+  serialized_end=2322,
 )
 
 
@@ -1313,8 +1311,8 @@ _SERVICECONFIG = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=2348,
-  serialized_end=2406,
+  serialized_start=2324,
+  serialized_end=2382,
 )
 
 
@@ -1345,8 +1343,8 @@ _SERVICEIDLIST = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=2408,
-  serialized_end=2464,
+  serialized_start=2384,
+  serialized_end=2440,
 )
 
 
@@ -1377,8 +1375,8 @@ _SERVICELIST = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=2466,
-  serialized_end=2515,
+  serialized_start=2442,
+  serialized_end=2491,
 )
 
 
@@ -1416,8 +1414,8 @@ _SERVICEEVENT = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=2517,
-  serialized_end=2602,
+  serialized_start=2493,
+  serialized_end=2578,
 )
 
 
@@ -1462,8 +1460,8 @@ _ENDPOINTID = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=2605,
-  serialized_end=2735,
+  serialized_start=2581,
+  serialized_end=2711,
 )
 
 
@@ -1501,8 +1499,8 @@ _ENDPOINT = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=2737,
-  serialized_end=2812,
+  serialized_start=2713,
+  serialized_end=2788,
 )
 
 
@@ -1535,13 +1533,6 @@ _CONFIGRULE = _descriptor.Descriptor(
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='kpi_sample_type', full_name='context.ConfigRule.kpi_sample_type', index=3,
-      number=4, type=14, cpp_type=8, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
   ],
   extensions=[
   ],
@@ -1554,8 +1545,8 @@ _CONFIGRULE = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=2815,
-  serialized_end=2974,
+  serialized_start=2790,
+  serialized_end=2891,
 )
 
 
@@ -1593,8 +1584,8 @@ _CONSTRAINT = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=2976,
-  serialized_end=3039,
+  serialized_start=2893,
+  serialized_end=2956,
 )
 
 
@@ -1625,8 +1616,8 @@ _CONNECTIONID = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=3041,
-  serialized_end=3095,
+  serialized_start=2958,
+  serialized_end=3012,
 )
 
 
@@ -1671,8 +1662,8 @@ _CONNECTION = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=3098,
-  serialized_end=3239,
+  serialized_start=3015,
+  serialized_end=3156,
 )
 
 
@@ -1703,8 +1694,8 @@ _CONNECTIONIDLIST = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=3241,
-  serialized_end=3306,
+  serialized_start=3158,
+  serialized_end=3223,
 )
 
 
@@ -1735,8 +1726,8 @@ _CONNECTIONLIST = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=3308,
-  serialized_end=3366,
+  serialized_start=3225,
+  serialized_end=3283,
 )
 
 
@@ -1781,8 +1772,8 @@ _TERAFLOWCONTROLLER = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=3368,
-  serialized_end=3462,
+  serialized_start=3285,
+  serialized_end=3379,
 )
 
 
@@ -1820,8 +1811,8 @@ _AUTHENTICATIONRESULT = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=3464,
-  serialized_end=3549,
+  serialized_start=3381,
+  serialized_end=3466,
 )
 
 _EVENT.fields_by_name['event_type'].enum_type = _EVENTTYPEENUM
@@ -1880,7 +1871,6 @@ _ENDPOINTID.fields_by_name['device_id'].message_type = _DEVICEID
 _ENDPOINTID.fields_by_name['endpoint_uuid'].message_type = _UUID
 _ENDPOINT.fields_by_name['endpoint_id'].message_type = _ENDPOINTID
 _CONFIGRULE.fields_by_name['action'].enum_type = _CONFIGACTIONENUM
-_CONFIGRULE.fields_by_name['kpi_sample_type'].enum_type = kpi__sample__types__pb2._KPISAMPLETYPE
 _CONNECTIONID.fields_by_name['connection_uuid'].message_type = _UUID
 _CONNECTION.fields_by_name['connection_id'].message_type = _CONNECTIONID
 _CONNECTION.fields_by_name['related_service_id'].message_type = _SERVICEID
@@ -2234,8 +2224,8 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor(
   index=0,
   serialized_options=None,
   create_key=_descriptor._internal_create_key,
-  serialized_start=4372,
-  serialized_end=6073,
+  serialized_start=4289,
+  serialized_end=5990,
   methods=[
   _descriptor.MethodDescriptor(
     name='ListContextIds',
diff --git a/src/context/proto/context_pb2_grpc.py b/src/context/proto/context_pb2_grpc.py
index 63a7edb530ab138e7f2d6c2ba2d6075db52e2fee..c344c8b539324140fddc411324959368da3c1b7b 100644
--- a/src/context/proto/context_pb2_grpc.py
+++ b/src/context/proto/context_pb2_grpc.py
@@ -6,7 +6,9 @@ from . import context_pb2 as context__pb2
 
 
 class ContextServiceStub(object):
-    """Missing associated documentation comment in .proto file."""
+    """import "kpi_sample_types.proto";
+
+    """
 
     def __init__(self, channel):
         """Constructor.
@@ -167,7 +169,9 @@ class ContextServiceStub(object):
 
 
 class ContextServiceServicer(object):
-    """Missing associated documentation comment in .proto file."""
+    """import "kpi_sample_types.proto";
+
+    """
 
     def ListContextIds(self, request, context):
         """Missing associated documentation comment in .proto file."""
@@ -510,7 +514,9 @@ def add_ContextServiceServicer_to_server(servicer, server):
 
  # This class is part of an EXPERIMENTAL API.
 class ContextService(object):
-    """Missing associated documentation comment in .proto file."""
+    """import "kpi_sample_types.proto";
+
+    """
 
     @staticmethod
     def ListContextIds(request,
diff --git a/src/context/proto/kpi_sample_types_pb2.py b/src/context/proto/kpi_sample_types_pb2.py
index 31fbaa216bca629a4de4272091c490982c1aa166..ad22554ec352d0aeae644fdce00c0f28996ed73b 100644
--- a/src/context/proto/kpi_sample_types_pb2.py
+++ b/src/context/proto/kpi_sample_types_pb2.py
@@ -2,7 +2,6 @@
 # Generated by the protocol buffer compiler.  DO NOT EDIT!
 # source: kpi_sample_types.proto
 """Generated protocol buffer code."""
-from google.protobuf.internal import enum_type_wrapper
 from google.protobuf import descriptor as _descriptor
 from google.protobuf import message as _message
 from google.protobuf import reflection as _reflection
@@ -16,62 +15,15 @@ _sym_db = _symbol_database.Default()
 
 DESCRIPTOR = _descriptor.FileDescriptor(
   name='kpi_sample_types.proto',
-  package='kpi_sample_types',
+  package='',
   syntax='proto3',
   serialized_options=None,
   create_key=_descriptor._internal_create_key,
-  serialized_pb=b'\n\x16kpi_sample_types.proto\x12\x10kpi_sample_types*x\n\rKpiSampleType\x12\x0b\n\x07UNKNOWN\x10\x00\x12\x17\n\x13PACKETS_TRANSMITTED\x10\x65\x12\x14\n\x10PACKETS_RECEIVED\x10\x66\x12\x16\n\x11\x42YTES_TRANSMITTED\x10\xc9\x01\x12\x13\n\x0e\x42YTES_RECEIVED\x10\xca\x01\x62\x06proto3'
+  serialized_pb=b'\n\x16kpi_sample_types.protob\x06proto3'
 )
 
-_KPISAMPLETYPE = _descriptor.EnumDescriptor(
-  name='KpiSampleType',
-  full_name='kpi_sample_types.KpiSampleType',
-  filename=None,
-  file=DESCRIPTOR,
-  create_key=_descriptor._internal_create_key,
-  values=[
-    _descriptor.EnumValueDescriptor(
-      name='UNKNOWN', index=0, number=0,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='PACKETS_TRANSMITTED', index=1, number=101,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='PACKETS_RECEIVED', index=2, number=102,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='BYTES_TRANSMITTED', index=3, number=201,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='BYTES_RECEIVED', index=4, number=202,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-  ],
-  containing_type=None,
-  serialized_options=None,
-  serialized_start=44,
-  serialized_end=164,
-)
-_sym_db.RegisterEnumDescriptor(_KPISAMPLETYPE)
-
-KpiSampleType = enum_type_wrapper.EnumTypeWrapper(_KPISAMPLETYPE)
-UNKNOWN = 0
-PACKETS_TRANSMITTED = 101
-PACKETS_RECEIVED = 102
-BYTES_TRANSMITTED = 201
-BYTES_RECEIVED = 202
 
 
-DESCRIPTOR.enum_types_by_name['KpiSampleType'] = _KPISAMPLETYPE
 _sym_db.RegisterFileDescriptor(DESCRIPTOR)
 
 
diff --git a/src/context/service/database/ConfigModel.py b/src/context/service/database/ConfigModel.py
index 0fe3484896a39545c4dd49042707dc1ee09fc868..d97cdb7dfe6594a59be10427ed52341346c19a97 100644
--- a/src/context/service/database/ConfigModel.py
+++ b/src/context/service/database/ConfigModel.py
@@ -1,8 +1,8 @@
 import functools, logging, operator
 from enum import Enum
-from typing import Dict, List, Tuple, Union
+from typing import Dict, List, Optional, Tuple, Union
 from common.orm.Database import Database
-from common.orm.HighLevel import get_or_create_object, update_or_create_object
+from common.orm.HighLevel import get_object, get_or_create_object, update_or_create_object
 from common.orm.backend.Tools import key_to_str
 from common.orm.fields.EnumeratedField import EnumeratedField
 from common.orm.fields.ForeignKeyField import ForeignKeyField
@@ -11,7 +11,7 @@ from common.orm.fields.PrimaryKeyField import PrimaryKeyField
 from common.orm.fields.StringField import StringField
 from common.orm.model.Model import Model
 from context.proto.context_pb2 import ConfigActionEnum
-from context.service.database.Tools import fast_hasher, grpc_to_enum, remove_dict_key
+from .Tools import fast_hasher, grpc_to_enum, remove_dict_key
 
 LOGGER = logging.getLogger(__name__)
 
@@ -50,35 +50,62 @@ class ConfigRuleModel(Model): # pylint: disable=abstract-method
         return result
 
 def set_config_rule(
-    database : Database, db_config : ConfigModel, grpc_config_rule, position : int
+    database : Database, db_config : ConfigModel, position : int, resource_key : str, resource_value : str
     ) -> Tuple[ConfigRuleModel, bool]:
 
-    str_rule_key_hash = fast_hasher(grpc_config_rule.resource_key)
+    str_rule_key_hash = fast_hasher(resource_key)
     str_config_rule_key = key_to_str([db_config.pk, str_rule_key_hash], separator=':')
-
     result : Tuple[ConfigRuleModel, bool] = update_or_create_object(database, ConfigRuleModel, str_config_rule_key, {
-        'config_fk': db_config,
-        'position' : position,
-        'action'   : grpc_to_enum__config_action(grpc_config_rule.action),
-        'key'      : grpc_config_rule.resource_key,
-        'value'    : grpc_config_rule.resource_value,
-    })
+        'config_fk': db_config, 'position': position, 'action': ORM_ConfigActionEnum.SET,
+        'key': resource_key, 'value': resource_value})
     db_config_rule, updated = result
     return db_config_rule, updated
 
-def set_config(
-    database : Database, db_parent_pk : str, config_name : str, grpc_config_rules
+def delete_config_rule(
+    database : Database, db_config : ConfigModel, resource_key : str
+    ) -> None:
+
+    str_rule_key_hash = fast_hasher(resource_key)
+    str_config_rule_key = key_to_str([db_config.pk, str_rule_key_hash], separator=':')
+    db_config_rule : Optional[ConfigRuleModel] = get_object(
+        database, ConfigRuleModel, str_config_rule_key, raise_if_not_found=False)
+    if db_config_rule is None: return
+    db_config_rule.delete()
+
+def delete_all_config_rules(
+    database : Database, db_config : ConfigModel
+    ) -> None:
+
+    db_config_rule_pks = db_config.references(ConfigRuleModel)
+    for pk,_ in db_config_rule_pks: ConfigRuleModel(database, pk).delete()
+
+def grpc_config_rules_to_raw(grpc_config_rules) -> List[Tuple[ORM_ConfigActionEnum, str, str]]:
+    def translate(grpc_config_rule):
+        action = grpc_to_enum__config_action(grpc_config_rule.action)
+        return action, grpc_config_rule.resource_key, grpc_config_rule.resource_value
+    return [translate(grpc_config_rule) for grpc_config_rule in grpc_config_rules]
+
+def update_config(
+    database : Database, db_parent_pk : str, config_name : str,
+    raw_config_rules : List[Tuple[ORM_ConfigActionEnum, str, str]]
     ) -> List[Tuple[Union[ConfigModel, ConfigRuleModel], bool]]:
 
     str_config_key = key_to_str([db_parent_pk, config_name], separator=':')
     result : Tuple[ConfigModel, bool] = get_or_create_object(database, ConfigModel, str_config_key)
     db_config, created = result
 
-    db_objects = [(db_config, created)]
+    db_objects : List[Tuple[Union[ConfigModel, ConfigRuleModel], bool]] = [(db_config, created)]
 
-    for position,grpc_config_rule in enumerate(grpc_config_rules):
-        result : Tuple[ConfigRuleModel, bool] = set_config_rule(database, db_config, grpc_config_rule, position)
-        db_config_rule, updated = result
-        db_objects.append((db_config_rule, updated))
+    for position,(action, resource_key, resource_value) in enumerate(raw_config_rules):
+        if action == ORM_ConfigActionEnum.SET:
+            result : Tuple[ConfigRuleModel, bool] = set_config_rule(
+                database, db_config, position, resource_key, resource_value)
+            db_config_rule, updated = result
+            db_objects.append((db_config_rule, updated))
+        elif action == ORM_ConfigActionEnum.DELETE:
+            delete_config_rule(database, db_config, resource_key)
+        else:
+            msg = 'Unsupported action({:s}) for resource_key({:s})/resource_value({:s})'
+            raise AttributeError(msg.format(str(ConfigActionEnum.Name(action)), str(resource_key), str(resource_value)))
 
     return db_objects
diff --git a/src/context/service/database/EndPointModel.py b/src/context/service/database/EndPointModel.py
index b7c220a00b2a85b2c3f4c11a2eceb3aa66aadc5a..38b87d6f37c4e99dd3790f4d8802acd03873f77d 100644
--- a/src/context/service/database/EndPointModel.py
+++ b/src/context/service/database/EndPointModel.py
@@ -4,8 +4,8 @@ from common.orm.fields.ForeignKeyField import ForeignKeyField
 from common.orm.fields.PrimaryKeyField import PrimaryKeyField
 from common.orm.fields.StringField import StringField
 from common.orm.model.Model import Model
-from context.service.database.DeviceModel import DeviceModel
-from context.service.database.TopologyModel import TopologyModel
+from .DeviceModel import DeviceModel
+from .TopologyModel import TopologyModel
 
 LOGGER = logging.getLogger(__name__)
 
diff --git a/src/context/service/grpc_server/ContextServiceServicerImpl.py b/src/context/service/grpc_server/ContextServiceServicerImpl.py
index d8f7b648b4b919cc61330f236195c444f550ede1..e76c399cd4e17578a01ac7bf88cb0fc3f7017b8e 100644
--- a/src/context/service/grpc_server/ContextServiceServicerImpl.py
+++ b/src/context/service/grpc_server/ContextServiceServicerImpl.py
@@ -12,7 +12,7 @@ from context.proto.context_pb2 import (
     DeviceList, Empty, EventTypeEnum, Link, LinkEvent, LinkId, LinkIdList, LinkList, Service, ServiceEvent, ServiceId,
     ServiceIdList, ServiceList, Topology, TopologyEvent, TopologyId, TopologyIdList, TopologyList)
 from context.proto.context_pb2_grpc import ContextServiceServicer
-from context.service.database.ConfigModel import ConfigModel, ConfigRuleModel, set_config
+from context.service.database.ConfigModel import ConfigModel, ConfigRuleModel, grpc_config_rules_to_raw, update_config
 from context.service.database.ConstraintModel import ConstraintModel, ConstraintsModel, set_constraints
 from context.service.database.ContextModel import ContextModel
 from context.service.database.DeviceModel import (
@@ -236,7 +236,8 @@ class ContextServiceServicerImpl(ContextServiceServicer):
                     'request.device_endpoints[{:d}].device_id.device_uuid.uuid'.format(i), endpoint_device_uuid,
                     ['should be == {:s}({:s})'.format('request.device_id.device_uuid.uuid', device_uuid)])
 
-        running_config_result = set_config(self.database, device_uuid, 'running', request.device_config.config_rules)
+        config_rules = grpc_config_rules_to_raw(request.device_config.config_rules)
+        running_config_result = update_config(self.database, device_uuid, 'running', config_rules)
         db_running_config = running_config_result[0][0]
 
         result : Tuple[DeviceModel, bool] = update_or_create_object(self.database, DeviceModel, device_uuid, {
@@ -452,8 +453,8 @@ class ContextServiceServicerImpl(ContextServiceServicer):
             self.database, str_service_key, 'constraints', request.service_constraints)
         db_constraints = constraints_result[0][0]
 
-        running_config_result = set_config(
-            self.database, str_service_key, 'running', request.service_config.config_rules)
+        config_rules = grpc_config_rules_to_raw(request.service_config.config_rules)
+        running_config_result = update_config(self.database, str_service_key, 'running', config_rules)
         db_running_config = running_config_result[0][0]
 
         result : Tuple[ServiceModel, bool] = update_or_create_object(self.database, ServiceModel, str_service_key, {
diff --git a/src/context/tests/example_objects.py b/src/context/tests/example_objects.py
index 226119f9d29213a6f1857a476b37b32c9941a813..81339c04e1fe77667bd41179f3fa0813c5fc69df 100644
--- a/src/context/tests/example_objects.py
+++ b/src/context/tests/example_objects.py
@@ -2,13 +2,12 @@ from copy import deepcopy
 from common.Constants import DEFAULT_CONTEXT_UUID, DEFAULT_TOPOLOGY_UUID
 from context.proto.context_pb2 import (
     ConfigActionEnum, DeviceDriverEnum, DeviceOperationalStatusEnum, ServiceStatusEnum, ServiceTypeEnum)
-from context.proto.kpi_sample_types_pb2 import KpiSampleType
 
 # Some example objects to be used by the tests
 
 # Helper methods
-def config_rule(action, resource_key, resource_value, kpi_sample_type):
-    return {'action': action, 'resource_key': resource_key, 'resource_value': resource_value, 'kpi_sample_type': kpi_sample_type}
+def config_rule(action, resource_key, resource_value):
+    return {'action': action, 'resource_key': resource_key, 'resource_value': resource_value}
 
 def endpoint_id(topology_id, device_id, endpoint_uuid):
     return {'topology_id': deepcopy(topology_id), 'device_id': deepcopy(device_id),
@@ -41,10 +40,9 @@ DEVICE1 = {
     'device_id': deepcopy(DEVICE1_ID),
     'device_type': 'packet-router',
     'device_config': {'config_rules': [
-        config_rule(ConfigActionEnum.CONFIGACTION_SET, 'dev/rsrc1/value', 'value1', KpiSampleType.PACKETS_TRANSMITTED),
-        config_rule(ConfigActionEnum.CONFIGACTION_SET, 'dev/rsrc2/value', 'value2', KpiSampleType.PACKETS_RECEIVED),
-        config_rule(ConfigActionEnum.CONFIGACTION_SET, 'dev/rsrc3/value', 'value3', KpiSampleType.BYTES_TRANSMITTED),
-        config_rule(ConfigActionEnum.CONFIGACTION_SET, 'dev/rsrc4/value', 'value4', KpiSampleType.BYTES_RECEIVED),
+        config_rule(ConfigActionEnum.CONFIGACTION_SET, 'dev/rsrc1/value', 'value1'),
+        config_rule(ConfigActionEnum.CONFIGACTION_SET, 'dev/rsrc2/value', 'value2'),
+        config_rule(ConfigActionEnum.CONFIGACTION_SET, 'dev/rsrc3/value', 'value3'),
     ]},
     'device_operational_status': DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_ENABLED,
     'device_drivers': [DeviceDriverEnum.DEVICEDRIVER_OPENCONFIG, DeviceDriverEnum.DEVICEDRIVER_P4],
@@ -61,9 +59,9 @@ DEVICE2 = {
     'device_id': deepcopy(DEVICE2_ID),
     'device_type': 'packet-router',
     'device_config': {'config_rules': [
-        config_rule(ConfigActionEnum.CONFIGACTION_SET, 'dev/rsrc1/value', 'value4', KpiSampleType.PACKETS_TRANSMITTED),
-        config_rule(ConfigActionEnum.CONFIGACTION_SET, 'dev/rsrc2/value', 'value5', KpiSampleType.PACKETS_RECEIVED),
-        config_rule(ConfigActionEnum.CONFIGACTION_SET, 'dev/rsrc3/value', 'value6', KpiSampleType.BYTES_TRANSMITTED),
+        config_rule(ConfigActionEnum.CONFIGACTION_SET, 'dev/rsrc1/value', 'value4'),
+        config_rule(ConfigActionEnum.CONFIGACTION_SET, 'dev/rsrc2/value', 'value5'),
+        config_rule(ConfigActionEnum.CONFIGACTION_SET, 'dev/rsrc3/value', 'value6'),
     ]},
     'device_operational_status': DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_ENABLED,
     'device_drivers': [DeviceDriverEnum.DEVICEDRIVER_OPENCONFIG, DeviceDriverEnum.DEVICEDRIVER_P4],
@@ -80,9 +78,9 @@ DEVICE3 = {
     'device_id': deepcopy(DEVICE3_ID),
     'device_type': 'packet-router',
     'device_config': {'config_rules': [
-        config_rule(ConfigActionEnum.CONFIGACTION_SET, 'dev/rsrc1/value', 'value4', KpiSampleType.PACKETS_TRANSMITTED),
-        config_rule(ConfigActionEnum.CONFIGACTION_SET, 'dev/rsrc2/value', 'value5', KpiSampleType.PACKETS_RECEIVED),
-        config_rule(ConfigActionEnum.CONFIGACTION_SET, 'dev/rsrc3/value', 'value6', KpiSampleType.BYTES_TRANSMITTED),
+        config_rule(ConfigActionEnum.CONFIGACTION_SET, 'dev/rsrc1/value', 'value4'),
+        config_rule(ConfigActionEnum.CONFIGACTION_SET, 'dev/rsrc2/value', 'value5'),
+        config_rule(ConfigActionEnum.CONFIGACTION_SET, 'dev/rsrc3/value', 'value6'),
     ]},
     'device_operational_status': DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_ENABLED,
     'device_drivers': [DeviceDriverEnum.DEVICEDRIVER_OPENCONFIG, DeviceDriverEnum.DEVICEDRIVER_P4],
@@ -141,9 +139,9 @@ SERVICE_DEV1_DEV2 = {
     ],
     'service_status': {'service_status': ServiceStatusEnum.SERVICESTATUS_ACTIVE},
     'service_config': {'config_rules': [
-        config_rule(ConfigActionEnum.CONFIGACTION_SET, 'svc/rsrc1/value', 'value7', KpiSampleType.PACKETS_TRANSMITTED),
-        config_rule(ConfigActionEnum.CONFIGACTION_SET, 'svc/rsrc2/value', 'value8', KpiSampleType.PACKETS_TRANSMITTED),
-        config_rule(ConfigActionEnum.CONFIGACTION_SET, 'svc/rsrc3/value', 'value9', KpiSampleType.PACKETS_TRANSMITTED),
+        config_rule(ConfigActionEnum.CONFIGACTION_SET, 'svc/rsrc1/value', 'value7'),
+        config_rule(ConfigActionEnum.CONFIGACTION_SET, 'svc/rsrc2/value', 'value8'),
+        config_rule(ConfigActionEnum.CONFIGACTION_SET, 'svc/rsrc3/value', 'value9'),
     ]},
 }
 
@@ -165,9 +163,9 @@ SERVICE_DEV1_DEV3 = {
     ],
     'service_status': {'service_status': ServiceStatusEnum.SERVICESTATUS_ACTIVE},
     'service_config': {'config_rules': [
-        config_rule(ConfigActionEnum.CONFIGACTION_SET, 'svc/rsrc1/value', 'value7', KpiSampleType.PACKETS_TRANSMITTED),
-        config_rule(ConfigActionEnum.CONFIGACTION_SET, 'svc/rsrc2/value', 'value8', KpiSampleType.PACKETS_TRANSMITTED),
-        config_rule(ConfigActionEnum.CONFIGACTION_SET, 'svc/rsrc3/value', 'value9', KpiSampleType.PACKETS_TRANSMITTED),
+        config_rule(ConfigActionEnum.CONFIGACTION_SET, 'svc/rsrc1/value', 'value7'),
+        config_rule(ConfigActionEnum.CONFIGACTION_SET, 'svc/rsrc2/value', 'value8'),
+        config_rule(ConfigActionEnum.CONFIGACTION_SET, 'svc/rsrc3/value', 'value9'),
     ]},
 }
 
@@ -189,8 +187,8 @@ SERVICE_DEV2_DEV3 = {
     ],
     'service_status': {'service_status': ServiceStatusEnum.SERVICESTATUS_ACTIVE},
     'service_config': {'config_rules': [
-        config_rule(ConfigActionEnum.CONFIGACTION_SET, 'svc/rsrc1/value', 'value7', KpiSampleType.PACKETS_TRANSMITTED),
-        config_rule(ConfigActionEnum.CONFIGACTION_SET, 'svc/rsrc2/value', 'value8', KpiSampleType.PACKETS_TRANSMITTED),
-        config_rule(ConfigActionEnum.CONFIGACTION_SET, 'svc/rsrc3/value', 'value9', KpiSampleType.PACKETS_TRANSMITTED),
+        config_rule(ConfigActionEnum.CONFIGACTION_SET, 'svc/rsrc1/value', 'value7'),
+        config_rule(ConfigActionEnum.CONFIGACTION_SET, 'svc/rsrc2/value', 'value8'),
+        config_rule(ConfigActionEnum.CONFIGACTION_SET, 'svc/rsrc3/value', 'value9'),
     ]},
 }
diff --git a/src/device/.gitlab-ci.yml b/src/device/.gitlab-ci.yml
index fca94bf712606f590c13c3e90f8fc686b9238471..e6ee81e379f8eeeca6c715bff5dfc6f2cf1c3d23 100644
--- a/src/device/.gitlab-ci.yml
+++ b/src/device/.gitlab-ci.yml
@@ -35,7 +35,7 @@ unit_test device:
     - sleep 5
     - docker ps -a
     - docker logs $IMAGE_NAME
-    - docker exec -i $IMAGE_NAME bash -c "pytest --log-level=DEBUG --verbose $IMAGE_NAME/tests/test_unitary_service.py $IMAGE_NAME/tests/test_unitary_driverapi.py"
+    - docker exec -i $IMAGE_NAME bash -c "pytest --log-level=DEBUG --verbose $IMAGE_NAME/tests/test_unitary_driverapi.py $IMAGE_NAME/tests/test_unitary.py"
   after_script:
     - docker stop $IMAGE_NAME
     - docker rm $IMAGE_NAME
diff --git a/src/device/Config.py b/src/device/Config.py
index 6787413a881b4d84ed6b4ecb5e041901399a4ae6..753f4b57b2767552317882e72622c613f40e3ea2 100644
--- a/src/device/Config.py
+++ b/src/device/Config.py
@@ -10,3 +10,9 @@ GRPC_GRACE_PERIOD = 60
 
 # Prometheus settings
 METRICS_PORT = 9192
+
+# Dependency micro-service connection settings
+CONTEXT_SERVICE_HOST = '127.0.0.1'
+CONTEXT_SERVICE_PORT = 1010
+MONITORING_SERVICE_HOST = '127.0.0.1'
+MONITORING_SERVICE_PORT = 7070
diff --git a/src/device/Dockerfile b/src/device/Dockerfile
index 52eb5c82cce99ebc488ae586fd29375dba709a09..70e3c8dcbbc6b78a53b468e51034ffb8e69f8e53 100644
--- a/src/device/Dockerfile
+++ b/src/device/Dockerfile
@@ -29,7 +29,9 @@ RUN python3 -m pip install -r device/requirements.in
 
 # Add files into working directory
 COPY common/. common
+COPY context/. context
 COPY device/. device
+COPY monitoring/. monitoring
 
 # Start device service
 ENTRYPOINT ["python", "-m", "device.service"]
diff --git a/src/device/client/DeviceClient.py b/src/device/client/DeviceClient.py
index d9cd16f77b607ac0817c16448d65c7f563201646..3841bf8cb4892dcc191d536061aea2bb1bd9d06d 100644
--- a/src/device/client/DeviceClient.py
+++ b/src/device/client/DeviceClient.py
@@ -1,7 +1,7 @@
 import grpc, logging
 from common.tools.client.RetryDecorator import retry, delay_exponential
-from device.proto.context_pb2 import Device, DeviceId, Empty
-from device.proto.device_pb2 import MonitoringSettings
+from device.proto.context_pb2 import Device, DeviceConfig, DeviceId, Empty
+#from device.proto.device_pb2 import MonitoringSettings
 from device.proto.device_pb2_grpc import DeviceServiceStub
 
 LOGGER = logging.getLogger(__name__)
@@ -22,7 +22,7 @@ class DeviceClient:
         self.stub = DeviceServiceStub(self.channel)
 
     def close(self):
-        if(self.channel is not None): self.channel.close()
+        if self.channel is not None: self.channel.close()
         self.channel = None
         self.stub = None
 
@@ -48,8 +48,15 @@ class DeviceClient:
         return response
 
     @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
-    def MonitorDeviceKpi(self, request: MonitoringSettings) -> Empty:
-        LOGGER.debug('MonitorDeviceKpi request: {:s}'.format(str(request)))
-        response = self.stub.MonitorDeviceKpi(request)
-        LOGGER.debug('MonitorDeviceKpi result: {:s}'.format(str(response)))
+    def GetInitialConfig(self, request : DeviceId) -> DeviceConfig:
+        LOGGER.debug('GetInitialConfig request: {:s}'.format(str(request)))
+        response = self.stub.GetInitialConfig(request)
+        LOGGER.debug('GetInitialConfig result: {:s}'.format(str(response)))
         return response
+
+    #@retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+    #def MonitorDeviceKpi(self, request : MonitoringSettings) -> Empty:
+    #    LOGGER.debug('MonitorDeviceKpi request: {:s}'.format(str(request)))
+    #    response = self.stub.MonitorDeviceKpi(request)
+    #    LOGGER.debug('MonitorDeviceKpi result: {:s}'.format(str(response)))
+    #    return response
diff --git a/src/device/genproto.sh b/src/device/genproto.sh
index c5f9ec20f289ff2005641ac2489db1469fcbcaaf..31632fb894f3ede9582063d81b1911fe2551d96d 100755
--- a/src/device/genproto.sh
+++ b/src/device/genproto.sh
@@ -26,10 +26,14 @@ touch proto/__init__.py
 python -m grpc_tools.protoc -I../../proto --python_out=proto --grpc_python_out=proto context.proto
 python -m grpc_tools.protoc -I../../proto --python_out=proto --grpc_python_out=proto device.proto
 python -m grpc_tools.protoc -I../../proto --python_out=proto --grpc_python_out=proto kpi_sample_types.proto
+python -m grpc_tools.protoc -I../../proto --python_out=proto --grpc_python_out=proto monitoring.proto
 
 rm proto/context_pb2_grpc.py
 rm proto/kpi_sample_types_pb2_grpc.py
+rm proto/monitoring_pb2_grpc.py
 
 sed -i -E 's/(import\ .*)_pb2/from . \1_pb2/g' proto/context_pb2.py
 sed -i -E 's/(import\ .*)_pb2/from . \1_pb2/g' proto/device_pb2.py
 sed -i -E 's/(import\ .*)_pb2/from . \1_pb2/g' proto/device_pb2_grpc.py
+sed -i -E 's/(import\ .*)_pb2/from . \1_pb2/g' proto/kpi_sample_types_pb2.py
+sed -i -E 's/(import\ .*)_pb2/from . \1_pb2/g' proto/monitoring_pb2.py
diff --git a/src/device/proto/context_pb2.py b/src/device/proto/context_pb2.py
index 658c58897615b33a435c7004d05b0a291abf95b7..8b4848bc33bfb0eba76590c8a3a627b2db84ca9f 100644
--- a/src/device/proto/context_pb2.py
+++ b/src/device/proto/context_pb2.py
@@ -12,7 +12,6 @@ from google.protobuf import symbol_database as _symbol_database
 _sym_db = _symbol_database.Default()
 
 
-from . import kpi_sample_types_pb2 as kpi__sample__types__pb2
 
 
 DESCRIPTOR = _descriptor.FileDescriptor(
@@ -21,9 +20,8 @@ DESCRIPTOR = _descriptor.FileDescriptor(
   syntax='proto3',
   serialized_options=None,
   create_key=_descriptor._internal_create_key,
-  serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"K\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\"\x9f\x01\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\x12\x38\n\x0fkpi_sample_type\x18\x04 \x01(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x8d\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12.\n\x12related_service_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12!\n\x04path\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xa5\r\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x62\x06proto3'
-  ,
-  dependencies=[kpi__sample__types__pb2.DESCRIPTOR,])
+  serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"K\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x8d\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12.\n\x12related_service_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12!\n\x04path\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xa5\r\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x62\x06proto3'
+)
 
 _EVENTTYPEENUM = _descriptor.EnumDescriptor(
   name='EventTypeEnum',
@@ -55,8 +53,8 @@ _EVENTTYPEENUM = _descriptor.EnumDescriptor(
   ],
   containing_type=None,
   serialized_options=None,
-  serialized_start=3551,
-  serialized_end=3657,
+  serialized_start=3468,
+  serialized_end=3574,
 )
 _sym_db.RegisterEnumDescriptor(_EVENTTYPEENUM)
 
@@ -101,8 +99,8 @@ _DEVICEDRIVERENUM = _descriptor.EnumDescriptor(
   ],
   containing_type=None,
   serialized_options=None,
-  serialized_start=3660,
-  serialized_end=3857,
+  serialized_start=3577,
+  serialized_end=3774,
 )
 _sym_db.RegisterEnumDescriptor(_DEVICEDRIVERENUM)
 
@@ -132,8 +130,8 @@ _DEVICEOPERATIONALSTATUSENUM = _descriptor.EnumDescriptor(
   ],
   containing_type=None,
   serialized_options=None,
-  serialized_start=3860,
-  serialized_end=4003,
+  serialized_start=3777,
+  serialized_end=3920,
 )
 _sym_db.RegisterEnumDescriptor(_DEVICEOPERATIONALSTATUSENUM)
 
@@ -168,8 +166,8 @@ _SERVICETYPEENUM = _descriptor.EnumDescriptor(
   ],
   containing_type=None,
   serialized_options=None,
-  serialized_start=4006,
-  serialized_end=4135,
+  serialized_start=3923,
+  serialized_end=4052,
 )
 _sym_db.RegisterEnumDescriptor(_SERVICETYPEENUM)
 
@@ -204,8 +202,8 @@ _SERVICESTATUSENUM = _descriptor.EnumDescriptor(
   ],
   containing_type=None,
   serialized_options=None,
-  serialized_start=4138,
-  serialized_end=4274,
+  serialized_start=4055,
+  serialized_end=4191,
 )
 _sym_db.RegisterEnumDescriptor(_SERVICESTATUSENUM)
 
@@ -235,8 +233,8 @@ _CONFIGACTIONENUM = _descriptor.EnumDescriptor(
   ],
   containing_type=None,
   serialized_options=None,
-  serialized_start=4276,
-  serialized_end=4369,
+  serialized_start=4193,
+  serialized_end=4286,
 )
 _sym_db.RegisterEnumDescriptor(_CONFIGACTIONENUM)
 
@@ -288,8 +286,8 @@ _EMPTY = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=50,
-  serialized_end=57,
+  serialized_start=26,
+  serialized_end=33,
 )
 
 
@@ -320,8 +318,8 @@ _UUID = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=59,
-  serialized_end=79,
+  serialized_start=35,
+  serialized_end=55,
 )
 
 
@@ -359,8 +357,8 @@ _EVENT = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=81,
-  serialized_end=151,
+  serialized_start=57,
+  serialized_end=127,
 )
 
 
@@ -391,8 +389,8 @@ _CONTEXTID = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=153,
-  serialized_end=201,
+  serialized_start=129,
+  serialized_end=177,
 )
 
 
@@ -444,8 +442,8 @@ _CONTEXT = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=204,
-  serialized_end=386,
+  serialized_start=180,
+  serialized_end=362,
 )
 
 
@@ -476,8 +474,8 @@ _CONTEXTIDLIST = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=388,
-  serialized_end=444,
+  serialized_start=364,
+  serialized_end=420,
 )
 
 
@@ -508,8 +506,8 @@ _CONTEXTLIST = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=446,
-  serialized_end=495,
+  serialized_start=422,
+  serialized_end=471,
 )
 
 
@@ -547,8 +545,8 @@ _CONTEXTEVENT = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=497,
-  serialized_end=582,
+  serialized_start=473,
+  serialized_end=558,
 )
 
 
@@ -586,8 +584,8 @@ _TOPOLOGYID = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=584,
-  serialized_end=674,
+  serialized_start=560,
+  serialized_end=650,
 )
 
 
@@ -632,8 +630,8 @@ _TOPOLOGY = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=676,
-  serialized_end=802,
+  serialized_start=652,
+  serialized_end=778,
 )
 
 
@@ -664,8 +662,8 @@ _TOPOLOGYIDLIST = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=804,
-  serialized_end=863,
+  serialized_start=780,
+  serialized_end=839,
 )
 
 
@@ -696,8 +694,8 @@ _TOPOLOGYLIST = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=865,
-  serialized_end=918,
+  serialized_start=841,
+  serialized_end=894,
 )
 
 
@@ -735,8 +733,8 @@ _TOPOLOGYEVENT = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=920,
-  serialized_end=1008,
+  serialized_start=896,
+  serialized_end=984,
 )
 
 
@@ -767,8 +765,8 @@ _DEVICEID = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=1010,
-  serialized_end=1056,
+  serialized_start=986,
+  serialized_end=1032,
 )
 
 
@@ -834,8 +832,8 @@ _DEVICE = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=1059,
-  serialized_end=1341,
+  serialized_start=1035,
+  serialized_end=1317,
 )
 
 
@@ -866,8 +864,8 @@ _DEVICECONFIG = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=1343,
-  serialized_end=1400,
+  serialized_start=1319,
+  serialized_end=1376,
 )
 
 
@@ -898,8 +896,8 @@ _DEVICEIDLIST = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=1402,
-  serialized_end=1455,
+  serialized_start=1378,
+  serialized_end=1431,
 )
 
 
@@ -930,8 +928,8 @@ _DEVICELIST = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=1457,
-  serialized_end=1503,
+  serialized_start=1433,
+  serialized_end=1479,
 )
 
 
@@ -969,8 +967,8 @@ _DEVICEEVENT = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=1505,
-  serialized_end=1587,
+  serialized_start=1481,
+  serialized_end=1563,
 )
 
 
@@ -1001,8 +999,8 @@ _LINKID = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=1589,
-  serialized_end=1631,
+  serialized_start=1565,
+  serialized_end=1607,
 )
 
 
@@ -1040,8 +1038,8 @@ _LINK = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=1633,
-  serialized_end=1721,
+  serialized_start=1609,
+  serialized_end=1697,
 )
 
 
@@ -1072,8 +1070,8 @@ _LINKIDLIST = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=1723,
-  serialized_end=1770,
+  serialized_start=1699,
+  serialized_end=1746,
 )
 
 
@@ -1104,8 +1102,8 @@ _LINKLIST = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=1772,
-  serialized_end=1812,
+  serialized_start=1748,
+  serialized_end=1788,
 )
 
 
@@ -1143,8 +1141,8 @@ _LINKEVENT = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=1814,
-  serialized_end=1890,
+  serialized_start=1790,
+  serialized_end=1866,
 )
 
 
@@ -1182,8 +1180,8 @@ _SERVICEID = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=1892,
-  serialized_end=1980,
+  serialized_start=1868,
+  serialized_end=1956,
 )
 
 
@@ -1249,8 +1247,8 @@ _SERVICE = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=1983,
-  serialized_end=2277,
+  serialized_start=1959,
+  serialized_end=2253,
 )
 
 
@@ -1281,8 +1279,8 @@ _SERVICESTATUS = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=2279,
-  serialized_end=2346,
+  serialized_start=2255,
+  serialized_end=2322,
 )
 
 
@@ -1313,8 +1311,8 @@ _SERVICECONFIG = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=2348,
-  serialized_end=2406,
+  serialized_start=2324,
+  serialized_end=2382,
 )
 
 
@@ -1345,8 +1343,8 @@ _SERVICEIDLIST = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=2408,
-  serialized_end=2464,
+  serialized_start=2384,
+  serialized_end=2440,
 )
 
 
@@ -1377,8 +1375,8 @@ _SERVICELIST = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=2466,
-  serialized_end=2515,
+  serialized_start=2442,
+  serialized_end=2491,
 )
 
 
@@ -1416,8 +1414,8 @@ _SERVICEEVENT = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=2517,
-  serialized_end=2602,
+  serialized_start=2493,
+  serialized_end=2578,
 )
 
 
@@ -1462,8 +1460,8 @@ _ENDPOINTID = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=2605,
-  serialized_end=2735,
+  serialized_start=2581,
+  serialized_end=2711,
 )
 
 
@@ -1501,8 +1499,8 @@ _ENDPOINT = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=2737,
-  serialized_end=2812,
+  serialized_start=2713,
+  serialized_end=2788,
 )
 
 
@@ -1535,13 +1533,6 @@ _CONFIGRULE = _descriptor.Descriptor(
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='kpi_sample_type', full_name='context.ConfigRule.kpi_sample_type', index=3,
-      number=4, type=14, cpp_type=8, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
   ],
   extensions=[
   ],
@@ -1554,8 +1545,8 @@ _CONFIGRULE = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=2815,
-  serialized_end=2974,
+  serialized_start=2790,
+  serialized_end=2891,
 )
 
 
@@ -1593,8 +1584,8 @@ _CONSTRAINT = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=2976,
-  serialized_end=3039,
+  serialized_start=2893,
+  serialized_end=2956,
 )
 
 
@@ -1625,8 +1616,8 @@ _CONNECTIONID = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=3041,
-  serialized_end=3095,
+  serialized_start=2958,
+  serialized_end=3012,
 )
 
 
@@ -1671,8 +1662,8 @@ _CONNECTION = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=3098,
-  serialized_end=3239,
+  serialized_start=3015,
+  serialized_end=3156,
 )
 
 
@@ -1703,8 +1694,8 @@ _CONNECTIONIDLIST = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=3241,
-  serialized_end=3306,
+  serialized_start=3158,
+  serialized_end=3223,
 )
 
 
@@ -1735,8 +1726,8 @@ _CONNECTIONLIST = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=3308,
-  serialized_end=3366,
+  serialized_start=3225,
+  serialized_end=3283,
 )
 
 
@@ -1781,8 +1772,8 @@ _TERAFLOWCONTROLLER = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=3368,
-  serialized_end=3462,
+  serialized_start=3285,
+  serialized_end=3379,
 )
 
 
@@ -1820,8 +1811,8 @@ _AUTHENTICATIONRESULT = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=3464,
-  serialized_end=3549,
+  serialized_start=3381,
+  serialized_end=3466,
 )
 
 _EVENT.fields_by_name['event_type'].enum_type = _EVENTTYPEENUM
@@ -1880,7 +1871,6 @@ _ENDPOINTID.fields_by_name['device_id'].message_type = _DEVICEID
 _ENDPOINTID.fields_by_name['endpoint_uuid'].message_type = _UUID
 _ENDPOINT.fields_by_name['endpoint_id'].message_type = _ENDPOINTID
 _CONFIGRULE.fields_by_name['action'].enum_type = _CONFIGACTIONENUM
-_CONFIGRULE.fields_by_name['kpi_sample_type'].enum_type = kpi__sample__types__pb2._KPISAMPLETYPE
 _CONNECTIONID.fields_by_name['connection_uuid'].message_type = _UUID
 _CONNECTION.fields_by_name['connection_id'].message_type = _CONNECTIONID
 _CONNECTION.fields_by_name['related_service_id'].message_type = _SERVICEID
@@ -2234,8 +2224,8 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor(
   index=0,
   serialized_options=None,
   create_key=_descriptor._internal_create_key,
-  serialized_start=4372,
-  serialized_end=6073,
+  serialized_start=4289,
+  serialized_end=5990,
   methods=[
   _descriptor.MethodDescriptor(
     name='ListContextIds',
diff --git a/src/device/proto/device_pb2.py b/src/device/proto/device_pb2.py
index 4d4dbb82567256dd79595884f0ed9c2f13498d31..e351738e6ac1ea9dadf4310897a979ab38db669b 100644
--- a/src/device/proto/device_pb2.py
+++ b/src/device/proto/device_pb2.py
@@ -12,7 +12,6 @@ _sym_db = _symbol_database.Default()
 
 
 from . import context_pb2 as context__pb2
-from . import monitoring_pb2 as monitoring__pb2
 
 
 DESCRIPTOR = _descriptor.FileDescriptor(
@@ -21,77 +20,14 @@ DESCRIPTOR = _descriptor.FileDescriptor(
   syntax='proto3',
   serialized_options=None,
   create_key=_descriptor._internal_create_key,
-  serialized_pb=b'\n\x0c\x64\x65vice.proto\x12\x06\x64\x65vice\x1a\rcontext.proto\x1a\x10monitoring.proto\"\xa4\x01\n\x12MonitoringSettings\x12!\n\x06kpi_id\x18\x01 \x01(\x0b\x32\x11.monitoring.KpiId\x12\x31\n\x0ekpi_descriptor\x18\x02 \x01(\x0b\x32\x19.monitoring.KpiDescriptor\x12\x1b\n\x13sampling_duration_s\x18\x03 \x01(\x02\x12\x1b\n\x13sampling_interval_s\x18\x04 \x01(\x02\x32\xb2\x02\n\rDeviceService\x12\x31\n\tAddDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x37\n\x0f\x43onfigureDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0c\x44\x65leteDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12>\n\x10GetInitialConfig\x12\x11.context.DeviceId\x1a\x15.context.DeviceConfig\"\x00\x12@\n\x10MonitorDeviceKpi\x12\x1a.device.MonitoringSettings\x1a\x0e.context.Empty\"\x00\x62\x06proto3'
+  serialized_pb=b'\n\x0c\x64\x65vice.proto\x12\x06\x64\x65vice\x1a\rcontext.proto2\xf0\x01\n\rDeviceService\x12\x31\n\tAddDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x37\n\x0f\x43onfigureDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0c\x44\x65leteDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12>\n\x10GetInitialConfig\x12\x11.context.DeviceId\x1a\x15.context.DeviceConfig\"\x00\x62\x06proto3'
   ,
-  dependencies=[context__pb2.DESCRIPTOR,monitoring__pb2.DESCRIPTOR,])
+  dependencies=[context__pb2.DESCRIPTOR,])
 
 
 
-
-_MONITORINGSETTINGS = _descriptor.Descriptor(
-  name='MonitoringSettings',
-  full_name='device.MonitoringSettings',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  create_key=_descriptor._internal_create_key,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='kpi_id', full_name='device.MonitoringSettings.kpi_id', index=0,
-      number=1, type=11, cpp_type=10, label=1,
-      has_default_value=False, default_value=None,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='kpi_descriptor', full_name='device.MonitoringSettings.kpi_descriptor', index=1,
-      number=2, type=11, cpp_type=10, label=1,
-      has_default_value=False, default_value=None,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='sampling_duration_s', full_name='device.MonitoringSettings.sampling_duration_s', index=2,
-      number=3, type=2, cpp_type=6, label=1,
-      has_default_value=False, default_value=float(0),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='sampling_interval_s', full_name='device.MonitoringSettings.sampling_interval_s', index=3,
-      number=4, type=2, cpp_type=6, label=1,
-      has_default_value=False, default_value=float(0),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto3',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=58,
-  serialized_end=222,
-)
-
-_MONITORINGSETTINGS.fields_by_name['kpi_id'].message_type = monitoring__pb2._KPIID
-_MONITORINGSETTINGS.fields_by_name['kpi_descriptor'].message_type = monitoring__pb2._KPIDESCRIPTOR
-DESCRIPTOR.message_types_by_name['MonitoringSettings'] = _MONITORINGSETTINGS
 _sym_db.RegisterFileDescriptor(DESCRIPTOR)
 
-MonitoringSettings = _reflection.GeneratedProtocolMessageType('MonitoringSettings', (_message.Message,), {
-  'DESCRIPTOR' : _MONITORINGSETTINGS,
-  '__module__' : 'device_pb2'
-  # @@protoc_insertion_point(class_scope:device.MonitoringSettings)
-  })
-_sym_db.RegisterMessage(MonitoringSettings)
-
 
 
 _DEVICESERVICE = _descriptor.ServiceDescriptor(
@@ -101,8 +37,8 @@ _DEVICESERVICE = _descriptor.ServiceDescriptor(
   index=0,
   serialized_options=None,
   create_key=_descriptor._internal_create_key,
-  serialized_start=225,
-  serialized_end=531,
+  serialized_start=40,
+  serialized_end=280,
   methods=[
   _descriptor.MethodDescriptor(
     name='AddDevice',
@@ -144,16 +80,6 @@ _DEVICESERVICE = _descriptor.ServiceDescriptor(
     serialized_options=None,
     create_key=_descriptor._internal_create_key,
   ),
-  _descriptor.MethodDescriptor(
-    name='MonitorDeviceKpi',
-    full_name='device.DeviceService.MonitorDeviceKpi',
-    index=4,
-    containing_service=None,
-    input_type=_MONITORINGSETTINGS,
-    output_type=context__pb2._EMPTY,
-    serialized_options=None,
-    create_key=_descriptor._internal_create_key,
-  ),
 ])
 _sym_db.RegisterServiceDescriptor(_DEVICESERVICE)
 
diff --git a/src/device/proto/device_pb2_grpc.py b/src/device/proto/device_pb2_grpc.py
index 2b9bfc47da3b33b632ff46a8454496a499305a6c..453aa2fcbc9296cf25298c2041433dfbb06b8e28 100644
--- a/src/device/proto/device_pb2_grpc.py
+++ b/src/device/proto/device_pb2_grpc.py
@@ -3,7 +3,6 @@
 import grpc
 
 from . import context_pb2 as context__pb2
-from . import device_pb2 as device__pb2
 
 
 class DeviceServiceStub(object):
@@ -35,11 +34,6 @@ class DeviceServiceStub(object):
                 request_serializer=context__pb2.DeviceId.SerializeToString,
                 response_deserializer=context__pb2.DeviceConfig.FromString,
                 )
-        self.MonitorDeviceKpi = channel.unary_unary(
-                '/device.DeviceService/MonitorDeviceKpi',
-                request_serializer=device__pb2.MonitoringSettings.SerializeToString,
-                response_deserializer=context__pb2.Empty.FromString,
-                )
 
 
 class DeviceServiceServicer(object):
@@ -69,12 +63,6 @@ class DeviceServiceServicer(object):
         context.set_details('Method not implemented!')
         raise NotImplementedError('Method not implemented!')
 
-    def MonitorDeviceKpi(self, request, context):
-        """Missing associated documentation comment in .proto file."""
-        context.set_code(grpc.StatusCode.UNIMPLEMENTED)
-        context.set_details('Method not implemented!')
-        raise NotImplementedError('Method not implemented!')
-
 
 def add_DeviceServiceServicer_to_server(servicer, server):
     rpc_method_handlers = {
@@ -98,11 +86,6 @@ def add_DeviceServiceServicer_to_server(servicer, server):
                     request_deserializer=context__pb2.DeviceId.FromString,
                     response_serializer=context__pb2.DeviceConfig.SerializeToString,
             ),
-            'MonitorDeviceKpi': grpc.unary_unary_rpc_method_handler(
-                    servicer.MonitorDeviceKpi,
-                    request_deserializer=device__pb2.MonitoringSettings.FromString,
-                    response_serializer=context__pb2.Empty.SerializeToString,
-            ),
     }
     generic_handler = grpc.method_handlers_generic_handler(
             'device.DeviceService', rpc_method_handlers)
@@ -180,20 +163,3 @@ class DeviceService(object):
             context__pb2.DeviceConfig.FromString,
             options, channel_credentials,
             insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
-
-    @staticmethod
-    def MonitorDeviceKpi(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, '/device.DeviceService/MonitorDeviceKpi',
-            device__pb2.MonitoringSettings.SerializeToString,
-            context__pb2.Empty.FromString,
-            options, channel_credentials,
-            insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
diff --git a/src/device/proto/kpi_sample_types_pb2.py b/src/device/proto/kpi_sample_types_pb2.py
index 31fbaa216bca629a4de4272091c490982c1aa166..ad22554ec352d0aeae644fdce00c0f28996ed73b 100644
--- a/src/device/proto/kpi_sample_types_pb2.py
+++ b/src/device/proto/kpi_sample_types_pb2.py
@@ -2,7 +2,6 @@
 # Generated by the protocol buffer compiler.  DO NOT EDIT!
 # source: kpi_sample_types.proto
 """Generated protocol buffer code."""
-from google.protobuf.internal import enum_type_wrapper
 from google.protobuf import descriptor as _descriptor
 from google.protobuf import message as _message
 from google.protobuf import reflection as _reflection
@@ -16,62 +15,15 @@ _sym_db = _symbol_database.Default()
 
 DESCRIPTOR = _descriptor.FileDescriptor(
   name='kpi_sample_types.proto',
-  package='kpi_sample_types',
+  package='',
   syntax='proto3',
   serialized_options=None,
   create_key=_descriptor._internal_create_key,
-  serialized_pb=b'\n\x16kpi_sample_types.proto\x12\x10kpi_sample_types*x\n\rKpiSampleType\x12\x0b\n\x07UNKNOWN\x10\x00\x12\x17\n\x13PACKETS_TRANSMITTED\x10\x65\x12\x14\n\x10PACKETS_RECEIVED\x10\x66\x12\x16\n\x11\x42YTES_TRANSMITTED\x10\xc9\x01\x12\x13\n\x0e\x42YTES_RECEIVED\x10\xca\x01\x62\x06proto3'
+  serialized_pb=b'\n\x16kpi_sample_types.protob\x06proto3'
 )
 
-_KPISAMPLETYPE = _descriptor.EnumDescriptor(
-  name='KpiSampleType',
-  full_name='kpi_sample_types.KpiSampleType',
-  filename=None,
-  file=DESCRIPTOR,
-  create_key=_descriptor._internal_create_key,
-  values=[
-    _descriptor.EnumValueDescriptor(
-      name='UNKNOWN', index=0, number=0,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='PACKETS_TRANSMITTED', index=1, number=101,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='PACKETS_RECEIVED', index=2, number=102,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='BYTES_TRANSMITTED', index=3, number=201,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='BYTES_RECEIVED', index=4, number=202,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-  ],
-  containing_type=None,
-  serialized_options=None,
-  serialized_start=44,
-  serialized_end=164,
-)
-_sym_db.RegisterEnumDescriptor(_KPISAMPLETYPE)
-
-KpiSampleType = enum_type_wrapper.EnumTypeWrapper(_KPISAMPLETYPE)
-UNKNOWN = 0
-PACKETS_TRANSMITTED = 101
-PACKETS_RECEIVED = 102
-BYTES_TRANSMITTED = 201
-BYTES_RECEIVED = 202
 
 
-DESCRIPTOR.enum_types_by_name['KpiSampleType'] = _KPISAMPLETYPE
 _sym_db.RegisterFileDescriptor(DESCRIPTOR)
 
 
diff --git a/src/device/proto/monitoring_pb2.py b/src/device/proto/monitoring_pb2.py
index b313ebb68f0da37a540898e8c362fd204a799076..7368609d2145f94cc3b746836a5297333151c738 100644
--- a/src/device/proto/monitoring_pb2.py
+++ b/src/device/proto/monitoring_pb2.py
@@ -2,6 +2,7 @@
 # Generated by the protocol buffer compiler.  DO NOT EDIT!
 # source: monitoring.proto
 """Generated protocol buffer code."""
+from google.protobuf.internal import enum_type_wrapper
 from google.protobuf import descriptor as _descriptor
 from google.protobuf import message as _message
 from google.protobuf import reflection as _reflection
@@ -12,7 +13,6 @@ _sym_db = _symbol_database.Default()
 
 
 from . import context_pb2 as context__pb2
-from . import kpi_sample_types_pb2 as kpi__sample__types__pb2
 
 
 DESCRIPTOR = _descriptor.FileDescriptor(
@@ -21,56 +21,180 @@ DESCRIPTOR = _descriptor.FileDescriptor(
   syntax='proto3',
   serialized_options=None,
   create_key=_descriptor._internal_create_key,
-  serialized_pb=b'\n\x10monitoring.proto\x12\nmonitoring\x1a\rcontext.proto\x1a\x16kpi_sample_types.proto\"\xda\x01\n\rKpiDescriptor\x12\x17\n\x0fkpi_description\x18\x01 \x01(\t\x12\x38\n\x0fkpi_sample_type\x18\x02 \x01(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\x12$\n\tdevice_id\x18\x03 \x01(\x0b\x32\x11.context.DeviceId\x12(\n\x0b\x65ndpoint_id\x18\x04 \x01(\x0b\x32\x13.context.EndPointId\x12&\n\nservice_id\x18\x05 \x01(\x0b\x32\x12.context.ServiceId\"p\n\x11MonitorKpiRequest\x12!\n\x06kpi_id\x18\x01 \x01(\x0b\x32\x11.monitoring.KpiId\x12\x1b\n\x13sampling_duration_s\x18\x02 \x01(\x02\x12\x1b\n\x13sampling_interval_s\x18\x03 \x01(\x02\"&\n\x05KpiId\x12\x1d\n\x06kpi_id\x18\x01 \x01(\x0b\x32\r.context.Uuid\"d\n\x03Kpi\x12!\n\x06kpi_id\x18\x01 \x01(\x0b\x32\x11.monitoring.KpiId\x12\x11\n\ttimestamp\x18\x02 \x01(\t\x12\'\n\tkpi_value\x18\x04 \x01(\x0b\x32\x14.monitoring.KpiValue\"a\n\x08KpiValue\x12\x10\n\x06intVal\x18\x01 \x01(\rH\x00\x12\x12\n\x08\x66loatVal\x18\x02 \x01(\x02H\x00\x12\x13\n\tstringVal\x18\x03 \x01(\tH\x00\x12\x11\n\x07\x62oolVal\x18\x04 \x01(\x08H\x00\x42\x07\n\x05value\",\n\x07KpiList\x12!\n\x08kpi_list\x18\x01 \x03(\x0b\x32\x0f.monitoring.Kpi2\xf3\x02\n\x11MonitoringService\x12;\n\tCreateKpi\x12\x19.monitoring.KpiDescriptor\x1a\x11.monitoring.KpiId\"\x00\x12\x42\n\x10GetKpiDescriptor\x12\x11.monitoring.KpiId\x1a\x19.monitoring.KpiDescriptor\"\x00\x12/\n\nIncludeKpi\x12\x0f.monitoring.Kpi\x1a\x0e.context.Empty\"\x00\x12=\n\nMonitorKpi\x12\x1d.monitoring.MonitorKpiRequest\x1a\x0e.context.Empty\"\x00\x12\x36\n\x0cGetStreamKpi\x12\x11.monitoring.KpiId\x1a\x0f.monitoring.Kpi\"\x00\x30\x01\x12\x35\n\rGetInstantKpi\x12\x11.monitoring.KpiId\x1a\x0f.monitoring.Kpi\"\x00\x62\x06proto3'
+  serialized_pb=b'\n\x10monitoring.proto\x12\nmonitoring\x1a\rcontext.proto\"\x84\x01\n\x10\x43reateKpiRequest\x12\x16\n\x0ekpiDescription\x18\x01 \x01(\t\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12\x32\n\x0fkpi_sample_type\x18\x03 \x01(\x0e\x32\x19.monitoring.KpiSampleType\"h\n\x11MonitorKpiRequest\x12!\n\x06kpi_id\x18\x01 \x01(\x0b\x32\x11.monitoring.KpiId\x12\x18\n\x10\x63onnexion_time_s\x18\x02 \x01(\r\x12\x16\n\x0esample_rate_ms\x18\x03 \x01(\r\"i\n\x17MonitorDeviceKpiRequest\x12\x1c\n\x03kpi\x18\x01 \x01(\x0b\x32\x0f.monitoring.Kpi\x12\x18\n\x10\x63onnexion_time_s\x18\x02 \x01(\r\x12\x16\n\x0esample_rate_ms\x18\x03 \x01(\r\"s\n\x11IncludeKpiRequest\x12!\n\x06kpi_id\x18\x01 \x01(\x0b\x32\x11.monitoring.KpiId\x12\x12\n\ntime_stamp\x18\x02 \x01(\t\x12\'\n\tkpi_value\x18\x03 \x01(\x0b\x32\x14.monitoring.KpiValue\"&\n\x05KpiId\x12\x1d\n\x06kpi_id\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xd6\x01\n\x03Kpi\x12!\n\x06kpi_id\x18\x01 \x01(\x0b\x32\x11.monitoring.KpiId\x12\x11\n\ttimestamp\x18\x02 \x01(\t\x12\x16\n\x0ekpiDescription\x18\x03 \x01(\t\x12\'\n\tkpi_value\x18\x04 \x01(\x0b\x32\x14.monitoring.KpiValue\x12\x32\n\x0fkpi_sample_type\x18\x05 \x01(\x0e\x32\x19.monitoring.KpiSampleType\x12$\n\tdevice_id\x18\x06 \x01(\x0b\x32\x11.context.DeviceId\"a\n\x08KpiValue\x12\x10\n\x06intVal\x18\x01 \x01(\rH\x00\x12\x12\n\x08\x66loatVal\x18\x02 \x01(\x02H\x00\x12\x13\n\tstringVal\x18\x03 \x01(\tH\x00\x12\x11\n\x07\x62oolVal\x18\x04 \x01(\x08H\x00\x42\x07\n\x05value\"+\n\x07KpiList\x12 \n\x07kpiList\x18\x01 \x03(\x0b\x32\x0f.monitoring.Kpi*x\n\rKpiSampleType\x12\x0b\n\x07UNKNOWN\x10\x00\x12\x17\n\x13PACKETS_TRANSMITTED\x10\x65\x12\x14\n\x10PACKETS_RECEIVED\x10\x66\x12\x16\n\x11\x42YTES_TRANSMITTED\x10\xc9\x01\x12\x13\n\x0e\x42YTES_RECEIVED\x10\xca\x01\x32\x8b\x03\n\x11MonitoringService\x12>\n\tCreateKpi\x12\x1c.monitoring.CreateKpiRequest\x1a\x11.monitoring.KpiId\"\x00\x12=\n\nIncludeKpi\x12\x1d.monitoring.IncludeKpiRequest\x1a\x0e.context.Empty\"\x00\x12=\n\nMonitorKpi\x12\x1d.monitoring.MonitorKpiRequest\x1a\x0e.context.Empty\"\x00\x12I\n\x10MonitorDeviceKpi\x12#.monitoring.MonitorDeviceKpiRequest\x1a\x0e.context.Empty\"\x00\x12\x36\n\x0cGetStreamKpi\x12\x11.monitoring.KpiId\x1a\x0f.monitoring.Kpi\"\x00\x30\x01\x12\x35\n\rGetInstantKpi\x12\x11.monitoring.KpiId\x1a\x0f.monitoring.Kpi\"\x00\x62\x06proto3'
   ,
-  dependencies=[context__pb2.DESCRIPTOR,kpi__sample__types__pb2.DESCRIPTOR,])
+  dependencies=[context__pb2.DESCRIPTOR,])
 
+_KPISAMPLETYPE = _descriptor.EnumDescriptor(
+  name='KpiSampleType',
+  full_name='monitoring.KpiSampleType',
+  filename=None,
+  file=DESCRIPTOR,
+  create_key=_descriptor._internal_create_key,
+  values=[
+    _descriptor.EnumValueDescriptor(
+      name='UNKNOWN', index=0, number=0,
+      serialized_options=None,
+      type=None,
+      create_key=_descriptor._internal_create_key),
+    _descriptor.EnumValueDescriptor(
+      name='PACKETS_TRANSMITTED', index=1, number=101,
+      serialized_options=None,
+      type=None,
+      create_key=_descriptor._internal_create_key),
+    _descriptor.EnumValueDescriptor(
+      name='PACKETS_RECEIVED', index=2, number=102,
+      serialized_options=None,
+      type=None,
+      create_key=_descriptor._internal_create_key),
+    _descriptor.EnumValueDescriptor(
+      name='BYTES_TRANSMITTED', index=3, number=201,
+      serialized_options=None,
+      type=None,
+      create_key=_descriptor._internal_create_key),
+    _descriptor.EnumValueDescriptor(
+      name='BYTES_RECEIVED', index=4, number=202,
+      serialized_options=None,
+      type=None,
+      create_key=_descriptor._internal_create_key),
+  ],
+  containing_type=None,
+  serialized_options=None,
+  serialized_start=913,
+  serialized_end=1033,
+)
+_sym_db.RegisterEnumDescriptor(_KPISAMPLETYPE)
+
+KpiSampleType = enum_type_wrapper.EnumTypeWrapper(_KPISAMPLETYPE)
+UNKNOWN = 0
+PACKETS_TRANSMITTED = 101
+PACKETS_RECEIVED = 102
+BYTES_TRANSMITTED = 201
+BYTES_RECEIVED = 202
 
 
 
-_KPIDESCRIPTOR = _descriptor.Descriptor(
-  name='KpiDescriptor',
-  full_name='monitoring.KpiDescriptor',
+_CREATEKPIREQUEST = _descriptor.Descriptor(
+  name='CreateKpiRequest',
+  full_name='monitoring.CreateKpiRequest',
   filename=None,
   file=DESCRIPTOR,
   containing_type=None,
   create_key=_descriptor._internal_create_key,
   fields=[
     _descriptor.FieldDescriptor(
-      name='kpi_description', full_name='monitoring.KpiDescriptor.kpi_description', index=0,
+      name='kpiDescription', full_name='monitoring.CreateKpiRequest.kpiDescription', index=0,
       number=1, type=9, cpp_type=9, label=1,
       has_default_value=False, default_value=b"".decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
     _descriptor.FieldDescriptor(
-      name='kpi_sample_type', full_name='monitoring.KpiDescriptor.kpi_sample_type', index=1,
-      number=2, type=14, cpp_type=8, label=1,
+      name='device_id', full_name='monitoring.CreateKpiRequest.device_id', index=1,
+      number=2, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='kpi_sample_type', full_name='monitoring.CreateKpiRequest.kpi_sample_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,
       serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=48,
+  serialized_end=180,
+)
+
+
+_MONITORKPIREQUEST = _descriptor.Descriptor(
+  name='MonitorKpiRequest',
+  full_name='monitoring.MonitorKpiRequest',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  create_key=_descriptor._internal_create_key,
+  fields=[
     _descriptor.FieldDescriptor(
-      name='device_id', full_name='monitoring.KpiDescriptor.device_id', index=2,
-      number=3, type=11, cpp_type=10, label=1,
+      name='kpi_id', full_name='monitoring.MonitorKpiRequest.kpi_id', index=0,
+      number=1, type=11, cpp_type=10, label=1,
       has_default_value=False, default_value=None,
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
     _descriptor.FieldDescriptor(
-      name='endpoint_id', full_name='monitoring.KpiDescriptor.endpoint_id', index=3,
-      number=4, type=11, cpp_type=10, label=1,
-      has_default_value=False, default_value=None,
+      name='connexion_time_s', full_name='monitoring.MonitorKpiRequest.connexion_time_s', 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,
       serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
     _descriptor.FieldDescriptor(
-      name='service_id', full_name='monitoring.KpiDescriptor.service_id', index=4,
-      number=5, type=11, cpp_type=10, label=1,
+      name='sample_rate_ms', full_name='monitoring.MonitorKpiRequest.sample_rate_ms', index=2,
+      number=3, type=13, cpp_type=3, label=1,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=182,
+  serialized_end=286,
+)
+
+
+_MONITORDEVICEKPIREQUEST = _descriptor.Descriptor(
+  name='MonitorDeviceKpiRequest',
+  full_name='monitoring.MonitorDeviceKpiRequest',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  create_key=_descriptor._internal_create_key,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='kpi', full_name='monitoring.MonitorDeviceKpiRequest.kpi', index=0,
+      number=1, type=11, cpp_type=10, label=1,
       has_default_value=False, default_value=None,
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='connexion_time_s', full_name='monitoring.MonitorDeviceKpiRequest.connexion_time_s', 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,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='sample_rate_ms', full_name='monitoring.MonitorDeviceKpiRequest.sample_rate_ms', index=2,
+      number=3, type=13, cpp_type=3, label=1,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
   ],
   extensions=[
   ],
@@ -83,37 +207,37 @@ _KPIDESCRIPTOR = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=72,
-  serialized_end=290,
+  serialized_start=288,
+  serialized_end=393,
 )
 
 
-_MONITORKPIREQUEST = _descriptor.Descriptor(
-  name='MonitorKpiRequest',
-  full_name='monitoring.MonitorKpiRequest',
+_INCLUDEKPIREQUEST = _descriptor.Descriptor(
+  name='IncludeKpiRequest',
+  full_name='monitoring.IncludeKpiRequest',
   filename=None,
   file=DESCRIPTOR,
   containing_type=None,
   create_key=_descriptor._internal_create_key,
   fields=[
     _descriptor.FieldDescriptor(
-      name='kpi_id', full_name='monitoring.MonitorKpiRequest.kpi_id', index=0,
+      name='kpi_id', full_name='monitoring.IncludeKpiRequest.kpi_id', index=0,
       number=1, type=11, cpp_type=10, label=1,
       has_default_value=False, default_value=None,
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
     _descriptor.FieldDescriptor(
-      name='sampling_duration_s', full_name='monitoring.MonitorKpiRequest.sampling_duration_s', index=1,
-      number=2, type=2, cpp_type=6, label=1,
-      has_default_value=False, default_value=float(0),
+      name='time_stamp', full_name='monitoring.IncludeKpiRequest.time_stamp', index=1,
+      number=2, type=9, cpp_type=9, label=1,
+      has_default_value=False, default_value=b"".decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
     _descriptor.FieldDescriptor(
-      name='sampling_interval_s', full_name='monitoring.MonitorKpiRequest.sampling_interval_s', index=2,
-      number=3, type=2, cpp_type=6, label=1,
-      has_default_value=False, default_value=float(0),
+      name='kpi_value', full_name='monitoring.IncludeKpiRequest.kpi_value', index=2,
+      number=3, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
@@ -129,8 +253,8 @@ _MONITORKPIREQUEST = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=292,
-  serialized_end=404,
+  serialized_start=395,
+  serialized_end=510,
 )
 
 
@@ -161,8 +285,8 @@ _KPIID = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=406,
-  serialized_end=444,
+  serialized_start=512,
+  serialized_end=550,
 )
 
 
@@ -189,12 +313,33 @@ _KPI = _descriptor.Descriptor(
       is_extension=False, extension_scope=None,
       serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
     _descriptor.FieldDescriptor(
-      name='kpi_value', full_name='monitoring.Kpi.kpi_value', index=2,
+      name='kpiDescription', full_name='monitoring.Kpi.kpiDescription', index=2,
+      number=3, type=9, cpp_type=9, label=1,
+      has_default_value=False, default_value=b"".decode('utf-8'),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='kpi_value', full_name='monitoring.Kpi.kpi_value', index=3,
       number=4, type=11, cpp_type=10, label=1,
       has_default_value=False, default_value=None,
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='kpi_sample_type', full_name='monitoring.Kpi.kpi_sample_type', 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,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='device_id', full_name='monitoring.Kpi.device_id', index=5,
+      number=6, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
   ],
   extensions=[
   ],
@@ -207,8 +352,8 @@ _KPI = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=446,
-  serialized_end=546,
+  serialized_start=553,
+  serialized_end=767,
 )
 
 
@@ -265,8 +410,8 @@ _KPIVALUE = _descriptor.Descriptor(
       create_key=_descriptor._internal_create_key,
     fields=[]),
   ],
-  serialized_start=548,
-  serialized_end=645,
+  serialized_start=769,
+  serialized_end=866,
 )
 
 
@@ -279,7 +424,7 @@ _KPILIST = _descriptor.Descriptor(
   create_key=_descriptor._internal_create_key,
   fields=[
     _descriptor.FieldDescriptor(
-      name='kpi_list', full_name='monitoring.KpiList.kpi_list', index=0,
+      name='kpiList', full_name='monitoring.KpiList.kpiList', 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,
@@ -297,18 +442,21 @@ _KPILIST = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=647,
-  serialized_end=691,
+  serialized_start=868,
+  serialized_end=911,
 )
 
-_KPIDESCRIPTOR.fields_by_name['kpi_sample_type'].enum_type = kpi__sample__types__pb2._KPISAMPLETYPE
-_KPIDESCRIPTOR.fields_by_name['device_id'].message_type = context__pb2._DEVICEID
-_KPIDESCRIPTOR.fields_by_name['endpoint_id'].message_type = context__pb2._ENDPOINTID
-_KPIDESCRIPTOR.fields_by_name['service_id'].message_type = context__pb2._SERVICEID
+_CREATEKPIREQUEST.fields_by_name['device_id'].message_type = context__pb2._DEVICEID
+_CREATEKPIREQUEST.fields_by_name['kpi_sample_type'].enum_type = _KPISAMPLETYPE
 _MONITORKPIREQUEST.fields_by_name['kpi_id'].message_type = _KPIID
+_MONITORDEVICEKPIREQUEST.fields_by_name['kpi'].message_type = _KPI
+_INCLUDEKPIREQUEST.fields_by_name['kpi_id'].message_type = _KPIID
+_INCLUDEKPIREQUEST.fields_by_name['kpi_value'].message_type = _KPIVALUE
 _KPIID.fields_by_name['kpi_id'].message_type = context__pb2._UUID
 _KPI.fields_by_name['kpi_id'].message_type = _KPIID
 _KPI.fields_by_name['kpi_value'].message_type = _KPIVALUE
+_KPI.fields_by_name['kpi_sample_type'].enum_type = _KPISAMPLETYPE
+_KPI.fields_by_name['device_id'].message_type = context__pb2._DEVICEID
 _KPIVALUE.oneofs_by_name['value'].fields.append(
   _KPIVALUE.fields_by_name['intVal'])
 _KPIVALUE.fields_by_name['intVal'].containing_oneof = _KPIVALUE.oneofs_by_name['value']
@@ -321,21 +469,24 @@ _KPIVALUE.fields_by_name['stringVal'].containing_oneof = _KPIVALUE.oneofs_by_nam
 _KPIVALUE.oneofs_by_name['value'].fields.append(
   _KPIVALUE.fields_by_name['boolVal'])
 _KPIVALUE.fields_by_name['boolVal'].containing_oneof = _KPIVALUE.oneofs_by_name['value']
-_KPILIST.fields_by_name['kpi_list'].message_type = _KPI
-DESCRIPTOR.message_types_by_name['KpiDescriptor'] = _KPIDESCRIPTOR
+_KPILIST.fields_by_name['kpiList'].message_type = _KPI
+DESCRIPTOR.message_types_by_name['CreateKpiRequest'] = _CREATEKPIREQUEST
 DESCRIPTOR.message_types_by_name['MonitorKpiRequest'] = _MONITORKPIREQUEST
+DESCRIPTOR.message_types_by_name['MonitorDeviceKpiRequest'] = _MONITORDEVICEKPIREQUEST
+DESCRIPTOR.message_types_by_name['IncludeKpiRequest'] = _INCLUDEKPIREQUEST
 DESCRIPTOR.message_types_by_name['KpiId'] = _KPIID
 DESCRIPTOR.message_types_by_name['Kpi'] = _KPI
 DESCRIPTOR.message_types_by_name['KpiValue'] = _KPIVALUE
 DESCRIPTOR.message_types_by_name['KpiList'] = _KPILIST
+DESCRIPTOR.enum_types_by_name['KpiSampleType'] = _KPISAMPLETYPE
 _sym_db.RegisterFileDescriptor(DESCRIPTOR)
 
-KpiDescriptor = _reflection.GeneratedProtocolMessageType('KpiDescriptor', (_message.Message,), {
-  'DESCRIPTOR' : _KPIDESCRIPTOR,
+CreateKpiRequest = _reflection.GeneratedProtocolMessageType('CreateKpiRequest', (_message.Message,), {
+  'DESCRIPTOR' : _CREATEKPIREQUEST,
   '__module__' : 'monitoring_pb2'
-  # @@protoc_insertion_point(class_scope:monitoring.KpiDescriptor)
+  # @@protoc_insertion_point(class_scope:monitoring.CreateKpiRequest)
   })
-_sym_db.RegisterMessage(KpiDescriptor)
+_sym_db.RegisterMessage(CreateKpiRequest)
 
 MonitorKpiRequest = _reflection.GeneratedProtocolMessageType('MonitorKpiRequest', (_message.Message,), {
   'DESCRIPTOR' : _MONITORKPIREQUEST,
@@ -344,6 +495,20 @@ MonitorKpiRequest = _reflection.GeneratedProtocolMessageType('MonitorKpiRequest'
   })
 _sym_db.RegisterMessage(MonitorKpiRequest)
 
+MonitorDeviceKpiRequest = _reflection.GeneratedProtocolMessageType('MonitorDeviceKpiRequest', (_message.Message,), {
+  'DESCRIPTOR' : _MONITORDEVICEKPIREQUEST,
+  '__module__' : 'monitoring_pb2'
+  # @@protoc_insertion_point(class_scope:monitoring.MonitorDeviceKpiRequest)
+  })
+_sym_db.RegisterMessage(MonitorDeviceKpiRequest)
+
+IncludeKpiRequest = _reflection.GeneratedProtocolMessageType('IncludeKpiRequest', (_message.Message,), {
+  'DESCRIPTOR' : _INCLUDEKPIREQUEST,
+  '__module__' : 'monitoring_pb2'
+  # @@protoc_insertion_point(class_scope:monitoring.IncludeKpiRequest)
+  })
+_sym_db.RegisterMessage(IncludeKpiRequest)
+
 KpiId = _reflection.GeneratedProtocolMessageType('KpiId', (_message.Message,), {
   'DESCRIPTOR' : _KPIID,
   '__module__' : 'monitoring_pb2'
@@ -381,45 +546,45 @@ _MONITORINGSERVICE = _descriptor.ServiceDescriptor(
   index=0,
   serialized_options=None,
   create_key=_descriptor._internal_create_key,
-  serialized_start=694,
-  serialized_end=1065,
+  serialized_start=1036,
+  serialized_end=1431,
   methods=[
   _descriptor.MethodDescriptor(
     name='CreateKpi',
     full_name='monitoring.MonitoringService.CreateKpi',
     index=0,
     containing_service=None,
-    input_type=_KPIDESCRIPTOR,
+    input_type=_CREATEKPIREQUEST,
     output_type=_KPIID,
     serialized_options=None,
     create_key=_descriptor._internal_create_key,
   ),
   _descriptor.MethodDescriptor(
-    name='GetKpiDescriptor',
-    full_name='monitoring.MonitoringService.GetKpiDescriptor',
+    name='IncludeKpi',
+    full_name='monitoring.MonitoringService.IncludeKpi',
     index=1,
     containing_service=None,
-    input_type=_KPIID,
-    output_type=_KPIDESCRIPTOR,
+    input_type=_INCLUDEKPIREQUEST,
+    output_type=context__pb2._EMPTY,
     serialized_options=None,
     create_key=_descriptor._internal_create_key,
   ),
   _descriptor.MethodDescriptor(
-    name='IncludeKpi',
-    full_name='monitoring.MonitoringService.IncludeKpi',
+    name='MonitorKpi',
+    full_name='monitoring.MonitoringService.MonitorKpi',
     index=2,
     containing_service=None,
-    input_type=_KPI,
+    input_type=_MONITORKPIREQUEST,
     output_type=context__pb2._EMPTY,
     serialized_options=None,
     create_key=_descriptor._internal_create_key,
   ),
   _descriptor.MethodDescriptor(
-    name='MonitorKpi',
-    full_name='monitoring.MonitoringService.MonitorKpi',
+    name='MonitorDeviceKpi',
+    full_name='monitoring.MonitoringService.MonitorDeviceKpi',
     index=3,
     containing_service=None,
-    input_type=_MONITORKPIREQUEST,
+    input_type=_MONITORDEVICEKPIREQUEST,
     output_type=context__pb2._EMPTY,
     serialized_options=None,
     create_key=_descriptor._internal_create_key,
diff --git a/src/device/requirements.in b/src/device/requirements.in
index 5a05d7efa6ef6964717635123756da0aead4ceff..5c38e92914207bf101ebc00b2cef453a3a85f82a 100644
--- a/src/device/requirements.in
+++ b/src/device/requirements.in
@@ -3,8 +3,12 @@ apscheduler
 fastcache
 grpcio-health-checking
 grpcio
+Jinja2
+netconf-client #1.7.3
 prometheus-client
 pytest
 pytest-benchmark
+python-json-logger
 pytz
 redis
+xmltodict
diff --git a/src/device/service/DeviceService.py b/src/device/service/DeviceService.py
index 29fdc97ce1ba5c8d7266ecdac340dce3e3670425..ae0d5c8396157d5398751587f5e7d808b0d8f484 100644
--- a/src/device/service/DeviceService.py
+++ b/src/device/service/DeviceService.py
@@ -1,20 +1,28 @@
-import grpc
-import logging
+import grpc, logging
 from concurrent import futures
 from grpc_health.v1.health import HealthServicer, OVERALL_HEALTH
 from grpc_health.v1.health_pb2 import HealthCheckResponse
 from grpc_health.v1.health_pb2_grpc import add_HealthServicer_to_server
-from device.proto.device_pb2_grpc import add_DeviceServiceServicer_to_server
-from device.service.DeviceServiceServicerImpl import DeviceServiceServicerImpl
+from context.client.ContextClient import ContextClient
 from device.Config import GRPC_SERVICE_PORT, GRPC_MAX_WORKERS, GRPC_GRACE_PERIOD
+from device.proto.device_pb2_grpc import add_DeviceServiceServicer_to_server
+from .DeviceServiceServicerImpl import DeviceServiceServicerImpl
+#from .MonitoringLoops import MonitoringLoops
+from .driver_api.DriverInstanceCache import DriverInstanceCache
 
 BIND_ADDRESS = '0.0.0.0'
 LOGGER = logging.getLogger(__name__)
 
 class DeviceService:
-    def __init__(self, database, address=BIND_ADDRESS, port=GRPC_SERVICE_PORT, max_workers=GRPC_MAX_WORKERS,
-                 grace_period=GRPC_GRACE_PERIOD):
-        self.database = database
+    def __init__(
+        self, context_client : ContextClient, driver_instance_cache : DriverInstanceCache,
+        #monitoring_loops : MonitoringLoops,
+        address=BIND_ADDRESS, port=GRPC_SERVICE_PORT, max_workers=GRPC_MAX_WORKERS,
+        grace_period=GRPC_GRACE_PERIOD):
+
+        self.context_client = context_client
+        self.driver_instance_cache = driver_instance_cache
+        #self.monitoring_loops = monitoring_loops
         self.address = address
         self.port = port
         self.endpoint = None
@@ -26,14 +34,17 @@ class DeviceService:
         self.server = None
 
     def start(self):
-        self.endpoint = '{}:{}'.format(self.address, self.port)
-        LOGGER.debug('Starting Service (tentative endpoint: {}, max_workers: {})...'.format(
-            self.endpoint, self.max_workers))
+        self.endpoint = '{:s}:{:s}'.format(str(self.address), str(self.port))
+        LOGGER.info('Starting Service (tentative endpoint: {:s}, max_workers: {:s})...'.format(
+            str(self.endpoint), str(self.max_workers)))
 
         self.pool = futures.ThreadPoolExecutor(max_workers=self.max_workers)
         self.server = grpc.server(self.pool) # , interceptors=(tracer_interceptor,))
 
-        self.device_servicer = DeviceServiceServicerImpl(self.database)
+        self.device_servicer = DeviceServiceServicerImpl(
+            self.context_client, self.driver_instance_cache,
+            #self.monitoring_loops
+        )
         add_DeviceServiceServicer_to_server(self.device_servicer, self.server)
 
         self.health_servicer = HealthServicer(
@@ -41,15 +52,15 @@ class DeviceService:
         add_HealthServicer_to_server(self.health_servicer, self.server)
 
         port = self.server.add_insecure_port(self.endpoint)
-        self.endpoint = '{}:{}'.format(self.address, port)
-        LOGGER.info('Listening on {}...'.format(self.endpoint))
+        self.endpoint = '{:s}:{:s}'.format(str(self.address), str(port))
+        LOGGER.info('Listening on {:s}...'.format(str(self.endpoint)))
         self.server.start()
         self.health_servicer.set(OVERALL_HEALTH, HealthCheckResponse.SERVING) # pylint: disable=maybe-no-member
 
         LOGGER.debug('Service started')
 
     def stop(self):
-        LOGGER.debug('Stopping service (grace period {} seconds)...'.format(self.grace_period))
+        LOGGER.debug('Stopping service (grace period {:s} seconds)...'.format(str(self.grace_period)))
         self.health_servicer.enter_graceful_shutdown()
         self.server.stop(self.grace_period)
         LOGGER.debug('Service stopped')
diff --git a/src/device/service/DeviceServiceServicerImpl.py b/src/device/service/DeviceServiceServicerImpl.py
index e07925aae31b7e2f275360e61fee3a8fe1347a2e..6c559fda37b7e4f8798be3747261e559a3eb0953 100644
--- a/src/device/service/DeviceServiceServicerImpl.py
+++ b/src/device/service/DeviceServiceServicerImpl.py
@@ -1,206 +1,290 @@
-import grpc, logging
-from prometheus_client import Counter, Histogram
-from common.database.api.context.Constants import DEFAULT_CONTEXT_ID, DEFAULT_TOPOLOGY_ID
-from common.database.api.Database import Database
-from common.database.api.context.topology.device.OperationalStatus import OperationalStatus
-from common.exceptions.ServiceException import ServiceException
-from device.proto.context_pb2 import DeviceId, Device, Empty
-from device.proto.device_pb2 import MonitoringSettings
+import grpc, json, logging
+from typing import Any, List, Tuple
+from google.protobuf.json_format import MessageToDict
+from common.orm.Database import Database
+from common.orm.Factory import get_database_backend
+from common.orm.HighLevel import get_object, update_or_create_object
+from common.orm.backend.BackendEnum import BackendEnum
+from common.orm.backend.Tools import key_to_str
+from common.rpc_method_wrapper.Decorator import create_metrics, safe_and_metered_rpc_method
+from common.rpc_method_wrapper.ServiceExceptions import InvalidArgumentException, OperationFailedException
+from context.client.ContextClient import ContextClient
+from device.proto.context_pb2 import ConfigActionEnum, Device, DeviceConfig, DeviceId, Empty
+#from device.proto.device_pb2 import MonitoringSettings
 from device.proto.device_pb2_grpc import DeviceServiceServicer
-from device.service.Tools import check_device_id_request, check_device_request
+#from .MonitoringLoops import MonitoringLoops
+from .database.ConfigModel import (
+    ConfigModel, ConfigRuleModel, ORM_ConfigActionEnum, get_config_rules, grpc_config_rules_to_raw, update_config)
+from .database.DatabaseTools import (
+    delete_device_from_context, get_device_driver_filter_fields, sync_device_from_context, sync_device_to_context,
+    update_device_in_local_database)
+from .database.DeviceModel import DeviceModel, DriverModel
+from .database.EndPointModel import EndPointModel
+#from .database.KpiModel import KpiModel
+#from .database.KpiSampleType import grpc_to_enum__kpi_sample_type
+from .driver_api._Driver import _Driver, RESOURCE_ENDPOINTS, RESOURCE_INTERFACES, RESOURCE_NETWORK_INSTANCES
+from .driver_api.DriverInstanceCache import DriverInstanceCache
+from .driver_api.Tools import (
+    check_delete_errors, check_set_errors,
+    #check_subscribe_errors, check_unsubscribe_errors
+)
 
 LOGGER = logging.getLogger(__name__)
 
-ADDDEVICE_COUNTER_STARTED    = Counter  ('device_adddevice_counter_started',
-                                          'Device:AddDevice counter of requests started'  )
-ADDDEVICE_COUNTER_COMPLETED  = Counter  ('device_adddevice_counter_completed',
-                                          'Device:AddDevice counter of requests completed')
-ADDDEVICE_COUNTER_FAILED     = Counter  ('device_adddevice_counter_failed',
-                                          'Device:AddDevice counter of requests failed'   )
-ADDDEVICE_HISTOGRAM_DURATION = Histogram('device_adddevice_histogram_duration',
-                                          'Device:AddDevice histogram of request duration')
-
-CONFIGUREDEVICE_COUNTER_STARTED    = Counter  ('device_configuredevice_counter_started',
-                                               'Device:ConfigureDevice counter of requests started'  )
-CONFIGUREDEVICE_COUNTER_COMPLETED  = Counter  ('device_configuredevice_counter_completed',
-                                               'Device:ConfigureDevice counter of requests completed')
-CONFIGUREDEVICE_COUNTER_FAILED     = Counter  ('device_configuredevice_counter_failed',
-                                               'Device:ConfigureDevice counter of requests failed'   )
-CONFIGUREDEVICE_HISTOGRAM_DURATION = Histogram('device_configuredevice_histogram_duration',
-                                               'Device:ConfigureDevice histogram of request duration')
-
-DELETEDEVICE_COUNTER_STARTED    = Counter  ('device_deletedevice_counter_started',
-                                            'Device:DeleteDevice counter of requests started'  )
-DELETEDEVICE_COUNTER_COMPLETED  = Counter  ('device_deletedevice_counter_completed',
-                                            'Device:DeleteDevice counter of requests completed')
-DELETEDEVICE_COUNTER_FAILED     = Counter  ('device_deletedevice_counter_failed',
-                                            'Device:DeleteDevice counter of requests failed'   )
-DELETEDEVICE_HISTOGRAM_DURATION = Histogram('device_deletedevice_histogram_duration',
-                                            'Device:DeleteDevice histogram of request duration')
-
-MONITORDEVICEKPI_COUNTER_STARTED    = Counter  ('device_monitordevicekpi_counter_started',
-                                            'Device:MonitorDeviceKpi counter of requests started'  )
-MONITORDEVICEKPI_COUNTER_COMPLETED  = Counter  ('device_monitordevicekpi_counter_completed',
-                                            'Device:MonitorDeviceKpi counter of requests completed')
-MONITORDEVICEKPI_COUNTER_FAILED     = Counter  ('device_monitordevicekpi_counter_failed',
-                                            'Device:MonitorDeviceKpi counter of requests failed'   )
-MONITORDEVICEKPI_HISTOGRAM_DURATION = Histogram('device_monitordevicekpi_histogram_duration',
-                                            'Device:MonitorDeviceKpi histogram of request duration')
-
+SERVICE_NAME = 'Device'
+METHOD_NAMES = ['AddDevice', 'ConfigureDevice', 'DeleteDevice', 'GetInitialConfig', 'MonitorDeviceKpi']
+METRICS = create_metrics(SERVICE_NAME, METHOD_NAMES)
 
 class DeviceServiceServicerImpl(DeviceServiceServicer):
-    def __init__(self, database : Database):
+    def __init__(
+        self, context_client : ContextClient, driver_instance_cache : DriverInstanceCache,
+        #monitoring_loops : MonitoringLoops
+        ):
+
         LOGGER.debug('Creating Servicer...')
-        self.database = database
+        self.context_client = context_client
+        self.database = Database(get_database_backend(backend=BackendEnum.INMEMORY))
+        self.driver_instance_cache = driver_instance_cache
+        #self.monitoring_loops = monitoring_loops
         LOGGER.debug('Servicer Created')
 
-    @ADDDEVICE_HISTOGRAM_DURATION.time()
-    def AddDevice(self, request : Device, grpc_context : grpc.ServicerContext) -> DeviceId:
-        ADDDEVICE_COUNTER_STARTED.inc()
-        try:
-            LOGGER.debug('AddDevice request: {}'.format(str(request)))
-
-            # ----- Validate request data and pre-conditions -----------------------------------------------------------
-            device_id, device_type, device_config, device_opstat, db_endpoints_ports = \
-                check_device_request('AddDevice', request, self.database, LOGGER)
-
-            # ----- Implement changes in the database ------------------------------------------------------------------
-            db_context = self.database.context(DEFAULT_CONTEXT_ID).create()
-            db_topology = db_context.topology(DEFAULT_TOPOLOGY_ID).create()
-            db_device = db_topology.device(device_id).create(device_type, device_config, device_opstat)
-            for db_endpoint,port_type in db_endpoints_ports:
-                db_endpoint.create(port_type)
-
-            # ----- Compose reply --------------------------------------------------------------------------------------
-            reply = DeviceId(**db_device.dump_id())
-            LOGGER.debug('AddDevice reply: {}'.format(str(reply)))
-            ADDDEVICE_COUNTER_COMPLETED.inc()
-            return reply
-        except ServiceException as e:
-            LOGGER.exception('AddDevice exception')
-            ADDDEVICE_COUNTER_FAILED.inc()
-            grpc_context.abort(e.code, e.details)
-        except Exception as e:                                      # pragma: no cover
-            LOGGER.exception('AddDevice exception')
-            ADDDEVICE_COUNTER_FAILED.inc()
-            grpc_context.abort(grpc.StatusCode.INTERNAL, str(e))
-
-    @CONFIGUREDEVICE_HISTOGRAM_DURATION.time()
-    def ConfigureDevice(self, request : Device, grpc_context : grpc.ServicerContext) -> DeviceId:
-        CONFIGUREDEVICE_COUNTER_STARTED.inc()
-        try:
-            LOGGER.debug('ConfigureDevice request: {}'.format(str(request)))
-
-            # ----- Validate request data and pre-conditions -----------------------------------------------------------
-            device_id, device_type, device_config, device_opstat, db_endpoints_ports = \
-                check_device_request('UpdateDevice', request, self.database, LOGGER)
-
-            # ----- Implement changes in the database ------------------------------------------------------------------
-            db_context = self.database.context(DEFAULT_CONTEXT_ID).create()
-            db_topology = db_context.topology(DEFAULT_TOPOLOGY_ID).create()
-            db_device = db_topology.device(device_id)
-
-            db_device_attributes = db_device.attributes.get(attributes=['device_type'])
-            # should not happen, device creation through Database API ensures all fields are always present
-            if len(db_device_attributes) == 0:                                                  # pragma: no cover
-                msg = 'Attribute device_type for Device({}) does not exist in the database.'    # pragma: no cover
-                msg = msg.format(device_id)                                                     # pragma: no cover
-                raise ServiceException(grpc.StatusCode.FAILED_PRECONDITION, msg)                # pragma: no cover
-
-            db_device_type = db_device_attributes.get('device_type')
-            # should not happen, device creation through Database API ensures all fields are always present
-            if len(db_device_type) == 0:                                                # pragma: no cover
-                msg = 'Attribute device_type for Device({}) is empty in the database.'  # pragma: no cover
-                msg = msg.format(device_id)                                             # pragma: no cover
-                raise ServiceException(grpc.StatusCode.FAILED_PRECONDITION, msg)        # pragma: no cover
-
-            if db_device_type != device_type:
-                msg = 'Device({}) has Type({}) in the database. Cannot be changed to Type({}).'
-                msg = msg.format(device_id, db_device_type, device_type)
-                raise ServiceException(grpc.StatusCode.INVALID_ARGUMENT, msg)
-
-            if len(db_endpoints_ports) > 0:
-                msg = 'Endpoints belonging to Device({}) cannot be modified.'
-                msg = msg.format(device_id)
-                raise ServiceException(grpc.StatusCode.INVALID_ARGUMENT, msg)
-
-            update_attributes = {}
-
-            if len(device_config) > 0:
-                update_attributes['device_config'] = device_config
-            
-            if device_opstat != OperationalStatus.KEEP_STATE:
-                update_attributes['device_operational_status'] = device_opstat
-
-            if len(update_attributes) == 0:
-                msg = ' '.join([
-                    'Any change has been requested for Device({}).',
-                    'Either specify a new configuration or a new device operational status.',
-                ])
-                msg = msg.format(device_id)
-                raise ServiceException(grpc.StatusCode.ABORTED, msg)
-
-            db_device.update(update_attributes=update_attributes)
-
-            # ----- Compose reply --------------------------------------------------------------------------------------
-            reply = DeviceId(**db_device.dump_id())
-            LOGGER.debug('ConfigureDevice reply: {}'.format(str(reply)))
-            CONFIGUREDEVICE_COUNTER_COMPLETED.inc()
-            return reply
-        except ServiceException as e:
-            LOGGER.exception('ConfigureDevice exception')
-            CONFIGUREDEVICE_COUNTER_FAILED.inc()
-            grpc_context.abort(e.code, e.details)
-        except Exception as e:                                      # pragma: no cover
-            LOGGER.exception('ConfigureDevice exception')
-            CONFIGUREDEVICE_COUNTER_FAILED.inc()
-            grpc_context.abort(grpc.StatusCode.INTERNAL, str(e))
-
-    @DELETEDEVICE_HISTOGRAM_DURATION.time()
-    def DeleteDevice(self, request : DeviceId, grpc_context : grpc.ServicerContext) -> Empty:
-        DELETEDEVICE_COUNTER_STARTED.inc()
-        try:
-            LOGGER.debug('DeleteDevice request: {}'.format(str(request)))
-
-            # ----- Validate request data and pre-conditions -----------------------------------------------------------
-            device_id = check_device_id_request('DeleteDevice', request, self.database, LOGGER)
-
-            # ----- Implement changes in the database ------------------------------------------------------------------
-            db_context = self.database.context(DEFAULT_CONTEXT_ID).create()
-            db_topology = db_context.topology(DEFAULT_TOPOLOGY_ID).create()
-            db_topology.device(device_id).delete()
-
-            # ----- Compose reply --------------------------------------------------------------------------------------
-            reply = Empty()
-            LOGGER.debug('DeleteDevice reply: {}'.format(str(reply)))
-            DELETEDEVICE_COUNTER_COMPLETED.inc()
-            return reply
-        except ServiceException as e:
-            LOGGER.exception('DeleteDevice exception')
-            DELETEDEVICE_COUNTER_FAILED.inc()
-            grpc_context.abort(e.code, e.details)
-        except Exception as e:                                      # pragma: no cover
-            LOGGER.exception('DeleteDevice exception')
-            DELETEDEVICE_COUNTER_FAILED.inc()
-            grpc_context.abort(grpc.StatusCode.INTERNAL, str(e))
-
-    @MONITORDEVICEKPI_HISTOGRAM_DURATION.time()
-    def MonitorDeviceKpi(self, request : MonitoringSettings, grpc_context : grpc.ServicerContext) -> Empty:
-        MONITORDEVICEKPI_COUNTER_STARTED.inc()
-        try:
-            LOGGER.debug('MonitorDeviceKpi request: {}'.format(str(request)))
-
-            # ---- Implement method ------------------------------------------------------------------------------------
-
-            reply = Empty()
-            LOGGER.debug('MonitorDeviceKpi reply: {}'.format(str(reply)))
-            MONITORDEVICEKPI_COUNTER_COMPLETED.inc()
-            return reply
-        except ServiceException as e:
-            LOGGER.exception('MonitorDeviceKpi exception')
-            MONITORDEVICEKPI_COUNTER_FAILED.inc()
-            grpc_context.abort(e.code, e.details)
-        except Exception as e:
-            LOGGER.exception('MonitorDeviceKpi exception')
-            MONITORDEVICEKPI_COUNTER_FAILED.inc()
-            grpc_context.abort(grpc.StatusCode.INTERNAL, str(e))
+    @safe_and_metered_rpc_method(METRICS, LOGGER)
+    def AddDevice(self, request : Device, context : grpc.ServicerContext) -> DeviceId:
+        device_id = request.device_id
+        device_uuid = device_id.device_uuid.uuid
+
+        connection_config_rules = {}
+        unexpected_config_rules = []
+        for config_rule in request.device_config.config_rules:
+            if (config_rule.action == ConfigActionEnum.CONFIGACTION_SET) and \
+               (config_rule.resource_key.startswith('_connect/')):
+                connection_config_rules[config_rule.resource_key.replace('_connect/', '')] = config_rule.resource_value
+            else:
+                unexpected_config_rules.append(config_rule)
+        if len(unexpected_config_rules) > 0:
+            unexpected_config_rules = MessageToDict(
+                request.device_config, including_default_value_fields=True,
+                preserving_proto_field_name=True, use_integers_for_enums=True)
+            unexpected_config_rules = unexpected_config_rules['config_rules']
+            unexpected_config_rules = list(filter(
+                lambda cr: cr['resource_key'].replace('_connect/', '') not in connection_config_rules,
+                unexpected_config_rules))
+            str_unexpected_config_rules = json.dumps(unexpected_config_rules, sort_keys=True)
+            raise InvalidArgumentException(
+                'device.device_config.config_rules', str_unexpected_config_rules,
+                extra_details='RPC method AddDevice only accepts connection Config Rules that should start '\
+                              'with "_connect/" tag. Others should be configured after adding the device.')
+
+        if len(request.device_endpoints) > 0:
+            unexpected_endpoints = MessageToDict(
+                request.device_endpoints, including_default_value_fields=True, preserving_proto_field_name=True,
+                use_integers_for_enums=True)
+            str_unexpected_endpoints = json.dumps(unexpected_endpoints, sort_keys=True)
+            raise InvalidArgumentException(
+                'device.device_endpoints', str_unexpected_endpoints,
+                extra_details='RPC method AddDevice does not accept endpoints. Endpoints are discovered through '\
+                              'interrogation of the physical device.')
+
+        # Remove device configuration
+        json_request = MessageToDict(
+            request, including_default_value_fields=True, preserving_proto_field_name=True,
+            use_integers_for_enums=True)
+        json_request['device_config'] = {}
+        request = Device(**json_request)
+
+        sync_device_from_context(device_uuid, self.context_client, self.database)
+        db_device,_ = update_device_in_local_database(self.database, request)
+
+        driver_filter_fields = get_device_driver_filter_fields(db_device)
+
+        address = connection_config_rules.pop('address', None)
+        port    = connection_config_rules.pop('port', None)
+        driver : _Driver = self.driver_instance_cache.get(
+            device_uuid, filter_fields=driver_filter_fields, address=address, port=port,
+            settings=connection_config_rules)
+        driver.Connect()
+
+        endpoints = driver.GetConfig([RESOURCE_ENDPOINTS])
+        for _, resource_value in endpoints:
+            endpoint_uuid = resource_value.get('name')
+            endpoint_type = resource_value.get('type')
+            str_endpoint_key = key_to_str([device_uuid, endpoint_uuid])
+            update_or_create_object(
+                self.database, EndPointModel, str_endpoint_key, {
+                'device_fk'    : db_device,
+                'endpoint_uuid': endpoint_uuid,
+                'endpoint_type': endpoint_type,
+            })
+
+        running_config_rules = driver.GetConfig([RESOURCE_INTERFACES, RESOURCE_NETWORK_INSTANCES])
+        running_config_rules = [
+            (ORM_ConfigActionEnum.SET, config_rule[0], json.dumps(config_rule[1], sort_keys=True))
+            for config_rule in running_config_rules
+        ]
+        #for running_config_rule in running_config_rules:
+        #    LOGGER.info('[AddDevice] running_config_rule: {:s}'.format(str(running_config_rule)))
+
+        update_config(self.database, device_uuid, 'running', running_config_rules)
+
+        initial_config_rules = driver.GetInitialConfig()
+        update_config(self.database, device_uuid, 'initial', initial_config_rules)
+
+        sync_device_to_context(db_device, self.context_client)
+        return DeviceId(**db_device.dump_id())
+
+    @safe_and_metered_rpc_method(METRICS, LOGGER)
+    def ConfigureDevice(self, request : Device, context : grpc.ServicerContext) -> DeviceId:
+        device_id = request.device_id
+        device_uuid = device_id.device_uuid.uuid
+
+        sync_device_from_context(device_uuid, self.context_client, self.database)
+
+        context_config_rules = get_config_rules(self.database, device_uuid, 'running')
+        context_config_rules = {config_rule[1]: config_rule[2] for config_rule in context_config_rules}
+        LOGGER.info('[ConfigureDevice] context_config_rules = {:s}'.format(str(context_config_rules)))
+
+        db_device,_ = update_device_in_local_database(self.database, request)
+
+        request_config_rules = grpc_config_rules_to_raw(request.device_config.config_rules)
+        LOGGER.info('[ConfigureDevice] request_config_rules = {:s}'.format(str(request_config_rules)))
+
+        resources_to_set         : List[Tuple[str, Any]]          = [] # key, value
+        resources_to_delete      : List[Tuple[str, Any]]          = [] # key, value
+
+        for config_rule in request_config_rules:
+            action, key, value = config_rule
+            if action == ORM_ConfigActionEnum.SET:
+                if (key not in context_config_rules) or (context_config_rules[key] != value):
+                    resources_to_set.append((key, value))
+            elif action == ORM_ConfigActionEnum.DELETE:
+                if key in context_config_rules:
+                    resources_to_delete.append((key, value))
+
+        LOGGER.info('[ConfigureDevice] resources_to_set = {:s}'.format(str(resources_to_set)))
+        LOGGER.info('[ConfigureDevice] resources_to_delete = {:s}'.format(str(resources_to_delete)))
+
+        # TODO: use of datastores (might be virtual ones) to enable rollbacks
+
+        errors = []
+
+        driver : _Driver = self.driver_instance_cache.get(device_uuid)
+        if driver is None:
+            errors.append('Device({:s}) has not been added to this Device instance'.format(str(device_uuid)))
+
+        if len(errors) == 0:
+            results_setconfig = driver.SetConfig(resources_to_set)
+            errors.extend(check_set_errors(resources_to_set, results_setconfig))
+
+        if len(errors) == 0:
+            results_deleteconfig = driver.DeleteConfig(resources_to_delete)
+            errors.extend(check_delete_errors(resources_to_delete, results_deleteconfig))
+
+        if len(errors) > 0:
+            raise OperationFailedException('ConfigureDevice', extra_details=errors)
+
+        sync_device_to_context(db_device, self.context_client)
+        return DeviceId(**db_device.dump_id())
+
+    @safe_and_metered_rpc_method(METRICS, LOGGER)
+    def DeleteDevice(self, request : DeviceId, context : grpc.ServicerContext) -> Empty:
+        device_uuid = request.device_uuid.uuid
+
+        sync_device_from_context(device_uuid, self.context_client, self.database)
+        db_device : DeviceModel = get_object(self.database, DeviceModel, device_uuid, raise_if_not_found=False)
+        if db_device is None: return Empty()
+
+        self.driver_instance_cache.delete(device_uuid)
+        delete_device_from_context(db_device, self.context_client)
+
+        for db_endpoint_pk,_ in db_device.references(EndPointModel):
+            EndPointModel(self.database, db_endpoint_pk).delete()
+
+        for db_driver_pk,_ in db_device.references(DriverModel):
+            DriverModel(self.database, db_driver_pk).delete()
+
+        db_initial_config = ConfigModel(self.database, db_device.device_initial_config_fk)
+        for db_config_rule_pk,_ in db_initial_config.references(ConfigRuleModel):
+            ConfigRuleModel(self.database, db_config_rule_pk).delete()
+
+        db_running_config = ConfigModel(self.database, db_device.device_running_config_fk)
+        for db_config_rule_pk,_ in db_running_config.references(ConfigRuleModel):
+            ConfigRuleModel(self.database, db_config_rule_pk).delete()
+
+        db_device.delete()
+        db_initial_config.delete()
+        db_running_config.delete()
+        return Empty()
+
+    @safe_and_metered_rpc_method(METRICS, LOGGER)
+    def GetInitialConfig(self, request : DeviceId, context : grpc.ServicerContext) -> DeviceConfig:
+        device_uuid = request.device_uuid.uuid
+
+        sync_device_from_context(device_uuid, self.context_client, self.database)
+        db_device : DeviceModel = get_object(self.database, DeviceModel, device_uuid, raise_if_not_found=False)
+
+        config_rules = {} if db_device is None else db_device.dump_initial_config()
+        return DeviceConfig(config_rules=config_rules)
+
+#    # Code under implemention and testing
+#    @safe_and_metered_rpc_method(METRICS, LOGGER)
+#    def MonitorDeviceKpi(self, request : MonitoringSettings, context : grpc.ServicerContext) -> Empty:
+#        kpi_uuid = request.kpi_id.kpi_id.uuid
+#
+#        device_uuid = request.kpi_descriptor.device_id.device_uuid.uuid
+#        db_device : DeviceModel = get_object(self.database, DeviceModel, device_uuid, raise_if_not_found=False)
+#
+#        endpoint_id = request.kpi_descriptor.endpoint_id
+#        endpoint_uuid = endpoint_id.endpoint_uuid.uuid
+#        endpoint_device_uuid = endpoint_id.device_id.device_uuid.uuid
+#        if len(endpoint_device_uuid) == 0: endpoint_device_uuid = device_uuid
+#        str_endpoint_key = key_to_str([device_uuid, endpoint_uuid])
+#        endpoint_topology_context_uuid = endpoint_id.topology_id.context_id.context_uuid.uuid
+#        endpoint_topology_uuid = endpoint_id.topology_id.topology_uuid.uuid
+#        if len(endpoint_topology_context_uuid) > 0 and len(endpoint_topology_uuid) > 0:
+#            str_topology_key = key_to_str([endpoint_topology_context_uuid, endpoint_topology_uuid])
+#            str_endpoint_key = key_to_str([str_endpoint_key, str_topology_key], separator=':')
+#        db_endpoint : EndPointModel = get_object(
+#            self.database, EndPointModel, str_endpoint_key, raise_if_not_found=False)
+#
+#        #db_kpi_prev = get_object(self.database, KpiModel, kpi_uuid, raise_if_not_found=False)
+#        result : Tuple[KpiModel, bool] = update_or_create_object(self.database, KpiModel, kpi_uuid, {
+#            'kpi_uuid'         : request.kpi_id.kpi_id.uuid,
+#            'kpi_description'  : request.kpi_descriptor.kpi_description,
+#            'kpi_sample_type'  : grpc_to_enum__kpi_sample_type(request.kpi_descriptor.kpi_sample_type),
+#            'device_fk'        : db_device,
+#            'endpoint_fk'      : db_endpoint,
+#            'sampling_duration': request.sampling_duration_s,
+#            'sampling_interval': request.sampling_interval_s,
+#        })
+#        db_kpi, updated = result
+#
+#        driver : _Driver = self.driver_instance_cache.get(device_uuid)
+#        if driver is None:
+#            msg = 'Device({:s}) has not been added to this Device instance'.format(str(device_uuid))
+#            raise OperationFailedException('ConfigureDevice', extra_details=msg)
+#
+#        sampling_resource = driver.GetResource(db_endpoint.endpoint_uuid)
+#
+#        #resources_to_subscribe   : List[Tuple[str, float, float]] = [] # key, sampling_duration, sampling_interval
+#        #resources_to_unsubscribe : List[Tuple[str, float, float]] = [] # key, sampling_duration, sampling_interval
+#        #LOGGER.info('[ConfigureDevice] resources_to_subscribe = {:s}'.format(str(resources_to_subscribe)))
+#        #LOGGER.info('[ConfigureDevice] resources_to_unsubscribe = {:s}'.format(str(resources_to_unsubscribe)))
+#        # TODO: Implement configuration of subscriptions
+#
+#        #if len(errors) == 0:
+#        #    results_subscribestate = driver.SubscribeState(resources_to_subscribe)
+#        #    errors.extend(check_subscribe_errors(resources_to_delete, results_subscribestate))
+#
+#        #if len(errors) == 0:
+#        #    results_unsubscribestate = driver.UnsubscribeState(resources_to_unsubscribe)
+#        #    errors.extend(check_unsubscribe_errors(resources_to_delete, results_unsubscribestate))
+#
+#        results = driver.SubscribeState([
+#            (sampling_resource, db_kpi.sampling_duration, db_kpi.sampling_interval),
+#        ])
+#        assert len(results) == 4
+#        for result in results: assert isinstance(result, bool) and result
+#
+#        self.monitoring_loops.add(device_uuid, driver)
+#
+#        return Empty()
diff --git a/src/device/service/MonitoringLoops.py b/src/device/service/MonitoringLoops.py
new file mode 100644
index 0000000000000000000000000000000000000000..658e1de0a99db161fabb88733182d3386b165cbd
--- /dev/null
+++ b/src/device/service/MonitoringLoops.py
@@ -0,0 +1,83 @@
+#import logging, queue, threading
+#from typing import Dict
+#from monitoring.client.monitoring_client import MonitoringClient
+#from monitoring.proto.monitoring_pb2 import Kpi
+#from .driver_api._Driver import _Driver
+#
+#LOGGER = logging.getLogger(__name__)
+#QUEUE_GET_WAIT_TIMEOUT = 0.5
+#
+#class MonitoringLoop:
+#    def __init__(self, driver : _Driver, samples_queue : queue.Queue) -> None:
+#        self._driver = driver
+#        self._samples_queue = samples_queue
+#        self._running = threading.Event()
+#        self._terminate = threading.Event()
+#        self._samples_stream = self._driver.GetState(blocking=True)
+#        self._collector_thread = threading.Thread(target=self._collect, daemon=False)
+#
+#    def _collect(self) -> None:
+#        for sample in self._samples_stream:
+#            if self._terminate.is_set(): break
+#            LOGGER.info('[MonitoringLoop:_collect] sample={:s}'.format(str(sample)))
+#            # TODO: add timestamp (if not present)
+#            self._samples_queue.put_nowait(sample)
+#
+#    def start(self):
+#        self._collector_thread.start()
+#        self._running.set()
+#
+#    @property
+#    def is_running(self): return self._running.is_set()
+#
+#    def stop(self):
+#        self._terminate.set()
+#        self._samples_stream.cancel()
+#        self._collector_thread.join()
+#
+#class MonitoringLoops:
+#    def __init__(self, monitoring_client : MonitoringClient) -> None:
+#        self._monitoring_client = monitoring_client
+#        self._samples_queue = queue.Queue()
+#        self._running = threading.Event()
+#        self._terminate = threading.Event()
+#        self._lock = threading.Lock()
+#        self._device_uuid__to__monitoring_loop : Dict[str, MonitoringLoop] = {}
+#        self._exporter_thread = threading.Thread(target=self._export, daemon=False)
+#
+#    def add(self, device_uuid : str, driver : _Driver) -> None:
+#        with self._lock:
+#            monitoring_loop = self._device_uuid__to__monitoring_loop.get(device_uuid)
+#            if (monitoring_loop is not None) and monitoring_loop.is_running: return
+#            monitoring_loop = MonitoringLoop(driver, self._samples_queue)
+#            self._device_uuid__to__monitoring_loop[device_uuid] = monitoring_loop
+#            monitoring_loop.start()
+#
+#    def remove(self, device_uuid : str) -> None:
+#        with self._lock:
+#            monitoring_loop = self._device_uuid__to__monitoring_loop.get(device_uuid)
+#            if monitoring_loop is None: return
+#            if monitoring_loop.is_running: monitoring_loop.stop()
+#            self._device_uuid__to__monitoring_loop.pop(device_uuid, None)
+#
+#    def start(self):
+#        self._exporter_thread.start()
+#        self._running.set()
+#
+#    @property
+#    def is_running(self): return self._running.is_set()
+#
+#    def stop(self):
+#        self._terminate.set()
+#        self._exporter_thread.join()
+#
+#    def _export(self) -> None:
+#        while not self._terminate.is_set():
+#            try:
+#                sample = self._samples_queue.get(block=True, timeout=QUEUE_GET_WAIT_TIMEOUT)
+#                LOGGER.info('[MonitoringLoops:_export] sample={:s}'.format(str(sample)))
+#            except queue.Empty:
+#                continue
+#            # TODO: find in database the KpiId, format KPI and send to Monitoring
+#            kpi_data = {}
+#            self._monitoring_client.IncludeKpi(Kpi(**kpi_data))
diff --git a/src/device/service/__main__.py b/src/device/service/__main__.py
index 6958d7e434f3f1765f209dfe63e4748cda4b7c1c..77572c51f9064712c2d9e4d9ccc9e943fe0df1c7 100644
--- a/src/device/service/__main__.py
+++ b/src/device/service/__main__.py
@@ -1,52 +1,83 @@
 import logging, signal, sys, threading
 from prometheus_client import start_http_server
 from common.Settings import get_setting
-from common.database.Factory import get_database
-from device.service.DeviceService import DeviceService
-from device.Config import GRPC_SERVICE_PORT, GRPC_MAX_WORKERS, GRPC_GRACE_PERIOD, LOG_LEVEL, METRICS_PORT
+from context.client.ContextClient import ContextClient
+from device.Config import (
+    CONTEXT_SERVICE_HOST, CONTEXT_SERVICE_PORT, GRPC_SERVICE_PORT, GRPC_MAX_WORKERS, GRPC_GRACE_PERIOD, LOG_LEVEL,
+    METRICS_PORT, MONITORING_SERVICE_HOST, MONITORING_SERVICE_PORT)
+#from monitoring.client.monitoring_client import MonitoringClient
+from .DeviceService import DeviceService
+#from .MonitoringLoops import MonitoringLoops
+from .driver_api.DriverFactory import DriverFactory
+from .driver_api.DriverInstanceCache import DriverInstanceCache
+from .drivers import DRIVERS
 
 terminate = threading.Event()
-logger = None
+LOGGER = None
 
-def signal_handler(signal, frame):
-    global terminate, logger
-    logger.warning('Terminate signal received')
+def signal_handler(signal, frame): # pylint: disable=redefined-outer-name
+    LOGGER.warning('Terminate signal received')
     terminate.set()
 
 def main():
-    global terminate, logger
+    global LOGGER # pylint: disable=global-statement
 
-    service_port = get_setting('DEVICESERVICE_SERVICE_PORT_GRPC', default=GRPC_SERVICE_PORT)
-    max_workers  = get_setting('MAX_WORKERS',                     default=GRPC_MAX_WORKERS )
-    grace_period = get_setting('GRACE_PERIOD',                    default=GRPC_GRACE_PERIOD)
-    log_level    = get_setting('LOG_LEVEL',                       default=LOG_LEVEL        )
-    metrics_port = get_setting('METRICS_PORT',                    default=METRICS_PORT     )
+    grpc_service_port       = get_setting('DEVICESERVICE_SERVICE_PORT_GRPC',     default=GRPC_SERVICE_PORT      )
+    max_workers             = get_setting('MAX_WORKERS',                         default=GRPC_MAX_WORKERS       )
+    grace_period            = get_setting('GRACE_PERIOD',                        default=GRPC_GRACE_PERIOD      )
+    log_level               = get_setting('LOG_LEVEL',                           default=LOG_LEVEL              )
+    metrics_port            = get_setting('METRICS_PORT',                        default=METRICS_PORT           )
+    context_service_host    = get_setting('CONTEXTSERVICE_SERVICE_HOST',         default=CONTEXT_SERVICE_HOST   )
+    context_service_port    = get_setting('CONTEXTSERVICE_SERVICE_PORT_GRPC',    default=CONTEXT_SERVICE_PORT   )
+    monitoring_service_host = get_setting('MONITORINGSERVICE_SERVICE_HOST',      default=MONITORING_SERVICE_HOST)
+    monitoring_service_port = get_setting('MONITORINGSERVICE_SERVICE_PORT_GRPC', default=MONITORING_SERVICE_PORT)
 
     logging.basicConfig(level=log_level)
-    logger = logging.getLogger(__name__)
+    LOGGER = logging.getLogger(__name__)
 
     signal.signal(signal.SIGINT,  signal_handler)
     signal.signal(signal.SIGTERM, signal_handler)
 
-    logger.info('Starting...')
+    LOGGER.info('Starting...')
 
     # Start metrics server
     start_http_server(metrics_port)
 
-    # Get database instance
-    database = get_database()
+    # Initialize Context Client
+    if context_service_host is None or context_service_port is None:
+        raise Exception('Wrong address({:s}):port({:s}) of Context component'.format(
+            str(context_service_host), str(context_service_port)))
+    context_client = ContextClient(context_service_host, context_service_port)
+
+    ## Initialize Monitoring Client
+    #if monitoring_service_host is None or monitoring_service_port is None:
+    #    raise Exception('Wrong address({:s}):port({:s}) of Monitoring component'.format(
+    #        str(monitoring_service_host), str(monitoring_service_port)))
+    #monitoring_client = MonitoringClient(monitoring_service_host, monitoring_service_port)
+
+    # Initialize Driver framework
+    driver_factory = DriverFactory(DRIVERS)
+    driver_instance_cache = DriverInstanceCache(driver_factory)
+    #monitoring_loops = MonitoringLoops(monitoring_client)
 
     # Starting device service
-    grpc_service = DeviceService(database, port=service_port, max_workers=max_workers, grace_period=grace_period)
+    grpc_service = DeviceService(
+        context_client, driver_instance_cache,
+        #monitoring_loops,
+        port=grpc_service_port, max_workers=max_workers,
+        grace_period=grace_period)
     grpc_service.start()
+    #monitoring_loops.start()
 
     # Wait for Ctrl+C or termination signal
     while not terminate.wait(timeout=0.1): pass
 
-    logger.info('Terminating...')
+    LOGGER.info('Terminating...')
+    #monitoring_loops.stop()
     grpc_service.stop()
+    driver_instance_cache.terminate()
 
-    logger.info('Bye')
+    LOGGER.info('Bye')
     return 0
 
 if __name__ == '__main__':
diff --git a/src/device/service/database/ConfigModel.py b/src/device/service/database/ConfigModel.py
new file mode 100644
index 0000000000000000000000000000000000000000..82697bfbae7d1f58fdc851aa66f86f0fc78a767e
--- /dev/null
+++ b/src/device/service/database/ConfigModel.py
@@ -0,0 +1,98 @@
+import functools, logging, operator
+from enum import Enum
+from typing import Dict, List, Tuple, Union
+from common.orm.Database import Database
+from common.orm.HighLevel import get_object, get_or_create_object, update_or_create_object
+from common.orm.backend.Tools import key_to_str
+from common.orm.fields.EnumeratedField import EnumeratedField
+from common.orm.fields.ForeignKeyField import ForeignKeyField
+from common.orm.fields.IntegerField import IntegerField
+from common.orm.fields.PrimaryKeyField import PrimaryKeyField
+from common.orm.fields.StringField import StringField
+from common.orm.model.Model import Model
+from device.proto.context_pb2 import ConfigActionEnum
+from .Tools import fast_hasher, grpc_to_enum, remove_dict_key
+
+LOGGER = logging.getLogger(__name__)
+
+class ORM_ConfigActionEnum(Enum):
+    UNDEFINED = ConfigActionEnum.CONFIGACTION_UNDEFINED
+    SET       = ConfigActionEnum.CONFIGACTION_SET
+    DELETE    = ConfigActionEnum.CONFIGACTION_DELETE
+
+grpc_to_enum__config_action = functools.partial(
+    grpc_to_enum, ConfigActionEnum, ORM_ConfigActionEnum)
+
+class ConfigModel(Model): # pylint: disable=abstract-method
+    pk = PrimaryKeyField()
+
+    def dump(self) -> List[Dict]:
+        db_config_rule_pks = self.references(ConfigRuleModel)
+        config_rules = [ConfigRuleModel(self.database, pk).dump(include_position=True) for pk,_ in db_config_rule_pks]
+        config_rules = sorted(config_rules, key=operator.itemgetter('position'))
+        return [remove_dict_key(config_rule, 'position') for config_rule in config_rules]
+
+class ConfigRuleModel(Model): # pylint: disable=abstract-method
+    pk = PrimaryKeyField()
+    config_fk = ForeignKeyField(ConfigModel)
+    position = IntegerField(min_value=0, required=True)
+    action = EnumeratedField(ORM_ConfigActionEnum, required=True)
+    key = StringField(required=True, allow_empty=False)
+    value = StringField(required=False, allow_empty=True)
+
+    def dump(self, include_position=True) -> Dict: # pylint: disable=arguments-differ
+        result = {
+            'action': self.action.value,
+            'resource_key': self.key,
+            'resource_value': self.value,
+        }
+        if include_position: result['position'] = self.position
+        return result
+
+def delete_all_config_rules(database : Database, db_parent_pk : str, config_name : str) -> None:
+    str_config_key = key_to_str([db_parent_pk, config_name], separator=':')
+    db_config : ConfigModel = get_object(database, ConfigModel, str_config_key, raise_if_not_found=False)
+    if db_config is None: return
+    db_config_rule_pks = db_config.references(ConfigRuleModel)
+    for pk,_ in db_config_rule_pks: ConfigRuleModel(database, pk).delete()
+
+def grpc_config_rules_to_raw(grpc_config_rules) -> List[Tuple[ORM_ConfigActionEnum, str, str]]:
+    def translate(grpc_config_rule):
+        action = grpc_to_enum__config_action(grpc_config_rule.action)
+        return action, grpc_config_rule.resource_key, grpc_config_rule.resource_value
+    return [translate(grpc_config_rule) for grpc_config_rule in grpc_config_rules]
+
+def get_config_rules(
+    database : Database, db_parent_pk : str, config_name : str
+    ) -> List[Tuple[ORM_ConfigActionEnum, str, str]]:
+
+    str_config_key = key_to_str([db_parent_pk, config_name], separator=':')
+    db_config = get_object(database, ConfigModel, str_config_key, raise_if_not_found=False)
+    return [] if db_config is None else [
+        (ORM_ConfigActionEnum._value2member_map_.get(config_rule['action']),
+            config_rule['resource_key'], config_rule['resource_value'])
+        for config_rule in db_config.dump()
+    ]
+
+def update_config(
+    database : Database, db_parent_pk : str, config_name : str,
+    raw_config_rules : List[Tuple[ORM_ConfigActionEnum, str, str]]
+    ) -> List[Tuple[Union[ConfigModel, ConfigRuleModel], bool]]:
+
+    str_config_key = key_to_str([db_parent_pk, config_name], separator=':')
+    result : Tuple[ConfigModel, bool] = get_or_create_object(database, ConfigModel, str_config_key)
+    db_config, created = result
+
+    db_objects : List[Tuple[Union[ConfigModel, ConfigRuleModel], bool]] = [(db_config, created)]
+
+    for position,(action, resource_key, resource_value) in enumerate(raw_config_rules):
+        str_rule_key_hash = fast_hasher(resource_key)
+        str_config_rule_key = key_to_str([db_config.pk, str_rule_key_hash], separator=':')
+        result : Tuple[ConfigRuleModel, bool] = update_or_create_object(
+            database, ConfigRuleModel, str_config_rule_key, {
+                'config_fk': db_config, 'position': position, 'action': action, 'key': resource_key,
+                'value': resource_value})
+        db_config_rule, updated = result
+        db_objects.append((db_config_rule, updated))
+
+    return db_objects
diff --git a/src/device/service/database/ContextModel.py b/src/device/service/database/ContextModel.py
new file mode 100644
index 0000000000000000000000000000000000000000..f4da5097e3f1ab0298ec66ff6007fb6a21be65ed
--- /dev/null
+++ b/src/device/service/database/ContextModel.py
@@ -0,0 +1,24 @@
+import logging
+from typing import Dict, List
+from common.orm.fields.PrimaryKeyField import PrimaryKeyField
+from common.orm.fields.StringField import StringField
+from common.orm.model.Model import Model
+
+LOGGER = logging.getLogger(__name__)
+
+class ContextModel(Model):
+    pk = PrimaryKeyField()
+    context_uuid = StringField(required=True, allow_empty=False)
+
+#    def dump_id(self) -> Dict:
+#        return {'context_uuid': {'uuid': self.context_uuid}}
+
+#    def dump_topology_ids(self) -> List[Dict]:
+#        from .TopologyModel import TopologyModel # pylint: disable=import-outside-toplevel
+#        db_topology_pks = self.references(TopologyModel)
+#        return [TopologyModel(self.database, pk).dump_id() for pk,_ in db_topology_pks]
+
+#    def dump(self, include_topologies=True) -> Dict: # pylint: disable=arguments-differ
+#        result = {'context_id': self.dump_id()}
+#        if include_topologies: result['topology_ids'] = self.dump_topology_ids()
+#        return result
diff --git a/src/device/service/database/DatabaseTools.py b/src/device/service/database/DatabaseTools.py
new file mode 100644
index 0000000000000000000000000000000000000000..5b43aae70af054f5d6da0bd92d9f2e59d152dc84
--- /dev/null
+++ b/src/device/service/database/DatabaseTools.py
@@ -0,0 +1,110 @@
+import grpc
+from typing import Any, Dict, Tuple
+from common.orm.Database import Database
+from common.orm.HighLevel import get_or_create_object, update_or_create_object
+from common.orm.backend.Tools import key_to_str
+from common.rpc_method_wrapper.ServiceExceptions import InvalidArgumentException
+from context.client.ContextClient import ContextClient
+from device.proto.context_pb2 import Device, DeviceId
+from device.service.driver_api.FilterFields import FilterFieldEnum
+from .ConfigModel import delete_all_config_rules, grpc_config_rules_to_raw, update_config
+from .ContextModel import ContextModel
+from .DeviceModel import DeviceModel, DriverModel, grpc_to_enum__device_operational_status, set_drivers
+from .EndPointModel import EndPointModel
+from .TopologyModel import TopologyModel
+
+def update_device_in_local_database(database : Database, device : Device) -> Tuple[DeviceModel, bool]:
+    device_uuid = device.device_id.device_uuid.uuid
+
+    for i,endpoint in enumerate(device.device_endpoints):
+        endpoint_device_uuid = endpoint.endpoint_id.device_id.device_uuid.uuid
+        if len(endpoint_device_uuid) == 0: endpoint_device_uuid = device_uuid
+        if device_uuid != endpoint_device_uuid:
+            raise InvalidArgumentException(
+                'request.device_endpoints[{:d}].device_id.device_uuid.uuid'.format(i), endpoint_device_uuid,
+                ['should be == {:s}({:s})'.format('request.device_id.device_uuid.uuid', device_uuid)])
+
+    initial_config_result = update_config(database, device_uuid, 'initial', [])
+
+    config_rules = grpc_config_rules_to_raw(device.device_config.config_rules)
+    delete_all_config_rules(database, device_uuid, 'running')
+    running_config_result = update_config(database, device_uuid, 'running', config_rules)
+
+    result : Tuple[DeviceModel, bool] = update_or_create_object(database, DeviceModel, device_uuid, {
+        'device_uuid'              : device_uuid,
+        'device_type'              : device.device_type,
+        'device_operational_status': grpc_to_enum__device_operational_status(device.device_operational_status),
+        'device_initial_config_fk' : initial_config_result[0][0],
+        'device_running_config_fk' : running_config_result[0][0],
+    })
+    db_device, updated = result
+    set_drivers(database, db_device, device.device_drivers)
+
+    for i,endpoint in enumerate(device.device_endpoints):
+        endpoint_uuid = endpoint.endpoint_id.endpoint_uuid.uuid
+        endpoint_device_uuid = endpoint.endpoint_id.device_id.device_uuid.uuid
+        if len(endpoint_device_uuid) == 0: endpoint_device_uuid = device_uuid
+
+        str_endpoint_key = key_to_str([device_uuid, endpoint_uuid])
+        endpoint_attributes = {
+            'device_fk'    : db_device,
+            'endpoint_uuid': endpoint_uuid,
+            'endpoint_type': endpoint.endpoint_type,
+        }
+
+        endpoint_topology_context_uuid = endpoint.endpoint_id.topology_id.context_id.context_uuid.uuid
+        endpoint_topology_uuid = endpoint.endpoint_id.topology_id.topology_uuid.uuid
+        if len(endpoint_topology_context_uuid) > 0 and len(endpoint_topology_uuid) > 0:
+            result : Tuple[ContextModel, bool] = get_or_create_object(
+                database, ContextModel, endpoint_topology_context_uuid, defaults={
+                    'context_uuid': endpoint_topology_context_uuid,
+                })
+            db_context, _ = result
+
+            str_topology_key = key_to_str([endpoint_topology_context_uuid, endpoint_topology_uuid])
+            result : Tuple[TopologyModel, bool] = get_or_create_object(
+                database, TopologyModel, str_topology_key, defaults={
+                    'context_fk': db_context,
+                    'topology_uuid': endpoint_topology_uuid,
+                })
+            db_topology, _ = result
+
+            str_endpoint_key = key_to_str([str_endpoint_key, str_topology_key], separator=':')
+            endpoint_attributes['topology_fk'] = db_topology
+
+        result : Tuple[EndPointModel, bool] = update_or_create_object(
+            database, EndPointModel, str_endpoint_key, endpoint_attributes)
+        _, db_endpoint_updated = result
+        updated = updated or db_endpoint_updated
+
+    return db_device, updated
+
+def sync_device_from_context(
+    device_uuid : str, context_client : ContextClient, database : Database
+    ) -> Tuple[DeviceModel, bool]:
+
+    try:
+        device : Device = context_client.GetDevice(DeviceId(device_uuid={'uuid': device_uuid}))
+    except grpc.RpcError as e:
+        if e.code() != grpc.StatusCode.NOT_FOUND: raise # pylint: disable=no-member
+        return None
+    return update_device_in_local_database(database, device)
+
+def sync_device_to_context(db_device : DeviceModel, context_client : ContextClient) -> None:
+    if db_device is None: return
+    context_client.SetDevice(Device(**db_device.dump(
+        include_config_rules=True, include_drivers=True, include_endpoints=True)))
+
+def delete_device_from_context(db_device : DeviceModel, context_client : ContextClient) -> None:
+    if db_device is None: return
+    context_client.RemoveDevice(DeviceId(**db_device.dump_id()))
+
+def get_device_driver_filter_fields(db_device : DeviceModel) -> Dict[FilterFieldEnum, Any]:
+    if db_device is None: return {}
+    database = db_device.database
+    db_driver_pks = db_device.references(DriverModel)
+    db_driver_names = [DriverModel(database, pk).driver.value for pk,_ in db_driver_pks]
+    return {
+        FilterFieldEnum.DEVICE_TYPE: db_device.device_type,
+        FilterFieldEnum.DRIVER     : db_driver_names,
+    }
diff --git a/src/device/service/database/DeviceModel.py b/src/device/service/database/DeviceModel.py
new file mode 100644
index 0000000000000000000000000000000000000000..bba19d787622019b7b8f25de9c07b7c0984ec42c
--- /dev/null
+++ b/src/device/service/database/DeviceModel.py
@@ -0,0 +1,91 @@
+import functools, logging
+from enum import Enum
+from typing import Dict, List
+from common.orm.Database import Database
+from common.orm.backend.Tools import key_to_str
+from common.orm.fields.EnumeratedField import EnumeratedField
+from common.orm.fields.ForeignKeyField import ForeignKeyField
+from common.orm.fields.PrimaryKeyField import PrimaryKeyField
+from common.orm.fields.StringField import StringField
+from common.orm.model.Model import Model
+from device.proto.context_pb2 import DeviceDriverEnum, DeviceOperationalStatusEnum
+from .ConfigModel import ConfigModel
+from .Tools import grpc_to_enum
+
+LOGGER = logging.getLogger(__name__)
+
+class ORM_DeviceDriverEnum(Enum):
+    UNDEFINED             = DeviceDriverEnum.DEVICEDRIVER_UNDEFINED
+    OPENCONFIG            = DeviceDriverEnum.DEVICEDRIVER_OPENCONFIG
+    TRANSPORT_API         = DeviceDriverEnum.DEVICEDRIVER_TRANSPORT_API
+    P4                    = DeviceDriverEnum.DEVICEDRIVER_P4
+    IETF_NETWORK_TOPOLOGY = DeviceDriverEnum.DEVICEDRIVER_IETF_NETWORK_TOPOLOGY
+    ONF_TR_352            = DeviceDriverEnum.DEVICEDRIVER_ONF_TR_352
+
+grpc_to_enum__device_driver = functools.partial(
+    grpc_to_enum, DeviceDriverEnum, ORM_DeviceDriverEnum)
+
+class ORM_DeviceOperationalStatusEnum(Enum):
+    UNDEFINED = DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_UNDEFINED
+    DISABLED  = DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_DISABLED
+    ENABLED   = DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_ENABLED
+
+grpc_to_enum__device_operational_status = functools.partial(
+    grpc_to_enum, DeviceOperationalStatusEnum, ORM_DeviceOperationalStatusEnum)
+
+class DeviceModel(Model):
+    pk = PrimaryKeyField()
+    device_uuid = StringField(required=True, allow_empty=False)
+    device_type = StringField()
+    device_initial_config_fk = ForeignKeyField(ConfigModel)
+    device_running_config_fk = ForeignKeyField(ConfigModel)
+    device_operational_status = EnumeratedField(ORM_DeviceOperationalStatusEnum, required=True)
+
+    def dump_id(self) -> Dict:
+        return {'device_uuid': {'uuid': self.device_uuid}}
+
+    def dump_initial_config(self) -> Dict:
+        return ConfigModel(self.database, self.device_initial_config_fk).dump()
+
+    def dump_running_config(self) -> Dict:
+        return ConfigModel(self.database, self.device_running_config_fk).dump()
+
+    def dump_drivers(self) -> List[int]:
+        db_driver_pks = self.references(DriverModel)
+        return [DriverModel(self.database, pk).dump() for pk,_ in db_driver_pks]
+
+    def dump_endpoints(self) -> List[Dict]:
+        from .EndPointModel import EndPointModel # pylint: disable=import-outside-toplevel
+        db_endpoints_pks = self.references(EndPointModel)
+        return [EndPointModel(self.database, pk).dump() for pk,_ in db_endpoints_pks]
+
+    def dump(   # pylint: disable=arguments-differ
+            self, include_config_rules=True, include_drivers=True, include_endpoints=True
+        ) -> Dict:
+        result = {
+            'device_id': self.dump_id(),
+            'device_type': self.device_type,
+            'device_operational_status': self.device_operational_status.value,
+        }
+        if include_config_rules: result.setdefault('device_config', {})['config_rules'] = self.dump_running_config()
+        if include_drivers: result['device_drivers'] = self.dump_drivers()
+        if include_endpoints: result['device_endpoints'] = self.dump_endpoints()
+        return result
+
+class DriverModel(Model): # pylint: disable=abstract-method
+    pk = PrimaryKeyField()
+    device_fk = ForeignKeyField(DeviceModel)
+    driver = EnumeratedField(ORM_DeviceDriverEnum, required=True)
+
+    def dump(self) -> Dict:
+        return self.driver.value
+
+def set_drivers(database : Database, db_device : DeviceModel, grpc_device_drivers):
+    db_device_pk = db_device.pk
+    for driver in grpc_device_drivers:
+        orm_driver = grpc_to_enum__device_driver(driver)
+        str_device_driver_key = key_to_str([db_device_pk, orm_driver.name])
+        db_device_driver = DriverModel(database, str_device_driver_key)
+        db_device_driver.device_fk = db_device
+        db_device_driver.driver = orm_driver
+        db_device_driver.save()
diff --git a/src/device/service/database/EndPointModel.py b/src/device/service/database/EndPointModel.py
new file mode 100644
index 0000000000000000000000000000000000000000..38b87d6f37c4e99dd3790f4d8802acd03873f77d
--- /dev/null
+++ b/src/device/service/database/EndPointModel.py
@@ -0,0 +1,33 @@
+import logging
+from typing import Dict
+from common.orm.fields.ForeignKeyField import ForeignKeyField
+from common.orm.fields.PrimaryKeyField import PrimaryKeyField
+from common.orm.fields.StringField import StringField
+from common.orm.model.Model import Model
+from .DeviceModel import DeviceModel
+from .TopologyModel import TopologyModel
+
+LOGGER = logging.getLogger(__name__)
+
+class EndPointModel(Model):
+    pk = PrimaryKeyField()
+    topology_fk = ForeignKeyField(TopologyModel, required=False)
+    device_fk = ForeignKeyField(DeviceModel)
+    endpoint_uuid = StringField(required=True, allow_empty=False)
+    endpoint_type = StringField()
+
+    def dump_id(self) -> Dict:
+        device_id = DeviceModel(self.database, self.device_fk).dump_id()
+        result = {
+            'device_id': device_id,
+            'endpoint_uuid': {'uuid': self.endpoint_uuid},
+        }
+        if self.topology_fk is not None:
+            result['topology_id'] = TopologyModel(self.database, self.topology_fk).dump_id()
+        return result
+
+    def dump(self) -> Dict:
+        return {
+            'endpoint_id': self.dump_id(),
+            'endpoint_type': self.endpoint_type,
+        }
diff --git a/src/device/service/database/KpiModel.py b/src/device/service/database/KpiModel.py
new file mode 100644
index 0000000000000000000000000000000000000000..1bed9fc7a169665eb459b295a6fc9903513e13f0
--- /dev/null
+++ b/src/device/service/database/KpiModel.py
@@ -0,0 +1,45 @@
+import logging
+from typing import Dict
+from common.orm.fields.EnumeratedField import EnumeratedField
+from common.orm.fields.FloatField import FloatField
+from common.orm.fields.ForeignKeyField import ForeignKeyField
+from common.orm.fields.PrimaryKeyField import PrimaryKeyField
+from common.orm.fields.StringField import StringField
+from common.orm.model.Model import Model
+from .DeviceModel import DeviceModel
+from .EndPointModel import EndPointModel
+from .KpiSampleType import ORM_KpiSampleType
+
+LOGGER = logging.getLogger(__name__)
+
+class KpiModel(Model):
+    pk = PrimaryKeyField()
+    kpi_uuid = StringField(required=True, allow_empty=False)
+    kpi_description = StringField(required=False, allow_empty=True)
+    kpi_sample_type = EnumeratedField(ORM_KpiSampleType, required=True)
+    device_fk = ForeignKeyField(DeviceModel)
+    endpoint_fk = ForeignKeyField(EndPointModel)
+    sampling_duration = FloatField(min_value=0, required=True)
+    sampling_interval = FloatField(min_value=0, required=True)
+
+    def dump_id(self) -> Dict:
+        return {'kpi_id': {'uuid': self.kpi_uuid}}
+
+    def dump_descriptor(self) -> Dict:
+        result = {
+            'kpi_description': self.kpi_description,
+            'kpi_sample_type': self.kpi_sample_type.value,
+        }
+        if self.device_fk is not None:
+            result['device_id'] = DeviceModel(self.database, self.device_fk).dump_id()
+        if self.endpoint_fk is not None:
+            result['endpoint_id'] = EndPointModel(self.database, self.endpoint_fk).dump_id()
+        return result
+
+    def dump(self) -> Dict:
+        return {
+            'kpi_id': self.dump_id(),
+            'kpi_descriptor': self.dump_descriptor(),
+            'sampling_duration_s': self.sampling_duration,
+            'sampling_interval_s': self.sampling_interval,
+        }
diff --git a/src/device/service/database/KpiSampleType.py b/src/device/service/database/KpiSampleType.py
new file mode 100644
index 0000000000000000000000000000000000000000..e5c4c5bbc0407e7dd3650ca4ff2f2e95a1202472
--- /dev/null
+++ b/src/device/service/database/KpiSampleType.py
@@ -0,0 +1,14 @@
+import functools
+from enum import Enum
+from device.proto.kpi_sample_types_pb2 import KpiSampleType
+from .Tools import grpc_to_enum
+
+class ORM_KpiSampleType(Enum):
+    UNKNOWN             = KpiSampleType.UNKNOWN
+    PACKETS_TRANSMITTED = KpiSampleType.PACKETS_TRANSMITTED
+    PACKETS_RECEIVED    = KpiSampleType.PACKETS_RECEIVED
+    BYTES_TRANSMITTED   = KpiSampleType.BYTES_TRANSMITTED
+    BYTES_RECEIVED      = KpiSampleType.BYTES_RECEIVED
+
+grpc_to_enum__kpi_sample_type = functools.partial(
+    grpc_to_enum, KpiSampleType, ORM_KpiSampleType)
diff --git a/src/device/service/database/Tools.py b/src/device/service/database/Tools.py
new file mode 100644
index 0000000000000000000000000000000000000000..36ffbcd46fcf686371b0799445ce4f9ce5b75838
--- /dev/null
+++ b/src/device/service/database/Tools.py
@@ -0,0 +1,58 @@
+import hashlib, re
+from enum import Enum
+from typing import Dict, List, Tuple, Union
+
+# Convenient helper function to remove dictionary items in dict/list/set comprehensions.
+
+def remove_dict_key(dictionary : Dict, key : str):
+    dictionary.pop(key, None)
+    return dictionary
+
+# Enumeration classes are redundant with gRPC classes, but gRPC does not provide a programmatical method to retrieve
+# the values it expects from strings containing the desired value symbol or its integer value, so a kind of mapping is
+# required. Besides, ORM Models expect Enum classes in EnumeratedFields; we create specific and conveniently defined
+# Enum classes to serve both purposes.
+
+def grpc_to_enum(grpc_enum_class, orm_enum_class : Enum, grpc_enum_value):
+    grpc_enum_name = grpc_enum_class.Name(grpc_enum_value)
+    grpc_enum_prefix = orm_enum_class.__name__.upper()
+    grpc_enum_prefix = re.sub(r'^ORM_(.+)$', r'\1', grpc_enum_prefix)
+    grpc_enum_prefix = re.sub(r'^(.+)ENUM$', r'\1', grpc_enum_prefix)
+    grpc_enum_prefix = grpc_enum_prefix + '_'
+    orm_enum_name = grpc_enum_name.replace(grpc_enum_prefix, '')
+    orm_enum_value = orm_enum_class._member_map_.get(orm_enum_name) # pylint: disable=protected-access
+    return orm_enum_value
+
+# For some models, it is convenient to produce a string hash for fast comparisons of existence or modification. Method
+# fast_hasher computes configurable length (between 1 and 64 byte) hashes and retrieves them in hex representation.
+
+FASTHASHER_ITEM_ACCEPTED_FORMAT = 'Union[bytes, str]'
+FASTHASHER_DATA_ACCEPTED_FORMAT = 'Union[{fmt:s}, List[{fmt:s}], Tuple[{fmt:s}]]'.format(
+    fmt=FASTHASHER_ITEM_ACCEPTED_FORMAT)
+
+def fast_hasher(data : Union[bytes, str, List[Union[bytes, str]], Tuple[Union[bytes, str]]], digest_size : int = 8):
+    hasher = hashlib.blake2b(digest_size=digest_size)
+    # Do not accept sets, dicts, or other unordered dats tructures since their order is arbitrary thus producing
+    # different hashes depending on the order. Consider adding support for sets or dicts with previous sorting of
+    # items by their key.
+
+    if isinstance(data, bytes):
+        data = [data]
+    elif isinstance(data, str):
+        data = [data.encode('UTF-8')]
+    elif isinstance(data, (list, tuple)):
+        pass
+    else:
+        msg = 'data({:s}) must be {:s}, found {:s}'
+        raise TypeError(msg.format(str(data), FASTHASHER_DATA_ACCEPTED_FORMAT, str(type(data))))
+
+    for i,item in enumerate(data):
+        if isinstance(item, str):
+            item = item.encode('UTF-8')
+        elif isinstance(item, bytes):
+            pass
+        else:
+            msg = 'data[{:d}]({:s}) must be {:s}, found {:s}'
+            raise TypeError(msg.format(i, str(item), FASTHASHER_ITEM_ACCEPTED_FORMAT, str(type(item))))
+        hasher.update(item)
+    return hasher.hexdigest()
diff --git a/src/device/service/database/TopologyModel.py b/src/device/service/database/TopologyModel.py
new file mode 100644
index 0000000000000000000000000000000000000000..71c1067f03803c6d89c69910c82a3c1e24970eee
--- /dev/null
+++ b/src/device/service/database/TopologyModel.py
@@ -0,0 +1,25 @@
+import logging
+from typing import Dict
+from common.orm.fields.ForeignKeyField import ForeignKeyField
+from common.orm.fields.PrimaryKeyField import PrimaryKeyField
+from common.orm.fields.StringField import StringField
+from common.orm.model.Model import Model
+from .ContextModel import ContextModel
+
+LOGGER = logging.getLogger(__name__)
+
+class TopologyModel(Model):
+    pk = PrimaryKeyField()
+    context_fk = ForeignKeyField(ContextModel)
+    topology_uuid = StringField(required=True, allow_empty=False)
+
+#    def dump_id(self) -> Dict:
+#        context_id = ContextModel(self.database, self.context_fk).dump_id()
+#        return {
+#            'context_id': context_id,
+#            'topology_uuid': {'uuid': self.topology_uuid},
+#        }
+
+#    def dump(self) -> Dict:
+#        result = {'topology_id': self.dump_id()}
+#        return result
diff --git a/src/device/service/database/__init__.py b/src/device/service/database/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..ff9b7da0cd0f83f8e01bbb904d8e2471503f00ca
--- /dev/null
+++ b/src/device/service/database/__init__.py
@@ -0,0 +1,2 @@
+# In-Memory database with a simplified representation of Context Database focused on the Device model.
+# Used as an internal configuration cache, for message validation, and message formatting purposes.
diff --git a/src/device/service/driver_api/AnyTreeTools.py b/src/device/service/driver_api/AnyTreeTools.py
new file mode 100644
index 0000000000000000000000000000000000000000..df61c7e030a13a3d0d758ce51a011aaa95deb49f
--- /dev/null
+++ b/src/device/service/driver_api/AnyTreeTools.py
@@ -0,0 +1,60 @@
+import anytree
+from typing import Any, List, Optional
+
+class TreeNode(anytree.node.Node):
+    def __init__(self, name, parent=None, children=None, **kwargs) -> None:
+        super().__init__(name, parent=parent, children=children, **kwargs)
+        self.value : Optional[Any] = None
+
+    def get_full_path(self):
+        return self.separator.join([''] + [str(node.name) for node in self.path])
+
+class RawStyle(anytree.render.AbstractStyle):
+    def __init__(self):
+        """
+        Raw style.
+
+        >>> from anytree import Node, RenderTree
+        >>> root = Node("root")
+        >>> s0 = Node("sub0", parent=root)
+        >>> s0b = Node("sub0B", parent=s0)
+        >>> s0a = Node("sub0A", parent=s0)
+        >>> s1 = Node("sub1", parent=root)
+        >>> print(RenderTree(root, style=RawStyle()))
+        Node('/root')
+        Node('/root/sub0')
+        Node('/root/sub0/sub0B')
+        Node('/root/sub0/sub0A')
+        Node('/root/sub1')
+        """
+        super(RawStyle, self).__init__(u'', u'', u'')
+
+def get_subnode(resolver : anytree.Resolver, root : TreeNode, path : List[str], default : Optional[Any] = None):
+    node = root
+    for path_item in path:
+        try:
+            node = resolver.get(node, path_item)
+        except anytree.ChildResolverError:
+            return default
+    return node
+
+def set_subnode_value(resolver : anytree.Resolver, root : TreeNode, path : List[str], value : Any):
+    node = root
+    for path_item in path:
+        try:
+            node = resolver.get(node, path_item)
+        except anytree.ChildResolverError:
+            node = TreeNode(path_item, parent=node)
+    node.value = value
+
+def dump_subtree(root : TreeNode):
+    if not isinstance(root, TreeNode): raise Exception('root must be a TreeNode')
+    results = []
+    for row in anytree.RenderTree(root, style=RawStyle()):
+        node : TreeNode = row.node
+        path = node.get_full_path()[2:] # get full path except the heading root placeholder "/."
+        if len(path) == 0: continue
+        value = node.value
+        if value is None: continue
+        results.append((path, value))
+    return results
diff --git a/src/device/service/driver_api/DriverFactory.py b/src/device/service/driver_api/DriverFactory.py
index c226e434c75286d99c2051098e0d18d9cf483caa..10b4961fcf98871cc4cbf3e078391006591b606c 100644
--- a/src/device/service/driver_api/DriverFactory.py
+++ b/src/device/service/driver_api/DriverFactory.py
@@ -1,50 +1,73 @@
-from typing import Dict, Set
-from device.service.driver_api.QueryFields import QUERY_FIELDS
+import logging, operator
+from enum import Enum
+from typing import Any, Dict, Iterable, List, Set, Tuple
 from device.service.driver_api._Driver import _Driver
-from device.service.driver_api.Exceptions import MultipleResultsForQueryException, UnsatisfiedQueryException, \
-    UnsupportedDriverClassException, UnsupportedQueryFieldException, UnsupportedQueryFieldValueException
+from device.service.driver_api.Exceptions import (
+    UnsatisfiedFilterException, UnsupportedDriverClassException, UnsupportedFilterFieldException,
+    UnsupportedFilterFieldValueException)
+from device.service.driver_api.FilterFields import FILTER_FIELD_ALLOWED_VALUES, FilterFieldEnum
+
+LOGGER = logging.getLogger(__name__)
 
 class DriverFactory:
-    def __init__(self) -> None:
+    def __init__(self, drivers : List[Tuple[type, List[Dict[FilterFieldEnum, Any]]]]) -> None:
         self.__indices : Dict[str, Dict[str, Set[_Driver]]] = {} # Dict{field_name => Dict{field_value => Set{Driver}}}
 
-    def register_driver_class(self, driver_class, **query_fields):
+        for driver_class,filter_field_sets in drivers:
+            for filter_fields in filter_field_sets:
+                filter_fields = {k.value:v for k,v in filter_fields.items()}
+                self.register_driver_class(driver_class, **filter_fields)
+
+    def register_driver_class(self, driver_class, **filter_fields):
         if not issubclass(driver_class, _Driver): raise UnsupportedDriverClassException(str(driver_class))
 
         driver_name = driver_class.__name__
-        unsupported_query_fields = set(query_fields.keys()).difference(set(QUERY_FIELDS.keys()))
-        if len(unsupported_query_fields) > 0:
-            raise UnsupportedQueryFieldException(unsupported_query_fields, driver_class_name=driver_name)
+        supported_filter_fields = set(FILTER_FIELD_ALLOWED_VALUES.keys())
+        unsupported_filter_fields = set(filter_fields.keys()).difference(supported_filter_fields)
+        if len(unsupported_filter_fields) > 0:
+            raise UnsupportedFilterFieldException(unsupported_filter_fields, driver_class_name=driver_name)
 
-        for field_name, field_value in query_fields.items():
+        for field_name, field_values in filter_fields.items():
             field_indice = self.__indices.setdefault(field_name, dict())
-            field_enum_values = QUERY_FIELDS.get(field_name)
-            if field_enum_values is not None and field_value not in field_enum_values:
-                raise UnsupportedQueryFieldValueException(
-                    field_name, field_value, field_enum_values, driver_class_name=driver_name)
-            field_indice_drivers = field_indice.setdefault(field_name, set())
-            field_indice_drivers.add(driver_class)
-
-    def get_driver_class(self, **query_fields) -> _Driver:
-        unsupported_query_fields = set(query_fields.keys()).difference(set(QUERY_FIELDS.keys()))
-        if len(unsupported_query_fields) > 0: raise UnsupportedQueryFieldException(unsupported_query_fields)
+            field_enum_values = FILTER_FIELD_ALLOWED_VALUES.get(field_name)
+            if not isinstance(field_values, Iterable) or isinstance(field_values, str):
+                field_values = [field_values]
+            for field_value in field_values:
+                if isinstance(field_value, Enum): field_value = field_value.value
+                if field_enum_values is not None and field_value not in field_enum_values:
+                    raise UnsupportedFilterFieldValueException(
+                        field_name, field_value, field_enum_values, driver_class_name=driver_name)
+                field_indice_drivers = field_indice.setdefault(field_value, set())
+                field_indice_drivers.add(driver_class)
 
-        candidate_driver_classes = None
+    def get_driver_class(self, **filter_fields) -> _Driver:
+        supported_filter_fields = set(FILTER_FIELD_ALLOWED_VALUES.keys())
+        unsupported_filter_fields = set(filter_fields.keys()).difference(supported_filter_fields)
+        if len(unsupported_filter_fields) > 0: raise UnsupportedFilterFieldException(unsupported_filter_fields)
 
-        for field_name, field_value in query_fields.items():
+        candidate_driver_classes : Dict[_Driver, int] = None # number of filter hits per driver
+        for field_name, field_values in filter_fields.items():
             field_indice = self.__indices.get(field_name)
             if field_indice is None: continue
-            field_enum_values = QUERY_FIELDS.get(field_name)
-            if field_enum_values is not None and field_value not in field_enum_values:
-                raise UnsupportedQueryFieldValueException(field_name, field_value, field_enum_values)
-            field_indice_drivers = field_indice.get(field_name)
-            if field_indice_drivers is None: continue
-
-            candidate_driver_classes = set().union(field_indice_drivers) if candidate_driver_classes is None else \
-                candidate_driver_classes.intersection(field_indice_drivers)
-
-        if len(candidate_driver_classes) == 0: raise UnsatisfiedQueryException(query_fields)
-        if len(candidate_driver_classes) >  1:
-            # TODO: Consider choosing driver with more query fields being satisfied (i.e., the most restrictive one)
-            raise MultipleResultsForQueryException(query_fields, {d.__name__ for d in candidate_driver_classes})
-        return candidate_driver_classes.pop()
+            field_enum_values = FILTER_FIELD_ALLOWED_VALUES.get(field_name)
+            if not isinstance(field_values, Iterable) or isinstance(field_values, str):
+                field_values = [field_values]
+
+            field_candidate_driver_classes = set()
+            for field_value in field_values:
+                if field_enum_values is not None and field_value not in field_enum_values:
+                    raise UnsupportedFilterFieldValueException(field_name, field_value, field_enum_values)
+                field_indice_drivers = field_indice.get(field_value)
+                if field_indice_drivers is None: continue
+                field_candidate_driver_classes = field_candidate_driver_classes.union(field_indice_drivers)
+
+            if candidate_driver_classes is None:
+                candidate_driver_classes = {k:1 for k in field_candidate_driver_classes}
+            else:
+                for candidate_driver_class in candidate_driver_classes:
+                    if candidate_driver_class not in field_candidate_driver_classes: continue
+                    candidate_driver_classes[candidate_driver_class] += 1
+
+        if len(candidate_driver_classes) == 0: raise UnsatisfiedFilterException(filter_fields)
+        candidate_driver_classes = sorted(candidate_driver_classes.items(), key=operator.itemgetter(1), reverse=True)
+        return candidate_driver_classes[0][0]
diff --git a/src/device/service/driver_api/DriverInstanceCache.py b/src/device/service/driver_api/DriverInstanceCache.py
new file mode 100644
index 0000000000000000000000000000000000000000..f960e37cbd22a7d4c06c9119ffaf64540dfc791a
--- /dev/null
+++ b/src/device/service/driver_api/DriverInstanceCache.py
@@ -0,0 +1,57 @@
+import logging, threading
+from typing import Any, Dict, Optional
+
+from device.service.driver_api.FilterFields import FilterFieldEnum
+from ._Driver import _Driver
+from .DriverFactory import DriverFactory
+from .Exceptions import DriverInstanceCacheTerminatedException
+
+LOGGER = logging.getLogger(__name__)
+
+class DriverInstanceCache:
+    def __init__(self, driver_factory : DriverFactory) -> None:
+        self._lock = threading.Lock()
+        self._terminate = threading.Event()
+        self._device_uuid__to__driver_instance : Dict[str, _Driver] = {}
+        self._driver_factory = driver_factory
+
+    def get(
+        self, device_uuid : str, filter_fields : Dict[FilterFieldEnum, Any] = {}, address : Optional[str] = None,
+        port : Optional[int] = None, settings : Dict[str, Any] = {}) -> _Driver:
+
+        if self._terminate.is_set():
+            raise DriverInstanceCacheTerminatedException()
+
+        filter_fields = {k.value:v for k,v in filter_fields.items()}
+
+        with self._lock:
+            driver_instance = self._device_uuid__to__driver_instance.get(device_uuid)
+            if driver_instance is not None: return driver_instance
+
+            if len(filter_fields) == 0: return None
+            MSG = 'Selecting driver for device({:s}) with filter_fields({:s})...'
+            LOGGER.info(MSG.format(str(device_uuid), str(filter_fields)))
+            driver_class = self._driver_factory.get_driver_class(**filter_fields)
+            MSG = 'Driver({:s}) selected for device({:s}) with filter_fields({:s})...'
+            LOGGER.info(MSG.format(str(driver_class.__name__), str(device_uuid), str(filter_fields)))
+            driver_instance : _Driver = driver_class(address, port, **settings)
+            self._device_uuid__to__driver_instance[device_uuid] = driver_instance
+            return driver_instance
+
+    def delete(self, device_uuid : str) -> None:
+        with self._lock:
+            device_driver = self._device_uuid__to__driver_instance.pop(device_uuid, None)
+            if device_driver is None: return
+            device_driver.Disconnect()
+
+    def terminate(self) -> None:
+        self._terminate.set()
+        with self._lock:
+            while len(self._device_uuid__to__driver_instance) > 0:
+                try:
+                    device_uuid,device_driver = self._device_uuid__to__driver_instance.popitem()
+                    device_driver.Disconnect()
+                except: # pylint: disable=bare-except
+                    msg = 'Error disconnecting Driver({:s}) from device. Will retry later...'
+                    LOGGER.exception(msg.format(device_uuid))
+                    self._device_uuid__to__driver_instance[device_uuid] = device_driver
diff --git a/src/device/service/driver_api/Exceptions.py b/src/device/service/driver_api/Exceptions.py
index 2275bb04cbf9334a6823544bc48e8ad345d2dc20..c35d6372d60b4ea465b40d98e9e43e979835451d 100644
--- a/src/device/service/driver_api/Exceptions.py
+++ b/src/device/service/driver_api/Exceptions.py
@@ -1,30 +1,54 @@
-class MultipleResultsForQueryException(Exception):
-    def __init__(self, query_fields, driver_names):
-        super().__init__('Multiple Drivers({}) satisfy QueryFields({})'.format(str(driver_names), str(query_fields)))
-
-class UnsatisfiedQueryException(Exception):
-    def __init__(self, query_fields):
-        super().__init__('No Driver satisfies QueryFields({})'.format(str(query_fields)))
+class UnsatisfiedFilterException(Exception):
+    def __init__(self, filter_fields):
+        msg = 'No Driver satisfies FilterFields({:s})'
+        super().__init__(msg.format(str(filter_fields)))
 
 class UnsupportedDriverClassException(Exception):
     def __init__(self, driver_class_name):
-        super().__init__('Class({}) is not a subclass of _Driver'.format(str(driver_class_name)))
+        msg = 'Class({:s}) is not a subclass of _Driver'
+        super().__init__(msg.format(str(driver_class_name)))
 
-class UnsupportedQueryFieldException(Exception):
-    def __init__(self, unsupported_query_fields, driver_class_name=None):
+class UnsupportedFilterFieldException(Exception):
+    def __init__(self, unsupported_filter_fields, driver_class_name=None):
         if driver_class_name:
-            msg = 'QueryFields({}) specified by Driver({}) are not supported'.format(
-                str(unsupported_query_fields), str(driver_class_name))
+            msg = 'FilterFields({:s}) specified by Driver({:s}) are not supported'
+            msg = msg.format(str(unsupported_filter_fields), str(driver_class_name))
         else:
-            msg = 'QueryFields({}) specified in query are not supported'.format(str(unsupported_query_fields))
+            msg = 'FilterFields({:s}) specified in Filter are not supported'
+            msg = msg.format(str(unsupported_filter_fields))
         super().__init__(msg)
 
-class UnsupportedQueryFieldValueException(Exception):
-    def __init__(self, query_field_name, query_field_value, allowed_query_field_values, driver_class_name=None):
+class UnsupportedFilterFieldValueException(Exception):
+    def __init__(self, filter_field_name, filter_field_value, allowed_filter_field_values, driver_class_name=None):
         if driver_class_name:
-            msg = 'QueryField({}={}) specified by Driver({}) is not supported. Allowed values are {}'.format(
-                str(query_field_name), str(query_field_value), str(driver_class_name), str(allowed_query_field_values))
+            msg = 'FilterField({:s}={:s}) specified by Driver({:s}) is not supported. Allowed values are {:s}'
+            msg = msg.format(
+                str(filter_field_name), str(filter_field_value), str(driver_class_name),
+                str(allowed_filter_field_values))
         else:
-            msg = 'QueryField({}={}) specified in query is not supported. Allowed values are {}'.format(
-                str(query_field_name), str(query_field_value), str(allowed_query_field_values))
+            msg = 'FilterField({:s}={:s}) specified in Filter is not supported. Allowed values are {:s}'
+            msg = msg.format(str(filter_field_name), str(filter_field_value), str(allowed_filter_field_values))
+        super().__init__(msg)
+
+class DriverInstanceCacheTerminatedException(Exception):
+    def __init__(self):
+        msg = 'DriverInstanceCache is terminated. No new instances can be processed.'
+        super().__init__(msg)
+
+class UnsupportedResourceKeyException(Exception):
+    def __init__(self, resource_key):
+        msg = 'ResourceKey({:s}) not supported'
+        msg = msg.format(str(resource_key))
+        super().__init__(msg)
+
+class ConfigFieldNotFoundException(Exception):
+    def __init__(self, config_field_name):
+        msg = 'ConfigField({:s}) not specified in resource'
+        msg = msg.format(str(config_field_name))
+        super().__init__(msg)
+
+class ConfigFieldsNotSupportedException(Exception):
+    def __init__(self, config_fields):
+        msg = 'ConfigFields({:s}) not supported in resource'
+        msg = msg.format(str(config_fields))
         super().__init__(msg)
diff --git a/src/device/service/driver_api/FilterFields.py b/src/device/service/driver_api/FilterFields.py
new file mode 100644
index 0000000000000000000000000000000000000000..c7de05f92a743c826d54897930b10013bd09c2b7
--- /dev/null
+++ b/src/device/service/driver_api/FilterFields.py
@@ -0,0 +1,26 @@
+from enum import Enum
+from device.service.database.DeviceModel import ORM_DeviceDriverEnum
+
+class DeviceTypeFilterFieldEnum(Enum):
+    EMULATED            = 'emulated'
+    OPTICAL_ROADM       = 'optical-roadm'
+    OPTICAL_TRANDPONDER = 'optical-trandponder'
+    OPTICAL_LINE_SYSTEM = 'optical-line-system'
+    PACKET_ROUTER       = 'packet-router'
+    PACKET_SWITCH       = 'packet-switch'
+
+class FilterFieldEnum(Enum):
+    DEVICE_TYPE   = 'device_type'
+    DRIVER        = 'driver'
+    VENDOR        = 'vendor'
+    MODEL         = 'model'
+    SERIAL_NUMBER = 'serial_number'
+
+# Map allowed filter fields to allowed values per Filter field. If no restriction (free text) None is specified
+FILTER_FIELD_ALLOWED_VALUES = {
+    FilterFieldEnum.DEVICE_TYPE.value   : {i.value for i in DeviceTypeFilterFieldEnum},
+    FilterFieldEnum.DRIVER.value        : {i.value for i in ORM_DeviceDriverEnum},
+    FilterFieldEnum.VENDOR.value        : None,
+    FilterFieldEnum.MODEL.value         : None,
+    FilterFieldEnum.SERIAL_NUMBER.value : None,
+}
diff --git a/src/device/service/driver_api/Tools.py b/src/device/service/driver_api/Tools.py
new file mode 100644
index 0000000000000000000000000000000000000000..ab22cbc040daee7e47e80b0235b79b19cdb7acbc
--- /dev/null
+++ b/src/device/service/driver_api/Tools.py
@@ -0,0 +1,57 @@
+import operator
+from typing import Any, Callable, List, Tuple, Union
+
+ACTION_MSG_GET         = 'Get resource(key={:s})'
+ACTION_MSG_SET         = 'Set resource(key={:s}, value={:s})'
+ACTION_MSG_DELETE      = 'Delete resource(key={:s}, value={:s})'
+ACTION_MSG_SUBSCRIBE   = 'Subscribe subscription(key={:s}, duration={:s}, interval={:s})'
+ACTION_MSG_UNSUBSCRIBE = 'Unsubscribe subscription(key={:s}, duration={:s}, interval={:s})'
+
+def _get(resource_key : str):
+    return ACTION_MSG_GET.format(str(resource_key))
+
+def _set(resource : Tuple[str, Any]):
+    return ACTION_MSG_SET.format(*tuple(map(str, resource)))
+
+def _delete(resource : Tuple[str, Any]):
+    return ACTION_MSG_SET.format(*tuple(map(str, resource)))
+
+def _subscribe(subscription : Tuple[str, float, float]):
+    return ACTION_MSG_SUBSCRIBE.format(*tuple(map(str, subscription)))
+
+def _unsubscribe(subscription : Tuple[str, float, float]):
+    return ACTION_MSG_UNSUBSCRIBE.format(*tuple(map(str, subscription)))
+
+def _check_errors(
+    error_func : Callable, parameters_list : List[Any], results_list : List[Union[bool, Exception]]
+    ) -> List[str]:
+    errors = []
+    for parameters, results in zip(parameters_list, results_list):
+        if not isinstance(results, Exception): continue
+        errors.append('Unable to {:s}; error({:s})'.format(error_func(parameters), str(results)))
+    return errors
+
+def check_get_errors(
+    resource_keys : List[str], results : List[Tuple[str, Union[Any, None, Exception]]]
+    ) -> List[str]:
+    return _check_errors(_get, resource_keys, map(operator.itemgetter(1), results))
+
+def check_set_errors(
+    resources : List[Tuple[str, Any]], results : List[Union[bool, Exception]]
+    ) -> List[str]:
+    return _check_errors(_set, resources, results)
+
+def check_delete_errors(
+    resources : List[Tuple[str, Any]], results : List[Union[bool, Exception]]
+    ) -> List[str]:
+    return _check_errors(_delete, resources, results)
+
+def check_subscribe_errors(
+    subscriptions : List[Tuple[str, float, float]], results : List[Union[bool, Exception]]
+    ) -> List[str]:
+    return _check_errors(_subscribe, subscriptions, results)
+
+def check_unsubscribe_errors(
+    subscriptions : List[Tuple[str, float, float]], results : List[Union[bool, Exception]]
+    ) -> List[str]:
+    return _check_errors(_unsubscribe, subscriptions, results)
diff --git a/src/device/service/driver_api/_Driver.py b/src/device/service/driver_api/_Driver.py
index 69ac93cb6a071a40aca25b28f930e390bfbfe6b0..73174ba53ac6e0b357d3bd790c53f429bea36d87 100644
--- a/src/device/service/driver_api/_Driver.py
+++ b/src/device/service/driver_api/_Driver.py
@@ -1,15 +1,21 @@
 from typing import Any, Iterator, List, Tuple, Union
 
+# Special resource names to request to the driver to retrieve the specified configuration/structural resources.
+# These resource names should be used with GetConfig() method.
+RESOURCE_ENDPOINTS         = '__endpoints__'
+RESOURCE_INTERFACES        = '__interfaces__'
+RESOURCE_NETWORK_INSTANCES = '__network_instances__'
+
 class _Driver:
-    def __init__(self, address : str, port : int, **kwargs) -> None:
+    def __init__(self, address : str, port : int, **settings) -> None:
         """ Initialize Driver.
             Parameters:
                 address : str
                     The address of the device
                 port : int
                     The port of the device
-                **kwargs
-                    Extra attributes can be configured using kwargs.
+                **settings
+                    Extra settings required by the driver.
         """
         raise NotImplementedError()
 
@@ -29,20 +35,38 @@ class _Driver:
         """
         raise NotImplementedError()
 
-    def GetConfig(self, resource_keys : List[str]) -> List[Union[Any, None, Exception]]:
+    def GetInitialConfig(self) -> List[Tuple[str, Any]]:
+        """ Retrieve initial configuration of entire device.
+            Returns:
+                values : List[Tuple[str, Any]]
+                    List of tuples (resource key, resource value) for resource keys.
+        """
+        raise NotImplementedError()
+
+    def GetConfig(self, resource_keys : List[str] = []) -> List[Tuple[str, Union[Any, None, Exception]]]:
         """ Retrieve running configuration of entire device, or selected resource keys.
             Parameters:
                 resource_keys : List[str]
                     List of keys pointing to the resources to be retrieved.
             Returns:
-                values : List[Union[Any, None, Exception]]
-                    List of values for resource keys requested. Return values must be in the same order than resource
-                    keys requested. If a resource is found, the appropriate value type must be retrieved, if a resource
-                    is not found, None must be retrieved in the List for that resource. In case of Exception processing
-                    a resource, the Exception must be retrieved.
+                values : List[Tuple[str, Union[Any, None, Exception]]]
+                    List of tuples (resource key, resource value) for resource keys requested. If a resource is found,
+                    the appropriate value type must be retrieved. If a resource is not found, None must be retrieved as
+                    value for that resource. In case of Exception, the Exception must be retrieved as value.
         """
         raise NotImplementedError()
 
+    #def GetResource(self, endpoint_uuid : str) -> Optional[str]:
+    #    """ Retrieve the endpoint path for subscriptions.
+    #        Parameters:
+    #            endpoint_uuid : str
+    #                Target endpoint UUID
+    #        Returns:
+    #            resource_path : Optional[str]
+    #                The path of the endpoint, or None if it is not found.
+    #    """
+    #    raise NotImplementedError()
+
     def SetConfig(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]:
         """ Create/Update configuration for a list of resources.
             Parameters:
@@ -57,11 +81,12 @@ class _Driver:
         """
         raise NotImplementedError()
 
-    def DeleteConfig(self, resource_keys : List[str]) -> List[Union[bool, Exception]]:
-        """ Delete configuration for a list of resource keys.
+    def DeleteConfig(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]:
+        """ Delete configuration for a list of resources.
             Parameters:
-                resource_keys : List[str]
-                    List of keys pointing to the resources to be deleted.
+                resources : List[Tuple[str, Any]]
+                    List of tuples, each containing a resource_key pointing the resource to be modified, and a
+                    resource_value containing possible additionally required values to locate the value to be removed.
             Returns:
                 results : List[bool]
                     List of results for resource key deletions requested. Return values must be in the same order than
diff --git a/src/device/service/drivers/__init__.py b/src/device/service/drivers/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..e59bae207e4cd14f238aabcb7e373bb973374005 100644
--- a/src/device/service/drivers/__init__.py
+++ b/src/device/service/drivers/__init__.py
@@ -0,0 +1,25 @@
+from ..driver_api.FilterFields import FilterFieldEnum, DeviceTypeFilterFieldEnum, ORM_DeviceDriverEnum
+from .emulated.EmulatedDriver import EmulatedDriver
+from .openconfig.OpenConfigDriver import OpenConfigDriver
+from .transport_api.TransportApiDriver import TransportApiDriver
+
+DRIVERS = [
+    (EmulatedDriver, [
+        {
+            FilterFieldEnum.DEVICE_TYPE: DeviceTypeFilterFieldEnum.EMULATED,
+            FilterFieldEnum.DRIVER     : ORM_DeviceDriverEnum.UNDEFINED,
+        }
+    ]),
+    (OpenConfigDriver, [
+        {
+            FilterFieldEnum.DEVICE_TYPE: DeviceTypeFilterFieldEnum.PACKET_ROUTER,
+            FilterFieldEnum.DRIVER     : ORM_DeviceDriverEnum.OPENCONFIG,
+        }
+    ]),
+    (TransportApiDriver, [
+        {
+            FilterFieldEnum.DEVICE_TYPE: DeviceTypeFilterFieldEnum.OPTICAL_LINE_SYSTEM,
+            FilterFieldEnum.DRIVER     : ORM_DeviceDriverEnum.TRANSPORT_API,
+        }
+    ]),
+]
diff --git a/src/device/service/drivers/emulated/EmulatedDriver.py b/src/device/service/drivers/emulated/EmulatedDriver.py
index 7dc5757f7afb928bd56e64b5a87d4c414171cd6b..06dc4bd7c36e99d5f081f97e2caa46b8bb5fd2d6 100644
--- a/src/device/service/drivers/emulated/EmulatedDriver.py
+++ b/src/device/service/drivers/emulated/EmulatedDriver.py
@@ -1,23 +1,25 @@
 import anytree, logging, pytz, queue, random, threading
 from datetime import datetime, timedelta
-from typing import Any, Iterator, List, Tuple, Union
+from typing import Any, Iterator, List, Optional, Tuple, Union
 from apscheduler.executors.pool import ThreadPoolExecutor
 from apscheduler.job import Job
 from apscheduler.jobstores.memory import MemoryJobStore
 from apscheduler.schedulers.background import BackgroundScheduler
-from common.Checkers import chk_float, chk_length, chk_string, chk_type
+from common.type_checkers.Checkers import chk_float, chk_length, chk_string, chk_type
 from device.service.driver_api._Driver import _Driver
-from .AnyTreeTools import TreeNode, dump_subtree, get_subnode, set_subnode_value
+from device.service.driver_api.AnyTreeTools import TreeNode, dump_subtree, get_subnode, set_subnode_value
 
 LOGGER = logging.getLogger(__name__)
 
-def sample(resource_key : str, out_samples : queue.Queue):
+def do_sampling(resource_key : str, out_samples : queue.Queue):
     out_samples.put_nowait((datetime.timestamp(datetime.utcnow()), resource_key, random.random()))
 
 class EmulatedDriver(_Driver):
-    def __init__(self, address : str, port : int, **kwargs) -> None:
+    def __init__(self, address : str, port : int, **settings) -> None: # pylint: disable=super-init-not-called
         self.__lock = threading.Lock()
-        self.__root = TreeNode('.')
+        self.__initial = TreeNode('.')
+        self.__running = TreeNode('.')
+        self.__started = threading.Event()
         self.__terminate = threading.Event()
         self.__scheduler = BackgroundScheduler(daemon=True) # scheduler used to emulate sampling events
         self.__scheduler.configure(
@@ -28,21 +30,35 @@ class EmulatedDriver(_Driver):
         self.__out_samples = queue.Queue()
 
     def Connect(self) -> bool:
+        # If started, assume it is already connected
+        if self.__started.is_set(): return True
+
         # Connect triggers activation of sampling events that will be scheduled based on subscriptions
         self.__scheduler.start()
+
+        # Indicate the driver is now connected to the device
+        self.__started.set()
         return True
 
     def Disconnect(self) -> bool:
         # 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
+
         # Disconnect triggers deactivation of sampling events
         self.__scheduler.shutdown()
         return True
 
-    def GetConfig(self, resource_keys : List[str] = []) -> List[Union[Any, None, Exception]]:
+    def GetInitialConfig(self) -> List[Tuple[str, Any]]:
+        with self.__lock:
+            return dump_subtree(self.__initial)
+
+    def GetConfig(self, resource_keys : List[str] = []) -> List[Tuple[str, Union[Any, None, Exception]]]:
         chk_type('resources', resource_keys, list)
         with self.__lock:
-            if len(resource_keys) == 0: return dump_subtree(self.__root)
+            if len(resource_keys) == 0: return dump_subtree(self.__running)
             results = []
             resolver = anytree.Resolver(pathattr='name')
             for i,resource_key in enumerate(resource_keys):
@@ -50,16 +66,23 @@ class EmulatedDriver(_Driver):
                 try:
                     chk_string(str_resource_name, resource_key, allow_empty=False)
                     resource_path = resource_key.split('/')
-                except Exception as e:
-                    LOGGER.exception('Exception validating {}: {}'.format(str_resource_name, str(resource_key)))
-                    results.append(e) # if validation fails, store the exception
+                except Exception as e: # pylint: disable=broad-except
+                    LOGGER.exception('Exception validating {:s}: {:s}'.format(str_resource_name, str(resource_key)))
+                    results.append((resource_key, e)) # if validation fails, store the exception
                     continue
 
-                resource_node = get_subnode(resolver, self.__root, resource_path, default=None)
+                resource_node = get_subnode(resolver, self.__running, resource_path, default=None)
                 # if not found, resource_node is None
-                results.append(None if resource_node is None else dump_subtree(resource_node))
+                if resource_node is None: continue
+                results.extend(dump_subtree(resource_node))
             return results
 
+    def GetResource(self, endpoint_uuid : str) -> Optional[str]:
+        chk_string('endpoint_uuid', endpoint_uuid)
+        return {
+            #'key': 'value',
+        }.get(endpoint_uuid)
+
     def SetConfig(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]:
         chk_type('resources', resources, list)
         if len(resources) == 0: return []
@@ -70,35 +93,39 @@ class EmulatedDriver(_Driver):
                 str_resource_name = 'resources[#{:d}]'.format(i)
                 try:
                     chk_type(str_resource_name, resource, (list, tuple))
-                    chk_length(str_resource_name, resource, allowed_lengths=2)
+                    chk_length(str_resource_name, resource, min_length=2, max_length=2)
                     resource_key,resource_value = resource
+                    chk_string(str_resource_name, resource_key, allow_empty=False)
                     resource_path = resource_key.split('/')
-                except Exception as e:
-                    LOGGER.exception('Exception validating {}: {}'.format(str_resource_name, str(resource_key)))
+                except Exception as e: # pylint: disable=broad-except
+                    LOGGER.exception('Exception validating {:s}: {:s}'.format(str_resource_name, str(resource_key)))
                     results.append(e) # if validation fails, store the exception
                     continue
 
-                set_subnode_value(resolver, self.__root, resource_path, resource_value)
+                set_subnode_value(resolver, self.__running, resource_path, resource_value)
                 results.append(True)
         return results
 
-    def DeleteConfig(self, resource_keys : List[str]) -> List[Union[bool, Exception]]:
-        chk_type('resources', resource_keys, list)
-        if len(resource_keys) == 0: return []
+    def DeleteConfig(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]:
+        chk_type('resources', resources, list)
+        if len(resources) == 0: return []
         results = []
         resolver = anytree.Resolver(pathattr='name')
         with self.__lock:
-            for i,resource_key in enumerate(resource_keys):
-                str_resource_name = 'resource_key[#{:d}]'.format(i)
+            for i,resource in enumerate(resources):
+                str_resource_name = 'resources[#{:d}]'.format(i)
                 try:
+                    chk_type(str_resource_name, resource, (list, tuple))
+                    chk_length(str_resource_name, resource, min_length=2, max_length=2)
+                    resource_key,_ = resource
                     chk_string(str_resource_name, resource_key, allow_empty=False)
                     resource_path = resource_key.split('/')
-                except Exception as e:
-                    LOGGER.exception('Exception validating {}: {}'.format(str_resource_name, str(resource_key)))
+                except Exception as e: # pylint: disable=broad-except
+                    LOGGER.exception('Exception validating {:s}: {:s}'.format(str_resource_name, str(resource_key)))
                     results.append(e) # if validation fails, store the exception
                     continue
 
-                resource_node = get_subnode(resolver, self.__root, resource_path, default=None)
+                resource_node = get_subnode(resolver, self.__running, resource_path, default=None)
                 # if not found, resource_node is None
                 if resource_node is None:
                     results.append(False)
@@ -111,24 +138,24 @@ class EmulatedDriver(_Driver):
                 results.append(True)
         return results
 
-    def SubscribeState(self, resources : List[Tuple[str, float, float]]) -> List[Union[bool, Exception]]:
-        chk_type('resources', resources, list)
-        if len(resources) == 0: return []
+    def SubscribeState(self, subscriptions : List[Tuple[str, float, float]]) -> List[Union[bool, Exception]]:
+        chk_type('subscriptions', subscriptions, list)
+        if len(subscriptions) == 0: return []
         results = []
         resolver = anytree.Resolver(pathattr='name')
         with self.__lock:
-            for i,resource in enumerate(resources):
-                str_resource_name = 'resources[#{:d}]'.format(i)
+            for i,subscription in enumerate(subscriptions):
+                str_subscription_name = 'subscriptions[#{:d}]'.format(i)
                 try:
-                    chk_type(str_resource_name, resource, (list, tuple))
-                    chk_length(str_resource_name, resource, allowed_lengths=3)
-                    resource_key,sampling_duration,sampling_interval = resource
-                    chk_string(str_resource_name + '.resource_key', resource_key, allow_empty=False)
+                    chk_type(str_subscription_name, subscription, (list, tuple))
+                    chk_length(str_subscription_name, subscription, min_length=3, max_length=3)
+                    resource_key,sampling_duration,sampling_interval = subscription
+                    chk_string(str_subscription_name + '.resource_key', resource_key, allow_empty=False)
                     resource_path = resource_key.split('/')
-                    chk_float(str_resource_name + '.sampling_duration', sampling_duration, min_value=0)
-                    chk_float(str_resource_name + '.sampling_interval', sampling_interval, min_value=0)
-                except Exception as e:
-                    LOGGER.exception('Exception validating {}: {}'.format(str_resource_name, str(resource_key)))
+                    chk_float(str_subscription_name + '.sampling_duration', sampling_duration, min_value=0)
+                    chk_float(str_subscription_name + '.sampling_interval', sampling_interval, min_value=0)
+                except Exception as e: # pylint: disable=broad-except
+                    LOGGER.exception('Exception validating {:s}: {:s}'.format(str_subscription_name, str(resource_key)))
                     results.append(e) # if validation fails, store the exception
                     continue
 
@@ -139,39 +166,39 @@ class EmulatedDriver(_Driver):
 
                 job_id = 'k={:s}/d={:f}/i={:f}'.format(resource_key, sampling_duration, sampling_interval)
                 job = self.__scheduler.add_job(
-                    sample, args=(resource_key, self.__out_samples), kwargs={},
+                    do_sampling, args=(resource_key, self.__out_samples), kwargs={},
                     id=job_id, trigger='interval', seconds=sampling_interval,
                     start_date=start_date, end_date=end_date, timezone=pytz.utc)
 
                 subscription_path = resource_path + ['{:.3f}:{:.3f}'.format(sampling_duration, sampling_interval)]
-                set_subnode_value(resolver, self.__root, subscription_path, job)
+                set_subnode_value(resolver, self.__running, subscription_path, job)
                 results.append(True)
         return results
 
-    def UnsubscribeState(self, resources : List[Tuple[str, float, float]]) -> List[Union[bool, Exception]]:
-        chk_type('resources', resources, list)
-        if len(resources) == 0: return []
+    def UnsubscribeState(self, subscriptions : List[Tuple[str, float, float]]) -> List[Union[bool, Exception]]:
+        chk_type('subscriptions', subscriptions, list)
+        if len(subscriptions) == 0: return []
         results = []
         resolver = anytree.Resolver(pathattr='name')
         with self.__lock:
-            for i,resource in enumerate(resources):
-                str_resource_name = 'resources[#{:d}]'.format(i)
+            for i,resource in enumerate(subscriptions):
+                str_subscription_name = 'resources[#{:d}]'.format(i)
                 try:
-                    chk_type(str_resource_name, resource, (list, tuple))
-                    chk_length(str_resource_name, resource, allowed_lengths=3)
+                    chk_type(str_subscription_name, resource, (list, tuple))
+                    chk_length(str_subscription_name, resource, min_length=3, max_length=3)
                     resource_key,sampling_duration,sampling_interval = resource
-                    chk_string(str_resource_name + '.resource_key', resource_key, allow_empty=False)
+                    chk_string(str_subscription_name + '.resource_key', resource_key, allow_empty=False)
                     resource_path = resource_key.split('/')
-                    chk_float(str_resource_name + '.sampling_duration', sampling_duration, min_value=0)
-                    chk_float(str_resource_name + '.sampling_interval', sampling_interval, min_value=0)
-                except Exception as e:
-                    LOGGER.exception('Exception validating {}: {}'.format(str_resource_name, str(resource_key)))
+                    chk_float(str_subscription_name + '.sampling_duration', sampling_duration, min_value=0)
+                    chk_float(str_subscription_name + '.sampling_interval', sampling_interval, min_value=0)
+                except Exception as e: # pylint: disable=broad-except
+                    LOGGER.exception('Exception validating {:s}: {:s}'.format(str_subscription_name, str(resource_key)))
                     results.append(e) # if validation fails, store the exception
                     continue
 
                 subscription_path = resource_path + ['{:.3f}:{:.3f}'.format(sampling_duration, sampling_interval)]
-                subscription_node = get_subnode(resolver, self.__root, subscription_path)
-                
+                subscription_node = get_subnode(resolver, self.__running, subscription_path)
+
                 # if not found, resource_node is None
                 if subscription_node is None:
                     results.append(False)
@@ -179,7 +206,7 @@ class EmulatedDriver(_Driver):
 
                 job : Job = getattr(subscription_node, 'value', None)
                 if job is None or not isinstance(job, Job):
-                    raise Exception('Malformed subscription node or wrong resource key: {}'.format(str(resource)))
+                    raise Exception('Malformed subscription node or wrong resource key: {:s}'.format(str(resource)))
                 job.remove()
 
                 parent = subscription_node.parent
diff --git a/src/device/service/drivers/openconfig/OpenConfigDriver.py b/src/device/service/drivers/openconfig/OpenConfigDriver.py
new file mode 100644
index 0000000000000000000000000000000000000000..e2381d81e5e1aa81907f90b6e473efb154e92e79
--- /dev/null
+++ b/src/device/service/drivers/openconfig/OpenConfigDriver.py
@@ -0,0 +1,231 @@
+import logging, pytz, queue, threading
+#from datetime import datetime, timedelta
+from typing import Any, List, Tuple, Union
+#from apscheduler.executors.pool import ThreadPoolExecutor
+#from apscheduler.job import Job
+#from apscheduler.jobstores.memory import MemoryJobStore
+#from apscheduler.schedulers.background import BackgroundScheduler
+from netconf_client.connect import connect_ssh
+from netconf_client.ncclient import Manager
+from common.type_checkers.Checkers import chk_length, chk_string, chk_type
+from device.service.driver_api.Exceptions import UnsupportedResourceKeyException
+from device.service.driver_api._Driver import _Driver
+from device.service.driver_api.AnyTreeTools import set_subnode_value
+from device.service.drivers.openconfig.Tools import xml_pretty_print, xml_to_dict, xml_to_file
+from device.service.drivers.openconfig.templates import ALL_RESOURCE_KEYS, compose_config, get_filter, parse
+
+#logging.getLogger('ncclient.transport.ssh').setLevel(logging.WARNING)
+
+LOGGER = logging.getLogger(__name__)
+
+#def do_sampling(resource_key : str, out_samples : queue.Queue):
+#    out_samples.put_nowait((datetime.timestamp(datetime.utcnow()), resource_key, random.random()))
+
+class OpenConfigDriver(_Driver):
+    def __init__(self, address : str, port : int, **settings) -> None: # pylint: disable=super-init-not-called
+        self.__address = address
+        self.__port = int(port)
+        self.__settings = settings
+        self.__lock = threading.Lock()
+        #self.__initial = TreeNode('.')
+        #self.__running = TreeNode('.')
+        self.__started = threading.Event()
+        self.__terminate = threading.Event()
+        self.__netconf_manager : Manager = None
+        #self.__scheduler = BackgroundScheduler(daemon=True) # scheduler used to emulate sampling events
+        #self.__scheduler.configure(
+        #    jobstores = {'default': MemoryJobStore()},
+        #    executors = {'default': ThreadPoolExecutor(max_workers=1)},
+        #    job_defaults = {'coalesce': False, 'max_instances': 3},
+        #    timezone=pytz.utc)
+        #self.__out_samples = queue.Queue()
+
+    def Connect(self) -> bool:
+        with self.__lock:
+            if self.__started.is_set(): return True
+            username = self.__settings.get('username')
+            password = self.__settings.get('password')
+            timeout = int(self.__settings.get('timeout', 120))
+            session = connect_ssh(
+                host=self.__address, port=self.__port, username=username, password=password)
+            self.__netconf_manager = Manager(session, timeout=timeout)
+            self.__netconf_manager.set_logger_level(logging.DEBUG)
+            # Connect triggers activation of sampling events that will be scheduled based on subscriptions
+            #self.__scheduler.start()
+            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
+            # Disconnect triggers deactivation of sampling events
+            #self.__scheduler.shutdown()
+            self.__netconf_manager.close_session()
+            return True
+
+    def GetInitialConfig(self) -> List[Tuple[str, Any]]:
+        with self.__lock:
+            return []
+
+    def GetConfig(self, resource_keys : List[str] = []) -> List[Tuple[str, Union[Any, None, Exception]]]:
+        chk_type('resources', resource_keys, list)
+        results = []
+        with self.__lock:
+            if len(resource_keys) == 0: resource_keys = ALL_RESOURCE_KEYS
+            for i,resource_key in enumerate(resource_keys):
+                str_resource_name = 'resource_key[#{:d}]'.format(i)
+                try:
+                    chk_string(str_resource_name, resource_key, allow_empty=False)
+                    str_filter = get_filter(resource_key)
+                    if str_filter is None: str_filter = resource_key
+                    xml_data = self.__netconf_manager.get(filter=str_filter).data_ele
+                    if isinstance(xml_data, Exception): raise xml_data
+                    results.extend(parse(resource_key, xml_data))
+                except Exception as e: # pylint: disable=broad-except
+                    LOGGER.exception('Exception retrieving {:s}: {:s}'.format(str_resource_name, str(resource_key)))
+                    results.append((resource_key, e)) # if validation fails, store the exception
+        return results
+
+    #def GetResource(self, endpoint_uuid : str) -> Optional[str]:
+    #    chk_string('endpoint_uuid', endpoint_uuid)
+    #    return {
+    #        #'key': 'value',
+    #    }.get(endpoint_uuid)
+
+    def SetConfig(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]:
+        chk_type('resources', resources, list)
+        if len(resources) == 0: return []
+        results = []
+        with self.__lock:
+            for i,resource in enumerate(resources):
+                str_resource_name = 'resources[#{:d}]'.format(i)
+                try:
+                    #LOGGER.info('resource = {:s}'.format(str(resource)))
+                    chk_type(str_resource_name, resource, (list, tuple))
+                    chk_length(str_resource_name, resource, min_length=2, max_length=2)
+                    resource_key,resource_value = resource
+                    chk_string(str_resource_name + '.key', resource_key, allow_empty=False)
+                    str_config_message = compose_config(resource_key, resource_value)
+                    if str_config_message is None: raise UnsupportedResourceKeyException(resource_key)
+                    #LOGGER.info('str_config_message = {:s}'.format(str(str_config_message)))
+                    self.__netconf_manager.edit_config(str_config_message, target='running')
+                    results.append(True)
+                except Exception as e: # pylint: disable=broad-except
+                    LOGGER.exception('Exception setting {:s}: {:s}'.format(str_resource_name, str(resource)))
+                    results.append(e) # if validation fails, store the exception
+        return results
+
+    def DeleteConfig(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]:
+        chk_type('resources', resources, list)
+        if len(resources) == 0: return []
+        results = []
+        with self.__lock:
+            for i,resource in enumerate(resources):
+                str_resource_name = 'resources[#{:d}]'.format(i)
+                try:
+                    #LOGGER.info('resource = {:s}'.format(str(resource)))
+                    chk_type(str_resource_name, resource, (list, tuple))
+                    chk_length(str_resource_name, resource, min_length=2, max_length=2)
+                    resource_key,resource_value = resource
+                    chk_string(str_resource_name + '.key', resource_key, allow_empty=False)
+                    str_config_message = compose_config(resource_key, resource_value, delete=True)
+                    if str_config_message is None: raise UnsupportedResourceKeyException(resource_key)
+                    #LOGGER.info('str_config_message = {:s}'.format(str(str_config_message)))
+                    self.__netconf_manager.edit_config(str_config_message, target='running')
+                    results.append(True)
+                except Exception as e: # pylint: disable=broad-except
+                    LOGGER.exception('Exception deleting {:s}: {:s}'.format(str_resource_name, str(resource_key)))
+                    results.append(e) # if validation fails, store the exception
+        return results
+
+#    def SubscribeState(self, subscriptions : List[Tuple[str, float, float]]) -> List[Union[bool, Exception]]:
+#        chk_type('subscriptions', subscriptions, list)
+#        if len(subscriptions) == 0: return []
+#        results = []
+#        resolver = anytree.Resolver(pathattr='name')
+#        with self.__lock:
+#            for i,subscription in enumerate(subscriptions):
+#                str_subscription_name = 'subscriptions[#{:d}]'.format(i)
+#                try:
+#                    chk_type(str_subscription_name, subscription, (list, tuple))
+#                    chk_length(str_subscription_name, subscription, min_length=3, max_length=3)
+#                    resource_key,sampling_duration,sampling_interval = subscription
+#                    chk_string(str_subscription_name + '.resource_key', resource_key, allow_empty=False)
+#                    resource_path = resource_key.split('/')
+#                    chk_float(str_subscription_name + '.sampling_duration', sampling_duration, min_value=0)
+#                    chk_float(str_subscription_name + '.sampling_interval', sampling_interval, min_value=0)
+#                except Exception as e: # pylint: disable=broad-except
+#                    LOGGER.exception('Exception validating {:s}: {:s}'.format(str_subscription_name, str(resource_key)))
+#                    results.append(e) # if validation fails, store the exception
+#                    continue
+#
+#                start_date,end_date = None,None
+#                if sampling_duration <= 1.e-12:
+#                    start_date = datetime.utcnow()
+#                    end_date = start_date + timedelta(seconds=sampling_duration)
+#
+#                job_id = 'k={:s}/d={:f}/i={:f}'.format(resource_key, sampling_duration, sampling_interval)
+#                job = self.__scheduler.add_job(
+#                    do_sampling, args=(resource_key, self.__out_samples), kwargs={},
+#                    id=job_id, trigger='interval', seconds=sampling_interval,
+#                    start_date=start_date, end_date=end_date, timezone=pytz.utc)
+#
+#                subscription_path = resource_path + ['{:.3f}:{:.3f}'.format(sampling_duration, sampling_interval)]
+#                set_subnode_value(resolver, self.__running, subscription_path, job)
+#                results.append(True)
+#        return results
+#
+#    def UnsubscribeState(self, subscriptions : List[Tuple[str, float, float]]) -> List[Union[bool, Exception]]:
+#        chk_type('subscriptions', subscriptions, list)
+#        if len(subscriptions) == 0: return []
+#        results = []
+#        resolver = anytree.Resolver(pathattr='name')
+#        with self.__lock:
+#            for i,resource in enumerate(subscriptions):
+#                str_subscription_name = 'resources[#{:d}]'.format(i)
+#                try:
+#                    chk_type(str_subscription_name, resource, (list, tuple))
+#                    chk_length(str_subscription_name, resource, min_length=3, max_length=3)
+#                    resource_key,sampling_duration,sampling_interval = resource
+#                    chk_string(str_subscription_name + '.resource_key', resource_key, allow_empty=False)
+#                    resource_path = resource_key.split('/')
+#                    chk_float(str_subscription_name + '.sampling_duration', sampling_duration, min_value=0)
+#                    chk_float(str_subscription_name + '.sampling_interval', sampling_interval, min_value=0)
+#                except Exception as e: # pylint: disable=broad-except
+#                    LOGGER.exception('Exception validating {:s}: {:s}'.format(str_subscription_name, str(resource_key)))
+#                    results.append(e) # if validation fails, store the exception
+#                    continue
+#
+#                subscription_path = resource_path + ['{:.3f}:{:.3f}'.format(sampling_duration, sampling_interval)]
+#                subscription_node = get_subnode(resolver, self.__running, subscription_path)
+#
+#                # if not found, resource_node is None
+#                if subscription_node is None:
+#                    results.append(False)
+#                    continue
+#
+#                job : Job = getattr(subscription_node, 'value', None)
+#                if job is None or not isinstance(job, Job):
+#                    raise Exception('Malformed subscription node or wrong resource key: {:s}'.format(str(resource)))
+#                job.remove()
+#
+#                parent = subscription_node.parent
+#                children = list(parent.children)
+#                children.remove(subscription_node)
+#                parent.children = tuple(children)
+#
+#                results.append(True)
+#        return results
+#
+#    def GetState(self, blocking=False) -> Iterator[Tuple[str, Any]]:
+#        while not self.__terminate.is_set():
+#            try:
+#                sample = self.__out_samples.get(block=blocking, timeout=0.1)
+#            except queue.Empty:
+#                if blocking: continue
+#                return
+#            if sample is None: continue
+#            yield sample
diff --git a/src/device/service/drivers/openconfig/Tools.py b/src/device/service/drivers/openconfig/Tools.py
new file mode 100644
index 0000000000000000000000000000000000000000..230a6e3c33586c5d1771a6da2fd9dabc533e9538
--- /dev/null
+++ b/src/device/service/drivers/openconfig/Tools.py
@@ -0,0 +1,11 @@
+import xml.dom.minidom, xmltodict
+
+def xml_pretty_print(data : str):
+    return xml.dom.minidom.parseString(data).toprettyxml()
+
+def xml_to_file(data : str, file_path : str) -> None:
+    with open(file_path, mode='w', encoding='UTF-8') as f:
+        f.write(xml_pretty_print(data))
+
+def xml_to_dict(data : str):
+    return xmltodict.parse(data)
diff --git a/src/device/service/drivers/openconfig/templates/EndPoints.py b/src/device/service/drivers/openconfig/templates/EndPoints.py
new file mode 100644
index 0000000000000000000000000000000000000000..4908c818374ca3b94fb77c5d5cd88d97d3c881b5
--- /dev/null
+++ b/src/device/service/drivers/openconfig/templates/EndPoints.py
@@ -0,0 +1,27 @@
+import logging, lxml.etree as ET
+from typing import Any, Dict, List, Tuple
+from .Namespace import NAMESPACES
+from .Tools import add_value_from_tag
+
+LOGGER = logging.getLogger(__name__)
+
+XPATH_PORTS = "//ocp:components/ocp:component/ocp:state[ocp:type='PORT']/.."
+
+def parse(xml_data : ET.Element) -> List[Tuple[str, Dict[str, Any]]]:
+    response = []
+    for xml_component in xml_data.xpath(XPATH_PORTS, namespaces=NAMESPACES):
+        #LOGGER.info('xml_component = {:s}'.format(str(ET.tostring(xml_component))))
+
+        endpoint = {}
+
+        component_name = xml_component.find('ocp:name', namespaces=NAMESPACES)
+        if component_name is None or component_name.text is None: continue
+        add_value_from_tag(endpoint, 'name', component_name)
+
+        component_type = xml_component.find(
+            'ocpp:port/ocpp:breakout-mode/ocpp:state/ocpp:channel-speed', namespaces=NAMESPACES)
+        add_value_from_tag(endpoint, 'type', component_type)
+
+        if len(endpoint) == 0: continue
+        response.append(('endpoint[{:s}]'.format(endpoint['name']), endpoint))
+    return response
diff --git a/src/device/service/drivers/openconfig/templates/Interfaces.py b/src/device/service/drivers/openconfig/templates/Interfaces.py
new file mode 100644
index 0000000000000000000000000000000000000000..0bbd70d04a3c093dcdbad4966be3030dbc8ba0fb
--- /dev/null
+++ b/src/device/service/drivers/openconfig/templates/Interfaces.py
@@ -0,0 +1,65 @@
+import logging, lxml.etree as ET
+from typing import Any, Dict, List, Tuple
+from .Namespace import NAMESPACES
+from .Tools import add_value_from_collection, add_value_from_tag
+
+LOGGER = logging.getLogger(__name__)
+
+XPATH_INTERFACES    = "//oci:interfaces/oci:interface"
+XPATH_SUBINTERFACES = ".//oci:subinterfaces/oci:subinterface"
+XPATH_IPV4ADDRESSES = ".//ociip:ipv4/ociip:addresses/ociip:address"
+
+def parse(xml_data : ET.Element) -> List[Tuple[str, Dict[str, Any]]]:
+    response = []
+    for xml_interface in xml_data.xpath(XPATH_INTERFACES, namespaces=NAMESPACES):
+        LOGGER.info('xml_interface = {:s}'.format(str(ET.tostring(xml_interface))))
+
+        interface = {}
+
+        interface_name = xml_interface.find('oci:name', namespaces=NAMESPACES)
+        if interface_name is None or interface_name.text is None: continue
+        add_value_from_tag(interface, 'name', interface_name)
+
+        interface_type = xml_interface.find('oci:config/oci:type', namespaces=NAMESPACES)
+        add_value_from_tag(interface, 'type', interface_type)
+
+        for xml_subinterface in xml_interface.xpath(XPATH_SUBINTERFACES, namespaces=NAMESPACES):
+            LOGGER.info('xml_subinterface = {:s}'.format(str(ET.tostring(xml_subinterface))))
+
+            subinterface = {}
+
+            subinterface_index = xml_subinterface.find('oci:index', namespaces=NAMESPACES)
+            if subinterface_index is None or subinterface_index.text is None: continue
+            add_value_from_tag(subinterface, 'index', subinterface_index)
+
+            vlan_id = xml_subinterface.find('ocv:vlan/ocv:config/ocv:vlan-id', namespaces=NAMESPACES)
+            add_value_from_tag(subinterface, 'vlan_id', vlan_id, cast=int)
+
+            ipv4_addresses = []
+            for xml_ipv4_address in xml_subinterface.xpath(XPATH_IPV4ADDRESSES, namespaces=NAMESPACES):
+                LOGGER.info('xml_ipv4_address = {:s}'.format(str(ET.tostring(xml_ipv4_address))))
+
+                ipv4_address = {}
+
+                origin = xml_ipv4_address.find('ociip:state/ociip:origin', namespaces=NAMESPACES)
+                add_value_from_tag(ipv4_address, 'origin', origin)
+
+                address = xml_ipv4_address.find('ociip:state/ociip:ip', namespaces=NAMESPACES)
+                add_value_from_tag(ipv4_address, 'ip', address)
+
+                prefix = xml_ipv4_address.find('ociip:state/ociip:prefix-length', namespaces=NAMESPACES)
+                add_value_from_tag(ipv4_address, 'prefix_length', prefix)
+
+                if len(ipv4_address) == 0: continue
+                ipv4_addresses.append(ipv4_address)
+
+            add_value_from_collection(subinterface, 'ipv4_addresses', ipv4_addresses)
+
+            if len(subinterface) == 0: continue
+            resource_key = 'interface[{:s}]/subinterface[{:s}]'.format(interface['name'], subinterface['index'])
+            response.append((resource_key, subinterface))
+
+        if len(interface) == 0: continue
+        response.append(('interface[{:s}]'.format(interface['name']), interface))
+
+    return response
diff --git a/src/device/service/drivers/openconfig/templates/Namespace.py b/src/device/service/drivers/openconfig/templates/Namespace.py
new file mode 100644
index 0000000000000000000000000000000000000000..ff4b7c9c747b4efa872ba177e6e6a4fd7f6203b4
--- /dev/null
+++ b/src/device/service/drivers/openconfig/templates/Namespace.py
@@ -0,0 +1,23 @@
+
+NAMESPACE_NETCONF       = 'urn:ietf:params:xml:ns:netconf:base:1.0'
+
+NAMESPACE_INTERFACES             = 'http://openconfig.net/yang/interfaces'
+NAMESPACE_INTERFACES_IP          = 'http://openconfig.net/yang/interfaces/ip'
+NAMESPACE_NETWORK_INSTANCE       = 'http://openconfig.net/yang/network-instance'
+NAMESPACE_NETWORK_INSTANCE_TYPES = 'http://openconfig.net/yang/network-instance-types'
+NAMESPACE_OPENCONFIG_TYPES       = 'http://openconfig.net/yang/openconfig-types'
+NAMESPACE_PLATFORM               = 'http://openconfig.net/yang/platform'
+NAMESPACE_PLATFORM_PORT          = 'http://openconfig.net/yang/platform/port'
+NAMESPACE_VLAN                   = 'http://openconfig.net/yang/vlan'
+
+NAMESPACES = {
+    'nc'   : NAMESPACE_NETCONF,
+    'oci'  : NAMESPACE_INTERFACES,
+    'ociip': NAMESPACE_INTERFACES_IP,
+    'ocni' : NAMESPACE_NETWORK_INSTANCE,
+    'ocnit': NAMESPACE_NETWORK_INSTANCE_TYPES,
+    'ococt': NAMESPACE_OPENCONFIG_TYPES,
+    'ocp'  : NAMESPACE_PLATFORM,
+    'ocpp' : NAMESPACE_PLATFORM_PORT,
+    'ocv'  : NAMESPACE_VLAN,
+}
diff --git a/src/device/service/drivers/openconfig/templates/NetworkInstances.py b/src/device/service/drivers/openconfig/templates/NetworkInstances.py
new file mode 100644
index 0000000000000000000000000000000000000000..06bf5cd0f38aaf8c61f2919c25891e15f9ac4343
--- /dev/null
+++ b/src/device/service/drivers/openconfig/templates/NetworkInstances.py
@@ -0,0 +1,35 @@
+import logging, lxml.etree as ET
+from typing import Any, Dict, List, Tuple
+from .Namespace import NAMESPACES
+from .Tools import add_value_from_collection, add_value_from_tag
+
+LOGGER = logging.getLogger(__name__)
+
+XPATH_NETWORK_INSTANCES = "//ocni:network-instances/ocni:network-instance"
+
+def parse(xml_data : ET.Element) -> List[Tuple[str, Dict[str, Any]]]:
+    response = []
+    for xml_network_instance in xml_data.xpath(XPATH_NETWORK_INSTANCES, namespaces=NAMESPACES):
+        #LOGGER.info('xml_network_instance = {:s}'.format(str(ET.tostring(xml_network_instance))))
+
+        network_instance = {}
+
+        ni_name = xml_network_instance.find('ocni:name', namespaces=NAMESPACES)
+        if ni_name is None or ni_name.text is None: continue
+        add_value_from_tag(network_instance, 'name', ni_name)
+
+        ni_type = xml_network_instance.find('ocni:config/ocni:type', namespaces=NAMESPACES)
+        add_value_from_tag(network_instance, 'type', ni_type)
+
+        ni_router_id = xml_network_instance.find('ocni:config/ocni:router-id', namespaces=NAMESPACES)
+        add_value_from_tag(network_instance, 'router_id', ni_router_id)
+
+        ni_route_dist = xml_network_instance.find('ocni:config/ocni:route-distinguisher', namespaces=NAMESPACES)
+        add_value_from_tag(network_instance, 'route_distinguisher', ni_route_dist)
+
+        ni_address_families = []
+        add_value_from_collection(network_instance, 'address_families', ni_address_families)
+
+        if len(network_instance) == 0: continue
+        response.append(('network_instance[{:s}]'.format(network_instance['name']), network_instance))
+    return response
diff --git a/src/device/service/drivers/openconfig/templates/Tools.py b/src/device/service/drivers/openconfig/templates/Tools.py
new file mode 100644
index 0000000000000000000000000000000000000000..e95167dc56dfd323b6fe88a716573a4246305df0
--- /dev/null
+++ b/src/device/service/drivers/openconfig/templates/Tools.py
@@ -0,0 +1,12 @@
+import lxml.etree as ET
+from typing import Collection, Dict
+
+def add_value_from_tag(target : Dict, field_name: str, field_value : ET.Element, cast=None) -> None:
+    if field_value is None or field_value.text is None: return
+    field_value = field_value.text
+    if cast is not None: field_value = cast(field_value)
+    target[field_name] = field_value
+
+def add_value_from_collection(target : Dict, field_name: str, field_value : Collection) -> None:
+    if field_value is None or len(field_value) == 0: return
+    target[field_name] = field_value
diff --git a/src/device/service/drivers/openconfig/templates/__init__.py b/src/device/service/drivers/openconfig/templates/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..c1d1450568ac6e7efc7265b434de8f6ebabd61bc
--- /dev/null
+++ b/src/device/service/drivers/openconfig/templates/__init__.py
@@ -0,0 +1,47 @@
+import json, logging, lxml.etree as ET, re
+from typing import Any, Dict
+from jinja2 import Environment, PackageLoader, select_autoescape
+from device.service.driver_api._Driver import RESOURCE_ENDPOINTS, RESOURCE_INTERFACES, RESOURCE_NETWORK_INSTANCES
+from .EndPoints import parse as parse_endpoints
+from .Interfaces import parse as parse_interfaces
+from .NetworkInstances import parse as parse_network_instances
+
+ALL_RESOURCE_KEYS = [
+    RESOURCE_ENDPOINTS,
+    RESOURCE_INTERFACES,
+    RESOURCE_NETWORK_INSTANCES,
+]
+
+RESOURCE_KEY_MAPPINGS = {
+    RESOURCE_ENDPOINTS        : 'component',
+    RESOURCE_INTERFACES       : 'interface',
+    RESOURCE_NETWORK_INSTANCES: 'network_instance',
+}
+
+RESOURCE_PARSERS = {
+    'component'       : parse_endpoints,
+    'interface'       : parse_interfaces,
+    'network_instance': parse_network_instances,
+}
+
+LOGGER = logging.getLogger(__name__)
+RE_REMOVE_FILTERS = re.compile(r'\[[^\]]+\]')
+JINJA_ENV = Environment(loader=PackageLoader('device.service.drivers.openconfig'), autoescape=select_autoescape())
+
+def get_filter(resource_key : str):
+    resource_key = RESOURCE_KEY_MAPPINGS.get(resource_key, resource_key)
+    template_name = '{:s}/get.xml'.format(RE_REMOVE_FILTERS.sub('', resource_key))
+    template = JINJA_ENV.get_template(template_name)
+    return '<filter>{:s}</filter>'.format(template.render())
+
+def parse(resource_key : str, xml_data : ET.Element):
+    parser = RESOURCE_PARSERS.get(RESOURCE_KEY_MAPPINGS.get(resource_key, resource_key))
+    if parser is None: return [(resource_key, xml_data)]
+    return parser(xml_data)
+
+def compose_config(resource_key : str, resource_value : str, delete : bool = False) -> str:
+    template_name = '{:s}/edit_config.xml'.format(RE_REMOVE_FILTERS.sub('', resource_key))
+    template = JINJA_ENV.get_template(template_name)
+    data : Dict[str, Any] = json.loads(resource_value)
+    operation = 'delete' if delete else 'merge'
+    return '<config>{:s}</config>'.format(template.render(**data, operation=operation))
diff --git a/src/device/service/drivers/openconfig/templates/component/get.xml b/src/device/service/drivers/openconfig/templates/component/get.xml
new file mode 100644
index 0000000000000000000000000000000000000000..aa25ed1e3b11e0c324b361eb52d064dac87a64c5
--- /dev/null
+++ b/src/device/service/drivers/openconfig/templates/component/get.xml
@@ -0,0 +1,3 @@
+<components xmlns="http://openconfig.net/yang/platform">
+    <component/>
+</components>
diff --git a/src/device/service/drivers/openconfig/templates/interface/edit_config.xml b/src/device/service/drivers/openconfig/templates/interface/edit_config.xml
new file mode 100644
index 0000000000000000000000000000000000000000..ae29586a607b8ffd25e61c0aa9056109aacd3cb9
--- /dev/null
+++ b/src/device/service/drivers/openconfig/templates/interface/edit_config.xml
@@ -0,0 +1,12 @@
+<interfaces xmlns="http://openconfig.net/yang/interfaces">
+    <interface{% if operation is defined %} xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" nc:operation="{{operation}}"{% endif %}>
+        <name>{{name}}</name>
+        {% if operation is not defined or operation != 'delete' %}
+        <config>
+            <name>{{name}}</name>
+            <description>{{description}}</description>
+            <mtu>{{mtu}}</mtu>
+        </config>
+        {% endif %}
+    </interface>
+</interfaces>
diff --git a/src/device/service/drivers/openconfig/templates/interface/get.xml b/src/device/service/drivers/openconfig/templates/interface/get.xml
new file mode 100644
index 0000000000000000000000000000000000000000..c0867655bd325ee9c3cdd74077ac905e36808d5f
--- /dev/null
+++ b/src/device/service/drivers/openconfig/templates/interface/get.xml
@@ -0,0 +1,3 @@
+<interfaces xmlns="http://openconfig.net/yang/interfaces">
+    <interface/>
+</interfaces>
diff --git a/src/device/service/drivers/openconfig/templates/interface/subinterface/edit_config.xml b/src/device/service/drivers/openconfig/templates/interface/subinterface/edit_config.xml
new file mode 100644
index 0000000000000000000000000000000000000000..bdc698eeffd626076f6f62e576fe6ef916361412
--- /dev/null
+++ b/src/device/service/drivers/openconfig/templates/interface/subinterface/edit_config.xml
@@ -0,0 +1,22 @@
+<interfaces xmlns="http://openconfig.net/yang/interfaces">
+    <interface>
+        <name>{{name}}</name>
+        {% if operation is not defined or operation != 'delete' %}
+        <config>
+            <name>{{name}}</name>
+            <description>{{description}}</description>
+            <mtu>{{mtu}}</mtu>
+        </config>
+        {% endif %}
+        <subinterfaces>
+            <subinterface{% if operation is defined %} xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" nc:operation="{{operation}}"{% endif %}>
+                <index>{{index}}</index>
+                {% if operation is not defined or operation != 'delete' %}
+                <config>
+                    <index>{{index}}</index>
+                </config>
+                {% endif %}
+            </subinterface>
+        </subinterfaces>
+    </interface>
+</interfaces>
diff --git a/src/device/service/drivers/openconfig/templates/network_instance/edit_config.xml b/src/device/service/drivers/openconfig/templates/network_instance/edit_config.xml
new file mode 100644
index 0000000000000000000000000000000000000000..fab1fdc61f0b6add9c915e914f830270d9875a0a
--- /dev/null
+++ b/src/device/service/drivers/openconfig/templates/network_instance/edit_config.xml
@@ -0,0 +1,16 @@
+<network-instances xmlns="http://openconfig.net/yang/network-instance">
+    <network-instance{% if operation is defined %} xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" nc:operation="{{operation}}"{% endif %}>
+        <name>{{name}}</name>
+        {% if operation is not defined or operation != 'delete' %}
+        <config>
+            <name>{{name}}</name>
+            <type xmlns:oc-ni-types="http://openconfig.net/yang/network-instance-types">oc-ni-types:{{type}}</type>
+            {% for address_family in address_families %}
+            <enabled-address-families xmlns:oc-types="http://openconfig.net/yang/openconfig-types">oc-types:{{address_family}}</enabled-address-families>
+            {% endfor %}
+            <router-id>{{router_id}}</router-id>
+            <route-distinguisher>{{route_distinguisher}}</route-distinguisher>
+        </config>
+        {% endif %}
+    </network-instance>
+</network-instances>
diff --git a/src/device/service/drivers/openconfig/templates/network_instance/get.xml b/src/device/service/drivers/openconfig/templates/network_instance/get.xml
new file mode 100644
index 0000000000000000000000000000000000000000..9a439926a782cae27cc6aea347454215492ba323
--- /dev/null
+++ b/src/device/service/drivers/openconfig/templates/network_instance/get.xml
@@ -0,0 +1,3 @@
+<network-instances xmlns="http://openconfig.net/yang/network-instance">
+    <network-instance/>
+</network-instances>
diff --git a/src/device/service/drivers/openconfig/templates/network_instance/interface/edit_config.xml b/src/device/service/drivers/openconfig/templates/network_instance/interface/edit_config.xml
new file mode 100644
index 0000000000000000000000000000000000000000..51f2d4318fbdd2d6da120851a72a6edafe173c73
--- /dev/null
+++ b/src/device/service/drivers/openconfig/templates/network_instance/interface/edit_config.xml
@@ -0,0 +1,16 @@
+<network-instances xmlns="http://openconfig.net/yang/network-instance">
+    <network-instance>
+        <name>{{name}}</name>
+        <interfaces>
+            <interface{% if operation is defined %} xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" nc:operation="{{operation}}"{% endif %}>
+                <id>{{id}}</id>
+                {% if operation is not defined or operation != 'delete' %}
+                <config>
+                    <id>{{id}}</id>
+                    <interface>{{id}}</interface>
+                </config>
+                {% endif %}
+            </interface>
+        </interfaces>
+    </network-instance>
+</network-instances>
diff --git a/src/device/service/drivers/transport_api/TransportApiDriver.py b/src/device/service/drivers/transport_api/TransportApiDriver.py
new file mode 100644
index 0000000000000000000000000000000000000000..f20173dd022a517d1e5630dc23c3455b0ed3c710
--- /dev/null
+++ b/src/device/service/drivers/transport_api/TransportApiDriver.py
@@ -0,0 +1,9 @@
+import logging
+from device.service.driver_api._Driver import _Driver
+
+LOGGER = logging.getLogger(__name__)
+
+# TODO: Implement TransportAPI Driver
+
+class TransportApiDriver(_Driver):
+    pass
diff --git a/src/device/service/drivers/transport_api/__init__.py b/src/device/service/drivers/transport_api/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/device/tests/.gitignore b/src/device/tests/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..067c7b77db596a97883a03426735b6ede9c6fa48
--- /dev/null
+++ b/src/device/tests/.gitignore
@@ -0,0 +1,2 @@
+# Add here your files containing confidential testbed details such as IP addresses, ports, usernames, passwords, etc.
+Device_OpenConfig_Infinera.py
diff --git a/src/device/tests/CommonObjects.py b/src/device/tests/CommonObjects.py
new file mode 100644
index 0000000000000000000000000000000000000000..2b51dcd2322e70dff9a5229e65e6a220708a834f
--- /dev/null
+++ b/src/device/tests/CommonObjects.py
@@ -0,0 +1,21 @@
+from copy import deepcopy
+from common.Constants import DEFAULT_CONTEXT_UUID, DEFAULT_TOPOLOGY_UUID
+
+# use "deepcopy" to prevent propagating forced changes during tests
+
+CONTEXT_ID = {'context_uuid': {'uuid': DEFAULT_CONTEXT_UUID}}
+CONTEXT = {
+    'context_id': deepcopy(CONTEXT_ID),
+    'topology_ids': [],
+    'service_ids': [],
+}
+
+TOPOLOGY_ID = {
+    'context_id': deepcopy(CONTEXT_ID),
+    'topology_uuid': {'uuid': DEFAULT_TOPOLOGY_UUID},
+}
+TOPOLOGY = {
+    'topology_id': deepcopy(TOPOLOGY_ID),
+    'device_ids': [],
+    'link_ids': [],
+}
diff --git a/src/device/tests/Device_Emulated.py b/src/device/tests/Device_Emulated.py
new file mode 100644
index 0000000000000000000000000000000000000000..155383b49fd12d780e901db1aa2614a55d4e5e14
--- /dev/null
+++ b/src/device/tests/Device_Emulated.py
@@ -0,0 +1,47 @@
+from copy import deepcopy
+from device.proto.context_pb2 import DeviceDriverEnum, DeviceOperationalStatusEnum
+from .Tools import config_rule_set, config_rule_delete
+
+# use "deepcopy" to prevent propagating forced changes during tests
+
+DEVICE_EMU_UUID           = 'EMULARED'
+DEVICE_EMU_TYPE           = 'emulated'
+DEVICE_EMU_ADDRESS        = '127.0.0.1'
+DEVICE_EMU_PORT           = '0'
+DEVICE_EMU_DRIVERS        = [DeviceDriverEnum.DEVICEDRIVER_UNDEFINED]
+
+DEVICE_EMU_ID = {'device_uuid': {'uuid': DEVICE_EMU_UUID}}
+DEVICE_EMU = {
+    'device_id': deepcopy(DEVICE_EMU_ID),
+    'device_type': DEVICE_EMU_TYPE,
+    'device_config': {'config_rules': []},
+    'device_operational_status': DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_DISABLED,
+    'device_drivers': DEVICE_EMU_DRIVERS,
+    'device_endpoints': [],
+}
+
+DEVICE_EMU_CONNECT_RULES = [
+    config_rule_set('_connect/address',  DEVICE_EMU_ADDRESS ),
+    config_rule_set('_connect/port',     DEVICE_EMU_PORT    ),
+]
+
+DEVICE_EMU_CONFIG_RULES = [
+    config_rule_set('dev/rsrc1/value', 'value1'),
+    config_rule_set('dev/rsrc2/value', 'value2'),
+    config_rule_set('dev/rsrc3/value', 'value3'),
+]
+
+DEVICE_EMU_RECONFIG_RULES = [
+    config_rule_delete('dev/rsrc1/value', ''),
+    config_rule_set   ('dev/rsrc10/value', 'value10'),
+    config_rule_set   ('dev/rsrc11/value', 'value11'),
+    config_rule_set   ('dev/rsrc12/value', 'value12'),
+]
+
+DEVICE_EMU_DECONFIG_RULES = [
+    config_rule_delete('dev/rsrc2/value', 'value2'),
+    config_rule_delete('dev/rsrc3/value', 'value3'),
+    config_rule_delete('dev/rsrc10/value', 'value10'),
+    config_rule_delete('dev/rsrc11/value', 'value11'),
+    config_rule_delete('dev/rsrc12/value', 'value12'),
+]
diff --git a/src/device/tests/Device_OpenConfig_Template.py b/src/device/tests/Device_OpenConfig_Template.py
new file mode 100644
index 0000000000000000000000000000000000000000..73b6d4f55e8ffb08eb4c2e0badf8aa8d012c0d2b
--- /dev/null
+++ b/src/device/tests/Device_OpenConfig_Template.py
@@ -0,0 +1,34 @@
+from copy import deepcopy
+from device.proto.context_pb2 import DeviceDriverEnum, DeviceOperationalStatusEnum
+from .Tools import config_rule_set, config_rule_delete
+
+# use "deepcopy" to prevent propagating forced changes during tests
+
+DEVICE_OC_UUID           = 'DEV2'
+DEVICE_OC_TYPE           = 'packet-router'
+DEVICE_OC_ADDRESS        = '127.0.0.1'  # populate the Netconf Server IP address of the device to test
+DEVICE_OC_PORT           = '830'        # populate the Netconf Server port of the device to test
+DEVICE_OC_USERNAME       = 'username'   # populate the Netconf Server username of the device to test
+DEVICE_OC_PASSWORD       = 'password'   # populate the Netconf Server password of the device to test
+DEVICE_OC_DRIVERS        = [DeviceDriverEnum.DEVICEDRIVER_OPENCONFIG]
+
+DEVICE_OC_ID = {'device_uuid': {'uuid': DEVICE_OC_UUID}}
+DEVICE_OC = {
+    'device_id': deepcopy(DEVICE_OC_ID),
+    'device_type': DEVICE_OC_TYPE,
+    'device_config': {'config_rules': []},
+    'device_operational_status': DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_DISABLED,
+    'device_drivers': DEVICE_OC_DRIVERS,
+    'device_endpoints': [],                     # populated through the driver, leave this list empty
+}
+
+DEVICE_OC_CONNECT_RULES = [
+    config_rule_set('_connect/address',  DEVICE_OC_ADDRESS ),
+    config_rule_set('_connect/port',     DEVICE_OC_PORT    ),
+    config_rule_set('_connect/username', DEVICE_OC_USERNAME),
+    config_rule_set('_connect/password', DEVICE_OC_PASSWORD),
+]
+
+DEVICE_OC_CONFIG_RULES   = []           # populate your configuration rules to test
+
+DEVICE_OC_DECONFIG_RULES = []           # populate your deconfiguration rules to test
diff --git a/src/device/tests/Tools.py b/src/device/tests/Tools.py
new file mode 100644
index 0000000000000000000000000000000000000000..94a6d50900eb1865c69064b2e98bca0d6e91643b
--- /dev/null
+++ b/src/device/tests/Tools.py
@@ -0,0 +1,21 @@
+import json
+from copy import deepcopy
+from typing import Any, Dict, Union
+from device.proto.context_pb2 import ConfigActionEnum
+
+def config_rule(action : ConfigActionEnum, resource_key : str, resource_value : Union[str, Dict[str, Any]]):
+    if not isinstance(resource_value, str): resource_value = json.dumps(resource_value, sort_keys=True)
+    return {'action': action, 'resource_key': resource_key, 'resource_value': resource_value}
+
+def config_rule_set(resource_key : str, resource_value : Union[str, Dict[str, Any]]):
+    return config_rule(ConfigActionEnum.CONFIGACTION_SET, resource_key, resource_value)
+
+def config_rule_delete(resource_key : str, resource_value : Union[str, Dict[str, Any]]):
+    return config_rule(ConfigActionEnum.CONFIGACTION_DELETE, resource_key, resource_value)
+
+def endpoint_id(topology_id, device_id, endpoint_uuid):
+    return {'topology_id': deepcopy(topology_id), 'device_id': deepcopy(device_id),
+            'endpoint_uuid': {'uuid': endpoint_uuid}}
+
+def endpoint(topology_id, device_id, endpoint_uuid, endpoint_type):
+    return {'endpoint_id': endpoint_id(topology_id, device_id, endpoint_uuid), 'endpoint_type': endpoint_type}
diff --git a/src/device/tests/test_unitary.py b/src/device/tests/test_unitary.py
new file mode 100644
index 0000000000000000000000000000000000000000..fc9175620b7763cabc9da58756054fda9092490d
--- /dev/null
+++ b/src/device/tests/test_unitary.py
@@ -0,0 +1,394 @@
+import copy, grpc, logging, operator, os, pytest
+from typing import Tuple
+from google.protobuf.json_format import MessageToDict
+from common.orm.Database import Database
+from common.orm.Factory import get_database_backend, BackendEnum as DatabaseBackendEnum
+from common.message_broker.Factory import get_messagebroker_backend, BackendEnum as MessageBrokerBackendEnum
+from common.message_broker.MessageBroker import MessageBroker
+from context.Config import (
+    GRPC_SERVICE_PORT as CONTEXT_GRPC_SERVICE_PORT, GRPC_MAX_WORKERS as CONTEXT_GRPC_MAX_WORKERS,
+    GRPC_GRACE_PERIOD as CONTEXT_GRPC_GRACE_PERIOD)
+from context.client.ContextClient import ContextClient
+from context.proto.context_pb2 import DeviceId
+from context.service.grpc_server.ContextService import ContextService
+from device.Config import (
+    GRPC_SERVICE_PORT as DEVICE_GRPC_SERVICE_PORT, GRPC_MAX_WORKERS as DEVICE_GRPC_MAX_WORKERS,
+    GRPC_GRACE_PERIOD as DEVICE_GRPC_GRACE_PERIOD)
+from device.client.DeviceClient import DeviceClient
+from device.proto.context_pb2 import ConfigActionEnum, Context, Device, Topology
+from device.service.DeviceService import DeviceService
+#from device.service.MonitoringLoops import MonitoringLoops
+from device.service.driver_api._Driver import _Driver
+from device.service.driver_api.DriverFactory import DriverFactory
+from device.service.driver_api.DriverInstanceCache import DriverInstanceCache
+from device.service.drivers import DRIVERS
+#from monitoring.client.monitoring_client import MonitoringClient
+from .CommonObjects import CONTEXT, TOPOLOGY
+from .Device_Emulated import (
+    DEVICE_EMU, DEVICE_EMU_CONFIG_RULES, DEVICE_EMU_CONNECT_RULES, DEVICE_EMU_DECONFIG_RULES, DEVICE_EMU_ID,
+    DEVICE_EMU_RECONFIG_RULES, DEVICE_EMU_UUID)
+try:
+    from .Device_OpenConfig_Infinera import(
+        DEVICE_OC, DEVICE_OC_CONFIG_RULES, DEVICE_OC_DECONFIG_RULES, DEVICE_OC_CONNECT_RULES, DEVICE_OC_ID,
+        DEVICE_OC_UUID)
+    ENABLE_OPENCONFIG = True
+except ImportError:
+    ENABLE_OPENCONFIG = False
+    # Create a Device_OpenConfig_??.py file with the details for your device to test it and import it as follows in the
+    # try block of this import statement.
+    #   from .Device_OpenConfig_?? import(
+    #       DEVICE_OC, DEVICE_OC_CONFIG_RULES, DEVICE_OC_DECONFIG_RULES, DEVICE_OC_CONNECT_RULES, DEVICE_OC_ID,
+    #       DEVICE_OC_UUID)
+
+LOGGER = logging.getLogger(__name__)
+LOGGER.setLevel(logging.DEBUG)
+
+CONTEXT_GRPC_SERVICE_PORT = 10000 + CONTEXT_GRPC_SERVICE_PORT # avoid privileged ports
+DEVICE_GRPC_SERVICE_PORT = 10000 + DEVICE_GRPC_SERVICE_PORT # avoid privileged ports
+
+DEFAULT_REDIS_SERVICE_HOST = '127.0.0.1'
+DEFAULT_REDIS_SERVICE_PORT = 6379
+DEFAULT_REDIS_DATABASE_ID  = 0
+
+REDIS_CONFIG = {
+    'REDIS_SERVICE_HOST': os.environ.get('REDIS_SERVICE_HOST', DEFAULT_REDIS_SERVICE_HOST),
+    'REDIS_SERVICE_PORT': os.environ.get('REDIS_SERVICE_PORT', DEFAULT_REDIS_SERVICE_PORT),
+    'REDIS_DATABASE_ID' : os.environ.get('REDIS_DATABASE_ID',  DEFAULT_REDIS_DATABASE_ID ),
+}
+
+SCENARIOS = [
+    ('all_inmemory', DatabaseBackendEnum.INMEMORY, {},           MessageBrokerBackendEnum.INMEMORY, {}          ),
+    #('all_redis',    DatabaseBackendEnum.REDIS,    REDIS_CONFIG, MessageBrokerBackendEnum.REDIS,    REDIS_CONFIG),
+]
+
+@pytest.fixture(scope='session', ids=[str(scenario[0]) for scenario in SCENARIOS], params=SCENARIOS)
+def context_db_mb(request) -> Tuple[Database, MessageBroker]:
+    name,db_backend,db_settings,mb_backend,mb_settings = request.param
+    msg = 'Running scenario {:s} db_backend={:s}, db_settings={:s}, mb_backend={:s}, mb_settings={:s}...'
+    LOGGER.info(msg.format(str(name), str(db_backend.value), str(db_settings), str(mb_backend.value), str(mb_settings)))
+    _database = Database(get_database_backend(backend=db_backend, **db_settings))
+    _message_broker = MessageBroker(get_messagebroker_backend(backend=mb_backend, **mb_settings))
+    yield _database, _message_broker
+    _message_broker.terminate()
+
+@pytest.fixture(scope='session')
+def context_service(context_db_mb : Tuple[Database, MessageBroker]): # pylint: disable=redefined-outer-name
+    _service = ContextService(
+        context_db_mb[0], context_db_mb[1], port=CONTEXT_GRPC_SERVICE_PORT, max_workers=CONTEXT_GRPC_MAX_WORKERS,
+        grace_period=CONTEXT_GRPC_GRACE_PERIOD)
+    _service.start()
+    yield _service
+    _service.stop()
+
+@pytest.fixture(scope='session')
+def context_client(context_service : ContextService): # pylint: disable=redefined-outer-name
+    _client = ContextClient(address='127.0.0.1', port=CONTEXT_GRPC_SERVICE_PORT)
+    yield _client
+    _client.close()
+
+@pytest.fixture(scope='session')
+def device_service(context_client : ContextClient):  # pylint: disable=redefined-outer-name
+    _driver_factory = DriverFactory(DRIVERS)
+    _driver_instance_cache = DriverInstanceCache(_driver_factory)
+    #_monitoring_loops = MonitoringLoops(None) # TODO: replace by monitoring client
+    #_monitoring_loops.start()
+    _service = DeviceService(
+        context_client, _driver_instance_cache,
+        #_monitoring_loops,
+        port=DEVICE_GRPC_SERVICE_PORT, max_workers=DEVICE_GRPC_MAX_WORKERS, grace_period=DEVICE_GRPC_GRACE_PERIOD)
+    _service.start()
+    yield _service
+    #_monitoring_loops.stop()
+    _service.stop()
+
+@pytest.fixture(scope='session')
+def device_client(device_service : DeviceService): # pylint: disable=redefined-outer-name
+    _client = DeviceClient(address='127.0.0.1', port=DEVICE_GRPC_SERVICE_PORT)
+    yield _client
+    _client.close()
+
+def grpc_message_to_json_string(message):
+    return str(MessageToDict(
+        message, including_default_value_fields=True, preserving_proto_field_name=True, use_integers_for_enums=False))
+
+
+def test_prepare_environment(
+    context_client : ContextClient,     # pylint: disable=redefined-outer-name
+    device_client : DeviceClient,       # pylint: disable=redefined-outer-name
+    device_service : DeviceService):    # pylint: disable=redefined-outer-name
+
+    context_client.SetContext(Context(**CONTEXT))
+    context_client.SetTopology(Topology(**TOPOLOGY))
+
+
+# ----- Test Device Driver Emulated ------------------------------------------------------------------------------------
+
+def test_device_emulated_add_error_cases(
+    context_client : ContextClient,     # pylint: disable=redefined-outer-name
+    device_client : DeviceClient,       # pylint: disable=redefined-outer-name
+    device_service : DeviceService):    # pylint: disable=redefined-outer-name
+
+    with pytest.raises(grpc.RpcError) as e:
+        DEVICE_EMU_WITH_EXTRA_RULES = copy.deepcopy(DEVICE_EMU)
+        DEVICE_EMU_WITH_EXTRA_RULES['device_config']['config_rules'].extend(DEVICE_EMU_CONNECT_RULES)
+        DEVICE_EMU_WITH_EXTRA_RULES['device_config']['config_rules'].extend(DEVICE_EMU_CONFIG_RULES)
+        device_client.AddDevice(Device(**DEVICE_EMU_WITH_EXTRA_RULES))
+    assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
+    msg_head = 'device.device_config.config_rules(['
+    msg_tail = ']) is invalid; RPC method AddDevice only accepts connection Config Rules that should start '\
+               'with "_connect/" tag. Others should be configured after adding the device.'
+    except_msg = str(e.value.details())
+    assert except_msg.startswith(msg_head) and except_msg.endswith(msg_tail)
+
+
+def test_device_emulated_add_correct(
+    context_client : ContextClient,     # pylint: disable=redefined-outer-name
+    device_client : DeviceClient,       # pylint: disable=redefined-outer-name
+    device_service : DeviceService):    # pylint: disable=redefined-outer-name
+
+    DEVICE_EMU_WITH_CONNECT_RULES = copy.deepcopy(DEVICE_EMU)
+    DEVICE_EMU_WITH_CONNECT_RULES['device_config']['config_rules'].extend(DEVICE_EMU_CONNECT_RULES)
+    device_client.AddDevice(Device(**DEVICE_EMU_WITH_CONNECT_RULES))
+    driver : _Driver = device_service.driver_instance_cache.get(DEVICE_EMU_UUID) # we know the driver exists now
+    assert driver is not None
+
+
+def test_device_emulated_get(
+    context_client : ContextClient,     # pylint: disable=redefined-outer-name
+    device_client : DeviceClient,       # pylint: disable=redefined-outer-name
+    device_service : DeviceService):    # pylint: disable=redefined-outer-name
+
+    initial_config = device_client.GetInitialConfig(DeviceId(**DEVICE_EMU_ID))
+    LOGGER.info('initial_config = {:s}'.format(grpc_message_to_json_string(initial_config)))
+
+    device_data = context_client.GetDevice(DeviceId(**DEVICE_EMU_ID))
+    LOGGER.info('device_data = {:s}'.format(grpc_message_to_json_string(device_data)))
+
+
+def test_device_emulated_configure(
+    context_client : ContextClient,     # pylint: disable=redefined-outer-name
+    device_client : DeviceClient,       # pylint: disable=redefined-outer-name
+    device_service : DeviceService):    # pylint: disable=redefined-outer-name
+
+    driver : _Driver = device_service.driver_instance_cache.get(DEVICE_EMU_UUID) # we know the driver exists now
+    assert driver is not None
+
+    driver_config = sorted(driver.GetConfig(), key=operator.itemgetter(0))
+    LOGGER.info('driver_config = {:s}'.format(str(driver_config)))
+    assert len(driver_config) == 0
+
+    DEVICE_EMU_WITH_CONFIG_RULES = copy.deepcopy(DEVICE_EMU)
+    DEVICE_EMU_WITH_CONFIG_RULES['device_config']['config_rules'].extend(DEVICE_EMU_CONFIG_RULES)
+    device_client.ConfigureDevice(Device(**DEVICE_EMU_WITH_CONFIG_RULES))
+
+    driver_config = sorted(driver.GetConfig(), key=operator.itemgetter(0))
+    LOGGER.info('driver_config = {:s}'.format(str(driver_config)))
+    assert len(driver_config) == 3
+    assert driver_config[0] == ('/dev/rsrc1/value', 'value1')
+    assert driver_config[1] == ('/dev/rsrc2/value', 'value2')
+    assert driver_config[2] == ('/dev/rsrc3/value', 'value3')
+
+    device_data = context_client.GetDevice(DeviceId(**DEVICE_EMU_ID))
+    config_rules = [
+        (ConfigActionEnum.Name(config_rule.action), config_rule.resource_key, config_rule.resource_value)
+        for config_rule in device_data.device_config.config_rules
+    ]
+    LOGGER.info('device_data.device_config.config_rules = \n{:s}'.format(
+        '\n'.join(['{:s} {:s} = {:s}'.format(*config_rule) for config_rule in config_rules])))
+    for config_rule in DEVICE_EMU_CONFIG_RULES:
+        config_rule = (
+            ConfigActionEnum.Name(config_rule['action']), config_rule['resource_key'], config_rule['resource_value'])
+        assert config_rule in config_rules
+
+    # Try to reconfigure...
+
+    DEVICE_EMU_WITH_RECONFIG_RULES = copy.deepcopy(DEVICE_EMU)
+    DEVICE_EMU_WITH_RECONFIG_RULES['device_config']['config_rules'].extend(DEVICE_EMU_RECONFIG_RULES)
+    device_client.ConfigureDevice(Device(**DEVICE_EMU_WITH_RECONFIG_RULES))
+
+    driver_config = sorted(driver.GetConfig(), key=operator.itemgetter(0))
+    LOGGER.info('driver_config = {:s}'.format(str(driver_config)))
+    assert len(driver_config) == 5
+    assert driver_config[0] == ('/dev/rsrc10/value', 'value10')
+    assert driver_config[1] == ('/dev/rsrc11/value', 'value11')
+    assert driver_config[2] == ('/dev/rsrc12/value', 'value12')
+    assert driver_config[3] == ('/dev/rsrc2/value', 'value2')
+    assert driver_config[4] == ('/dev/rsrc3/value', 'value3')
+
+    device_data = context_client.GetDevice(DeviceId(**DEVICE_EMU_ID))
+    config_rules = [
+        (ConfigActionEnum.Name(config_rule.action), config_rule.resource_key, config_rule.resource_value)
+        for config_rule in device_data.device_config.config_rules
+    ]
+    LOGGER.info('device_data.device_config.config_rules = \n{:s}'.format(
+        '\n'.join(['{:s} {:s} = {:s}'.format(*config_rule) for config_rule in config_rules])))
+    final_config_rules = DEVICE_EMU_CONFIG_RULES[1:] + DEVICE_EMU_RECONFIG_RULES[1:] # remove '/dev/rsrc1/value'
+    for config_rule in final_config_rules:
+        config_rule = (
+            ConfigActionEnum.Name(config_rule['action']), config_rule['resource_key'], config_rule['resource_value'])
+        assert config_rule in config_rules
+
+
+def test_device_emulated_deconfigure(
+    context_client : ContextClient,     # pylint: disable=redefined-outer-name
+    device_client : DeviceClient,       # pylint: disable=redefined-outer-name
+    device_service : DeviceService):    # pylint: disable=redefined-outer-name
+
+    driver : _Driver = device_service.driver_instance_cache.get(DEVICE_EMU_UUID) # we know the driver exists now
+    assert driver is not None
+
+    driver_config = driver.GetConfig()
+    LOGGER.info('driver_config = {:s}'.format(str(driver_config)))
+
+    DEVICE_EMU_WITH_DECONFIG_RULES = copy.deepcopy(DEVICE_EMU)
+    DEVICE_EMU_WITH_DECONFIG_RULES['device_config']['config_rules'].extend(DEVICE_EMU_DECONFIG_RULES)
+    device_client.ConfigureDevice(Device(**DEVICE_EMU_WITH_DECONFIG_RULES))
+
+    driver_config = sorted(driver.GetConfig(), key=operator.itemgetter(0))
+    LOGGER.info('driver_config = {:s}'.format(str(driver_config)))
+    assert len(driver_config) == 0
+
+    device_data = context_client.GetDevice(DeviceId(**DEVICE_EMU_ID))
+    assert len(device_data.device_config.config_rules) == 0
+
+
+def test_device_emulated_delete(
+    context_client : ContextClient,     # pylint: disable=redefined-outer-name
+    device_client : DeviceClient,       # pylint: disable=redefined-outer-name
+    device_service : DeviceService):    # pylint: disable=redefined-outer-name
+
+    device_client.DeleteDevice(DeviceId(**DEVICE_EMU_ID))
+    driver : _Driver = device_service.driver_instance_cache.get(DEVICE_EMU_UUID, {})
+    assert driver is None
+
+
+# ----- Test Device Driver OpenConfig ----------------------------------------------------------------------------------
+
+def test_device_openconfig_add_error_cases(
+    context_client : ContextClient,     # pylint: disable=redefined-outer-name
+    device_client : DeviceClient,       # pylint: disable=redefined-outer-name
+    device_service : DeviceService):    # pylint: disable=redefined-outer-name
+
+    if not ENABLE_OPENCONFIG: return # if there is no device to test against, asusme test is silently passed.
+
+    with pytest.raises(grpc.RpcError) as e:
+        DEVICE_OC_WITH_EXTRA_RULES = copy.deepcopy(DEVICE_OC)
+        DEVICE_OC_WITH_EXTRA_RULES['device_config']['config_rules'].extend(DEVICE_OC_CONNECT_RULES)
+        DEVICE_OC_WITH_EXTRA_RULES['device_config']['config_rules'].extend(DEVICE_OC_CONFIG_RULES)
+        device_client.AddDevice(Device(**DEVICE_OC_WITH_EXTRA_RULES))
+    assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
+    msg_head = 'device.device_config.config_rules(['
+    msg_tail = ']) is invalid; RPC method AddDevice only accepts connection Config Rules that should start '\
+               'with "_connect/" tag. Others should be configured after adding the device.'
+    except_msg = str(e.value.details())
+    assert except_msg.startswith(msg_head) and except_msg.endswith(msg_tail)
+
+
+def test_device_openconfig_add_correct(
+    context_client : ContextClient,     # pylint: disable=redefined-outer-name
+    device_client : DeviceClient,       # pylint: disable=redefined-outer-name
+    device_service : DeviceService):    # pylint: disable=redefined-outer-name
+
+    if not ENABLE_OPENCONFIG: return # if there is no device to test against, asusme test is silently passed.
+
+    DEVICE_OC_WITH_CONNECT_RULES = copy.deepcopy(DEVICE_OC)
+    DEVICE_OC_WITH_CONNECT_RULES['device_config']['config_rules'].extend(DEVICE_OC_CONNECT_RULES)
+    device_client.AddDevice(Device(**DEVICE_OC_WITH_CONNECT_RULES))
+    driver : _Driver = device_service.driver_instance_cache.get(DEVICE_OC_UUID) # we know the driver exists now
+    assert driver is not None
+
+
+def test_device_openconfig_get(
+    context_client : ContextClient,     # pylint: disable=redefined-outer-name
+    device_client : DeviceClient,       # pylint: disable=redefined-outer-name
+    device_service : DeviceService):    # pylint: disable=redefined-outer-name
+
+    if not ENABLE_OPENCONFIG: return # if there is no device to test against, asusme test is silently passed.
+
+    initial_config = device_client.GetInitialConfig(DeviceId(**DEVICE_OC_ID))
+    LOGGER.info('initial_config = {:s}'.format(grpc_message_to_json_string(initial_config)))
+
+    device_data = context_client.GetDevice(DeviceId(**DEVICE_OC_ID))
+    LOGGER.info('device_data = {:s}'.format(grpc_message_to_json_string(device_data)))
+
+
+def test_device_openconfig_configure(
+    context_client : ContextClient,     # pylint: disable=redefined-outer-name
+    device_client : DeviceClient,       # pylint: disable=redefined-outer-name
+    device_service : DeviceService):    # pylint: disable=redefined-outer-name
+
+    if not ENABLE_OPENCONFIG: return # if there is no device to test against, asusme test is silently passed.
+
+    driver : _Driver = device_service.driver_instance_cache.get(DEVICE_OC_UUID) # we know the driver exists now
+    assert driver is not None
+
+    # Requires to retrieve data from device; might be slow. Uncomment only when needed and test does not pass directly.
+    #driver_config = sorted(driver.GetConfig(), key=operator.itemgetter(0))
+    #LOGGER.info('driver_config = {:s}'.format(str(driver_config)))
+
+    DEVICE_OC_WITH_CONFIG_RULES = copy.deepcopy(DEVICE_OC)
+    DEVICE_OC_WITH_CONFIG_RULES['device_config']['config_rules'].extend(DEVICE_OC_CONFIG_RULES)
+    device_client.ConfigureDevice(Device(**DEVICE_OC_WITH_CONFIG_RULES))
+
+    # Requires to retrieve data from device; might be slow. Uncomment only when needed and test does not pass directly.
+    #driver_config = sorted(driver.GetConfig(), key=operator.itemgetter(0))
+    #LOGGER.info('driver_config = {:s}'.format(str(driver_config)))
+
+    device_data = context_client.GetDevice(DeviceId(**DEVICE_OC_ID))
+    config_rules = [
+        (ConfigActionEnum.Name(config_rule.action), config_rule.resource_key, config_rule.resource_value)
+        for config_rule in device_data.device_config.config_rules
+    ]
+    LOGGER.info('device_data.device_config.config_rules = \n{:s}'.format(
+        '\n'.join(['{:s} {:s} = {:s}'.format(*config_rule) for config_rule in config_rules])))
+    for config_rule in DEVICE_OC_CONFIG_RULES:
+        config_rule = (
+            ConfigActionEnum.Name(config_rule['action']), config_rule['resource_key'], config_rule['resource_value'])
+        assert config_rule in config_rules
+
+
+def test_device_openconfig_deconfigure(
+    context_client : ContextClient,     # pylint: disable=redefined-outer-name
+    device_client : DeviceClient,       # pylint: disable=redefined-outer-name
+    device_service : DeviceService):    # pylint: disable=redefined-outer-name
+
+    if not ENABLE_OPENCONFIG: return # if there is no device to test against, asusme test is silently passed.
+
+    driver : _Driver = device_service.driver_instance_cache.get(DEVICE_OC_UUID) # we know the driver exists now
+    assert driver is not None
+
+    # Requires to retrieve data from device; might be slow. Uncomment only when needed and test does not pass directly.
+    #driver_config = sorted(driver.GetConfig(), key=operator.itemgetter(0))
+    #LOGGER.info('driver_config = {:s}'.format(str(driver_config)))
+
+    DEVICE_OC_WITH_DECONFIG_RULES = copy.deepcopy(DEVICE_OC)
+    DEVICE_OC_WITH_DECONFIG_RULES['device_config']['config_rules'].extend(DEVICE_OC_DECONFIG_RULES)
+    device_client.ConfigureDevice(Device(**DEVICE_OC_WITH_DECONFIG_RULES))
+
+    # Requires to retrieve data from device; might be slow. Uncomment only when needed and test does not pass directly.
+    #driver_config = sorted(driver.GetConfig(), key=operator.itemgetter(0))
+    #LOGGER.info('driver_config = {:s}'.format(str(driver_config)))
+
+    device_data = context_client.GetDevice(DeviceId(**DEVICE_OC_ID))
+    config_rules = [
+        (ConfigActionEnum.Name(config_rule.action), config_rule.resource_key, config_rule.resource_value)
+        for config_rule in device_data.device_config.config_rules
+    ]
+    LOGGER.info('device_data.device_config.config_rules = \n{:s}'.format(
+        '\n'.join(['{:s} {:s} = {:s}'.format(*config_rule) for config_rule in config_rules])))
+    for config_rule in DEVICE_OC_DECONFIG_RULES:
+        action_set = ConfigActionEnum.Name(ConfigActionEnum.CONFIGACTION_SET)
+        config_rule = (action_set, config_rule['resource_key'], config_rule['resource_value'])
+        assert config_rule not in config_rules
+
+
+def test_device_openconfig_delete(
+    context_client : ContextClient,     # pylint: disable=redefined-outer-name
+    device_client : DeviceClient,       # pylint: disable=redefined-outer-name
+    device_service : DeviceService):    # pylint: disable=redefined-outer-name
+
+    if not ENABLE_OPENCONFIG: return # if there is no device to test against, asusme test is silently passed.
+
+    device_client.DeleteDevice(DeviceId(**DEVICE_OC_ID))
+    driver : _Driver = device_service.driver_instance_cache.get(DEVICE_OC_UUID, {})
+    assert driver is None
diff --git a/src/device/tests/test_unitary_driverapi.py b/src/device/tests/test_unitary_driverapi.py
index ac7231cf76d4bcba0ea37da0cab781e21bc1c560..027e7775eae4a3f7a19c056266e1fc807b09cf2d 100644
--- a/src/device/tests/test_unitary_driverapi.py
+++ b/src/device/tests/test_unitary_driverapi.py
@@ -37,44 +37,44 @@ def device_driverapi_emulated():
     yield _driver
     _driver.Disconnect()
 
-def test_device_driverapi_emulated_setconfig(device_driverapi_emulated : EmulatedDriver):
-    # should work
+def test_device_driverapi_emulated_setconfig(
+    device_driverapi_emulated : EmulatedDriver): # pylint: disable=redefined-outer-name
+
     results = device_driverapi_emulated.SetConfig(DEVICE_CONFIG_IF1)
-    LOGGER.info('results:\n{}'.format('\n'.join(map(str, results))))
+    LOGGER.info('results:\n{:s}'.format('\n'.join(map(str, results))))
     assert len(results) == len(DEVICE_CONFIG_IF1)
     for result in results: assert isinstance(result, bool) and result
 
     results = device_driverapi_emulated.SetConfig(DEVICE_CONFIG_IF2)
-    LOGGER.info('results:\n{}'.format('\n'.join(map(str, results))))
+    LOGGER.info('results:\n{:s}'.format('\n'.join(map(str, results))))
     assert len(results) == len(DEVICE_CONFIG_IF2)
     for result in results: assert isinstance(result, bool) and result
 
-def test_device_driverapi_emulated_getconfig(device_driverapi_emulated : EmulatedDriver):
+def test_device_driverapi_emulated_getconfig(
+    device_driverapi_emulated : EmulatedDriver): # pylint: disable=redefined-outer-name
+
     stored_config = device_driverapi_emulated.GetConfig()
-    LOGGER.info('stored_config:\n{}'.format('\n'.join(map(str, stored_config))))
+    LOGGER.info('stored_config:\n{:s}'.format('\n'.join(map(str, stored_config))))
     assert len(stored_config) == len(DEVICE_CONFIG_IF1) + len(DEVICE_CONFIG_IF2)
     for config_row in stored_config: assert (config_row in DEVICE_CONFIG_IF1) or (config_row in DEVICE_CONFIG_IF2)
     for config_row in DEVICE_CONFIG_IF1: assert config_row in stored_config
     for config_row in DEVICE_CONFIG_IF2: assert config_row in stored_config
 
-    # should work
     stored_config = device_driverapi_emulated.GetConfig([PATH_IF.format('IF2')])
-    LOGGER.info('stored_config:\n{}'.format('\n'.join(map(str, stored_config))))
-    assert len(stored_config) == 1
-    stored_config = stored_config[0]
-    LOGGER.info('stored_config[0]:\n{}'.format('\n'.join(map(str, stored_config))))
+    LOGGER.info('stored_config:\n{:s}'.format('\n'.join(map(str, stored_config))))
     assert len(stored_config) == len(DEVICE_CONFIG_IF2)
     for config_row in stored_config: assert config_row in DEVICE_CONFIG_IF2
     for config_row in DEVICE_CONFIG_IF2: assert config_row in stored_config
 
-def test_device_driverapi_emulated_deleteconfig(device_driverapi_emulated : EmulatedDriver):
-    # should work
-    results = device_driverapi_emulated.DeleteConfig([PATH_ADDRIPV4.format('IF2', 0, '10.2.0.1')])
-    LOGGER.info('results:\n{}'.format('\n'.join(map(str, results))))
+def test_device_driverapi_emulated_deleteconfig(
+    device_driverapi_emulated : EmulatedDriver): # pylint: disable=redefined-outer-name
+
+    results = device_driverapi_emulated.DeleteConfig([(PATH_ADDRIPV4.format('IF2', 0, '10.2.0.1'), '')])
+    LOGGER.info('results:\n{:s}'.format('\n'.join(map(str, results))))
     assert (len(results) == 1) and isinstance(results[0], bool) and results[0]
 
     stored_config = device_driverapi_emulated.GetConfig()
-    LOGGER.info('stored_config:\n{}'.format('\n'.join(map(str, stored_config))))
+    LOGGER.info('stored_config:\n{:s}'.format('\n'.join(map(str, stored_config))))
 
     device_config_if2 = list(filter(lambda row: '10.2.0.1' not in row[0], copy.deepcopy(DEVICE_CONFIG_IF2)))
     assert len(stored_config) == len(DEVICE_CONFIG_IF1) + len(device_config_if2)
@@ -82,8 +82,9 @@ def test_device_driverapi_emulated_deleteconfig(device_driverapi_emulated : Emul
     for config_row in DEVICE_CONFIG_IF1: assert config_row in stored_config
     for config_row in device_config_if2: assert config_row in stored_config
 
-def test_device_driverapi_emulated_subscriptions(device_driverapi_emulated : EmulatedDriver):
-    # should work
+def test_device_driverapi_emulated_subscriptions(
+    device_driverapi_emulated : EmulatedDriver): # pylint: disable=redefined-outer-name
+
     duration = 10.0
     interval = 1.5
     results = device_driverapi_emulated.SubscribeState([
@@ -92,21 +93,21 @@ def test_device_driverapi_emulated_subscriptions(device_driverapi_emulated : Emu
         (DEVICE_STATE_IF2_TX_PKTS, duration, interval),
         (DEVICE_STATE_IF2_RX_PKTS, duration, interval),
     ])
-    LOGGER.info('results:\n{}'.format('\n'.join(map(str, results))))
+    LOGGER.info('results:\n{:s}'.format('\n'.join(map(str, results))))
     assert len(results) == 4
     for result in results: assert isinstance(result, bool) and result
 
     stored_config = device_driverapi_emulated.GetConfig()
-    LOGGER.info('stored_config:\n{}'.format('\n'.join(map(str, stored_config))))
+    LOGGER.info('stored_config:\n{:s}'.format('\n'.join(map(str, stored_config))))
 
     time.sleep(duration + 1.0) # let time to generate samples, plus 1 second extra time
 
     samples = []
     for sample in device_driverapi_emulated.GetState(blocking=False):
-        LOGGER.info('sample: {}'.format(str(sample)))
+        LOGGER.info('sample: {:s}'.format(str(sample)))
         timestamp,resource_key,resource_value = sample
         samples.append((timestamp, resource_key, resource_value))
-    LOGGER.info('samples:\n{}'.format('\n'.join(map(str, samples))))
+    LOGGER.info('samples:\n{:s}'.format('\n'.join(map(str, samples))))
     assert len(samples) == 4 * (math.floor(duration/interval) + 1)
 
     results = device_driverapi_emulated.UnsubscribeState([
@@ -115,12 +116,12 @@ def test_device_driverapi_emulated_subscriptions(device_driverapi_emulated : Emu
         (DEVICE_STATE_IF2_TX_PKTS, 10.0, 1.5),
         (DEVICE_STATE_IF2_RX_PKTS, 10.0, 1.5),
     ])
-    LOGGER.info('results:\n{}'.format('\n'.join(map(str, results))))
+    LOGGER.info('results:\n{:s}'.format('\n'.join(map(str, results))))
     assert len(results) == 4
     for result in results: assert isinstance(result, bool) and result
 
     stored_config = device_driverapi_emulated.GetConfig()
-    LOGGER.info('stored_config:\n{}'.format('\n'.join(map(str, stored_config))))
+    LOGGER.info('stored_config:\n{:s}'.format('\n'.join(map(str, stored_config))))
     device_config_if2 = list(filter(lambda row: '10.2.0.1' not in row[0], copy.deepcopy(DEVICE_CONFIG_IF2)))
     assert len(stored_config) == len(DEVICE_CONFIG_IF1) + len(device_config_if2)
     for config_row in stored_config: assert (config_row in DEVICE_CONFIG_IF1) or (config_row in device_config_if2)