VOL-1451 Initial checkin of openonu build
Produced docker container capable of building and running
openonu/brcm_openonci_onu. Copied over current onu code
and resolved all imports by copying into the local source tree.
Change-Id: Ib9785d37afc65b7d32ecf74aee2456352626e2b6
diff --git a/python/adapters/brcm_openomci_onu/heartbeat.py b/python/adapters/brcm_openomci_onu/heartbeat.py
new file mode 100644
index 0000000..4a7ab1f
--- /dev/null
+++ b/python/adapters/brcm_openomci_onu/heartbeat.py
@@ -0,0 +1,179 @@
+# Copyright 2017-present Adtran, Inc.
+#
+# 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 structlog
+from twisted.internet import reactor
+from voltha.protos.common_pb2 import OperStatus, ConnectStatus
+from voltha.extensions.omci.omci_me import OntGFrame
+
+
+class HeartBeat(object):
+ """Wraps health-check support for ONU"""
+ INITIAL_DELAY = 60 # Delay after start until first check
+ TICK_DELAY = 2 # Heartbeat interval
+
+ def __init__(self, handler, device_id):
+ self.log = structlog.get_logger(device_id=device_id)
+ self._enabled = False
+ self._handler = handler
+ self._device_id = device_id
+ self._defer = None
+ self._alarm_active = False
+ self._heartbeat_count = 0
+ self._heartbeat_miss = 0
+ self._alarms_raised_count = 0
+ self.heartbeat_failed_limit = 5
+ self.heartbeat_last_reason = ''
+ self.heartbeat_interval = self.TICK_DELAY
+
+ def __str__(self):
+ return "HeartBeat: count:{}, miss: {}".format(self._heartbeat_count,
+ self._heartbeat_miss)
+
+ @staticmethod
+ def create(handler, device_id):
+ return HeartBeat(handler, device_id)
+
+ def _start(self, delay=INITIAL_DELAY):
+ self._defer = reactor.callLater(delay, self.check_pulse)
+
+ def _stop(self):
+ d, self._defeered = self._defeered, None
+ if d is not None and not d.called():
+ d.cancel()
+
+ @property
+ def enabled(self):
+ return self._enabled
+
+ @enabled.setter
+ def enabled(self, value):
+ if self._enabled != value:
+ self._enabled = value
+
+ # if value:
+ # self._start()
+ # else:
+ # self._stop()
+
+ @property
+ def check_item(self):
+ return 'vendor_id'
+
+ @property
+ def check_value(self):
+ # device = self._handler.adapter_agent.get_device(self._device_id)
+ # return device.serial_number
+ return 'ADTN'
+
+ @property
+ def alarm_active(self):
+ return self._alarm_active
+
+ @property
+ def heartbeat_count(self):
+ return self._heartbeat_count
+
+ @property
+ def heartbeat_miss(self):
+ return self._heartbeat_miss
+
+ @property
+ def alarms_raised_count(self):
+ return self._alarms_raised_count
+
+ def check_pulse(self):
+ if self.enabled:
+ try:
+ self._defer = self._handler.openomci.omci_cc.send(OntGFrame(self.check_item).get())
+ self._defer.addCallbacks(self._heartbeat_success, self._heartbeat_fail)
+
+ except Exception as e:
+ self._defer = reactor.callLater(5, self._heartbeat_fail, e)
+
+ def _heartbeat_success(self, results):
+ self.log.debug('heartbeat-success')
+
+ try:
+ omci_response = results.getfieldval("omci_message")
+ data = omci_response.getfieldval("data")
+ value = data[self.check_item]
+
+ if value != self.check_value:
+ self._heartbeat_miss = self.heartbeat_failed_limit
+ self.heartbeat_last_reason = "Invalid {}, got '{}' but expected '{}'".\
+ format(self.check_item, value, self.check_value)
+ else:
+ self._heartbeat_miss = 0
+ self.heartbeat_last_reason = ''
+
+ except Exception as e:
+ self._heartbeat_miss = self.heartbeat_failed_limit
+ self.heartbeat_last_reason = e.message
+
+ self.heartbeat_check_status(results)
+
+ def _heartbeat_fail(self, failure):
+ self._heartbeat_miss += 1
+ self.log.info('heartbeat-miss', failure=failure,
+ count=self._heartbeat_count,
+ miss=self._heartbeat_miss)
+ self.heartbeat_last_reason = 'OMCI connectivity error'
+ self.heartbeat_check_status(None)
+
+ def on_heartbeat_alarm(self, active):
+ # TODO: Do something here ?
+ #
+ # TODO: If failed (active = true) due to bad serial-number shut off the UNI port?
+ pass
+
+ def heartbeat_check_status(self, results):
+ """
+ Check the number of heartbeat failures against the limit and emit an alarm if needed
+ """
+ device = self._handler.adapter_agent.get_device(self._device_id)
+
+ try:
+ from voltha.extensions.alarms.heartbeat_alarm import HeartbeatAlarm
+
+ if self._heartbeat_miss >= self.heartbeat_failed_limit:
+ if device.connect_status == ConnectStatus.REACHABLE:
+ self.log.warning('heartbeat-failed', count=self._heartbeat_miss)
+ device.connect_status = ConnectStatus.UNREACHABLE
+ device.oper_status = OperStatus.FAILED
+ device.reason = self.heartbeat_last_reason
+ self._handler.adapter_agent.update_device(device)
+ HeartbeatAlarm(self._handler.alarms, 'onu', self._heartbeat_miss).raise_alarm()
+ self._alarm_active = True
+ self.on_heartbeat_alarm(True)
+ else:
+ # Update device states
+ if device.connect_status != ConnectStatus.REACHABLE and self._alarm_active:
+ device.connect_status = ConnectStatus.REACHABLE
+ device.oper_status = OperStatus.ACTIVE
+ device.reason = ''
+ self._handler.adapter_agent.update_device(device)
+ HeartbeatAlarm(self._handler.alarms, 'onu').clear_alarm()
+
+ self._alarm_active = False
+ self._alarms_raised_count += 1
+ self.on_heartbeat_alarm(False)
+
+ except Exception as e:
+ self.log.exception('heartbeat-check', e=e)
+
+ # Reschedule next heartbeat
+ if self.enabled:
+ self._heartbeat_count += 1
+ self._defer = reactor.callLater(self.heartbeat_interval, self.check_pulse)