blob: 55116b6f8bbbb8e959e8a440355e718612d4e6e7 [file] [log] [blame]
Girish Gowdra6afb56a2021-04-27 17:47:57 -07001/*
2 * Copyright 2021-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//Package adaptercoreonu provides the utility for onu devices, flows and statistics
18package adaptercoreonu
19
20import (
21 "context"
22 "fmt"
23 "github.com/looplab/fsm"
24 "github.com/opencord/omci-lib-go"
25 "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/extension"
28 "sync"
29 "time"
30)
31
32const (
33 // events of Self Test FSM
34 selfTestEventTestRequest = "selfTestEventTestRequest"
35 selfTestEventTestResponseSuccess = "selfTestEventTestResponseSuccess"
36 selfTestEventTestResultSuccess = "selfTestEventTestResultSuccess"
37 selfTestEventAbort = "selfTestEventAbort"
38)
39const (
40 // states of Self Test FSM
41 selfTestStNull = "selfTestStNull"
42 selfTestStHandleSelfTestReq = "selfTestStHandleSelfTestReq"
43 selfTestStHandleSelfTestResp = "selfTestStHandleSelfTestResp"
44 selfTestStHandleTestResult = "selfTestStHandleTestResult"
45)
46
47const (
48 //SelfTestResponseWaitTimeout specifies timeout value waiting for self test response. Unit in seconds
49 SelfTestResponseWaitTimeout = 2
50)
51
52// We initiate an fsmCb per Self Test Request
53type fsmCb struct {
54 fsm *AdapterFsm
55 reqMsg extension.SingleGetValueRequest
56 respChan chan extension.SingleGetValueResponse
57 stopOmciChan chan bool
58}
59
60type selfTestControlBlock struct {
61 pDeviceHandler *deviceHandler
62 deviceID string
63
64 selfTestFsmMap map[generated.ClassID]*fsmCb // The fsmCb is indexed by ME Class ID of the Test Action procedure
65 selfTestFsmLock sync.RWMutex
66
67 stopSelfTestModule chan bool
68}
69
70// newSelfTestMsgHandlerCb creates the selfTestControlBlock
71// Self Test Handler module supports sending SelfTestRequest and handling of SelfTestResponse/SelfTestResults
72// An ephemeral Self Test FSM is initiated for every Self Test request and multiple Self Tests on different
73// MEs (that support it) can be handled in parallel.
74// At the time of creating this module, only ANI-G self-test is supported.
75func newSelfTestMsgHandlerCb(ctx context.Context, dh *deviceHandler) *selfTestControlBlock {
76 selfTestCb := selfTestControlBlock{pDeviceHandler: dh}
77 selfTestCb.selfTestFsmMap = make(map[generated.ClassID]*fsmCb)
78 selfTestCb.deviceID = selfTestCb.pDeviceHandler.deviceID
79 selfTestCb.stopSelfTestModule = make(chan bool)
80
81 go selfTestCb.waitForStopSelfTestModuleSignal(ctx)
82
83 return &selfTestCb
84}
85
86func (selfTestCb *selfTestControlBlock) initiateNewSelfTestFsm(ctx context.Context, reqMsg extension.SingleGetValueRequest, commChan chan Message, classID generated.ClassID, respChan chan extension.SingleGetValueResponse) error {
87 aFsm := NewAdapterFsm("selfTestFsm", selfTestCb.deviceID, commChan)
88
89 if aFsm == nil {
90 logger.Errorw(ctx, "selfTestFsm AdapterFsm could not be instantiated!!", log.Fields{
91 "device-id": selfTestCb.deviceID})
92 return fmt.Errorf("nil-adapter-fsm")
93 }
94 // Self Test FSM related state machine
95 aFsm.pFsm = fsm.NewFSM(
96
97 selfTestStNull,
98 fsm.Events{
99 {Name: selfTestEventTestRequest, Src: []string{selfTestStNull}, Dst: selfTestStHandleSelfTestReq},
100 {Name: selfTestEventTestResponseSuccess, Src: []string{selfTestStHandleSelfTestReq}, Dst: selfTestStHandleSelfTestResp},
101 {Name: selfTestEventTestResultSuccess, Src: []string{selfTestStHandleSelfTestResp}, Dst: selfTestStNull},
102 {Name: selfTestEventAbort, Src: []string{selfTestStHandleSelfTestReq, selfTestStHandleSelfTestReq, selfTestStHandleTestResult, selfTestStNull}, Dst: selfTestStNull},
103 },
104 fsm.Callbacks{
105 "enter_state": func(e *fsm.Event) { aFsm.logFsmStateChange(ctx, e) },
106 "enter_" + selfTestStHandleSelfTestReq: func(e *fsm.Event) { selfTestCb.selfTestFsmHandleSelfTestRequest(ctx, e) },
107 "enter_" + selfTestStHandleSelfTestResp: func(e *fsm.Event) { selfTestCb.selfTestFsmHandleSelfTestResponse(ctx, e) },
108 },
109 )
110 selfTestCb.selfTestFsmLock.Lock()
111 selfTestCb.selfTestFsmMap[classID] = &fsmCb{fsm: aFsm, reqMsg: reqMsg, respChan: respChan, stopOmciChan: make(chan bool)}
112 // Initiate the selfTestEventTestRequest on the FSM. Also pass the additional argument - classID.
113 // This is useful for the the FSM handler function to pull out fsmCb from the selfTestCb.selfTestFsmMap map.
114 selfTestCb.triggerFsmEvent(aFsm, selfTestEventTestRequest, classID)
115 selfTestCb.selfTestFsmLock.Unlock()
116
117 return nil
118}
119
120///// FSM Handlers
121
122func (selfTestCb *selfTestControlBlock) selfTestFsmHandleSelfTestRequest(ctx context.Context, e *fsm.Event) {
123 classID := e.Args[0].(generated.ClassID)
124 selfTestCb.selfTestFsmLock.RLock()
125 pFsmCb, ok := selfTestCb.selfTestFsmMap[classID]
126 selfTestCb.selfTestFsmLock.RUnlock()
127 if !ok {
128 // This case is impossible. Would be curious to see if this happens
129 logger.Fatalw(ctx, "class-id-not-found", log.Fields{"device-id": selfTestCb.deviceID, "classID": classID})
130 }
131 instKeys := selfTestCb.pDeviceHandler.pOnuOmciDevice.pOnuDB.getSortedInstKeys(ctx, classID)
132
133 // TODO: Choosing the first index from the instance keys. For ANI-G, this is fine as there is only one ANI-G instance. How do we handle and report self test for multiple instances?
134 if err := selfTestCb.pDeviceHandler.pOnuOmciDevice.PDevOmciCC.sendSelfTestReq(ctx, classID, instKeys[0], selfTestCb.pDeviceHandler.pOpenOnuAc.omciTimeout, false, pFsmCb.fsm.commChan); err != nil {
135 logger.Errorw(ctx, "error sending self test request", log.Fields{"device-id": selfTestCb.deviceID, "classID": classID})
136 selfTestCb.triggerFsmEvent(pFsmCb.fsm, selfTestEventAbort)
137 selfTestCb.submitFailureGetValueResponse(ctx, pFsmCb.respChan, extension.GetValueResponse_INTERNAL_ERROR, extension.GetValueResponse_ERROR)
138 return
139 }
140
141 go selfTestCb.handleOmciResponse(ctx, classID)
142}
143
144func (selfTestCb *selfTestControlBlock) selfTestFsmHandleSelfTestResponse(ctx context.Context, e *fsm.Event) {
145 classID := e.Args[0].(generated.ClassID)
146 // Pass the test result processing to another routine
147 go selfTestCb.handleOmciResponse(ctx, classID)
148
149}
150
151///// Utility functions
152
153func (selfTestCb *selfTestControlBlock) getMeClassID(ctx context.Context, reqMsg extension.SingleGetValueRequest) (generated.ClassID, error) {
154 switch reqMsg.GetRequest().GetRequest().(type) {
155 case *extension.GetValueRequest_OnuOpticalInfo:
156 return aniGClassID, nil
157 default:
158 logger.Warnw(ctx, "unsupported me class id for self test", log.Fields{"device-id": selfTestCb.deviceID})
159 return 0, fmt.Errorf("unsupported me class id for self test %v", selfTestCb.deviceID)
160 }
161}
162
163func (selfTestCb *selfTestControlBlock) triggerFsmEvent(pSelfTestFsm *AdapterFsm, event string, args ...generated.ClassID) {
164 go func() {
165 if len(args) > 0 {
166 _ = pSelfTestFsm.pFsm.Event(event, args[0])
167 } else {
168 _ = pSelfTestFsm.pFsm.Event(event)
169 }
170 }()
171}
172
173func (selfTestCb *selfTestControlBlock) submitFailureGetValueResponse(ctx context.Context, respChan chan extension.SingleGetValueResponse, errorCode extension.GetValueResponse_ErrorReason, statusCode extension.GetValueResponse_Status) {
174 singleValResp := extension.SingleGetValueResponse{
175 Response: &extension.GetValueResponse{
176 Status: statusCode,
177 ErrReason: errorCode,
178 },
179 }
180 logger.Infow(ctx, "OMCI test response failure - pushing failure response", log.Fields{"device-id": selfTestCb.deviceID})
181 respChan <- singleValResp
182 logger.Infow(ctx, "OMCI test response failure - pushing failure response complete", log.Fields{"device-id": selfTestCb.deviceID})
183}
184
185func (selfTestCb *selfTestControlBlock) handleOmciMessage(ctx context.Context, msg OmciMessage, cb *fsmCb, classID generated.ClassID) {
186 logger.Debugw(ctx, "omci Msg", log.Fields{"device-id": selfTestCb.deviceID, "msgType": msg.OmciMsg.MessageType, "msg": msg})
187 switch msg.OmciMsg.MessageType {
188 case omci.TestResponseType:
189 selfTestCb.handleOmciTestResponse(ctx, msg, cb, classID)
190 case omci.TestResultType:
191 selfTestCb.handleOmciTestResult(ctx, msg, cb, classID)
192 default:
193 logger.Warnw(ctx, "Unknown Message Type", log.Fields{"msgType": msg.OmciMsg.MessageType})
194 selfTestCb.triggerFsmEvent(cb.fsm, selfTestEventAbort)
195 selfTestCb.submitFailureGetValueResponse(ctx, cb.respChan, extension.GetValueResponse_UNSUPPORTED, extension.GetValueResponse_ERROR)
196 }
197}
198
199func (selfTestCb *selfTestControlBlock) handleOmciTestResponse(ctx context.Context, msg OmciMessage, cb *fsmCb, classID generated.ClassID) {
200 msgLayer := (*msg.OmciPacket).Layer(omci.LayerTypeTestResponse)
201 if msgLayer == nil {
202 logger.Errorw(ctx, "omci Msg layer nil self test response", log.Fields{"device-id": selfTestCb.deviceID, "classID": classID})
203 selfTestCb.pDeviceHandler.pOnuOmciDevice.PDevOmciCC.ReleaseTid(ctx, msg.OmciMsg.TransactionID)
204 selfTestCb.triggerFsmEvent(cb.fsm, selfTestEventAbort)
205 selfTestCb.submitFailureGetValueResponse(ctx, cb.respChan, extension.GetValueResponse_INTERNAL_ERROR, extension.GetValueResponse_ERROR)
206 return
207 }
208 msgObj, msgOk := msgLayer.(*omci.TestResponse)
209 if !msgOk {
210 logger.Errorw(ctx, "omci Msg layer could not be detected for self test response", log.Fields{"device-id": selfTestCb.deviceID, "classID": classID})
211 selfTestCb.pDeviceHandler.pOnuOmciDevice.PDevOmciCC.ReleaseTid(ctx, msg.OmciMsg.TransactionID)
212 selfTestCb.triggerFsmEvent(cb.fsm, selfTestEventAbort)
213 selfTestCb.submitFailureGetValueResponse(ctx, cb.respChan, extension.GetValueResponse_INTERNAL_ERROR, extension.GetValueResponse_ERROR)
214 return
215 }
216 logger.Debugw(ctx, "OMCI test response Data", log.Fields{"device-id": selfTestCb.deviceID, "data-fields": msgObj})
217 if msgObj.Result == generated.Success && msgObj.EntityClass == classID {
218 logger.Infow(ctx, "OMCI test response success", log.Fields{"device-id": selfTestCb.deviceID, "classID": classID})
219 selfTestCb.triggerFsmEvent(cb.fsm, selfTestEventTestResponseSuccess, classID)
220 return
221 }
222
223 logger.Infow(ctx, "OMCI test response failure", log.Fields{"device-id": selfTestCb.deviceID, "classID": classID})
224 selfTestCb.pDeviceHandler.pOnuOmciDevice.PDevOmciCC.ReleaseTid(ctx, msg.OmciMsg.TransactionID)
225 selfTestCb.triggerFsmEvent(cb.fsm, selfTestEventAbort)
226 selfTestCb.submitFailureGetValueResponse(ctx, cb.respChan, extension.GetValueResponse_UNSUPPORTED, extension.GetValueResponse_ERROR)
227}
228
229func (selfTestCb *selfTestControlBlock) handleOmciTestResult(ctx context.Context, msg OmciMessage, cb *fsmCb, classID generated.ClassID) {
230 msgLayer := (*msg.OmciPacket).Layer(omci.LayerTypeTestResult)
231 if msgLayer == nil {
232 logger.Errorw(ctx, "omci Msg layer nil self test result", log.Fields{"device-id": selfTestCb.deviceID, "classID": classID})
233 selfTestCb.triggerFsmEvent(cb.fsm, selfTestEventAbort)
234 selfTestCb.submitFailureGetValueResponse(ctx, cb.respChan, extension.GetValueResponse_INTERNAL_ERROR, extension.GetValueResponse_ERROR)
235 return
236 }
237 var msgObj *omci.OpticalLineSupervisionTestResult
238 var msgOk bool
239 switch classID {
240 case aniGClassID:
241 msgObj, msgOk = msgLayer.(*omci.OpticalLineSupervisionTestResult)
242 default:
243 // We should not really land here
244 selfTestCb.triggerFsmEvent(cb.fsm, selfTestEventAbort)
245 selfTestCb.submitFailureGetValueResponse(ctx, cb.respChan, extension.GetValueResponse_INTERNAL_ERROR, extension.GetValueResponse_ERROR)
246 return
247 }
248 if !msgOk {
249 logger.Errorw(ctx, "omci Msg layer could not be detected for self test result", log.Fields{"device-id": selfTestCb.deviceID, "classID": classID})
250 selfTestCb.triggerFsmEvent(cb.fsm, selfTestEventAbort)
251 selfTestCb.submitFailureGetValueResponse(ctx, cb.respChan, extension.GetValueResponse_INTERNAL_ERROR, extension.GetValueResponse_ERROR)
252 return
253 }
254 logger.Debugw(ctx, "raw omci values of ani-g test result",
255 log.Fields{"device-id": selfTestCb.deviceID,
256 "power-feed-voltage": msgObj.PowerFeedVoltage,
257 "rx-power": msgObj.ReceivedOpticalPower,
258 "tx-power": msgObj.MeanOpticalLaunch,
259 "laser-bias-current": msgObj.LaserBiasCurrent,
260 "temperature": msgObj.Temperature})
261 singleValResp := extension.SingleGetValueResponse{
262 Response: &extension.GetValueResponse{
263 Status: extension.GetValueResponse_OK,
264 Response: &extension.GetValueResponse_OnuOpticalInfo{
265 OnuOpticalInfo: &extension.GetOnuPonOpticalInfoResponse{
266 // OMCI representation is Volts, 2s compliment, 20mV resolution
267 PowerFeedVoltage: float32(TwosComplementToSignedInt16(msgObj.PowerFeedVoltage)) * 0.02,
Girish Gowdra444522f2021-05-12 14:32:24 -0700268 // OMCI representation is Decibel-microwatts, 2s compliment, 0.002dB resolution.
269 // Subtract 30 to convert the unit from dBu to dBm (as expected by proto interface)
270 ReceivedOpticalPower: float32(TwosComplementToSignedInt16(msgObj.ReceivedOpticalPower))*0.002 - 30,
Girish Gowdra6afb56a2021-04-27 17:47:57 -0700271 // OMCI representation is Decibel-microwatts, 2s compliment, 0.002dB resolution
Girish Gowdra444522f2021-05-12 14:32:24 -0700272 // Subtract 30 to convert the unit from dBu to dBm (as expected by proto interface)
273 MeanOpticalLaunchPower: float32(TwosComplementToSignedInt16(msgObj.MeanOpticalLaunch))*0.002 - 30,
Girish Gowdra6afb56a2021-04-27 17:47:57 -0700274 // OMCI representation is unsigned int, 2uA resolution
275 // units of gRPC interface is mA.
276 LaserBiasCurrent: float32(msgObj.LaserBiasCurrent) * 0.000002 * 1000, // multiply by 1000 to get units in mA
277 // OMCI representation is 2s complement, 1/256 degree Celsius resolution
278 Temperature: float32(TwosComplementToSignedInt16(msgObj.Temperature)) / 256.0,
279 },
280 },
281 },
282 }
283 logger.Debugw(ctx, "ani-g test result after type/value conversion",
284 log.Fields{"device-id": selfTestCb.deviceID,
285 "power-feed-voltage": singleValResp.Response.GetOnuOpticalInfo().PowerFeedVoltage,
286 "rx-power": singleValResp.Response.GetOnuOpticalInfo().ReceivedOpticalPower,
287 "tx-power": singleValResp.Response.GetOnuOpticalInfo().MeanOpticalLaunchPower,
288 "laser-bias-current": singleValResp.Response.GetOnuOpticalInfo().LaserBiasCurrent,
289 "temperature": singleValResp.Response.GetOnuOpticalInfo().Temperature})
290 selfTestCb.triggerFsmEvent(cb.fsm, selfTestEventTestResultSuccess)
291 logger.Infow(ctx, "OMCI test result success - pushing results", log.Fields{"device-id": selfTestCb.deviceID, "classID": classID})
292 cb.respChan <- singleValResp
293 selfTestCb.selfTestRequestComplete(ctx, cb.reqMsg)
294 logger.Infow(ctx, "OMCI test result success - pushing results complete", log.Fields{"device-id": selfTestCb.deviceID, "classID": classID})
295}
296
297func (selfTestCb *selfTestControlBlock) handleOmciResponse(ctx context.Context, classID generated.ClassID) {
298 selfTestCb.selfTestFsmLock.RLock()
299 pFsmCb, ok := selfTestCb.selfTestFsmMap[classID]
300 selfTestCb.selfTestFsmLock.RUnlock()
301 if !ok {
302 logger.Errorw(ctx, "fsb control block unavailable", log.Fields{"device-id": selfTestCb.deviceID, "class-id": classID})
303 return
304 }
305 select {
306 case <-pFsmCb.stopOmciChan:
307 logger.Infow(ctx, "omci processing stopped", log.Fields{"device-id": selfTestCb.deviceID, "class-id": classID})
308 selfTestCb.triggerFsmEvent(pFsmCb.fsm, selfTestEventAbort)
309 selfTestCb.submitFailureGetValueResponse(ctx, pFsmCb.respChan, extension.GetValueResponse_REASON_UNDEFINED, extension.GetValueResponse_ERROR)
310 case message, ok := <-pFsmCb.fsm.commChan:
311 if !ok {
312 logger.Errorw(ctx, "Message couldn't be read from channel", log.Fields{"device-id": selfTestCb.deviceID})
313 selfTestCb.triggerFsmEvent(pFsmCb.fsm, selfTestEventAbort)
314 selfTestCb.submitFailureGetValueResponse(ctx, pFsmCb.respChan, extension.GetValueResponse_INTERNAL_ERROR, extension.GetValueResponse_ERROR)
315 }
316 logger.Debugw(ctx, "Received message on self test result channel", log.Fields{"device-id": selfTestCb.deviceID})
317
318 switch message.Type {
319 case OMCI:
320 msg, _ := message.Data.(OmciMessage)
321 selfTestCb.handleOmciMessage(ctx, msg, pFsmCb, classID)
322 default:
323 logger.Errorw(ctx, "Unknown message type received", log.Fields{"device-id": selfTestCb.deviceID, "message.Type": message.Type})
324 selfTestCb.submitFailureGetValueResponse(ctx, pFsmCb.respChan, extension.GetValueResponse_UNSUPPORTED, extension.GetValueResponse_ERROR)
325 }
326 case <-time.After(time.Duration(SelfTestResponseWaitTimeout) * time.Second):
327 logger.Errorw(ctx, "timeout waiting for test result", log.Fields{"device-id": selfTestCb.deviceID, "classID": classID})
328 selfTestCb.triggerFsmEvent(pFsmCb.fsm, selfTestEventAbort)
329 selfTestCb.submitFailureGetValueResponse(ctx, pFsmCb.respChan, extension.GetValueResponse_TIMEOUT, extension.GetValueResponse_ERROR)
330 }
331}
332
333// selfTestRequestComplete removes the fsmCb from the local cache if found
334func (selfTestCb *selfTestControlBlock) selfTestRequestComplete(ctx context.Context, reqMsg extension.SingleGetValueRequest) {
335 meClassID, err := selfTestCb.getMeClassID(ctx, reqMsg)
336 if err != nil {
337 return
338 }
339 logger.Infow(ctx, "self test req handling complete", log.Fields{"device-id": selfTestCb.deviceID, "meClassID": meClassID})
340 // Clear the fsmCb from the map
341 delete(selfTestCb.selfTestFsmMap, meClassID)
342}
343
344func (selfTestCb *selfTestControlBlock) waitForStopSelfTestModuleSignal(ctx context.Context) {
345
346 <-selfTestCb.stopSelfTestModule // block on stop signal
347
348 logger.Infow(ctx, "received stop signal - clean up start", log.Fields{"device-id": selfTestCb.deviceID})
349 selfTestCb.selfTestFsmLock.Lock()
350 for classID, fsmCb := range selfTestCb.selfTestFsmMap {
351 select {
352 case fsmCb.stopOmciChan <- true: // stop omci processing routine if one was active. It eventually aborts the fsm
353 logger.Debugw(ctx, "stopped omci processing", log.Fields{"device-id": selfTestCb.deviceID, "meClassID": classID})
354 default:
355 selfTestCb.triggerFsmEvent(fsmCb.fsm, selfTestEventAbort)
356 selfTestCb.submitFailureGetValueResponse(ctx, fsmCb.respChan, extension.GetValueResponse_REASON_UNDEFINED, extension.GetValueResponse_ERROR)
357 }
358 }
359 selfTestCb.selfTestFsmMap = make(map[generated.ClassID]*fsmCb) // reset map
360 selfTestCb.selfTestFsmLock.Unlock()
361 logger.Infow(ctx, "received stop signal - clean up end", log.Fields{"device-id": selfTestCb.deviceID})
362}
363
364//// Exported functions
365
366// selfTestRequest initiate Test Request handling procedure. The results are asynchronously conveyed on the respChan.
367// If the return from selfTestRequest is NOT nil, the caller shall not wait for async response.
368func (selfTestCb *selfTestControlBlock) SelfTestRequestStart(ctx context.Context, reqMsg extension.SingleGetValueRequest, commChan chan Message, respChan chan extension.SingleGetValueResponse) error {
369 meClassID, err := selfTestCb.getMeClassID(ctx, reqMsg)
370 if err != nil {
371 return err
372 }
373 if _, ok := selfTestCb.selfTestFsmMap[meClassID]; ok {
374 logger.Errorw(ctx, "self test already in progress for class id", log.Fields{"device-id": selfTestCb.deviceID, "class-id": meClassID})
375 return fmt.Errorf("self-test-already-in-progress-for-class-id-%v-device-id-%v", meClassID, selfTestCb.deviceID)
376 }
377 logger.Infow(ctx, "self test request initiated", log.Fields{"device-id": selfTestCb.deviceID, "meClassID": meClassID})
378 // indicates only if the FSM was initiated correctly. Response is asynchronous on respChan.
379 // If the return from here is NOT nil, the caller shall not wait for async response.
380 return selfTestCb.initiateNewSelfTestFsm(ctx, reqMsg, commChan, meClassID, respChan)
381}