blob: b24113f557c784e4874ac6e3f8f8145a2a8ad88b [file] [log] [blame]
Chip Boling32aab302019-01-23 10:50:18 -06001#
2# Copyright 2017 the original author or authors.
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16import structlog
17import arrow
18from voltha.protos.events_pb2 import AlarmEventType, AlarmEventSeverity,\
19 AlarmEventState, AlarmEventCategory
20log = structlog.get_logger()
21
22
23# TODO: In the device adapter, the following alarms are still TBD
24# (Taken from openolt_alarms)
25# onu_alarm_ind
26# onu_startup_failure_indication
27# onu_signal_degrade_indication
28# onu_drift_of_window_ind
29# onu_loss_omci_ind
30# onu_signals_fail_ind
31# onu_tiwi_ind
32# onu_activation_fail_ind
33# onu_processing_error_ind
34
35
36class AdapterAlarms:
37 """
38 Class for managing Alarms within a given Device Handler instance
39 """
40 def __init__(self, adapter_agent, device_id, logical_device_id):
41 """
42 Adapter alarm manager initializer
43
44 :param adapter_agent: (AdapterAgent) Adapter agent reference
45 :param device_id: (str) Device handler's unique device id
46 :param logical_device_id: (str) Logical Device that the device is a member of
47 """
48 self.log = structlog.get_logger(device_id=device_id)
49 self.adapter_agent = adapter_agent
50 self.device_id = device_id
51 self.logical_device_id = logical_device_id
52 self.adapter_name = adapter_agent.adapter_name
53 self.lc = None
54
55 def format_id(self, alarm):
56 """
57 Format the Unique Alarm ID for this alarm. This is provided in the alarms
58 'id' field
59
60 :param alarm: (str) The name of the alarm such as 'Discover' or 'LOS'
61
62 :return: (str) Alarm ID
63 """
64 return 'voltha.{}.{}.{}'.format(self.adapter_name,
65 self.device_id,
66 alarm)
67
68 def format_description(self, _object, alarm, status):
69 """
70 Format the textual description field of this alarm
71
72 :param _object: ()
73 :param alarm: (str) The name of the alarm such as 'Discover' or 'LOS'
74 :param status: (bool) If True, the alarm is active (it is being raised)
75
76 :return: (str) Alarm description
77 """
78 return '{} Alarm - {} - {}'.format(_object.upper(),
79 alarm.upper(),
80 'Raised' if status else 'Cleared')
81
82 def send_alarm(self, context_data, alarm_data):
83 """
84 Send the alarm to the event bus
85
86 :param context_data: (dict) Alarm specific context data
87 :param alarm_data: (dict) Common Alarm information dictionary
88 """
89 try:
90 current_context = {}
91 if isinstance(context_data, dict):
92 for key, value in context_data.iteritems():
93 current_context[key] = str(value)
94 ser_num = None
95 device = self.adapter_agent.get_device(device_id=self.device_id)
96 ser_num = device.serial_number
97
98
99 """
100 Only put in the onu serial numbers since the OLT does not currently have a serial number and the
101 value is the ip:port address.
102 """
103 if isinstance(context_data, dict) and '_onu' in device.type.lower():
104 current_context["onu_serial_number"] = ser_num
105 alarm_event = self.adapter_agent.create_alarm(
106 id=alarm_data.get('id', 'voltha.{}.{}.olt'.format(self.adapter_name,
107 self.device_id)),
108 resource_id=str(alarm_data.get('resource_id', self.device_id)),
109 description="{}.{} - {}".format(self.adapter_name, self.device_id,
110 alarm_data.get('description')),
111 type=alarm_data.get('type'),
112 category=alarm_data.get('category'),
113 severity=alarm_data.get('severity'),
114 state=alarm_data.get('state'),
115 raised_ts=alarm_data.get('ts', 0),
116 context=current_context,
117 logical_device_id=self.logical_device_id,
118 alarm_type_name=alarm_data.get('alarm_type_name')
119 )
120 self.adapter_agent.submit_alarm(self.device_id, alarm_event)
121
122 except Exception as e:
123 self.log.exception('failed-to-send-alarm', e=e)
124 raise
125
126
127class AlarmBase(object):
128 """Base class for alarms"""
129 def __init__(self, alarm_mgr, object_type, alarm,
130 alarm_category,
131 resource_id=None,
132 alarm_type=AlarmEventType.EQUIPMENT,
133 alarm_severity=AlarmEventSeverity.CRITICAL):
134 """
135 Initializer for the Alarm base class
136
137 :param alarm_mgr: (AdapterAlarms) Reference to the device handler's Adapter
138 Alarm manager
139 :param object_type: (str) Type of device generating the alarm such as 'olt' or 'onu'
140 :param alarm: (str) A textual name for the alarm such as 'HeartBeat' or 'Discovery'
141 :param alarm_category: (AlarmEventCategory) Refers to functional category of
142 the alarm
143 :param resource_id: (str) Identifier of the originating resource of the alarm
144 :param alarm_type: (AlarmEventType) Refers to the area of the system impacted
145 by the alarm
146 :param alarm_severity: (AlarmEventSeverity) Overall impact of the alarm on the
147 system
148 """
149 self._alarm_mgr = alarm_mgr
150 self._object_type = object_type
151 self._alarm = alarm
152 self._alarm_category = alarm_category
153 self._alarm_type = alarm_type
154 self._alarm_severity = alarm_severity
155 self._resource_id = resource_id
156
157 def get_alarm_data(self, status):
158 """
159 Get the alarm specific data and format it into a dictionary. When the alarm
160 is being sent to the event bus, this dictionary provides a majority of the
161 fields for the alarms.
162
163 :param status: (bool) True if the alarm is active/raised
164 :return: (dict) Alarm data
165 """
166 data = {
167 'ts': arrow.utcnow().timestamp,
168 'description': self._alarm_mgr.format_description(self._object_type,
169 self._alarm,
170 status),
171 'id': self._alarm_mgr.format_id(self._alarm),
172 'type': self._alarm_type,
173 'category': self._alarm_category,
174 'severity': self._alarm_severity,
175 'state': AlarmEventState.RAISED if status else AlarmEventState.CLEARED,
176 'alarm_type_name': self._alarm
177 }
178 if self._resource_id is not None:
179 data['resource_id'] = self._resource_id
180 return data
181
182 def get_context_data(self):
183 """
184 Get alarm specific context data. If an alarm has specific data to specify, it is
185 included in the context field in the published event
186
187 :return: (dict) Dictionary with alarm specific context data
188 """
189 return {} # NOTE: You should override this if needed
190
191 def raise_alarm(self):
192 """
193 Called to set the state of an alarm to active and to send it to the event bus
194 """
195 alarm_data = self.get_alarm_data(True)
196 context_data = self.get_context_data()
197 self._alarm_mgr.send_alarm(context_data, alarm_data)
198
199 def clear_alarm(self):
200 """
201 Called to set the state of an alarm to inactive and to send it to the event bus
202 """
203 alarm_data = self.get_alarm_data(False)
204 context_data = self.get_context_data()
205 self._alarm_mgr.send_alarm(context_data, alarm_data)