Adding generation of simulated alarms in PONSIM OLT.
- Alarms are raised and cleared periodically.

Change-Id: Ibce71353bb852afa28b640d440a186573fb6acd2
diff --git a/voltha/adapters/ponsim_olt/ponsim_olt.py b/voltha/adapters/ponsim_olt/ponsim_olt.py
index dd347c0..3d60633 100644
--- a/voltha/adapters/ponsim_olt/ponsim_olt.py
+++ b/voltha/adapters/ponsim_olt/ponsim_olt.py
@@ -39,13 +39,16 @@
 from voltha.protos.common_pb2 import LogLevel, OperStatus, ConnectStatus, \
     AdminState
 from voltha.protos.device_pb2 import DeviceType, DeviceTypes, Port, Device, \
-PmConfig, PmConfigs
+    PmConfig, PmConfigs
 from voltha.protos.events_pb2 import KpiEvent, KpiEventType, MetricValuePairs
+from voltha.protos.events_pb2 import AlarmEventType, AlarmEventSeverity, \
+    AlarmEventState, AlarmEventCategory
 from voltha.protos.health_pb2 import HealthStatus
 from google.protobuf.empty_pb2 import Empty
 
 from voltha.protos.logical_device_pb2 import LogicalPort, LogicalDevice
-from voltha.protos.openflow_13_pb2 import OFPPS_LIVE, OFPPF_FIBER, OFPPF_1GB_FD, \
+from voltha.protos.openflow_13_pb2 import OFPPS_LIVE, OFPPF_FIBER, \
+    OFPPF_1GB_FD, \
     OFPC_GROUP_STATS, OFPC_PORT_STATS, OFPC_TABLE_STATS, OFPC_FLOW_STATS, \
     ofp_switch_features, ofp_desc
 from voltha.protos.openflow_13_pb2 import ofp_port
@@ -55,21 +58,22 @@
 _ = third_party
 log = structlog.get_logger()
 
-
 PACKET_IN_VLAN = 4000
 is_inband_frame = BpfProgramFilter('(ether[14:2] & 0xfff) = 0x{:03x}'.format(
     PACKET_IN_VLAN))
 
+
 class AdapterPmMetrics:
-    def __init__(self,device):
-        self.pm_names = {'tx_64','tx_65_127', 'tx_128_255', 'tx_256_511',
-                        'tx_512_1023', 'tx_1024_1518', 'tx_1519_9k', 'rx_64',
-                        'rx_65_127', 'rx_128_255', 'rx_256_511', 'rx_512_1023',
-                        'rx_1024_1518', 'rx_1519_9k'}
+    def __init__(self, device):
+        self.pm_names = {'tx_64', 'tx_65_127', 'tx_128_255', 'tx_256_511',
+                         'tx_512_1023', 'tx_1024_1518', 'tx_1519_9k', 'rx_64',
+                         'rx_65_127', 'rx_128_255', 'rx_256_511',
+                         'rx_512_1023',
+                         'rx_1024_1518', 'rx_1519_9k'}
         self.device = device
         self.id = device.id
         self.name = 'ponsim_olt'
-        #self.id = "abc"
+        # self.id = "abc"
         self.default_freq = 150
         self.grouped = False
         self.freq_override = False
@@ -89,7 +93,7 @@
             # Update the callback to the new frequency.
             self.default_freq = pm_config.default_freq
             self.lc.stop()
-            self.lc.start(interval=self.default_freq/10)
+            self.lc.start(interval=self.default_freq / 10)
         for m in pm_config.metrics:
             self.pon_metrics_config[m.name].enabled = m.enabled
             self.nni_metrics_config[m.name].enabled = m.enabled
@@ -98,10 +102,10 @@
         pm_config = PmConfigs(
             id=self.id,
             default_freq=self.default_freq,
-            grouped = False,
-            freq_override = False)
+            grouped=False,
+            freq_override=False)
         for m in sorted(self.pon_metrics_config):
-            pm=self.pon_metrics_config[m] # Either will do they're the same
+            pm = self.pon_metrics_config[m]  # Either will do they're the same
             pm_config.metrics.extend([PmConfig(name=pm.name,
                                                type=pm.type,
                                                enabled=pm.enabled)])
@@ -115,7 +119,6 @@
         rtrn_port_metrics['nni'] = self.extract_nni_metrics(stats)
         return rtrn_port_metrics
 
-
     def extract_pon_metrics(self, stats):
         rtrn_pon_metrics = dict()
         for m in stats.metrics:
@@ -139,12 +142,11 @@
                  device_id=self.device.id)
         prefix = 'voltha.{}.{}'.format(self.name, self.device.id)
         self.lc = LoopingCall(callback, self.device.id, prefix)
-        self.lc.start(interval=self.default_freq/10)
+        self.lc.start(interval=self.default_freq / 10)
 
 
 @implementer(IAdapterInterface)
 class PonSimOltAdapter(object):
-
     name = 'ponsim_olt'
 
     supported_device_types = [
@@ -193,7 +195,8 @@
         raise NotImplementedError()
 
     def update_pm_config(self, device, pm_config):
-        log.info("adapter-update-pm-config", device=device, pm_config=pm_config)
+        log.info("adapter-update-pm-config", device=device,
+                 pm_config=pm_config)
         handler = self.devices_handlers[device.id]
         handler.update_pm_config(device, pm_config)
 
@@ -230,7 +233,7 @@
 
     def update_flows_bulk(self, device, flows, groups):
         log.info('bulk-flow-update', device_id=device.id,
-                  flows=flows, groups=groups)
+                 flows=flows, groups=groups)
         assert len(groups.items) == 0
         handler = self.devices_handlers[device.id]
         return handler.update_flow_table(flows.items)
@@ -247,7 +250,6 @@
         raise NotImplementedError()
 
     def receive_packet_out(self, logical_device_id, egress_port_no, msg):
-
         def ldi_to_di(ldi):
             di = self.logical_device_id_to_root_device_id.get(ldi)
             if di is None:
@@ -262,7 +264,6 @@
 
 
 class PonSimOltHandler(object):
-
     def __init__(self, adapter, device_id):
         self.adapter = adapter
         self.adapter_agent = adapter.adapter_agent
@@ -275,6 +276,7 @@
         self.ofp_port_no = None
         self.interface = registry('main').get_args().interface
         self.pm_metrics = None
+        self.alarm_lc = None
 
     def __del__(self):
         if self.io_port is not None:
@@ -301,16 +303,16 @@
 
         device.root = True
         device.vendor = 'ponsim'
-        device.model ='n/a'
+        device.model = 'n/a'
         device.serial_number = device.host_and_port
         device.connect_status = ConnectStatus.REACHABLE
         self.adapter_agent.update_device(device)
 
         # Now set the initial PM configuration for this device
-        self.pm_metrics=AdapterPmMetrics(device)
+        self.pm_metrics = AdapterPmMetrics(device)
         pm_config = self.pm_metrics.make_proto()
         log.info("initial-pm-config", pm_config=pm_config)
-        self.adapter_agent.update_device_pm_config(pm_config,init=True)
+        self.adapter_agent.update_device_pm_config(pm_config, init=True)
 
         nni_port = Port(
             port_no=2,
@@ -357,7 +359,8 @@
             id='nni',
             ofp_port=ofp_port(
                 port_no=info.nni_port,
-                hw_addr=mac_str_to_tuple('00:00:00:00:00:%02x' % info.nni_port),
+                hw_addr=mac_str_to_tuple(
+                    '00:00:00:00:00:%02x' % info.nni_port),
                 name='nni',
                 config=0,
                 state=OFPPS_LIVE,
@@ -401,9 +404,12 @@
         # Start collecting stats from the device after a brief pause
         self.start_kpi_collection(device.id)
 
+        # Start generating simulated alarms
+        self.start_alarm_simulation(device.id)
+
     def rcv_io(self, port, frame):
         self.log.info('reveived', iface_name=port.iface_name,
-                       frame_len=len(frame))
+                      frame_len=len(frame))
         pkt = Ether(frame)
         if pkt.haslayer(Dot1Q):
             outer_shim = pkt.getlayer(Dot1Q)
@@ -423,8 +429,6 @@
                 self.adapter_agent.send_packet_in(
                     packet=str(popped_frame), **kw)
 
-
-
     def update_flow_table(self, flows):
         stub = ponsim_pb2.PonSimStub(self.get_channel())
         self.log.info('pushing-olt-flow-table')
@@ -435,7 +439,8 @@
         self.log.info('success')
 
     def update_pm_config(self, device, pm_config):
-        log.info("handler-update-pm-config", device=device, pm_config=pm_config)
+        log.info("handler-update-pm-config", device=device,
+                 pm_config=pm_config)
         self.pm_metrics.update(pm_config)
 
     def send_proxied_message(self, proxy_address, msg):
@@ -472,7 +477,7 @@
         self.adapter_agent.update_device(device)
 
         # Sleep 10 secs, simulating a reboot
-        #TODO: send alert and clear alert after the reboot
+        # TODO: send alert and clear alert after the reboot
         yield asleep(10)
 
         # Change the operational status back to its previous state.  With a
@@ -498,7 +503,7 @@
 
         # Remove the logical device
         logical_device = self.adapter_agent.get_logical_device(
-                                                    self.logical_device_id)
+            self.logical_device_id)
         self.adapter_agent.delete_logical_device(logical_device)
 
         # Disable all child devices first
@@ -513,13 +518,15 @@
         # close the frameio port
         registry('frameio').close_port(self.io_port)
 
+        # Stop the generation of simulated alarms
+        self.stop_alarm_simulation(self.device_id)
+
         # TODO:
         # 1) Remove all flows from the device
         # 2) Remove the device from ponsim
 
         self.log.info('disabled', device_id=device.id)
 
-
     def reenable(self):
         self.log.info('re-enabling', device_id=self.device_id)
 
@@ -593,7 +600,6 @@
 
         self.log.info('re-enabled', device_id=device.id)
 
-
     def delete(self):
         self.log.info('deleting', device_id=self.device_id)
 
@@ -613,7 +619,7 @@
             try:
                 # Step 1: gather metrics from device
                 port_metrics = \
-                self.pm_metrics.collect_port_metrics(self.get_channel())
+                    self.pm_metrics.collect_port_metrics(self.get_channel())
 
                 # Step 2: prepare the KpiEvent for submission
                 # we can time-stamp them here (or could use time derived from OLT
@@ -636,5 +642,101 @@
 
             except Exception as e:
                 log.exception('failed-to-submit-kpis', e=e)
+
         self.pm_metrics.start_collector(_collect)
 
+    def start_alarm_simulation(self, device_id):
+        log.info("starting-alarm-simulation", device_id=device_id)
+
+        """Simulate periodic device alarms"""
+        import random
+
+        @inlineCallbacks
+        def _generate_alarm(device_id):
+            log.info("generate-alarm", device_id=device_id)
+
+            alarm = _prepare_alarm(device_id)
+            _raise_alarm(alarm)
+            yield asleep(random.randint(30, 50))
+            _clear_alarm(alarm)
+
+        def _prepare_alarm(device_id):
+            log.info("prepare-alarm", device_id=device_id)
+
+            try:
+                # Randomly choose values for each enum types
+                severity = random.choice(list(
+                    v for k, v in
+                    AlarmEventSeverity.DESCRIPTOR.enum_values_by_name.items()))
+
+                type = random.choice(list(
+                    v for k, v in
+                    AlarmEventType.DESCRIPTOR.enum_values_by_name.items()))
+
+                category = random.choice(list(
+                    v for k, v in
+                    AlarmEventCategory.DESCRIPTOR.enum_values_by_name.items()))
+
+                current_context = {}
+                for key, value in self.__dict__.items():
+                    current_context[key] = str(value)
+
+                return self.adapter_agent.create_alarm(
+                    resource_id=device_id,
+                    type=type.number,
+                    category=category.number,
+                    severity=severity.number,
+                    context=current_context)
+
+            except Exception as e:
+                log.exception('failed-to-prepare-alarm', e=e)
+
+        def _raise_alarm(alarm_event):
+            log.info("raise-alarm", alarm_event=alarm_event)
+
+            try:
+                alarm_event.description = \
+                    "RAISE simulated alarm - " \
+                    "resource:{} " \
+                    "type:{} " \
+                    "severity:{} " \
+                    "category:{}".format(
+                        alarm_event.resource_id,
+                        alarm_event.type,
+                        alarm_event.severity,
+                        alarm_event.category
+                    )
+
+                self.adapter_agent.submit_alarm(alarm_event)
+
+            except Exception as e:
+                log.exception('failed-to-raise-alarm', e=e)
+
+        def _clear_alarm(alarm_event):
+            log.info("clear-alarm", alarm_event=alarm_event)
+
+            try:
+                alarm_event.description = \
+                    "CLEAR simulated alarm - " \
+                    "resource:{} " \
+                    "type:{} " \
+                    "severity:{} " \
+                    "category:{}".format(
+                        alarm_event.resource_id,
+                        alarm_event.type,
+                        alarm_event.severity,
+                        alarm_event.category
+                    )
+
+                alarm_event.state = AlarmEventState.CLEARED
+                self.adapter_agent.submit_alarm(alarm_event)
+
+            except Exception as e:
+                log.exception('failed-to-clear-alarm', e=e)
+
+        self.alarm_lc = LoopingCall(_generate_alarm, device_id)
+        self.alarm_lc.start(60)
+
+    def stop_alarm_simulation(self, device_id):
+        log.info("stopping-alarm-simulation", device_id=device_id)
+        self.alarm_lc.stop()