Major rework of gRPC handling (do not merge yet)

Includes the following chages:

* Refactored proto files
  - separation of logical devices vs devices
  - common flow related message types moved to openflow_13
  - most RPC is defined in voltha.proto now
* Expanded RPC definitions to cover now most of what we
  need (a few device provisioning RPCs are still missing)
* Reworked RPC handlers to work with new config tree
* Implemented test cases for all existing RPCs, tested via
  chameleon's REST service
* Did away wih the OrderedDict internal representation
  in the config nodes (3x performance boost on bulk
  add, and negligible penalty in other ops)
* Refactored transacton merge handling to align with
  new structures

Change-Id: I3740ec13b8296943b307782e86e6b596af78140e
diff --git a/voltha/adapters/interface.py b/voltha/adapters/interface.py
index e28b59c..60eec7d 100644
--- a/voltha/adapters/interface.py
+++ b/voltha/adapters/interface.py
@@ -17,7 +17,19 @@
 """
 Interface definition for Voltha Adapters
 """
+import structlog
+from twisted.internet.defer import inlineCallbacks, returnValue
 from zope.interface import Interface
+from zope.interface import implementer
+
+from voltha.protos import third_party
+from voltha.protos.device_pb2 import Device, Port
+from voltha.protos.openflow_13_pb2 import ofp_port
+from voltha.protos.voltha_pb2 import DeviceGroup, LogicalDevice
+from voltha.registry import registry
+
+
+log = structlog.get_logger()
 
 
 class IAdapterInterface(Interface):
@@ -97,3 +109,147 @@
 
     # TODO work in progress
     # ...
+
+
+class IAdapterProxy(Interface):
+    """
+    This object is passed in to the __init__ function of each adapter,
+    and can be used by the adapter implementation to initiate async calls
+    toward Voltha's CORE via the APIs defined here.
+    """
+
+    def create_device(device):
+        # TODO add doc
+        """"""
+
+    def add_port(device_id, port):
+        # TODO add doc
+        """"""
+
+    def create_logical_device(logical_device):
+        # TODO add doc
+        """"""
+
+    def add_logical_port(logical_device_id, port):
+        # TODO add doc
+        """"""
+
+    # TODO work in progress
+    pass
+
+
+@implementer(IAdapterProxy)
+class AdapterProxy(object):
+    """
+    Gate-keeper between CORE and device adapters.
+
+    On one side it interacts with Core's internal model and update/dispatch
+    mechanisms.
+
+    On the other side, it interacts with the adapters standard interface as
+    defined in
+    """
+
+    def __init__(self, adapter_name, adapter_cls):
+        self.adapter_name = adapter_name
+        self.adapter_cls = adapter_cls
+        self.core = registry('core')
+        self.adapter = None
+        self.adapter_node_proxy = None
+
+    @inlineCallbacks
+    def start(self):
+        log.debug('starting')
+        config = self._get_adapter_config()  # this may be None
+        adapter = self.adapter_cls(self, config)
+        yield adapter.start()
+        self.adapter = adapter
+        self.adapter_node_proxy = self._update_adapter_node()
+        self._update_device_types()
+        log.info('started')
+        returnValue(self)
+
+    @inlineCallbacks
+    def stop(self):
+        log.debug('stopping')
+        if self.adapter is not None:
+            yield self.adapter.stop()
+            self.adapter = None
+        log.info('stopped')
+
+    def _get_adapter_config(self):
+        """
+        Opportunistically load persisted adapter configuration.
+        Return None if no configuration exists yet.
+        """
+        proxy = self.core.get_proxy('/')
+        try:
+            config = proxy.get('/adapters/' + self.adapter_name)
+            return config
+        except KeyError:
+            return None
+
+    def _update_adapter_node(self):
+        """
+        Creates or updates the adapter node object based on self
+        description from the adapter.
+        """
+
+        adapter_desc = self.adapter.adapter_descriptor()
+        assert adapter_desc.id == self.adapter_name
+        path = self._make_up_to_date(
+            '/adapters', self.adapter_name, adapter_desc)
+        return self.core.get_proxy(path)
+
+    def _update_device_types(self):
+        """
+        Make sure device types are registered in Core
+        """
+        device_types = self.adapter.device_types()
+        for device_type in device_types.items:
+            key = device_type.id
+            self._make_up_to_date('/device_types', key, device_type)
+
+    def _make_up_to_date(self, container_path, key, data):
+        full_path = container_path + '/' + str(key)
+        root_proxy = self.core.get_proxy('/')
+        try:
+            root_proxy.get(full_path)
+            root_proxy.update(full_path, data)
+        except KeyError:
+            root_proxy.add(container_path, data)
+        return full_path
+
+    # ~~~~~~~~~~~~~~~~~ Adapter-Facing Service ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    def create_device(self, device):
+        assert isinstance(device, Device)
+        self._make_up_to_date('/devices', device.id, device)
+
+        # TODO for now, just map everything into a single device group
+        # which we create if it does not yet exist
+
+        dg = DeviceGroup(id='1')
+        self._make_up_to_date('/device_groups', dg.id, dg)
+
+        # add device to device group
+        # TODO how to do that?
+
+    def create_logical_device(self, logical_device):
+        assert isinstance(logical_device, LogicalDevice)
+        self._make_up_to_date('/logical_devices',
+                              logical_device.id, logical_device)
+
+        # TODO link logical device to root device and back...
+
+    def add_port(self, device_id, port):
+        assert isinstance(port, Port)
+        self._make_up_to_date('/devices/{}/ports'.format(device_id),
+                              port.id, port)
+
+    def add_logical_port(self, logical_device_id, port):
+        assert isinstance(port, ofp_port)
+        self._make_up_to_date(
+            '/logical_devices/{}/ports'.format(logical_device_id),
+            port.port_no, port)
+