VOL-773 Basic Alarm reporting

added los and dying gasp to openolt_alarms.py.

Added alarms directory for alarms classes.   Modified from adtran_olt/alarms
Currently calling calling adapter_alarms.py, olt_los_alarm.py,
onu_dying_gasp_alarm.py  from  openolt_alarms.py

The openolt/alarms/.  was modified from voltha/adtran_olt/alarms to get a
basis for alarms classes that will be used to get consensus for SEBA alarm templates

Added ONU to events.proto AlarmEventCategory

Synched with master voltha/adapters/openolt/openolt_device.py

Change-Id: I790462a709b8f60841fe93cf9bf96f0e80e0beac
diff --git a/voltha/adapters/openolt/alarms/__init__.py b/voltha/adapters/openolt/alarms/__init__.py
new file mode 100644
index 0000000..b0fb0b2
--- /dev/null
+++ b/voltha/adapters/openolt/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/adapters/openolt/alarms/adapter_alarms.py b/voltha/adapters/openolt/alarms/adapter_alarms.py
new file mode 100644
index 0000000..50d4d9b
--- /dev/null
+++ b/voltha/adapters/openolt/alarms/adapter_alarms.py
@@ -0,0 +1,129 @@
+#
+# 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, AlarmEventCategory
+
+# TODO: In the device adapter, the following alarms are still TBD
+# 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:
+    def __init__(self, adapter, device_id, logical_device_id):
+        self.log = structlog.get_logger(device_id=device_id)
+        self.adapter = adapter
+        self.device_id = device_id
+        self.logical_device_id = logical_device_id
+        self.lc = None
+
+    def format_id(self, alarm):
+        return 'voltha.{}.{}.{}'.format(self.adapter.adapter.name,
+                                        self.device_id,
+                                        alarm)
+
+    def format_description(self, _object, alarm, status):
+        return '{} Alarm - {} - {}'.format(_object.upper(),
+                                           alarm.upper(),
+                                           'Raised' if status else 'Cleared')
+
+    def send_alarm(self, context_data, alarm_data):
+        try:
+            current_context = {}
+
+            if isinstance(context_data, dict):
+                for key, value in context_data.iteritems():
+                    current_context[key] = str(value)
+
+            alarm_event = self.adapter.create_alarm(
+                id=alarm_data.get('id', 'voltha.{}.{}.olt'.format(self.adapter.adapter_name,
+                                                                  self.device_id)),
+                resource_id=str(alarm_data.get('resource_id', self.device_id)),
+                description="{}.{} - {}".format(self.adapter.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.submit_alarm(self.device_id, alarm_event)
+
+        except Exception as e:
+            self.log.exception('failed-to-send-alarm', e=e)
+            raise Exception(e)
+
+
+class AlarmBase(object):
+    def __init__(self, handler, object_type, alarm,
+                 alarm_category,
+                 alarm_indication,
+                 resource_id=None,
+                 alarm_type=AlarmEventType.EQUIPMENT,
+                 alarm_severity=AlarmEventSeverity.CRITICAL
+                 ):
+
+        self._handler = handler
+        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
+        self.alarm_indication = alarm_indication
+
+    def get_alarm_data(self, status):
+        data = {
+            'ts': arrow.utcnow().timestamp,
+            'description': self._handler.format_description(self._object_type,
+                                                            self._alarm,
+                                                            status),
+            'id': self._handler.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):
+        return {}   # You should override this if needed
+
+    def raise_alarm(self):
+        alarm_data = self.get_alarm_data(True)
+        context_data = self.get_context_data()
+        self._handler.send_alarm(context_data, alarm_data)
+
+    def clear_alarm(self):
+        alarm_data = self.get_alarm_data(False)
+        context_data = self.get_context_data()
+        self._handler.send_alarm(context_data, alarm_data)
diff --git a/voltha/adapters/openolt/alarms/heartbeat_alarm.py b/voltha/adapters/openolt/alarms/heartbeat_alarm.py
new file mode 100644
index 0000000..6115eb3
--- /dev/null
+++ b/voltha/adapters/openolt/alarms/heartbeat_alarm.py
@@ -0,0 +1,32 @@
+# 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
+"""
+TODO:  To be Implemented in the openolt alarms
+"""
+
+
+class HeartbeatAlarm(AlarmBase):
+    def __init__(self, handler, object_type='olt', heartbeat_misses=0):
+        super(HeartbeatAlarm, self).__init__(handler, 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/adapters/openolt/alarms/olt_los_alarm.py b/voltha/adapters/openolt/alarms/olt_los_alarm.py
new file mode 100644
index 0000000..bd1f8f7
--- /dev/null
+++ b/voltha/adapters/openolt/alarms/olt_los_alarm.py
@@ -0,0 +1,42 @@
+# 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 OltLosAlarm(AlarmBase):
+    def __init__(self, handler, alarm_indication):
+        try:
+            super(OltLosAlarm, self).__init__(handler, 'olt LOS',
+                                              alarm='LOS',
+                                              alarm_indication=alarm_indication,
+                                              alarm_category=AlarmEventCategory.OLT,
+                                              alarm_type=AlarmEventType.COMMUNICATION,
+                                              alarm_severity=AlarmEventSeverity.MAJOR)
+            self._intf_id = self.alarm_indication.intf_id
+            self._status = self.alarm_indication.status
+        except Exception as e:
+            self._handler.adapter.log.exception("olt-los-alarm-object", errmsg=e.message)
+            raise Exception(e)
+
+    def get_context_data(self):
+        try:
+            retval = {'olt-id': self._handler.device_id,
+                      'logical-device-id': self._handler.logical_device_id,
+                      'olt-intf-id:': self.alarm_indication.intf_id
+                      }
+        except Exception as e:
+            raise Exception(e)
+        return retval
diff --git a/voltha/adapters/openolt/alarms/onu_active_alarm.py b/voltha/adapters/openolt/alarms/onu_active_alarm.py
new file mode 100644
index 0000000..fa24ffc
--- /dev/null
+++ b/voltha/adapters/openolt/alarms/onu_active_alarm.py
@@ -0,0 +1,52 @@
+# 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 adapter_alarms import AlarmBase
+from voltha.protos.events_pb2 import AlarmEventType, AlarmEventSeverity, AlarmEventCategory
+"""
+TODO:  To be Implemented in the openolt alarms
+"""
+
+
+class OnuActiveAlarm(AlarmBase):
+    def __init__(self, handler, pon_id, serial_number, reg_id):
+        super(OnuActiveAlarm, self).__init__(handler, '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._serial_number = serial_number
+        self._device_id = handler.device_id
+        device = handler.adapter_agent.get_device(handler.device_id)
+        self._olt_serial_number = device.serial_number
+        self._host = device.ipv4_address
+        self._datapath_id = device.parent_id
+        self._reg_id = reg_id
+
+    def get_context_data(self):
+        return {
+            'pon-id': self._pon_id,
+            'serial-number': self._serial_number,
+            'host': self._host,
+            'olt_serial_number': self._olt_serial_number,
+            'datapath_id': self._datapath_id,
+            'device_id' : self._device_id,
+            'registration_id' : self._reg_id
+        }
+
+    def clear_alarm(self):
+        raise NotImplementedError('ONU Active Alarms are auto-clear')
+
diff --git a/voltha/adapters/openolt/alarms/onu_discovery_alarm.py b/voltha/adapters/openolt/alarms/onu_discovery_alarm.py
new file mode 100644
index 0000000..f3e2b57
--- /dev/null
+++ b/voltha/adapters/openolt/alarms/onu_discovery_alarm.py
@@ -0,0 +1,39 @@
+# 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
+"""
+TODO:  To be Implemented in the openolt alarms
+"""
+
+
+class OnuDiscoveryAlarm(AlarmBase):
+    def __init__(self, handler, pon_id, serial_number):
+        super(OnuDiscoveryAlarm, self).__init__(handler, 'ONU Discovery',
+                                                alarm='Discovery',
+                                                alarm_category=AlarmEventCategory.PON,
+                                                resource_id=pon_id,
+                                                alarm_type=AlarmEventType.EQUIPMENT,
+                                                alarm_severity=AlarmEventSeverity.CRITICAL)
+        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/adapters/openolt/alarms/onu_dying_gasp_alarm.py b/voltha/adapters/openolt/alarms/onu_dying_gasp_alarm.py
new file mode 100644
index 0000000..71d264f
--- /dev/null
+++ b/voltha/adapters/openolt/alarms/onu_dying_gasp_alarm.py
@@ -0,0 +1,37 @@
+# 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 OnuDyingGaspAlarm(AlarmBase):
+    def __init__(self, handler, alarm_indication, onu_id):
+        super(OnuDyingGaspAlarm, self).__init__(handler, 'onu DYING_GASP',
+                                                alarm='DYING_GASP',
+                                                alarm_indication=alarm_indication,
+                                                alarm_category=AlarmEventCategory.ONU,
+                                                alarm_type=AlarmEventType.COMMUNICATION,
+                                                alarm_severity=AlarmEventSeverity.MINOR)
+        self._onu_id = onu_id
+        self._intf_id = self.alarm_indication.intf_id
+
+    def get_context_data(self):
+        try:
+            retval = {'onu-id': self._onu_id,
+                      'onu-intf-id': self._intf_id,
+                      "logical-device-id": self._handler.logical_device_id}
+        except Exception as e:
+            raise Exception(e)
+        return retval
diff --git a/voltha/adapters/openolt/alarms/onu_los_alarm.py b/voltha/adapters/openolt/alarms/onu_los_alarm.py
new file mode 100644
index 0000000..1bfdd4d
--- /dev/null
+++ b/voltha/adapters/openolt/alarms/onu_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 adapter_alarms import AlarmBase
+"""
+TODO:  To be Implemented in the openolt alarms
+"""
+
+
+class OnuLosAlarm(AlarmBase):
+    def __init__(self, handler, onu_id, intf_id):
+        super(OnuLosAlarm, self).__init__(handler, 'onu LOS',
+                                          alarm='LOS',
+                                          alarm_category=AlarmEventCategory.ONT,
+                                          alarm_type=AlarmEventType.COMMUNICATION,
+                                          alarm_severity=AlarmEventSeverity.MAJOR)
+        self._onu_id = onu_id
+        self._intf_id = intf_id
+
+    def get_context_data(self):
+        return {'onu-id': self._onu_id}
diff --git a/voltha/adapters/openolt/openolt_alarms.py b/voltha/adapters/openolt/openolt_alarms.py
index 576065e..74f34c3 100644
--- a/voltha/adapters/openolt/openolt_alarms.py
+++ b/voltha/adapters/openolt/openolt_alarms.py
@@ -14,17 +14,38 @@
 # limitations under the License.
 #
 
+import arrow
+from voltha.protos.events_pb2 import AlarmEventType, \
+    AlarmEventSeverity, AlarmEventState, AlarmEventCategory
+import voltha.adapters.openolt.openolt_platform as platform
+from voltha.protos.device_pb2 import Port
+from voltha.adapters.openolt.alarms.adapter_alarms import AdapterAlarms
+from voltha.adapters.openolt.alarms.olt_los_alarm import OltLosAlarm
+from voltha.adapters.openolt.alarms.onu_dying_gasp_alarm import OnuDyingGaspAlarm
 
 
 class OpenOltAlarmMgr(object):
-    def __init__(self, log):
+    def __init__(self, log, adapter_agent, device_id, logical_device_id):
+        """
+        20180711 -  Addition of adapter_agent and device_id
+            to facilitate alarm processing and kafka posting
+        :param log:
+        :param adapter_agent:
+        :param device_id:
+        """
         self.log = log
+        self.adapter_agent = adapter_agent
+        self.device_id = device_id
+        self.logical_device_id = logical_device_id
+        try:
+            self.alarms = AdapterAlarms(self.adapter_agent, self.device_id, self.logical_device_id)
+        except Exception as initerr:
+            self.log.exception("alarmhandler-init-error", errmsg=initerr.message)
+            raise Exception(initerr)
 
     def process_alarms(self, alarm_ind):
-        self.log.debug('alarm indication', alarm=alarm_ind)
-
         try:
-
+            self.log.debug('alarm-indication', alarm=alarm_ind, device_id=self.device_id)
             if alarm_ind.HasField('los_ind'):
                 self.los_indication(alarm_ind.los_ind)
             elif alarm_ind.HasField('dying_gasp_ind'):
@@ -62,15 +83,54 @@
                            alarm=alarm_ind)
 
     def los_indication(self, los_ind):
-        self.log.debug('los indication received', los_ind=los_ind)
+
         try:
-            self.log.info('los indication', intf_id=los_ind.intf_id,
-                          status=los_ind.status)
+            self.log.debug('los indication received', los_ind=los_ind,
+                           int_id=los_ind.intf_id, status=los_ind.status)
+
+            try:
+
+                if (los_ind.status == 1 or los_ind.status == "on"):
+                    OltLosAlarm(self.alarms, alarm_indication=los_ind).raise_alarm()
+                else:
+                    OltLosAlarm(self.alarms, alarm_indication=los_ind).clear_alarm()
+            except Exception as alarm_err:
+                self.log.error('los-indication', errmsg=alarm_err.message)
+
         except Exception as e:
-            self.log.error('error parsing los indication', error=e)
+            self.log.error('los-indication', errmsg=e.message)
 
     def dying_gasp_indication(self, dying_gasp_ind):
-        self.log.info('not implemented yet')
+        try:
+            alarm_dgi = dying_gasp_ind
+            onu_id = alarm_dgi.onu_id
+            self.log.debug('openolt-alarmindication-dispatch-dying-gasp', int_id=alarm_dgi.intf_id,
+                           onu_id=alarm_dgi.onu_id, status=alarm_dgi.status)
+            try:
+                """
+                Get the ONU ID.  This isw necessary since the dirvers are 
+                not passing the id.  They are using a placeholder
+                """
+                onu_device_id = "place_holder"
+                try:
+                    ind_onu_id = dying_gasp_ind.onu_id
+                    onu_device = self.adapter_agent.get_child_device(
+                        self.device_id,
+                        parent_port_no=platform.intf_id_to_port_no(
+                            dying_gasp_ind.intf_id, Port.PON_OLT),
+                        onu_id=dying_gasp_ind.onu_id)
+                    onu_device_id = onu_device.id
+                except Exception as inner:
+                    self.log.exception('dying-gasp-indication-resolve_onu-id', errmsg=inner.message)
+                if (dying_gasp_ind.status == 1 or dying_gasp_ind.status == "on"):
+                    OnuDyingGaspAlarm(self.alarms, dying_gasp_ind, onu_device_id).raise_alarm()
+                else:
+                    OnuDyingGaspAlarm(self.alarms, dying_gasp_ind, onu_device_id).clear_alarm()
+            except Exception as alarm_err:
+                self.log.exception('dying-gasp-indication', errmsg=alarm_err.message)
+
+        except Exception as e:
+            self.log.error('dying_gasp_indication', error=e)
 
     def onu_alarm_indication(self, onu_alarm_ind):
         self.log.info('not implemented yet')
@@ -97,4 +157,4 @@
         self.log.info('not implemented yet')
 
     def onu_processing_error_indication(self, onu_processing_error_ind):
-        self.log.info('not implemented yet')
\ No newline at end of file
+        self.log.info('not implemented yet')
diff --git a/voltha/adapters/openolt/openolt_device.py b/voltha/adapters/openolt/openolt_device.py
index d1f6a2d..5971b68 100644
--- a/voltha/adapters/openolt/openolt_device.py
+++ b/voltha/adapters/openolt/openolt_device.py
@@ -172,7 +172,8 @@
 
         self.stub = openolt_pb2_grpc.OpenoltStub(self.channel)
         self.flow_mgr = OpenOltFlowMgr(self.log, self.stub, self.device_id)
-        self.alarm_mgr = OpenOltAlarmMgr(self.log)
+        self.alarm_mgr = OpenOltAlarmMgr(self.log, self.adapter_agent, self.device_id,
+                                         self.logical_device_id)
         self.stats_mgr = OpenOltStatisticsMgr(self, self.log)
 
     def do_state_up(self, event):
diff --git a/voltha/core/adapter_agent.py b/voltha/core/adapter_agent.py
index e1dd1ee..19f03f6 100644
--- a/voltha/core/adapter_agent.py
+++ b/voltha/core/adapter_agent.py
@@ -908,7 +908,6 @@
             self.log.exception('failed-kpi-submission',
                                type=type(kpi_event_msg))
 
-
     # # ~~~~~~~~~~~~~~~~~~~~~~~~~~ Handle flow stats ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
     def update_flow_stats(self, logical_device_id, flow_id, packet_count=0,
@@ -927,8 +926,8 @@
                 'logical_devices/{}/flows'.format(logical_device_id),
                 flow_to_update.id, flow_to_update)
         else:
-            self.log.warn('flow-to-update-not-found', logical_device_id=
-                logical_device_id, flow_id=flow_id)
+            self.log.warn('flow-to-update-not-found',
+                          logical_device_id=logical_device_id, flow_id=flow_id)
 
     # ~~~~~~~~~~~~~~~~~~~ Handle alarm submissions ~~~~~~~~~~~~~~~~~~~~~
 
@@ -938,10 +937,11 @@
                      category=AlarmEventCategory.PON,
                      severity=AlarmEventSeverity.MINOR,
                      state=AlarmEventState.RAISED,
-                     context=None):
+                     context=None,
+                     logical_device_id=None):
 
         # Construct the ID if it is not provided
-        if id == None:
+        if id is None:
             id = 'voltha.{}.{}'.format(self.adapter_name, resource_id)
 
         return AlarmEvent(
@@ -955,7 +955,8 @@
             reported_ts=arrow.utcnow().timestamp,
             raised_ts=raised_ts,
             changed_ts=changed_ts,
-            context=context
+            context=context,
+            logical_device_id=logical_device_id
         )
 
     def filter_alarm(self, device_id, alarm_event):
diff --git a/voltha/protos/events.proto b/voltha/protos/events.proto
index e583546..883bc68 100644
--- a/voltha/protos/events.proto
+++ b/voltha/protos/events.proto
@@ -75,6 +75,7 @@
         PON = 0;
         OLT = 1;
         ONT = 2;
+        ONU = 3;
     }
 }
 
@@ -137,4 +138,7 @@
 
     // Key/Value storage for extra information that may give context to the alarm
     map<string, string> context = 11;
+
+    // logical device id
+    string logical_device_id = 12;
 }