blob: a07e2c7ef199d0464f0d9274188344df4916521b [file] [log] [blame]
Chip Boling67b674a2019-02-08 11:42: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
Zack Williams84a71e92019-11-15 09:00:19 -070015from __future__ import absolute_import, division
William Kurkianede82e92019-03-05 13:02:57 -050016from voltha_protos.device_pb2 import PmConfig, PmConfigs, PmGroupConfig
Chip Boling67b674a2019-02-08 11:42:18 -060017from pyvoltha.adapters.extensions.kpi.adapter_pm_metrics import AdapterPmMetrics
Zack Williams84a71e92019-11-15 09:00:19 -070018import six
Chip Boling67b674a2019-02-08 11:42:18 -060019
20
21class OltPmMetrics(AdapterPmMetrics):
22 """
23 Shared OL Device Adapter PM Metrics Manager
24
25 This class specifically addresses ONU general PM (health, ...) area
26 specific PM (OMCI, PON, UNI) is supported in encapsulated classes accessible
27 from this object
28 """
William Kurkian6e643802019-04-02 12:49:59 -040029 def __init__(self, core_proxy, device_id, logical_device_id, serial_number,
Chip Boling67b674a2019-02-08 11:42:18 -060030 grouped=False, freq_override=False, **kwargs):
31 """
32 Initializer for shared ONU Device Adapter PM metrics
33
Arun Aroraa587f992019-03-14 09:54:29 +000034 :param core_proxy: (CoreProxy) Gateway between CORE and an adapter
Chip Boling67b674a2019-02-08 11:42:18 -060035 :param device_id: (str) Device ID
36 :param logical_device_id: (str) VOLTHA Logical Device ID
37 :param grouped: (bool) Flag indicating if statistics are managed as a group
38 :param freq_override: (bool) Flag indicating if frequency collection can be specified
39 on a per group basis
40 :param kwargs: (dict) Device Adapter specific values. For an ONU Device adapter, the
41 expected key-value pairs are listed below. If not provided, the
42 associated PM statistics are not gathered:
43
44 'nni-ports': List of objects that provide NNI (northbound) port statistics
45 'pon-ports': List of objects that provide PON port statistics
46 """
William Kurkian6e643802019-04-02 12:49:59 -040047 super(OltPmMetrics, self).__init__(core_proxy, device_id, logical_device_id, serial_number,
Chip Boling67b674a2019-02-08 11:42:18 -060048 grouped=grouped, freq_override=freq_override,
49 **kwargs)
50
51 # PM Config Types are COUNTER, GAUGE, and STATE
52 self.nni_pm_names = {
53 ('intf_id', PmConfig.CONTEXT), # Physical device interface ID/Port number
54
55 ('admin_state', PmConfig.STATE),
56 ('oper_status', PmConfig.STATE),
57
58 ('rx_bytes', PmConfig.COUNTER),
59 ('rx_packets', PmConfig.COUNTER),
60 ('rx_ucast_packets', PmConfig.COUNTER),
61 ('rx_mcast_packets', PmConfig.COUNTER),
62 ('rx_bcast_packets', PmConfig.COUNTER),
63 ('rx_error_packets', PmConfig.COUNTER),
64
65 ('tx_bytes', PmConfig.COUNTER),
66 ('tx_packets', PmConfig.COUNTER),
67 ('tx_ucast_packets', PmConfig.COUNTER),
68 ('tx_mcast_packets', PmConfig.COUNTER),
69 ('tx_bcast_packets', PmConfig.COUNTER),
70 ('tx_error_packets', PmConfig.COUNTER),
71 ('rx_crc_errors', PmConfig.COUNTER),
72 ('bip_errors', PmConfig.COUNTER),
73 }
74 self.pon_pm_names = {
75 ('intf_id', PmConfig.CONTEXT), # Physical device port number (PON)
76 ('pon_id', PmConfig.CONTEXT), # PON ID (0..n)
77
78 ('admin_state', PmConfig.STATE),
79 ('oper_status', PmConfig.STATE),
80 ('rx_packets', PmConfig.COUNTER),
81 ('rx_bytes', PmConfig.COUNTER),
82 ('tx_packets', PmConfig.COUNTER),
83 ('tx_bytes', PmConfig.COUNTER),
84 ('tx_bip_errors', PmConfig.COUNTER),
85 ('in_service_onus', PmConfig.GAUGE),
86 ('closest_onu_distance', PmConfig.GAUGE)
87 }
88 self.onu_pm_names = {
89 ('intf_id', PmConfig.CONTEXT), # Physical device port number (PON)
90 ('pon_id', PmConfig.CONTEXT),
91 ('onu_id', PmConfig.CONTEXT),
92
93 ('fiber_length', PmConfig.GAUGE),
94 ('equalization_delay', PmConfig.GAUGE),
95 ('rssi', PmConfig.GAUGE),
96 }
97 self.gem_pm_names = {
98 ('intf_id', PmConfig.CONTEXT), # Physical device port number (PON)
99 ('pon_id', PmConfig.CONTEXT),
100 ('onu_id', PmConfig.CONTEXT),
101 ('gem_id', PmConfig.CONTEXT),
102
103 ('alloc_id', PmConfig.GAUGE),
104 ('rx_packets', PmConfig.COUNTER),
105 ('rx_bytes', PmConfig.COUNTER),
106 ('tx_packets', PmConfig.COUNTER),
107 ('tx_bytes', PmConfig.COUNTER),
108 }
109 self.nni_metrics_config = {m: PmConfig(name=m, type=t, enabled=True)
110 for (m, t) in self.nni_pm_names}
111 self.pon_metrics_config = {m: PmConfig(name=m, type=t, enabled=True)
112 for (m, t) in self.pon_pm_names}
113 self.onu_metrics_config = {m: PmConfig(name=m, type=t, enabled=True)
114 for (m, t) in self.onu_pm_names}
115 self.gem_metrics_config = {m: PmConfig(name=m, type=t, enabled=True)
116 for (m, t) in self.gem_pm_names}
117
118 self._nni_ports = kwargs.pop('nni-ports', None)
119 self._pon_ports = kwargs.pop('pon-ports', None)
120
121 def update(self, pm_config):
122 try:
123 # TODO: Test frequency override capability for a particular group
124 if self.default_freq != pm_config.default_freq:
125 # Update the callback to the new frequency.
126 self.default_freq = pm_config.default_freq
127 self.lc.stop()
Chip Bolingde43a702021-01-26 11:46:38 -0600128 self.lc.start(interval=self.default_freq)
Chip Boling67b674a2019-02-08 11:42:18 -0600129
130 if pm_config.grouped:
131 for group in pm_config.groups:
132 group_config = self.pm_group_metrics.get(group.group_name)
133 if group_config is not None:
134 group_config.enabled = group.enabled
135 else:
136 msg = 'There are no independent OLT metrics, only group metrics at this time'
137 raise NotImplemented(msg)
138
139 except Exception as e:
140 self.log.exception('update-failure', e=e)
141 raise
142
143 def make_proto(self, pm_config=None):
144 if pm_config is None:
145 pm_config = PmConfigs(id=self.device_id, default_freq=self.default_freq,
146 grouped=self.grouped,
147 freq_override=self.freq_override)
148 metrics = set()
149 have_nni = self._nni_ports is not None and len(self._nni_ports) > 0
150 have_pon = self._pon_ports is not None and len(self._pon_ports) > 0
151
152 if self.grouped:
153 if have_nni:
Chip Bolingde43a702021-01-26 11:46:38 -0600154 pm_ether_stats = PmGroupConfig(group_name='ETHERNET_NNI',
Chip Boling67b674a2019-02-08 11:42:18 -0600155 group_freq=self.default_freq,
156 enabled=True)
157 self.pm_group_metrics[pm_ether_stats.group_name] = pm_ether_stats
158
159 else:
160 pm_ether_stats = None
161
162 if have_pon:
Chip Bolingde43a702021-01-26 11:46:38 -0600163 pm_pon_stats = PmGroupConfig(group_name='PON_OLT',
Chip Boling67b674a2019-02-08 11:42:18 -0600164 group_freq=self.default_freq,
165 enabled=True)
166
167 pm_onu_stats = PmGroupConfig(group_name='ONU',
168 group_freq=self.default_freq,
169 enabled=True)
170
171 pm_gem_stats = PmGroupConfig(group_name='GEM',
172 group_freq=self.default_freq,
173 enabled=True)
174
175 self.pm_group_metrics[pm_pon_stats.group_name] = pm_pon_stats
176 self.pm_group_metrics[pm_onu_stats.group_name] = pm_onu_stats
177 self.pm_group_metrics[pm_gem_stats.group_name] = pm_gem_stats
178 else:
179 pm_pon_stats = None
180 pm_onu_stats = None
181 pm_gem_stats = None
182
183 else:
184 pm_ether_stats = pm_config if have_nni else None
185 pm_pon_stats = pm_config if have_pon else None
186 pm_onu_stats = pm_config if have_pon else None
187 pm_gem_stats = pm_config if have_pon else None
188
189 if have_nni:
190 for m in sorted(self.nni_metrics_config):
191 pm = self.nni_metrics_config[m]
192 if not self.grouped:
193 if pm.name in metrics:
194 continue
195 metrics.add(pm.name)
196 pm_ether_stats.metrics.extend([PmConfig(name=pm.name,
197 type=pm.type,
198 enabled=pm.enabled)])
199 if have_pon:
200 for m in sorted(self.pon_metrics_config):
201 pm = self.pon_metrics_config[m]
202 if not self.grouped:
203 if pm.name in metrics:
204 continue
205 metrics.add(pm.name)
206 pm_pon_stats.metrics.extend([PmConfig(name=pm.name,
207 type=pm.type,
208 enabled=pm.enabled)])
209
210 for m in sorted(self.onu_metrics_config):
211 pm = self.onu_metrics_config[m]
212 if not self.grouped:
213 if pm.name in metrics:
214 continue
215 metrics.add(pm.name)
216 pm_onu_stats.metrics.extend([PmConfig(name=pm.name,
217 type=pm.type,
218 enabled=pm.enabled)])
219
220 for m in sorted(self.gem_metrics_config):
221 pm = self.gem_metrics_config[m]
222 if not self.grouped:
223 if pm.name in metrics:
224 continue
225 metrics.add(pm.name)
226 pm_gem_stats.metrics.extend([PmConfig(name=pm.name,
227 type=pm.type,
228 enabled=pm.enabled)])
229 if self.grouped:
230 pm_config.groups.extend([stats for stats in
Zack Williams84a71e92019-11-15 09:00:19 -0700231 six.itervalues(self.pm_group_metrics)])
Chip Boling67b674a2019-02-08 11:42:18 -0600232
233 return pm_config
234
235 def collect_metrics(self, data=None):
236 """
237 Collect metrics for this adapter.
238
239 The data collected (or passed in) is a list of pairs/tuples. Each
240 pair is composed of a MetricMetaData metadata-portion and list of MetricValuePairs
241 that contains a single individual metric or list of metrics if this is a
242 group metric.
243
244 This method is called for each adapter at a fixed frequency.
245 TODO: Currently all group metrics are collected on a single timer tick.
246 This needs to be fixed as independent group or instance collection is
247 desirable.
248
249 :param data: (list) Existing list of collected metrics (MetricInformation).
250 This is provided to allow derived classes to call into
251 further encapsulated classes.
252
253 :return: (list) metadata and metrics pairs - see description above
254 """
255 if data is None:
256 data = list()
257
Chip Bolingde43a702021-01-26 11:46:38 -0600258 group_name = 'ETHERNET_NNI'
Chip Boling67b674a2019-02-08 11:42:18 -0600259 if self.pm_group_metrics[group_name].enabled:
260 for port in self._nni_ports:
261 group_data = self.collect_group_metrics(group_name,
262 port,
263 self.nni_pm_names,
264 self.nni_metrics_config)
265 if group_data is not None:
266 data.append(group_data)
267
268 for port in self._pon_ports:
Chip Bolingde43a702021-01-26 11:46:38 -0600269 group_name = 'PON_OLT'
Chip Boling67b674a2019-02-08 11:42:18 -0600270 if self.pm_group_metrics[group_name].enabled:
271 group_data = self.collect_group_metrics(group_name,
272 port,
273 self.pon_pm_names,
274 self.pon_metrics_config)
275 if group_data is not None:
276 data.append(group_data)
277
278 for onu_id in port.onu_ids:
279 onu = port.onu(onu_id)
280 if onu is not None:
281 group_name = 'ONU'
282 if self.pm_group_metrics[group_name].enabled:
283 group_data = self.collect_group_metrics(group_name,
284 onu,
285 self.onu_pm_names,
286 self.onu_metrics_config)
287 if group_data is not None:
288 data.append(group_data)
289
290 group_name = 'GEM'
291 if self.pm_group_metrics[group_name].enabled:
292 for gem in onu.gem_ports:
293 if not gem.multicast:
294 group_data = self.collect_group_metrics(group_name,
295 onu,
296 self.gem_pm_names,
297 self.gem_metrics_config)
298 if group_data is not None:
299 data.append(group_data)
300
301 # TODO: Do any multicast GEM PORT metrics here...
302 return data