blob: d7c496c963bc137f42af489aa92645c654f85150 [file] [log] [blame]
Takahiro Suzuki241c10e2020-12-17 20:17:57 +09001/*
2 * Copyright 2020-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//Package adaptercoreonu provides the utility for onu devices, flows and statistics
18package adaptercoreonu
19
20import (
21 "context"
22 "errors"
23 "fmt"
24 "strconv"
25 "time"
26
27 "github.com/cevaris/ordered_map"
28 "github.com/looplab/fsm"
29 "github.com/opencord/omci-lib-go"
30 me "github.com/opencord/omci-lib-go/generated"
31 "github.com/opencord/voltha-lib-go/v3/pkg/log"
32)
33
34const (
35 aniEvStart = "uniEvStart"
36 aniEvStartConfig = "aniEvStartConfig"
37 aniEvRxDot1pmapCResp = "aniEvRxDot1pmapCResp"
38 aniEvRxMbpcdResp = "aniEvRxMbpcdResp"
39 aniEvRxTcontsResp = "aniEvRxTcontsResp"
40 aniEvRxGemntcpsResp = "aniEvRxGemntcpsResp"
41 aniEvRxGemiwsResp = "aniEvRxGemiwsResp"
42 aniEvRxPrioqsResp = "aniEvRxPrioqsResp"
43 aniEvRxDot1pmapSResp = "aniEvRxDot1pmapSResp"
44 aniEvTimeoutSimple = "aniEvTimeoutSimple"
45 aniEvTimeoutMids = "aniEvTimeoutMids"
46 aniEvReset = "aniEvReset"
47 aniEvRestart = "aniEvRestart"
48)
49const (
50 aniStDisabled = "aniStDisabled"
51 aniStStarting = "aniStStarting"
52 aniStCreatingDot1PMapper = "aniStCreatingDot1PMapper"
53 aniStCreatingMBPCD = "aniStCreatingMBPCD"
54 aniStSettingTconts = "aniStSettingTconts"
55 aniStCreatingGemNCTPs = "aniStCreatingGemNCTPs"
56 aniStCreatingGemIWs = "aniStCreatingGemIWs"
57 aniStSettingPQs = "aniStSettingPQs"
58 aniStSettingDot1PMapper = "aniStSettingDot1PMapper"
59 aniStConfigDone = "aniStConfigDone"
60 aniStResetting = "aniStResetting"
61)
62
63type ponAniGemPortAttribs struct {
64 gemPortID uint16
65 upQueueID uint16
66 downQueueID uint16
67 direction uint8
68 qosPolicy string
69 weight uint8
70 pbitString string
71}
72
73//uniPonAniConfigFsm defines the structure for the state machine to config the PON ANI ports of ONU UNI ports via OMCI
74type uniPonAniConfigFsm struct {
75 pOmciCC *omciCC
76 pOnuUniPort *onuUniPort
77 pUniTechProf *onuUniTechProf
78 pOnuDB *onuDeviceDB
79 techProfileID uint16
80 requestEvent OnuDeviceEvent
81 omciMIdsResponseReceived chan bool //separate channel needed for checking multiInstance OMCI message responses
82 pAdaptFsm *AdapterFsm
83 aniConfigCompleted bool
84 chSuccess chan<- uint8
85 procStep uint8
86 chanSet bool
87 mapperSP0ID uint16
88 macBPCD0ID uint16
89 tcont0ID uint16
90 alloc0ID uint16
91 gemPortAttribsSlice []ponAniGemPortAttribs
92}
93
94//newUniPonAniConfigFsm is the 'constructor' for the state machine to config the PON ANI ports of ONU UNI ports via OMCI
95func newUniPonAniConfigFsm(apDevOmciCC *omciCC, apUniPort *onuUniPort, apUniTechProf *onuUniTechProf,
96 apOnuDB *onuDeviceDB, aTechProfileID uint16, aRequestEvent OnuDeviceEvent, aName string,
97 aDeviceID string, aCommChannel chan Message) *uniPonAniConfigFsm {
98 instFsm := &uniPonAniConfigFsm{
99 pOmciCC: apDevOmciCC,
100 pOnuUniPort: apUniPort,
101 pUniTechProf: apUniTechProf,
102 pOnuDB: apOnuDB,
103 techProfileID: aTechProfileID,
104 requestEvent: aRequestEvent,
105 aniConfigCompleted: false,
106 chanSet: false,
107 }
108 instFsm.pAdaptFsm = NewAdapterFsm(aName, aDeviceID, aCommChannel)
109 if instFsm.pAdaptFsm == nil {
110 logger.Errorw("uniPonAniConfigFsm's AdapterFsm could not be instantiated!!", log.Fields{
111 "device-id": aDeviceID})
112 return nil
113 }
114
115 instFsm.pAdaptFsm.pFsm = fsm.NewFSM(
116 aniStDisabled,
117 fsm.Events{
118
119 {Name: aniEvStart, Src: []string{aniStDisabled}, Dst: aniStStarting},
120
121 //Note: .1p-Mapper and MBPCD might also have multi instances (per T-Cont) - by now only one 1 T-Cont considered!
122 {Name: aniEvStartConfig, Src: []string{aniStStarting}, Dst: aniStCreatingDot1PMapper},
123 {Name: aniEvRxDot1pmapCResp, Src: []string{aniStCreatingDot1PMapper}, Dst: aniStCreatingMBPCD},
124 {Name: aniEvRxMbpcdResp, Src: []string{aniStCreatingMBPCD}, Dst: aniStSettingTconts},
125 {Name: aniEvRxTcontsResp, Src: []string{aniStSettingTconts}, Dst: aniStCreatingGemNCTPs},
126 // the creatingGemNCTPs state is used for multi ME config if required for all configured/available GemPorts
127 {Name: aniEvRxGemntcpsResp, Src: []string{aniStCreatingGemNCTPs}, Dst: aniStCreatingGemIWs},
128 // the creatingGemIWs state is used for multi ME config if required for all configured/available GemPorts
129 {Name: aniEvRxGemiwsResp, Src: []string{aniStCreatingGemIWs}, Dst: aniStSettingPQs},
130 // the settingPQs state is used for multi ME config if required for all configured/available upstream PriorityQueues
131 {Name: aniEvRxPrioqsResp, Src: []string{aniStSettingPQs}, Dst: aniStSettingDot1PMapper},
132 {Name: aniEvRxDot1pmapSResp, Src: []string{aniStSettingDot1PMapper}, Dst: aniStConfigDone},
133
134 {Name: aniEvTimeoutSimple, Src: []string{
135 aniStCreatingDot1PMapper, aniStCreatingMBPCD, aniStSettingTconts, aniStSettingDot1PMapper}, Dst: aniStStarting},
136 {Name: aniEvTimeoutMids, Src: []string{
137 aniStCreatingGemNCTPs, aniStCreatingGemIWs, aniStSettingPQs}, Dst: aniStStarting},
138
139 // exceptional treatment for all states except aniStResetting
140 {Name: aniEvReset, Src: []string{aniStStarting, aniStCreatingDot1PMapper, aniStCreatingMBPCD,
141 aniStSettingTconts, aniStCreatingGemNCTPs, aniStCreatingGemIWs, aniStSettingPQs, aniStSettingDot1PMapper,
142 aniStConfigDone}, Dst: aniStResetting},
143 // the only way to get to resource-cleared disabled state again is via "resseting"
144 {Name: aniEvRestart, Src: []string{aniStResetting}, Dst: aniStDisabled},
145 },
146
147 fsm.Callbacks{
148 "enter_state": func(e *fsm.Event) { instFsm.pAdaptFsm.logFsmStateChange(e) },
149 ("enter_" + aniStStarting): func(e *fsm.Event) { instFsm.enterConfigStartingState(e) },
150 ("enter_" + aniStCreatingDot1PMapper): func(e *fsm.Event) { instFsm.enterCreatingDot1PMapper(e) },
151 ("enter_" + aniStCreatingMBPCD): func(e *fsm.Event) { instFsm.enterCreatingMBPCD(e) },
152 ("enter_" + aniStSettingTconts): func(e *fsm.Event) { instFsm.enterSettingTconts(e) },
153 ("enter_" + aniStCreatingGemNCTPs): func(e *fsm.Event) { instFsm.enterCreatingGemNCTPs(e) },
154 ("enter_" + aniStCreatingGemIWs): func(e *fsm.Event) { instFsm.enterCreatingGemIWs(e) },
155 ("enter_" + aniStSettingPQs): func(e *fsm.Event) { instFsm.enterSettingPQs(e) },
156 ("enter_" + aniStSettingDot1PMapper): func(e *fsm.Event) { instFsm.enterSettingDot1PMapper(e) },
157 ("enter_" + aniStConfigDone): func(e *fsm.Event) { instFsm.enterAniConfigDone(e) },
158 ("enter_" + aniStResetting): func(e *fsm.Event) { instFsm.enterResettingState(e) },
159 ("enter_" + aniStDisabled): func(e *fsm.Event) { instFsm.enterDisabledState(e) },
160 },
161 )
162 if instFsm.pAdaptFsm.pFsm == nil {
163 logger.Errorw("uniPonAniConfigFsm's Base FSM could not be instantiated!!", log.Fields{
164 "device-id": aDeviceID})
165 return nil
166 }
167
168 logger.Infow("uniPonAniConfigFsm created", log.Fields{"device-id": aDeviceID})
169 return instFsm
170}
171
172//setFsmCompleteChannel sets the requested channel and channel result for transfer on success
173func (oFsm *uniPonAniConfigFsm) setFsmCompleteChannel(aChSuccess chan<- uint8, aProcStep uint8) {
174 oFsm.chSuccess = aChSuccess
175 oFsm.procStep = aProcStep
176 oFsm.chanSet = true
177}
178
179func (oFsm *uniPonAniConfigFsm) prepareAndEnterConfigState(aPAFsm *AdapterFsm) {
180 if aPAFsm != nil && aPAFsm.pFsm != nil {
181 //stick to pythonAdapter numbering scheme
182 //index 0 in naming refers to possible usage of multiple instances (later)
183 oFsm.mapperSP0ID = ieeeMapperServiceProfileEID + uint16(oFsm.pOnuUniPort.macBpNo) + oFsm.techProfileID
184 oFsm.macBPCD0ID = macBridgePortAniEID + uint16(oFsm.pOnuUniPort.entityID) + oFsm.techProfileID
185
186 // For the time being: if there are multiple T-Conts on the ONU the first one from the entityID-ordered list is used
187 // TODO!: if more T-Conts have to be supported (tcontXID!), then use the first instances of the entity-ordered list
188 // or use the py code approach, which might be a bit more complicated, but also more secure, as it
189 // ensures that the selected T-Cont also has queues (which I would assume per definition from ONU, but who knows ...)
190 // so this approach would search the (sorted) upstream PrioQueue list and use the T-Cont (if available) from highest Bytes
191 // or sndHighByte of relatedPort Attribute (T-Cont Reference) and in case of multiple TConts find the next free TContIndex
192 // that way from PrioQueue.relatedPort list
193 if tcontInstKeys := oFsm.pOnuDB.getSortedInstKeys(me.TContClassID); len(tcontInstKeys) > 0 {
194 oFsm.tcont0ID = tcontInstKeys[0]
195 logger.Debugw("Used TcontId:", log.Fields{"TcontId": strconv.FormatInt(int64(oFsm.tcont0ID), 16),
196 "device-id": oFsm.pAdaptFsm.deviceID})
197 } else {
198 logger.Warnw("No TCont instances found", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
199 }
200 oFsm.alloc0ID = (*(oFsm.pUniTechProf.mapPonAniConfig[oFsm.pOnuUniPort.uniID]))[0].tcontParams.allocID
201 loGemPortAttribs := ponAniGemPortAttribs{}
202 //for all TechProfile set GemIndices
203 for _, gemEntry := range (*(oFsm.pUniTechProf.mapPonAniConfig[oFsm.pOnuUniPort.uniID]))[0].mapGemPortParams {
204 //collect all GemConfigData in a separate Fsm related slice (needed also to avoid mix-up with unsorted mapPonAniConfig)
205
206 if queueInstKeys := oFsm.pOnuDB.getSortedInstKeys(me.PriorityQueueClassID); len(queueInstKeys) > 0 {
207
208 loGemPortAttribs.gemPortID = gemEntry.gemPortID
209 // MibDb usage: upstream PrioQueue.RelatedPort = xxxxyyyy with xxxx=TCont.Entity(incl. slot) and yyyy=prio
210 // i.e.: search PrioQueue list with xxxx=actual T-Cont.Entity,
211 // from that list use the PrioQueue.Entity with gemEntry.prioQueueIndex == yyyy (expect 0..7)
212 usQrelPortMask := uint32((((uint32)(oFsm.tcont0ID)) << 16) + uint32(gemEntry.prioQueueIndex))
213
214 // MibDb usage: downstream PrioQueue.RelatedPort = xxyyzzzz with xx=slot, yy=UniPort and zzzz=prio
215 // i.e.: search PrioQueue list with yy=actual pOnuUniPort.uniID,
216 // from that list use the PrioQueue.Entity with gemEntry.prioQueueIndex == zzzz (expect 0..7)
217 // Note: As we do not maintain any slot numbering, slot number will be excluded from seatch pattern.
218 // Furthermore OMCI Onu port-Id is expected to start with 1 (not 0).
219 dsQrelPortMask := uint32((((uint32)(oFsm.pOnuUniPort.uniID + 1)) << 16) + uint32(gemEntry.prioQueueIndex))
220
221 usQueueFound := false
222 dsQueueFound := false
223 for _, mgmtEntityID := range queueInstKeys {
224 if meAttributes := oFsm.pOnuDB.GetMe(me.PriorityQueueClassID, mgmtEntityID); meAttributes != nil {
225 returnVal := meAttributes["RelatedPort"]
226 if returnVal != nil {
227 if relatedPort, err := oFsm.pOnuDB.getUint32Attrib(returnVal); err == nil {
228 if relatedPort == usQrelPortMask {
229 loGemPortAttribs.upQueueID = mgmtEntityID
230 logger.Debugw("UpQueue for GemPort found:", log.Fields{"gemPortID": loGemPortAttribs.gemPortID,
231 "upQueueID": strconv.FormatInt(int64(loGemPortAttribs.upQueueID), 16), "device-id": oFsm.pAdaptFsm.deviceID})
232 usQueueFound = true
233 } else if (relatedPort&0xFFFFFF) == dsQrelPortMask && mgmtEntityID < 0x8000 {
234 loGemPortAttribs.downQueueID = mgmtEntityID
235 logger.Debugw("DownQueue for GemPort found:", log.Fields{"gemPortID": loGemPortAttribs.gemPortID,
236 "downQueueID": strconv.FormatInt(int64(loGemPortAttribs.downQueueID), 16), "device-id": oFsm.pAdaptFsm.deviceID})
237 dsQueueFound = true
238 }
239 if usQueueFound && dsQueueFound {
240 break
241 }
242 } else {
243 logger.Warnw("Could not convert attribute value", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
244 }
245 } else {
246 logger.Warnw("'RelatedPort' not found in meAttributes:", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
247 }
248 } else {
249 logger.Warnw("No attributes available in DB:", log.Fields{"meClassID": me.PriorityQueueClassID,
250 "mgmtEntityID": mgmtEntityID, "device-id": oFsm.pAdaptFsm.deviceID})
251 }
252 }
253 } else {
254 logger.Warnw("No PriorityQueue instances found", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
255 }
256 loGemPortAttribs.direction = gemEntry.direction
257 loGemPortAttribs.qosPolicy = gemEntry.queueSchedPolicy
258 loGemPortAttribs.weight = gemEntry.queueWeight
259 loGemPortAttribs.pbitString = gemEntry.pbitString
260
261 logger.Debugw("prio-related GemPort attributes:", log.Fields{
262 "gemPortID": loGemPortAttribs.gemPortID,
263 "upQueueID": loGemPortAttribs.upQueueID,
264 "downQueueID": loGemPortAttribs.downQueueID,
265 "pbitString": loGemPortAttribs.pbitString,
266 "prioQueueIndex": gemEntry.prioQueueIndex,
267 })
268
269 oFsm.gemPortAttribsSlice = append(oFsm.gemPortAttribsSlice, loGemPortAttribs)
270 }
271 _ = aPAFsm.pFsm.Event(aniEvStartConfig)
272 }
273}
274
275func (oFsm *uniPonAniConfigFsm) enterConfigStartingState(e *fsm.Event) {
276 logger.Debugw("UniPonAniConfigFsm start", log.Fields{"in state": e.FSM.Current(),
277 "device-id": oFsm.pAdaptFsm.deviceID})
278 if oFsm.omciMIdsResponseReceived == nil {
279 oFsm.omciMIdsResponseReceived = make(chan bool)
280 logger.Debug("uniPonAniConfigFsm - OMCI multiInstance RxChannel defined")
281 } else {
282 // as we may 're-use' this instance of FSM and the connected channel
283 // make sure there is no 'lingering' request in the already existing channel:
284 // (simple loop sufficient as we are the only receiver)
285 for len(oFsm.omciMIdsResponseReceived) > 0 {
286 <-oFsm.omciMIdsResponseReceived
287 }
288 }
289 oFsm.gemPortAttribsSlice = nil
290
291 go oFsm.processOmciAniMessages()
292
293 pConfigAniStateAFsm := oFsm.pAdaptFsm
294 if pConfigAniStateAFsm != nil {
295 // obviously calling some FSM event here directly does not work - so trying to decouple it ...
296 go oFsm.prepareAndEnterConfigState(pConfigAniStateAFsm)
297
298 }
299}
300
301func (oFsm *uniPonAniConfigFsm) enterCreatingDot1PMapper(e *fsm.Event) {
302 logger.Debugw("uniPonAniConfigFsm Tx Create::Dot1PMapper", log.Fields{
303 "EntitytId": strconv.FormatInt(int64(oFsm.mapperSP0ID), 16),
304 "in state": e.FSM.Current(), "device-id": oFsm.pAdaptFsm.deviceID})
305 meInstance := oFsm.pOmciCC.sendCreateDot1PMapper(context.TODO(), ConstDefaultOmciTimeout, true,
306 oFsm.mapperSP0ID, oFsm.pAdaptFsm.commChan)
307 oFsm.pOmciCC.pLastTxMeInstance = meInstance
308}
309
310func (oFsm *uniPonAniConfigFsm) enterCreatingMBPCD(e *fsm.Event) {
311 logger.Debugw("uniPonAniConfigFsm Tx Create::MBPCD", log.Fields{
312 "EntitytId": strconv.FormatInt(int64(oFsm.macBPCD0ID), 16),
313 "TPPtr": strconv.FormatInt(int64(oFsm.mapperSP0ID), 16),
314 "in state": e.FSM.Current(), "device-id": oFsm.pAdaptFsm.deviceID})
315 bridgePtr := macBridgeServiceProfileEID + uint16(oFsm.pOnuUniPort.macBpNo) //cmp also omci_cc.go::sendCreateMBServiceProfile
316 meParams := me.ParamData{
317 EntityID: oFsm.macBPCD0ID,
318 Attributes: me.AttributeValueMap{
319 "BridgeIdPointer": bridgePtr,
320 "PortNum": 0xFF, //fixed unique ANI side indication
321 "TpType": 3, //for .1PMapper
322 "TpPointer": oFsm.mapperSP0ID,
323 },
324 }
325 meInstance := oFsm.pOmciCC.sendCreateMBPConfigDataVar(context.TODO(), ConstDefaultOmciTimeout, true,
326 oFsm.pAdaptFsm.commChan, meParams)
327 oFsm.pOmciCC.pLastTxMeInstance = meInstance
328
329}
330
331func (oFsm *uniPonAniConfigFsm) enterSettingTconts(e *fsm.Event) {
332 logger.Debugw("uniPonAniConfigFsm Tx Set::Tcont", log.Fields{
333 "EntitytId": strconv.FormatInt(int64(oFsm.tcont0ID), 16),
334 "AllocId": strconv.FormatInt(int64(oFsm.alloc0ID), 16),
335 "in state": e.FSM.Current(), "device-id": oFsm.pAdaptFsm.deviceID})
336 meParams := me.ParamData{
337 EntityID: oFsm.tcont0ID,
338 Attributes: me.AttributeValueMap{
339 "AllocId": oFsm.alloc0ID,
340 },
341 }
342 meInstance := oFsm.pOmciCC.sendSetTcontVar(context.TODO(), ConstDefaultOmciTimeout, true,
343 oFsm.pAdaptFsm.commChan, meParams)
344 oFsm.pOmciCC.pLastTxMeInstance = meInstance
345}
346
347func (oFsm *uniPonAniConfigFsm) enterCreatingGemNCTPs(e *fsm.Event) {
348 logger.Debugw("uniPonAniConfigFsm - start creating GemNWCtp loop", log.Fields{
349 "in state": e.FSM.Current(), "device-id": oFsm.pAdaptFsm.deviceID})
350 go oFsm.performCreatingGemNCTPs()
351}
352
353func (oFsm *uniPonAniConfigFsm) enterCreatingGemIWs(e *fsm.Event) {
354 logger.Debugw("uniPonAniConfigFsm - start creating GemIwTP loop", log.Fields{
355 "in state": e.FSM.Current(), "device-id": oFsm.pAdaptFsm.deviceID})
356 go oFsm.performCreatingGemIWs()
357}
358
359func (oFsm *uniPonAniConfigFsm) enterSettingPQs(e *fsm.Event) {
360 logger.Debugw("uniPonAniConfigFsm - start setting PrioQueue loop", log.Fields{
361 "in state": e.FSM.Current(), "device-id": oFsm.pAdaptFsm.deviceID})
362 go oFsm.performSettingPQs()
363}
364
365func (oFsm *uniPonAniConfigFsm) enterSettingDot1PMapper(e *fsm.Event) {
366 logger.Debugw("uniPonAniConfigFsm Tx Set::.1pMapper with all PBits set", log.Fields{"EntitytId": 0x8042, /*cmp above*/
367 "toGemIw": 1024 /* cmp above */, "in state": e.FSM.Current(), "device-id": oFsm.pAdaptFsm.deviceID})
368
369 logger.Debugw("uniPonAniConfigFsm Tx Set::1pMapper", log.Fields{
370 "EntitytId": strconv.FormatInt(int64(oFsm.mapperSP0ID), 16),
371 "in state": e.FSM.Current(), "device-id": oFsm.pAdaptFsm.deviceID})
372
373 meParams := me.ParamData{
374 EntityID: oFsm.mapperSP0ID,
375 Attributes: make(me.AttributeValueMap),
376 }
377
378 var loPrioGemPortArray [8]uint16
379 for _, gemPortAttribs := range oFsm.gemPortAttribsSlice {
380 for i := 0; i < 8; i++ {
381 // "lenOfPbitMap(8) - i + 1" will give i-th pbit value from LSB position in the pbit map string
382 if prio, err := strconv.Atoi(string(gemPortAttribs.pbitString[7-i])); err == nil {
383 if prio == 1 { // Check this p-bit is set
384 if loPrioGemPortArray[i] == 0 {
385 loPrioGemPortArray[i] = gemPortAttribs.gemPortID //gemPortId=EntityID and unique
386 } else {
387 logger.Warnw("uniPonAniConfigFsm PrioString not unique", log.Fields{
388 "device-id": oFsm.pAdaptFsm.deviceID, "IgnoredGemPort": gemPortAttribs.gemPortID,
389 "SetGemPort": loPrioGemPortArray[i]})
390 }
391 }
392 } else {
393 logger.Warnw("uniPonAniConfigFsm PrioString evaluation error", log.Fields{
394 "device-id": oFsm.pAdaptFsm.deviceID, "GemPort": gemPortAttribs.gemPortID,
395 "prioString": gemPortAttribs.pbitString, "position": i})
396 }
397
398 }
399 }
400 var foundIwPtr bool = false
401 for index, value := range loPrioGemPortArray {
402 if value != 0 {
403 foundIwPtr = true
404 meAttribute := fmt.Sprintf("InterworkTpPointerForPBitPriority%d", index)
405 logger.Debugf("UniPonAniConfigFsm Set::1pMapper", log.Fields{
406 "IwPtr for Prio%d": strconv.FormatInt(int64(value), 16), "device-id": oFsm.pAdaptFsm.deviceID}, index)
407 meParams.Attributes[meAttribute] = value
408
409 }
410 }
411
412 if !foundIwPtr {
413 logger.Errorw("UniPonAniConfigFsm no GemIwPtr found for .1pMapper - abort", log.Fields{
414 "device-id": oFsm.pAdaptFsm.deviceID})
415 //let's reset the state machine in order to release all resources now
416 pConfigAniStateAFsm := oFsm.pAdaptFsm
417 if pConfigAniStateAFsm != nil {
418 // obviously calling some FSM event here directly does not work - so trying to decouple it ...
419 go func(aPAFsm *AdapterFsm) {
420 if aPAFsm != nil && aPAFsm.pFsm != nil {
421 _ = aPAFsm.pFsm.Event(aniEvReset)
422 }
423 }(pConfigAniStateAFsm)
424 }
425 }
426
427 meInstance := oFsm.pOmciCC.sendSetDot1PMapperVar(context.TODO(), ConstDefaultOmciTimeout, true,
428 oFsm.pAdaptFsm.commChan, meParams)
429 oFsm.pOmciCC.pLastTxMeInstance = meInstance
430}
431
432func (oFsm *uniPonAniConfigFsm) enterAniConfigDone(e *fsm.Event) {
433
434 oFsm.aniConfigCompleted = true
435
436 pConfigAniStateAFsm := oFsm.pAdaptFsm
437 if pConfigAniStateAFsm != nil {
438 // obviously calling some FSM event here directly does not work - so trying to decouple it ...
439 go func(aPAFsm *AdapterFsm) {
440 if aPAFsm != nil && aPAFsm.pFsm != nil {
441 _ = aPAFsm.pFsm.Event(aniEvReset)
442 }
443 }(pConfigAniStateAFsm)
444 }
445}
446
447func (oFsm *uniPonAniConfigFsm) enterResettingState(e *fsm.Event) {
448 logger.Debugw("uniPonAniConfigFsm resetting", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
449
450 pConfigAniStateAFsm := oFsm.pAdaptFsm
451 if pConfigAniStateAFsm != nil {
452 // abort running message processing
453 fsmAbortMsg := Message{
454 Type: TestMsg,
455 Data: TestMessage{
456 TestMessageVal: AbortMessageProcessing,
457 },
458 }
459 pConfigAniStateAFsm.commChan <- fsmAbortMsg
460
461 //try to restart the FSM to 'disabled', decouple event transfer
462 go func(aPAFsm *AdapterFsm) {
463 if aPAFsm != nil && aPAFsm.pFsm != nil {
464 _ = aPAFsm.pFsm.Event(aniEvRestart)
465 }
466 }(pConfigAniStateAFsm)
467 }
468}
469
470func (oFsm *uniPonAniConfigFsm) enterDisabledState(e *fsm.Event) {
471 logger.Debugw("uniPonAniConfigFsm enters disabled state", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
472
473 if oFsm.aniConfigCompleted {
474 logger.Debugw("uniPonAniConfigFsm send dh event notification", log.Fields{
475 "from_State": e.FSM.Current(), "device-id": oFsm.pAdaptFsm.deviceID})
476 //use DeviceHandler event notification directly
477 oFsm.pOmciCC.pBaseDeviceHandler.deviceProcStatusUpdate(oFsm.requestEvent)
478 oFsm.aniConfigCompleted = false
479 }
480 oFsm.pUniTechProf.setConfigDone(oFsm.pOnuUniPort.uniID, true)
481 go oFsm.pOmciCC.pBaseDeviceHandler.verifyUniVlanConfigRequest(oFsm.pOnuUniPort)
482
483 if oFsm.chanSet {
484 // indicate processing done to the caller
485 logger.Debugw("uniPonAniConfigFsm processingDone on channel", log.Fields{
486 "ProcessingStep": oFsm.procStep, "from_State": e.FSM.Current(), "device-id": oFsm.pAdaptFsm.deviceID})
487 oFsm.chSuccess <- oFsm.procStep
488 oFsm.chanSet = false //reset the internal channel state
489 }
490
491}
492
493func (oFsm *uniPonAniConfigFsm) processOmciAniMessages( /*ctx context.Context*/ ) {
494 logger.Debugw("Start uniPonAniConfigFsm Msg processing", log.Fields{"for device-id": oFsm.pAdaptFsm.deviceID})
495loop:
496 for {
497 // case <-ctx.Done():
498 // logger.Info("MibSync Msg", log.Fields{"Message handling canceled via context for device-id": oFsm.pAdaptFsm.deviceID})
499 // break loop
500 message, ok := <-oFsm.pAdaptFsm.commChan
501 if !ok {
502 logger.Info("UniPonAniConfigFsm Rx Msg - could not read from channel", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
503 // but then we have to ensure a restart of the FSM as well - as exceptional procedure
504 _ = oFsm.pAdaptFsm.pFsm.Event(aniEvReset)
505 break loop
506 }
507 logger.Debugw("UniPonAniConfigFsm Rx Msg", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
508
509 switch message.Type {
510 case TestMsg:
511 msg, _ := message.Data.(TestMessage)
512 if msg.TestMessageVal == AbortMessageProcessing {
513 logger.Infow("UniPonAniConfigFsm abort ProcessMsg", log.Fields{"for device-id": oFsm.pAdaptFsm.deviceID})
514 break loop
515 }
516 logger.Warnw("UniPonAniConfigFsm unknown TestMessage", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID, "MessageVal": msg.TestMessageVal})
517 case OMCI:
518 msg, _ := message.Data.(OmciMessage)
519 oFsm.handleOmciAniConfigMessage(msg)
520 default:
521 logger.Warn("UniPonAniConfigFsm Rx unknown message", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID,
522 "message.Type": message.Type})
523 }
524
525 }
526 logger.Infow("End uniPonAniConfigFsm Msg processing", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
527}
528
529func (oFsm *uniPonAniConfigFsm) handleOmciAniConfigCreateResponseMessage(msg OmciMessage) {
530 msgLayer := (*msg.OmciPacket).Layer(omci.LayerTypeCreateResponse)
531 if msgLayer == nil {
532 logger.Error("Omci Msg layer could not be detected for CreateResponse")
533 return
534 }
535 msgObj, msgOk := msgLayer.(*omci.CreateResponse)
536 if !msgOk {
537 logger.Error("Omci Msg layer could not be assigned for CreateResponse")
538 return
539 }
540 logger.Debugw("CreateResponse Data", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID, "data-fields": msgObj})
541 if msgObj.Result != me.Success {
542 logger.Errorw("Omci CreateResponse Error - later: drive FSM to abort state ?", log.Fields{"Error": msgObj.Result})
543 // possibly force FSM into abort or ignore some errors for some messages? store error for mgmt display?
544 return
545 }
546 if msgObj.EntityClass == oFsm.pOmciCC.pLastTxMeInstance.GetClassID() &&
547 msgObj.EntityInstance == oFsm.pOmciCC.pLastTxMeInstance.GetEntityID() {
548 // maybe we can use just the same eventName for different state transitions like "forward"
549 // - might be checked, but so far I go for sure and have to inspect the concrete state events ...
550 switch oFsm.pOmciCC.pLastTxMeInstance.GetName() {
551 case "Ieee8021PMapperServiceProfile":
552 { // let the FSM proceed ...
553 _ = oFsm.pAdaptFsm.pFsm.Event(aniEvRxDot1pmapCResp)
554 }
555 case "MacBridgePortConfigurationData":
556 { // let the FSM proceed ...
557 _ = oFsm.pAdaptFsm.pFsm.Event(aniEvRxMbpcdResp)
558 }
559 case "GemPortNetworkCtp", "GemInterworkingTerminationPoint":
560 { // let aniConfig Multi-Id processing proceed by stopping the wait function
561 oFsm.omciMIdsResponseReceived <- true
562 }
563 }
564 }
565}
566
567func (oFsm *uniPonAniConfigFsm) handleOmciAniConfigSetResponseMessage(msg OmciMessage) {
568 msgLayer := (*msg.OmciPacket).Layer(omci.LayerTypeSetResponse)
569 if msgLayer == nil {
570 logger.Error("UniPonAniConfigFsm - Omci Msg layer could not be detected for SetResponse")
571 return
572 }
573 msgObj, msgOk := msgLayer.(*omci.SetResponse)
574 if !msgOk {
575 logger.Error("UniPonAniConfigFsm - Omci Msg layer could not be assigned for SetResponse")
576 return
577 }
578 logger.Debugw("UniPonAniConfigFsm SetResponse Data", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID, "data-fields": msgObj})
579 if msgObj.Result != me.Success {
580 logger.Errorw("UniPonAniConfigFsm - Omci SetResponse Error - later: drive FSM to abort state ?", log.Fields{"Error": msgObj.Result})
581 // possibly force FSM into abort or ignore some errors for some messages? store error for mgmt display?
582 return
583 }
584 if msgObj.EntityClass == oFsm.pOmciCC.pLastTxMeInstance.GetClassID() &&
585 msgObj.EntityInstance == oFsm.pOmciCC.pLastTxMeInstance.GetEntityID() {
586 //store the created ME into DB //TODO??? obviously the Python code does not store the config ...
587 // if, then something like:
588 //oFsm.pOnuDB.StoreMe(msgObj)
589
590 switch oFsm.pOmciCC.pLastTxMeInstance.GetName() {
591 case "TCont":
592 { // let the FSM proceed ...
593 _ = oFsm.pAdaptFsm.pFsm.Event(aniEvRxTcontsResp)
594 }
595 case "PriorityQueue":
596 { // let the PrioQueue init proceed by stopping the wait function
597 oFsm.omciMIdsResponseReceived <- true
598 }
599 case "Ieee8021PMapperServiceProfile":
600 { // let the FSM proceed ...
601 _ = oFsm.pAdaptFsm.pFsm.Event(aniEvRxDot1pmapSResp)
602 }
603 }
604 }
605}
606
607func (oFsm *uniPonAniConfigFsm) handleOmciAniConfigMessage(msg OmciMessage) {
608 logger.Debugw("Rx OMCI UniPonAniConfigFsm Msg", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID,
609 "msgType": msg.OmciMsg.MessageType})
610
611 switch msg.OmciMsg.MessageType {
612 case omci.CreateResponseType:
613 {
614 oFsm.handleOmciAniConfigCreateResponseMessage(msg)
615
616 } //CreateResponseType
617 case omci.SetResponseType:
618 {
619 oFsm.handleOmciAniConfigSetResponseMessage(msg)
620
621 } //SetResponseType
622 default:
623 {
624 logger.Errorw("uniPonAniConfigFsm - Rx OMCI unhandled MsgType", log.Fields{"omciMsgType": msg.OmciMsg.MessageType})
625 return
626 }
627 }
628}
629
630func (oFsm *uniPonAniConfigFsm) performCreatingGemNCTPs() {
631 for gemIndex, gemPortAttribs := range oFsm.gemPortAttribsSlice {
632 logger.Debugw("uniPonAniConfigFsm Tx Create::GemNWCtp", log.Fields{
633 "EntitytId": strconv.FormatInt(int64(gemPortAttribs.gemPortID), 16),
634 "TcontId": strconv.FormatInt(int64(oFsm.tcont0ID), 16),
635 "device-id": oFsm.pAdaptFsm.deviceID})
636 meParams := me.ParamData{
637 EntityID: gemPortAttribs.gemPortID, //unique, same as PortId
638 Attributes: me.AttributeValueMap{
639 "PortId": gemPortAttribs.gemPortID,
640 "TContPointer": oFsm.tcont0ID,
641 "Direction": gemPortAttribs.direction,
642 //ONU-G.TrafficManagementOption dependency ->PrioQueue or TCont
643 // TODO!! verify dependency and QueueId in case of Multi-GemPort setup!
644 "TrafficManagementPointerForUpstream": gemPortAttribs.upQueueID, //might be different in wrr-only Setup - tcont0ID
645 "PriorityQueuePointerForDownStream": gemPortAttribs.downQueueID,
646 },
647 }
648 meInstance := oFsm.pOmciCC.sendCreateGemNCTPVar(context.TODO(), ConstDefaultOmciTimeout, true,
649 oFsm.pAdaptFsm.commChan, meParams)
650 //accept also nil as (error) return value for writing to LastTx
651 // - this avoids misinterpretation of new received OMCI messages
652 oFsm.pOmciCC.pLastTxMeInstance = meInstance
653
654 //verify response
655 err := oFsm.waitforOmciResponse()
656 if err != nil {
657 logger.Errorw("GemNWCtp create failed, aborting AniConfig FSM!",
658 log.Fields{"device-id": oFsm.pAdaptFsm.deviceID, "GemIndex": gemIndex})
659 _ = oFsm.pAdaptFsm.pFsm.Event(aniEvReset)
660 return
661 }
662 } //for all GemPorts of this T-Cont
663
664 logger.Debugw("GemNWCtp create loop finished", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
665 _ = oFsm.pAdaptFsm.pFsm.Event(aniEvRxGemntcpsResp)
666}
667
668func (oFsm *uniPonAniConfigFsm) performCreatingGemIWs() {
669 for gemIndex, gemPortAttribs := range oFsm.gemPortAttribsSlice {
670 logger.Debugw("uniPonAniConfigFsm Tx Create::GemIwTp", log.Fields{
671 "EntitytId": strconv.FormatInt(int64(gemPortAttribs.gemPortID), 16),
672 "SPPtr": strconv.FormatInt(int64(oFsm.mapperSP0ID), 16),
673 "device-id": oFsm.pAdaptFsm.deviceID})
674 meParams := me.ParamData{
675 EntityID: gemPortAttribs.gemPortID,
676 Attributes: me.AttributeValueMap{
677 "GemPortNetworkCtpConnectivityPointer": gemPortAttribs.gemPortID, //same as EntityID, see above
678 "InterworkingOption": 5, //fixed model:: G.998 .1pMapper
679 "ServiceProfilePointer": oFsm.mapperSP0ID,
680 "InterworkingTerminationPointPointer": 0, //not used with .1PMapper Mac bridge
681 "GalProfilePointer": galEthernetEID,
682 },
683 }
684 meInstance := oFsm.pOmciCC.sendCreateGemIWTPVar(context.TODO(), ConstDefaultOmciTimeout, true,
685 oFsm.pAdaptFsm.commChan, meParams)
686 //accept also nil as (error) return value for writing to LastTx
687 // - this avoids misinterpretation of new received OMCI messages
688 oFsm.pOmciCC.pLastTxMeInstance = meInstance
689
690 //verify response
691 err := oFsm.waitforOmciResponse()
692 if err != nil {
693 logger.Errorw("GemIwTp create failed, aborting AniConfig FSM!",
694 log.Fields{"device-id": oFsm.pAdaptFsm.deviceID, "GemIndex": gemIndex})
695 _ = oFsm.pAdaptFsm.pFsm.Event(aniEvReset)
696 return
697 }
698 } //for all GemPort's of this T-Cont
699
700 logger.Debugw("GemIwTp create loop finished", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
701 _ = oFsm.pAdaptFsm.pFsm.Event(aniEvRxGemiwsResp)
702}
703
704func (oFsm *uniPonAniConfigFsm) performSettingPQs() {
705 const cu16StrictPrioWeight uint16 = 0xFFFF
706 loQueueMap := ordered_map.NewOrderedMap()
707 for _, gemPortAttribs := range oFsm.gemPortAttribsSlice {
708 if gemPortAttribs.qosPolicy == "WRR" {
709 if _, ok := loQueueMap.Get(gemPortAttribs.upQueueID); !ok {
710 //key does not yet exist
711 loQueueMap.Set(gemPortAttribs.upQueueID, uint16(gemPortAttribs.weight))
712 }
713 } else {
714 loQueueMap.Set(gemPortAttribs.upQueueID, cu16StrictPrioWeight) //use invalid weight value to indicate SP
715 }
716 }
717
718
719 loTrafficSchedulerEID := 0x8000
720 iter := loQueueMap.IterFunc()
721 for kv, ok := iter(); ok; kv, ok = iter() {
722 queueIndex := (kv.Key).(uint16)
723 meParams := me.ParamData{
724 EntityID: queueIndex,
725 Attributes: make(me.AttributeValueMap),
726 }
727 if (kv.Value).(uint16) == cu16StrictPrioWeight {
728 //StrictPrio indication
729 logger.Debugw("uniPonAniConfigFsm Tx Set::PrioQueue to StrictPrio", log.Fields{
730 "EntitytId": strconv.FormatInt(int64(queueIndex), 16),
731 "device-id": oFsm.pAdaptFsm.deviceID})
732 meParams.Attributes["TrafficSchedulerPointer"] = 0 //ensure T-Cont defined StrictPrio scheduling
733 } else {
734 //WRR indication
735 logger.Debugw("uniPonAniConfigFsm Tx Set::PrioQueue to WRR", log.Fields{
736 "EntitytId": strconv.FormatInt(int64(queueIndex), 16),
737 "Weight": kv.Value,
738 "device-id": oFsm.pAdaptFsm.deviceID})
739 meParams.Attributes["TrafficSchedulerPointer"] = loTrafficSchedulerEID //ensure assignment of the relevant trafficScheduler
740 meParams.Attributes["Weight"] = uint8(kv.Value.(uint16))
741 }
742 meInstance := oFsm.pOmciCC.sendSetPrioQueueVar(context.TODO(), ConstDefaultOmciTimeout, true,
743 oFsm.pAdaptFsm.commChan, meParams)
744 //accept also nil as (error) return value for writing to LastTx
745 // - this avoids misinterpretation of new received OMCI messages
746 oFsm.pOmciCC.pLastTxMeInstance = meInstance
747
748 //verify response
749 err := oFsm.waitforOmciResponse()
750 if err != nil {
751 logger.Errorw("PrioQueue set failed, aborting AniConfig FSM!",
752 log.Fields{"device-id": oFsm.pAdaptFsm.deviceID, "QueueId": strconv.FormatInt(int64(queueIndex), 16)})
753 _ = oFsm.pAdaptFsm.pFsm.Event(aniEvReset)
754 return
755 }
756
757 //TODO: In case of WRR setting of the GemPort/PrioQueue it might further be necessary to
758 // write the assigned trafficScheduler with the requested Prio to be considered in the StrictPrio scheduling
759 // of the (next upstream) assigned T-Cont, which is f(prioQueue[priority]) - in relation to other SP prioQueues
760 // not yet done because of BBSIM TrafficScheduler issues (and not done in py code as well)
761
762 } //for all upstream prioQueues
763
764 logger.Debugw("PrioQueue set loop finished", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
765 _ = oFsm.pAdaptFsm.pFsm.Event(aniEvRxPrioqsResp)
766}
767
768func (oFsm *uniPonAniConfigFsm) waitforOmciResponse() error {
769 select {
770 case <-time.After(30 * time.Second): //3s was detected to be to less in 8*8 bbsim test with debug Info/Debug
771 logger.Warnw("UniPonAniConfigFsm multi entity timeout", log.Fields{"for device-id": oFsm.pAdaptFsm.deviceID})
772 return errors.New("uniPonAniConfigFsm multi entity timeout")
773 case success := <-oFsm.omciMIdsResponseReceived:
774 if success {
775 logger.Debug("uniPonAniConfigFsm multi entity response received")
776 return nil
777 }
778 // should not happen so far
779 logger.Warnw("uniPonAniConfigFsm multi entity response error", log.Fields{"for device-id": oFsm.pAdaptFsm.deviceID})
780 return errors.New("uniPonAniConfigFsm multi entity responseError")
781 }
782}