blob: 311050102f4807a7b736243888c1541ed4a002a2 [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"
Girish Gowdrae09a6202021-01-12 18:10:59 -080022 "fmt"
Girish Gowdrae0140f02021-02-02 16:55:09 -080023 "github.com/looplab/fsm"
Girish Gowdrae09a6202021-01-12 18:10:59 -080024 "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 Gowdrae0140f02021-02-02 16:55:09 -080032const (
33 // events of L2 PM FSM
34 l2PmEventInit = "l2PmEventInit"
35 l2PmEventTick = "l2PmEventTick"
36 l2PmEventSuccess = "l2PmEventSuccess"
37 l2PmEventFailure = "l2PmEventFailure"
38 l2PmEventAddMe = "l2PmEventAddMe"
39 l2PmEventDeleteMe = "l2PmEventDeleteMe"
40 l2PmEventStop = "l2PmEventStop"
41)
42const (
43 // states of L2 PM FSM
44 l2PmStNull = "l2PmStNull"
45 l2PmStStarting = "l2PmStStarting"
46 l2PmStSyncTime = "l2PmStSyncTime"
47 l2PmStIdle = "l2PmStIdle"
48 l2PmStCreatePmMe = "l2PmStCreatePm"
49 l2PmStDeletePmMe = "l2PmStDeletePmMe"
50 l2PmStCollectData = "l2PmStCollectData"
51)
52
53const cL2PmFsmIdleState = l2PmStIdle
54
Girish Gowdra5a7c4922021-01-22 18:33:41 -080055// general constants used for overall Metric Collection management
56const (
57 DefaultMetricCollectionFrequency = 15 * 60 // unit in seconds. This setting can be changed from voltha NBI PmConfig configuration
58 GroupMetricEnabled = true // This is READONLY and cannot be changed from VOLTHA NBI
59 DefaultFrequencyOverrideEnabled = true // This is READONLY and cannot be changed from VOLTHA NBI
60 FrequencyGranularity = 5 // The frequency (in seconds) has to be multiple of 5. This setting cannot changed later.
61)
62
63// OpticalPowerGroupMetrics are supported optical pm names
64var OpticalPowerGroupMetrics = map[string]voltha.PmConfig_PmType{
65 "ani_g_instance_id": voltha.PmConfig_CONTEXT,
66 "transmit_power": voltha.PmConfig_GAUGE,
67 "receive_power": voltha.PmConfig_GAUGE,
68}
69
70// OpticalPowerGroupMetrics specific constants
71const (
Girish Gowdrae0140f02021-02-02 16:55:09 -080072 OpticalPowerGroupMetricName = "PON_Optical"
Girish Gowdra5a7c4922021-01-22 18:33:41 -080073 OpticalPowerGroupMetricEnabled = true // This setting can be changed from voltha NBI PmConfig configuration
74 OpticalPowerMetricGroupCollectionFrequency = 5 * 60 // unit in seconds. This setting can be changed from voltha NBI PmConfig configuration
75)
76
77// UniStatusGroupMetrics are supported UNI status names
78var UniStatusGroupMetrics = map[string]voltha.PmConfig_PmType{
79 "uni_port_no": voltha.PmConfig_CONTEXT,
80 "ethernet_type": voltha.PmConfig_GAUGE,
81 "oper_status": voltha.PmConfig_GAUGE,
82 "uni_admin_state": voltha.PmConfig_GAUGE,
83}
84
85// UniStatusGroupMetrics specific constants
86const (
Girish Gowdrae0140f02021-02-02 16:55:09 -080087 UniStatusGroupMetricName = "UNI_Status"
Girish Gowdra5a7c4922021-01-22 18:33:41 -080088 UniStatusGroupMetricEnabled = true // This setting can be changed from voltha NBI PmConfig configuration
89 UniStatusMetricGroupCollectionFrequency = 5 * 60 // unit in seconds. This setting can be changed from voltha NBI PmConfig configuration
90)
91
Girish Gowdrae0140f02021-02-02 16:55:09 -080092// *** Classical L2 PM Counters begin ***
93
94// EthernetBridgeHistory are supported ethernet bridge history counters fetched from
95// Ethernet Frame Performance Monitoring History Data Downstream and Ethernet Frame Performance Monitoring History Data Upstream MEs.
96var EthernetBridgeHistory = map[string]voltha.PmConfig_PmType{
97 "class_id": voltha.PmConfig_CONTEXT,
98 "entity_id": voltha.PmConfig_CONTEXT,
99 "interval_end_time": voltha.PmConfig_CONTEXT,
100 "parent_class_id": voltha.PmConfig_CONTEXT,
101 "parent_entity_id": voltha.PmConfig_CONTEXT,
102 "upstream": voltha.PmConfig_CONTEXT,
103
104 "drop_events": voltha.PmConfig_COUNTER,
105 "octets": voltha.PmConfig_COUNTER,
106 "packets": voltha.PmConfig_COUNTER,
107 "broadcast_packets": voltha.PmConfig_COUNTER,
108 "multicast_packets": voltha.PmConfig_COUNTER,
109 "crc_errored_packets": voltha.PmConfig_COUNTER,
110 "undersize_packets": voltha.PmConfig_COUNTER,
111 "oversize_packets": voltha.PmConfig_COUNTER,
112 "64_octets": voltha.PmConfig_COUNTER,
113 "65_to_127_octets": voltha.PmConfig_COUNTER,
114 "128_to_255_octets": voltha.PmConfig_COUNTER,
115 "256_to_511_octets": voltha.PmConfig_COUNTER,
116 "512_to_1023_octets": voltha.PmConfig_COUNTER,
117 "1024_to_1518_octets": voltha.PmConfig_COUNTER,
118}
119
120// EthernetUniHistory are supported ethernet uni history counters fetched from
121// Ethernet Performance Monitoring History Data ME.
122var EthernetUniHistory = map[string]voltha.PmConfig_PmType{
123 "class_id": voltha.PmConfig_CONTEXT,
124 "entity_id": voltha.PmConfig_CONTEXT,
125 "interval_end_time": voltha.PmConfig_CONTEXT,
126
127 "fcs_errors": voltha.PmConfig_COUNTER,
128 "excessive_collision_counter": voltha.PmConfig_COUNTER,
129 "late_collision_counter": voltha.PmConfig_COUNTER,
130 "frames_too_long": voltha.PmConfig_COUNTER,
131 "buffer_overflows_on_rx": voltha.PmConfig_COUNTER,
132 "buffer_overflows_on_tx": voltha.PmConfig_COUNTER,
133 "single_collision_frame_counter": voltha.PmConfig_COUNTER,
134 "multiple_collisions_frame_counter": voltha.PmConfig_COUNTER,
135 "sqe_counter": voltha.PmConfig_COUNTER,
136 "deferred_tx_counter": voltha.PmConfig_COUNTER,
137 "internal_mac_tx_error_counter": voltha.PmConfig_COUNTER,
138 "carrier_sense_error_counter": voltha.PmConfig_COUNTER,
139 "alignment_error_counter": voltha.PmConfig_COUNTER,
140 "internal_mac_rx_error_counter": voltha.PmConfig_COUNTER,
141}
142
143// Constants specific for L2 PM collection
144const (
145 L2PmCollectionInterval = 15 * 60 // Unit in seconds. Do not change this as this fixed by OMCI specification for L2 PM counters
146 SyncTimeRetryInterval = 15 // Unit seconds
147 L2PmCreateAttempts = 3
148 L2PmCollectAttempts = 3
149 MaxL2PMGetPayLoadSize = 29
150)
151
152// EthernetUniHistoryName specific constants
153const (
154 EthernetBridgeHistoryName = "Ethernet_Bridge_Port_History"
155 EthernetBridgeHistoryEnabled = true // This setting can be changed from voltha NBI PmConfig configuration
156 EthernetBridgeHistoryFrequency = L2PmCollectionInterval
157)
158
159// EthernetBridgeHistory specific constants
160const (
161 EthernetUniHistoryName = "Ethernet_UNI_History"
162 EthernetUniHistoryEnabled = true // This setting can be changed from voltha NBI PmConfig configuration
163 EthernetUniHistoryFrequency = L2PmCollectionInterval
164)
165
166// *** Classical L2 PM Counters end ***
167
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800168type groupMetric struct {
169 groupName string
170 enabled bool
171 frequency uint32 // valid only if FrequencyOverride is enabled.
172 metricMap map[string]voltha.PmConfig_PmType
173 nextCollectionInterval time.Time // valid only if FrequencyOverride is enabled.
Girish Gowdrae0140f02021-02-02 16:55:09 -0800174 isL2PMCounter bool // true for only L2 PM counters
175 collectAttempts uint32 // number of attempts to collect L2 PM data
176 createRetryAttempts uint32 // number of attempts to try creating the L2 PM ME
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800177}
178
179type standaloneMetric struct {
180 metricName string
181 enabled bool
182 frequency uint32 // valid only if FrequencyOverride is enabled.
183 nextCollectionInterval time.Time // valid only if FrequencyOverride is enabled.
184}
185
Girish Gowdrae09a6202021-01-12 18:10:59 -0800186type onuMetricsManager struct {
187 pDeviceHandler *deviceHandler
Girish Gowdrae0140f02021-02-02 16:55:09 -0800188 pAdaptFsm *AdapterFsm
Girish Gowdrae09a6202021-01-12 18:10:59 -0800189
Girish Gowdrae0140f02021-02-02 16:55:09 -0800190 opticalMetricsChan chan me.AttributeValueMap
191 uniStatusMetricsChan chan me.AttributeValueMap
192 l2PmChan chan me.AttributeValueMap
193 syncTimeResponseChan chan bool // true is success, false is fail
194 l2PmCreateOrDeleteResponseChan chan bool // true is success, false is fail
195
196 activeL2Pms []string // list of active l2 pm MEs created on the ONU.
197 l2PmToDelete []string // list of L2 PMs to delete
198 l2PmToAdd []string // list of L2 PM to add
Girish Gowdrae09a6202021-01-12 18:10:59 -0800199
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800200 groupMetricMap map[string]*groupMetric
201 standaloneMetricMap map[string]*standaloneMetric
202
Girish Gowdrae09a6202021-01-12 18:10:59 -0800203 stopProcessingOmciResponses chan bool
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800204
Girish Gowdrae0140f02021-02-02 16:55:09 -0800205 stopTicks chan bool
206
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800207 nextGlobalMetricCollectionTime time.Time // valid only if pmConfig.FreqOverride is set to false.
208
209 onuMetricsManagerLock sync.RWMutex
Girish Gowdrae09a6202021-01-12 18:10:59 -0800210}
211
212// newonuMetricsManager returns a new instance of the newonuMetricsManager
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800213// Note that none of the context stored internally in onuMetricsManager is backed up on KV store for resiliency.
214// Metric collection is not a critical operation that needs support for resiliency. On adapter restart, some context
215// could be lost (except for Device.PmConfigs which is backed up the rw-core on KV store). An example of information
216// that is lost on adapter restart is nextCollectionInterval time.
Girish Gowdrae09a6202021-01-12 18:10:59 -0800217func newonuMetricsManager(ctx context.Context, dh *deviceHandler) *onuMetricsManager {
218
219 var metricsManager onuMetricsManager
220 logger.Debugw(ctx, "init-onuMetricsManager", log.Fields{"device-id": dh.deviceID})
221 metricsManager.pDeviceHandler = dh
222
Girish Gowdrae0140f02021-02-02 16:55:09 -0800223 commMetricsChan := make(chan Message)
Girish Gowdrae09a6202021-01-12 18:10:59 -0800224 metricsManager.opticalMetricsChan = make(chan me.AttributeValueMap)
225 metricsManager.uniStatusMetricsChan = make(chan me.AttributeValueMap)
Girish Gowdrae0140f02021-02-02 16:55:09 -0800226 metricsManager.l2PmChan = make(chan me.AttributeValueMap)
227
228 metricsManager.syncTimeResponseChan = make(chan bool)
229 metricsManager.l2PmCreateOrDeleteResponseChan = make(chan bool)
230
Girish Gowdrae09a6202021-01-12 18:10:59 -0800231 metricsManager.stopProcessingOmciResponses = make(chan bool)
Girish Gowdrae0140f02021-02-02 16:55:09 -0800232 metricsManager.stopTicks = make(chan bool)
Girish Gowdrae09a6202021-01-12 18:10:59 -0800233
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800234 metricsManager.groupMetricMap = make(map[string]*groupMetric)
235 metricsManager.standaloneMetricMap = make(map[string]*standaloneMetric)
236
237 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
238 dh.pmConfigs = &voltha.PmConfigs{}
239 dh.pmConfigs.Id = dh.deviceID
240 dh.pmConfigs.DefaultFreq = DefaultMetricCollectionFrequency
241 dh.pmConfigs.Grouped = GroupMetricEnabled
242 dh.pmConfigs.FreqOverride = DefaultFrequencyOverrideEnabled
243
244 // Populate group metrics.
245 // Lets populate irrespective of GroupMetricEnabled is true or not.
246 // The group metrics collection will decided on this flag later
247
248 // Populate optical power group metrics
249 var opPmConfigSlice []*voltha.PmConfig
250 for k, v := range OpticalPowerGroupMetrics {
251 opPmConfigSlice = append(opPmConfigSlice, &voltha.PmConfig{Name: k, Type: v})
252 }
253 opticalPowerGroupMetric := voltha.PmGroupConfig{
254 GroupName: OpticalPowerGroupMetricName,
Girish Gowdraaf0ad632021-01-27 13:00:01 -0800255 Enabled: OpticalPowerGroupMetricEnabled && dh.pOpenOnuAc.metricsEnabled,
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800256 GroupFreq: OpticalPowerMetricGroupCollectionFrequency,
257 Metrics: opPmConfigSlice,
258 }
259 dh.pmConfigs.Groups = append(dh.pmConfigs.Groups, &opticalPowerGroupMetric)
260
261 // Populate uni status group metrics
262 var uniStPmConfigSlice []*voltha.PmConfig
263 for k, v := range UniStatusGroupMetrics {
264 uniStPmConfigSlice = append(uniStPmConfigSlice, &voltha.PmConfig{Name: k, Type: v})
265 }
266 uniStatusGroupMetric := voltha.PmGroupConfig{
267 GroupName: UniStatusGroupMetricName,
Girish Gowdraaf0ad632021-01-27 13:00:01 -0800268 Enabled: UniStatusGroupMetricEnabled && dh.pOpenOnuAc.metricsEnabled,
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800269 GroupFreq: UniStatusMetricGroupCollectionFrequency,
270 Metrics: uniStPmConfigSlice,
271 }
272 dh.pmConfigs.Groups = append(dh.pmConfigs.Groups, &uniStatusGroupMetric)
273
Girish Gowdrae0140f02021-02-02 16:55:09 -0800274 // classical l2 pm counter start
275
276 // Populate ethernet bridge history group metrics
277 var ethBridgeHistoryConfigSlice []*voltha.PmConfig
278 for k, v := range EthernetBridgeHistory {
279 ethBridgeHistoryConfigSlice = append(ethBridgeHistoryConfigSlice, &voltha.PmConfig{Name: k, Type: v})
280 }
281 ethBridgeHistoryGroupMetric := voltha.PmGroupConfig{
282 GroupName: EthernetBridgeHistoryName,
283 Enabled: EthernetBridgeHistoryEnabled && dh.pOpenOnuAc.metricsEnabled,
284 GroupFreq: EthernetBridgeHistoryFrequency,
285 Metrics: ethBridgeHistoryConfigSlice,
286 }
287 dh.pmConfigs.Groups = append(dh.pmConfigs.Groups, &ethBridgeHistoryGroupMetric)
288
289 // Populate ethernet bridge history group metrics
290 var ethUniHistoryConfigSlice []*voltha.PmConfig
291 for k, v := range EthernetUniHistory {
292 ethUniHistoryConfigSlice = append(ethUniHistoryConfigSlice, &voltha.PmConfig{Name: k, Type: v})
293 }
294 ethUniHistoryGroupMetric := voltha.PmGroupConfig{
295 GroupName: EthernetUniHistoryName,
296 Enabled: EthernetUniHistoryEnabled && dh.pOpenOnuAc.metricsEnabled,
297 GroupFreq: EthernetUniHistoryFrequency,
298 Metrics: ethUniHistoryConfigSlice,
299 }
300 dh.pmConfigs.Groups = append(dh.pmConfigs.Groups, &ethUniHistoryGroupMetric)
301
302 // classical l2 pm counter end
303
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800304 // Add standalone metric (if present) after this (will be added to dh.pmConfigs.Metrics)
305 }
306
307 // Populate local group metric structures
308 for _, g := range dh.pmConfigs.Groups {
309 metricsManager.groupMetricMap[g.GroupName] = &groupMetric{
310 groupName: g.GroupName,
311 enabled: g.Enabled,
312 frequency: g.GroupFreq,
313 }
314 switch g.GroupName {
315 case OpticalPowerGroupMetricName:
316 metricsManager.groupMetricMap[g.GroupName].metricMap = OpticalPowerGroupMetrics
317 case UniStatusGroupMetricName:
318 metricsManager.groupMetricMap[g.GroupName].metricMap = UniStatusGroupMetrics
Girish Gowdrae0140f02021-02-02 16:55:09 -0800319 case EthernetBridgeHistoryName:
320 metricsManager.groupMetricMap[g.GroupName].metricMap = EthernetBridgeHistory
321 metricsManager.groupMetricMap[g.GroupName].isL2PMCounter = true
322 case EthernetUniHistoryName:
323 metricsManager.groupMetricMap[g.GroupName].metricMap = EthernetUniHistory
324 metricsManager.groupMetricMap[g.GroupName].isL2PMCounter = true
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800325 default:
326 logger.Errorw(ctx, "unhandled-group-name", log.Fields{"groupName": g.GroupName})
327 }
328 }
329
330 // Populate local standalone metric structures
331 for _, m := range dh.pmConfigs.Metrics {
332 metricsManager.standaloneMetricMap[m.Name] = &standaloneMetric{
333 metricName: m.Name,
334 enabled: m.Enabled,
335 frequency: m.SampleFreq,
336 }
337 switch m.Name {
338 // None exist as of now. Add when available.
339 default:
340 logger.Errorw(ctx, "unhandled-metric-name", log.Fields{"metricName": m.Name})
341 }
342 }
343
Girish Gowdrae0140f02021-02-02 16:55:09 -0800344 metricsManager.pAdaptFsm = NewAdapterFsm("L2PmFSM", dh.deviceID, commMetricsChan)
345 if metricsManager.pAdaptFsm == nil {
346 logger.Errorw(ctx, "L2PMFsm AdapterFsm could not be instantiated!!", log.Fields{
347 "device-id": dh.deviceID})
348 return nil
349 }
350 // L2 PM FSM related state machine
351 metricsManager.pAdaptFsm.pFsm = fsm.NewFSM(
352 l2PmStNull,
353 fsm.Events{
354 {Name: l2PmEventInit, Src: []string{l2PmStNull}, Dst: l2PmStStarting},
355 {Name: l2PmEventTick, Src: []string{l2PmStStarting}, Dst: l2PmStSyncTime},
356 {Name: l2PmEventTick, Src: []string{l2PmStIdle, l2PmEventDeleteMe, l2PmEventAddMe}, Dst: l2PmStCollectData},
357 {Name: l2PmEventSuccess, Src: []string{l2PmStSyncTime, l2PmStCreatePmMe, l2PmStDeletePmMe, l2PmStCollectData}, Dst: l2PmStIdle},
358 {Name: l2PmEventFailure, Src: []string{l2PmStCreatePmMe, l2PmStDeletePmMe, l2PmStCollectData}, Dst: l2PmStIdle},
359 {Name: l2PmEventFailure, Src: []string{l2PmStSyncTime}, Dst: l2PmStSyncTime},
360 {Name: l2PmEventAddMe, Src: []string{l2PmStIdle}, Dst: l2PmStCreatePmMe},
361 {Name: l2PmEventDeleteMe, Src: []string{l2PmStIdle}, Dst: l2PmStDeletePmMe},
362 {Name: l2PmEventStop, Src: []string{l2PmStNull, l2PmStStarting, l2PmStSyncTime, l2PmStIdle, l2PmStCreatePmMe, l2PmStDeletePmMe, l2PmStCollectData}, Dst: l2PmStNull},
363 },
364 fsm.Callbacks{
365 "enter_state": func(e *fsm.Event) { metricsManager.pAdaptFsm.logFsmStateChange(ctx, e) },
366 "enter_" + l2PmStNull: func(e *fsm.Event) { metricsManager.l2PMFsmNull(ctx, e) },
367 "enter_" + l2PmStIdle: func(e *fsm.Event) { metricsManager.l2PMFsmIdle(ctx, e) },
368 "enter_" + l2PmStStarting: func(e *fsm.Event) { metricsManager.l2PMFsmStarting(ctx, e) },
369 "enter_" + l2PmStSyncTime: func(e *fsm.Event) { metricsManager.l2PMFsmSyncTime(ctx, e) },
370 "enter_" + l2PmStCollectData: func(e *fsm.Event) { metricsManager.l2PmFsmCollectData(ctx, e) },
371 "enter_" + l2PmStCreatePmMe: func(e *fsm.Event) { metricsManager.l2PmFsmCreatePM(ctx, e) },
372 "enter_" + l2PmStDeletePmMe: func(e *fsm.Event) { metricsManager.l2PmFsmDeletePM(ctx, e) },
373 },
374 )
375
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800376 // initialize the next metric collection intervals.
377 metricsManager.initializeMetricCollectionTime(ctx)
378 logger.Info(ctx, "init-onuMetricsManager completed", log.Fields{"device-id": dh.deviceID})
Girish Gowdrae09a6202021-01-12 18:10:59 -0800379 return &metricsManager
380}
381
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800382func (mm *onuMetricsManager) initializeMetricCollectionTime(ctx context.Context) {
383 if mm.pDeviceHandler.pmConfigs.FreqOverride {
384 // If mm.pDeviceHandler.pmConfigs.FreqOverride is set to true, then group/standalone metric specific interval applies
385 mm.onuMetricsManagerLock.Lock()
386 defer mm.onuMetricsManagerLock.Unlock()
387 for _, v := range mm.groupMetricMap {
Girish Gowdrae0140f02021-02-02 16:55:09 -0800388 if v.enabled && !v.isL2PMCounter { // L2 PM counter collection is managed in a L2PmFsm
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800389 v.nextCollectionInterval = time.Now().Add(time.Duration(v.frequency) * time.Second)
390 }
391 }
392
393 for _, v := range mm.standaloneMetricMap {
394 if v.enabled {
395 v.nextCollectionInterval = time.Now().Add(time.Duration(v.frequency) * time.Second)
396 }
397 }
398 } else {
399 // If mm.pDeviceHandler.pmConfigs.FreqOverride is set to false, then overall metric specific interval applies
400 mm.nextGlobalMetricCollectionTime = time.Now().Add(time.Duration(mm.pDeviceHandler.pmConfigs.DefaultFreq) * time.Second)
401 }
402 logger.Infow(ctx, "initialized standalone group/metric collection time", log.Fields{"device-id": mm.pDeviceHandler.deviceID})
403}
404
405func (mm *onuMetricsManager) updateDefaultFrequency(ctx context.Context, pmConfigs *voltha.PmConfigs) error {
406 // Verify that the configured DefaultFrequency is > 0 and is a multiple of FrequencyGranularity
Girish Gowdraaf0ad632021-01-27 13:00:01 -0800407 if pmConfigs.DefaultFreq == 0 || (pmConfigs.DefaultFreq > 0 && pmConfigs.DefaultFreq%FrequencyGranularity != 0) {
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800408 logger.Errorf(ctx, "frequency-%u-should-be-a-multiple-of-%u", pmConfigs.DefaultFreq, FrequencyGranularity)
409 return fmt.Errorf("frequency-%d-should-be-a-multiple-of-%d", pmConfigs.DefaultFreq, FrequencyGranularity)
410 }
411 mm.pDeviceHandler.pmConfigs.DefaultFreq = pmConfigs.DefaultFreq
412 // re-set the nextGlobalMetricCollectionTime based on the new DefaultFreq
413 mm.nextGlobalMetricCollectionTime = time.Now().Add(time.Duration(mm.pDeviceHandler.pmConfigs.DefaultFreq) * time.Second)
414 logger.Debugw(ctx, "frequency-updated--new-frequency", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "frequency": mm.pDeviceHandler.pmConfigs.DefaultFreq})
415 return nil
416}
417
418func (mm *onuMetricsManager) updateGroupFreq(ctx context.Context, aGroupName string, pmConfigs *voltha.PmConfigs) error {
419 var newGroupFreq uint32
420 found := false
421 groupSliceIdx := 0
422 var group *voltha.PmGroupConfig
423 for groupSliceIdx, group = range pmConfigs.Groups {
424 if group.GroupName == aGroupName {
Girish Gowdraaf0ad632021-01-27 13:00:01 -0800425 // freq 0 is not allowed and it should be multiple of FrequencyGranularity
426 if group.GroupFreq == 0 || (group.GroupFreq > 0 && group.GroupFreq%FrequencyGranularity != 0) {
427 logger.Errorf(ctx, "frequency-%u-should-be-a-multiple-of-%u", group.GroupFreq, FrequencyGranularity)
428 return fmt.Errorf("frequency-%d-should-be-a-multiple-of-%d", group.GroupFreq, FrequencyGranularity)
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800429 }
Girish Gowdraaf0ad632021-01-27 13:00:01 -0800430 newGroupFreq = group.GroupFreq
431 found = true
432 break
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800433 }
434 }
435 // if not found update group freq and next collection interval for the group
436 if !found {
437 logger.Errorw(ctx, "group name not found", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "groupName": aGroupName})
438 return fmt.Errorf("group-name-not-found-%v", aGroupName)
439 }
440
441 updated := false
442 mm.onuMetricsManagerLock.Lock()
443 defer mm.onuMetricsManagerLock.Unlock()
444 for k, v := range mm.groupMetricMap {
Girish Gowdrae0140f02021-02-02 16:55:09 -0800445 if k == aGroupName && !v.isL2PMCounter { // We cannot allow the L2 PM counter frequency to be updated. It is 15min fixed by OMCI spec
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800446 v.frequency = newGroupFreq
447 // update internal pm config
448 mm.pDeviceHandler.pmConfigs.Groups[groupSliceIdx].GroupFreq = newGroupFreq
449 // Also updated the next group metric collection time from now
450 v.nextCollectionInterval = time.Now().Add(time.Duration(newGroupFreq) * time.Second)
451 updated = true
452 logger.Infow(ctx, "group frequency updated", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "newGroupFreq": newGroupFreq, "groupName": aGroupName})
453 }
454 }
455 if !updated {
456 logger.Errorw(ctx, "group frequency not updated", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "newGroupFreq": newGroupFreq, "groupName": aGroupName})
457 return fmt.Errorf("internal-error-during-group-freq-update--groupname-%s-freq-%d", aGroupName, newGroupFreq)
458 }
459 return nil
460}
461
462func (mm *onuMetricsManager) updateMetricFreq(ctx context.Context, aMetricName string, pmConfigs *voltha.PmConfigs) error {
463 var newMetricFreq uint32
464 found := false
465 metricSliceIdx := 0
466 var metric *voltha.PmConfig
467 for metricSliceIdx, metric = range pmConfigs.Metrics {
468 if metric.Name == aMetricName {
Girish Gowdraaf0ad632021-01-27 13:00:01 -0800469 // freq 0 is not allowed and it should be multiple of FrequencyGranularity
470 if metric.SampleFreq == 0 || (metric.SampleFreq > 0 && metric.SampleFreq%FrequencyGranularity != 0) {
471 logger.Errorf(ctx, "frequency-%u-should-be-a-multiple-of-%u", metric.SampleFreq, FrequencyGranularity)
472 return fmt.Errorf("frequency-%d-should-be-a-multiple-of-%d", metric.SampleFreq, FrequencyGranularity)
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800473 }
Girish Gowdraaf0ad632021-01-27 13:00:01 -0800474 newMetricFreq = metric.SampleFreq
475 found = true
476 break
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800477 }
478 }
479 if !found {
480 logger.Errorw(ctx, "metric name not found", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "metricName": aMetricName})
481 return fmt.Errorf("metric-name-not-found-%v", aMetricName)
482 }
483
484 updated := false
485 mm.onuMetricsManagerLock.Lock()
486 defer mm.onuMetricsManagerLock.Unlock()
487 for k, v := range mm.groupMetricMap {
Girish Gowdraaf0ad632021-01-27 13:00:01 -0800488 if k == aMetricName {
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800489 v.frequency = newMetricFreq
490 // update internal pm config
491 mm.pDeviceHandler.pmConfigs.Metrics[metricSliceIdx].SampleFreq = newMetricFreq
492 // Also updated the next standalone metric collection time from now
493 v.nextCollectionInterval = time.Now().Add(time.Duration(newMetricFreq) * time.Second)
494 updated = true
495 logger.Infow(ctx, "metric frequency updated", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "newMetricFreq": newMetricFreq, "aMetricName": aMetricName})
496 }
497 }
498 if !updated {
499 logger.Errorw(ctx, "metric frequency not updated", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "newMetricFreq": newMetricFreq, "aMetricName": aMetricName})
500 return fmt.Errorf("internal-error-during-standalone-metric-update--matricnane-%s-freq-%d", aMetricName, newMetricFreq)
501 }
502 return nil
503}
504
505func (mm *onuMetricsManager) updateGroupSupport(ctx context.Context, aGroupName string, pmConfigs *voltha.PmConfigs) error {
506 groupSliceIdx := 0
507 var group *voltha.PmGroupConfig
508
509 for groupSliceIdx, group = range pmConfigs.Groups {
510 if group.GroupName == aGroupName {
511 break
512 }
513 }
514 if group == nil {
515 logger.Errorw(ctx, "group metric not found", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "groupName": aGroupName})
516 return fmt.Errorf("group-not-found--groupName-%s", aGroupName)
517 }
518
519 updated := false
520 mm.onuMetricsManagerLock.Lock()
521 defer mm.onuMetricsManagerLock.Unlock()
522 for k, v := range mm.groupMetricMap {
523 if k == aGroupName && v.enabled != group.Enabled {
524 mm.pDeviceHandler.pmConfigs.Groups[groupSliceIdx].Enabled = group.Enabled
525 v.enabled = group.Enabled
Girish Gowdrae0140f02021-02-02 16:55:09 -0800526 if group.Enabled {
527 if v.isL2PMCounter {
528 // If it is a L2 PM counter we need to mark the PM to be added
529 mm.l2PmToAdd = mm.appendIfMissing(mm.l2PmToAdd, v.groupName)
530 // If the group support flag toggles too soon, we need to delete the group name from l2PmToDelete slice
531 mm.l2PmToDelete = mm.removeIfFound(mm.l2PmToDelete, v.groupName)
532 } else if mm.pDeviceHandler.pmConfigs.FreqOverride { // otherwise just update the next collection interval
533 v.nextCollectionInterval = time.Now().Add(time.Duration(v.frequency) * time.Second)
534 }
535 } else { // group counter is disabled
536 if v.isL2PMCounter {
537 // If it is a L2 PM counter we need to mark the PM to be deleted
538 mm.l2PmToDelete = mm.appendIfMissing(mm.l2PmToDelete, v.groupName)
539 // If the group support flag toggles too soon, we need to delete the group name from l2PmToAdd slice
540 mm.l2PmToAdd = mm.removeIfFound(mm.l2PmToAdd, v.groupName)
541 }
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800542 }
543 updated = true
Girish Gowdrae0140f02021-02-02 16:55:09 -0800544 if v.isL2PMCounter {
545 logger.Infow(ctx, "l2 pm group metric support updated",
546 log.Fields{"device-id": mm.pDeviceHandler.deviceID, "groupName": aGroupName, "enabled": group.Enabled, "l2PmToAdd": mm.l2PmToAdd, "l2PmToDelete": mm.l2PmToDelete})
547 } else {
548 logger.Infow(ctx, "group metric support updated", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "groupName": aGroupName, "enabled": group.Enabled})
549 }
550 break
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800551 }
552 }
553
554 if !updated {
555 logger.Errorw(ctx, "group metric support not updated", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "groupName": aGroupName})
556 return fmt.Errorf("internal-error-during-group-support-update--groupName-%s", aGroupName)
557 }
558 return nil
559}
560
561func (mm *onuMetricsManager) updateMetricSupport(ctx context.Context, aMetricName string, pmConfigs *voltha.PmConfigs) error {
562 metricSliceIdx := 0
563 var metric *voltha.PmConfig
564
565 for metricSliceIdx, metric = range pmConfigs.Metrics {
566 if metric.Name == aMetricName {
567 break
568 }
569 }
570
571 if metric == nil {
572 logger.Errorw(ctx, "standalone metric not found", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "metricName": aMetricName})
573 return fmt.Errorf("metric-not-found--metricname-%s", aMetricName)
574 }
575
576 updated := false
577 mm.onuMetricsManagerLock.Lock()
578 defer mm.onuMetricsManagerLock.Unlock()
579 for k, v := range mm.standaloneMetricMap {
580 if k == aMetricName && v.enabled != metric.Enabled {
581 mm.pDeviceHandler.pmConfigs.Metrics[metricSliceIdx].Enabled = metric.Enabled
582 v.enabled = metric.Enabled
583 // If the standalone metric is now enabled and frequency override is enabled, set the next metric collection time
584 if metric.Enabled && mm.pDeviceHandler.pmConfigs.FreqOverride {
585 v.nextCollectionInterval = time.Now().Add(time.Duration(v.frequency) * time.Second)
586 }
587 updated = true
588 logger.Infow(ctx, "standalone metric support updated", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "metricName": aMetricName, "enabled": metric.Enabled})
589 }
590 }
591 if !updated {
592 logger.Errorw(ctx, "standalone metric support not updated", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "metricName": aMetricName})
593 return fmt.Errorf("internal-error-during-standalone-support-update--metricname-%s", aMetricName)
594 }
595 return nil
596}
597
598func (mm *onuMetricsManager) collectAllGroupAndStandaloneMetrics(ctx context.Context) {
599 if mm.pDeviceHandler.pmConfigs.Grouped { // metrics are managed as a group.
600 go mm.collectAllGroupMetrics(ctx)
601 } else {
602 go mm.collectAllStandaloneMetrics(ctx)
603 }
604}
605
606func (mm *onuMetricsManager) collectAllGroupMetrics(ctx context.Context) {
607 go func() {
608 logger.Debug(ctx, "startCollector before collecting optical metrics")
609 metricInfo := mm.collectOpticalMetrics(ctx)
610 if metricInfo != nil {
611 mm.publishMetrics(ctx, metricInfo)
612 }
613 }()
614
615 go func() {
616 logger.Debug(ctx, "startCollector before collecting uni metrics")
617 metricInfo := mm.collectUniStatusMetrics(ctx)
618 if metricInfo != nil {
619 mm.publishMetrics(ctx, metricInfo)
620 }
621 }()
622
623 // Add more here
624}
625
626func (mm *onuMetricsManager) collectAllStandaloneMetrics(ctx context.Context) {
627 // None exists as of now, add when available here
628}
629
630func (mm *onuMetricsManager) collectGroupMetric(ctx context.Context, groupName string) {
631 switch groupName {
632 case OpticalPowerGroupMetricName:
633 go func() {
634 if mi := mm.collectOpticalMetrics(ctx); mm != nil {
635 mm.publishMetrics(ctx, mi)
636 }
637 }()
638 case UniStatusGroupMetricName:
639 go func() {
640 if mi := mm.collectUniStatusMetrics(ctx); mm != nil {
641 mm.publishMetrics(ctx, mi)
642 }
643 }()
644 default:
645 logger.Errorw(ctx, "unhandled group metric name", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "groupName": groupName})
646 }
647}
648
649func (mm *onuMetricsManager) collectStandaloneMetric(ctx context.Context, metricName string) {
650 switch metricName {
651 // None exist as of now, add when available
652 default:
653 logger.Errorw(ctx, "unhandled standalone metric name", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "metricName": metricName})
654 }
655}
656
657// collectOpticalMetrics collects groups metrics related to optical power from ani-g ME.
Girish Gowdrae09a6202021-01-12 18:10:59 -0800658func (mm *onuMetricsManager) collectOpticalMetrics(ctx context.Context) []*voltha.MetricInformation {
659 logger.Debugw(ctx, "collectOpticalMetrics", log.Fields{"device-id": mm.pDeviceHandler.deviceID})
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800660
661 mm.onuMetricsManagerLock.RLock()
662 if !mm.groupMetricMap[OpticalPowerGroupMetricName].enabled {
663 mm.onuMetricsManagerLock.RUnlock()
664 logger.Debugw(ctx, "optical power group metric is not enabled", log.Fields{"device-id": mm.pDeviceHandler.deviceID})
665 return nil
666 }
667 mm.onuMetricsManagerLock.RUnlock()
668
Girish Gowdrae09a6202021-01-12 18:10:59 -0800669 var metricInfoSlice []*voltha.MetricInformation
670 metricsContext := make(map[string]string)
671 metricsContext["onuID"] = fmt.Sprintf("%d", mm.pDeviceHandler.device.ProxyAddress.OnuId)
672 metricsContext["intfID"] = fmt.Sprintf("%d", mm.pDeviceHandler.device.ProxyAddress.ChannelId)
673 metricsContext["devicetype"] = mm.pDeviceHandler.DeviceType
674
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800675 raisedTs := time.Now().Unix()
Girish Gowdrae09a6202021-01-12 18:10:59 -0800676 mmd := voltha.MetricMetaData{
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800677 Title: OpticalPowerGroupMetricName,
Girish Gowdrae09a6202021-01-12 18:10:59 -0800678 Ts: float64(raisedTs),
679 Context: metricsContext,
680 DeviceId: mm.pDeviceHandler.deviceID,
681 LogicalDeviceId: mm.pDeviceHandler.logicalDeviceID,
682 SerialNo: mm.pDeviceHandler.device.SerialNumber,
683 }
684
Girish Gowdrae09a6202021-01-12 18:10:59 -0800685 // get the ANI-G instance IDs
686 anigInstKeys := mm.pDeviceHandler.pOnuOmciDevice.pOnuDB.getSortedInstKeys(ctx, me.AniGClassID)
687loop:
688 for _, anigInstID := range anigInstKeys {
689 var meAttributes me.AttributeValueMap
690 opticalMetrics := make(map[string]float32)
691 // Get the ANI-G instance optical power attributes
692 requestedAttributes := me.AttributeValueMap{"OpticalSignalLevel": 0, "TransmitOpticalLevel": 0}
Girish Gowdrae0140f02021-02-02 16:55:09 -0800693 if meInstance := mm.pDeviceHandler.pOnuOmciDevice.PDevOmciCC.sendGetMe(ctx, me.AniGClassID, anigInstID, requestedAttributes, ConstDefaultOmciTimeout, true, mm.pAdaptFsm.commChan); meInstance != nil {
Girish Gowdrae09a6202021-01-12 18:10:59 -0800694 select {
695 case meAttributes = <-mm.opticalMetricsChan:
696 logger.Debugw(ctx, "received optical metrics", log.Fields{"device-id": mm.pDeviceHandler.deviceID})
697 case <-time.After(time.Duration(ConstDefaultOmciTimeout) * time.Second):
698 logger.Errorw(ctx, "timeout waiting for omci-get response for uni status", log.Fields{"device-id": mm.pDeviceHandler.deviceID})
699 // The metrics will be empty in this case
700 break loop
701 }
702 // Populate metric only if it was enabled.
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800703 for k := range OpticalPowerGroupMetrics {
704 switch k {
705 case "ani_g_instance_id":
706 if val, ok := meAttributes["ManagedEntityId"]; ok && val != nil {
707 opticalMetrics[k] = float32(val.(uint16))
708 }
Girish Gowdrae09a6202021-01-12 18:10:59 -0800709 case "transmit_power":
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800710 if val, ok := meAttributes["TransmitOpticalLevel"]; ok && val != nil {
711 opticalMetrics[k] = float32(val.(uint16))
712 }
Girish Gowdrae09a6202021-01-12 18:10:59 -0800713 case "receive_power":
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800714 if val, ok := meAttributes["OpticalSignalLevel"]; ok && val != nil {
715 opticalMetrics[k] = float32(val.(uint16))
716 }
Girish Gowdrae09a6202021-01-12 18:10:59 -0800717 default:
718 // do nothing
719 }
720 }
721 }
722 // create slice of metrics given that there could be more than one ANI-G instance and
723 // optical metrics are collected per ANI-G instance
724 metricInfo := voltha.MetricInformation{Metadata: &mmd, Metrics: opticalMetrics}
725 metricInfoSlice = append(metricInfoSlice, &metricInfo)
726 }
727
728 return metricInfoSlice
729}
730
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800731// collectUniStatusMetrics collects UNI status group metric from various MEs (uni-g, pptp and veip).
Girish Gowdrae09a6202021-01-12 18:10:59 -0800732// nolint: gocyclo
733func (mm *onuMetricsManager) collectUniStatusMetrics(ctx context.Context) []*voltha.MetricInformation {
734 logger.Debugw(ctx, "collectUniStatusMetrics", log.Fields{"device-id": mm.pDeviceHandler.deviceID})
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800735 mm.onuMetricsManagerLock.RLock()
736 if !mm.groupMetricMap[UniStatusGroupMetricName].enabled {
737 mm.onuMetricsManagerLock.RUnlock()
738 logger.Debugw(ctx, "uni status group metric is not enabled", log.Fields{"device-id": mm.pDeviceHandler.deviceID})
739 return nil
740 }
741 mm.onuMetricsManagerLock.RUnlock()
742
Girish Gowdrae09a6202021-01-12 18:10:59 -0800743 var metricInfoSlice []*voltha.MetricInformation
744 metricsContext := make(map[string]string)
745 metricsContext["onuID"] = fmt.Sprintf("%d", mm.pDeviceHandler.device.ProxyAddress.OnuId)
746 metricsContext["intfID"] = fmt.Sprintf("%d", mm.pDeviceHandler.device.ProxyAddress.ChannelId)
747 metricsContext["devicetype"] = mm.pDeviceHandler.DeviceType
748
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800749 raisedTs := time.Now().Unix()
Girish Gowdrae09a6202021-01-12 18:10:59 -0800750 mmd := voltha.MetricMetaData{
751 Title: "UniStatus", // Is this ok to hard code?
752 Ts: float64(raisedTs),
753 Context: metricsContext,
754 DeviceId: mm.pDeviceHandler.deviceID,
755 LogicalDeviceId: mm.pDeviceHandler.logicalDeviceID,
756 SerialNo: mm.pDeviceHandler.device.SerialNumber,
757 }
758
Girish Gowdrae09a6202021-01-12 18:10:59 -0800759 // get the UNI-G instance IDs
760 unigInstKeys := mm.pDeviceHandler.pOnuOmciDevice.pOnuDB.getSortedInstKeys(ctx, me.UniGClassID)
761loop1:
762 for _, unigInstID := range unigInstKeys {
763 // TODO: Include additional information in the voltha.MetricMetaData - like portno, uni-id, instance-id
764 // to uniquely identify this ME instance and also to correlate the ME instance to physical instance
765 unigMetrics := make(map[string]float32)
766 var meAttributes me.AttributeValueMap
767 // Get the UNI-G instance optical power attributes
768 requestedAttributes := me.AttributeValueMap{"AdministrativeState": 0}
Girish Gowdrae0140f02021-02-02 16:55:09 -0800769 if meInstance := mm.pDeviceHandler.pOnuOmciDevice.PDevOmciCC.sendGetMe(ctx, me.UniGClassID, unigInstID, requestedAttributes, ConstDefaultOmciTimeout, true, mm.pAdaptFsm.commChan); meInstance != nil {
Girish Gowdrae09a6202021-01-12 18:10:59 -0800770 // Wait for metrics or timeout
771 select {
772 case meAttributes = <-mm.uniStatusMetricsChan:
773 logger.Debugw(ctx, "received uni-g metrics", log.Fields{"device-id": mm.pDeviceHandler.deviceID})
774 case <-time.After(time.Duration(ConstDefaultOmciTimeout) * time.Second):
775 logger.Errorw(ctx, "timeout waiting for omci-get response for uni status", log.Fields{"device-id": mm.pDeviceHandler.deviceID})
776 // The metrics could be empty in this case
777 break loop1
778 }
779 // Populate metric only if it was enabled.
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800780 for k := range UniStatusGroupMetrics {
781 switch k {
Girish Gowdrae09a6202021-01-12 18:10:59 -0800782 case "uni_admin_state":
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800783 if val, ok := meAttributes["AdministrativeState"]; ok && val != nil {
784 unigMetrics[k] = float32(val.(byte))
785 }
Girish Gowdrae09a6202021-01-12 18:10:59 -0800786 default:
787 // do nothing
788 }
789 }
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800790 var entityID uint32
791 if val, ok := meAttributes["ManagedEntityId"]; ok && val != nil {
792 entityID = uint32(val.(uint16))
793 }
794 // TODO: Rlock needed for reading uniEntityMap?
795 if uniPort, ok := mm.pDeviceHandler.uniEntityMap[entityID]; ok && uniPort != nil {
796 unigMetrics["uni_port_no"] = float32(uniPort.portNo)
797 }
Girish Gowdrae09a6202021-01-12 18:10:59 -0800798 // create slice of metrics given that there could be more than one UNI-G instance
799 metricInfo := voltha.MetricInformation{Metadata: &mmd, Metrics: unigMetrics}
800 metricInfoSlice = append(metricInfoSlice, &metricInfo)
801 }
802 }
803
804 // get the PPTP instance IDs
805 pptpInstKeys := mm.pDeviceHandler.pOnuOmciDevice.pOnuDB.getSortedInstKeys(ctx, me.PhysicalPathTerminationPointEthernetUniClassID)
806loop2:
807 for _, pptpInstID := range pptpInstKeys {
808 // TODO: Include additional information in the voltha.MetricMetaData - like portno, uni-id, instance-id
809 // to uniquely identify this ME instance and also to correlate the ME instance to physical instance
810 var meAttributes me.AttributeValueMap
811 pptpMetrics := make(map[string]float32)
812
813 requestedAttributes := me.AttributeValueMap{"SensedType": 0, "OperationalState": 0, "AdministrativeState": 0}
Girish Gowdrae0140f02021-02-02 16:55:09 -0800814 if meInstance := mm.pDeviceHandler.pOnuOmciDevice.PDevOmciCC.sendGetMe(ctx, me.PhysicalPathTerminationPointEthernetUniClassID, pptpInstID, requestedAttributes, ConstDefaultOmciTimeout, true, mm.pAdaptFsm.commChan); meInstance != nil {
Girish Gowdrae09a6202021-01-12 18:10:59 -0800815 // Wait for metrics or timeout
816 select {
817 case meAttributes = <-mm.uniStatusMetricsChan:
818 logger.Debugw(ctx, "received pptp metrics", log.Fields{"device-id": mm.pDeviceHandler.deviceID})
819 case <-time.After(time.Duration(ConstDefaultOmciTimeout) * time.Second):
820 logger.Errorw(ctx, "timeout waiting for omci-get response for uni status", log.Fields{"device-id": mm.pDeviceHandler.deviceID})
821 // The metrics could be empty in this case
822 break loop2
823 }
824
825 // Populate metric only if it was enabled.
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800826 for k := range UniStatusGroupMetrics {
827 switch k {
Girish Gowdrae09a6202021-01-12 18:10:59 -0800828 case "ethernet_type":
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800829 if val, ok := meAttributes["SensedType"]; ok && val != nil {
830 pptpMetrics[k] = float32(val.(byte))
831 }
Girish Gowdrae09a6202021-01-12 18:10:59 -0800832 case "oper_status":
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800833 if val, ok := meAttributes["OperationalState"]; ok && val != nil {
834 pptpMetrics[k] = float32(val.(byte))
835 }
Girish Gowdrae09a6202021-01-12 18:10:59 -0800836 case "uni_admin_state":
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800837 if val, ok := meAttributes["AdministrativeState"]; ok && val != nil {
838 pptpMetrics[k] = float32(val.(byte))
839 }
Girish Gowdrae09a6202021-01-12 18:10:59 -0800840 default:
841 // do nothing
842 }
843 }
844 }
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800845 var entityID uint32
846 if val, ok := meAttributes["ManagedEntityId"]; ok && val != nil {
847 entityID = uint32(val.(uint16))
848 }
849 // TODO: Rlock needed for reading uniEntityMap?
850 if uniPort, ok := mm.pDeviceHandler.uniEntityMap[entityID]; ok && uniPort != nil {
851 pptpMetrics["uni_port_no"] = float32(uniPort.portNo)
852 }
853
Girish Gowdrae09a6202021-01-12 18:10:59 -0800854 // create slice of metrics given that there could be more than one PPTP instance and
855 metricInfo := voltha.MetricInformation{Metadata: &mmd, Metrics: pptpMetrics}
856 metricInfoSlice = append(metricInfoSlice, &metricInfo)
857 }
858
859 // get the VEIP instance IDs
860 veipInstKeys := mm.pDeviceHandler.pOnuOmciDevice.pOnuDB.getSortedInstKeys(ctx, me.VirtualEthernetInterfacePointClassID)
861loop3:
862 for _, veipInstID := range veipInstKeys {
863 // TODO: Include additional information in the voltha.MetricMetaData - like portno, uni-id, instance-id
864 // to uniquely identify this ME instance and also to correlate the ME instance to physical instance
865 var meAttributes me.AttributeValueMap
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800866 veipMetrics := make(map[string]float32)
Girish Gowdrae09a6202021-01-12 18:10:59 -0800867
868 requestedAttributes := me.AttributeValueMap{"OperationalState": 0, "AdministrativeState": 0}
Girish Gowdrae0140f02021-02-02 16:55:09 -0800869 if meInstance := mm.pDeviceHandler.pOnuOmciDevice.PDevOmciCC.sendGetMe(ctx, me.VirtualEthernetInterfacePointClassID, veipInstID, requestedAttributes, ConstDefaultOmciTimeout, true, mm.pAdaptFsm.commChan); meInstance != nil {
Girish Gowdrae09a6202021-01-12 18:10:59 -0800870 // Wait for metrics or timeout
871 select {
872 case meAttributes = <-mm.uniStatusMetricsChan:
873 logger.Debugw(ctx, "received veip metrics", log.Fields{"device-id": mm.pDeviceHandler.deviceID})
874 case <-time.After(time.Duration(ConstDefaultOmciTimeout) * time.Second):
875 logger.Errorw(ctx, "timeout waiting for omci-get response for uni status", log.Fields{"device-id": mm.pDeviceHandler.deviceID})
876 // The metrics could be empty in this case
877 break loop3
878 }
879
880 // Populate metric only if it was enabled.
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800881 for k := range UniStatusGroupMetrics {
882 switch k {
Girish Gowdrae09a6202021-01-12 18:10:59 -0800883 case "oper_status":
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800884 if val, ok := meAttributes["OperationalState"]; ok && val != nil {
885 veipMetrics[k] = float32(val.(byte))
886 }
Girish Gowdrae09a6202021-01-12 18:10:59 -0800887 case "uni_admin_state":
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800888 if val, ok := meAttributes["AdministrativeState"]; ok && val != nil {
889 veipMetrics[k] = float32(val.(byte))
890 }
Girish Gowdrae09a6202021-01-12 18:10:59 -0800891 default:
892 // do nothing
893 }
894 }
895 }
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800896
897 var entityID uint32
898 if val, ok := meAttributes["ManagedEntityId"]; ok && val != nil {
899 entityID = uint32(meAttributes["ManagedEntityId"].(uint16))
900 }
901 // TODO: Rlock needed for reading uniEntityMap?
902 if uniPort, ok := mm.pDeviceHandler.uniEntityMap[entityID]; ok && uniPort != nil {
903 veipMetrics["uni_port_no"] = float32(uniPort.portNo)
904 }
905
Girish Gowdrae09a6202021-01-12 18:10:59 -0800906 // create slice of metrics given that there could be more than one VEIP instance
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800907 metricInfo := voltha.MetricInformation{Metadata: &mmd, Metrics: veipMetrics}
Girish Gowdrae09a6202021-01-12 18:10:59 -0800908 metricInfoSlice = append(metricInfoSlice, &metricInfo)
909 }
910
911 return metricInfoSlice
912}
913
914// publishMetrics publishes the metrics on kafka
915func (mm *onuMetricsManager) publishMetrics(ctx context.Context, metricInfo []*voltha.MetricInformation) {
916 var ke voltha.KpiEvent2
Girish Gowdra5a7c4922021-01-22 18:33:41 -0800917 ts := time.Now().Unix()
Girish Gowdrae09a6202021-01-12 18:10:59 -0800918 ke.SliceData = metricInfo
919 ke.Type = voltha.KpiEventType_slice
920 ke.Ts = float64(ts)
921
922 if err := mm.pDeviceHandler.EventProxy.SendKpiEvent(ctx, "STATS_EVENT", &ke, voltha.EventCategory_EQUIPMENT, voltha.EventSubCategory_ONU, ts); err != nil {
923 logger.Errorw(ctx, "failed-to-send-pon-stats", log.Fields{"err": err})
924 }
925}
926
927func (mm *onuMetricsManager) processOmciMessages(ctx context.Context) {
928 logger.Infow(ctx, "Start routine to process OMCI-GET messages for device-id", log.Fields{"device-id": mm.pDeviceHandler.deviceID})
929 // Flush metric collection channels to be safe.
930 // It is possible that there is stale data on this channel if the processOmciMessages routine
931 // is stopped right after issuing a OMCI-GET request and started again.
932 // The processOmciMessages routine will get stopped if startCollector routine (in device_handler.go)
933 // is stopped - as a result of ONU going down.
934 mm.flushMetricCollectionChannels(ctx)
935
936 for {
937 select {
938 case <-mm.stopProcessingOmciResponses: // stop this routine
939 logger.Infow(ctx, "Stop routine to process OMCI-GET messages for device-id", log.Fields{"device-id": mm.pDeviceHandler.deviceID})
940 return
Girish Gowdrae0140f02021-02-02 16:55:09 -0800941 case message, ok := <-mm.pAdaptFsm.commChan:
Girish Gowdrae09a6202021-01-12 18:10:59 -0800942 if !ok {
943 logger.Errorw(ctx, "Message couldn't be read from channel", log.Fields{"device-id": mm.pDeviceHandler.deviceID})
944 continue
945 }
946 logger.Debugw(ctx, "Received message on ONU metrics channel", log.Fields{"device-id": mm.pDeviceHandler.deviceID})
947
948 switch message.Type {
949 case OMCI:
950 msg, _ := message.Data.(OmciMessage)
951 mm.handleOmciMessage(ctx, msg)
952 default:
953 logger.Warn(ctx, "Unknown message type received", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "message.Type": message.Type})
954 }
955 }
956 }
957}
958
959func (mm *onuMetricsManager) handleOmciMessage(ctx context.Context, msg OmciMessage) {
960 logger.Debugw(ctx, "omci Msg", log.Fields{"device-id": mm.pDeviceHandler.deviceID,
961 "msgType": msg.OmciMsg.MessageType, "msg": msg})
962 switch msg.OmciMsg.MessageType {
963 case omci.GetResponseType:
964 //TODO: error handling
965 _ = mm.handleOmciGetResponseMessage(ctx, msg)
Girish Gowdrae0140f02021-02-02 16:55:09 -0800966 case omci.SynchronizeTimeResponseType:
967 _ = mm.handleOmciSynchronizeTimeResponseMessage(ctx, msg)
968 case omci.CreateResponseType:
969 _ = mm.handleOmciCreateResponseMessage(ctx, msg)
970 case omci.DeleteResponseType:
971 _ = mm.handleOmciDeleteResponseMessage(ctx, msg)
Girish Gowdrae09a6202021-01-12 18:10:59 -0800972 default:
973 logger.Warnw(ctx, "Unknown Message Type", log.Fields{"msgType": msg.OmciMsg.MessageType})
974
975 }
976}
977
978func (mm *onuMetricsManager) handleOmciGetResponseMessage(ctx context.Context, msg OmciMessage) error {
979 msgLayer := (*msg.OmciPacket).Layer(omci.LayerTypeGetResponse)
980 if msgLayer == nil {
981 logger.Errorw(ctx, "omci Msg layer could not be detected for GetResponse - handling stopped", log.Fields{"device-id": mm.pDeviceHandler.deviceID})
982 return fmt.Errorf("omci Msg layer could not be detected for GetResponse - handling stopped: %s", mm.pDeviceHandler.deviceID)
983 }
984 msgObj, msgOk := msgLayer.(*omci.GetResponse)
985 if !msgOk {
986 logger.Errorw(ctx, "omci Msg layer could not be assigned for GetResponse - handling stopped", log.Fields{"device-id": mm.pDeviceHandler.deviceID})
987 return fmt.Errorf("omci Msg layer could not be assigned for GetResponse - handling stopped: %s", mm.pDeviceHandler.deviceID)
988 }
989 logger.Debugw(ctx, "OMCI GetResponse Data", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "data-fields": msgObj})
990 if msgObj.Result == me.Success {
991 meAttributes := msgObj.Attributes
992 switch msgObj.EntityClass {
993 case me.AniGClassID:
994 mm.opticalMetricsChan <- meAttributes
995 return nil
996 case me.UniGClassID:
997 mm.uniStatusMetricsChan <- meAttributes
998 return nil
999 case me.PhysicalPathTerminationPointEthernetUniClassID:
1000 mm.uniStatusMetricsChan <- meAttributes
1001 return nil
1002 case me.VirtualEthernetInterfacePointClassID:
1003 mm.uniStatusMetricsChan <- meAttributes
1004 return nil
Girish Gowdrae0140f02021-02-02 16:55:09 -08001005 case me.EthernetFramePerformanceMonitoringHistoryDataUpstreamClassID,
1006 me.EthernetFramePerformanceMonitoringHistoryDataDownstreamClassID,
1007 me.EthernetPerformanceMonitoringHistoryDataClassID:
1008 mm.l2PmChan <- meAttributes
Girish Gowdrae09a6202021-01-12 18:10:59 -08001009 default:
1010 logger.Errorw(ctx, "unhandled omci get response message",
1011 log.Fields{"device-id": mm.pDeviceHandler.deviceID, "class-id": msgObj.EntityClass})
1012 }
1013 }
1014
Girish Gowdrae0140f02021-02-02 16:55:09 -08001015 return fmt.Errorf("unhandled-omci-get-response-message")
1016}
1017
1018func (mm *onuMetricsManager) handleOmciSynchronizeTimeResponseMessage(ctx context.Context, msg OmciMessage) error {
1019 msgLayer := (*msg.OmciPacket).Layer(omci.LayerTypeSynchronizeTimeResponse)
1020 if msgLayer == nil {
1021 logger.Errorw(ctx, "omci Msg layer could not be detected for synchronize time response - handling stopped", log.Fields{"device-id": mm.pDeviceHandler.deviceID})
1022 return fmt.Errorf("omci Msg layer could not be detected for synchronize time response - handling stopped: %s", mm.pDeviceHandler.deviceID)
1023 }
1024 msgObj, msgOk := msgLayer.(*omci.SynchronizeTimeResponse)
1025 if !msgOk {
1026 logger.Errorw(ctx, "omci Msg layer could not be assigned for synchronize time response - handling stopped", log.Fields{"device-id": mm.pDeviceHandler.deviceID})
1027 return fmt.Errorf("omci Msg layer could not be assigned for synchronize time response - handling stopped: %s", mm.pDeviceHandler.deviceID)
1028 }
1029 logger.Debugw(ctx, "OMCI synchronize time response Data", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "data-fields": msgObj})
1030 if msgObj.Result == me.Success {
1031 switch msgObj.EntityClass {
1032 case me.OnuGClassID:
1033 logger.Infow(ctx, "omci synchronize time success", log.Fields{"device-id": mm.pDeviceHandler.deviceID})
1034 mm.syncTimeResponseChan <- true
1035 return nil
1036 default:
1037 logger.Errorw(ctx, "unhandled omci message",
1038 log.Fields{"device-id": mm.pDeviceHandler.deviceID, "class-id": msgObj.EntityClass})
1039 }
1040 }
1041 mm.syncTimeResponseChan <- false
1042 logger.Errorf(ctx, "unhandled-omci-synchronize-time-response-message--error-code-%v", msgObj.Result)
1043 return fmt.Errorf("unhandled-omci-synchronize-time-response-message--error-code-%v", msgObj.Result)
Girish Gowdrae09a6202021-01-12 18:10:59 -08001044}
1045
1046// flushMetricCollectionChannels flushes all metric collection channels for any stale OMCI responses
1047func (mm *onuMetricsManager) flushMetricCollectionChannels(ctx context.Context) {
1048 // flush commMetricsChan
1049 select {
Girish Gowdrae0140f02021-02-02 16:55:09 -08001050 case <-mm.pAdaptFsm.commChan:
Girish Gowdrae09a6202021-01-12 18:10:59 -08001051 logger.Debug(ctx, "flushed common metrics channel")
1052 default:
1053 }
1054
1055 // flush opticalMetricsChan
1056 select {
1057 case <-mm.opticalMetricsChan:
1058 logger.Debug(ctx, "flushed optical metrics channel")
1059 default:
1060 }
1061
1062 // flush uniStatusMetricsChan
1063 select {
1064 case <-mm.uniStatusMetricsChan:
1065 logger.Debug(ctx, "flushed uni status metrics channel")
1066 default:
1067 }
Girish Gowdrae0140f02021-02-02 16:55:09 -08001068
1069 // flush syncTimeResponseChan
1070 select {
1071 case <-mm.syncTimeResponseChan:
1072 logger.Debug(ctx, "flushed sync time response channel")
1073 default:
1074 }
1075
1076 // flush l2PmChan
1077 select {
1078 case <-mm.l2PmChan:
1079 logger.Debug(ctx, "flushed L2 PM collection channel")
1080 default:
1081 }
1082
1083 // flush stopTicks
1084 select {
1085 case <-mm.stopTicks:
1086 logger.Debug(ctx, "flushed stopTicks channel")
1087 default:
1088 }
1089
1090}
1091
1092// ** L2 PM FSM Handlers start **
1093
1094func (mm *onuMetricsManager) l2PMFsmStarting(ctx context.Context, e *fsm.Event) {
1095 // Loop through all the group metrics
1096 // If it is a L2 PM Interval metric and it is enabled, then if it is not in the
1097 // list of active L2 PM list then mark it for creation
1098 // It it is a L2 PM Interval metric and it is disabled, then if it is in the
1099 // list of active L2 PM list then mark it for deletion
1100 mm.onuMetricsManagerLock.Lock()
1101 for n, g := range mm.groupMetricMap {
1102 if g.isL2PMCounter { // it is a l2 pm counter
1103 if g.enabled { // metric enabled.
1104 found := false
1105 inner1:
1106 for _, v := range mm.activeL2Pms {
1107 if v == n {
1108 found = true // metric already present in active l2 pm list
1109 break inner1
1110 }
1111 }
1112 if !found { // metric not in active l2 pm list. Mark this to be added later
1113 mm.l2PmToAdd = mm.appendIfMissing(mm.l2PmToAdd, n)
1114 }
1115 } else { // metric not enabled.
1116 found := false
1117 inner2:
1118 for _, v := range mm.activeL2Pms {
1119 if v == n {
1120 found = true // metric is found in active l2 pm list
1121 break inner2
1122 }
1123 }
1124 if found { // metric is found in active l2 pm list. Mark this to be deleted later
1125 mm.l2PmToDelete = mm.appendIfMissing(mm.l2PmToDelete, n)
1126 }
1127 }
1128 }
1129 }
1130 mm.onuMetricsManagerLock.Unlock()
1131 logger.Debugw(ctx, "pms to add and delete",
1132 log.Fields{"device-id": mm.pDeviceHandler.deviceID, "pms-to-add": mm.l2PmToAdd, "pms-to-delete": mm.l2PmToDelete})
1133 go func() {
1134 // push a tick event to move to next state
1135 if err := mm.pAdaptFsm.pFsm.Event(l2PmEventTick); err != nil {
1136 logger.Errorw(ctx, "error calling event", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "err": err})
1137 }
1138 }()
1139}
1140
1141func (mm *onuMetricsManager) l2PMFsmSyncTime(ctx context.Context, e *fsm.Event) {
1142 // Sync time with the ONU to establish 15min boundary for PM collection.
1143 if err := mm.syncTime(ctx); err != nil {
1144 go func() {
1145 time.Sleep(SyncTimeRetryInterval * time.Second) // retry to sync time after this timeout
1146 // This will result in FSM attempting to sync time again
1147 if err := mm.pAdaptFsm.pFsm.Event(l2PmEventFailure); err != nil {
1148 logger.Errorw(ctx, "error calling event", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "err": err})
1149 }
1150 }()
1151 }
1152 // Initiate a tick generation routine every L2PmCollectionInterval
1153 go mm.generateTicks(ctx)
1154
1155 go func() {
1156 if err := mm.pAdaptFsm.pFsm.Event(l2PmEventSuccess); err != nil {
1157 logger.Errorw(ctx, "error calling event", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "err": err})
1158 }
1159 }()
1160}
1161
1162func (mm *onuMetricsManager) l2PMFsmNull(ctx context.Context, e *fsm.Event) {
1163 // We need to reset the local data so that the L2 PM MEs are re-provisioned once the ONU is back up based on the latest PM CONFIG
1164 mm.onuMetricsManagerLock.Lock()
1165 mm.activeL2Pms = nil
1166 mm.l2PmToAdd = nil
1167 mm.l2PmToDelete = nil
1168 mm.onuMetricsManagerLock.Unlock()
1169}
1170func (mm *onuMetricsManager) l2PMFsmIdle(ctx context.Context, e *fsm.Event) {
1171 logger.Debugw(ctx, "Enter state idle", log.Fields{"device-id": mm.pDeviceHandler.deviceID})
1172
1173 mm.onuMetricsManagerLock.RLock()
1174 numOfPmToDelete := len(mm.l2PmToDelete)
1175 numOfPmToAdd := len(mm.l2PmToAdd)
1176 mm.onuMetricsManagerLock.RUnlock()
1177
1178 if numOfPmToDelete > 0 {
1179 logger.Debugw(ctx, "state idle - pms to delete", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "pms-to-delete": numOfPmToDelete})
1180 go func() {
1181 if err := mm.pAdaptFsm.pFsm.Event(l2PmEventDeleteMe); err != nil {
1182 logger.Errorw(ctx, "error calling event", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "err": err})
1183 }
1184 }()
1185 } else if numOfPmToAdd > 0 {
1186 logger.Debugw(ctx, "state idle - pms to add", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "pms-to-add": numOfPmToAdd})
1187 go func() {
1188 if err := mm.pAdaptFsm.pFsm.Event(l2PmEventAddMe); err != nil {
1189 logger.Errorw(ctx, "error calling event", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "err": err})
1190 }
1191 }()
1192 }
1193}
1194
1195func (mm *onuMetricsManager) l2PmFsmCollectData(ctx context.Context, e *fsm.Event) {
1196 logger.Debugw(ctx, "state collect data", log.Fields{"device-id": mm.pDeviceHandler.deviceID})
1197 // Copy the activeL2Pms for which we want to collect the metrics since activeL2Pms can change dynamically
1198 mm.onuMetricsManagerLock.RLock()
1199 copyOfActiveL2Pms := make([]string, len(mm.activeL2Pms))
1200 _ = copy(copyOfActiveL2Pms, mm.activeL2Pms)
1201 mm.onuMetricsManagerLock.RUnlock()
1202
1203 for _, n := range copyOfActiveL2Pms {
1204 switch n {
1205 case EthernetBridgeHistoryName:
1206 logger.Debugw(ctx, "state collect data - collecting data for EthernetFramePerformanceMonitoringHistoryData ME", log.Fields{"device-id": mm.pDeviceHandler.deviceID})
1207 var metricInfoSlice []*voltha.MetricInformation
1208 for _, uniPort := range mm.pDeviceHandler.uniEntityMap {
1209 // Attach the EthernetFramePerformanceMonitoringHistoryData ME to MacBridgePortConfigData on the UNI port
1210 entityID := macBridgePortAniEID + uniPort.entityID
1211 if metricInfo := mm.collectEthernetFramePerformanceMonitoringHistoryData(ctx, true, entityID); metricInfo != nil { // upstream
1212 metricInfoSlice = append(metricInfoSlice, metricInfo)
1213 }
1214 if metricInfo := mm.collectEthernetFramePerformanceMonitoringHistoryData(ctx, false, entityID); metricInfo != nil { // downstream
1215 metricInfoSlice = append(metricInfoSlice, metricInfo)
1216 }
1217 }
1218 // Publish metrics if it is valid
1219 if metricInfoSlice != nil {
1220 mm.publishMetrics(ctx, metricInfoSlice)
1221 } else {
1222 // If collectAttempts exceeds L2PmCollectAttempts then remove it from activeL2Pms
1223 // slice so that we do not collect data from that PM ME anymore
1224 mm.onuMetricsManagerLock.Lock()
1225 mm.groupMetricMap[n].collectAttempts++
1226 if mm.groupMetricMap[n].collectAttempts > L2PmCollectAttempts {
1227 mm.removeIfFound(mm.activeL2Pms, n)
1228 }
1229 logger.Warnw(ctx, "state collect data - no metrics collected",
1230 log.Fields{"device-id": mm.pDeviceHandler.deviceID, "metricName": n, "collectAttempts": mm.groupMetricMap[n].collectAttempts})
1231 mm.onuMetricsManagerLock.Unlock()
1232 }
1233 case EthernetUniHistoryName:
1234 logger.Debugw(ctx, "state collect data - collecting data for EthernetPerformanceMonitoringHistoryData ME", log.Fields{"device-id": mm.pDeviceHandler.deviceID})
1235 var metricInfoSlice []*voltha.MetricInformation
1236 for _, uniPort := range mm.pDeviceHandler.uniEntityMap {
1237 if uniPort.portType == uniPPTP { // This metric is only applicable for PPTP Uni Type
1238 // Attach the EthernetFramePerformanceMonitoringHistoryData ME to MacBridgePortConfigData on the UNI port
1239 entityID := uniPort.entityID
1240 if metricInfo := mm.collectEthernetUniHistoryData(ctx, entityID); metricInfo != nil { // upstream
1241 metricInfoSlice = append(metricInfoSlice, metricInfo)
1242 }
1243 }
1244 }
1245 // Publish metrics if it is valid
1246 if metricInfoSlice != nil {
1247 mm.publishMetrics(ctx, metricInfoSlice)
1248 } else {
1249 // If collectAttempts exceeds L2PmCollectAttempts then remove it from activeL2Pms
1250 // slice so that we do not collect data from that PM ME anymore
1251 mm.onuMetricsManagerLock.Lock()
1252 mm.groupMetricMap[n].collectAttempts++
1253 if mm.groupMetricMap[n].collectAttempts > L2PmCollectAttempts {
1254 mm.removeIfFound(mm.activeL2Pms, n)
1255 }
1256 logger.Warnw(ctx, "state collect data - no metrics collected",
1257 log.Fields{"device-id": mm.pDeviceHandler.deviceID, "metricName": n, "collectAttempts": mm.groupMetricMap[n].collectAttempts})
1258 mm.onuMetricsManagerLock.Unlock()
1259 }
1260 default:
1261 logger.Errorw(ctx, "unsupported l2 pm", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "name": n})
1262 }
1263 }
1264 // Does not matter we send success or failure here.
1265 // Those PMs that we failed to collect data will be attempted to collect again in the next PM collection cycle (assuming
1266 // we have not exceed max attempts to collect the PM data)
1267 go func() {
1268 if err := mm.pAdaptFsm.pFsm.Event(l2PmEventSuccess); err != nil {
1269 logger.Errorw(ctx, "error calling event", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "err": err})
1270 }
1271 }()
1272}
1273
1274func (mm *onuMetricsManager) l2PmFsmCreatePM(ctx context.Context, e *fsm.Event) {
1275 // Copy the l2PmToAdd for which we want to collect the metrics since l2PmToAdd can change dynamically
1276 mm.onuMetricsManagerLock.RLock()
1277 copyOfL2PmToAdd := make([]string, len(mm.l2PmToAdd))
1278 _ = copy(copyOfL2PmToAdd, mm.l2PmToAdd)
1279 mm.onuMetricsManagerLock.RUnlock()
1280
1281 logger.Debugw(ctx, "state create pm", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "pms-to-add": copyOfL2PmToAdd})
1282 for _, n := range copyOfL2PmToAdd {
1283 resp := false
1284 switch n {
1285 case EthernetBridgeHistoryName:
1286 boolForDirection := make([]bool, 2) // stores true and false to indicate upstream and downstream directions.
1287 boolForDirection = append(boolForDirection, true, false)
1288 // Create ME twice, one for each direction. Boolean true is used to indicate upstream and false for downstream.
1289 for _, direction := range boolForDirection {
1290 inner1:
1291 for _, uniPort := range mm.pDeviceHandler.uniEntityMap {
1292 // Attach the EthernetFramePerformanceMonitoringHistoryData ME to MacBridgePortConfigData on the UNI port
1293 entityID := macBridgePortAniEID + uniPort.entityID
1294 mm.pDeviceHandler.pOnuOmciDevice.PDevOmciCC.sendCreateOrDeleteEthernetPerformanceMonitoringHistoryME(
1295 ctx, ConstDefaultOmciTimeout, true, direction, true, mm.pAdaptFsm.commChan, entityID)
1296 select {
1297 case resp = <-mm.l2PmCreateOrDeleteResponseChan:
1298 logger.Debugw(ctx, "received create EthernetFramePerformanceMonitoringHistoryData l2 pm me response",
1299 log.Fields{"device-id": mm.pDeviceHandler.deviceID, "resp": resp, "uni": uniPort.uniID})
1300 if !resp {
1301 // We will attempt to create the MEs again in the next L2 PM Collection cycle
1302 break inner1
1303 }
1304 case <-time.After(time.Duration(ConstDefaultOmciTimeout) * time.Second):
1305 logger.Errorw(ctx, "timeout waiting for create EthernetFramePerformanceMonitoringHistoryData l2 pm me response",
1306 log.Fields{"device-id": mm.pDeviceHandler.deviceID, "uni": uniPort.uniID})
1307 }
1308 }
1309 }
1310 case EthernetUniHistoryName:
1311
1312 inner2:
1313 for _, uniPort := range mm.pDeviceHandler.uniEntityMap {
1314 if uniPort.portType == uniPPTP { // This metric is only applicable for PPTP Uni Type
1315 // Attach the EthernetPerformanceMonitoringHistoryData ME to MacBridgePortConfigData on the UNI port
1316 entityID := uniPort.entityID
1317 mm.pDeviceHandler.pOnuOmciDevice.PDevOmciCC.sendCreateOrDeleteEthernetUniHistoryME(
1318 ctx, ConstDefaultOmciTimeout, true, true, mm.pAdaptFsm.commChan, entityID)
1319 select {
1320 case resp = <-mm.l2PmCreateOrDeleteResponseChan:
1321 logger.Debugw(ctx, "received create EthernetPerformanceMonitoringHistoryData l2 pm me response",
1322 log.Fields{"device-id": mm.pDeviceHandler.deviceID, "resp": resp, "uni": uniPort.uniID})
1323 if !resp {
1324 // We will attempt to create the MEs again in the next L2 PM Collection cycle
1325 break inner2
1326 }
1327 case <-time.After(time.Duration(ConstDefaultOmciTimeout) * time.Second):
1328 logger.Errorw(ctx, "timeout waiting for create EthernetPerformanceMonitoringHistoryData l2 pm me response",
1329 log.Fields{"device-id": mm.pDeviceHandler.deviceID, "uni": uniPort.uniID})
1330 }
1331 }
1332 }
1333 default:
1334 logger.Errorw(ctx, "unsupported l2 pm", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "name": n})
1335 }
1336 // On success Update the local list maintained for active PMs and PMs to add
1337 if resp {
1338 mm.onuMetricsManagerLock.Lock()
1339 mm.activeL2Pms = mm.appendIfMissing(mm.activeL2Pms, n)
1340 mm.l2PmToAdd = mm.removeIfFound(mm.l2PmToAdd, n)
1341 mm.onuMetricsManagerLock.Unlock()
1342 } else {
1343 // If createRetryAttempts exceeds L2PmCreateAttempts then locally disable the PM
1344 // and also remove it from l2PmToAdd slice so that we do not try to create the PM ME anymore
1345 mm.onuMetricsManagerLock.Lock()
1346 mm.groupMetricMap[n].createRetryAttempts++
1347 if mm.groupMetricMap[n].createRetryAttempts > L2PmCreateAttempts {
1348 mm.groupMetricMap[n].enabled = false
1349 mm.removeIfFound(mm.l2PmToAdd, n)
1350 }
1351 logger.Warnw(ctx, "state create pm - failed to create pm",
1352 log.Fields{"device-id": mm.pDeviceHandler.deviceID, "metricName": n, "createRetryAttempts": mm.groupMetricMap[n].createRetryAttempts})
1353 mm.onuMetricsManagerLock.Unlock()
1354 }
1355 }
1356 // Does not matter we send success or failure here.
1357 // Those PMs that we failed to create will be attempted to create again in the next PM creation cycle (assuming
1358 // we have not exceed max attempts to create the PM ME)
1359 go func() {
1360 if err := mm.pAdaptFsm.pFsm.Event(l2PmEventSuccess); err != nil {
1361 logger.Errorw(ctx, "error calling event", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "err": err})
1362 }
1363 }()
1364}
1365
1366func (mm *onuMetricsManager) l2PmFsmDeletePM(ctx context.Context, e *fsm.Event) {
1367 // Copy the l2PmToDelete for which we want to collect the metrics since l2PmToDelete can change dynamically
1368 mm.onuMetricsManagerLock.RLock()
1369 copyOfL2PmToDelete := make([]string, len(mm.l2PmToDelete))
1370 _ = copy(copyOfL2PmToDelete, mm.l2PmToDelete)
1371 mm.onuMetricsManagerLock.RUnlock()
1372
1373 logger.Debugw(ctx, "state delete pm", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "pms-to-delete": mm.l2PmToDelete})
1374 for _, n := range copyOfL2PmToDelete {
1375 resp := false
1376 switch n {
1377 case EthernetBridgeHistoryName:
1378 boolForDirection := make([]bool, 2) // stores true and false to indicate upstream and downstream directions.
1379 boolForDirection = append(boolForDirection, true, false)
1380 // Create ME twice, one for each direction. Boolean true is used to indicate upstream and false for downstream.
1381 for _, direction := range boolForDirection {
1382 inner1:
1383 for _, uniPort := range mm.pDeviceHandler.uniEntityMap {
1384 // Attach the EthernetFramePerformanceMonitoringHistoryData ME to MacBridgePortConfigData on the UNI port
1385 entityID := macBridgePortAniEID + uniPort.entityID
1386 mm.pDeviceHandler.pOnuOmciDevice.PDevOmciCC.sendCreateOrDeleteEthernetPerformanceMonitoringHistoryME(
1387 ctx, ConstDefaultOmciTimeout, true, direction, false, mm.pAdaptFsm.commChan, entityID)
1388 select {
1389 case resp = <-mm.l2PmCreateOrDeleteResponseChan:
1390 logger.Debugw(ctx, "received delete EthernetFramePerformanceMonitoringHistoryData l2 pm me response",
1391 log.Fields{"device-id": mm.pDeviceHandler.deviceID, "resp": resp, "uni": uniPort.uniID})
1392 if !resp {
1393 // We will attempt to delete the MEs again in the next L2 PM Collection cycle
1394 break inner1
1395 }
1396 case <-time.After(time.Duration(ConstDefaultOmciTimeout) * time.Second):
1397 logger.Errorw(ctx, "timeout waiting for delete EthernetFramePerformanceMonitoringHistoryData l2 pm me response",
1398 log.Fields{"device-id": mm.pDeviceHandler.deviceID, "uni": uniPort.uniID})
1399 }
1400 }
1401 }
1402 case EthernetUniHistoryName:
1403
1404 inner2:
1405 for _, uniPort := range mm.pDeviceHandler.uniEntityMap {
1406 if uniPort.portType == uniPPTP { // This metric is only applicable for PPTP Uni Type
1407 // Attach the EthernetPerformanceMonitoringHistoryData ME to MacBridgePortConfigData on the UNI port
1408 entityID := uniPort.entityID
1409 mm.pDeviceHandler.pOnuOmciDevice.PDevOmciCC.sendCreateOrDeleteEthernetUniHistoryME(
1410 ctx, ConstDefaultOmciTimeout, true, false, mm.pAdaptFsm.commChan, entityID)
1411 select {
1412 case resp = <-mm.l2PmCreateOrDeleteResponseChan:
1413 logger.Debugw(ctx, "received delete EthernetPerformanceMonitoringHistoryData l2 pm me response",
1414 log.Fields{"device-id": mm.pDeviceHandler.deviceID, "resp": resp, "uni": uniPort.uniID})
1415 if !resp {
1416 // We will attempt to delete the MEs again in the next L2 PM Collection cycle
1417 break inner2
1418 }
1419 case <-time.After(time.Duration(ConstDefaultOmciTimeout) * time.Second):
1420 logger.Errorw(ctx, "timeout waiting for delete EthernetPerformanceMonitoringHistoryData l2 pm me response",
1421 log.Fields{"device-id": mm.pDeviceHandler.deviceID, "uni": uniPort.uniID})
1422 }
1423 }
1424 }
1425 default:
1426 logger.Errorw(ctx, "unsupported l2 pm", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "name": n})
1427 }
1428 // On success Update the local list maintained for active PMs and PMs to delete
1429 if resp {
1430 mm.onuMetricsManagerLock.Lock()
1431 mm.activeL2Pms = mm.removeIfFound(mm.activeL2Pms, n)
1432 mm.l2PmToDelete = mm.removeIfFound(mm.l2PmToDelete, n)
1433 mm.onuMetricsManagerLock.Unlock()
1434 }
1435 }
1436 // Does not matter we send success or failure here.
1437 // Those PMs that we failed to delete will be attempted to create again in the next PM collection cycle
1438 go func() {
1439 if err := mm.pAdaptFsm.pFsm.Event(l2PmEventSuccess); err != nil {
1440 logger.Errorw(ctx, "error calling event", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "err": err})
1441 }
1442 }()
1443}
1444
1445// ** L2 PM FSM Handlers end **
1446
1447// syncTime synchronizes time with the ONU to establish a 15 min boundary for PM collection and reporting.
1448func (mm *onuMetricsManager) syncTime(ctx context.Context) error {
1449 if err := mm.pDeviceHandler.pOnuOmciDevice.PDevOmciCC.sendSyncTime(ctx, ConstDefaultOmciTimeout, true, mm.pAdaptFsm.commChan); err != nil {
1450 logger.Errorw(ctx, "cannot send sync time request", log.Fields{"device-id": mm.pDeviceHandler.deviceID})
1451 return err
1452 }
1453
1454 select {
1455 case <-time.After(time.Duration(ConstDefaultOmciTimeout) * time.Second):
1456 logger.Errorf(ctx, "timed out waiting for sync time response from onu", log.Fields{"device-id": mm.pDeviceHandler.deviceID})
1457 return fmt.Errorf("timed-out-waiting-for-sync-time-response-%v", mm.pDeviceHandler.deviceID)
1458 case syncTimeRes := <-mm.syncTimeResponseChan:
1459 if !syncTimeRes {
1460 return fmt.Errorf("failed-to-sync-time-%v", mm.pDeviceHandler.deviceID)
1461 }
1462 logger.Infow(ctx, "sync time success", log.Fields{"device-id": mm.pDeviceHandler.deviceID})
1463 return nil
1464 }
1465}
1466
1467func (mm *onuMetricsManager) collectEthernetFramePerformanceMonitoringHistoryData(ctx context.Context, upstream bool, entityID uint16) *voltha.MetricInformation {
1468 var mEnt *me.ManagedEntity
1469 var omciErr me.OmciErrors
1470 var classID me.ClassID
1471 var meAttributes me.AttributeValueMap
1472 logger.Debugw(ctx, "collecting data for EthernetFramePerformanceMonitoringHistoryData", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "entityID": entityID, "upstream": upstream})
1473 meParam := me.ParamData{EntityID: entityID}
1474 if upstream {
1475 if mEnt, omciErr = me.NewEthernetFramePerformanceMonitoringHistoryDataUpstream(meParam); omciErr == nil || mEnt == nil || omciErr.GetError() != nil {
1476 logger.Errorw(ctx, "error creating me", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "entityID": entityID, "upstream": upstream})
1477 return nil
1478 }
1479 classID = me.EthernetFramePerformanceMonitoringHistoryDataUpstreamClassID
1480 } else {
1481 if mEnt, omciErr = me.NewEthernetFramePerformanceMonitoringHistoryDataDownstream(meParam); omciErr == nil || mEnt == nil || omciErr.GetError() != nil {
1482 logger.Errorw(ctx, "error creating me", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "entityID": entityID, "upstream": upstream})
1483 return nil
1484 }
1485 classID = me.EthernetFramePerformanceMonitoringHistoryDataDownstreamClassID
1486 }
1487
1488 requestedAttributes := make(me.AttributeValueMap)
1489 size := 0
1490 intervalEndTime := -1
1491 ethPMHistData := make(map[string]float32)
1492
1493 for _, v := range mEnt.GetAttributeDefinitions() {
1494 if (v.Size + size) <= MaxL2PMGetPayLoadSize {
1495 requestedAttributes[v.Name] = v.DefValue
1496 size = v.Size + size
1497 } else { // We exceeded the allow omci get size
1498 // Let's collect the attributes via get now and collect remaining in the next iteration
1499 if err := mm.populateEthernetBridgeHistoryMetrics(ctx, upstream, classID, entityID, meAttributes, requestedAttributes, ethPMHistData, &intervalEndTime); err != nil {
1500 logger.Errorw(ctx, "error during metric collection",
1501 log.Fields{"device-id": mm.pDeviceHandler.deviceID, "entityID": entityID, "upstream": upstream, "err": err})
1502 return nil
1503 }
1504 size = 0 // reset size
1505 requestedAttributes = make(me.AttributeValueMap) // reset map
1506 }
1507 }
1508 // Collect the omci get attributes for the last bunch of attributes.
1509 if len(requestedAttributes) > 0 {
1510 if err := mm.populateEthernetBridgeHistoryMetrics(ctx, upstream, classID, entityID, meAttributes, requestedAttributes, ethPMHistData, &intervalEndTime); err != nil {
1511 logger.Errorw(ctx, "error during metric collection",
1512 log.Fields{"device-id": mm.pDeviceHandler.deviceID, "entityID": entityID, "upstream": upstream, "err": err})
1513 return nil
1514 }
1515 }
1516
1517 // Populate some relevant context for the EthernetFramePerformanceMonitoringHistoryData PM
1518 ethPMHistData["class_id"] = float32(classID)
1519 ethPMHistData["interval_end_time"] = float32(intervalEndTime)
1520 ethPMHistData["parent_class_id"] = float32(me.MacBridgeConfigurationDataClassID) // EthernetFramePerformanceMonitoringHistoryData is attached to MBPCD ME
1521 ethPMHistData["parent_entity_id"] = float32(entityID)
1522 if upstream {
1523 ethPMHistData["upstream"] = float32(1)
1524 } else {
1525 ethPMHistData["upstream"] = float32(0)
1526 }
1527
1528 metricsContext := make(map[string]string)
1529 metricsContext["onuID"] = fmt.Sprintf("%d", mm.pDeviceHandler.device.ProxyAddress.OnuId)
1530 metricsContext["intfID"] = fmt.Sprintf("%d", mm.pDeviceHandler.device.ProxyAddress.ChannelId)
1531 metricsContext["devicetype"] = mm.pDeviceHandler.DeviceType
1532
1533 raisedTs := time.Now().Unix()
1534 mmd := voltha.MetricMetaData{
1535 Title: EthernetBridgeHistoryName,
1536 Ts: float64(raisedTs),
1537 Context: metricsContext,
1538 DeviceId: mm.pDeviceHandler.deviceID,
1539 LogicalDeviceId: mm.pDeviceHandler.logicalDeviceID,
1540 SerialNo: mm.pDeviceHandler.device.SerialNumber,
1541 }
1542
1543 // create slice of metrics given that there could be more than one VEIP instance
1544 metricInfo := voltha.MetricInformation{Metadata: &mmd, Metrics: ethPMHistData}
1545 logger.Debugw(ctx, "collecting data for EthernetFramePerformanceMonitoringHistoryData successful",
1546 log.Fields{"device-id": mm.pDeviceHandler.deviceID, "entityID": entityID, "upstream": upstream, "metricInfo": metricInfo})
1547 return &metricInfo
1548}
1549
1550func (mm *onuMetricsManager) collectEthernetUniHistoryData(ctx context.Context, entityID uint16) *voltha.MetricInformation {
1551 var mEnt *me.ManagedEntity
1552 var omciErr me.OmciErrors
1553 var classID me.ClassID
1554 var meAttributes me.AttributeValueMap
1555 logger.Debugw(ctx, "collecting data for EthernetFramePerformanceMonitoringHistoryData", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "entityID": entityID})
1556 meParam := me.ParamData{EntityID: entityID}
1557 if mEnt, omciErr = me.NewEthernetPerformanceMonitoringHistoryData(meParam); omciErr == nil || mEnt == nil || omciErr.GetError() != nil {
1558 logger.Errorw(ctx, "error creating me", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "entityID": entityID})
1559 return nil
1560 }
1561 classID = me.EthernetPerformanceMonitoringHistoryDataClassID
1562
1563 requestedAttributes := make(me.AttributeValueMap)
1564 size := 0
1565 intervalEndTime := -1
1566 ethUniHistData := make(map[string]float32)
1567
1568 for _, v := range mEnt.GetAttributeDefinitions() {
1569 if (v.Size + size) <= MaxL2PMGetPayLoadSize {
1570 requestedAttributes[v.Name] = v.DefValue
1571 size = v.Size + size
1572 } else { // We exceeded the allow omci get size
1573 // Let's collect the attributes via get now and collect remaining in the next iteration
1574 if err := mm.populateEthernetUniHistoryMetrics(ctx, classID, entityID, meAttributes, requestedAttributes, ethUniHistData, &intervalEndTime); err != nil {
1575 logger.Errorw(ctx, "error during metric collection",
1576 log.Fields{"device-id": mm.pDeviceHandler.deviceID, "entityID": entityID, "err": err})
1577 return nil
1578 }
1579 size = 0 // reset size
1580 requestedAttributes = make(me.AttributeValueMap) // reset map
1581 }
1582 }
1583 // Collect the omci get attributes for the last bunch of attributes.
1584 if len(requestedAttributes) > 0 {
1585 if err := mm.populateEthernetUniHistoryMetrics(ctx, classID, entityID, meAttributes, requestedAttributes, ethUniHistData, &intervalEndTime); err != nil {
1586 logger.Errorw(ctx, "error during metric collection",
1587 log.Fields{"device-id": mm.pDeviceHandler.deviceID, "entityID": entityID, "err": err})
1588 return nil
1589 }
1590 }
1591
1592 // Populate some relevant context for the EthernetPerformanceMonitoringHistoryData PM
1593 ethUniHistData["class_id"] = float32(classID)
1594 ethUniHistData["interval_end_time"] = float32(intervalEndTime)
1595
1596 metricsContext := make(map[string]string)
1597 metricsContext["onuID"] = fmt.Sprintf("%d", mm.pDeviceHandler.device.ProxyAddress.OnuId)
1598 metricsContext["intfID"] = fmt.Sprintf("%d", mm.pDeviceHandler.device.ProxyAddress.ChannelId)
1599 metricsContext["devicetype"] = mm.pDeviceHandler.DeviceType
1600
1601 raisedTs := time.Now().Unix()
1602 mmd := voltha.MetricMetaData{
1603 Title: EthernetUniHistoryName,
1604 Ts: float64(raisedTs),
1605 Context: metricsContext,
1606 DeviceId: mm.pDeviceHandler.deviceID,
1607 LogicalDeviceId: mm.pDeviceHandler.logicalDeviceID,
1608 SerialNo: mm.pDeviceHandler.device.SerialNumber,
1609 }
1610
1611 // create slice of metrics given that there could be more than one PPTP instance
1612 metricInfo := voltha.MetricInformation{Metadata: &mmd, Metrics: ethUniHistData}
1613 logger.Debugw(ctx, "collecting data for EthernetPerformanceMonitoringHistoryData successful",
1614 log.Fields{"device-id": mm.pDeviceHandler.deviceID, "entityID": entityID, "metricInfo": metricInfo})
1615 return &metricInfo
1616}
1617
1618// nolint: gocyclo
1619func (mm *onuMetricsManager) populateEthernetBridgeHistoryMetrics(ctx context.Context, upstream bool, classID me.ClassID, entityID uint16,
1620 meAttributes me.AttributeValueMap, requestedAttributes me.AttributeValueMap, ethPMHistData map[string]float32, intervalEndTime *int) error {
1621 // Make sure "IntervalEndTime" is part of the requested attributes as we need this to compare the get responses when get request is multipart
1622 if _, ok := requestedAttributes["IntervalEndTime"]; !ok {
1623 requestedAttributes["IntervalEndTime"] = 0
1624 }
1625 if meInstance := mm.pDeviceHandler.pOnuOmciDevice.PDevOmciCC.sendGetMe(ctx, classID, entityID, requestedAttributes, ConstDefaultOmciTimeout, true, mm.pAdaptFsm.commChan); meInstance != nil {
1626 select {
1627 case meAttributes = <-mm.l2PmChan:
1628 logger.Debugw(ctx, "received ethernet pm history data metrics",
1629 log.Fields{"device-id": mm.pDeviceHandler.deviceID, "upstream": upstream, "entityID": entityID})
1630 case <-time.After(time.Duration(ConstDefaultOmciTimeout) * time.Second):
1631 logger.Errorw(ctx, "timeout waiting for omci-get response for ethernet pm history data",
1632 log.Fields{"device-id": mm.pDeviceHandler.deviceID, "upstream": upstream, "entityID": entityID})
1633 // The metrics will be empty in this case
1634 return fmt.Errorf("timeout-during-l2-pm-collection-for-ethernet-bridge-history-%v", mm.pDeviceHandler.deviceID)
1635 }
1636 // verify that interval end time has not changed during metric collection. If it changed, we abort the procedure
1637 if *intervalEndTime == -1 { // first time
1638 // Update the interval end time
1639 if val, ok := meAttributes["IntervalEndTime"]; ok && val != nil {
1640 *intervalEndTime = int(meAttributes["IntervalEndTime"].(uint8))
1641 }
1642 } else {
1643 var currIntervalEndTime int
1644 if val, ok := meAttributes["IntervalEndTime"]; ok && val != nil {
1645 currIntervalEndTime = int(meAttributes["IntervalEndTime"].(uint8))
1646 }
1647 if currIntervalEndTime != *intervalEndTime { // interval end time changed during metric collection
1648 logger.Errorw(ctx, "interval end time changed during metrics collection for ethernet pm history data",
1649 log.Fields{"device-id": mm.pDeviceHandler.deviceID, "entityID": entityID,
1650 "currIntervalEndTime": *intervalEndTime, "newIntervalEndTime": currIntervalEndTime})
1651 return fmt.Errorf("interval-end-time-changed-during-metric-collection-for-ethernet-bridge-history-%v", mm.pDeviceHandler.deviceID)
1652 }
1653 }
1654 }
1655 for k := range EthernetBridgeHistory {
1656 // populate ethPMHistData only if metric key not already present (or populated), since it is possible that we populate
1657 // the attributes in multiple iterations for a given L2 PM ME as there is a limit on the max OMCI GET payload size.
1658 if _, ok := ethPMHistData[k]; !ok {
1659 switch k {
1660 case "drop_events":
1661 if val, ok := meAttributes["DropEvents"]; ok && val != nil {
1662 ethPMHistData[k] = float32(val.(uint32))
1663 }
1664 case "octets":
1665 if val, ok := meAttributes["Octets"]; ok && val != nil {
1666 ethPMHistData[k] = float32(val.(uint32))
1667 }
1668 case "packets":
1669 if val, ok := meAttributes["Packets"]; ok && val != nil {
1670 ethPMHistData[k] = float32(val.(uint32))
1671 }
1672 case "broadcast_packets":
1673 if val, ok := meAttributes["BroadcastPackets"]; ok && val != nil {
1674 ethPMHistData[k] = float32(val.(uint32))
1675 }
1676 case "multicast_packets":
1677 if val, ok := meAttributes["MulticastPackets"]; ok && val != nil {
1678 ethPMHistData[k] = float32(val.(uint32))
1679 }
1680 case "crc_errored_packets":
1681 if val, ok := meAttributes["CrcErroredPackets"]; ok && val != nil {
1682 ethPMHistData[k] = float32(val.(uint32))
1683 }
1684 case "undersize_packets":
1685 if val, ok := meAttributes["UndersizePackets"]; ok && val != nil {
1686 ethPMHistData[k] = float32(val.(uint32))
1687 }
1688 case "oversize_packets":
1689 if val, ok := meAttributes["OversizePackets"]; ok && val != nil {
1690 ethPMHistData[k] = float32(val.(uint32))
1691 }
1692 case "64_octets":
1693 if val, ok := meAttributes["Packets64Octets"]; ok && val != nil {
1694 ethPMHistData[k] = float32(val.(uint32))
1695 }
1696 case "65_to_127_octets":
1697 if val, ok := meAttributes["Packets65To127Octets"]; ok && val != nil {
1698 ethPMHistData[k] = float32(val.(uint32))
1699 }
1700 case "128_to_255_octets":
1701 if val, ok := meAttributes["Packets128To255Octets"]; ok && val != nil {
1702 ethPMHistData[k] = float32(val.(uint32))
1703 }
1704 case "256_to_511_octets":
1705 if val, ok := meAttributes["Packets256To511Octets"]; ok && val != nil {
1706 ethPMHistData[k] = float32(val.(uint32))
1707 }
1708 case "512_to_1023_octets":
1709 if val, ok := meAttributes["Packets512To1023Octets"]; ok && val != nil {
1710 ethPMHistData[k] = float32(val.(uint32))
1711 }
1712 case "1024_to_1518_octets":
1713 if val, ok := meAttributes["Packets1024To1518Octets"]; ok && val != nil {
1714 ethPMHistData[k] = float32(val.(uint32))
1715 }
1716 default:
1717 // do nothing
1718 }
1719 }
1720 }
1721 return nil
1722}
1723
1724// nolint: gocyclo
1725func (mm *onuMetricsManager) populateEthernetUniHistoryMetrics(ctx context.Context, classID me.ClassID, entityID uint16,
1726 meAttributes me.AttributeValueMap, requestedAttributes me.AttributeValueMap, ethPMUniHistData map[string]float32, intervalEndTime *int) error {
1727 // Make sure "IntervalEndTime" is part of the requested attributes as we need this to compare the get responses when get request is multipart
1728 if _, ok := requestedAttributes["IntervalEndTime"]; !ok {
1729 requestedAttributes["IntervalEndTime"] = 0
1730 }
1731 if meInstance := mm.pDeviceHandler.pOnuOmciDevice.PDevOmciCC.sendGetMe(ctx, classID, entityID, requestedAttributes, ConstDefaultOmciTimeout, true, mm.pAdaptFsm.commChan); meInstance != nil {
1732 select {
1733 case meAttributes = <-mm.l2PmChan:
1734 logger.Debugw(ctx, "received ethernet uni history data metrics",
1735 log.Fields{"device-id": mm.pDeviceHandler.deviceID, "entityID": entityID})
1736 case <-time.After(time.Duration(ConstDefaultOmciTimeout) * time.Second):
1737 logger.Errorw(ctx, "timeout waiting for omci-get response for ethernet uni history data",
1738 log.Fields{"device-id": mm.pDeviceHandler.deviceID, "entityID": entityID})
1739 // The metrics will be empty in this case
1740 return fmt.Errorf("timeout-during-l2-pm-collection-for-ethernet-uni-history-%v", mm.pDeviceHandler.deviceID)
1741 }
1742 // verify that interval end time has not changed during metric collection. If it changed, we abort the procedure
1743 if *intervalEndTime == -1 { // first time
1744 // Update the interval end time
1745 if val, ok := meAttributes["IntervalEndTime"]; ok && val != nil {
1746 *intervalEndTime = int(meAttributes["IntervalEndTime"].(uint8))
1747 }
1748 } else {
1749 var currIntervalEndTime int
1750 if val, ok := meAttributes["IntervalEndTime"]; ok && val != nil {
1751 currIntervalEndTime = int(meAttributes["IntervalEndTime"].(uint8))
1752 }
1753 if currIntervalEndTime != *intervalEndTime { // interval end time changed during metric collection
1754 logger.Errorw(ctx, "interval end time changed during metrics collection for ethernet uni history data",
1755 log.Fields{"device-id": mm.pDeviceHandler.deviceID, "entityID": entityID,
1756 "currIntervalEndTime": *intervalEndTime, "newIntervalEndTime": currIntervalEndTime})
1757 return fmt.Errorf("interval-end-time-changed-during-metric-collection-for-ethernet-uni-history-%v", mm.pDeviceHandler.deviceID)
1758 }
1759 }
1760 }
1761 for k := range EthernetUniHistory {
1762 // populate ethPMUniHistData only if metric key not already present (or populated), since it is possible that we populate
1763 // the attributes in multiple iterations for a given L2 PM ME as there is a limit on the max OMCI GET payload size.
1764 if _, ok := ethPMUniHistData[k]; !ok {
1765 switch k {
1766 case "fcs_errors":
1767 if val, ok := meAttributes["FcsErrors"]; ok && val != nil {
1768 ethPMUniHistData[k] = float32(val.(uint32))
1769 }
1770 case "excessive_collision_counter":
1771 if val, ok := meAttributes["ExcessiveCollisionCounter"]; ok && val != nil {
1772 ethPMUniHistData[k] = float32(val.(uint32))
1773 }
1774 case "late_collision_counter":
1775 if val, ok := meAttributes["LateCollisionCounter"]; ok && val != nil {
1776 ethPMUniHistData[k] = float32(val.(uint32))
1777 }
1778 case "frames_too_long":
1779 if val, ok := meAttributes["FramesTooLong"]; ok && val != nil {
1780 ethPMUniHistData[k] = float32(val.(uint32))
1781 }
1782 case "buffer_overflows_on_rx":
1783 if val, ok := meAttributes["BufferOverflowsOnReceive"]; ok && val != nil {
1784 ethPMUniHistData[k] = float32(val.(uint32))
1785 }
1786 case "buffer_overflows_on_tx":
1787 if val, ok := meAttributes["BufferOverflowsOnTransmit"]; ok && val != nil {
1788 ethPMUniHistData[k] = float32(val.(uint32))
1789 }
1790 case "single_collision_frame_counter":
1791 if val, ok := meAttributes["SingleCollisionFrameCounter"]; ok && val != nil {
1792 ethPMUniHistData[k] = float32(val.(uint32))
1793 }
1794 case "multiple_collisions_frame_counter":
1795 if val, ok := meAttributes["MultipleCollisionsFrameCounter"]; ok && val != nil {
1796 ethPMUniHistData[k] = float32(val.(uint32))
1797 }
1798 case "sqe_counter":
1799 if val, ok := meAttributes["SqeCounter"]; ok && val != nil {
1800 ethPMUniHistData[k] = float32(val.(uint32))
1801 }
1802 case "deferred_tx_counter":
1803 if val, ok := meAttributes["DeferredTransmissionCounter"]; ok && val != nil {
1804 ethPMUniHistData[k] = float32(val.(uint32))
1805 }
1806 case "internal_mac_tx_error_counter":
1807 if val, ok := meAttributes["InternalMacTransmitErrorCounter"]; ok && val != nil {
1808 ethPMUniHistData[k] = float32(val.(uint32))
1809 }
1810 case "carrier_sense_error_counter":
1811 if val, ok := meAttributes["CarrierSenseErrorCounter"]; ok && val != nil {
1812 ethPMUniHistData[k] = float32(val.(uint32))
1813 }
1814 case "alignment_error_counter":
1815 if val, ok := meAttributes["AlignmentErrorCounter"]; ok && val != nil {
1816 ethPMUniHistData[k] = float32(val.(uint32))
1817 }
1818 case "internal_mac_rx_error_counter":
1819 if val, ok := meAttributes["InternalMacReceiveErrorCounter"]; ok && val != nil {
1820 ethPMUniHistData[k] = float32(val.(uint32))
1821 }
1822 default:
1823 // do nothing
1824 }
1825 }
1826 }
1827 return nil
1828}
1829
1830func (mm *onuMetricsManager) handleOmciCreateResponseMessage(ctx context.Context, msg OmciMessage) error {
1831 msgLayer := (*msg.OmciPacket).Layer(omci.LayerTypeCreateResponse)
1832 if msgLayer == nil {
1833 logger.Errorw(ctx, "omci Msg layer could not be detected for create response - handling stopped", log.Fields{"device-id": mm.pDeviceHandler.deviceID})
1834 return fmt.Errorf("omci Msg layer could not be detected for create response - handling stopped: %s", mm.pDeviceHandler.deviceID)
1835 }
1836 msgObj, msgOk := msgLayer.(*omci.CreateResponse)
1837 if !msgOk {
1838 logger.Errorw(ctx, "omci Msg layer could not be assigned for create response - handling stopped", log.Fields{"device-id": mm.pDeviceHandler.deviceID})
1839 return fmt.Errorf("omci Msg layer could not be assigned for delete response - handling stopped: %s", mm.pDeviceHandler.deviceID)
1840 }
1841 logger.Debugw(ctx, "OMCI create response Data", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "data-fields": msgObj})
1842 switch msgObj.EntityClass {
1843 case me.EthernetFramePerformanceMonitoringHistoryDataUpstreamClassID,
1844 me.EthernetFramePerformanceMonitoringHistoryDataDownstreamClassID,
1845 me.EthernetPerformanceMonitoringHistoryDataClassID:
1846 // If the result is me.InstanceExists it means the entity was already created. It is ok handled that as success
1847 if msgObj.Result == me.Success || msgObj.Result == me.InstanceExists {
1848 mm.l2PmCreateOrDeleteResponseChan <- true
1849 } else {
1850 logger.Warnw(ctx, "failed to create me", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "class-id": msgObj.EntityClass})
1851 mm.l2PmCreateOrDeleteResponseChan <- false
1852 }
1853 return nil
1854 default:
1855 logger.Errorw(ctx, "unhandled omci create response message",
1856 log.Fields{"device-id": mm.pDeviceHandler.deviceID, "class-id": msgObj.EntityClass})
1857 }
1858 return fmt.Errorf("unhandled-omci-create-response-message-%v", mm.pDeviceHandler.deviceID)
1859}
1860
1861func (mm *onuMetricsManager) handleOmciDeleteResponseMessage(ctx context.Context, msg OmciMessage) error {
1862 msgLayer := (*msg.OmciPacket).Layer(omci.LayerTypeDeleteResponse)
1863 if msgLayer == nil {
1864 logger.Errorw(ctx, "omci Msg layer could not be detected for delete response - handling stopped", log.Fields{"device-id": mm.pDeviceHandler.deviceID})
1865 return fmt.Errorf("omci Msg layer could not be detected for create response - handling stopped: %s", mm.pDeviceHandler.deviceID)
1866 }
1867 msgObj, msgOk := msgLayer.(*omci.DeleteResponse)
1868 if !msgOk {
1869 logger.Errorw(ctx, "omci Msg layer could not be assigned for delete response - handling stopped", log.Fields{"device-id": mm.pDeviceHandler.deviceID})
1870 return fmt.Errorf("omci Msg layer could not be assigned for delete response - handling stopped: %s", mm.pDeviceHandler.deviceID)
1871 }
1872 logger.Debugw(ctx, "OMCI delete response Data", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "data-fields": msgObj})
1873 switch msgObj.EntityClass {
1874 case me.EthernetFramePerformanceMonitoringHistoryDataUpstreamClassID,
1875 me.EthernetFramePerformanceMonitoringHistoryDataDownstreamClassID,
1876 me.EthernetPerformanceMonitoringHistoryDataClassID:
1877 // If the result is me.UnknownInstance it means the entity was already deleted. It is ok handled that as success
1878 if msgObj.Result == me.Success || msgObj.Result == me.UnknownInstance {
1879 mm.l2PmCreateOrDeleteResponseChan <- true
1880 } else {
1881 logger.Warnw(ctx, "failed to delete me", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "class-id": msgObj.EntityClass})
1882 mm.l2PmCreateOrDeleteResponseChan <- false
1883 }
1884 return nil
1885 default:
1886 logger.Errorw(ctx, "unhandled omci delete response message",
1887 log.Fields{"device-id": mm.pDeviceHandler.deviceID, "class-id": msgObj.EntityClass})
1888 }
1889 return fmt.Errorf("unhandled-omci-delete-response-message-%v", mm.pDeviceHandler.deviceID)
1890}
1891
1892func (mm *onuMetricsManager) generateTicks(ctx context.Context) {
1893 for {
1894 select {
1895 case <-time.After(L2PmCollectionInterval * time.Second):
1896 go func() {
1897 if err := mm.pAdaptFsm.pFsm.Event(l2PmEventTick); err != nil {
1898 logger.Errorw(ctx, "error calling event", log.Fields{"device-id": mm.pDeviceHandler.deviceID, "err": err})
1899 }
1900 }()
1901 case <-mm.stopTicks:
1902 logger.Infow(ctx, "stopping ticks", log.Fields{"device-id": mm.pDeviceHandler.deviceID})
1903 return
1904 }
1905 }
1906}
1907
1908func (mm *onuMetricsManager) appendIfMissing(slice []string, n string) []string {
1909 for _, ele := range slice {
1910 if ele == n {
1911 return slice
1912 }
1913 }
1914 return append(slice, n)
1915}
1916
1917func (mm *onuMetricsManager) removeIfFound(slice []string, n string) []string {
1918 for i, ele := range slice {
1919 if ele == n {
1920 return append(slice[:i], slice[i+1:]...)
1921 }
1922 }
1923 return slice
Girish Gowdrae09a6202021-01-12 18:10:59 -08001924}