VOL-720: Integrated OpenOMCI Alarms with the Alarm Manager/Kafka
Change-Id: Ifecee4420903f0d844822436b7e790c723c89adc
diff --git a/voltha/adapters/brcm_openomci_onu/brcm_openomci_onu_handler.py b/voltha/adapters/brcm_openomci_onu/brcm_openomci_onu_handler.py
index 4254bf9..146253d 100644
--- a/voltha/adapters/brcm_openomci_onu/brcm_openomci_onu_handler.py
+++ b/voltha/adapters/brcm_openomci_onu/brcm_openomci_onu_handler.py
@@ -25,6 +25,7 @@
from heartbeat import HeartBeat
from voltha.extensions.kpi.onu.onu_pm_metrics import OnuPmMetrics
from voltha.extensions.kpi.onu.onu_omci_pm import OnuOmciPmMetrics
+from voltha.extensions.alarms.adapter_alarms import AdapterAlarms
from common.utils.indexpool import IndexPool
import voltha.core.flow_decomposer as fd
@@ -71,6 +72,7 @@
self.proxy_address = None
self.tx_id = 0
self._enabled = False
+ self.alarms = None
self.pm_metrics = None
self._omcc_version = OMCCVersion.Unknown
self._total_tcont_count = 0 # From ANI-G ME
@@ -197,6 +199,12 @@
self.log.info("initial-pm-config", pm_config=pm_config)
self.adapter_agent.update_device_pm_config(pm_config, init=True)
+ ############################################################################
+ # Setup Alarm handler
+ self.alarms = AdapterAlarms(self.adapter_agent, device.id, self.logical_device_id)
+ # Note, ONU ID and UNI intf set in add_uni_port method
+ self._onu_omci_device.alarm_synchronizer.set_alarm_params(mgr=self.alarms,
+ ani_ports=[self._pon])
self.enabled = True
else:
self.log.info('onu-already-activated')
@@ -877,6 +885,8 @@
self._unis[uni_port.port_number] = uni_port
+ self._onu_omci_device.alarm_synchronizer.set_alarm_params(onu_id=self._onu_indication.onu_id,
+ uni_ports=self._unis.values())
# TODO: this should be in the PonPortclass
pon_port = self._pon.get_port()
self.adapter_agent.delete_port_reference_from_parent(self.device_id,
diff --git a/voltha/extensions/alarms/onu/onu_equipment_alarm.py b/voltha/extensions/alarms/onu/onu_equipment_alarm.py
new file mode 100644
index 0000000..e7e3a7a
--- /dev/null
+++ b/voltha/extensions/alarms/onu/onu_equipment_alarm.py
@@ -0,0 +1,45 @@
+# 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 OnuEquipmentAlarm(AlarmBase):
+ """
+ The ONU Equipment Alarm is reported by both the CircuitPack (ME #6) and
+ the ONT-G (ME # 256) to indicate failure on an internal interface or
+ failed self-test.
+
+ For CircuitPack equipment alarms, the intf_id reported is that of the
+ UNI's logical port number
+
+ For ONT-G equipment alarms, the intf_id reported is that of the PON/ANI
+ physical port number
+
+ Note: Some ONUs may use this alarm to report a self-test failure or may
+ may report it with a different alarm number specifically for a
+ self-test failure.
+ """
+ def __init__(self, alarm_mgr, onu_id, intf_id):
+ super(OnuEquipmentAlarm, self).__init__(alarm_mgr, object_type='onu equipment',
+ alarm='ONU_EQUIPMENT',
+ alarm_category=AlarmEventCategory.ONU,
+ alarm_type=AlarmEventType.EQUIPTMENT,
+ alarm_severity=AlarmEventSeverity.CRITICAL)
+ 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_high_rx_optical_power_alarm.py b/voltha/extensions/alarms/onu/onu_high_rx_optical_power_alarm.py
new file mode 100644
index 0000000..7b59d55
--- /dev/null
+++ b/voltha/extensions/alarms/onu/onu_high_rx_optical_power_alarm.py
@@ -0,0 +1,37 @@
+# Copyright 2018 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.
+from voltha.protos.events_pb2 import AlarmEventType, AlarmEventSeverity, AlarmEventCategory
+from voltha.extensions.alarms.adapter_alarms import AlarmBase
+
+
+class OnuHighRxOpticalAlarm(AlarmBase):
+ """
+ The ONU High Tx Optical Power Alarm is reported by the ANI-G (ME # 263) to
+ indicate that the received downstream optical power above threshold..
+
+ For ANI-G equipment alarms, the intf_id reported is that of the PON/ANI
+ physical port number
+ """
+ def __init__(self, alarm_mgr, onu_id, intf_id):
+ super(OnuHighRxOpticalAlarm, self).__init__(alarm_mgr, object_type='onu high rx optical power',
+ alarm='ONU_HIGH_RX_OPTICAL',
+ alarm_category=AlarmEventCategory.ONU,
+ 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,
+ 'onu-intf-id': self._intf_id}
diff --git a/voltha/extensions/alarms/onu/onu_high_tx_optical_power_alarm.py b/voltha/extensions/alarms/onu/onu_high_tx_optical_power_alarm.py
new file mode 100644
index 0000000..64caefe
--- /dev/null
+++ b/voltha/extensions/alarms/onu/onu_high_tx_optical_power_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 voltha.extensions.alarms.adapter_alarms import AlarmBase
+
+
+class OnuHighTxOpticalAlarm(AlarmBase):
+ """
+ The ONU High Tx Optical Power Alarm is reported by the ANI-G (ME # 263) to
+ indicate that the received downstream optical power above upper threshold.
+
+ For ANI-G equipment alarms, the intf_id reported is that of the PON/ANI
+ physical port number
+ """
+ def __init__(self, alarm_mgr, onu_id, intf_id):
+ super(OnuHighTxOpticalAlarm, self).__init__(alarm_mgr, object_type='onu high tx optical power',
+ alarm='ONU_HIGH_TX_OPTICAL',
+ alarm_category=AlarmEventCategory.ONU,
+ 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,
+ 'onu-intf-id': self._intf_id}
\ No newline at end of file
diff --git a/voltha/extensions/alarms/onu/onu_laser_bias_current_alarm.py b/voltha/extensions/alarms/onu/onu_laser_bias_current_alarm.py
new file mode 100644
index 0000000..8daf5a6
--- /dev/null
+++ b/voltha/extensions/alarms/onu/onu_laser_bias_current_alarm.py
@@ -0,0 +1,38 @@
+# 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 OnuLaserBiasAlarm(AlarmBase):
+ """
+ The ONU Laser Bias Current Alarm is reported by the ANI-G (ME # 263) to
+ indicate that the laser bias current above threshold determined by
+ vendor and that laser end of life is pending
+
+ For ANI-G equipment alarms, the intf_id reported is that of the PON/ANI
+ physical port number
+ """
+ def __init__(self, alarm_mgr, onu_id, intf_id):
+ super(OnuLaserBiasAlarm, self).__init__(alarm_mgr, object_type='onu laser bias current',
+ alarm='ONU_LASER_BIAS_CURRENT',
+ alarm_category=AlarmEventCategory.ONU,
+ alarm_type=AlarmEventType.EQUIPTMENT,
+ 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,
+ 'onu-intf-id': self._intf_id}
diff --git a/voltha/extensions/alarms/onu/onu_laser_eol_alarm.py b/voltha/extensions/alarms/onu/onu_laser_eol_alarm.py
new file mode 100644
index 0000000..fa5039c
--- /dev/null
+++ b/voltha/extensions/alarms/onu/onu_laser_eol_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 OnuLaserEolAlarm(AlarmBase):
+ """
+ The ONU Laser End-of-Lifer Alarm is reported by both the CircuitPack (ME #6)
+ to indicate that failure of transmit laser imminent
+
+ The intf_id reported is that of the UNI's logical port number
+ """
+ def __init__(self, alarm_mgr, onu_id, intf_id):
+ super(OnuLaserEolAlarm, self).__init__(alarm_mgr, object_type='onu laser EOL',
+ alarm='ONU_LASER_EOL',
+ alarm_category=AlarmEventCategory.ONU,
+ alarm_type=AlarmEventType.EQUIPTMENT,
+ 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,
+ 'onu-intf-id': self._intf_id}
diff --git a/voltha/extensions/alarms/onu/onu_low_rx_optical_power_alarm.py b/voltha/extensions/alarms/onu/onu_low_rx_optical_power_alarm.py
new file mode 100644
index 0000000..ee6f4d2
--- /dev/null
+++ b/voltha/extensions/alarms/onu/onu_low_rx_optical_power_alarm.py
@@ -0,0 +1,37 @@
+# Copyright 2018 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.
+from voltha.protos.events_pb2 import AlarmEventType, AlarmEventSeverity, AlarmEventCategory
+from voltha.extensions.alarms.adapter_alarms import AlarmBase
+
+
+class OnuLowRxOpticalAlarm(AlarmBase):
+ """
+ The ONU Low Rx Optical Power Alarm is reported by the ANI-G (ME # 263) to
+ indicate that the received downstream optical power below threshold.
+
+ For ANI-G equipment alarms, the intf_id reported is that of the PON/ANI
+ physical port number
+ """
+ def __init__(self, alarm_mgr, onu_id, intf_id):
+ super(OnuLowRxOpticalAlarm, self).__init__(alarm_mgr, object_type='onu low rx optical power',
+ alarm='ONU_LOW_RX_OPTICAL',
+ alarm_category=AlarmEventCategory.ONU,
+ 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,
+ 'onu-intf-id': self._intf_id}
diff --git a/voltha/extensions/alarms/onu/onu_low_tx_optical_power_alarm.py b/voltha/extensions/alarms/onu/onu_low_tx_optical_power_alarm.py
new file mode 100644
index 0000000..e28a556
--- /dev/null
+++ b/voltha/extensions/alarms/onu/onu_low_tx_optical_power_alarm.py
@@ -0,0 +1,37 @@
+# Copyright 2018 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.
+from voltha.protos.events_pb2 import AlarmEventType, AlarmEventSeverity, AlarmEventCategory
+from voltha.extensions.alarms.adapter_alarms import AlarmBase
+
+
+class OnuLowTxOpticalAlarm(AlarmBase):
+ """
+ The ONU Low Tx Optical Power Alarm is reported by the ANI-G (ME # 263) to
+ indicate that the transmit optical power below lower threshold
+
+ For ANI-G equipment alarms, the intf_id reported is that of the PON/ANI
+ physical port number
+ """
+ def __init__(self, alarm_mgr, onu_id, intf_id):
+ super(OnuLowTxOpticalAlarm, self).__init__(alarm_mgr, object_type='onu low tx optical power',
+ alarm='ONU_LOW_TX_OPTICAL',
+ alarm_category=AlarmEventCategory.ONU,
+ 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,
+ 'onu-intf-id': self._intf_id}
diff --git a/voltha/extensions/alarms/onu/onu_selftest_failure_alarm.py b/voltha/extensions/alarms/onu/onu_selftest_failure_alarm.py
new file mode 100644
index 0000000..c742762
--- /dev/null
+++ b/voltha/extensions/alarms/onu/onu_selftest_failure_alarm.py
@@ -0,0 +1,44 @@
+# Copyright 2018 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.
+from voltha.protos.events_pb2 import AlarmEventType, AlarmEventSeverity, AlarmEventCategory
+from voltha.extensions.alarms.adapter_alarms import AlarmBase
+
+
+class OnuSelfTestFailureAlarm(AlarmBase):
+ """
+ The ONU Self Test Failure Alarm is reported by both the CircuitPack (ME #6)
+ and the ONT-G (ME # 256) to indicate failure a failed autonomous self-test.
+
+ For CircuitPack equipment alarms, the intf_id reported is that of the
+ UNI's logical port number
+
+ For ONT-G equipment alarms, the intf_id reported is that of the PON/ANI
+ physical port number
+
+ Note: Some ONUs may use this alarm to report a self-test failure or may
+ may report it with the ONU Equipment Alarm which can also cover a
+ self-test failure.
+ """
+ def __init__(self, alarm_mgr, onu_id, intf_id):
+ super(OnuSelfTestFailureAlarm, self).__init__(alarm_mgr, object_type='onu self-test failure',
+ alarm='ONU_SELF_TEST_FAIL',
+ alarm_category=AlarmEventCategory.ONU,
+ alarm_type=AlarmEventType.EQUIPTMENT,
+ alarm_severity=AlarmEventSeverity.CRITICAL)
+ 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_temp_red_alarm.py b/voltha/extensions/alarms/onu/onu_temp_red_alarm.py
new file mode 100644
index 0000000..bfa1623
--- /dev/null
+++ b/voltha/extensions/alarms/onu/onu_temp_red_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 voltha.extensions.alarms.adapter_alarms import AlarmBase
+
+
+class OnuTempRedAlarm(AlarmBase):
+ """
+ The ONU Temperature Yellow Alarm is reported by both the CircuitPack
+ (ME #6) and the ONT-G (ME # 256) to indicate no service has been shut
+ down to avoid equipment damage. The operational state of the affected
+ PPTPs indicates the affected services.
+
+ For CircuitPack equipment alarms, the intf_id reported is that of the
+ UNI's logical port number
+
+ For ONT-G equipment alarms, the intf_id reported is that of the PON/ANI
+ physical port number
+ """
+ def __init__(self, alarm_mgr, onu_id, intf_id):
+ super(OnuTempRedAlarm, self).__init__(alarm_mgr, object_type='onu temperature red',
+ alarm='ONU_TEMP_RED',
+ alarm_category=AlarmEventCategory.ONU,
+ alarm_type=AlarmEventType.ENVIRONMENT,
+ alarm_severity=AlarmEventSeverity.CRITICAL)
+ 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_temp_yellow_alarm.py b/voltha/extensions/alarms/onu/onu_temp_yellow_alarm.py
new file mode 100644
index 0000000..7a28f81
--- /dev/null
+++ b/voltha/extensions/alarms/onu/onu_temp_yellow_alarm.py
@@ -0,0 +1,41 @@
+# 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 OnuTempYellowAlarm(AlarmBase):
+ """
+ The ONU Temperature Yellow Alarm is reported by both the CircuitPack
+ (ME #6) and the ONT-G (ME # 256) to indicate no service shutdown at
+ present, but the circuit pack is operating beyond its recommended range.
+
+ For CircuitPack equipment alarms, the intf_id reported is that of the
+ UNI's logical port number
+
+ For ONT-G equipment alarms, the intf_id reported is that of the PON/ANI
+ physical port number
+ """
+ def __init__(self, alarm_mgr, onu_id, intf_id):
+ super(OnuTempYellowAlarm, self).__init__(alarm_mgr, object_type='onu temperature yellow',
+ alarm='ONU_TEMP_YELLOW',
+ alarm_category=AlarmEventCategory.ONU,
+ alarm_type=AlarmEventType.ENVIRONMENT,
+ 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,
+ 'onu-intf-id': self._intf_id}
diff --git a/voltha/extensions/alarms/onu/onu_voltage_red_alarm.py b/voltha/extensions/alarms/onu/onu_voltage_red_alarm.py
new file mode 100644
index 0000000..506351c
--- /dev/null
+++ b/voltha/extensions/alarms/onu/onu_voltage_red_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 voltha.extensions.alarms.adapter_alarms import AlarmBase
+
+
+class OnuVoltageRedAlarm(AlarmBase):
+ """
+ The ONU Voltage Red Alarm is reported by the ONT-G (ME # 256) to
+ indicate some services have been shut down to avoid power collapse.
+ The operational state of the affected PPTPs indicates the affected
+ services.
+
+ For ONT-G equipment alarms, the intf_id reported is that of the PON/ANI
+ physical port number
+ """
+ def __init__(self, alarm_mgr, onu_id, intf_id):
+ super(OnuVoltageRedAlarm, self).__init__(alarm_mgr, object_type='onu voltage red',
+ alarm='ONU_VOLTAGE_RED',
+ alarm_category=AlarmEventCategory.ONU,
+ alarm_type=AlarmEventType.COMMUNICATION,
+ alarm_severity=AlarmEventSeverity.CRITICAL)
+ 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_voltage_yellow_alarm.py b/voltha/extensions/alarms/onu/onu_voltage_yellow_alarm.py
new file mode 100644
index 0000000..1bb3ef6
--- /dev/null
+++ b/voltha/extensions/alarms/onu/onu_voltage_yellow_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 voltha.extensions.alarms.adapter_alarms import AlarmBase
+
+
+class OnuVoltageYellowAlarm(AlarmBase):
+ """
+ The ONU Voltage Red Alarm is reported by the ONT-G (ME # 256) to
+ indicate some services have been shut down to avoid power collapse.
+ The operational state of the affected PPTPs indicates the affected
+ services.
+
+ For ONT-G equipment alarms, the intf_id reported is that of the PON/ANI
+ physical port number
+ """
+ def __init__(self, alarm_mgr, onu_id, intf_id):
+ super(OnuVoltageYellowAlarm, self).__init__(alarm_mgr, object_type='onu voltage yellow',
+ alarm='ONU_VOLTAGE_YELLOW',
+ alarm_category=AlarmEventCategory.ONU,
+ 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,
+ 'onu-intf-id': self._intf_id}
diff --git a/voltha/extensions/omci/database/alarm_db_ext.py b/voltha/extensions/omci/database/alarm_db_ext.py
index 699f9b7..2af6923 100644
--- a/voltha/extensions/omci/database/alarm_db_ext.py
+++ b/voltha/extensions/omci/database/alarm_db_ext.py
@@ -16,8 +16,6 @@
from mib_db_api import *
from voltha.protos.omci_alarm_db_pb2 import AlarmInstanceData, AlarmClassData, \
AlarmDeviceData, AlarmAttributeData
-from voltha.extensions.omci.omci_entities import *
-from scapy.fields import StrField, FieldListField
class AlarmDbExternal(MibDbApi):
@@ -25,6 +23,7 @@
A persistent external OpenOMCI Alarm Database
"""
CURRENT_VERSION = 1 # VOLTHA v1.3.0 release
+ ALARM_BITMAP_KEY = 'alarm_bit_map'
_TIME_FORMAT = '%Y%m%d-%H%M%S.%f'
@@ -33,8 +32,8 @@
DEVICE_PATH = ALARM_PATH + '/{}' # .format(device_id)
# Classes, Instances, and Attributes as lists from root proxy
- CLASSES_PATH = DEVICE_PATH + '/classes' # .format(device_id)
- INSTANCES_PATH = DEVICE_PATH +'/classes/{}/instances' # .format(device_id, class_id)
+ CLASSES_PATH = DEVICE_PATH + '/classes' # .format(device_id)
+ INSTANCES_PATH = DEVICE_PATH + '/classes/{}/instances' # .format(device_id, class_id)
ATTRIBUTES_PATH = DEVICE_PATH + '/classes/{}/instances/{}/attributes' # .format(device_id, class_id, instance_id)
# Single Class, Instance, and Attribute as objects from device proxy
@@ -85,76 +84,16 @@
def _string_to_time(self, time):
return datetime.strptime(time, AlarmDbExternal._TIME_FORMAT) if len(time) else None
- def _attribute_to_string(self, device_id, class_id, attr_name, value):
+ def _attribute_to_string(self, value):
"""
Convert an ME's attribute value to string representation
- :param device_id: (str) ONU Device ID
- :param class_id: (int) Class ID
- :param attr_name: (str) Attribute Name (see EntityClasses)
- :param value: (various) Attribute Value
-
+ :param value: (long) Alarm bitmaps are always a Long
:return: (str) String representation of the value
- :raises KeyError: Device, Class ID, or Attribute does not exist
"""
- try:
- me_map = self._omci_agent.get_device(device_id).me_map
+ return str(value)
- if class_id in me_map:
- entity = me_map[class_id]
- attr_index = entity.attribute_name_to_index_map[attr_name]
- eca = entity.attributes[attr_index]
- field = eca.field
- else:
- # Here for auto-defined MEs (ones not defined in ME Map)
- from voltha.extensions.omci.omci_cc import UNKNOWN_CLASS_ATTRIBUTE_KEY
- field = StrFixedLenField(UNKNOWN_CLASS_ATTRIBUTE_KEY, None, 24)
-
- if isinstance(field, StrFixedLenField):
- from scapy.base_classes import Packet_metaclass
- # For StrFixedLenField, value is a str already (or possibly JSON encoded)
- if hasattr(value, 'to_json'):
- # Packet Class to string
- str_value = value.to_json()
- elif isinstance(field.default, Packet_metaclass) \
- and hasattr(field.default, 'json_from_value'):
- # Value/hex of Packet Class to string
- str_value = field.default.json_from_value(value)
- else:
- str_value = str(value)
-
- elif isinstance(field, (StrField, MACField, IPField)):
- # For StrField, value is an str already
- # For MACField, value is a string in ':' delimited form
- # For IPField, value is a string in '.' delimited form
- str_value = str(value)
-
- elif isinstance(field, (ByteField, ShortField, IntField, LongField)):
- # For ByteField, ShortField, IntField, and LongField value is an int
- str_value = str(value)
-
- elif isinstance(field, BitField):
- # For BitField, value is a long
- #
- str_value = str(value)
-
- elif isinstance(field, FieldListField):
- str_value = json.dumps(value, separators=(',', ':'))
-
- else:
- self.log.warning('default-conversion', type=type(field),
- class_id=class_id, attribute=attr_name, value=str(value))
- str_value = str(value)
-
- return str_value
-
- except Exception as e:
- self.log.exception('attr-to-string', device_id=device_id,
- class_id=class_id, attr=attr_name,
- value=value, e=e)
- raise
-
- def _string_to_attribute(self, device_id, class_id, attr_name, str_value):
+ def _string_to_attribute(self, str_value):
"""
Convert an ME's attribute value-string to its Scapy decode equivalent
@@ -166,56 +105,8 @@
:return: (various) String representation of the value
:raises KeyError: Device, Class ID, or Attribute does not exist
"""
- try:
- me_map = self._omci_agent.get_device(device_id).me_map
-
- if class_id in me_map:
- entity = me_map[class_id]
- attr_index = entity.attribute_name_to_index_map[attr_name]
- eca = entity.attributes[attr_index]
- field = eca.field
- else:
- # Here for auto-defined MEs (ones not defined in ME Map)
- from voltha.extensions.omci.omci_cc import UNKNOWN_CLASS_ATTRIBUTE_KEY
- field = StrFixedLenField(UNKNOWN_CLASS_ATTRIBUTE_KEY, None, 24)
-
- if isinstance(field, StrFixedLenField):
- from scapy.base_classes import Packet_metaclass
- if isinstance(field.default, Packet_metaclass) and \
- hasattr(field.default, 'to_json'):
- value = json.loads(str_value)
- else:
- value = str_value
-
- elif isinstance(field, MACField):
- value = str_value
-
- elif isinstance(field, IPField):
- value = str_value
-
- elif isinstance(field, (ByteField, ShortField, IntField, LongField)):
- if str_value.lower() in ('true', 'false'):
- str_value = '1' if str_value.lower() == 'true' else '0'
- value = int(str_value)
-
- elif isinstance(field, BitField):
- value = long(str_value)
-
- elif isinstance(field, FieldListField):
- value = json.loads(str_value)
-
- else:
- self.log.warning('default-conversion', type=type(field),
- class_id=class_id, attribute=attr_name, value=str_value)
- value = None
-
- return value
-
- except Exception as e:
- self.log.exception('attr-to-string', device_id=device_id,
- class_id=class_id, attr=attr_name,
- value=str_value, e=e)
- raise
+ # Alarms are always a bitmap which is a long
+ return long(str_value) if len(str_value) else 0L
def add(self, device_id, overwrite=False):
"""
@@ -505,15 +396,12 @@
now = self._time_to_string(datetime.utcnow())
attrs = [AlarmAttributeData(name=k,
- value=self._attribute_to_string(device_id,
- class_id,
- k,
- v)) for k, v in attributes.items()]
+ value=self._attribute_to_string(v)) for k, v in attributes.items()]
class_data = AlarmClassData(class_id=class_id,
- instances=[AlarmInstanceData(instance_id=instance_id,
- created=now,
- modified=now,
- attributes=attrs)])
+ instances=[AlarmInstanceData(instance_id=instance_id,
+ created=now,
+ modified=now,
+ attributes=attrs)])
self._root_proxy.add(AlarmDbExternal.CLASSES_PATH.format(device_id), class_data)
self.log.debug('set-complete', device_id=device_id, class_id=class_id,
@@ -537,14 +425,11 @@
now = self._time_to_string(datetime.utcnow())
attrs = [AlarmAttributeData(name=k,
- value=self._attribute_to_string(device_id,
- class_id,
- k,
- v)) for k, v in attributes.items()]
+ value=self._attribute_to_string(v)) for k, v in attributes.items()]
instance_data = AlarmInstanceData(instance_id=instance_id,
- created=now,
- modified=now,
- attributes=attrs)
+ created=now,
+ modified=now,
+ attributes=attrs)
self._root_proxy.add(AlarmDbExternal.INSTANCES_PATH.format(device_id, class_id),
instance_data)
@@ -609,11 +494,12 @@
exist_attr_indexes[inst_data.attributes[index].name] = index
modified = False
+ str_value = ''
new_attributes = []
for k, v in attributes.items():
try:
- str_value = self._attribute_to_string(device_id, class_id, k, v)
+ str_value = self._attribute_to_string(v)
new_attributes.append(AlarmAttributeData(name=k, value=str_value))
except Exception as e:
@@ -627,12 +513,12 @@
if modified:
now = datetime.utcnow()
new_data = AlarmInstanceData(instance_id=instance_id,
- created=inst_data.created,
- modified=self._time_to_string(now),
- attributes=new_attributes)
+ created=inst_data.created,
+ modified=self._time_to_string(now),
+ attributes=new_attributes)
dev_proxy.remove(AlarmDbExternal.INSTANCE_PATH.format(class_id, instance_id))
self._root_proxy.add(AlarmDbExternal.INSTANCES_PATH.format(device_id,
- class_id), new_data)
+ class_id), new_data)
self.log.debug('set-complete', device_id=device_id, class_id=class_id,
entity_id=instance_id, attributes=attributes, modified=modified)
@@ -728,7 +614,7 @@
# Get all instances of the class
try:
cls_data = self._class_proxy(device_id, class_id).get('/', depth=-1)
- data = self._class_to_dict(device_id, cls_data)
+ data = self._class_to_dict(cls_data)
except KeyError:
data = dict()
@@ -741,7 +627,7 @@
if attributes is None:
# All Attributes
- data = self._instance_to_dict(device_id, class_id, inst_data)
+ data = self._instance_to_dict(inst_data)
else:
# Specific attribute(s)
@@ -749,10 +635,7 @@
attributes = {attributes}
data = {
- attr.name: self._string_to_attribute(device_id,
- class_id,
- attr.name,
- attr.value)
+ attr.name: self._string_to_attribute(attr.value)
for attr in inst_data.attributes if attr.name in attributes}
except KeyError:
@@ -768,7 +651,7 @@
self.log.exception('get-last-sync-exception', device_id=device_id, e=e)
raise
- def _instance_to_dict(self, device_id, class_id, instance):
+ def _instance_to_dict(self, instance):
if not isinstance(instance, AlarmInstanceData):
raise TypeError('{} is not of type AlarmInstanceData'.format(type(instance)))
@@ -779,13 +662,10 @@
ATTRIBUTES_KEY: dict()
}
for attribute in instance.attributes:
- data[ATTRIBUTES_KEY][attribute.name] = self._string_to_attribute(device_id,
- class_id,
- attribute.name,
- attribute.value)
+ data[ATTRIBUTES_KEY][attribute.name] = self._string_to_attribute(attribute.value)
return data
- def _class_to_dict(self, device_id, val):
+ def _class_to_dict(self, val):
if not isinstance(val, AlarmClassData):
raise TypeError('{} is not of type AlarmClassData'.format(type(val)))
@@ -793,9 +673,7 @@
CLASS_ID_KEY: val.class_id,
}
for instance in val.instances:
- data[instance.instance_id] = self._instance_to_dict(device_id,
- val.class_id,
- instance)
+ data[instance.instance_id] = self._instance_to_dict(instance)
return data
def _device_to_dict(self, val):
@@ -810,8 +688,7 @@
MSG_TYPE_KEY: set()
}
for class_data in val.classes:
- data[class_data.class_id] = self._class_to_dict(val.device_id,
- class_data)
+ data[class_data.class_id] = self._class_to_dict(class_data)
for managed_entity in val.managed_entities:
data[ME_KEY][managed_entity.class_id] = managed_entity.name
@@ -819,9 +696,3 @@
data[MSG_TYPE_KEY].add(msg_type.message_type)
return data
-
- def _managed_entity_to_name(self, device_id, class_id):
- me_map = self._omci_agent.get_device(device_id).me_map
- entity = me_map.get(class_id)
-
- return entity.__name__ if entity is not None else 'UnknownManagedEntity'
diff --git a/voltha/extensions/omci/database/mib_db_ext.py b/voltha/extensions/omci/database/mib_db_ext.py
index e12331f..0e1857a 100644
--- a/voltha/extensions/omci/database/mib_db_ext.py
+++ b/voltha/extensions/omci/database/mib_db_ext.py
@@ -167,8 +167,8 @@
# Packet Class to string
str_value = value.to_json()
elif isinstance(field.default, Packet_metaclass) \
- and hasattr(field.default, 'json_from_value')\
- and not isinstance(value, basestring):
+ and hasattr(field.default, 'json_from_value'):
+ #and not isinstance(value, basestring):
# Value/hex of Packet Class to string
str_value = field.default.json_from_value(value)
else:
@@ -382,7 +382,7 @@
except KeyError:
if not create:
- self.log.error('class-proxy-does-not-exist', device_id=device_id,
+ self.log.debug('class-proxy-does-not-exist', device_id=device_id,
class_id=class_id)
raise
diff --git a/voltha/extensions/omci/omci_entities.py b/voltha/extensions/omci/omci_entities.py
index fa4f33a..bf47f8e 100644
--- a/voltha/extensions/omci/omci_entities.py
+++ b/voltha/extensions/omci/omci_entities.py
@@ -138,6 +138,7 @@
mandatory_operations = set()
optional_operations = set()
notifications = set()
+ alarms = dict() # Alarm Number -> Alarm Name
hidden = False # If true, this attribute is not reported by a MIB upload.
# This attribute is needed to be able to properly perform
# MIB Audits.
@@ -259,6 +260,13 @@
]
mandatory_operations = {OP.Get, OP.Set}
notifications = {OP.AttributeValueChange, OP.AlarmNotification}
+ alarms = {
+ 0: 'Plug-in circuit pack missing',
+ 1: 'Plug-in type mismatch alarm',
+ 2: 'Improper card removal',
+ 3: 'Plug-in equipment ID mismatch alarm',
+ 4: 'Protection switch',
+ }
class CircuitPack(EntityClass):
@@ -290,7 +298,14 @@
mandatory_operations = {OP.Get, OP.Set, OP.Reboot}
optional_operations = {OP.Create, OP.Delete, OP.Test}
notifications = {OP.AttributeValueChange, OP.AlarmNotification}
-
+ alarms = {
+ 0: 'Equipment alarm',
+ 1: 'Powering alarm',
+ 2: 'Self-test failure',
+ 3: 'Laser end of life',
+ 4: 'Temperature yellow',
+ 5: 'Temperature red',
+ }
class SoftwareImage(EntityClass):
class_id = 7
@@ -347,6 +362,9 @@
]
mandatory_operations = {OP.Get, OP.Set}
notifications = {OP.AttributeValueChange, OP.AlarmNotification}
+ alarms = {
+ 0: 'LAN Loss Of Signal',
+ }
class MacBridgeServiceProfile(EntityClass):
@@ -403,6 +421,9 @@
]
mandatory_operations = {OP.Create, OP.Delete, OP.Get, OP.Set}
notifications = {OP.AlarmNotification}
+ alarms = {
+ 0: 'Port blocking',
+ }
class MacBridgePortFilterPreAssignTable(EntityClass):
@@ -431,7 +452,6 @@
range_check=lambda x: 0 <= x <= 1)
]
mandatory_operations = {OP.Get, OP.Set}
- notifications = {OP.AlarmNotification}
class VlanTaggingFilterData(EntityClass):
@@ -653,6 +673,24 @@
OP.Get, OP.Set, OP.Reboot, OP.Test, OP.SynchronizeTime}
notifications = {OP.TestResult, OP.AttributeValueChange,
OP.AlarmNotification}
+ alarms = {
+ 0: 'Equipment alarm',
+ 1: 'Powering alarm',
+ 2: 'Battery missing',
+ 3: 'Battery failure',
+ 4: 'Battery low',
+ 5: 'Physical intrusion',
+ 6: 'Self-test failure',
+ 7: 'Dying gasp',
+ 8: 'Temperature yellow',
+ 9: 'Temperature red',
+ 10: 'Voltage yellow',
+ 11: 'Voltage red',
+ 12: 'ONU manual power off',
+ 13: 'Invalid image',
+ 14: 'PSE overload yellow',
+ 15: 'PSE overload red',
+ }
class Ont2G(EntityClass):
@@ -722,6 +760,15 @@
]
mandatory_operations = {OP.Get, OP.Set, OP.Test}
notifications = {OP.AttributeValueChange, OP.AlarmNotification}
+ alarms = {
+ 0: 'Low received optical power',
+ 1: 'High received optical power',
+ 2: 'Signal fail',
+ 3: 'Signal degrade',
+ 4: 'Low transmit optical power',
+ 5: 'High transmit optical power',
+ 6: 'Laser bias current',
+ }
class UniG(EntityClass):
@@ -759,6 +806,9 @@
]
mandatory_operations = {OP.Create, OP.Delete, OP.Get, OP.Set}
notifications = {OP.AttributeValueChange, OP.AlarmNotification}
+ alarms = {
+ 6: 'Operational state change',
+ }
class GemPortNetworkCtp(EntityClass):
@@ -784,6 +834,9 @@
]
mandatory_operations = {OP.Create, OP.Delete, OP.Get, OP.Set}
notifications = {OP.AlarmNotification}
+ alarms = {
+ 5: 'End-to-end loss of continuity',
+ }
class GalEthernetProfile(EntityClass):
@@ -823,6 +876,9 @@
]
mandatory_operations = {OP.Get, OP.Set}
notifications = {OP.AlarmNotification}
+ alarms = {
+ 0: 'Block loss',
+ }
class TrafficSchedulerG(EntityClass):
@@ -861,6 +917,9 @@
mandatory_operations = {OP.Create, OP.Delete, OP.Get, OP.GetNext, OP.Set}
optional_operations = {OP.SetTable}
notifications = {OP.AttributeValueChange, OP.AlarmNotification}
+ alarms = {
+ 0: 'Deprecated',
+ }
class AccessControlRow0(Packet):
@@ -965,6 +1024,9 @@
mandatory_operations = {OP.Create, OP.Delete, OP.Set, OP.Get, OP.GetNext}
optional_operations = {OP.SetTable}
notifications = {OP.AlarmNotification}
+ alarms = {
+ 0: 'Lost multicast group',
+ }
class MulticastServicePackage(Packet):
@@ -1060,6 +1122,9 @@
]
mandatory_operations = {OP.Get, OP.Set}
notifications = {OP.AttributeValueChange, OP.AlarmNotification}
+ alarms = {
+ 0: 'Connecting function fail',
+ }
class Omci(EntityClass):
@@ -1147,6 +1212,22 @@
]
mandatory_operations = {OP.Create, OP.Delete, OP.Get, OP.Set, OP.GetCurrentData}
notifications = {OP.AlarmNotification}
+ alarms = {
+ 0: 'FCS errors',
+ 1: 'Excessive collision counter',
+ 2: 'Late collision counter',
+ 3: 'Frames too long',
+ 4: 'Buffer overflows on receive',
+ 5: 'Buffer overflows on transmit',
+ 6: 'Single collision frame counter',
+ 7: 'Multiple collision frame counter',
+ 8: 'SQE counter',
+ 9: 'Deferred transmission counter',
+ 10: 'Internal MAC transmit error counter',
+ 11: 'Carrier sense error counter',
+ 12: 'Alignment error counter',
+ 13: 'Internal MAC receive error counter',
+ }
class FecPerformanceMonitoringHistoryData(EntityClass):
@@ -1164,6 +1245,12 @@
]
mandatory_operations = {OP.Create, OP.Delete, OP.Get, OP.Set, OP.GetCurrentData}
notifications = {OP.AlarmNotification}
+ alarms = {
+ 0: 'Corrected bytes',
+ 1: 'Corrected code words',
+ 2: 'Uncorrectable code words',
+ 4: 'FEC seconds',
+ }
class EthernetFrameDownstreamPerformanceMonitoringHistoryData(EntityClass):
@@ -1190,6 +1277,12 @@
]
mandatory_operations = {OP.Create, OP.Delete, OP.Get, OP.Set, OP.GetCurrentData}
notifications = {OP.AlarmNotification}
+ alarms = {
+ 0: 'Drop events',
+ 1: 'CRC errored packets',
+ 2: 'Undersize packets',
+ 3: 'Oversize packets',
+ }
class EthernetFrameUpstreamPerformanceMonitoringHistoryData(EntityClass):
@@ -1216,6 +1309,12 @@
]
mandatory_operations = {OP.Create, OP.Delete, OP.Get, OP.Set, OP.GetCurrentData}
notifications = {OP.AlarmNotification}
+ alarms = {
+ 0: 'Drop events',
+ 1: 'CRC errored packets',
+ 2: 'Undersize packets',
+ 3: 'Oversize packets',
+ }
class VeipUni(EntityClass):
@@ -1233,6 +1332,9 @@
]
mandatory_operations = {OP.Get, OP.Set}
notifications = {OP.AttributeValueChange, OP.AlarmNotification}
+ alarms = {
+ 0: 'Connecting function fail'
+ }
class EthernetFrameExtendedPerformanceMonitoring(EntityClass):
@@ -1269,6 +1371,12 @@
mandatory_operations = {OP.Create, OP.Delete, OP.Get, OP.Set}
optional_operations = {OP.GetCurrentData}
notifications = {OP.AlarmNotification}
+ alarms = {
+ 0: 'Drop events',
+ 1: 'CRC errored packets',
+ 2: 'Undersize packets',
+ 3: 'Oversize packets',
+ }
class EthernetFrameExtendedPerformanceMonitoring64Bit(EntityClass):
@@ -1305,6 +1413,12 @@
mandatory_operations = {OP.Create, OP.Delete, OP.Get, OP.Set}
optional_operations = {OP.GetCurrentData}
notifications = {OP.AlarmNotification}
+ alarms = {
+ 0: 'Drop events',
+ 1: 'CRC errored packets',
+ 2: 'Undersize packets',
+ 3: 'Oversize packets',
+ }
class GemPortNetworkCtpMonitoringHistoryData(EntityClass):
@@ -1322,6 +1436,9 @@
]
mandatory_operations = {OP.Create, OP.Delete, OP.Get, OP.Set, OP.GetCurrentData}
notifications = {OP.AlarmNotification}
+ alarms = {
+ 1: 'Encryption key errors',
+ }
class XgPonTcPerformanceMonitoringHistoryData(EntityClass):
@@ -1343,6 +1460,14 @@
mandatory_operations = {OP.Create, OP.Delete, OP.Get, OP.Set}
optional_operations = {OP.GetCurrentData}
notifications = {OP.AlarmNotification}
+ alarms = {
+ 1: 'PSBd HEC error count',
+ 2: 'XGTC HEC error count',
+ 3: 'Unknown profile count',
+ 4: 'XGEM HEC loss count',
+ 5: 'XGEM key errors',
+ 6: 'XGEM HEC error count',
+ }
class XgPonDownstreamPerformanceMonitoringHistoryData(EntityClass):
@@ -1370,6 +1495,10 @@
mandatory_operations = {OP.Create, OP.Delete, OP.Get, OP.Set}
optional_operations = {OP.GetCurrentData}
notifications = {OP.AlarmNotification}
+ alarms = {
+ 1: 'PLOAM MIC error count',
+ 2: 'OMCI MIC error count',
+ }
class XgPonUpstreamPerformanceMonitoringHistoryData(EntityClass):
diff --git a/voltha/extensions/omci/omci_messages.py b/voltha/extensions/omci/omci_messages.py
index 0fb2125..b836199 100644
--- a/voltha/extensions/omci/omci_messages.py
+++ b/voltha/extensions/omci/omci_messages.py
@@ -14,7 +14,7 @@
# limitations under the License.
#
import structlog
-from scapy.fields import ByteField, StrFixedLenField, ConditionalField, Field
+from scapy.fields import ByteField, ThreeBytesField, ConditionalField, Field
from scapy.fields import ShortField, BitField
from scapy.packet import Packet
@@ -306,6 +306,7 @@
ShortField("entity_class", None),
ShortField("entity_id", 0),
BitField("alarm_bit_map", 0, 224),
+ ThreeBytesField("zero_padding", 0),
ByteField("alarm_sequence_number", None)
]
diff --git a/voltha/extensions/omci/onu_device_entry.py b/voltha/extensions/omci/onu_device_entry.py
index 8ddfe87..0d07522 100644
--- a/voltha/extensions/omci/onu_device_entry.py
+++ b/voltha/extensions/omci/onu_device_entry.py
@@ -288,8 +288,7 @@
topic = OnuDeviceEntry.event_bus_topic(self.device_id,
OnuDeviceEvents.AlarmDatabaseSyncEvent)
msg = {
- IN_SYNC_KEY: self._alarm_db_in_sync,
- LAST_IN_SYNC_KEY: self.alarm_synchronizer.last_alarm_sync_time
+ IN_SYNC_KEY: self._alarm_db_in_sync
}
self.event_bus.publish(topic=topic, msg=msg)
diff --git a/voltha/extensions/omci/state_machines/alarm_sync.py b/voltha/extensions/omci/state_machines/alarm_sync.py
index ea30b74..dee6d83 100644
--- a/voltha/extensions/omci/state_machines/alarm_sync.py
+++ b/voltha/extensions/omci/state_machines/alarm_sync.py
@@ -17,15 +17,20 @@
from datetime import datetime
from transitions import Machine
from twisted.internet import reactor
-from voltha.extensions.omci.omci_defs import ReasonCodes
+from voltha.extensions.omci.omci_defs import ReasonCodes, EntityOperations
from voltha.extensions.omci.omci_cc import OmciCCRxEvents, OMCI_CC, RX_RESPONSE_KEY
from voltha.extensions.omci.omci_messages import OmciGetAllAlarmsResponse
from voltha.extensions.omci.omci_frame import OmciFrame
+from voltha.extensions.omci.database.alarm_db_ext import AlarmDbExternal
+from voltha.extensions.omci.database.mib_db_api import ATTRIBUTES_KEY
+from voltha.extensions.omci.omci_entities import CircuitPack, PptpEthernetUni, OntG, AniG
+
from common.event_bus import EventBusClient
from voltha.protos.omci_alarm_db_pb2 import AlarmOpenOmciEventType
RxEvent = OmciCCRxEvents
RC = ReasonCodes
+OP = EntityOperations
class AlarmSynchronizer(object):
@@ -50,7 +55,7 @@
{'trigger': 'stop', 'source': '*', 'dest': 'disabled'},
]
DEFAULT_TIMEOUT_RETRY = 15 # Seconds to delay after task failure/timeout
- DEFAULT_AUDIT_DELAY = 0 # 300 # Periodic tick to audit the ONU's alarm table
+ DEFAULT_AUDIT_DELAY = 180 # Periodic tick to audit the ONU's alarm table
def __init__(self, agent, device_id, alarm_sync_tasks, db,
advertise_events=False,
@@ -87,14 +92,16 @@
self._audit_delay = audit_delay
self._resync_task = alarm_sync_tasks['alarm-resync']
self._advertise_events = advertise_events
+ self._alarm_manager = None
+ self._onu_id = None
+ self._uni_ports = list()
+ self._ani_ports = list()
self._deferred = None
self._current_task = None
self._task_deferred = None
- self._last_alarm_sequence_value = None
+ self._last_alarm_sequence_value = 0
self._device_in_db = False
- # self._alarm_bit_map_notification = dict()
- # self._alarm_sequence_number_notification = dict()
self._event_bus = EventBusClient()
self._omci_cc_subscriptions = { # RxEvent.enum -> Subscription Object
@@ -178,6 +185,21 @@
'time': str(datetime.utcnow())
})
+ def set_alarm_params(self, mgr=None, onu_id=None, uni_ports=None, ani_ports=None):
+ if mgr is not None:
+ self._alarm_manager = mgr
+
+ if onu_id is not None:
+ self._onu_id = onu_id
+
+ if uni_ports is not None:
+ assert isinstance(uni_ports, list)
+ self._uni_ports = uni_ports
+
+ if ani_ports is not None:
+ assert isinstance(ani_ports, list)
+ self._ani_ports = ani_ports
+
def on_enter_disabled(self):
"""
State machine is being stopped
@@ -288,24 +310,78 @@
onu_only = results['onu-only']
olt_only = results['olt-only']
attr_diffs = results['attr-diffs']
+ onu_db = results['onu-db']
+ olt_db = results['olt-db']
+
+ if any(item is not None for item in (onu_only, olt_only, attr_diffs)):
+ self._device.alarm_db_in_sync = False
# Compare the differences. During upload, if there are no alarms at all,
# then the ONU alarm table retrieved may be empty (instead of MEs with all
# bits cleared) depending upon the ONU's OMCI Stack.
if onu_only is not None:
- pass
- # ONU only alarms will typically occur when doing the first audit as our
- # database is clear and we are seeding the alarm table. Save the entries
- # and if any are set, we need to raise that alarm.
- #
- # self._database.set(self._device_id, class_id, entity_id, alarm_bit_map)
+ self.process_onu_only_diffs(onu_only, onu_db)
if olt_only is not None:
- pass
+ self.process_olt_only_diffs(olt_only)
if attr_diffs is not None:
- pass
+ self.process_attr_diffs(attr_diffs, olt_db, onu_db)
+
+ def process_onu_only_diffs(self, onu_only, onu_db):
+ """
+ ONU only alarms will typically occur when doing the first audit as our
+ database is clear and we are seeding the alarm table. Save the entries
+ and if any are set, we need to raise that alarm.
+
+ :param onu_only: (list) Tuples with [0]=class ID, [1]=entity ID
+ :param onu_db: (dict) ONU Alarm database from the alarm audit upload
+ """
+ for cid_eid in onu_only:
+ class_id = cid_eid[0]
+ entity_id = cid_eid[1]
+ try:
+ bitmap = onu_db[class_id][entity_id][ATTRIBUTES_KEY][AlarmDbExternal.ALARM_BITMAP_KEY]
+ self.process_alarm_data(class_id, entity_id, bitmap, -1)
+
+ except KeyError as e:
+ self.log.error('alarm-not-found', class_id=class_id, entity_id=entity_id, e=e)
+
+ def process_olt_only_diffs(self, olt_only):
+ """
+ OLT only alarms may occur if the alarm(s) are no longer active on the ONU
+ and the notification was missed. Process this by sending a cleared bitmap
+ for any alarm in the OLT database only
+
+ :param olt_only: (list) Tuples with [0]=class ID, [1]=entity ID
+ """
+ for cid_eid in olt_only:
+ # First process the alarm clearing
+ self.process_alarm_data(cid_eid[0], cid_eid[1], 0, -1)
+ # Now remove from alarm DB so we match the ONU alarm table
+ self._database.delete(self._device_id, cid_eid[0], cid_eid[1])
+
+ def process_attr_diffs(self, attr_diffs, onu_db):
+ """
+ Mismatch in alarm settings. Note that the attribute should always be the
+ alarm bitmap attribute (long). For differences, the ONU is always right
+
+ :param attr_diffs: (list(int,int,str)) [0]=class ID, [1]=entity ID, [1]=attr
+ :param olt_db: (dict) OLT Alarm database snapshot from the alarm audit
+ :param onu_db: (dict) ONU Alarm database from the alarm audit upload
+ """
+ for cid_eid_attr in attr_diffs:
+ class_id = cid_eid_attr[0]
+ entity_id = cid_eid_attr[1]
+
+ try:
+ assert AlarmDbExternal.ALARM_BITMAP_KEY == cid_eid_attr[2]
+ bitmap = onu_db[class_id][entity_id][ATTRIBUTES_KEY][AlarmDbExternal.ALARM_BITMAP_KEY]
+ self.process_alarm_data(class_id, entity_id, bitmap, -1)
+
+ except KeyError as e:
+ self.log.error('alarm-not-found', class_id=class_id, entity_id=entity_id, e=e)
def on_alarm_update_response(self, _topic, msg):
"""
@@ -347,23 +423,82 @@
alarm_msg = msg.get(RX_RESPONSE_KEY)
if alarm_msg is not None:
- # TODO: Process alarm
- # decode message, note that the seq number should never
- # be zero.
+ omci_msg = alarm_msg.fields['omci_message'].fields
+ class_id = omci_msg['entity_class']
+ seq_no = omci_msg['alarm_sequence_number']
+ # Validate that this ME supports alarm notifications
+ if class_id not in self._device.me_map or \
+ OP.AlarmNotification not in self._device.me_map[class_id].notifications or \
+ len(self._device.me_map[class_id].alarms) == 0:
+ self.log.warn('invalid-alarm-notification', class_id=class_id)
+ return
+
+ self.process_alarm_data(class_id,
+ omci_msg['entity_id'],
+ omci_msg['alarm_bit_map'],
+ seq_no)
+
+ def process_alarm_data(self, class_id, entity_id, bitmap, msg_seq_no):
+ """
+ Process new alarm data
+
+ :param class_id: (int) Class ID of alarm
+ :param entity_id: (int) Entity ID of alarm
+ :param bitmap: (long) Alarm bitmap value
+ :param msg_seq_no: (int) Alarm sequence number. -1 if generated during an audit
+ """
+ if msg_seq_no > 0:
# increment alarm number & compare to alarm # in message
+ # Signal early audit if no match and audits are enabled
self.increment_alarm_sequence()
- # Signal early audit if no match and audits are enabled
- # if self.last_alarm_sequence != msg_seq_no and self._audit_delay > 0:
- # self._deferred = reactor.callLater(0, self.audit_alarm)
+ if self.last_alarm_sequence != msg_seq_no and self._audit_delay > 0:
+ self._deferred = reactor.callLater(0, self.audit_alarm)
- # update alarm table/db (compare current db with alarm msg)
- # notify ONU Device Handler, ...
- pass
- # Note that right now we do not alarm anyone or save it to the database, so
- # if we can create (or clear) an alarm on the ONU, then the audit logic
- # should detect the difference. So we can test the audit that way.
+ key = AlarmDbExternal.ALARM_BITMAP_KEY
+ prev_entry = self._database.query(class_id, entity_id)
+ prev_bitmap = 0 if len(prev_entry) == 0 else long(prev_entry(key, '0'))
+
+ # Save current entry before going on
+ try:
+ self._database.set(self._device_id, class_id, entity_id, {key: bitmap})
+
+ except Exception as e:
+ self.log.exception('alarm-save-failure', class_id=class_id,
+ entity_id=entity_id, value=bitmap, e=e)
+
+ if self._alarm_manager is not None:
+ # Generate a set of alarm number that are raised in current and previous
+ previously_raised = {alarm_no for alarm_no in xrange(224)
+ if prev_bitmap & (1L << (223-alarm_no)) != 0L}
+
+ currently_raised = {alarm_no for alarm_no in xrange(224)
+ if bitmap & (1L << (223-alarm_no)) != 0L}
+
+ newly_cleared = previously_raised - currently_raised
+ newly_raised = currently_raised - previously_raised
+
+ # Generate the set/clear alarms now
+ for alarm_number in newly_cleared:
+ reactor.callLater(0, self.clear_alarm, class_id, entity_id, alarm_number)
+
+ for alarm_number in newly_raised:
+ reactor.callLater(0, self.raise_alarm, class_id, entity_id, alarm_number)
+
+ def get_alarm_description(self, class_id, alarm_number):
+ """
+ Get the alarm description, both as a printable-string and also a CamelCase value
+ """
+ if alarm_number in self._device.me_map[class_id].alarms:
+ description = self._device.me_map[class_id].alarms[alarm_number]
+ elif alarm_number <= 207:
+ description = 'Reserved alarm {}'.format(alarm_number)
+ else:
+ description = 'Vendor specific alarm {}'.format(alarm_number)
+
+ # For CamelCase, replace hyphens with spaces before camel casing the string
+ return description, description.replace('-', ' ').title().replace(' ', '')
def raise_alarm(self, class_id, entity_id, alarm_number):
"""
@@ -373,7 +508,15 @@
:param entity_id: (int) Entity ID of the Alarm
:param alarm_number: (int) Alarm number (bit) that is alarmed
"""
- pass # TODO: Implement this
+ description, name = self.get_alarm_description(class_id, alarm_number)
+
+ self.log.warn('alarm-set', class_id=class_id, entity_id=entity_id,
+ alarm_number=alarm_number, name=name, description=description)
+
+ if self._alarm_manager is not None:
+ alarm = self.omci_alarm_to_onu_alarm(class_id, entity_id, alarm_number)
+ if alarm is not None:
+ alarm.raise_alarm()
def clear_alarm(self, class_id, entity_id, alarm_number):
"""
@@ -383,7 +526,15 @@
:param entity_id: (int) Entity ID of the Alarm
:param alarm_number: (int) Alarm number (bit) that is alarmed
"""
- pass # TODO: Implement this
+ description, name = self.get_alarm_description(class_id, alarm_number)
+
+ self.log.info('alarm-cleared', class_id=class_id, entity_id=entity_id,
+ alarm_number=alarm_number, name=name, description=description)
+
+ if self._alarm_manager is not None:
+ alarm = self.omci_alarm_to_onu_alarm(class_id, entity_id, alarm_number)
+ if alarm is not None:
+ alarm.clear_alarm()
def query_mib(self, class_id=None, instance_id=None):
"""
@@ -406,3 +557,110 @@
raise DatabaseStateError('Database does not yet exist')
return self._database.query(self._device_id, class_id=class_id, instance_id=instance_id)
+
+ def omci_alarm_to_onu_alarm(self, class_id, entity_id, alarm_number):
+ """
+ Map an OMCI Alarm Notification alarm to the proper ONU Alarm Library alarm
+
+ :param class_id: (int) ME Class ID
+ :param entity_id: (int) ME Class instance ID
+ :param alarm_number: (int) Alarm Number
+ :return: (AlarmBase) Alarm library alarm or None if not supported/found
+ """
+ from voltha.extensions.alarms.onu.onu_dying_gasp_alarm import OnuDyingGaspAlarm
+ from voltha.extensions.alarms.onu.onu_los_alarm import OnuLosAlarm
+ from voltha.extensions.alarms.onu.onu_equipment_alarm import OnuEquipmentAlarm
+ from voltha.extensions.alarms.onu.onu_selftest_failure_alarm import OnuSelfTestFailureAlarm
+ from voltha.extensions.alarms.onu.onu_laser_eol_alarm import OnuLaserEolAlarm
+ from voltha.extensions.alarms.onu.onu_laser_bias_current_alarm import OnuLaserBiasAlarm
+ from voltha.extensions.alarms.onu.onu_temp_yellow_alarm import OnuTempYellowAlarm
+ from voltha.extensions.alarms.onu.onu_temp_red_alarm import OnuTempRedAlarm
+ from voltha.extensions.alarms.onu.onu_voltage_yellow_alarm import OnuVoltageYellowAlarm
+ from voltha.extensions.alarms.onu.onu_voltage_red_alarm import OnuVoltageRedAlarm
+ from voltha.extensions.alarms.onu.onu_low_rx_optical_power_alarm import OnuLowRxOpticalAlarm
+ from voltha.extensions.alarms.onu.onu_high_rx_optical_power_alarm import OnuHighRxOpticalAlarm
+ from voltha.extensions.alarms.onu.onu_low_tx_optical_power_alarm import OnuLowTxOpticalAlarm
+ from voltha.extensions.alarms.onu.onu_high_tx_optical_power_alarm import OnuHighTxOpticalAlarm
+
+ mgr = self._alarm_manager
+ if class_id in (CircuitPack.class_id, PptpEthernetUni.class_id):
+ intf_id = self.select_uni_port(class_id, entity_id)
+
+ elif class_id in (AniG.class_id, OntG.class_id):
+ intf_id = self.select_ani_port(class_id, entity_id)
+
+ else:
+ self.log.error('unsupported-class-id', class_id=class_id, alarm_number=alarm_number)
+ return
+
+ alarm_map = {
+ (CircuitPack.class_id, 0): OnuEquipmentAlarm,
+ (CircuitPack.class_id, 2): OnuSelfTestFailureAlarm,
+ (CircuitPack.class_id, 3): OnuLaserEolAlarm,
+ (CircuitPack.class_id, 4): OnuTempYellowAlarm,
+ (CircuitPack.class_id, 5): OnuTempRedAlarm,
+
+ (PptpEthernetUni.class_id, 0): OnuLosAlarm,
+
+ (OntG.class_id, 0): OnuEquipmentAlarm,
+ (OntG.class_id, 6): OnuSelfTestFailureAlarm,
+ (OntG.class_id, 7): OnuDyingGaspAlarm,
+ (OntG.class_id, 8): OnuTempYellowAlarm,
+ (OntG.class_id, 9): OnuTempRedAlarm,
+ (OntG.class_id, 10): OnuVoltageYellowAlarm,
+ (OntG.class_id, 11): OnuVoltageRedAlarm,
+
+ (AniG.class_id, 0): OnuLowRxOpticalAlarm,
+ (AniG.class_id, 1): OnuHighRxOpticalAlarm,
+ (AniG.class_id, 4): OnuLowTxOpticalAlarm,
+ (AniG.class_id, 5): OnuHighTxOpticalAlarm,
+ (AniG.class_id, 6): OnuLaserBiasAlarm,
+ }
+ alarm_cls = alarm_map.get((class_id, alarm_number))
+
+ return alarm_cls(mgr, self._onu_id, intf_id) if alarm_cls is not None else None
+
+ def select_uni_port(self, class_id, entity_id):
+ """
+ Select the best possible UNI Port (logical) interface number for this ME class and
+ entity ID.
+
+ This base implementation will assume that a UNI Port object has been registered
+ on startup and supports both an 'entity_id' and also 'logical_port_number'
+ property. See both the Adtran and BroadCom OpenOMCI ONU DA for an example
+ of this UNI port object.
+
+ :param class_id: (int) ME Class ID for which the alarms belongs to
+ :param entity_id: (int) Instance ID
+
+ :return: (int) Logical Port number for the UNI port
+ """
+ # NOTE: Of the three class ID's supported in this version of code, only the CircuitPack,
+ # and PptpEthernetUni MEs will map to the UNI port
+ assert class_id in (CircuitPack.class_id, PptpEthernetUni.class_id)
+
+ return next((uni.logical_port_number for uni in self._uni_ports if
+ uni.entity_id == entity_id), None)
+
+ def select_ani_port(self, class_id, _entity_id):
+ """
+ Select the best possible ANI Port (physical) interface number for this ME class and
+ entity ID.
+
+ Currently the base implementation assumes only a single PON port and it will be
+ chosen. A future implementation may want to have a PON Port object (similar to
+ the BroadCom Open OMCI and Adtran ONU's UNI Port object) that provides a match
+ for entity ID. This does assume that the PON port object supports a property
+ of 'port_number' to return the physical port number.
+
+ :param class_id: (int) ME Class ID for which the alarms belongs to
+ :param _entity_id: (int) Instance ID
+
+ :return: (int) Logical Port number for the UNI port
+ """
+ # NOTE: Of the three class ID's supported in this version of code, only the AniG
+ # MEs will map to the ANI port. For some the OntG alarms (Dying Gasp) the
+ # PON interface will also be selected.
+ assert class_id in (AniG.class_id, OntG.class_id)
+
+ return self._ani_ports[0].port_number if len(self._ani_ports) else None
diff --git a/voltha/extensions/omci/tasks/alarm_resync_task.py b/voltha/extensions/omci/tasks/alarm_resync_task.py
index 826bf00..1007724 100644
--- a/voltha/extensions/omci/tasks/alarm_resync_task.py
+++ b/voltha/extensions/omci/tasks/alarm_resync_task.py
@@ -18,8 +18,9 @@
from twisted.internet import reactor
from common.utils.asleep import asleep
from voltha.extensions.omci.database.mib_db_dict import *
-from voltha.extensions.omci.omci_entities import OntData
from voltha.extensions.omci.omci_defs import AttributeAccess
+from voltha.extensions.omci.database.alarm_db_ext import AlarmDbExternal
+
AA = AttributeAccess
@@ -128,8 +129,8 @@
self.deferred.errback(failure.Failure(e))
else:
# Start the ALARM upload sequence, save alarms to the table
-
self.strobe_watchdog()
+
if number_of_commands > 0:
commands_retrieved = yield self.upload_alarm(number_of_commands)
else:
@@ -261,22 +262,8 @@
alarm_class_id = omci_msg['alarmed_entity_class']
alarm_entity_id = omci_msg['alarmed_entity_id']
- # Filter out the 'alarm_data_sync' from the database. We save that at
- # the device level and do not want it showing up during a re-sync
- # during data comparison
-
- if alarm_class_id == OntData.class_id:
- break
-
- bit_map = omci_msg['alarm_bit_map'].encode('hex')
- bit_map_hex = "{0:b}".format(int(bit_map, 16))
- alarm_bit_map = eval(bit_map_hex)
-
- # alarm bit map space is 28 bytes * 8 = 224 bits
- if len(bit_map_hex) != 224:
- continue
-
- attributes = alarm_bit_map
+ alarm_bit_map = omci_msg['alarm_bit_map']
+ attributes = {AlarmDbExternal.ALARM_BITMAP_KEY: alarm_bit_map}
# Save to the database
self._db_active.set(self.device_id, alarm_class_id,