Initial log framework

Also:

- Renamed voltha.py to main.py (due to module collision)
- Added logging related dependencies
- Twisted loop
diff --git a/Dockerfile b/Dockerfile
index 11b2251..1a9ecc4 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -36,4 +36,4 @@
 
 # Exposing process and default entry point
 # EXPOSE 8000
-CMD ["python", "voltha/voltha.py"]
+CMD ["python", "voltha/main.py"]
diff --git a/Makefile b/Makefile
index f3b50e4..fa2c0bd 100644
--- a/Makefile
+++ b/Makefile
@@ -25,6 +25,7 @@
 	@echo "where available targets are:"
 	@echo
 	@echo "build        : Build the Voltha docker image (default target)"
+	@echo "clean        : Remove files created by the build and tests"
 	@echo "fetch        : Pre-fetch artifacts for subsequent local builds"
 	@echo "help         : Print this help"
 	@echo "rebuild-venv : Rebuild local Python virtualenv from scratch"
@@ -35,8 +36,12 @@
 build: fetch utest
 	docker build -t cord/voltha -f Dockerfile .
 
+clean:
+	find voltha -name '*.pyc' | xargs rm -f
+
 fetch:
-	# noop
+	docker pull consul
+	docker pull fluent/fluentd
 
 purge-venv:
 	rm -fr ${VENVDIR}
diff --git a/build.gradle b/build.gradle
index 83c07b9..9fb6546 100644
--- a/build.gradle
+++ b/build.gradle
@@ -50,8 +50,8 @@
 // ~~~~~~~~~~~~~~~~~~~ Global tasks ~~~~~~~~~~~~~~~~~~~~~~~
 
 // To be used to fetch upstream binaries, clone repos, etc.
-task fetch {
-    // ...
+task fetch(type: Exec) {
+    commandLine "make", "fetch"
 }
 
 // To be used to generate all needed binaries that need to be present on the target
diff --git a/requirements.txt b/requirements.txt
index 06454ac..486202b 100755
--- a/requirements.txt
+++ b/requirements.txt
@@ -2,6 +2,7 @@
 colorama>=0.2.5
 decorator>=3.4.0
 flake8>=2.5.1
+fluent-logger>=0.4.3
 nose>=1.3.7
 mock>=1.3.0
 netifaces>=0.10.4
@@ -14,9 +15,9 @@
 PyYAML>=3.10
 requests
 scapy>=2.3.2
+service-identity
 simplejson>=3.8.1
 six>=1.10.0
+structlog>=16.1.0
 Twisted>=13.2.0
 urllib3>=1.7.1
-
-
diff --git a/setup.py b/setup.py
index 626adc6..9284e21 100644
--- a/setup.py
+++ b/setup.py
@@ -34,4 +34,3 @@
         'all' : twisted_deps
     },
 )
-                                        
diff --git a/voltha/main.py b/voltha/main.py
new file mode 100755
index 0000000..b8fc26b
--- /dev/null
+++ b/voltha/main.py
@@ -0,0 +1,98 @@
+#!/usr/bin/env python
+#
+# 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.
+#
+
+""" virtual OLT Hardware Abstraction """
+
+import argparse
+import logging
+import sys
+import structlog
+
+
+def parse_args():
+
+    parser = argparse.ArgumentParser()
+
+    parser.add_argument('-i', '--interface', dest='interface', action='store', default='eth0',
+                        help='ETH interface to send (default: eth0)')
+    parser.add_argument('--no-banner', dest='no_banner', action='store_true', default=False,
+                        help='omit startup banner log lines')
+    parser.add_argument('-v', '--verbose', dest='verbose', action='store_true', default=False,
+                        help='verbose print out')
+
+    return parser.parse_args()
+
+
+def setup_logging(args):
+    """
+    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
+    - In the future, fluentd or other backends will be supported too
+    """
+
+    # Configure standard logging
+    DATEFMT = '%Y-%m-%dT%H:%M:%S'
+    FORMAT = '%(asctime)-15s.%(msecs)03d %(levelname)-8s %(msg)s'
+    level = logging.DEBUG if args.verbose else logging.INFO
+    logging.basicConfig(stream=sys.stdout, level=level, format=FORMAT, datefmt=DATEFMT)
+
+    # Hook up structlog to standard logging
+    processors = [
+        structlog.processors.StackInfoRenderer(),
+        structlog.processors.format_exc_info,
+        structlog.processors.KeyValueRenderer(),  # structlog.processorsJSONRenderer(),
+
+    ]
+    structlog.configure(logger_factory=structlog.stdlib.LoggerFactory(), processors=processors)
+
+    # Mark first line of log
+    log = structlog.get_logger()
+    log.info("first-line")
+    return log
+
+
+def print_banner(args, log):
+    log.info(' _    ______  __  ________  _____ ')
+    log.info('| |  / / __ \/ / /_  __/ / / /   |')
+    log.info('| | / / / / / /   / / / /_/ / /| |')
+    log.info('| |/ / /_/ / /___/ / / __  / ___ |')
+    log.info('|___/\____/_____/_/ /_/ /_/_/  |_|')
+
+
+def cleanup(log):
+    """Execute before the reactor is shut down"""
+    log.info('exiting-on-keyboard-interrupt')
+
+
+def start_reactor(args, log):
+    from twisted.internet import reactor
+    reactor.callWhenRunning(lambda: log.info('twisted-reactor-started'))
+    reactor.addSystemEventTrigger('before', 'shutdown', lambda: cleanup(log))
+    reactor.run()
+
+
+def main():
+    args = parse_args()
+    log = setup_logging(args)
+    if not args.no_banner:
+        print_banner(args, log)
+    start_reactor(args, log)  # will not return except Keyboard interrupt
+
+
+if __name__ == '__main__':
+    main()
diff --git a/voltha/voltha.py b/voltha/voltha.py
deleted file mode 100755
index 1036efe..0000000
--- a/voltha/voltha.py
+++ /dev/null
@@ -1,55 +0,0 @@
-#!/usr/bin/env python
-#
-# 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.
-#
-
-""" virtual OLT Hardware Abstraction """
-
-import argparse
-
-
-def parse_args():
-
-    parser = argparse.ArgumentParser()
-    parser.add_argument('-i', '--interface', dest='interface', action='store', default='eth0',
-                        help='ETH interface to send (default: eth0)')
-    parser.add_argument('-v', '--verbose', dest='verbose', action='store_true', default=False,
-                        help='verbose print out')
-
-    return parser.parse_args()
-
-
-def main():
-
-    args = parse_args()
-
-    if args.verbose:
-        vType = '*verbose*'
-        vHint = ''
-    else:
-        vType = '*regular*'
-        vHint = '(hint: try --verbose)'
-
-    print 'Hello, I am {} VOLTHA {}'.format(vType, vHint)
-    print ' _    ______  __  ________  _____ '
-    print '| |  / / __ \/ / /_  __/ / / /   |'
-    print '| | / / / / / /   / / / /_/ / /| |'
-    print '| |/ / /_/ / /___/ / / __  / ___ |'
-    print '|___/\____/_____/_/ /_/ /_/_/  |_|'
-    print ''
-
-
-if __name__ == '__main__':
-    main()