blob: ea2e0c8c1c22d3e4422d292924eb7984d920ef5c [file] [log] [blame]
Chip Boling32aab302019-01-23 10:50:18 -06001# Copyright 2017-present Open Networking Foundation
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15from voltha.protos.device_pb2 import PmConfig, PmConfigs, PmGroupConfig
16from voltha.extensions.kpi.adapter_pm_metrics import AdapterPmMetrics
17
18
19class OltPmMetrics(AdapterPmMetrics):
20 """
21 Shared OL Device Adapter PM Metrics Manager
22
23 This class specifically addresses ONU general PM (health, ...) area
24 specific PM (OMCI, PON, UNI) is supported in encapsulated classes accessible
25 from this object
26 """
27 def __init__(self, adapter_agent, device_id, logical_device_id,
28 grouped=False, freq_override=False, **kwargs):
29 """
30 Initializer for shared ONU Device Adapter PM metrics
31
32 :param adapter_agent: (AdapterAgent) Adapter agent for the device
33 :param device_id: (str) Device ID
34 :param logical_device_id: (str) VOLTHA Logical Device ID
35 :param grouped: (bool) Flag indicating if statistics are managed as a group
36 :param freq_override: (bool) Flag indicating if frequency collection can be specified
37 on a per group basis
38 :param kwargs: (dict) Device Adapter specific values. For an ONU Device adapter, the
39 expected key-value pairs are listed below. If not provided, the
40 associated PM statistics are not gathered:
41
42 'nni-ports': List of objects that provide NNI (northbound) port statistics
43 'pon-ports': List of objects that provide PON port statistics
44 """
45 super(OltPmMetrics, self).__init__(adapter_agent, device_id, logical_device_id,
46 grouped=grouped, freq_override=freq_override,
47 **kwargs)
48
49 # PM Config Types are COUNTER, GAUGE, and STATE
50 self.nni_pm_names = {
51 ('intf_id', PmConfig.CONTEXT), # Physical device interface ID/Port number
52
53 ('admin_state', PmConfig.STATE),
54 ('oper_status', PmConfig.STATE),
55
56 ('rx_bytes', PmConfig.COUNTER),
57 ('rx_packets', PmConfig.COUNTER),
58 ('rx_ucast_packets', PmConfig.COUNTER),
59 ('rx_mcast_packets', PmConfig.COUNTER),
60 ('rx_bcast_packets', PmConfig.COUNTER),
61 ('rx_error_packets', PmConfig.COUNTER),
62
63 ('tx_bytes', PmConfig.COUNTER),
64 ('tx_packets', PmConfig.COUNTER),
65 ('tx_ucast_packets', PmConfig.COUNTER),
66 ('tx_mcast_packets', PmConfig.COUNTER),
67 ('tx_bcast_packets', PmConfig.COUNTER),
68 ('tx_error_packets', PmConfig.COUNTER),
69 ('rx_crc_errors', PmConfig.COUNTER),
70 ('bip_errors', PmConfig.COUNTER),
71 }
72 self.pon_pm_names = {
73 ('intf_id', PmConfig.CONTEXT), # Physical device port number (PON)
74 ('pon_id', PmConfig.CONTEXT), # PON ID (0..n)
75
76 ('admin_state', PmConfig.STATE),
77 ('oper_status', PmConfig.STATE),
78 ('rx_packets', PmConfig.COUNTER),
79 ('rx_bytes', PmConfig.COUNTER),
80 ('tx_packets', PmConfig.COUNTER),
81 ('tx_bytes', PmConfig.COUNTER),
82 ('tx_bip_errors', PmConfig.COUNTER),
83 ('in_service_onus', PmConfig.GAUGE),
84 ('closest_onu_distance', PmConfig.GAUGE)
85 }
86 self.onu_pm_names = {
87 ('intf_id', PmConfig.CONTEXT), # Physical device port number (PON)
88 ('pon_id', PmConfig.CONTEXT),
89 ('onu_id', PmConfig.CONTEXT),
90
91 ('fiber_length', PmConfig.GAUGE),
92 ('equalization_delay', PmConfig.GAUGE),
93 ('rssi', PmConfig.GAUGE),
94 }
95 self.gem_pm_names = {
96 ('intf_id', PmConfig.CONTEXT), # Physical device port number (PON)
97 ('pon_id', PmConfig.CONTEXT),
98 ('onu_id', PmConfig.CONTEXT),
99 ('gem_id', PmConfig.CONTEXT),
100
101 ('alloc_id', PmConfig.GAUGE),
102 ('rx_packets', PmConfig.COUNTER),
103 ('rx_bytes', PmConfig.COUNTER),
104 ('tx_packets', PmConfig.COUNTER),
105 ('tx_bytes', PmConfig.COUNTER),
106 }
107 self.nni_metrics_config = {m: PmConfig(name=m, type=t, enabled=True)
108 for (m, t) in self.nni_pm_names}
109 self.pon_metrics_config = {m: PmConfig(name=m, type=t, enabled=True)
110 for (m, t) in self.pon_pm_names}
111 self.onu_metrics_config = {m: PmConfig(name=m, type=t, enabled=True)
112 for (m, t) in self.onu_pm_names}
113 self.gem_metrics_config = {m: PmConfig(name=m, type=t, enabled=True)
114 for (m, t) in self.gem_pm_names}
115
116 self._nni_ports = kwargs.pop('nni-ports', None)
117 self._pon_ports = kwargs.pop('pon-ports', None)
118
119 def update(self, pm_config):
120 try:
121 # TODO: Test frequency override capability for a particular group
122 if self.default_freq != pm_config.default_freq:
123 # Update the callback to the new frequency.
124 self.default_freq = pm_config.default_freq
125 self.lc.stop()
126 self.lc.start(interval=self.default_freq / 10)
127
128 if pm_config.grouped:
129 for group in pm_config.groups:
130 group_config = self.pm_group_metrics.get(group.group_name)
131 if group_config is not None:
132 group_config.enabled = group.enabled
133 else:
134 msg = 'There are no independent OLT metrics, only group metrics at this time'
135 raise NotImplemented(msg)
136
137 except Exception as e:
138 self.log.exception('update-failure', e=e)
139 raise
140
141 def make_proto(self, pm_config=None):
142 if pm_config is None:
143 pm_config = PmConfigs(id=self.device_id, default_freq=self.default_freq,
144 grouped=self.grouped,
145 freq_override=self.freq_override)
146 metrics = set()
147 have_nni = self._nni_ports is not None and len(self._nni_ports) > 0
148 have_pon = self._pon_ports is not None and len(self._pon_ports) > 0
149
150 if self.grouped:
151 if have_nni:
152 pm_ether_stats = PmGroupConfig(group_name='Ethernet',
153 group_freq=self.default_freq,
154 enabled=True)
155 self.pm_group_metrics[pm_ether_stats.group_name] = pm_ether_stats
156
157 else:
158 pm_ether_stats = None
159
160 if have_pon:
161 pm_pon_stats = PmGroupConfig(group_name='PON',
162 group_freq=self.default_freq,
163 enabled=True)
164
165 pm_onu_stats = PmGroupConfig(group_name='ONU',
166 group_freq=self.default_freq,
167 enabled=True)
168
169 pm_gem_stats = PmGroupConfig(group_name='GEM',
170 group_freq=self.default_freq,
171 enabled=True)
172
173 self.pm_group_metrics[pm_pon_stats.group_name] = pm_pon_stats
174 self.pm_group_metrics[pm_onu_stats.group_name] = pm_onu_stats
175 self.pm_group_metrics[pm_gem_stats.group_name] = pm_gem_stats
176 else:
177 pm_pon_stats = None
178 pm_onu_stats = None
179 pm_gem_stats = None
180
181 else:
182 pm_ether_stats = pm_config if have_nni else None
183 pm_pon_stats = pm_config if have_pon else None
184 pm_onu_stats = pm_config if have_pon else None
185 pm_gem_stats = pm_config if have_pon else None
186
187 if have_nni:
188 for m in sorted(self.nni_metrics_config):
189 pm = self.nni_metrics_config[m]
190 if not self.grouped:
191 if pm.name in metrics:
192 continue
193 metrics.add(pm.name)
194 pm_ether_stats.metrics.extend([PmConfig(name=pm.name,
195 type=pm.type,
196 enabled=pm.enabled)])
197 if have_pon:
198 for m in sorted(self.pon_metrics_config):
199 pm = self.pon_metrics_config[m]
200 if not self.grouped:
201 if pm.name in metrics:
202 continue
203 metrics.add(pm.name)
204 pm_pon_stats.metrics.extend([PmConfig(name=pm.name,
205 type=pm.type,
206 enabled=pm.enabled)])
207
208 for m in sorted(self.onu_metrics_config):
209 pm = self.onu_metrics_config[m]
210 if not self.grouped:
211 if pm.name in metrics:
212 continue
213 metrics.add(pm.name)
214 pm_onu_stats.metrics.extend([PmConfig(name=pm.name,
215 type=pm.type,
216 enabled=pm.enabled)])
217
218 for m in sorted(self.gem_metrics_config):
219 pm = self.gem_metrics_config[m]
220 if not self.grouped:
221 if pm.name in metrics:
222 continue
223 metrics.add(pm.name)
224 pm_gem_stats.metrics.extend([PmConfig(name=pm.name,
225 type=pm.type,
226 enabled=pm.enabled)])
227 if self.grouped:
228 pm_config.groups.extend([stats for stats in
229 self.pm_group_metrics.itervalues()])
230
231 return pm_config
232
233 def collect_metrics(self, data=None):
234 """
235 Collect metrics for this adapter.
236
237 The data collected (or passed in) is a list of pairs/tuples. Each
238 pair is composed of a MetricMetaData metadata-portion and list of MetricValuePairs
239 that contains a single individual metric or list of metrics if this is a
240 group metric.
241
242 This method is called for each adapter at a fixed frequency.
243 TODO: Currently all group metrics are collected on a single timer tick.
244 This needs to be fixed as independent group or instance collection is
245 desirable.
246
247 :param data: (list) Existing list of collected metrics (MetricInformation).
248 This is provided to allow derived classes to call into
249 further encapsulated classes.
250
251 :return: (list) metadata and metrics pairs - see description above
252 """
253 if data is None:
254 data = list()
255
256 group_name = 'Ethernet'
257 if self.pm_group_metrics[group_name].enabled:
258 for port in self._nni_ports:
259 group_data = self.collect_group_metrics(group_name,
260 port,
261 self.nni_pm_names,
262 self.nni_metrics_config)
263 if group_data is not None:
264 data.append(group_data)
265
266 for port in self._pon_ports:
267 group_name = 'PON'
268 if self.pm_group_metrics[group_name].enabled:
269 group_data = self.collect_group_metrics(group_name,
270 port,
271 self.pon_pm_names,
272 self.pon_metrics_config)
273 if group_data is not None:
274 data.append(group_data)
275
276 for onu_id in port.onu_ids:
277 onu = port.onu(onu_id)
278 if onu is not None:
279 group_name = 'ONU'
280 if self.pm_group_metrics[group_name].enabled:
281 group_data = self.collect_group_metrics(group_name,
282 onu,
283 self.onu_pm_names,
284 self.onu_metrics_config)
285 if group_data is not None:
286 data.append(group_data)
287
288 group_name = 'GEM'
289 if self.pm_group_metrics[group_name].enabled:
290 for gem in onu.gem_ports:
291 if not gem.multicast:
292 group_data = self.collect_group_metrics(group_name,
293 onu,
294 self.gem_pm_names,
295 self.gem_metrics_config)
296 if group_data is not None:
297 data.append(group_data)
298
299 # TODO: Do any multicast GEM PORT metrics here...
300 return data