VOL-1109: Common directory for Alarms package with initial objects
Changed ONU Discovery Alarm to MAJOR severity level
Added logical_device_id value as part of base alarm information
Updated README.md to contain TODO list
Change-Id: Ibb3d817efc9f3d94b64388eaf891d564ab944ea0
diff --git a/voltha/extensions/alarms/README.md b/voltha/extensions/alarms/README.md
new file mode 100644
index 0000000..2ac971a
--- /dev/null
+++ b/voltha/extensions/alarms/README.md
@@ -0,0 +1,62 @@
+# VOLTHA Alarm Library
+
+This directory provides a common library for the creation of Alarms by adapters within VOLTHA
+and should be used to insure that published alarms from different adapters use the same format
+
+## Alarm Manager Creation
+
+Each device handler should create an instance of the **AdapterAlarms** alarm manager shortly after
+initial activation. This alarm manager is responsible for the formatting and sending of alarms
+by the adapters.
+
+## Raising and Clearing Alarms
+
+To create a specific alarm, create an instance of the specific alarm you wish to publish
+(such as **OnuDiscoveryAlarms** for newly discovered ONUs) and pass in alarm specific information
+to the initialize.
+
+Once constructed, you can call the alarm's **_raise_alarm()_** method to format and send an active
+alarm, or the **_clear_alarm()_** to clear it.
+
+# Basic Alarm Format
+
+Here is an JSON example of a current alarm published on the kafka bus under the
+_voltha.alarms_ topic:
+
+```json
+{
+ "id": "voltha.adtran_olt.000198f9c4d2ae80.Discovery",
+ "description": "adtran_olt.000198f9c4d2ae80 - ONU DISCOVERY Alarm - DISCOVERY - Raised",
+ "logical_device_id": "0001112233445566",
+ "state": "RAISED",
+ "category": "PON",
+ "severity": "CRITICAL",
+ "resource_id": "0",
+ "type": "EQUIPMENT",
+ "reported_ts": 1532031872.0,
+ "raised_ts": 1532031872.0,
+ "changed_ts": 0.0,
+ "context": {
+ "serial-number": "ADTN17230031",
+ "pon-id": "0"
+ }
+}
+```
+
+# Remaining Work Items
+This initial code is only a prelimenary sample. The following tasks need to be
+added to the VOLTHA JIRA or performed in the SEBA group.
+
+- Get a list from SEBA/VOLTHA on required alarms
+
+- Provide example JSON output and verify that it meets SEBA's requirements
+
+- Get feedback from other OLT/ONU developers on any needed changes
+
+- For the logical_device_id, this is reported in the format that the device adapter has which
+ includes the vcore number (often 0001) in the first four nibble. Should this be normalized to
+ all zeros?
+
+- Support alarm_suppression capability (via IAdapter call). Needs investigation
+
+- TODO: Probably a few more. Look through code for more 'TODO' Notes
diff --git a/voltha/extensions/alarms/__init__.py b/voltha/extensions/alarms/__init__.py
new file mode 100644
index 0000000..b0fb0b2
--- /dev/null
+++ b/voltha/extensions/alarms/__init__.py
@@ -0,0 +1,13 @@
+# Copyright 2017-present Open Networking Foundation
+#
+# 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.
diff --git a/voltha/extensions/alarms/adapter_alarms.py b/voltha/extensions/alarms/adapter_alarms.py
new file mode 100644
index 0000000..5d3cfcb
--- /dev/null
+++ b/voltha/extensions/alarms/adapter_alarms.py
@@ -0,0 +1,192 @@
+#
+# Copyright 2017 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 structlog
+import arrow
+from voltha.protos.events_pb2 import AlarmEventType, AlarmEventSeverity,\
+ AlarmEventState
+
+# TODO: In the device adapter, the following alarms are still TBD
+# (Taken from openolt_alarms)
+# onu_alarm_ind
+# onu_startup_failure_indication
+# onu_signal_degrade_indication
+# onu_drift_of_window_ind
+# onu_loss_omci_ind
+# onu_signals_fail_ind
+# onu_tiwi_ind
+# onu_activation_fail_ind
+# onu_processing_error_ind
+
+
+class AdapterAlarms:
+ """
+ Class for managing Alarms within a given Device Handler instance
+ """
+ def __init__(self, adapter_agent, device_id, logical_device_id):
+ """
+ Adapter alarm manager initializer
+
+ :param adapter_agent: (AdapterAgent) Adapter agent reference
+ :param device_id: (str) Device handler's unique device id
+ :param logical_device_id: (str) Logical Device that the device is a member of
+ """
+ self.log = structlog.get_logger(device_id=device_id)
+ self.adapter_agent = adapter_agent
+ self.device_id = device_id
+ self.logical_device_id = logical_device_id
+ self.adapter_name = adapter_agent.adapter_name
+ self.lc = None
+
+ def format_id(self, alarm):
+ """
+ Format the Unique Alarm ID for this alarm. This is provided in the alarms
+ 'id' field
+
+ :param alarm: (str) The name of the alarm such as 'Discover' or 'LOS'
+
+ :return: (str) Alarm ID
+ """
+ return 'voltha.{}.{}.{}'.format(self.adapter_name,
+ self.device_id,
+ alarm)
+
+ def format_description(self, _object, alarm, status):
+ """
+ Format the textual description field of this alarm
+
+ :param _object: ()
+ :param alarm: (str) The name of the alarm such as 'Discover' or 'LOS'
+ :param status: (bool) If True, the alarm is active (it is being raised)
+
+ :return: (str) Alarm description
+ """
+ return '{} Alarm - {} - {}'.format(_object.upper(),
+ alarm.upper(),
+ 'Raised' if status else 'Cleared')
+
+ def send_alarm(self, context_data, alarm_data):
+ """
+ Send the alarm to the event bus
+
+ :param context_data: (dict) Alarm specific context data
+ :param alarm_data: (dict) Common Alarm information dictionary
+ """
+ try:
+ current_context = {}
+
+ if isinstance(context_data, dict):
+ for key, value in context_data.iteritems():
+ current_context[key] = str(value)
+
+ alarm_event = self.adapter_agent.create_alarm(
+ id=alarm_data.get('id', 'voltha.{}.{}.olt'.format(self.adapter_name,
+ self.device_id)),
+ resource_id=str(alarm_data.get('resource_id', self.device_id)),
+ description="{}.{} - {}".format(self.adapter_name, self.device_id,
+ alarm_data.get('description')),
+ type=alarm_data.get('type'),
+ category=alarm_data.get('category'),
+ severity=alarm_data.get('severity'),
+ state=alarm_data.get('state'),
+ raised_ts=alarm_data.get('ts', 0),
+ context=current_context,
+ logical_device_id=self.logical_device_id
+ )
+ self.adapter_agent.submit_alarm(self.device_id, alarm_event)
+
+ except Exception as e:
+ self.log.exception('failed-to-send-alarm', e=e)
+ raise
+
+
+class AlarmBase(object):
+ """Base class for alarms"""
+ def __init__(self, alarm_mgr, object_type, alarm,
+ alarm_category,
+ resource_id=None,
+ alarm_type=AlarmEventType.EQUIPMENT,
+ alarm_severity=AlarmEventSeverity.CRITICAL):
+ """
+ Initializer for the Alarm base class
+
+ :param alarm_mgr: (AdapterAlarms) Reference to the device handler's Adapter
+ Alarm manager
+ :param object_type: (str) Type of device generating the alarm such as 'olt' or 'onu'
+ :param alarm: (str) A textual name for the alarm such as 'HeartBeat' or 'Discovery'
+ :param alarm_category: (AlarmEventCategory) Refers to functional category of
+ the alarm
+ :param resource_id: (str) Identifier of the originating resource of the alarm
+ :param alarm_type: (AlarmEventType) Refers to the area of the system impacted
+ by the alarm
+ :param alarm_severity: (AlarmEventSeverity) Overall impact of the alarm on the
+ system
+ """
+ self._alarm_mgr = alarm_mgr
+ self._object_type = object_type
+ self._alarm = alarm
+ self._alarm_category = alarm_category
+ self._alarm_type = alarm_type
+ self._alarm_severity = alarm_severity
+ self._resource_id = resource_id
+
+ def get_alarm_data(self, status):
+ """
+ Get the alarm specific data and format it into a dictionary. When the alarm
+ is being sent to the event bus, this dictionary provides a majority of the
+ fields for the alarms.
+
+ :param status: (bool) True if the alarm is active/raised
+ :return: (dict) Alarm data
+ """
+ data = {
+ 'ts': arrow.utcnow().timestamp,
+ 'description': self._alarm_mgr.format_description(self._object_type,
+ self._alarm,
+ status),
+ 'id': self._alarm_mgr.format_id(self._alarm),
+ 'type': self._alarm_type,
+ 'category': self._alarm_category,
+ 'severity': self._alarm_severity,
+ 'state': AlarmEventState.RAISED if status else AlarmEventState.CLEARED
+ }
+ if self._resource_id is not None:
+ data['resource_id'] = self._resource_id
+ return data
+
+ def get_context_data(self):
+ """
+ Get alarm specific context data. If an alarm has specific data to specify, it is
+ included in the context field in the published event
+
+ :return: (dict) Dictionary with alarm specific context data
+ """
+ return {} # NOTE: You should override this if needed
+
+ def raise_alarm(self):
+ """
+ Called to set the state of an alarm to active and to send it to the event bus
+ """
+ alarm_data = self.get_alarm_data(True)
+ context_data = self.get_context_data()
+ self._alarm_mgr.send_alarm(context_data, alarm_data)
+
+ def clear_alarm(self):
+ """
+ Called to set the state of an alarm to inactive and to send it to the event bus
+ """
+ alarm_data = self.get_alarm_data(False)
+ context_data = self.get_context_data()
+ self._alarm_mgr.send_alarm(context_data, alarm_data)
diff --git a/voltha/extensions/alarms/heartbeat_alarm.py b/voltha/extensions/alarms/heartbeat_alarm.py
new file mode 100644
index 0000000..4f5f4f4
--- /dev/null
+++ b/voltha/extensions/alarms/heartbeat_alarm.py
@@ -0,0 +1,28 @@
+# 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.
+from voltha.protos.events_pb2 import AlarmEventType, AlarmEventSeverity, AlarmEventCategory
+from adapter_alarms import AlarmBase
+
+
+class HeartbeatAlarm(AlarmBase):
+ def __init__(self, alarm_mgr, object_type='olt', heartbeat_misses=0):
+ super(HeartbeatAlarm, self).__init__(alarm_mgr, object_type,
+ alarm='Heartbeat',
+ alarm_category=AlarmEventCategory.PON,
+ alarm_type=AlarmEventType.EQUIPMENT,
+ alarm_severity=AlarmEventSeverity.CRITICAL)
+ self._misses = heartbeat_misses
+
+ def get_context_data(self):
+ return {'heartbeats-missed': self._misses}
diff --git a/voltha/extensions/alarms/olt/__init__.py b/voltha/extensions/alarms/olt/__init__.py
new file mode 100644
index 0000000..b0fb0b2
--- /dev/null
+++ b/voltha/extensions/alarms/olt/__init__.py
@@ -0,0 +1,13 @@
+# Copyright 2017-present Open Networking Foundation
+#
+# 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.
diff --git a/voltha/extensions/alarms/olt/olt_los_alarm.py b/voltha/extensions/alarms/olt/olt_los_alarm.py
new file mode 100644
index 0000000..26ec50e
--- /dev/null
+++ b/voltha/extensions/alarms/olt/olt_los_alarm.py
@@ -0,0 +1,33 @@
+# 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.
+
+from voltha.protos.events_pb2 import AlarmEventType, AlarmEventSeverity, AlarmEventCategory
+from voltha.extensions.alarms.adapter_alarms import AlarmBase
+
+
+class OltLosAlarm(AlarmBase):
+ def __init__(self, alarm_mgr, onu_id, intf_id):
+ super(OltLosAlarm, self).__init__(alarm_mgr, 'olt LOS',
+ alarm='LOS',
+ alarm_category=AlarmEventCategory.OLT,
+ alarm_type=AlarmEventType.COMMUNICATION,
+ alarm_severity=AlarmEventSeverity.MAJOR)
+ self._intf_id = intf_id
+ self._onu_id = onu_id
+
+ def get_context_data(self):
+ return {
+ 'onu-id': self._onu_id,
+ 'olt-intf-id:': self._intf_id
+ }
diff --git a/voltha/extensions/alarms/onu/__init__.py b/voltha/extensions/alarms/onu/__init__.py
new file mode 100644
index 0000000..b0fb0b2
--- /dev/null
+++ b/voltha/extensions/alarms/onu/__init__.py
@@ -0,0 +1,13 @@
+# Copyright 2017-present Open Networking Foundation
+#
+# 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.
diff --git a/voltha/extensions/alarms/onu/onu_active_alarm.py b/voltha/extensions/alarms/onu/onu_active_alarm.py
new file mode 100644
index 0000000..fe0b554
--- /dev/null
+++ b/voltha/extensions/alarms/onu/onu_active_alarm.py
@@ -0,0 +1,50 @@
+# 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.
+
+from voltha.extensions.alarms.adapter_alarms import AlarmBase
+from voltha.protos.events_pb2 import AlarmEventType, AlarmEventSeverity, AlarmEventCategory
+
+
+class OnuActiveAlarm(AlarmBase):
+ def __init__(self, alarm_mgr, device_id, pon_id, onu_serial_number,
+ reg_id, olt_serial_number, ipv4_address=None):
+ super(OnuActiveAlarm, self).__init__(alarm_mgr, 'ONU',
+ alarm='ONU_ACTIVATED',
+ alarm_category=AlarmEventCategory.PON,
+ resource_id=pon_id,
+ alarm_type=AlarmEventType.EQUIPMENT,
+ alarm_severity=AlarmEventSeverity.CRITICAL)
+ self._pon_id = pon_id
+ self._onu_serial_number = onu_serial_number
+ self._device_id = device_id
+ self._olt_serial_number = olt_serial_number
+ self._host = ipv4_address
+ self._reg_id = reg_id
+
+ def get_context_data(self):
+ data = {
+ 'pon-id': self._pon_id,
+ 'serial-number': self._onu_serial_number,
+ 'olt_serial_number': self._olt_serial_number,
+ 'device_id': self._device_id,
+ 'registration_id': self._reg_id
+ }
+ if self._host is not None:
+ data['host'] = self._host
+
+ return data
+
+ def clear_alarm(self):
+ raise NotImplementedError('ONU Active Alarms are auto-clear')
+
diff --git a/voltha/extensions/alarms/onu/onu_discovery_alarm.py b/voltha/extensions/alarms/onu/onu_discovery_alarm.py
new file mode 100644
index 0000000..24211ef
--- /dev/null
+++ b/voltha/extensions/alarms/onu/onu_discovery_alarm.py
@@ -0,0 +1,36 @@
+# 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.
+from voltha.protos.events_pb2 import AlarmEventType, AlarmEventSeverity, AlarmEventCategory
+from voltha.extensions.alarms.adapter_alarms import AlarmBase
+
+
+class OnuDiscoveryAlarm(AlarmBase):
+ def __init__(self, alarm_mgr, pon_id, serial_number):
+ super(OnuDiscoveryAlarm, self).__init__(alarm_mgr, 'ONU Discovery',
+ alarm='Discovery',
+ alarm_category=AlarmEventCategory.PON,
+ resource_id=pon_id,
+ alarm_type=AlarmEventType.EQUIPMENT,
+ alarm_severity=AlarmEventSeverity.MAJOR)
+ self._pon_id = pon_id
+ self._serial_number = serial_number
+
+ def get_context_data(self):
+ return {
+ 'pon-id': self._pon_id,
+ 'serial-number': self._serial_number
+ }
+
+ def clear_alarm(self):
+ raise NotImplementedError('ONU Discovery Alarms are auto-clear')
diff --git a/voltha/extensions/alarms/onu/onu_dying_gasp_alarm.py b/voltha/extensions/alarms/onu/onu_dying_gasp_alarm.py
new file mode 100644
index 0000000..e92755c
--- /dev/null
+++ b/voltha/extensions/alarms/onu/onu_dying_gasp_alarm.py
@@ -0,0 +1,33 @@
+# 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.
+
+from voltha.protos.events_pb2 import AlarmEventType, AlarmEventSeverity, AlarmEventCategory
+from voltha.extensions.alarms.adapter_alarms import AlarmBase
+
+
+class OnuDyingGaspAlarm(AlarmBase):
+ def __init__(self, alarm_mgr, onu_id, intf_id):
+ super(OnuDyingGaspAlarm, self).__init__(alarm_mgr, 'onu DYING_GASP',
+ alarm='DYING_GASP',
+ alarm_category=AlarmEventCategory.ONU,
+ alarm_type=AlarmEventType.COMMUNICATION,
+ alarm_severity=AlarmEventSeverity.MINOR)
+ self._onu_id = onu_id
+ self._intf_id = intf_id
+
+ def get_context_data(self):
+ return {
+ 'onu-id': self._onu_id,
+ 'onu-intf-id': self._intf_id
+ }
diff --git a/voltha/extensions/alarms/onu/onu_los_alarm.py b/voltha/extensions/alarms/onu/onu_los_alarm.py
new file mode 100644
index 0000000..95c59d3
--- /dev/null
+++ b/voltha/extensions/alarms/onu/onu_los_alarm.py
@@ -0,0 +1,28 @@
+# 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.
+from voltha.protos.events_pb2 import AlarmEventType, AlarmEventSeverity, AlarmEventCategory
+from voltha.extensions.alarms.adapter_alarms import AlarmBase
+
+
+class OnuLosAlarm(AlarmBase):
+ def __init__(self, alarm_mgr, onu_id):
+ super(OnuLosAlarm, self).__init__(alarm_mgr, 'onu LOS',
+ alarm='LOS',
+ alarm_category=AlarmEventCategory.ONT,
+ alarm_type=AlarmEventType.COMMUNICATION,
+ alarm_severity=AlarmEventSeverity.MAJOR)
+ self._onu_id = onu_id
+
+ def get_context_data(self):
+ return {'onu-id': self._onu_id}