This commit adds support for:
- twisted
- config file
- banner ;)
Change-Id: I234fa97b9d55a7e647dfdd82a46537c323b760a5
diff --git a/voltha/adapters/microsemi/RubyAdapter.py b/voltha/adapters/microsemi/RubyAdapter.py
index cdfec1a..b06c2e0 100644
--- a/voltha/adapters/microsemi/RubyAdapter.py
+++ b/voltha/adapters/microsemi/RubyAdapter.py
@@ -18,12 +18,12 @@
Microsemi/Celestica Ruby vOLTHA adapter.
"""
import structlog
+from twisted.internet import reactor
from voltha.adapters.interface import IAdapterInterface
from voltha.adapters.microsemi.PAS5211_comm import PAS5211Communication
from voltha.adapters.microsemi.StateMachine import Disconnected
-import signal
-from voltha.protos.adapter_pb2 import Adapter, AdapterConfig, DeviceTypes
-from voltha.protos.health_pb2 import HealthStatus
+#from voltha.protos.adapter_pb2 import Adapter, AdapterConfig, DeviceTypes
+#from voltha.protos.health_pb2 import HealthStatus
from zope.interface import implementer
@@ -34,39 +34,43 @@
@implementer(IAdapterInterface)
class RubyAdapter(object):
- def __init__(self, config):
+ def __init__(self, args, config):
+ self.args = args
self.config = config
- self.descriptor = Adapter(
- id='ruby',
- config=AdapterConfig()
- # TODO
- )
+ #self.descriptor = Adapter(
+ # id='ruby',
+ # config=AdapterConfig()
+ # # TODO
+ #)
self.comm = comm = PAS5211Communication(dst_mac=olt_conf['olts']['mac'],
iface=olt_conf['iface'])
self.olt = Disconnected(comm)
- signal.signal(signal.SIGINT, self.stop)
def start(self):
- log.debug('starting')
- self.init_olt()
+ log.info('starting')
+ reactor.callLater(0, self.init_olt)
log.info('started')
+ return self
def stop(self):
log.debug('stopping')
self.olt.disconnect()
log.info('stopped')
+ return self
def adapter_descriptor(self):
- return self.descriptor
+ pass
+ #return self.descriptor
def device_types(self):
pass
- return DeviceTypes(
- items=[] # TODO
- )
+ #return DeviceTypes(
+ # items=[] # TODO
+ #)
def health(self):
- return HealthStatus(state=HealthStatus.HealthState.HEALTHY)
+ pass
+ #return HealthStatus(state=HealthStatus.HealthState.HEALTHY)
def change_master_state(self, master):
raise NotImplementedError()
@@ -85,11 +89,4 @@
self.olt = self.olt.transition()
self.olt.run()
self.olt = self.olt.transition()
- self.olt.run()
-
-if __name__ == '__main__':
- try:
- ruby = RubyAdapter(None)
- ruby.start()
- except KeyboardInterrupt:
- ruby.stop()
\ No newline at end of file
+ self.olt.run()
\ No newline at end of file
diff --git a/voltha/adapters/microsemi/StateMachine.py b/voltha/adapters/microsemi/StateMachine.py
index 5ad1622..51289d7 100644
--- a/voltha/adapters/microsemi/StateMachine.py
+++ b/voltha/adapters/microsemi/StateMachine.py
@@ -19,8 +19,12 @@
"""
import threading
import time
+from structlog import get_logger
+from twisted.internet import reactor, task
from voltha.adapters.microsemi.PAS5211 import PAS5211MsgGetProtocolVersion, PAS5211MsgGetOltVersion
+log = get_logger()
+
class State(object):
def __init__(self):
pass
@@ -124,10 +128,10 @@
self.comm = pas_comm
self.completed = False
self.packet = None
- self.t = threading.Thread(target = self.keepalive)
+ self.scheduledTask = task.LoopingCall(self.keepalive)
def run(self):
- self.t.start()
+ self.scheduledTask.start(1.0)
def transition(self):
if self.completed:
@@ -140,16 +144,14 @@
def send_msg(self, msg):
return self.comm.communicate(msg)
- # FIXME replace with twisted
def keepalive(self):
- while not self.completed:
- self.packet = self.comm.communicate(PAS5211MsgGetOltVersion())
- if self.packet is not None:
- time.sleep(1)
- else:
- break
- self.completed = True
+ if self.completed:
+ log.info('OLT has been disconnected')
+ return
+ self.packet = self.comm.communicate(PAS5211MsgGetOltVersion())
+ if self.packet is None:
+ self.completed = True
def disconnect(self):
- print "Stopping"
- self.completed = True
+ print "Disconnecting OLT"
+ self.scheduledTask.stop()
diff --git a/voltha/adapters/microsemi/main.py b/voltha/adapters/microsemi/main.py
new file mode 100644
index 0000000..3d63d6a
--- /dev/null
+++ b/voltha/adapters/microsemi/main.py
@@ -0,0 +1,196 @@
+#
+# 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.
+#
+
+import argparse
+from common.structlog_setup import setup_logging
+from common.utils.dockerhelpers import get_my_containers_name
+import os
+from twisted.internet import reactor
+from twisted.internet.defer import inlineCallbacks
+from voltha.adapters.microsemi.RubyAdapter import RubyAdapter
+import yaml
+
+defs = dict(
+ config=os.environ.get('CONFIG', './ruby.yml'),
+ consul=os.environ.get('CONSUL', 'localhost:8500'),
+ grpc_endpoint=os.environ.get('GRPC_ENDPOINT', 'localhost:50055'),
+ fluentd=os.environ.get('FLUENTD', None),
+ instance_id=os.environ.get('INSTANCE_ID', os.environ.get('HOSTNAME', '1'))
+)
+
+def parse_args():
+
+ parser = argparse.ArgumentParser()
+
+ _help = ('Path to ruby.yml config file (default: %s). '
+ 'If relative, it is relative to main.py of microsemi.'
+ % defs['config'])
+ parser.add_argument('-c', '--config',
+ dest='config',
+ action='store',
+ default=defs['config'],
+ help=_help)
+
+ _help = '<hostname>:<port> to consul agent (default: %s)' % defs['consul']
+ parser.add_argument(
+ '-C', '--consul', dest='consul', action='store',
+ default=defs['consul'],
+ help=_help)
+
+
+ _help = ('<hostname>:<port> to fluentd server (default: %s). (If not '
+ 'specified (None), the address from the config file is used'
+ % defs['fluentd'])
+ parser.add_argument('-F', '--fluentd',
+ dest='fluentd',
+ action='store',
+ default=defs['fluentd'],
+ help=_help)
+
+ _help = ('gRPC end-point to connect to. It can either be a direct'
+ 'definition in the form of <hostname>:<port>, or it can be an'
+ 'indirect definition in the form of @<service-name> where'
+ '<service-name> is the name of the grpc service as registered'
+ 'in consul (example: @voltha-grpc). (default: %s'
+ % defs['grpc_endpoint'])
+ parser.add_argument('-G', '--grpc-endpoint',
+ dest='grpc_endpoint',
+ action='store',
+ default=defs['grpc_endpoint'],
+ help=_help)
+
+ _help = ('unique string id of this ofagent instance (default: %s)'
+ % defs['instance_id'])
+ parser.add_argument('-i', '--instance-id',
+ dest='instance_id',
+ action='store',
+ default=defs['instance_id'],
+ help=_help)
+
+ _help = "suppress debug and info logs"
+ parser.add_argument('-q', '--quiet',
+ dest='quiet',
+ action='count',
+ help=_help)
+
+ _help = 'enable verbose logging'
+ parser.add_argument('-v', '--verbose',
+ dest='verbose',
+ action='count',
+ help=_help)
+
+ _help = ('use docker container name as ofagent instance id'
+ ' (overrides -i/--instance-id option)')
+ parser.add_argument('--instance-id-is-container-name',
+ dest='instance_id_is_container_name',
+ action='store_true',
+ default=False,
+ help=_help)
+
+ _help = 'omit startup banner log lines'
+ parser.add_argument('-n', '--no-banner',
+ dest='no_banner',
+ action='store_true',
+ default=False,
+ help=_help)
+
+
+ args = parser.parse_args()
+
+ # post-processing
+
+ if args.instance_id_is_container_name:
+ args.instance_id = get_my_containers_name()
+
+ return args
+
+
+def load_config(args):
+ path = args.config
+ if path.startswith('.'):
+ dir = os.path.dirname(os.path.abspath(__file__))
+ path = os.path.join(dir, path)
+ path = os.path.abspath(path)
+ with open(path) as fd:
+ config = yaml.load(fd)
+ return config
+
+
+banner = r'''
+ ________ ___ ___ ________ ___ ___
+|\ __ \|\ \|\ \|\ __ \ |\ \ / /|
+\ \ \|\ \ \ \\\ \ \ \|\ /_ \ \ \/ / /
+ \ \ _ _\ \ \\\ \ \ __ \ \ \ / /
+ \ \ \\ \\ \ \\\ \ \ \|\ \ \/ / /
+ \ \__\\ _\\ \_______\ \_______\__/ / /
+ \|__|\|__|\|_______|\|_______|\___/ /
+ \|___|/
+'''
+
+def print_banner(log):
+ for line in banner.strip('\n').splitlines():
+ log.info(line)
+ log.info('(to stop: press Ctrl-C)')
+
+class Main(object):
+
+ def __init__(self):
+
+ self.args = args = parse_args()
+ self.config = load_config(args)
+
+ verbosity_adjust = (args.verbose or 0) - (args.quiet or 0)
+ self.log = setup_logging(self.config.get('logging', {}),
+ args.instance_id,
+ verbosity_adjust=verbosity_adjust,
+ fluentd=args.fluentd)
+
+ # components
+ self.ruby_adapter = None
+
+ self.exiting = False
+
+ if not args.no_banner:
+ print_banner(self.log)
+
+ self.startup_components()
+
+ def start(self):
+ self.start_reactor() # will not return except Keyboard interrupt
+
+ def startup_components(self):
+ self.log.info('starting-internal-components')
+ args = self.args
+ self.ruby_adapter = RubyAdapter(args, self.config).start()
+ self.log.info('started ruby adapter')
+
+ def shutdown_components(self):
+ """Execute before the reactor is shut down"""
+ self.log.info('exiting-on-keyboard-interrupt')
+ self.exiting = True
+ if self.ruby_adapter is not None:
+ self.ruby_adapter.stop()
+
+ def start_reactor(self):
+ reactor.callWhenRunning(
+ lambda: self.log.info('twisted-reactor-started'))
+
+ reactor.addSystemEventTrigger('before', 'shutdown',
+ self.shutdown_components)
+ reactor.run()
+
+if __name__ == '__main__':
+ Main().start()
\ No newline at end of file
diff --git a/voltha/adapters/microsemi/ruby.yml b/voltha/adapters/microsemi/ruby.yml
new file mode 100644
index 0000000..2b1d69e
--- /dev/null
+++ b/voltha/adapters/microsemi/ruby.yml
@@ -0,0 +1,44 @@
+logging:
+ version: 1
+
+ formatters:
+ brief:
+ format: '%(message)s'
+ default:
+ format: '%(asctime)s.%(msecs)03d %(levelname)-8s %(module)s.%(funcName)s %(message)s'
+ datefmt: '%Y%m%dT%H%M%S'
+ fluent_fmt:
+ '()': fluent.handler.FluentRecordFormatter
+ format:
+ level: '%(levelname)s'
+ hostname: '%(hostname)s'
+ where: '%(module)s.%(funcName)s'
+
+ handlers:
+ console:
+ class : logging.StreamHandler
+ level: DEBUG
+ formatter: default
+ stream: ext://sys.stdout
+ fluent:
+ class: fluent.handler.FluentHandler
+ host: localhost
+ port: 24224
+ tag: voltha.logging
+ formatter: fluent_fmt
+ level: DEBUG
+ null:
+ class: logging.NullHandler
+
+ loggers:
+ amqp:
+ handlers: [null]
+ propagate: False
+ conf:
+ handlers: [null]
+ propagate: False
+ '': # root logger
+ handlers: [console, fluent]
+ level: INFO # this can be bumped up/down by -q and -v command line
+ # options
+ propagate: False