blob: 68c628cf3fcb3c47e02ba5be747f88d982f3c088 [file] [log] [blame]
Girish Gowdrae09a6202021-01-12 18:10:59 -08001/*
2 * Copyright 2021-present Open Networking Foundation
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 */
16
17//Package adaptercoreonu provides the utility for onu devices, flows and statistics
18package adaptercoreonu
19
20import (
21 "context"
22 "errors"
23 "fmt"
24 "github.com/opencord/omci-lib-go"
25 me "github.com/opencord/omci-lib-go/generated"
26 "github.com/opencord/voltha-lib-go/v4/pkg/log"
27 "github.com/opencord/voltha-protos/v4/go/voltha"
Girish Gowdra5a7c4922021-01-22 18:33:41 -080028 "sync"
Girish Gowdrae09a6202021-01-12 18:10:59 -080029 "time"
30)
31
Girish Gowdra5a7c4922021-01-22 18:33:41 -080032// general constants used for overall Metric Collection management
33const (
34 DefaultMetricCollectionFrequency = 15 * 60 // unit in seconds. This setting can be changed from voltha NBI PmConfig configuration
35 GroupMetricEnabled = true // This is READONLY and cannot be changed from VOLTHA NBI
36 DefaultFrequencyOverrideEnabled = true // This is READONLY and cannot be changed from VOLTHA NBI
37 FrequencyGranularity = 5 // The frequency (in seconds) has to be multiple of 5. This setting cannot changed later.
38)
39
40// OpticalPowerGroupMetrics are supported optical pm names
41var OpticalPowerGroupMetrics = map[string]voltha.PmConfig_PmType{
42 "ani_g_instance_id": voltha.PmConfig_CONTEXT,
43 "transmit_power": voltha.PmConfig_GAUGE,
44 "receive_power": voltha.PmConfig_GAUGE,
45}
46
47// OpticalPowerGroupMetrics specific constants
48const (
49 OpticalPowerGroupMetricName = "OpticalPower"
50 OpticalPowerGroupMetricEnabled = true // This setting can be changed from voltha NBI PmConfig configuration
51 OpticalPowerMetricGroupCollectionFrequency = 5 * 60 // unit in seconds. This setting can be changed from voltha NBI PmConfig configuration
52)
53
54// UniStatusGroupMetrics are supported UNI status names
55var UniStatusGroupMetrics = map[string]voltha.PmConfig_PmType{
56 "uni_port_no": voltha.PmConfig_CONTEXT,
57 "ethernet_type": voltha.PmConfig_GAUGE,
58 "oper_status": voltha.PmConfig_GAUGE,
59 "uni_admin_state": voltha.PmConfig_GAUGE,
60}
61
62// UniStatusGroupMetrics specific constants
63const (
64 UniStatusGroupMetricName = "UniStatus"
65 UniStatusGroupMetricEnabled = true // This setting can be changed from voltha NBI PmConfig configuration
66 UniStatusMetricGroupCollectionFrequency = 5 * 60 // unit in seconds. This setting can be changed from voltha NBI PmConfig configuration
67)
68
69type groupMetric struct {
70 groupName string
71 enabled bool
72 frequency uint32 // valid only if FrequencyOverride is enabled.
73 metricMap map[string]voltha.PmConfig_PmType
74 nextCollectionInterval time.Time // valid only if FrequencyOverride is enabled.
75}
76
77type standaloneMetric struct {
78 metricName string
79 enabled bool
80 frequency uint32 // valid only if FrequencyOverride is enabled.
81 nextCollectionInterval time.Time // valid only if FrequencyOverride is enabled.
82}
83
Girish Gowdrae09a6202021-01-12 18:10:59 -080084type onuMetricsManager struct {
85 pDeviceHandler *deviceHandler
86
87 commMetricsChan chan Message
88 opticalMetricsChan chan me.AttributeValueMap
89 uniStatusMetricsChan chan me.AttributeValueMap
90
Girish Gowdra5a7c4922021-01-22 18:33:41 -080091 groupMetricMap map[string]*groupMetric
92 standaloneMetricMap map[string]*standaloneMetric
93
Girish Gowdrae09a6202021-01-12 18:10:59 -080094 stopProcessingOmciResponses chan bool
Girish Gowdra5a7c4922021-01-22 18:33:41 -080095
96 nextGlobalMetricCollectionTime time.Time // valid only if pmConfig.FreqOverride is set to false.
97
98 onuMetricsManagerLock sync.RWMutex
Girish Gowdrae09a6202021-01-12 18:10:59 -080099}
100
101// newonuMetricsManager returns a new instance of the newonuMetricsManager
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800102// Note that none of the context stored internally in onuMetricsManager is backed up on KV store for resiliency.
103// Metric collection is not a critical operation that needs support for resiliency. On adapter restart, some context
104// could be lost (except for Device.PmConfigs which is backed up the rw-core on KV store). An example of information
105// that is lost on adapter restart is nextCollectionInterval time.
Girish Gowdrae09a6202021-01-12 18:10:59 -0800106func newonuMetricsManager(ctx context.Context, dh *deviceHandler) *onuMetricsManager {
107
108 var metricsManager onuMetricsManager
109 logger.Debugw(ctx, "init-onuMetricsManager", log.Fields{"device-id": dh.deviceID})
110 metricsManager.pDeviceHandler = dh
111
112 metricsManager.commMetricsChan = make(chan Message)
113 metricsManager.opticalMetricsChan = make(chan me.AttributeValueMap)
114 metricsManager.uniStatusMetricsChan = make(chan me.AttributeValueMap)
115 metricsManager.stopProcessingOmciResponses = make(chan bool)
116
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800117 metricsManager.groupMetricMap = make(map[string]*groupMetric)
118 metricsManager.standaloneMetricMap = make(map[string]*standaloneMetric)
119
120 if dh.pmConfigs == nil { // dh.pmConfigs is NOT nil if adapter comes back from a restart. We should NOT go back to defaults in this case
121 dh.pmConfigs = &voltha.PmConfigs{}
122 dh.pmConfigs.Id = dh.deviceID
123 dh.pmConfigs.DefaultFreq = DefaultMetricCollectionFrequency
124 dh.pmConfigs.Grouped = GroupMetricEnabled
125 dh.pmConfigs.FreqOverride = DefaultFrequencyOverrideEnabled
126
127 // Populate group metrics.
128 // Lets populate irrespective of GroupMetricEnabled is true or not.
129 // The group metrics collection will decided on this flag later
130
131 // Populate optical power group metrics
132 var opPmConfigSlice []*voltha.PmConfig
133 for k, v := range OpticalPowerGroupMetrics {
134 opPmConfigSlice = append(opPmConfigSlice, &voltha.PmConfig{Name: k, Type: v})
135 }
136 opticalPowerGroupMetric := voltha.PmGroupConfig{
137 GroupName: OpticalPowerGroupMetricName,
Girish Gowdraaf0ad632021-01-27 13:00:01 -0800138 Enabled: OpticalPowerGroupMetricEnabled && dh.pOpenOnuAc.metricsEnabled,
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800139 GroupFreq: OpticalPowerMetricGroupCollectionFrequency,
140 Metrics: opPmConfigSlice,
141 }
142 dh.pmConfigs.Groups = append(dh.pmConfigs.Groups, &opticalPowerGroupMetric)
143
144 // Populate uni status group metrics
145 var uniStPmConfigSlice []*voltha.PmConfig
146 for k, v := range UniStatusGroupMetrics {
147 uniStPmConfigSlice = append(uniStPmConfigSlice, &voltha.PmConfig{Name: k, Type: v})
148 }
149 uniStatusGroupMetric := voltha.PmGroupConfig{
150 GroupName: UniStatusGroupMetricName,
Girish Gowdraaf0ad632021-01-27 13:00:01 -0800151 Enabled: UniStatusGroupMetricEnabled && dh.pOpenOnuAc.metricsEnabled,
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800152 GroupFreq: UniStatusMetricGroupCollectionFrequency,
153 Metrics: uniStPmConfigSlice,
154 }
155 dh.pmConfigs.Groups = append(dh.pmConfigs.Groups, &uniStatusGroupMetric)
156
157 // Add standalone metric (if present) after this (will be added to dh.pmConfigs.Metrics)
158 }
159
160 // Populate local group metric structures
161 for _, g := range dh.pmConfigs.Groups {
162 metricsManager.groupMetricMap[g.GroupName] = &groupMetric{
163 groupName: g.GroupName,
164 enabled: g.Enabled,
165 frequency: g.GroupFreq,
166 }
167 switch g.GroupName {
168 case OpticalPowerGroupMetricName:
169 metricsManager.groupMetricMap[g.GroupName].metricMap = OpticalPowerGroupMetrics
170 case UniStatusGroupMetricName:
171 metricsManager.groupMetricMap[g.GroupName].metricMap = UniStatusGroupMetrics
172
173 default:
174 logger.Errorw(ctx, "unhandled-group-name", log.Fields{"groupName": g.GroupName})
175 }
176 }
177
178 // Populate local standalone metric structures
179 for _, m := range dh.pmConfigs.Metrics {
180 metricsManager.standaloneMetricMap[m.Name] = &standaloneMetric{
181 metricName: m.Name,
182 enabled: m.Enabled,
183 frequency: m.SampleFreq,
184 }
185 switch m.Name {
186 // None exist as of now. Add when available.
187 default:
188 logger.Errorw(ctx, "unhandled-metric-name", log.Fields{"metricName": m.Name})
189 }
190 }
191
192 // initialize the next metric collection intervals.
193 metricsManager.initializeMetricCollectionTime(ctx)
194 logger.Info(ctx, "init-onuMetricsManager completed", log.Fields{"device-id": dh.deviceID})
Girish Gowdrae09a6202021-01-12 18:10:59 -0800195 return &metricsManager
196}
197
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800198func (mm *onuMetricsManager) initializeMetricCollectionTime(ctx context.Context) {
199 if mm.pDeviceHandler.pmConfigs.FreqOverride {
200 // If mm.pDeviceHandler.pmConfigs.FreqOverride is set to true, then group/standalone metric specific interval applies
201 mm.onuMetricsManagerLock.Lock()
202 defer mm.onuMetricsManagerLock.Unlock()
203 for _, v := range mm.groupMetricMap {
204 if v.enabled {
205 v.nextCollectionInterval = time.Now().Add(time.Duration(v.frequency) * time.Second)
206 }
207 }
208
209 for _, v := range mm.standaloneMetricMap {
210 if v.enabled {
211 v.nextCollectionInterval = time.Now().Add(time.Duration(v.frequency) * time.Second)
212 }
213 }
214 } else {
215 // If mm.pDeviceHandler.pmConfigs.FreqOverride is set to false, then overall metric specific interval applies
216 mm.nextGlobalMetricCollectionTime = time.Now().Add(time.Duration(mm.pDeviceHandler.pmConfigs.DefaultFreq) * time.Second)
217 }
218 logger.Infow(ctx, "initialized standalone group/metric collection time", log.Fields{"device-id": mm.pDeviceHandler.deviceID})
219}
220
221func (mm *onuMetricsManager) updateDefaultFrequency(ctx context.Context, pmConfigs *voltha.PmConfigs) error {
222 // Verify that the configured DefaultFrequency is > 0 and is a multiple of FrequencyGranularity
Girish Gowdraaf0ad632021-01-27 13:00:01 -0800223 if pmConfigs.DefaultFreq == 0 || (pmConfigs.DefaultFreq > 0 && pmConfigs.DefaultFreq%FrequencyGranularity != 0) {
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800224 logger.Errorf(ctx, "frequency-%u-should-be-a-multiple-of-%u", pmConfigs.DefaultFreq, FrequencyGranularity)
225 return fmt.Errorf("frequency-%d-should-be-a-multiple-of-%d", pmConfigs.DefaultFreq, FrequencyGranularity)
226 }
227 mm.pDeviceHandler.pmConfigs.DefaultFreq = pmConfigs.DefaultFreq
228 // re-set the nextGlobalMetricCollectionTime based on the new DefaultFreq
229 mm.nextGlobalMetricCollectionTime = time.Now().Add(time.Duration(mm.pDeviceHandler.pmConfigs.DefaultFreq) * time.Second)
230 logger.Debugw(ctx, "frequency-updated--new-frequency", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "frequency": mm.pDeviceHandler.pmConfigs.DefaultFreq})
231 return nil
232}
233
234func (mm *onuMetricsManager) updateGroupFreq(ctx context.Context, aGroupName string, pmConfigs *voltha.PmConfigs) error {
235 var newGroupFreq uint32
236 found := false
237 groupSliceIdx := 0
238 var group *voltha.PmGroupConfig
239 for groupSliceIdx, group = range pmConfigs.Groups {
240 if group.GroupName == aGroupName {
Girish Gowdraaf0ad632021-01-27 13:00:01 -0800241 // freq 0 is not allowed and it should be multiple of FrequencyGranularity
242 if group.GroupFreq == 0 || (group.GroupFreq > 0 && group.GroupFreq%FrequencyGranularity != 0) {
243 logger.Errorf(ctx, "frequency-%u-should-be-a-multiple-of-%u", group.GroupFreq, FrequencyGranularity)
244 return fmt.Errorf("frequency-%d-should-be-a-multiple-of-%d", group.GroupFreq, FrequencyGranularity)
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800245 }
Girish Gowdraaf0ad632021-01-27 13:00:01 -0800246 newGroupFreq = group.GroupFreq
247 found = true
248 break
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800249 }
250 }
251 // if not found update group freq and next collection interval for the group
252 if !found {
253 logger.Errorw(ctx, "group name not found", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "groupName": aGroupName})
254 return fmt.Errorf("group-name-not-found-%v", aGroupName)
255 }
256
257 updated := false
258 mm.onuMetricsManagerLock.Lock()
259 defer mm.onuMetricsManagerLock.Unlock()
260 for k, v := range mm.groupMetricMap {
Girish Gowdraaf0ad632021-01-27 13:00:01 -0800261 if k == aGroupName {
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800262 v.frequency = newGroupFreq
263 // update internal pm config
264 mm.pDeviceHandler.pmConfigs.Groups[groupSliceIdx].GroupFreq = newGroupFreq
265 // Also updated the next group metric collection time from now
266 v.nextCollectionInterval = time.Now().Add(time.Duration(newGroupFreq) * time.Second)
267 updated = true
268 logger.Infow(ctx, "group frequency updated", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "newGroupFreq": newGroupFreq, "groupName": aGroupName})
269 }
270 }
271 if !updated {
272 logger.Errorw(ctx, "group frequency not updated", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "newGroupFreq": newGroupFreq, "groupName": aGroupName})
273 return fmt.Errorf("internal-error-during-group-freq-update--groupname-%s-freq-%d", aGroupName, newGroupFreq)
274 }
275 return nil
276}
277
278func (mm *onuMetricsManager) updateMetricFreq(ctx context.Context, aMetricName string, pmConfigs *voltha.PmConfigs) error {
279 var newMetricFreq uint32
280 found := false
281 metricSliceIdx := 0
282 var metric *voltha.PmConfig
283 for metricSliceIdx, metric = range pmConfigs.Metrics {
284 if metric.Name == aMetricName {
Girish Gowdraaf0ad632021-01-27 13:00:01 -0800285 // freq 0 is not allowed and it should be multiple of FrequencyGranularity
286 if metric.SampleFreq == 0 || (metric.SampleFreq > 0 && metric.SampleFreq%FrequencyGranularity != 0) {
287 logger.Errorf(ctx, "frequency-%u-should-be-a-multiple-of-%u", metric.SampleFreq, FrequencyGranularity)
288 return fmt.Errorf("frequency-%d-should-be-a-multiple-of-%d", metric.SampleFreq, FrequencyGranularity)
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800289 }
Girish Gowdraaf0ad632021-01-27 13:00:01 -0800290 newMetricFreq = metric.SampleFreq
291 found = true
292 break
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800293 }
294 }
295 if !found {
296 logger.Errorw(ctx, "metric name not found", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "metricName": aMetricName})
297 return fmt.Errorf("metric-name-not-found-%v", aMetricName)
298 }
299
300 updated := false
301 mm.onuMetricsManagerLock.Lock()
302 defer mm.onuMetricsManagerLock.Unlock()
303 for k, v := range mm.groupMetricMap {
Girish Gowdraaf0ad632021-01-27 13:00:01 -0800304 if k == aMetricName {
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800305 v.frequency = newMetricFreq
306 // update internal pm config
307 mm.pDeviceHandler.pmConfigs.Metrics[metricSliceIdx].SampleFreq = newMetricFreq
308 // Also updated the next standalone metric collection time from now
309 v.nextCollectionInterval = time.Now().Add(time.Duration(newMetricFreq) * time.Second)
310 updated = true
311 logger.Infow(ctx, "metric frequency updated", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "newMetricFreq": newMetricFreq, "aMetricName": aMetricName})
312 }
313 }
314 if !updated {
315 logger.Errorw(ctx, "metric frequency not updated", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "newMetricFreq": newMetricFreq, "aMetricName": aMetricName})
316 return fmt.Errorf("internal-error-during-standalone-metric-update--matricnane-%s-freq-%d", aMetricName, newMetricFreq)
317 }
318 return nil
319}
320
321func (mm *onuMetricsManager) updateGroupSupport(ctx context.Context, aGroupName string, pmConfigs *voltha.PmConfigs) error {
322 groupSliceIdx := 0
323 var group *voltha.PmGroupConfig
324
325 for groupSliceIdx, group = range pmConfigs.Groups {
326 if group.GroupName == aGroupName {
327 break
328 }
329 }
330 if group == nil {
331 logger.Errorw(ctx, "group metric not found", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "groupName": aGroupName})
332 return fmt.Errorf("group-not-found--groupName-%s", aGroupName)
333 }
334
335 updated := false
336 mm.onuMetricsManagerLock.Lock()
337 defer mm.onuMetricsManagerLock.Unlock()
338 for k, v := range mm.groupMetricMap {
339 if k == aGroupName && v.enabled != group.Enabled {
340 mm.pDeviceHandler.pmConfigs.Groups[groupSliceIdx].Enabled = group.Enabled
341 v.enabled = group.Enabled
342 // If the group is now enabled and frequency override is enabled, set the next group metric collection time
343 if group.Enabled && mm.pDeviceHandler.pmConfigs.FreqOverride {
344 v.nextCollectionInterval = time.Now().Add(time.Duration(v.frequency) * time.Second)
345 }
346 updated = true
347 logger.Infow(ctx, "group metric support updated", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "groupName": aGroupName, "enabled": group.Enabled})
348 }
349 }
350
351 if !updated {
352 logger.Errorw(ctx, "group metric support not updated", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "groupName": aGroupName})
353 return fmt.Errorf("internal-error-during-group-support-update--groupName-%s", aGroupName)
354 }
355 return nil
356}
357
358func (mm *onuMetricsManager) updateMetricSupport(ctx context.Context, aMetricName string, pmConfigs *voltha.PmConfigs) error {
359 metricSliceIdx := 0
360 var metric *voltha.PmConfig
361
362 for metricSliceIdx, metric = range pmConfigs.Metrics {
363 if metric.Name == aMetricName {
364 break
365 }
366 }
367
368 if metric == nil {
369 logger.Errorw(ctx, "standalone metric not found", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "metricName": aMetricName})
370 return fmt.Errorf("metric-not-found--metricname-%s", aMetricName)
371 }
372
373 updated := false
374 mm.onuMetricsManagerLock.Lock()
375 defer mm.onuMetricsManagerLock.Unlock()
376 for k, v := range mm.standaloneMetricMap {
377 if k == aMetricName && v.enabled != metric.Enabled {
378 mm.pDeviceHandler.pmConfigs.Metrics[metricSliceIdx].Enabled = metric.Enabled
379 v.enabled = metric.Enabled
380 // If the standalone metric is now enabled and frequency override is enabled, set the next metric collection time
381 if metric.Enabled && mm.pDeviceHandler.pmConfigs.FreqOverride {
382 v.nextCollectionInterval = time.Now().Add(time.Duration(v.frequency) * time.Second)
383 }
384 updated = true
385 logger.Infow(ctx, "standalone metric support updated", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "metricName": aMetricName, "enabled": metric.Enabled})
386 }
387 }
388 if !updated {
389 logger.Errorw(ctx, "standalone metric support not updated", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "metricName": aMetricName})
390 return fmt.Errorf("internal-error-during-standalone-support-update--metricname-%s", aMetricName)
391 }
392 return nil
393}
394
395func (mm *onuMetricsManager) collectAllGroupAndStandaloneMetrics(ctx context.Context) {
396 if mm.pDeviceHandler.pmConfigs.Grouped { // metrics are managed as a group.
397 go mm.collectAllGroupMetrics(ctx)
398 } else {
399 go mm.collectAllStandaloneMetrics(ctx)
400 }
401}
402
403func (mm *onuMetricsManager) collectAllGroupMetrics(ctx context.Context) {
404 go func() {
405 logger.Debug(ctx, "startCollector before collecting optical metrics")
406 metricInfo := mm.collectOpticalMetrics(ctx)
407 if metricInfo != nil {
408 mm.publishMetrics(ctx, metricInfo)
409 }
410 }()
411
412 go func() {
413 logger.Debug(ctx, "startCollector before collecting uni metrics")
414 metricInfo := mm.collectUniStatusMetrics(ctx)
415 if metricInfo != nil {
416 mm.publishMetrics(ctx, metricInfo)
417 }
418 }()
419
420 // Add more here
421}
422
423func (mm *onuMetricsManager) collectAllStandaloneMetrics(ctx context.Context) {
424 // None exists as of now, add when available here
425}
426
427func (mm *onuMetricsManager) collectGroupMetric(ctx context.Context, groupName string) {
428 switch groupName {
429 case OpticalPowerGroupMetricName:
430 go func() {
431 if mi := mm.collectOpticalMetrics(ctx); mm != nil {
432 mm.publishMetrics(ctx, mi)
433 }
434 }()
435 case UniStatusGroupMetricName:
436 go func() {
437 if mi := mm.collectUniStatusMetrics(ctx); mm != nil {
438 mm.publishMetrics(ctx, mi)
439 }
440 }()
441 default:
442 logger.Errorw(ctx, "unhandled group metric name", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "groupName": groupName})
443 }
444}
445
446func (mm *onuMetricsManager) collectStandaloneMetric(ctx context.Context, metricName string) {
447 switch metricName {
448 // None exist as of now, add when available
449 default:
450 logger.Errorw(ctx, "unhandled standalone metric name", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "metricName": metricName})
451 }
452}
453
454// collectOpticalMetrics collects groups metrics related to optical power from ani-g ME.
Girish Gowdrae09a6202021-01-12 18:10:59 -0800455func (mm *onuMetricsManager) collectOpticalMetrics(ctx context.Context) []*voltha.MetricInformation {
456 logger.Debugw(ctx, "collectOpticalMetrics", log.Fields{"device-id": mm.pDeviceHandler.deviceID})
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800457
458 mm.onuMetricsManagerLock.RLock()
459 if !mm.groupMetricMap[OpticalPowerGroupMetricName].enabled {
460 mm.onuMetricsManagerLock.RUnlock()
461 logger.Debugw(ctx, "optical power group metric is not enabled", log.Fields{"device-id": mm.pDeviceHandler.deviceID})
462 return nil
463 }
464 mm.onuMetricsManagerLock.RUnlock()
465
Girish Gowdrae09a6202021-01-12 18:10:59 -0800466 var metricInfoSlice []*voltha.MetricInformation
467 metricsContext := make(map[string]string)
468 metricsContext["onuID"] = fmt.Sprintf("%d", mm.pDeviceHandler.device.ProxyAddress.OnuId)
469 metricsContext["intfID"] = fmt.Sprintf("%d", mm.pDeviceHandler.device.ProxyAddress.ChannelId)
470 metricsContext["devicetype"] = mm.pDeviceHandler.DeviceType
471
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800472 raisedTs := time.Now().Unix()
Girish Gowdrae09a6202021-01-12 18:10:59 -0800473 mmd := voltha.MetricMetaData{
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800474 Title: OpticalPowerGroupMetricName,
Girish Gowdrae09a6202021-01-12 18:10:59 -0800475 Ts: float64(raisedTs),
476 Context: metricsContext,
477 DeviceId: mm.pDeviceHandler.deviceID,
478 LogicalDeviceId: mm.pDeviceHandler.logicalDeviceID,
479 SerialNo: mm.pDeviceHandler.device.SerialNumber,
480 }
481
Girish Gowdrae09a6202021-01-12 18:10:59 -0800482 // get the ANI-G instance IDs
483 anigInstKeys := mm.pDeviceHandler.pOnuOmciDevice.pOnuDB.getSortedInstKeys(ctx, me.AniGClassID)
484loop:
485 for _, anigInstID := range anigInstKeys {
486 var meAttributes me.AttributeValueMap
487 opticalMetrics := make(map[string]float32)
488 // Get the ANI-G instance optical power attributes
489 requestedAttributes := me.AttributeValueMap{"OpticalSignalLevel": 0, "TransmitOpticalLevel": 0}
490 if meInstance := mm.pDeviceHandler.pOnuOmciDevice.PDevOmciCC.sendGetMe(ctx, me.AniGClassID, anigInstID, requestedAttributes, ConstDefaultOmciTimeout, true, mm.commMetricsChan); meInstance != nil {
491 select {
492 case meAttributes = <-mm.opticalMetricsChan:
493 logger.Debugw(ctx, "received optical metrics", log.Fields{"device-id": mm.pDeviceHandler.deviceID})
494 case <-time.After(time.Duration(ConstDefaultOmciTimeout) * time.Second):
495 logger.Errorw(ctx, "timeout waiting for omci-get response for uni status", log.Fields{"device-id": mm.pDeviceHandler.deviceID})
496 // The metrics will be empty in this case
497 break loop
498 }
499 // Populate metric only if it was enabled.
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800500 for k := range OpticalPowerGroupMetrics {
501 switch k {
502 case "ani_g_instance_id":
503 if val, ok := meAttributes["ManagedEntityId"]; ok && val != nil {
504 opticalMetrics[k] = float32(val.(uint16))
505 }
Girish Gowdrae09a6202021-01-12 18:10:59 -0800506 case "transmit_power":
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800507 if val, ok := meAttributes["TransmitOpticalLevel"]; ok && val != nil {
508 opticalMetrics[k] = float32(val.(uint16))
509 }
Girish Gowdrae09a6202021-01-12 18:10:59 -0800510 case "receive_power":
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800511 if val, ok := meAttributes["OpticalSignalLevel"]; ok && val != nil {
512 opticalMetrics[k] = float32(val.(uint16))
513 }
Girish Gowdrae09a6202021-01-12 18:10:59 -0800514 default:
515 // do nothing
516 }
517 }
518 }
519 // create slice of metrics given that there could be more than one ANI-G instance and
520 // optical metrics are collected per ANI-G instance
521 metricInfo := voltha.MetricInformation{Metadata: &mmd, Metrics: opticalMetrics}
522 metricInfoSlice = append(metricInfoSlice, &metricInfo)
523 }
524
525 return metricInfoSlice
526}
527
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800528// collectUniStatusMetrics collects UNI status group metric from various MEs (uni-g, pptp and veip).
Girish Gowdrae09a6202021-01-12 18:10:59 -0800529// nolint: gocyclo
530func (mm *onuMetricsManager) collectUniStatusMetrics(ctx context.Context) []*voltha.MetricInformation {
531 logger.Debugw(ctx, "collectUniStatusMetrics", log.Fields{"device-id": mm.pDeviceHandler.deviceID})
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800532 mm.onuMetricsManagerLock.RLock()
533 if !mm.groupMetricMap[UniStatusGroupMetricName].enabled {
534 mm.onuMetricsManagerLock.RUnlock()
535 logger.Debugw(ctx, "uni status group metric is not enabled", log.Fields{"device-id": mm.pDeviceHandler.deviceID})
536 return nil
537 }
538 mm.onuMetricsManagerLock.RUnlock()
539
Girish Gowdrae09a6202021-01-12 18:10:59 -0800540 var metricInfoSlice []*voltha.MetricInformation
541 metricsContext := make(map[string]string)
542 metricsContext["onuID"] = fmt.Sprintf("%d", mm.pDeviceHandler.device.ProxyAddress.OnuId)
543 metricsContext["intfID"] = fmt.Sprintf("%d", mm.pDeviceHandler.device.ProxyAddress.ChannelId)
544 metricsContext["devicetype"] = mm.pDeviceHandler.DeviceType
545
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800546 raisedTs := time.Now().Unix()
Girish Gowdrae09a6202021-01-12 18:10:59 -0800547 mmd := voltha.MetricMetaData{
548 Title: "UniStatus", // Is this ok to hard code?
549 Ts: float64(raisedTs),
550 Context: metricsContext,
551 DeviceId: mm.pDeviceHandler.deviceID,
552 LogicalDeviceId: mm.pDeviceHandler.logicalDeviceID,
553 SerialNo: mm.pDeviceHandler.device.SerialNumber,
554 }
555
Girish Gowdrae09a6202021-01-12 18:10:59 -0800556 // get the UNI-G instance IDs
557 unigInstKeys := mm.pDeviceHandler.pOnuOmciDevice.pOnuDB.getSortedInstKeys(ctx, me.UniGClassID)
558loop1:
559 for _, unigInstID := range unigInstKeys {
560 // TODO: Include additional information in the voltha.MetricMetaData - like portno, uni-id, instance-id
561 // to uniquely identify this ME instance and also to correlate the ME instance to physical instance
562 unigMetrics := make(map[string]float32)
563 var meAttributes me.AttributeValueMap
564 // Get the UNI-G instance optical power attributes
565 requestedAttributes := me.AttributeValueMap{"AdministrativeState": 0}
566 if meInstance := mm.pDeviceHandler.pOnuOmciDevice.PDevOmciCC.sendGetMe(ctx, me.UniGClassID, unigInstID, requestedAttributes, ConstDefaultOmciTimeout, true, mm.commMetricsChan); meInstance != nil {
567 // Wait for metrics or timeout
568 select {
569 case meAttributes = <-mm.uniStatusMetricsChan:
570 logger.Debugw(ctx, "received uni-g metrics", log.Fields{"device-id": mm.pDeviceHandler.deviceID})
571 case <-time.After(time.Duration(ConstDefaultOmciTimeout) * time.Second):
572 logger.Errorw(ctx, "timeout waiting for omci-get response for uni status", log.Fields{"device-id": mm.pDeviceHandler.deviceID})
573 // The metrics could be empty in this case
574 break loop1
575 }
576 // Populate metric only if it was enabled.
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800577 for k := range UniStatusGroupMetrics {
578 switch k {
Girish Gowdrae09a6202021-01-12 18:10:59 -0800579 case "uni_admin_state":
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800580 if val, ok := meAttributes["AdministrativeState"]; ok && val != nil {
581 unigMetrics[k] = float32(val.(byte))
582 }
Girish Gowdrae09a6202021-01-12 18:10:59 -0800583 default:
584 // do nothing
585 }
586 }
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800587 var entityID uint32
588 if val, ok := meAttributes["ManagedEntityId"]; ok && val != nil {
589 entityID = uint32(val.(uint16))
590 }
591 // TODO: Rlock needed for reading uniEntityMap?
592 if uniPort, ok := mm.pDeviceHandler.uniEntityMap[entityID]; ok && uniPort != nil {
593 unigMetrics["uni_port_no"] = float32(uniPort.portNo)
594 }
Girish Gowdrae09a6202021-01-12 18:10:59 -0800595 // create slice of metrics given that there could be more than one UNI-G instance
596 metricInfo := voltha.MetricInformation{Metadata: &mmd, Metrics: unigMetrics}
597 metricInfoSlice = append(metricInfoSlice, &metricInfo)
598 }
599 }
600
601 // get the PPTP instance IDs
602 pptpInstKeys := mm.pDeviceHandler.pOnuOmciDevice.pOnuDB.getSortedInstKeys(ctx, me.PhysicalPathTerminationPointEthernetUniClassID)
603loop2:
604 for _, pptpInstID := range pptpInstKeys {
605 // TODO: Include additional information in the voltha.MetricMetaData - like portno, uni-id, instance-id
606 // to uniquely identify this ME instance and also to correlate the ME instance to physical instance
607 var meAttributes me.AttributeValueMap
608 pptpMetrics := make(map[string]float32)
609
610 requestedAttributes := me.AttributeValueMap{"SensedType": 0, "OperationalState": 0, "AdministrativeState": 0}
611 if meInstance := mm.pDeviceHandler.pOnuOmciDevice.PDevOmciCC.sendGetMe(ctx, me.PhysicalPathTerminationPointEthernetUniClassID, pptpInstID, requestedAttributes, ConstDefaultOmciTimeout, true, mm.commMetricsChan); meInstance != nil {
612 // Wait for metrics or timeout
613 select {
614 case meAttributes = <-mm.uniStatusMetricsChan:
615 logger.Debugw(ctx, "received pptp metrics", log.Fields{"device-id": mm.pDeviceHandler.deviceID})
616 case <-time.After(time.Duration(ConstDefaultOmciTimeout) * time.Second):
617 logger.Errorw(ctx, "timeout waiting for omci-get response for uni status", log.Fields{"device-id": mm.pDeviceHandler.deviceID})
618 // The metrics could be empty in this case
619 break loop2
620 }
621
622 // Populate metric only if it was enabled.
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800623 for k := range UniStatusGroupMetrics {
624 switch k {
Girish Gowdrae09a6202021-01-12 18:10:59 -0800625 case "ethernet_type":
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800626 if val, ok := meAttributes["SensedType"]; ok && val != nil {
627 pptpMetrics[k] = float32(val.(byte))
628 }
Girish Gowdrae09a6202021-01-12 18:10:59 -0800629 case "oper_status":
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800630 if val, ok := meAttributes["OperationalState"]; ok && val != nil {
631 pptpMetrics[k] = float32(val.(byte))
632 }
Girish Gowdrae09a6202021-01-12 18:10:59 -0800633 case "uni_admin_state":
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800634 if val, ok := meAttributes["AdministrativeState"]; ok && val != nil {
635 pptpMetrics[k] = float32(val.(byte))
636 }
Girish Gowdrae09a6202021-01-12 18:10:59 -0800637 default:
638 // do nothing
639 }
640 }
641 }
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800642 var entityID uint32
643 if val, ok := meAttributes["ManagedEntityId"]; ok && val != nil {
644 entityID = uint32(val.(uint16))
645 }
646 // TODO: Rlock needed for reading uniEntityMap?
647 if uniPort, ok := mm.pDeviceHandler.uniEntityMap[entityID]; ok && uniPort != nil {
648 pptpMetrics["uni_port_no"] = float32(uniPort.portNo)
649 }
650
Girish Gowdrae09a6202021-01-12 18:10:59 -0800651 // create slice of metrics given that there could be more than one PPTP instance and
652 metricInfo := voltha.MetricInformation{Metadata: &mmd, Metrics: pptpMetrics}
653 metricInfoSlice = append(metricInfoSlice, &metricInfo)
654 }
655
656 // get the VEIP instance IDs
657 veipInstKeys := mm.pDeviceHandler.pOnuOmciDevice.pOnuDB.getSortedInstKeys(ctx, me.VirtualEthernetInterfacePointClassID)
658loop3:
659 for _, veipInstID := range veipInstKeys {
660 // TODO: Include additional information in the voltha.MetricMetaData - like portno, uni-id, instance-id
661 // to uniquely identify this ME instance and also to correlate the ME instance to physical instance
662 var meAttributes me.AttributeValueMap
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800663 veipMetrics := make(map[string]float32)
Girish Gowdrae09a6202021-01-12 18:10:59 -0800664
665 requestedAttributes := me.AttributeValueMap{"OperationalState": 0, "AdministrativeState": 0}
666 if meInstance := mm.pDeviceHandler.pOnuOmciDevice.PDevOmciCC.sendGetMe(ctx, me.VirtualEthernetInterfacePointClassID, veipInstID, requestedAttributes, ConstDefaultOmciTimeout, true, mm.commMetricsChan); meInstance != nil {
667 // Wait for metrics or timeout
668 select {
669 case meAttributes = <-mm.uniStatusMetricsChan:
670 logger.Debugw(ctx, "received veip metrics", log.Fields{"device-id": mm.pDeviceHandler.deviceID})
671 case <-time.After(time.Duration(ConstDefaultOmciTimeout) * time.Second):
672 logger.Errorw(ctx, "timeout waiting for omci-get response for uni status", log.Fields{"device-id": mm.pDeviceHandler.deviceID})
673 // The metrics could be empty in this case
674 break loop3
675 }
676
677 // Populate metric only if it was enabled.
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800678 for k := range UniStatusGroupMetrics {
679 switch k {
Girish Gowdrae09a6202021-01-12 18:10:59 -0800680 case "oper_status":
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800681 if val, ok := meAttributes["OperationalState"]; ok && val != nil {
682 veipMetrics[k] = float32(val.(byte))
683 }
Girish Gowdrae09a6202021-01-12 18:10:59 -0800684 case "uni_admin_state":
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800685 if val, ok := meAttributes["AdministrativeState"]; ok && val != nil {
686 veipMetrics[k] = float32(val.(byte))
687 }
Girish Gowdrae09a6202021-01-12 18:10:59 -0800688 default:
689 // do nothing
690 }
691 }
692 }
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800693
694 var entityID uint32
695 if val, ok := meAttributes["ManagedEntityId"]; ok && val != nil {
696 entityID = uint32(meAttributes["ManagedEntityId"].(uint16))
697 }
698 // TODO: Rlock needed for reading uniEntityMap?
699 if uniPort, ok := mm.pDeviceHandler.uniEntityMap[entityID]; ok && uniPort != nil {
700 veipMetrics["uni_port_no"] = float32(uniPort.portNo)
701 }
702
Girish Gowdrae09a6202021-01-12 18:10:59 -0800703 // create slice of metrics given that there could be more than one VEIP instance
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800704 metricInfo := voltha.MetricInformation{Metadata: &mmd, Metrics: veipMetrics}
Girish Gowdrae09a6202021-01-12 18:10:59 -0800705 metricInfoSlice = append(metricInfoSlice, &metricInfo)
706 }
707
708 return metricInfoSlice
709}
710
711// publishMetrics publishes the metrics on kafka
712func (mm *onuMetricsManager) publishMetrics(ctx context.Context, metricInfo []*voltha.MetricInformation) {
713 var ke voltha.KpiEvent2
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800714 ts := time.Now().Unix()
Girish Gowdrae09a6202021-01-12 18:10:59 -0800715 ke.SliceData = metricInfo
716 ke.Type = voltha.KpiEventType_slice
717 ke.Ts = float64(ts)
718
719 if err := mm.pDeviceHandler.EventProxy.SendKpiEvent(ctx, "STATS_EVENT", &ke, voltha.EventCategory_EQUIPMENT, voltha.EventSubCategory_ONU, ts); err != nil {
720 logger.Errorw(ctx, "failed-to-send-pon-stats", log.Fields{"err": err})
721 }
722}
723
724func (mm *onuMetricsManager) processOmciMessages(ctx context.Context) {
725 logger.Infow(ctx, "Start routine to process OMCI-GET messages for device-id", log.Fields{"device-id": mm.pDeviceHandler.deviceID})
726 // Flush metric collection channels to be safe.
727 // It is possible that there is stale data on this channel if the processOmciMessages routine
728 // is stopped right after issuing a OMCI-GET request and started again.
729 // The processOmciMessages routine will get stopped if startCollector routine (in device_handler.go)
730 // is stopped - as a result of ONU going down.
731 mm.flushMetricCollectionChannels(ctx)
732
733 for {
734 select {
735 case <-mm.stopProcessingOmciResponses: // stop this routine
736 logger.Infow(ctx, "Stop routine to process OMCI-GET messages for device-id", log.Fields{"device-id": mm.pDeviceHandler.deviceID})
737 return
738 case message, ok := <-mm.commMetricsChan:
739 if !ok {
740 logger.Errorw(ctx, "Message couldn't be read from channel", log.Fields{"device-id": mm.pDeviceHandler.deviceID})
741 continue
742 }
743 logger.Debugw(ctx, "Received message on ONU metrics channel", log.Fields{"device-id": mm.pDeviceHandler.deviceID})
744
745 switch message.Type {
746 case OMCI:
747 msg, _ := message.Data.(OmciMessage)
748 mm.handleOmciMessage(ctx, msg)
749 default:
750 logger.Warn(ctx, "Unknown message type received", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "message.Type": message.Type})
751 }
752 }
753 }
754}
755
756func (mm *onuMetricsManager) handleOmciMessage(ctx context.Context, msg OmciMessage) {
757 logger.Debugw(ctx, "omci Msg", log.Fields{"device-id": mm.pDeviceHandler.deviceID,
758 "msgType": msg.OmciMsg.MessageType, "msg": msg})
759 switch msg.OmciMsg.MessageType {
760 case omci.GetResponseType:
761 //TODO: error handling
762 _ = mm.handleOmciGetResponseMessage(ctx, msg)
763
764 default:
765 logger.Warnw(ctx, "Unknown Message Type", log.Fields{"msgType": msg.OmciMsg.MessageType})
766
767 }
768}
769
770func (mm *onuMetricsManager) handleOmciGetResponseMessage(ctx context.Context, msg OmciMessage) error {
771 msgLayer := (*msg.OmciPacket).Layer(omci.LayerTypeGetResponse)
772 if msgLayer == nil {
773 logger.Errorw(ctx, "omci Msg layer could not be detected for GetResponse - handling stopped", log.Fields{"device-id": mm.pDeviceHandler.deviceID})
774 return fmt.Errorf("omci Msg layer could not be detected for GetResponse - handling stopped: %s", mm.pDeviceHandler.deviceID)
775 }
776 msgObj, msgOk := msgLayer.(*omci.GetResponse)
777 if !msgOk {
778 logger.Errorw(ctx, "omci Msg layer could not be assigned for GetResponse - handling stopped", log.Fields{"device-id": mm.pDeviceHandler.deviceID})
779 return fmt.Errorf("omci Msg layer could not be assigned for GetResponse - handling stopped: %s", mm.pDeviceHandler.deviceID)
780 }
781 logger.Debugw(ctx, "OMCI GetResponse Data", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "data-fields": msgObj})
782 if msgObj.Result == me.Success {
783 meAttributes := msgObj.Attributes
784 switch msgObj.EntityClass {
785 case me.AniGClassID:
786 mm.opticalMetricsChan <- meAttributes
787 return nil
788 case me.UniGClassID:
789 mm.uniStatusMetricsChan <- meAttributes
790 return nil
791 case me.PhysicalPathTerminationPointEthernetUniClassID:
792 mm.uniStatusMetricsChan <- meAttributes
793 return nil
794 case me.VirtualEthernetInterfacePointClassID:
795 mm.uniStatusMetricsChan <- meAttributes
796 return nil
797 default:
798 logger.Errorw(ctx, "unhandled omci get response message",
799 log.Fields{"device-id": mm.pDeviceHandler.deviceID, "class-id": msgObj.EntityClass})
800 }
801 }
802
803 return errors.New("unhandled-omci-get-response-message")
804}
805
806// flushMetricCollectionChannels flushes all metric collection channels for any stale OMCI responses
807func (mm *onuMetricsManager) flushMetricCollectionChannels(ctx context.Context) {
808 // flush commMetricsChan
809 select {
810 case <-mm.commMetricsChan:
811 logger.Debug(ctx, "flushed common metrics channel")
812 default:
813 }
814
815 // flush opticalMetricsChan
816 select {
817 case <-mm.opticalMetricsChan:
818 logger.Debug(ctx, "flushed optical metrics channel")
819 default:
820 }
821
822 // flush uniStatusMetricsChan
823 select {
824 case <-mm.uniStatusMetricsChan:
825 logger.Debug(ctx, "flushed uni status metrics channel")
826 default:
827 }
828}