Async/streaming gRPC client/server proto

This experiment was to fine-tune how we can implement
async gRPC client and server code inside a Twisted
python app.

Change-Id: I945014e27f4b9d6ed624666e0284cc298548adb3

Major cleanup of openflow_13.proto

Change-Id: I4e54eaf87b682124ec518a0ade1a6050a6ec6da8

Relocated openflow_13.proto to voltha

Change-Id: I66ae45a9142d180c2c6651e75c7a1ee08aef7ef8

Removed forced utest from make build

Change-Id: If0da58e9d135ebde6ca68c3316688a03a7b10f2f

twisted openflow agent first pass

Change-Id: Ibe5b4727ccfe92e6fd464ccd3baf6275569ef5d3

store openflow derived files

Change-Id: Ib3e1384bb2ca2a9c0872767f7b793f96b0a154e2

Minor cleanup

Change-Id: I1280ed3acb606121b616a0efd573f5f59d010dca

Factored out common utils

Change-Id: Icd86fcd50f60d0900924674cbcd65e13e47782a1

Refactored twisted agent

Change-Id: I71f26ce5357a4f98477df60b8c5ddc068cf75d43

Relocated openflow agent to ofagent

... and preserved obsolete working (non-twisted) agent under
~/obsolete, so we can still run the olt-oftest and pass tests,
unit the new twisted based agent reaches that maturity point.

Change-Id: I727f8d7144b1291a40276dad2966b7643bd7bc4b

olt-oftest in fake mode works with new agent

Change-Id: I43b4f5812e8dfaa9f45e4a77fdcf6c30ac520f8d

Initial ofagent/voltha operation

Change-Id: Ia8104f1285a6b1c51635d36d7d78fc113f800e79

Additional callouts to Voltha

Change-Id: If8f483d5140d3c9d45f22b480b8d33249a29cd4e

More gRPC calls

Change-Id: I7d24fadf9425217fb26ffe18f25359d072ef38fa

Flow add/list now works

Change-Id: Ie3e3e73108645b47891cef798fc61372a022fd93

Missed some files

Change-Id: I29e81238ff1a26c095c0c73e521579edf7092e21
diff --git a/asleep.py b/asleep.py
deleted file mode 100644
index e1868ab..0000000
--- a/asleep.py
+++ /dev/null
@@ -1,27 +0,0 @@
-#
-# Copyright 2016 the original author or authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-""" Async sleep (asleep) method and other twisted goodies """
-
-from twisted.internet import reactor
-from twisted.internet.defer import Deferred
-
-
-def asleep(dt):
-    assert isinstance(dt, (int, float))
-    d = Deferred()
-    reactor.callLater(dt, lambda: d.callback(None))
-    return d
diff --git a/dockerhelpers.py b/dockerhelpers.py
deleted file mode 100644
index 0927711..0000000
--- a/dockerhelpers.py
+++ /dev/null
@@ -1,50 +0,0 @@
-#
-# Copyright 2016 the original author or authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-"""
-Some docker related convenience functions
-"""
-
-import os
-from structlog import get_logger
-
-from docker import Client
-
-
-log = get_logger()
-
-
-def get_my_containers_name():
-    """
-    Return the docker containers name in which this process is running.
-    To look up the container name, we use the container ID extracted from the
-    $HOSTNAME environment variable (which is set by docker conventions).
-    :return: String with the docker container name (or None if any issue is
-             encountered)
-    """
-    my_container_id = os.environ.get('HOSTNAME', None)
-
-    try:
-        docker_cli = Client(base_url='unix://tmp/docker.sock')
-        info = docker_cli.inspect_container(my_container_id)
-
-    except Exception, e:
-        log.exception('failed', my_container_id=my_container_id, e=e)
-        raise
-
-    name = info['Name'].lstrip('/')
-
-    return name
diff --git a/grpc_client/grpc_client.py b/grpc_client/grpc_client.py
index 790bab3..9129b2e 100644
--- a/grpc_client/grpc_client.py
+++ b/grpc_client/grpc_client.py
@@ -33,7 +33,7 @@
 from twisted.internet.defer import inlineCallbacks
 from werkzeug.exceptions import ServiceUnavailable
 
-from chameleon.asleep import asleep
+from common.utils.asleep import asleep
 from chameleon.protos.schema_pb2 import NullMessage, SchemaServiceStub
 
 log = get_logger()
diff --git a/main.py b/main.py
index 3892dac..14c854b 100755
--- a/main.py
+++ b/main.py
@@ -28,11 +28,11 @@
 sys.path.append(base_dir)
 sys.path.append(os.path.join(base_dir, '/chameleon/protos/third_party'))
 
-from chameleon.structlog_setup import setup_logging
-from chameleon.nethelpers import get_my_primary_local_ipv4
-from chameleon.dockerhelpers import get_my_containers_name
 from chameleon.grpc_client.grpc_client import GrpcClient
 from chameleon.web_server.web_server import WebServer
+from common.utils.dockerhelpers import get_my_containers_name
+from common.utils.nethelpers import get_my_primary_local_ipv4
+from common.utils.structlog_setup import setup_logging
 
 
 defs = dict(
diff --git a/nethelpers.py b/nethelpers.py
deleted file mode 100644
index 52f7f4c..0000000
--- a/nethelpers.py
+++ /dev/null
@@ -1,46 +0,0 @@
-#
-# Copyright 2016 the original author or authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-"""
-Some network related convenience functions
-"""
-
-from netifaces import AF_INET
-
-import netifaces as ni
-
-
-def get_my_primary_interface():
-    gateways = ni.gateways()
-    assert 'default' in gateways, \
-        ("No default gateway on host/container, "
-         "cannot determine primary interface")
-    default_gw_index = gateways['default'].keys()[0]
-    # gateways[default_gw_index] has the format (example):
-    # [('10.15.32.1', 'en0', True)]
-    interface_name = gateways[default_gw_index][0][1]
-    return interface_name
-
-
-def get_my_primary_local_ipv4(ifname=None):
-    ifname = get_my_primary_interface() if ifname is None else ifname
-    addresses = ni.ifaddresses(ifname)
-    ipv4 = addresses[AF_INET][0]['addr']
-    return ipv4
-
-
-if __name__ == '__main__':
-    print get_my_primary_local_ipv4()
diff --git a/protoc_plugins/gw_gen.py b/protoc_plugins/gw_gen.py
index 77f74fa..5398c32 100755
--- a/protoc_plugins/gw_gen.py
+++ b/protoc_plugins/gw_gen.py
@@ -40,6 +40,8 @@
 
 def add_routes(app, grpc_client):
 
+    pass  # so that if no endpoints are defined, Python is still happy
+
     {% for method in methods %}
     {% set method_name = method['service'] + '_' + method['method'] %}
     {% set path = method['path'].replace('{', '<string:').replace('}', '>') %}
diff --git a/structlog_setup.py b/structlog_setup.py
deleted file mode 100644
index d4b2e8e..0000000
--- a/structlog_setup.py
+++ /dev/null
@@ -1,113 +0,0 @@
-#
-# Copyright 2016 the original author or authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-"""Setting up proper logging for Voltha"""
-
-import logging
-import logging.config
-from collections import OrderedDict
-
-import structlog
-from structlog.stdlib import BoundLogger, INFO
-
-try:
-    from thread import get_ident as _get_ident
-except ImportError:
-    from dummy_thread import get_ident as _get_ident
-
-
-class FluentRenderer(object):
-    def __call__(self, logger, name, event_dict):
-        # in order to keep structured log data in event_dict to be forwarded as
-        # is to the fluent logger, we need to pass it into the logger framework
-        # as the first positional argument.
-        args = (event_dict, )
-        kwargs = {}
-        return args, kwargs
-
-
-class PlainRenderedOrderedDict(OrderedDict):
-    """Our special version of OrderedDict that renders into string as a dict,
-       to make the log stream output cleaner.
-    """
-    def __repr__(self, _repr_running={}):
-        'od.__repr__() <==> repr(od)'
-        call_key = id(self), _get_ident()
-        if call_key in _repr_running:
-            return '...'
-        _repr_running[call_key] = 1
-        try:
-            if not self:
-                return '{}'
-            return '{%s}' % ", ".join("%s: %s" % (k, v)
-                                      for k, v in self.items())
-        finally:
-            del _repr_running[call_key]
-
-
-def setup_logging(log_config, instance_id, verbosity_adjust=0, fluentd=None):
-    """
-    Set up logging such that:
-    - The primary logging entry method is structlog
-      (see http://structlog.readthedocs.io/en/stable/index.html)
-    - By default, the logging backend is Python standard lib logger
-    - Alternatively, fluentd can be configured with to be the backend,
-      providing direct bridge to a fluent logging agent.
-    """
-
-    def add_exc_info_flag_for_exception(_, name, event_dict):
-        if name == 'exception':
-            event_dict['exc_info'] = True
-        return event_dict
-
-    def add_instance_id(_, __, event_dict):
-        event_dict['instance_id'] = instance_id
-        return event_dict
-
-    # if fluentd is specified, we need to override the config data with
-    # its host and port info
-    if fluentd is not None:
-        fluentd_host = fluentd.split(':')[0].strip()
-        fluentd_port = int(fluentd.split(':')[1].strip())
-
-        handlers = log_config.get('handlers', None)
-        if isinstance(handlers, dict):
-            for _, defs in handlers.iteritems():
-                if isinstance(defs, dict):
-                    if defs.get('class', '').endswith('FluentHandler'):
-                        defs['host'] = fluentd_host
-                        defs['port'] = fluentd_port
-
-    # Configure standard logging
-    logging.config.dictConfig(log_config)
-    logging.root.level -= 10 * verbosity_adjust
-
-    processors = [
-        add_exc_info_flag_for_exception,
-        structlog.processors.StackInfoRenderer(),
-        structlog.processors.format_exc_info,
-        add_instance_id,
-        FluentRenderer(),
-    ]
-    structlog.configure(logger_factory=structlog.stdlib.LoggerFactory(),
-                        context_class=PlainRenderedOrderedDict,
-                        wrapper_class=BoundLogger,
-                        processors=processors)
-
-    # Mark first line of log
-    log = structlog.get_logger()
-    log.info("first-line")
-    return log