[VOL-3380] Functional area specific logging
Change-Id: I67414da013d8fc82827fcdb69d4f8a34040625d3
diff --git a/internal/pkg/avcfg/omci_ani_config.go b/internal/pkg/avcfg/omci_ani_config.go
new file mode 100755
index 0000000..6ade746
--- /dev/null
+++ b/internal/pkg/avcfg/omci_ani_config.go
@@ -0,0 +1,1819 @@
+/*
+ * Copyright 2020-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//Package avcfg provides anig and vlan configuration functionality
+package avcfg
+
+import (
+ "context"
+ "encoding/binary"
+ "fmt"
+ "net"
+ "strconv"
+ "sync"
+ "time"
+
+ "github.com/cevaris/ordered_map"
+ "github.com/looplab/fsm"
+ "github.com/opencord/omci-lib-go"
+ me "github.com/opencord/omci-lib-go/generated"
+ "github.com/opencord/voltha-lib-go/v7/pkg/log"
+
+ //ic "github.com/opencord/voltha-protos/v5/go/inter_container"
+ //"github.com/opencord/voltha-protos/v5/go/openflow_13"
+ //"github.com/opencord/voltha-protos/v5/go/voltha"
+ cmn "github.com/opencord/voltha-openonu-adapter-go/internal/pkg/common"
+ "github.com/opencord/voltha-openonu-adapter-go/internal/pkg/devdb"
+)
+
+const (
+ // events of config PON ANI port FSM
+ aniEvStart = "aniEvStart"
+ aniEvStartConfig = "aniEvStartConfig"
+ aniEvRxDot1pmapCResp = "aniEvRxDot1pmapCResp"
+ aniEvRxMbpcdResp = "aniEvRxMbpcdResp"
+ aniEvRxTcontsResp = "aniEvRxTcontsResp"
+ aniEvRxGemntcpsResp = "aniEvRxGemntcpsResp"
+ aniEvRxGemiwsResp = "aniEvRxGemiwsResp"
+ aniEvRxPrioqsResp = "aniEvRxPrioqsResp"
+ aniEvRxDot1pmapSResp = "aniEvRxDot1pmapSResp"
+ aniEvRemGemiw = "aniEvRemGemiw"
+ aniEvWaitFlowRem = "aniEvWaitFlowRem"
+ aniEvFlowRemDone = "aniEvFlowRemDone"
+ aniEvRxRemGemiwResp = "aniEvRxRemGemiwResp"
+ aniEvRxRemGemntpResp = "aniEvRxRemGemntpResp"
+ aniEvRxRemTdResp = "aniEvRxRemTdResp"
+ aniEvRemTcontPath = "aniEvRemTcontPath"
+ aniEvRxResetTcontResp = "aniEvRxResetTcontResp"
+ aniEvRxRem1pMapperResp = "aniEvRxRem1pMapperResp"
+ aniEvRxRemAniBPCDResp = "aniEvRxRemAniBPCDResp"
+ aniEvTimeoutSimple = "aniEvTimeoutSimple"
+ aniEvTimeoutMids = "aniEvTimeoutMids"
+ aniEvReset = "aniEvReset"
+ aniEvRestart = "aniEvRestart"
+ aniEvSkipOmciConfig = "aniEvSkipOmciConfig"
+ aniEvRemGemDone = "aniEvRemGemDone"
+)
+const (
+ // states of config PON ANI port FSM
+ aniStDisabled = "aniStDisabled"
+ aniStStarting = "aniStStarting"
+ aniStCreatingDot1PMapper = "aniStCreatingDot1PMapper"
+ aniStCreatingMBPCD = "aniStCreatingMBPCD"
+ aniStSettingTconts = "aniStSettingTconts"
+ aniStCreatingGemNCTPs = "aniStCreatingGemNCTPs"
+ aniStCreatingGemIWs = "aniStCreatingGemIWs"
+ aniStSettingPQs = "aniStSettingPQs"
+ aniStSettingDot1PMapper = "aniStSettingDot1PMapper"
+ aniStConfigDone = "aniStConfigDone"
+ aniStRemovingGemIW = "aniStRemovingGemIW"
+ aniStWaitingFlowRem = "aniStWaitingFlowRem"
+ aniStRemovingGemNCTP = "aniStRemovingGemNCTP"
+ aniStRemovingTD = "aniStRemovingTD"
+ aniStResetTcont = "aniStResetTcont"
+ aniStRemDot1PMapper = "aniStRemDot1PMapper"
+ aniStRemAniBPCD = "aniStRemAniBPCD"
+ aniStRemoveDone = "aniStRemoveDone"
+ aniStResetting = "aniStResetting"
+)
+
+const (
+ bitTrafficSchedulerPtrSetPermitted = 0x0002 // Refer section 9.1.2 ONU-2G, table for "Quality of service (QoS) configuration flexibility" IE
+)
+
+// CAniFsmIdleState - TODO: add comment
+const CAniFsmIdleState = aniStConfigDone
+
+type ponAniGemPortAttribs struct {
+ gemPortID uint16
+ upQueueID uint16
+ downQueueID uint16
+ direction uint8
+ qosPolicy string
+ weight uint8
+ pbitString string
+ isMulticast bool
+ multicastGemID uint16
+ staticACL string
+ dynamicACL string
+}
+
+//UniPonAniConfigFsm defines the structure for the state machine to config the PON ANI ports of ONU UNI ports via OMCI
+type UniPonAniConfigFsm struct {
+ deviceID string
+ pDeviceHandler cmn.IdeviceHandler
+ pOnuDeviceEntry cmn.IonuDeviceEntry
+ pOmciCC *cmn.OmciCC
+ pOnuUniPort *cmn.OnuUniPort
+ pUniTechProf *OnuUniTechProf
+ pOnuDB *devdb.OnuDeviceDB
+ techProfileID uint8
+ uniTpKey uniTP
+ requestEvent cmn.OnuDeviceEvent
+ mutexIsAwaitingResponse sync.RWMutex
+ isCanceled bool
+ isAwaitingResponse bool
+ omciMIdsResponseReceived chan bool //separate channel needed for checking multiInstance OMCI message responses
+ PAdaptFsm *cmn.AdapterFsm
+ chSuccess chan<- uint8
+ procStep uint8
+ mutexChanSet sync.RWMutex
+ chanSet bool
+ mapperSP0ID uint16
+ macBPCD0ID uint16
+ tcont0ID uint16
+ alloc0ID uint16
+ gemPortAttribsSlice []ponAniGemPortAttribs
+ mutexPLastTxMeInstance sync.RWMutex
+ pLastTxMeInstance *me.ManagedEntity
+ requestEventOffset uint8 //used to indicate ConfigDone or Removed using successor (enum)
+ isWaitingForFlowDelete bool
+ waitFlowDeleteChannel chan bool
+ tcontSetBefore bool
+}
+
+//NewUniPonAniConfigFsm is the 'constructor' for the state machine to config the PON ANI ports of ONU UNI ports via OMCI
+func NewUniPonAniConfigFsm(ctx context.Context, apDevOmciCC *cmn.OmciCC, apUniPort *cmn.OnuUniPort, apUniTechProf *OnuUniTechProf,
+ apOnuDB *devdb.OnuDeviceDB, aTechProfileID uint8, aRequestEvent cmn.OnuDeviceEvent, aName string,
+ apDeviceHandler cmn.IdeviceHandler, apOnuDeviceEntry cmn.IonuDeviceEntry, aCommChannel chan cmn.Message) *UniPonAniConfigFsm {
+ instFsm := &UniPonAniConfigFsm{
+ pDeviceHandler: apDeviceHandler,
+ pOnuDeviceEntry: apOnuDeviceEntry,
+ deviceID: apDeviceHandler.GetDeviceID(),
+ pOmciCC: apDevOmciCC,
+ pOnuUniPort: apUniPort,
+ pUniTechProf: apUniTechProf,
+ pOnuDB: apOnuDB,
+ techProfileID: aTechProfileID,
+ requestEvent: aRequestEvent,
+ chanSet: false,
+ tcontSetBefore: false,
+ }
+ instFsm.uniTpKey = uniTP{uniID: apUniPort.UniID, tpID: aTechProfileID}
+ instFsm.waitFlowDeleteChannel = make(chan bool)
+
+ instFsm.PAdaptFsm = cmn.NewAdapterFsm(aName, instFsm.deviceID, aCommChannel)
+ if instFsm.PAdaptFsm == nil {
+ logger.Errorw(ctx, "UniPonAniConfigFsm's cmn.AdapterFsm could not be instantiated!!", log.Fields{
+ "device-id": instFsm.deviceID})
+ return nil
+ }
+
+ instFsm.PAdaptFsm.PFsm = fsm.NewFSM(
+ aniStDisabled,
+ fsm.Events{
+
+ {Name: aniEvStart, Src: []string{aniStDisabled}, Dst: aniStStarting},
+
+ //Note: .1p-Mapper and MBPCD might also have multi instances (per T-Cont) - by now only one 1 T-Cont considered!
+ {Name: aniEvStartConfig, Src: []string{aniStStarting}, Dst: aniStCreatingDot1PMapper},
+ {Name: aniEvRxDot1pmapCResp, Src: []string{aniStCreatingDot1PMapper}, Dst: aniStCreatingMBPCD},
+ {Name: aniEvRxMbpcdResp, Src: []string{aniStCreatingMBPCD}, Dst: aniStSettingTconts},
+ {Name: aniEvRxTcontsResp, Src: []string{aniStSettingTconts}, Dst: aniStCreatingGemNCTPs},
+ // the creatingGemNCTPs state is used for multi ME config if required for all configured/available GemPorts
+ {Name: aniEvRxGemntcpsResp, Src: []string{aniStCreatingGemNCTPs}, Dst: aniStCreatingGemIWs},
+ // the creatingGemIWs state is used for multi ME config if required for all configured/available GemPorts
+ {Name: aniEvRxGemiwsResp, Src: []string{aniStCreatingGemIWs}, Dst: aniStSettingPQs},
+ // the settingPQs state is used for multi ME config if required for all configured/available upstream PriorityQueues
+ {Name: aniEvRxPrioqsResp, Src: []string{aniStSettingPQs}, Dst: aniStSettingDot1PMapper},
+ {Name: aniEvRxDot1pmapSResp, Src: []string{aniStSettingDot1PMapper}, Dst: aniStConfigDone},
+
+ //for removing Gem related resources
+ {Name: aniEvRemGemiw, Src: []string{aniStConfigDone}, Dst: aniStRemovingGemIW},
+ {Name: aniEvWaitFlowRem, Src: []string{aniStRemovingGemIW}, Dst: aniStWaitingFlowRem},
+ {Name: aniEvFlowRemDone, Src: []string{aniStWaitingFlowRem}, Dst: aniStRemovingGemIW},
+ {Name: aniEvRxRemGemiwResp, Src: []string{aniStRemovingGemIW}, Dst: aniStRemovingGemNCTP},
+ {Name: aniEvRxRemGemntpResp, Src: []string{aniStRemovingGemNCTP}, Dst: aniStRemovingTD},
+ {Name: aniEvRxRemTdResp, Src: []string{aniStRemovingTD}, Dst: aniStRemDot1PMapper},
+ {Name: aniEvRemGemDone, Src: []string{aniStRemDot1PMapper}, Dst: aniStConfigDone},
+ {Name: aniEvRxRem1pMapperResp, Src: []string{aniStRemDot1PMapper}, Dst: aniStRemAniBPCD},
+ {Name: aniEvRxRemAniBPCDResp, Src: []string{aniStRemAniBPCD}, Dst: aniStRemoveDone},
+
+ //for removing TCONT related resources
+ {Name: aniEvRemTcontPath, Src: []string{aniStConfigDone}, Dst: aniStResetTcont},
+ {Name: aniEvRxResetTcontResp, Src: []string{aniStResetTcont}, Dst: aniStConfigDone},
+
+ {Name: aniEvTimeoutSimple, Src: []string{aniStCreatingDot1PMapper, aniStCreatingMBPCD, aniStSettingTconts, aniStSettingDot1PMapper,
+ aniStRemovingGemIW, aniStRemovingGemNCTP, aniStRemovingTD,
+ aniStResetTcont, aniStRemDot1PMapper, aniStRemAniBPCD, aniStRemoveDone}, Dst: aniStStarting},
+ {Name: aniEvTimeoutMids, Src: []string{
+ aniStCreatingGemNCTPs, aniStCreatingGemIWs, aniStSettingPQs}, Dst: aniStStarting},
+
+ // exceptional treatment for all states except aniStResetting
+ {Name: aniEvReset, Src: []string{aniStStarting, aniStCreatingDot1PMapper, aniStCreatingMBPCD,
+ aniStSettingTconts, aniStCreatingGemNCTPs, aniStCreatingGemIWs, aniStSettingPQs, aniStSettingDot1PMapper,
+ aniStConfigDone, aniStRemovingGemIW, aniStWaitingFlowRem, aniStRemovingGemNCTP, aniStRemovingTD,
+ aniStResetTcont, aniStRemDot1PMapper, aniStRemAniBPCD, aniStRemoveDone}, Dst: aniStResetting},
+ // the only way to get to resource-cleared disabled state again is via "resseting"
+ {Name: aniEvRestart, Src: []string{aniStResetting}, Dst: aniStDisabled},
+ {Name: aniEvSkipOmciConfig, Src: []string{aniStStarting}, Dst: aniStConfigDone},
+ },
+
+ fsm.Callbacks{
+ "enter_state": func(e *fsm.Event) { instFsm.PAdaptFsm.LogFsmStateChange(ctx, e) },
+ ("enter_" + aniStStarting): func(e *fsm.Event) { instFsm.enterConfigStartingState(ctx, e) },
+ ("enter_" + aniStCreatingDot1PMapper): func(e *fsm.Event) { instFsm.enterCreatingDot1PMapper(ctx, e) },
+ ("enter_" + aniStCreatingMBPCD): func(e *fsm.Event) { instFsm.enterCreatingMBPCD(ctx, e) },
+ ("enter_" + aniStSettingTconts): func(e *fsm.Event) { instFsm.enterSettingTconts(ctx, e) },
+ ("enter_" + aniStCreatingGemNCTPs): func(e *fsm.Event) { instFsm.enterCreatingGemNCTPs(ctx, e) },
+ ("enter_" + aniStCreatingGemIWs): func(e *fsm.Event) { instFsm.enterCreatingGemIWs(ctx, e) },
+ ("enter_" + aniStSettingPQs): func(e *fsm.Event) { instFsm.enterSettingPQs(ctx, e) },
+ ("enter_" + aniStSettingDot1PMapper): func(e *fsm.Event) { instFsm.enterSettingDot1PMapper(ctx, e) },
+ ("enter_" + aniStConfigDone): func(e *fsm.Event) { instFsm.enterAniConfigDone(ctx, e) },
+ ("enter_" + aniStRemovingGemIW): func(e *fsm.Event) { instFsm.enterRemovingGemIW(ctx, e) },
+ ("enter_" + aniStWaitingFlowRem): func(e *fsm.Event) { instFsm.enterWaitingFlowRem(ctx, e) },
+ ("enter_" + aniStRemovingGemNCTP): func(e *fsm.Event) { instFsm.enterRemovingGemNCTP(ctx, e) },
+ ("enter_" + aniStRemovingTD): func(e *fsm.Event) { instFsm.enterRemovingTD(ctx, e) },
+ ("enter_" + aniStResetTcont): func(e *fsm.Event) { instFsm.enterResettingTcont(ctx, e) },
+ ("enter_" + aniStRemDot1PMapper): func(e *fsm.Event) { instFsm.enterRemoving1pMapper(ctx, e) },
+ ("enter_" + aniStRemAniBPCD): func(e *fsm.Event) { instFsm.enterRemovingAniBPCD(ctx, e) },
+ ("enter_" + aniStRemoveDone): func(e *fsm.Event) { instFsm.enterAniRemoveDone(ctx, e) },
+ ("enter_" + aniStResetting): func(e *fsm.Event) { instFsm.enterResettingState(ctx, e) },
+ ("enter_" + aniStDisabled): func(e *fsm.Event) { instFsm.enterDisabledState(ctx, e) },
+ },
+ )
+ if instFsm.PAdaptFsm.PFsm == nil {
+ logger.Errorw(ctx, "UniPonAniConfigFsm's Base FSM could not be instantiated!!", log.Fields{
+ "device-id": instFsm.deviceID})
+ return nil
+ }
+
+ logger.Debugw(ctx, "UniPonAniConfigFsm created", log.Fields{"device-id": instFsm.deviceID})
+ return instFsm
+}
+
+//setFsmCompleteChannel sets the requested channel and channel result for transfer on success
+func (oFsm *UniPonAniConfigFsm) setFsmCompleteChannel(aChSuccess chan<- uint8, aProcStep uint8) {
+ oFsm.chSuccess = aChSuccess
+ oFsm.procStep = aProcStep
+ oFsm.setChanSet(true)
+}
+
+//CancelProcessing ensures that suspended processing at waiting on some response is aborted and reset of FSM
+func (oFsm *UniPonAniConfigFsm) CancelProcessing(ctx context.Context) {
+ //early indication about started reset processing
+ oFsm.pUniTechProf.setProfileResetting(ctx, oFsm.pOnuUniPort.UniID, oFsm.techProfileID, true)
+ //mutex protection is required for possible concurrent access to FSM members
+ oFsm.mutexIsAwaitingResponse.Lock()
+ oFsm.isCanceled = true
+ if oFsm.isAwaitingResponse {
+ //attention: for an unbuffered channel the sender is blocked until the value is received (processed)!
+ // accordingly the mutex must be released before sending to channel here (mutex acquired in receiver)
+ oFsm.mutexIsAwaitingResponse.Unlock()
+ //use channel to indicate that the response waiting shall be aborted
+ oFsm.omciMIdsResponseReceived <- false
+ } else {
+ oFsm.mutexIsAwaitingResponse.Unlock()
+ }
+
+ oFsm.mutexIsAwaitingResponse.Lock()
+ if oFsm.isWaitingForFlowDelete {
+ oFsm.mutexIsAwaitingResponse.Unlock()
+ //use channel to indicate that the response waiting shall be aborted
+ oFsm.waitFlowDeleteChannel <- false
+ } else {
+ oFsm.mutexIsAwaitingResponse.Unlock()
+ }
+
+ // in any case (even if it might be automatically requested by above cancellation of waiting) ensure resetting the FSM
+ PAdaptFsm := oFsm.PAdaptFsm
+ if PAdaptFsm != nil {
+ // obviously calling some FSM event here directly does not work - so trying to decouple it ...
+ go func(aPAFsm *cmn.AdapterFsm) {
+ if aPAFsm.PFsm != nil {
+ _ = oFsm.PAdaptFsm.PFsm.Event(aniEvReset)
+ }
+ }(PAdaptFsm)
+ }
+
+ // possible access conflicts on internal data by next needed data clearance
+ // are avoided by using mutexTPState also from within clearAniSideConfig
+ // do not try to lock TpProcMutex here as done in previous code version
+ // as it may result in deadlock situations (as observed at soft-reboot handling where
+ // TpProcMutex is already locked by some ongoing TechProfile config/removal processing
+ //remove all TechProf related internal data to allow for new configuration
+ oFsm.pUniTechProf.clearAniSideConfig(ctx, oFsm.pOnuUniPort.UniID, oFsm.techProfileID)
+}
+
+//nolint: gocyclo
+//TODO:visit here for refactoring for gocyclo
+func (oFsm *UniPonAniConfigFsm) prepareAndEnterConfigState(ctx context.Context, aPAFsm *cmn.AdapterFsm) {
+ if aPAFsm != nil && aPAFsm.PFsm != nil {
+ var err error
+ oFsm.mapperSP0ID, err = cmn.GenerateIeeMaperServiceProfileEID(uint16(oFsm.pOnuUniPort.MacBpNo), uint16(oFsm.techProfileID))
+ if err != nil {
+ logger.Errorw(ctx, "error generating maper id", log.Fields{"device-id": oFsm.deviceID,
+ "techProfileID": oFsm.techProfileID, "error": err})
+ return
+ }
+ oFsm.macBPCD0ID, err = cmn.GenerateANISideMBPCDEID(uint16(oFsm.pOnuUniPort.MacBpNo), uint16(oFsm.techProfileID))
+ if err != nil {
+ logger.Errorw(ctx, "error generating mbpcd id", log.Fields{"device-id": oFsm.deviceID,
+ "techProfileID": oFsm.techProfileID, "error": err})
+ return
+ }
+ logger.Debugw(ctx, "generated ids for ani config", log.Fields{"mapperSP0ID": strconv.FormatInt(int64(oFsm.mapperSP0ID), 16),
+ "macBPCD0ID": strconv.FormatInt(int64(oFsm.macBPCD0ID), 16), "device-id": oFsm.deviceID,
+ "macBpNo": oFsm.pOnuUniPort.MacBpNo, "techProfileID": oFsm.techProfileID})
+ if oFsm.pOnuDeviceEntry == nil {
+ logger.Errorw(ctx, "No valid OnuDevice - aborting", log.Fields{"device-id": oFsm.deviceID})
+ return
+ }
+ tcontInstID, tcontAlreadyExist, err := oFsm.pOnuDeviceEntry.AllocateFreeTcont(ctx, oFsm.pUniTechProf.mapPonAniConfig[oFsm.uniTpKey].tcontParams.allocID)
+ if err != nil {
+ logger.Errorw(ctx, "No TCont instances found", log.Fields{"device-id": oFsm.deviceID, "err": err})
+ //reset the state machine to enable usage on subsequent requests
+ _ = aPAFsm.PFsm.Event(aniEvReset)
+ return
+ }
+ oFsm.tcont0ID = tcontInstID
+ oFsm.tcontSetBefore = tcontAlreadyExist
+ logger.Debugw(ctx, "used-tcont-instance-id", log.Fields{"tcont-inst-id": oFsm.tcont0ID,
+ "alloc-id": oFsm.pUniTechProf.mapPonAniConfig[oFsm.uniTpKey].tcontParams.allocID,
+ "tcontAlreadyExist": tcontAlreadyExist,
+ "device-id": oFsm.deviceID})
+
+ // Access critical state with lock
+ oFsm.pUniTechProf.mutexTPState.RLock()
+ oFsm.alloc0ID = oFsm.pUniTechProf.mapPonAniConfig[oFsm.uniTpKey].tcontParams.allocID
+ mapGemPortParams := oFsm.pUniTechProf.mapPonAniConfig[oFsm.uniTpKey].mapGemPortParams
+ oFsm.pUniTechProf.mutexTPState.RUnlock()
+
+ //for all TechProfile set GemIndices
+ for _, gemEntry := range mapGemPortParams {
+ loGemPortAttribs := ponAniGemPortAttribs{}
+
+ //collect all GemConfigData in a separate Fsm related slice (needed also to avoid mix-up with unsorted mapPonAniConfig)
+
+ if queueInstKeys := oFsm.pOnuDB.GetSortedInstKeys(ctx, me.PriorityQueueClassID); len(queueInstKeys) > 0 {
+
+ loGemPortAttribs.gemPortID = gemEntry.gemPortID
+ // MibDb usage: upstream PrioQueue.RelatedPort = xxxxyyyy with xxxx=TCont.Entity(incl. slot) and yyyy=prio
+ // i.e.: search PrioQueue list with xxxx=actual T-Cont.Entity,
+ // from that list use the PrioQueue.Entity with gemEntry.prioQueueIndex == yyyy (expect 0..7)
+ usQrelPortMask := uint32((((uint32)(oFsm.tcont0ID)) << 16) + uint32(gemEntry.prioQueueIndex))
+
+ // MibDb usage: downstream PrioQueue.RelatedPort = xxyyzzzz with xx=slot, yy=UniPort and zzzz=prio
+ // i.e.: search PrioQueue list with yy=actual pOnuUniPort.UniID,
+ // from that list use the PrioQueue.Entity with gemEntry.prioQueueIndex == zzzz (expect 0..7)
+ // Note: As we do not maintain any slot numbering, slot number will be excluded from seatch pattern.
+ // Furthermore OMCI Onu port-Id is expected to start with 1 (not 0).
+ dsQrelPortMask := uint32((((uint32)(oFsm.pOnuUniPort.UniID + 1)) << 16) + uint32(gemEntry.prioQueueIndex))
+
+ usQueueFound := false
+ dsQueueFound := false
+ for _, mgmtEntityID := range queueInstKeys {
+ if meAttributes := oFsm.pOnuDB.GetMe(me.PriorityQueueClassID, mgmtEntityID); meAttributes != nil {
+ returnVal := meAttributes["RelatedPort"]
+ if returnVal != nil {
+ if relatedPort, err := oFsm.pOnuDB.GetUint32Attrib(returnVal); err == nil {
+ if relatedPort == usQrelPortMask {
+ loGemPortAttribs.upQueueID = mgmtEntityID
+ logger.Debugw(ctx, "UpQueue for GemPort found:", log.Fields{"gemPortID": loGemPortAttribs.gemPortID,
+ "upQueueID": strconv.FormatInt(int64(loGemPortAttribs.upQueueID), 16), "device-id": oFsm.deviceID})
+ usQueueFound = true
+ } else if (relatedPort&0xFFFFFF) == dsQrelPortMask && mgmtEntityID < 0x8000 {
+ loGemPortAttribs.downQueueID = mgmtEntityID
+ logger.Debugw(ctx, "DownQueue for GemPort found:", log.Fields{"gemPortID": loGemPortAttribs.gemPortID,
+ "downQueueID": strconv.FormatInt(int64(loGemPortAttribs.downQueueID), 16), "device-id": oFsm.deviceID})
+ dsQueueFound = true
+ }
+ if usQueueFound && dsQueueFound {
+ break
+ }
+ } else {
+ logger.Warnw(ctx, "Could not convert attribute value", log.Fields{"device-id": oFsm.deviceID})
+ }
+ } else {
+ logger.Warnw(ctx, "'RelatedPort' not found in meAttributes:", log.Fields{"device-id": oFsm.deviceID})
+ }
+ } else {
+ logger.Warnw(ctx, "No attributes available in DB:", log.Fields{"meClassID": me.PriorityQueueClassID,
+ "mgmtEntityID": mgmtEntityID, "device-id": oFsm.deviceID})
+ }
+ }
+ } else {
+ logger.Warnw(ctx, "No PriorityQueue instances found", log.Fields{"device-id": oFsm.deviceID})
+ }
+ loGemPortAttribs.direction = gemEntry.direction
+ loGemPortAttribs.qosPolicy = gemEntry.queueSchedPolicy
+ loGemPortAttribs.weight = gemEntry.queueWeight
+ loGemPortAttribs.pbitString = gemEntry.pbitString
+
+ if gemEntry.isMulticast {
+ //TODO this might effectively ignore the for loop starting at line 316
+ loGemPortAttribs.gemPortID = gemEntry.multicastGemPortID
+ loGemPortAttribs.isMulticast = true
+ loGemPortAttribs.multicastGemID = gemEntry.multicastGemPortID
+ loGemPortAttribs.staticACL = gemEntry.staticACL
+ loGemPortAttribs.dynamicACL = gemEntry.dynamicACL
+
+ logger.Debugw(ctx, "Multicast GemPort attributes:", log.Fields{
+ "gemPortID": loGemPortAttribs.gemPortID,
+ "isMulticast": loGemPortAttribs.isMulticast,
+ "multicastGemID": loGemPortAttribs.multicastGemID,
+ "staticACL": loGemPortAttribs.staticACL,
+ "dynamicACL": loGemPortAttribs.dynamicACL,
+ "device-id": oFsm.deviceID,
+ })
+
+ } else {
+ logger.Debugw(ctx, "Upstream GemPort attributes:", log.Fields{
+ "gemPortID": loGemPortAttribs.gemPortID,
+ "upQueueID": loGemPortAttribs.upQueueID,
+ "downQueueID": loGemPortAttribs.downQueueID,
+ "pbitString": loGemPortAttribs.pbitString,
+ "prioQueueIndex": gemEntry.prioQueueIndex,
+ "device-id": oFsm.deviceID,
+ })
+ }
+
+ oFsm.gemPortAttribsSlice = append(oFsm.gemPortAttribsSlice, loGemPortAttribs)
+ }
+ if !oFsm.pDeviceHandler.IsSkipOnuConfigReconciling() {
+ _ = aPAFsm.PFsm.Event(aniEvStartConfig)
+ } else {
+ logger.Debugw(ctx, "reconciling - skip omci-config of ANI side ", log.Fields{"device-id": oFsm.deviceID})
+ _ = aPAFsm.PFsm.Event(aniEvSkipOmciConfig)
+ }
+ }
+}
+
+func (oFsm *UniPonAniConfigFsm) enterConfigStartingState(ctx context.Context, e *fsm.Event) {
+ logger.Debugw(ctx, "UniPonAniConfigFsm start", log.Fields{
+ "device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID})
+ // in case the used channel is not yet defined (can be re-used after restarts)
+ if oFsm.omciMIdsResponseReceived == nil {
+ oFsm.omciMIdsResponseReceived = make(chan bool)
+ logger.Debug(ctx, "UniPonAniConfigFsm - OMCI multiInstance RxChannel defined")
+ } else {
+ // as we may 're-use' this instance of FSM and the connected channel
+ // make sure there is no 'lingering' request in the already existing channel:
+ // (simple loop sufficient as we are the only receiver)
+ for len(oFsm.omciMIdsResponseReceived) > 0 {
+ <-oFsm.omciMIdsResponseReceived
+ }
+ }
+ //ensure internal slices are empty (which might be set from previous run) - release memory
+ oFsm.gemPortAttribsSlice = nil
+ oFsm.mutexIsAwaitingResponse.Lock()
+ //reset the canceled state possibly existing from previous reset
+ oFsm.isCanceled = false
+ oFsm.mutexIsAwaitingResponse.Unlock()
+
+ // start go routine for processing of ANI config messages
+ go oFsm.processOmciAniMessages(ctx)
+
+ //let the state machine run forward from here directly
+ pConfigAniStateAFsm := oFsm.PAdaptFsm
+ if pConfigAniStateAFsm != nil {
+ // obviously calling some FSM event here directly does not work - so trying to decouple it ...
+ go oFsm.prepareAndEnterConfigState(ctx, pConfigAniStateAFsm)
+
+ }
+}
+
+func (oFsm *UniPonAniConfigFsm) enterCreatingDot1PMapper(ctx context.Context, e *fsm.Event) {
+ logger.Debugw(ctx, "UniPonAniConfigFsm Tx Create::Dot1PMapper", log.Fields{
+ "EntitytId": strconv.FormatInt(int64(oFsm.mapperSP0ID), 16),
+ "device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID})
+ oFsm.requestEventOffset = 0 //0 offset for last config request activity
+ oFsm.mutexPLastTxMeInstance.Lock()
+ meInstance, err := oFsm.pOmciCC.SendCreateDot1PMapper(log.WithSpanFromContext(context.TODO(), ctx), oFsm.pDeviceHandler.GetOmciTimeout(), true,
+ oFsm.mapperSP0ID, oFsm.PAdaptFsm.CommChan)
+ if err != nil {
+ logger.Errorw(ctx, "Dot1PMapper create failed, aborting UniPonAniConfigFsm!",
+ log.Fields{"device-id": oFsm.deviceID})
+ pConfigAniStateAFsm := oFsm.PAdaptFsm
+ if pConfigAniStateAFsm != nil {
+ oFsm.mutexPLastTxMeInstance.Unlock()
+ // obviously calling some FSM event here directly does not work - so trying to decouple it ...
+ go func(aPAFsm *cmn.AdapterFsm) {
+ if aPAFsm != nil && aPAFsm.PFsm != nil {
+ _ = aPAFsm.PFsm.Event(aniEvReset)
+ }
+ }(pConfigAniStateAFsm)
+ return
+ }
+ }
+ //accept also nil as (error) return value for writing to LastTx
+ // - this avoids misinterpretation of new received OMCI messages
+ oFsm.pLastTxMeInstance = meInstance
+ oFsm.mutexPLastTxMeInstance.Unlock()
+
+}
+
+func (oFsm *UniPonAniConfigFsm) enterCreatingMBPCD(ctx context.Context, e *fsm.Event) {
+ logger.Debugw(ctx, "UniPonAniConfigFsm Tx Create::MBPCD", log.Fields{
+ "EntitytId": strconv.FormatInt(int64(oFsm.macBPCD0ID), 16),
+ "TPPtr": strconv.FormatInt(int64(oFsm.mapperSP0ID), 16),
+ "device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID})
+ bridgePtr := cmn.MacBridgeServiceProfileEID + uint16(oFsm.pOnuUniPort.MacBpNo) //cmp also omci_cc.go::sendCreateMBServiceProfile
+ meParams := me.ParamData{
+ EntityID: oFsm.macBPCD0ID,
+ Attributes: me.AttributeValueMap{
+ "BridgeIdPointer": bridgePtr,
+ "PortNum": 0xFF, //fixed unique ANI side indication
+ "TpType": 3, //for .1PMapper
+ "TpPointer": oFsm.mapperSP0ID,
+ },
+ }
+ oFsm.mutexPLastTxMeInstance.Lock()
+ meInstance, err := oFsm.pOmciCC.SendCreateMBPConfigDataVar(log.WithSpanFromContext(context.TODO(), ctx), oFsm.pDeviceHandler.GetOmciTimeout(), true,
+ oFsm.PAdaptFsm.CommChan, meParams)
+ if err != nil {
+ logger.Errorw(ctx, "MBPConfigDataVar create failed, aborting UniPonAniConfigFsm!",
+ log.Fields{"device-id": oFsm.deviceID})
+ pConfigAniStateAFsm := oFsm.PAdaptFsm
+ if pConfigAniStateAFsm != nil {
+ oFsm.mutexPLastTxMeInstance.Unlock()
+ // obviously calling some FSM event here directly does not work - so trying to decouple it ...
+ go func(aPAFsm *cmn.AdapterFsm) {
+ if aPAFsm != nil && aPAFsm.PFsm != nil {
+ _ = aPAFsm.PFsm.Event(aniEvReset)
+ }
+ }(pConfigAniStateAFsm)
+ return
+ }
+ }
+ //accept also nil as (error) return value for writing to LastTx
+ // - this avoids misinterpretation of new received OMCI messages
+ oFsm.pLastTxMeInstance = meInstance
+ oFsm.mutexPLastTxMeInstance.Unlock()
+
+}
+
+func (oFsm *UniPonAniConfigFsm) enterSettingTconts(ctx context.Context, e *fsm.Event) {
+ logger.Debugw(ctx, "UniPonAniConfigFsm Tx Set::Tcont", log.Fields{
+ "EntitytId": strconv.FormatInt(int64(oFsm.tcont0ID), 16),
+ "AllocId": strconv.FormatInt(int64(oFsm.alloc0ID), 16),
+ "device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID,
+ "tcontExist": oFsm.tcontSetBefore})
+ //If tcont was set before, then no need to set it again. Let state machine to proceed.
+ if oFsm.tcontSetBefore {
+ go func(aPAFsm *cmn.AdapterFsm) {
+ if aPAFsm != nil && aPAFsm.PFsm != nil {
+ _ = aPAFsm.PFsm.Event(aniEvRxTcontsResp)
+ }
+ }(oFsm.PAdaptFsm)
+ return
+ }
+ meParams := me.ParamData{
+ EntityID: oFsm.tcont0ID,
+ Attributes: me.AttributeValueMap{
+ "AllocId": oFsm.alloc0ID,
+ },
+ }
+ oFsm.mutexPLastTxMeInstance.Lock()
+ meInstance, err := oFsm.pOmciCC.SendSetTcontVar(log.WithSpanFromContext(context.TODO(), ctx), oFsm.pDeviceHandler.GetOmciTimeout(), true,
+ oFsm.PAdaptFsm.CommChan, meParams)
+ if err != nil {
+ logger.Errorw(ctx, "TcontVar set failed, aborting UniPonAniConfigFsm!",
+ log.Fields{"device-id": oFsm.deviceID})
+ pConfigAniStateAFsm := oFsm.PAdaptFsm
+ if pConfigAniStateAFsm != nil {
+ oFsm.mutexPLastTxMeInstance.Unlock()
+ // obviously calling some FSM event here directly does not work - so trying to decouple it ...
+ go func(aPAFsm *cmn.AdapterFsm) {
+ if aPAFsm != nil && aPAFsm.PFsm != nil {
+ _ = aPAFsm.PFsm.Event(aniEvReset)
+ }
+ }(pConfigAniStateAFsm)
+ return
+ }
+ }
+ //accept also nil as (error) return value for writing to LastTx
+ // - this avoids misinterpretation of new received OMCI messages
+ oFsm.pLastTxMeInstance = meInstance
+ oFsm.mutexPLastTxMeInstance.Unlock()
+
+}
+
+func (oFsm *UniPonAniConfigFsm) enterCreatingGemNCTPs(ctx context.Context, e *fsm.Event) {
+ logger.Debugw(ctx, "UniPonAniConfigFsm - start creating GemNWCtp loop", log.Fields{
+ "device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID})
+ go oFsm.performCreatingGemNCTPs(ctx)
+}
+
+func (oFsm *UniPonAniConfigFsm) enterCreatingGemIWs(ctx context.Context, e *fsm.Event) {
+ logger.Debugw(ctx, "UniPonAniConfigFsm - start creating GemIwTP loop", log.Fields{
+ "device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID})
+ go oFsm.performCreatingGemIWs(ctx)
+}
+
+func (oFsm *UniPonAniConfigFsm) enterSettingPQs(ctx context.Context, e *fsm.Event) {
+ logger.Debugw(ctx, "UniPonAniConfigFsm - start setting PrioQueue loop", log.Fields{
+ "in state": e.FSM.Current(), "device-id": oFsm.deviceID})
+ go oFsm.performSettingPQs(ctx)
+}
+
+func (oFsm *UniPonAniConfigFsm) enterSettingDot1PMapper(ctx context.Context, e *fsm.Event) {
+
+ logger.Debugw(ctx, "UniPonAniConfigFsm Tx Set::.1pMapper with all PBits set", log.Fields{"EntitytId": 0x8042, /*cmp above*/
+ "toGemIw": 1024, /* cmp above */
+ "device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID})
+
+ logger.Debugw(ctx, "UniPonAniConfigFsm Tx Set::1pMapper", log.Fields{
+ "EntitytId": strconv.FormatInt(int64(oFsm.mapperSP0ID), 16),
+ "in state": e.FSM.Current(), "device-id": oFsm.deviceID})
+
+ meParams := me.ParamData{
+ EntityID: oFsm.mapperSP0ID,
+ Attributes: make(me.AttributeValueMap),
+ }
+
+ //assign the GemPorts according to the configured Prio
+ var loPrioGemPortArray [8]uint16
+ for _, gemPortAttribs := range oFsm.gemPortAttribsSlice {
+ if gemPortAttribs.isMulticast {
+ logger.Debugw(ctx, "UniPonAniConfigFsm Port is Multicast, ignoring .1pMapper", log.Fields{
+ "device-id": oFsm.deviceID, "GemPort": gemPortAttribs.gemPortID,
+ "prioString": gemPortAttribs.pbitString})
+ continue
+ }
+ if gemPortAttribs.pbitString == "" {
+ logger.Warnw(ctx, "UniPonAniConfigFsm PrioString empty string error", log.Fields{
+ "device-id": oFsm.deviceID, "GemPort": gemPortAttribs.gemPortID,
+ "prioString": gemPortAttribs.pbitString})
+ continue
+ }
+ for i := 0; i < 8; i++ {
+ // "lenOfPbitMap(8) - i + 1" will give i-th pbit value from LSB position in the pbit map string
+ if prio, err := strconv.Atoi(string(gemPortAttribs.pbitString[7-i])); err == nil {
+ if prio == 1 { // Check this p-bit is set
+ if loPrioGemPortArray[i] == 0 {
+ loPrioGemPortArray[i] = gemPortAttribs.gemPortID //gemPortId=EntityID and unique
+ } else {
+ logger.Warnw(ctx, "UniPonAniConfigFsm PrioString not unique", log.Fields{
+ "device-id": oFsm.deviceID, "IgnoredGemPort": gemPortAttribs.gemPortID,
+ "SetGemPort": loPrioGemPortArray[i]})
+ }
+ }
+ } else {
+ logger.Warnw(ctx, "UniPonAniConfigFsm PrioString evaluation error", log.Fields{
+ "device-id": oFsm.deviceID, "GemPort": gemPortAttribs.gemPortID,
+ "prioString": gemPortAttribs.pbitString, "position": i})
+ }
+
+ }
+ }
+
+ var foundIwPtr = false
+ for index, value := range loPrioGemPortArray {
+ meAttribute := fmt.Sprintf("InterworkTpPointerForPBitPriority%d", index)
+ if value != 0 {
+ foundIwPtr = true
+ meParams.Attributes[meAttribute] = value
+ logger.Debugw(ctx, "UniPonAniConfigFsm Set::1pMapper", log.Fields{
+ "for Prio": index,
+ "IwPtr": strconv.FormatInt(int64(value), 16),
+ "device-id": oFsm.deviceID})
+ } else {
+ // The null pointer 0xFFFF specifies that frames with the associated priority are to be discarded.
+ // setting this parameter is not strictly needed anymore with the ensured .1pMapper create default setting
+ // but except for processing effort does not really harm - left to keep changes low
+ meParams.Attributes[meAttribute] = 0xffff
+ }
+ }
+ // The TP type value 0 also indicates bridging mapping, and the TP pointer should be set to 0xFFFF
+ // setting this parameter is not strictly needed anymore with the ensured .1pMapper create default setting
+ // but except for processing effort does not really harm - left to keep changes low
+ meParams.Attributes["TpPointer"] = 0xffff
+
+ if !foundIwPtr {
+ logger.Debugw(ctx, "UniPonAniConfigFsm no GemIwPtr found for .1pMapper - abort", log.Fields{
+ "device-id": oFsm.deviceID})
+ //TODO With multicast is possible that no upstream gem ports are not present in the tech profile,
+ // this reset needs to be performed only if the tech profile provides upstream gem ports but no priority is set
+ //let's reset the state machine in order to release all resources now
+ //pConfigAniStateAFsm := oFsm.PAdaptFsm
+ //if pConfigAniStateAFsm != nil {
+ // // obviously calling some FSM event here directly does not work - so trying to decouple it ...
+ // go func(aPAFsm *cmn.AdapterFsm) {
+ // if aPAFsm != nil && aPAFsm.PFsm != nil {
+ // _ = aPAFsm.PFsm.Event(aniEvReset)
+ // }
+ // }(pConfigAniStateAFsm)
+ //}
+ //Moving forward the FSM as if the response was received correctly.
+ pConfigAniStateAFsm := oFsm.PAdaptFsm
+ if pConfigAniStateAFsm != nil {
+ // obviously calling some FSM event here directly does not work - so trying to decouple it ...
+ go func(aPAFsm *cmn.AdapterFsm) {
+ if aPAFsm != nil && aPAFsm.PFsm != nil {
+ _ = aPAFsm.PFsm.Event(aniEvRxDot1pmapSResp)
+ }
+ }(pConfigAniStateAFsm)
+ }
+ } else {
+ oFsm.mutexPLastTxMeInstance.Lock()
+ meInstance, err := oFsm.pOmciCC.SendSetDot1PMapperVar(context.TODO(), oFsm.pDeviceHandler.GetOmciTimeout(), true,
+ oFsm.PAdaptFsm.CommChan, meParams)
+ if err != nil {
+ logger.Errorw(ctx, "Dot1PMapperVar set failed, aborting UniPonAniConfigFsm!",
+ log.Fields{"device-id": oFsm.deviceID})
+ pConfigAniStateAFsm := oFsm.PAdaptFsm
+ if pConfigAniStateAFsm != nil {
+ oFsm.mutexPLastTxMeInstance.Unlock()
+ // obviously calling some FSM event here directly does not work - so trying to decouple it ...
+ go func(aPAFsm *cmn.AdapterFsm) {
+ if aPAFsm != nil && aPAFsm.PFsm != nil {
+ _ = aPAFsm.PFsm.Event(aniEvReset)
+ }
+ }(pConfigAniStateAFsm)
+ return
+ }
+ }
+ //accept also nil as (error) return value for writing to LastTx
+ // - this avoids misinterpretation of new received OMCI messages
+ oFsm.pLastTxMeInstance = meInstance
+ oFsm.mutexPLastTxMeInstance.Unlock()
+ }
+}
+
+func (oFsm *UniPonAniConfigFsm) enterAniConfigDone(ctx context.Context, e *fsm.Event) {
+ logger.Debugw(ctx, "UniPonAniConfigFsm ani config done", log.Fields{
+ "device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID, "techProfile-id": oFsm.techProfileID})
+ //store that the UNI related techProfile processing is done for the given Profile and Uni
+ oFsm.pUniTechProf.setConfigDone(oFsm.pOnuUniPort.UniID, oFsm.techProfileID, true)
+ if !oFsm.pDeviceHandler.IsSkipOnuConfigReconciling() {
+ //use DeviceHandler event notification directly
+ oFsm.pDeviceHandler.DeviceProcStatusUpdate(ctx, cmn.OnuDeviceEvent((uint8(oFsm.requestEvent) + oFsm.requestEventOffset)))
+ //if techProfile processing is done it must be checked, if some prior/parallel flow configuration is pending
+ // but only in case the techProfile was configured (not deleted)
+ if oFsm.requestEventOffset == 0 {
+ go oFsm.pDeviceHandler.VerifyUniVlanConfigRequest(ctx, oFsm.pOnuUniPort, oFsm.techProfileID)
+ }
+ } else {
+ logger.Debugw(ctx, "reconciling - skip AniConfigDone processing", log.Fields{"device-id": oFsm.deviceID})
+ }
+ if oFsm.isChanSet() {
+ // indicate processing done to the caller
+ logger.Debugw(ctx, "UniPonAniConfigFsm processingDone on channel", log.Fields{
+ "ProcessingStep": oFsm.procStep, "from_State": e.FSM.Current(), "device-id": oFsm.deviceID})
+ oFsm.chSuccess <- oFsm.procStep
+ oFsm.setChanSet(false) //reset the internal channel state
+ }
+
+ //the FSM is left active in this state as long as no specific reset or remove is requested from outside
+}
+
+func (oFsm *UniPonAniConfigFsm) enterRemovingGemIW(ctx context.Context, e *fsm.Event) {
+ // no need to protect access to oFsm.waitFlowDeleteChannel, only used in synchronized state entries
+ // or CancelProcessing() that uses separate isWaitingForFlowDelete to write to the channel
+ //flush the waitFlowDeleteChannel - possibly already/still set by some previous activity
+ select {
+ case <-oFsm.waitFlowDeleteChannel:
+ logger.Debug(ctx, "flushed waitFlowDeleteChannel")
+ default:
+ }
+
+ uniVlanConfigFsm := oFsm.pDeviceHandler.GetUniVlanConfigFsm(oFsm.pOnuUniPort.UniID)
+ if uniVlanConfigFsm != nil {
+ // ensure mutexTPState not locked before calling some VlanConfigFsm activity (that might already be pending on it)
+ if uniVlanConfigFsm.IsFlowRemovePending(oFsm.waitFlowDeleteChannel) {
+ logger.Debugw(ctx, "flow remove pending - wait before processing gem port delete",
+ log.Fields{"device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID, "techProfile-id": oFsm.techProfileID})
+ // if flow remove is pending then wait for flow remove to finish first before proceeding with gem port delete
+ pConfigAniStateAFsm := oFsm.PAdaptFsm
+ if pConfigAniStateAFsm != nil {
+ // obviously calling some FSM event here directly does not work - so trying to decouple it ...
+ go func(aPAFsm *cmn.AdapterFsm) {
+ if aPAFsm != nil && aPAFsm.PFsm != nil {
+ _ = aPAFsm.PFsm.Event(aniEvWaitFlowRem)
+ }
+ }(pConfigAniStateAFsm)
+ } else {
+ logger.Errorw(ctx, "pConfigAniStateAFsm is nil", log.Fields{"device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID, "techProfile-id": oFsm.techProfileID})
+ }
+ return
+ }
+ } else {
+ logger.Debugw(ctx, "uni vlan config doesn't exist - no flow remove could be pending",
+ log.Fields{"device-id": oFsm.deviceID, "techProfile-id": oFsm.techProfileID})
+ }
+
+ oFsm.pUniTechProf.mutexTPState.RLock()
+ // get the related GemPort entity Id from pUniTechProf, OMCI Gem* entityID is set to be equal to GemPortId!
+ loGemPortID := (*(oFsm.pUniTechProf.mapRemoveGemEntry[oFsm.uniTpKey])).gemPortID
+ oFsm.pUniTechProf.mutexTPState.RUnlock()
+ logger.Debugw(ctx, "UniPonAniConfigFsm - start removing one GemIwTP", log.Fields{
+ "device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID,
+ "GemIwTp-entity-id": loGemPortID})
+ oFsm.requestEventOffset = 1 //offset 1 to indicate last activity = remove
+
+ // this state entry is only expected in a suitable state (checked outside in onu_uni_tp)
+ oFsm.mutexPLastTxMeInstance.Lock()
+ meInstance, err := oFsm.pOmciCC.SendDeleteGemIWTP(log.WithSpanFromContext(context.TODO(), ctx), oFsm.pDeviceHandler.GetOmciTimeout(), true,
+ oFsm.PAdaptFsm.CommChan, loGemPortID)
+ if err != nil {
+ logger.Errorw(ctx, "GemIWTP delete failed, aborting UniPonAniConfigFsm!",
+ log.Fields{"device-id": oFsm.deviceID})
+ pConfigAniStateAFsm := oFsm.PAdaptFsm
+ if pConfigAniStateAFsm != nil {
+ oFsm.mutexPLastTxMeInstance.Unlock()
+ // obviously calling some FSM event here directly does not work - so trying to decouple it ...
+ go func(aPAFsm *cmn.AdapterFsm) {
+ if aPAFsm != nil && aPAFsm.PFsm != nil {
+ _ = oFsm.PAdaptFsm.PFsm.Event(aniEvReset)
+ }
+ }(pConfigAniStateAFsm)
+ return
+ }
+ }
+ oFsm.pLastTxMeInstance = meInstance
+ oFsm.mutexPLastTxMeInstance.Unlock()
+}
+
+func (oFsm *UniPonAniConfigFsm) enterWaitingFlowRem(ctx context.Context, e *fsm.Event) {
+ oFsm.mutexIsAwaitingResponse.Lock()
+ oFsm.isWaitingForFlowDelete = true
+ oFsm.mutexIsAwaitingResponse.Unlock()
+ select {
+ // maybe be also some outside cancel (but no context modeled for the moment ...)
+ // case <-ctx.Done():
+ // logger.Infow("LockState-bridge-init message reception canceled", log.Fields{"for device-id": oFsm.deviceID})
+ case <-time.After(2 * oFsm.pOmciCC.GetMaxOmciTimeoutWithRetries() * time.Second): //give flow processing enough time to finish (but try to be less than rwCore flow timeouts)
+ logger.Warnw(ctx, "UniPonAniConfigFsm WaitingFlowRem timeout", log.Fields{
+ "for device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID, "techProfile-id": oFsm.techProfileID})
+ oFsm.mutexIsAwaitingResponse.Lock()
+ oFsm.isWaitingForFlowDelete = false
+ oFsm.mutexIsAwaitingResponse.Unlock()
+ //if the flow is not removed as expected we just try to continue with GemPort removal and hope things are clearing up afterwards
+ pConfigAniStateAFsm := oFsm.PAdaptFsm
+ if pConfigAniStateAFsm != nil {
+ // obviously calling some FSM event here directly does not work - so trying to decouple it ...
+ go func(aPAFsm *cmn.AdapterFsm) {
+ if aPAFsm != nil && aPAFsm.PFsm != nil {
+ _ = aPAFsm.PFsm.Event(aniEvFlowRemDone)
+ }
+ }(pConfigAniStateAFsm)
+ } else {
+ logger.Errorw(ctx, "pConfigAniStateAFsm is nil", log.Fields{
+ "device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID, "techProfile-id": oFsm.techProfileID})
+ }
+ return
+
+ case success := <-oFsm.waitFlowDeleteChannel:
+ if success {
+ logger.Debugw(ctx, "UniPonAniConfigFsm flow removed info received", log.Fields{
+ "device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID, "techProfile-id": oFsm.techProfileID})
+ oFsm.mutexIsAwaitingResponse.Lock()
+ oFsm.isWaitingForFlowDelete = false
+ oFsm.mutexIsAwaitingResponse.Unlock()
+ pConfigAniStateAFsm := oFsm.PAdaptFsm
+ if pConfigAniStateAFsm != nil {
+ // obviously calling some FSM event here directly does not work - so trying to decouple it ...
+ go func(aPAFsm *cmn.AdapterFsm) {
+ if aPAFsm != nil && aPAFsm.PFsm != nil {
+ _ = aPAFsm.PFsm.Event(aniEvFlowRemDone)
+ }
+ }(pConfigAniStateAFsm)
+ } else {
+ logger.Errorw(ctx, "pConfigAniStateAFsm is nil", log.Fields{
+ "device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID, "techProfile-id": oFsm.techProfileID})
+ }
+ return
+ }
+ // waiting was aborted (probably on external request)
+ logger.Debugw(ctx, "UniPonAniConfigFsm WaitingFlowRem aborted", log.Fields{
+ "device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID, "techProfile-id": oFsm.techProfileID})
+ oFsm.mutexIsAwaitingResponse.Lock()
+ oFsm.isWaitingForFlowDelete = false
+ oFsm.mutexIsAwaitingResponse.Unlock()
+ //to be sure we can just generate the reset-event to ensure leaving this state towards 'reset'
+ pConfigAniStateAFsm := oFsm.PAdaptFsm
+ if pConfigAniStateAFsm != nil {
+ // obviously calling some FSM event here directly does not work - so trying to decouple it ...
+ go func(aPAFsm *cmn.AdapterFsm) {
+ if aPAFsm != nil && aPAFsm.PFsm != nil {
+ _ = aPAFsm.PFsm.Event(aniEvReset)
+ }
+ }(pConfigAniStateAFsm)
+ }
+ return
+ }
+}
+
+func (oFsm *UniPonAniConfigFsm) enterRemovingGemNCTP(ctx context.Context, e *fsm.Event) {
+ oFsm.pUniTechProf.mutexTPState.RLock()
+ loGemPortID := (*(oFsm.pUniTechProf.mapRemoveGemEntry[oFsm.uniTpKey])).gemPortID
+ oFsm.pUniTechProf.mutexTPState.RUnlock()
+ logger.Debugw(ctx, "UniPonAniConfigFsm - start removing one GemNCTP", log.Fields{
+ "device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID,
+ "GemNCTP-entity-id": loGemPortID})
+ // this state entry is only expected in a suitable state (checked outside in onu_uni_tp)
+ oFsm.mutexPLastTxMeInstance.Lock()
+ meInstance, err := oFsm.pOmciCC.SendDeleteGemNCTP(log.WithSpanFromContext(context.TODO(), ctx), oFsm.pDeviceHandler.GetOmciTimeout(), true,
+ oFsm.PAdaptFsm.CommChan, loGemPortID)
+ if err != nil {
+ logger.Errorw(ctx, "GemNCTP delete failed, aborting UniPonAniConfigFsm!",
+ log.Fields{"device-id": oFsm.deviceID})
+ pConfigAniStateAFsm := oFsm.PAdaptFsm
+ if pConfigAniStateAFsm != nil {
+ oFsm.mutexPLastTxMeInstance.Unlock()
+ // obviously calling some FSM event here directly does not work - so trying to decouple it ...
+ go func(aPAFsm *cmn.AdapterFsm) {
+ if aPAFsm != nil && aPAFsm.PFsm != nil {
+ _ = aPAFsm.PFsm.Event(aniEvReset)
+ }
+ }(pConfigAniStateAFsm)
+ return
+ }
+ }
+ oFsm.pLastTxMeInstance = meInstance
+ oFsm.mutexPLastTxMeInstance.Unlock()
+
+ // Mark the gem port to be removed for Performance History monitoring
+ OnuMetricsManager := oFsm.pDeviceHandler.GetOnuMetricsManager()
+ if OnuMetricsManager != nil {
+ OnuMetricsManager.RemoveGemPortForPerfMonitoring(ctx, loGemPortID)
+ }
+}
+func (oFsm *UniPonAniConfigFsm) enterRemovingTD(ctx context.Context, e *fsm.Event) {
+ oFsm.pUniTechProf.mutexTPState.RLock()
+ loGemPortID := (*(oFsm.pUniTechProf.mapRemoveGemEntry[oFsm.uniTpKey])).gemPortID
+ oFsm.pUniTechProf.mutexTPState.RUnlock()
+ logger.Debugw(ctx, "UniPonAniConfigFsm - start removing Traffic Descriptor", log.Fields{
+ "device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID,
+ "TD-entity-id": loGemPortID})
+
+ oFsm.mutexPLastTxMeInstance.Lock()
+ meInstance, err := oFsm.pOmciCC.SendDeleteTD(log.WithSpanFromContext(context.TODO(), ctx),
+ oFsm.pDeviceHandler.GetOmciTimeout(), true, oFsm.PAdaptFsm.CommChan, loGemPortID)
+
+ if err != nil {
+ logger.Errorw(ctx, "TD delete failed - proceed fsm",
+ log.Fields{"device-id": oFsm.deviceID, "gemPortID": loGemPortID})
+ pConfigAniStateAFsm := oFsm.PAdaptFsm
+ if pConfigAniStateAFsm != nil {
+ oFsm.mutexPLastTxMeInstance.Unlock()
+ // obviously calling some FSM event here directly does not work - so trying to decouple it ...
+ go func(aPAFsm *cmn.AdapterFsm) {
+ if aPAFsm != nil && aPAFsm.PFsm != nil {
+ _ = aPAFsm.PFsm.Event(aniEvReset)
+ }
+ }(pConfigAniStateAFsm)
+ return
+ }
+ }
+ oFsm.pLastTxMeInstance = meInstance
+ oFsm.mutexPLastTxMeInstance.Unlock()
+}
+
+func (oFsm *UniPonAniConfigFsm) enterResettingTcont(ctx context.Context, e *fsm.Event) {
+ logger.Debugw(ctx, "UniPonAniConfigFsm - start resetting the TCont", log.Fields{
+ "device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID})
+
+ oFsm.requestEventOffset = 1 //offset 1 for last remove activity
+ // this state entry is only expected in a suitable state (checked outside in onu_uni_tp)
+ meParams := me.ParamData{
+ EntityID: oFsm.tcont0ID,
+ Attributes: me.AttributeValueMap{
+ "AllocId": cmn.UnusedTcontAllocID,
+ },
+ }
+ oFsm.mutexPLastTxMeInstance.Lock()
+ meInstance, err := oFsm.pOmciCC.SendSetTcontVar(log.WithSpanFromContext(context.TODO(), ctx), oFsm.pDeviceHandler.GetOmciTimeout(), true,
+ oFsm.PAdaptFsm.CommChan, meParams)
+ if err != nil {
+ logger.Errorw(ctx, "TcontVar set failed, aborting UniPonAniConfigFsm!",
+ log.Fields{"device-id": oFsm.deviceID})
+ pConfigAniStateAFsm := oFsm.PAdaptFsm
+ if pConfigAniStateAFsm != nil {
+ oFsm.mutexPLastTxMeInstance.Unlock()
+ // obviously calling some FSM event here directly does not work - so trying to decouple it ...
+ go func(aPAFsm *cmn.AdapterFsm) {
+ if aPAFsm != nil && aPAFsm.PFsm != nil {
+ _ = aPAFsm.PFsm.Event(aniEvReset)
+ }
+ }(pConfigAniStateAFsm)
+ return
+ }
+ }
+ oFsm.pLastTxMeInstance = meInstance
+ oFsm.mutexPLastTxMeInstance.Unlock()
+
+}
+
+func (oFsm *UniPonAniConfigFsm) enterRemoving1pMapper(ctx context.Context, e *fsm.Event) {
+ logger.Debugw(ctx, "UniPonAniConfigFsm - start deleting the .1pMapper", log.Fields{
+ "device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID})
+ mapGemPortParams := oFsm.pUniTechProf.mapPonAniConfig[oFsm.uniTpKey].mapGemPortParams
+ unicastGemCount := 0
+ for _, gemEntry := range mapGemPortParams {
+ if !gemEntry.isMulticast {
+ unicastGemCount++
+ }
+ }
+ if unicastGemCount > 1 {
+ logger.Debugw(ctx, "UniPonAniConfigFsm - Not the last gem in fsm. Skip the rest", log.Fields{
+ "device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID, "unicast-gem-count": unicastGemCount, "gem-count": len(mapGemPortParams)})
+ pConfigAniStateAFsm := oFsm.PAdaptFsm
+ if pConfigAniStateAFsm != nil {
+ // obviously calling some FSM event here directly does not work - so trying to decouple it ...
+ go func(aPAFsm *cmn.AdapterFsm) {
+ if aPAFsm != nil && aPAFsm.PFsm != nil {
+ _ = aPAFsm.PFsm.Event(aniEvRemGemDone)
+ }
+ }(pConfigAniStateAFsm)
+ return
+ }
+ }
+ logger.Debugw(ctx, "UniPonAniConfigFsm - Last gem in fsm. Continue with Mapper removal", log.Fields{
+ "device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID, "unicast-gem-count": unicastGemCount, "gem-count": len(mapGemPortParams)})
+
+ oFsm.mutexPLastTxMeInstance.Lock()
+ meInstance, err := oFsm.pOmciCC.SendDeleteDot1PMapper(log.WithSpanFromContext(context.TODO(), ctx), oFsm.pDeviceHandler.GetOmciTimeout(), true,
+ oFsm.PAdaptFsm.CommChan, oFsm.mapperSP0ID)
+ if err != nil {
+ logger.Errorw(ctx, "Dot1Mapper delete failed, aborting UniPonAniConfigFsm!",
+ log.Fields{"device-id": oFsm.deviceID})
+ pConfigAniStateAFsm := oFsm.PAdaptFsm
+ if pConfigAniStateAFsm != nil {
+ oFsm.mutexPLastTxMeInstance.Unlock()
+ // obviously calling some FSM event here directly does not work - so trying to decouple it ...
+ go func(aPAFsm *cmn.AdapterFsm) {
+ if aPAFsm != nil && aPAFsm.PFsm != nil {
+ _ = aPAFsm.PFsm.Event(aniEvReset)
+ }
+ }(pConfigAniStateAFsm)
+ return
+ }
+ }
+ oFsm.pLastTxMeInstance = meInstance
+ oFsm.mutexPLastTxMeInstance.Unlock()
+
+}
+
+func (oFsm *UniPonAniConfigFsm) enterRemovingAniBPCD(ctx context.Context, e *fsm.Event) {
+ logger.Debugw(ctx, "UniPonAniConfigFsm - start deleting the ANI MBCD", log.Fields{
+ "device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID})
+
+ oFsm.mutexPLastTxMeInstance.Lock()
+ meInstance, err := oFsm.pOmciCC.SendDeleteMBPConfigData(log.WithSpanFromContext(context.TODO(), ctx), oFsm.pDeviceHandler.GetOmciTimeout(), true,
+ oFsm.PAdaptFsm.CommChan, oFsm.macBPCD0ID)
+ if err != nil {
+ logger.Errorw(ctx, "MBPConfigData delete failed, aborting UniPonAniConfigFsm!",
+ log.Fields{"device-id": oFsm.deviceID})
+ pConfigAniStateAFsm := oFsm.PAdaptFsm
+ if pConfigAniStateAFsm != nil {
+ oFsm.mutexPLastTxMeInstance.Unlock()
+ // obviously calling some FSM event here directly does not work - so trying to decouple it ...
+ go func(aPAFsm *cmn.AdapterFsm) {
+ if aPAFsm != nil && aPAFsm.PFsm != nil {
+ _ = aPAFsm.PFsm.Event(aniEvReset)
+ }
+ }(pConfigAniStateAFsm)
+ return
+ }
+ }
+ oFsm.pLastTxMeInstance = meInstance
+ oFsm.mutexPLastTxMeInstance.Unlock()
+}
+
+func (oFsm *UniPonAniConfigFsm) enterAniRemoveDone(ctx context.Context, e *fsm.Event) {
+ logger.Debugw(ctx, "UniPonAniConfigFsm ani removal done", log.Fields{
+ "device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID})
+ //use DeviceHandler event notification directly
+ oFsm.pDeviceHandler.DeviceProcStatusUpdate(ctx, cmn.OnuDeviceEvent((uint8(oFsm.requestEvent) + oFsm.requestEventOffset)))
+ if oFsm.isChanSet() {
+ // indicate processing done to the caller
+ logger.Debugw(ctx, "UniPonAniConfigFsm processingDone on channel", log.Fields{
+ "ProcessingStep": oFsm.procStep, "from_State": e.FSM.Current(), "device-id": oFsm.deviceID})
+ oFsm.chSuccess <- oFsm.procStep
+ oFsm.setChanSet(false) //reset the internal channel state
+ }
+
+ //let's reset the state machine in order to release all resources now
+ pConfigAniStateAFsm := oFsm.PAdaptFsm
+ if pConfigAniStateAFsm != nil {
+ // obviously calling some FSM event here directly does not work - so trying to decouple it ...
+ go func(aPAFsm *cmn.AdapterFsm) {
+ if aPAFsm != nil && aPAFsm.PFsm != nil {
+ _ = aPAFsm.PFsm.Event(aniEvReset)
+ }
+ }(pConfigAniStateAFsm)
+ }
+}
+
+func (oFsm *UniPonAniConfigFsm) enterResettingState(ctx context.Context, e *fsm.Event) {
+ logger.Debugw(ctx, "UniPonAniConfigFsm resetting", log.Fields{
+ "device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID})
+
+ if oFsm.isChanSet() {
+ // indicate processing error to the caller (in case there was still some open request)
+ logger.Debugw(ctx, "UniPonAniConfigFsm processingError on channel", log.Fields{
+ "ProcessingStep": oFsm.procStep, "from_State": e.FSM.Current(), "device-id": oFsm.deviceID})
+ //use non-blocking channel send to avoid blocking because of non-existing receiver
+ // (even though the channel is checked on 'set', the outside receiver channel might (theoretically) already be deleted)
+ select {
+ case oFsm.chSuccess <- 0:
+ default:
+ logger.Debugw(ctx, "UniPonAniConfigFsm processingError not send on channel (no receiver)", log.Fields{
+ "device-id": oFsm.deviceID})
+ }
+ oFsm.setChanSet(false) //reset the internal channel state
+ }
+
+ pConfigAniStateAFsm := oFsm.PAdaptFsm
+ if pConfigAniStateAFsm != nil {
+ // abort running message processing
+ fsmAbortMsg := cmn.Message{
+ Type: cmn.TestMsg,
+ Data: cmn.TestMessage{
+ TestMessageVal: cmn.AbortMessageProcessing,
+ },
+ }
+ pConfigAniStateAFsm.CommChan <- fsmAbortMsg
+
+ //try to restart the FSM to 'disabled', decouple event transfer
+ go func(aPAFsm *cmn.AdapterFsm) {
+ if aPAFsm != nil && aPAFsm.PFsm != nil {
+ _ = aPAFsm.PFsm.Event(aniEvRestart)
+ }
+ }(pConfigAniStateAFsm)
+ }
+}
+
+func (oFsm *UniPonAniConfigFsm) enterDisabledState(ctx context.Context, e *fsm.Event) {
+ logger.Debugw(ctx, "UniPonAniConfigFsm enters disabled state", log.Fields{
+ "device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID})
+ oFsm.mutexPLastTxMeInstance.Lock()
+ defer oFsm.mutexPLastTxMeInstance.Unlock()
+ oFsm.pLastTxMeInstance = nil
+}
+
+func (oFsm *UniPonAniConfigFsm) processOmciAniMessages(ctx context.Context) {
+ logger.Debugw(ctx, "Start UniPonAniConfigFsm Msg processing", log.Fields{"for device-id": oFsm.deviceID})
+loop:
+ for {
+ // case <-ctx.Done():
+ // logger.Info("MibSync Msg", log.Fields{"Message handling canceled via context for device-id": oFsm.deviceID})
+ // break loop
+ message, ok := <-oFsm.PAdaptFsm.CommChan
+ if !ok {
+ logger.Info(ctx, "UniPonAniConfigFsm Rx Msg - could not read from channel", log.Fields{"device-id": oFsm.deviceID})
+ // but then we have to ensure a restart of the FSM as well - as exceptional procedure
+ _ = oFsm.PAdaptFsm.PFsm.Event(aniEvReset)
+ break loop
+ }
+ logger.Debugw(ctx, "UniPonAniConfigFsm Rx Msg", log.Fields{"device-id": oFsm.deviceID})
+
+ switch message.Type {
+ case cmn.TestMsg:
+ msg, _ := message.Data.(cmn.TestMessage)
+ if msg.TestMessageVal == cmn.AbortMessageProcessing {
+ logger.Infow(ctx, "UniPonAniConfigFsm abort ProcessMsg", log.Fields{"for device-id": oFsm.deviceID})
+ break loop
+ }
+ logger.Warnw(ctx, "UniPonAniConfigFsm unknown TestMessage", log.Fields{"device-id": oFsm.deviceID, "MessageVal": msg.TestMessageVal})
+ case cmn.OMCI:
+ msg, _ := message.Data.(cmn.OmciMessage)
+ oFsm.handleOmciAniConfigMessage(ctx, msg)
+ default:
+ logger.Warn(ctx, "UniPonAniConfigFsm Rx unknown message", log.Fields{"device-id": oFsm.deviceID,
+ "message.Type": message.Type})
+ }
+
+ }
+ logger.Infow(ctx, "End UniPonAniConfigFsm Msg processing", log.Fields{"device-id": oFsm.deviceID})
+}
+
+func (oFsm *UniPonAniConfigFsm) handleOmciAniConfigCreateResponseMessage(ctx context.Context, msg cmn.OmciMessage) {
+ msgLayer := (*msg.OmciPacket).Layer(omci.LayerTypeCreateResponse)
+ if msgLayer == nil {
+ logger.Errorw(ctx, "Omci Msg layer could not be detected for CreateResponse",
+ log.Fields{"device-id": oFsm.deviceID})
+ return
+ }
+ msgObj, msgOk := msgLayer.(*omci.CreateResponse)
+ if !msgOk {
+ logger.Errorw(ctx, "Omci Msg layer could not be assigned for CreateResponse",
+ log.Fields{"device-id": oFsm.deviceID})
+ return
+ }
+ logger.Debugw(ctx, "CreateResponse Data", log.Fields{"device-id": oFsm.deviceID, "data-fields": msgObj})
+ if msgObj.Result == me.Success || msgObj.Result == me.InstanceExists {
+ //if the result is ok or Instance already exists (latest needed at least as long as we do not clear the OMCI techProfile data)
+ oFsm.mutexPLastTxMeInstance.RLock()
+ if oFsm.pLastTxMeInstance != nil {
+ if msgObj.EntityClass == oFsm.pLastTxMeInstance.GetClassID() &&
+ msgObj.EntityInstance == oFsm.pLastTxMeInstance.GetEntityID() {
+ // maybe we can use just the same eventName for different state transitions like "forward"
+ // - might be checked, but so far I go for sure and have to inspect the concrete state events ...
+ switch oFsm.pLastTxMeInstance.GetName() {
+ case "Ieee8021PMapperServiceProfile":
+ { // let the FSM proceed ...
+ oFsm.mutexPLastTxMeInstance.RUnlock()
+ _ = oFsm.PAdaptFsm.PFsm.Event(aniEvRxDot1pmapCResp)
+ }
+ case "MacBridgePortConfigurationData":
+ { // let the FSM proceed ...
+ oFsm.mutexPLastTxMeInstance.RUnlock()
+ _ = oFsm.PAdaptFsm.PFsm.Event(aniEvRxMbpcdResp)
+ }
+ case "GemPortNetworkCtp", "GemInterworkingTerminationPoint", "MulticastGemInterworkingTerminationPoint":
+ { // let aniConfig Multi-Id processing proceed by stopping the wait function
+ oFsm.mutexPLastTxMeInstance.RUnlock()
+ oFsm.omciMIdsResponseReceived <- true
+ }
+ default:
+ {
+ oFsm.mutexPLastTxMeInstance.RUnlock()
+ logger.Warnw(ctx, "Unsupported ME name received!",
+ log.Fields{"ME name": oFsm.pLastTxMeInstance.GetName(), "device-id": oFsm.deviceID})
+ }
+ }
+ } else {
+ oFsm.mutexPLastTxMeInstance.RUnlock()
+ }
+ } else {
+ oFsm.mutexPLastTxMeInstance.RUnlock()
+ logger.Warnw(ctx, "Pointer to last Tx MeInstance is nil!", log.Fields{"device-id": oFsm.deviceID})
+ }
+ } else {
+ logger.Errorw(ctx, "Omci CreateResponse Error - later: drive FSM to abort state ?",
+ log.Fields{"Error": msgObj.Result, "device-id": oFsm.deviceID})
+ // possibly force FSM into abort or ignore some errors for some messages? store error for mgmt display?
+ return
+ }
+}
+func (oFsm *UniPonAniConfigFsm) handleOmciAniConfigSetFailResponseMessage(ctx context.Context, msgObj *omci.SetResponse) {
+ //If TCONT fails, then we need to revert the allocated TCONT in DB.
+ //Because FSMs are running sequentially, we don't expect the same TCONT hit by another tech-profile FSM while this FSM is running.
+ oFsm.mutexPLastTxMeInstance.RLock()
+ defer oFsm.mutexPLastTxMeInstance.RUnlock()
+ if oFsm.pLastTxMeInstance != nil && msgObj.EntityClass == oFsm.pLastTxMeInstance.GetClassID() &&
+ msgObj.EntityInstance == oFsm.pLastTxMeInstance.GetEntityID() {
+ switch oFsm.pLastTxMeInstance.GetName() {
+ case "TCont":
+ //If this is for TCONT creation(requestEventOffset=0) and this is the first allocation of TCONT(so noone else is using the same TCONT)
+ //We should revert DB
+ if oFsm.requestEventOffset == 0 && !oFsm.tcontSetBefore && oFsm.pUniTechProf.mapPonAniConfig[oFsm.uniTpKey] != nil {
+ logger.Debugw(ctx, "UniPonAniConfigFsm TCONT creation failed on device. Freeing alloc id", log.Fields{"device-id": oFsm.deviceID,
+ "alloc-id": oFsm.pUniTechProf.mapPonAniConfig[oFsm.uniTpKey].tcontParams.allocID, "uni-tp": oFsm.uniTpKey})
+ if oFsm.pOnuDeviceEntry != nil {
+ oFsm.pOnuDeviceEntry.FreeTcont(ctx, oFsm.pUniTechProf.mapPonAniConfig[oFsm.uniTpKey].tcontParams.allocID)
+ } else {
+ logger.Warnw(ctx, "Unable to get device entry! couldn't free tcont",
+ log.Fields{"ME name": oFsm.pLastTxMeInstance.GetName(), "device-id": oFsm.deviceID})
+ }
+ }
+ default:
+ logger.Warnw(ctx, "Unsupported ME name received with error!",
+ log.Fields{"ME name": oFsm.pLastTxMeInstance.GetName(), "result": msgObj.Result, "device-id": oFsm.deviceID})
+ }
+ }
+}
+func (oFsm *UniPonAniConfigFsm) handleOmciAniConfigSetResponseMessage(ctx context.Context, msg cmn.OmciMessage) {
+ msgLayer := (*msg.OmciPacket).Layer(omci.LayerTypeSetResponse)
+ if msgLayer == nil {
+ logger.Errorw(ctx, "UniPonAniConfigFsm - Omci Msg layer could not be detected for SetResponse",
+ log.Fields{"device-id": oFsm.deviceID})
+ return
+ }
+ msgObj, msgOk := msgLayer.(*omci.SetResponse)
+ if !msgOk {
+ logger.Errorw(ctx, "UniPonAniConfigFsm - Omci Msg layer could not be assigned for SetResponse",
+ log.Fields{"device-id": oFsm.deviceID})
+ return
+ }
+ logger.Debugw(ctx, "UniPonAniConfigFsm SetResponse Data", log.Fields{"device-id": oFsm.deviceID, "data-fields": msgObj})
+ if msgObj.Result != me.Success {
+ logger.Errorw(ctx, "UniPonAniConfigFsm - Omci SetResponse Error - later: drive FSM to abort state ?",
+ log.Fields{"device-id": oFsm.deviceID, "Error": msgObj.Result})
+ // possibly force FSM into abort or ignore some errors for some messages? store error for mgmt display?
+
+ oFsm.handleOmciAniConfigSetFailResponseMessage(ctx, msgObj)
+ return
+ }
+ oFsm.mutexPLastTxMeInstance.RLock()
+ if oFsm.pLastTxMeInstance != nil {
+ if msgObj.EntityClass == oFsm.pLastTxMeInstance.GetClassID() &&
+ msgObj.EntityInstance == oFsm.pLastTxMeInstance.GetEntityID() {
+ //store the created ME into DB //TODO??? obviously the Python code does not store the config ...
+ // if, then something like:
+ //oFsm.pOnuDB.StoreMe(msgObj)
+
+ switch oFsm.pLastTxMeInstance.GetName() {
+ case "TCont":
+ { // let the FSM proceed ...
+ oFsm.mutexPLastTxMeInstance.RUnlock()
+ if oFsm.requestEventOffset == 0 { //from TCont config request
+ _ = oFsm.PAdaptFsm.PFsm.Event(aniEvRxTcontsResp)
+ } else { // from T-Cont reset request
+ _ = oFsm.PAdaptFsm.PFsm.Event(aniEvRxResetTcontResp)
+ }
+ }
+ case "PriorityQueue", "MulticastGemInterworkingTerminationPoint":
+ { // let the PrioQueue init proceed by stopping the wait function
+ oFsm.mutexPLastTxMeInstance.RUnlock()
+ oFsm.omciMIdsResponseReceived <- true
+ }
+ case "Ieee8021PMapperServiceProfile":
+ { // let the FSM proceed ...
+ oFsm.mutexPLastTxMeInstance.RUnlock()
+ _ = oFsm.PAdaptFsm.PFsm.Event(aniEvRxDot1pmapSResp)
+ }
+ default:
+ {
+ oFsm.mutexPLastTxMeInstance.RUnlock()
+ logger.Warnw(ctx, "Unsupported ME name received!",
+ log.Fields{"ME name": oFsm.pLastTxMeInstance.GetName(), "device-id": oFsm.deviceID})
+ }
+ }
+ } else {
+ oFsm.mutexPLastTxMeInstance.RUnlock()
+ }
+ } else {
+ oFsm.mutexPLastTxMeInstance.RUnlock()
+ logger.Warnw(ctx, "Pointer to last Tx MeInstance is nil!", log.Fields{"device-id": oFsm.deviceID})
+ }
+}
+
+func (oFsm *UniPonAniConfigFsm) handleOmciAniConfigDeleteResponseMessage(ctx context.Context, msg cmn.OmciMessage) {
+ msgLayer := (*msg.OmciPacket).Layer(omci.LayerTypeDeleteResponse)
+ if msgLayer == nil {
+ logger.Errorw(ctx, "UniPonAniConfigFsm - Omci Msg layer could not be detected for DeleteResponse",
+ log.Fields{"device-id": oFsm.deviceID})
+ return
+ }
+ msgObj, msgOk := msgLayer.(*omci.DeleteResponse)
+ if !msgOk {
+ logger.Errorw(ctx, "UniPonAniConfigFsm - Omci Msg layer could not be assigned for DeleteResponse",
+ log.Fields{"device-id": oFsm.deviceID})
+ return
+ }
+ logger.Debugw(ctx, "UniPonAniConfigFsm DeleteResponse Data", log.Fields{"device-id": oFsm.deviceID, "data-fields": msgObj})
+ if msgObj.Result != me.Success {
+ logger.Errorw(ctx, "UniPonAniConfigFsm - Omci DeleteResponse Error",
+ log.Fields{"device-id": oFsm.deviceID, "Error": msgObj.Result})
+ //TODO: - later: possibly force FSM into abort or ignore some errors for some messages?
+ // store error for mgmt display?
+ return
+ }
+ oFsm.mutexPLastTxMeInstance.RLock()
+ if oFsm.pLastTxMeInstance != nil {
+ if msgObj.EntityClass == oFsm.pLastTxMeInstance.GetClassID() &&
+ msgObj.EntityInstance == oFsm.pLastTxMeInstance.GetEntityID() {
+ //remove ME from DB //TODO??? obviously the Python code does not store/remove the config ...
+ // if, then something like: oFsm.pOnuDB.XyyMe(msgObj)
+
+ switch oFsm.pLastTxMeInstance.GetName() {
+ case "GemInterworkingTerminationPoint":
+ { // let the FSM proceed ...
+ oFsm.mutexPLastTxMeInstance.RUnlock()
+ _ = oFsm.PAdaptFsm.PFsm.Event(aniEvRxRemGemiwResp)
+ }
+ case "GemPortNetworkCtp":
+ { // let the FSM proceed ...
+ oFsm.mutexPLastTxMeInstance.RUnlock()
+ _ = oFsm.PAdaptFsm.PFsm.Event(aniEvRxRemGemntpResp)
+ }
+ case "TrafficDescriptor":
+ { // let the FSM proceed ...
+ oFsm.mutexPLastTxMeInstance.RUnlock()
+ _ = oFsm.PAdaptFsm.PFsm.Event(aniEvRxRemTdResp)
+ }
+ case "Ieee8021PMapperServiceProfile":
+ { // let the FSM proceed ...
+ oFsm.mutexPLastTxMeInstance.RUnlock()
+ _ = oFsm.PAdaptFsm.PFsm.Event(aniEvRxRem1pMapperResp)
+ }
+ case "MacBridgePortConfigurationData":
+ { // this is the last event of the T-Cont cleanup procedure, FSM may be reset here
+ oFsm.mutexPLastTxMeInstance.RUnlock()
+ _ = oFsm.PAdaptFsm.PFsm.Event(aniEvRxRemAniBPCDResp)
+ }
+ default:
+ {
+ oFsm.mutexPLastTxMeInstance.RUnlock()
+ logger.Warnw(ctx, "Unsupported ME name received!",
+ log.Fields{"ME name": oFsm.pLastTxMeInstance.GetName(), "device-id": oFsm.deviceID})
+ }
+ }
+ } else {
+ oFsm.mutexPLastTxMeInstance.RUnlock()
+ }
+ } else {
+ oFsm.mutexPLastTxMeInstance.RUnlock()
+ logger.Warnw(ctx, "Pointer to last Tx MeInstance is nil!", log.Fields{"device-id": oFsm.deviceID})
+ }
+}
+
+func (oFsm *UniPonAniConfigFsm) handleOmciAniConfigMessage(ctx context.Context, msg cmn.OmciMessage) {
+ logger.Debugw(ctx, "Rx OMCI UniPonAniConfigFsm Msg", log.Fields{"device-id": oFsm.deviceID,
+ "msgType": msg.OmciMsg.MessageType})
+
+ switch msg.OmciMsg.MessageType {
+ case omci.CreateResponseType:
+ {
+ oFsm.handleOmciAniConfigCreateResponseMessage(ctx, msg)
+
+ } //CreateResponseType
+ case omci.SetResponseType:
+ {
+ oFsm.handleOmciAniConfigSetResponseMessage(ctx, msg)
+
+ } //SetResponseType
+ case omci.DeleteResponseType:
+ {
+ oFsm.handleOmciAniConfigDeleteResponseMessage(ctx, msg)
+
+ } //DeleteResponseType
+ default:
+ {
+ logger.Errorw(ctx, "UniPonAniConfigFsm - Rx OMCI unhandled MsgType",
+ log.Fields{"omciMsgType": msg.OmciMsg.MessageType, "device-id": oFsm.deviceID})
+ return
+ }
+ }
+}
+
+func (oFsm *UniPonAniConfigFsm) performCreatingGemNCTPs(ctx context.Context) {
+ // for all GemPorts of this T-Cont as given by the size of set gemPortAttribsSlice
+ for gemIndex, gemPortAttribs := range oFsm.gemPortAttribsSlice {
+ logger.Debugw(ctx, "UniPonAniConfigFsm Tx Create::GemNWCtp", log.Fields{
+ "EntitytId": strconv.FormatInt(int64(gemPortAttribs.gemPortID), 16),
+ "TcontId": strconv.FormatInt(int64(oFsm.tcont0ID), 16),
+ "device-id": oFsm.deviceID})
+ meParams := me.ParamData{
+ EntityID: gemPortAttribs.gemPortID, //unique, same as PortId
+ Attributes: me.AttributeValueMap{
+ "PortId": gemPortAttribs.gemPortID,
+ "TContPointer": oFsm.tcont0ID,
+ "Direction": gemPortAttribs.direction,
+ //ONU-G.TrafficManagementOption dependency ->PrioQueue or TCont
+ // TODO!! verify dependency and QueueId in case of Multi-GemPort setup!
+ "TrafficManagementPointerForUpstream": gemPortAttribs.upQueueID, //might be different in wrr-only Setup - tcont0ID
+ "PriorityQueuePointerForDownStream": gemPortAttribs.downQueueID,
+ },
+ }
+ oFsm.mutexPLastTxMeInstance.Lock()
+ meInstance, err := oFsm.pOmciCC.SendCreateGemNCTPVar(log.WithSpanFromContext(context.TODO(), ctx), oFsm.pDeviceHandler.GetOmciTimeout(), true,
+ oFsm.PAdaptFsm.CommChan, meParams)
+ if err != nil {
+ oFsm.mutexPLastTxMeInstance.Unlock()
+ logger.Errorw(ctx, "GemNCTPVar create failed, aborting UniPonAniConfigFsm!",
+ log.Fields{"device-id": oFsm.deviceID})
+ _ = oFsm.PAdaptFsm.PFsm.Event(aniEvReset)
+ return
+ }
+ //accept also nil as (error) return value for writing to LastTx
+ // - this avoids misinterpretation of new received OMCI messages
+ oFsm.pLastTxMeInstance = meInstance
+ oFsm.mutexPLastTxMeInstance.Unlock()
+ //verify response
+ err = oFsm.waitforOmciResponse(ctx)
+ if err != nil {
+ logger.Errorw(ctx, "GemNWCtp create failed, aborting AniConfig FSM!",
+ log.Fields{"device-id": oFsm.deviceID, "GemIndex": gemIndex})
+ _ = oFsm.PAdaptFsm.PFsm.Event(aniEvReset)
+ return
+ }
+ // Mark the gem port to be added for Performance History monitoring
+ OnuMetricsManager := oFsm.pDeviceHandler.GetOnuMetricsManager()
+ if OnuMetricsManager != nil {
+ OnuMetricsManager.AddGemPortForPerfMonitoring(ctx, gemPortAttribs.gemPortID)
+ }
+ } //for all GemPorts of this T-Cont
+
+ // if Config has been done for all GemPort instances let the FSM proceed
+ logger.Debugw(ctx, "GemNWCtp create loop finished", log.Fields{"device-id": oFsm.deviceID})
+ _ = oFsm.PAdaptFsm.PFsm.Event(aniEvRxGemntcpsResp)
+}
+func (oFsm *UniPonAniConfigFsm) hasMulticastGem(ctx context.Context) bool {
+ for _, gemPortAttribs := range oFsm.gemPortAttribsSlice {
+ if gemPortAttribs.isMulticast {
+ logger.Debugw(ctx, "Found multicast gem", log.Fields{"device-id": oFsm.deviceID})
+ return true
+ }
+ }
+ return false
+}
+
+func (oFsm *UniPonAniConfigFsm) performCreatingGemIWs(ctx context.Context) {
+ // for all GemPorts of this T-Cont as given by the size of set gemPortAttribsSlice
+ for gemIndex, gemPortAttribs := range oFsm.gemPortAttribsSlice {
+ logger.Debugw(ctx, "UniPonAniConfigFsm Tx Create::GemIwTp", log.Fields{
+ "EntitytId": strconv.FormatInt(int64(gemPortAttribs.gemPortID), 16),
+ "SPPtr": strconv.FormatInt(int64(oFsm.mapperSP0ID), 16),
+ "device-id": oFsm.deviceID})
+
+ //TODO if the port has only downstream direction the isMulticast flag can be removed.
+ if gemPortAttribs.isMulticast {
+
+ meParams := me.ParamData{
+ EntityID: gemPortAttribs.multicastGemID,
+ Attributes: me.AttributeValueMap{
+ "GemPortNetworkCtpConnectivityPointer": gemPortAttribs.multicastGemID,
+ "InterworkingOption": 0, // Don't Care
+ "ServiceProfilePointer": 0, // Don't Care
+ "GalProfilePointer": cmn.GalEthernetEID,
+ },
+ }
+ if oFsm.pUniTechProf.multicastConfiguredForOtherUniTps(ctx, oFsm.uniTpKey) {
+ logger.Debugw(ctx, "MulticastGemInterworkingTP already exist", log.Fields{"device-id": oFsm.deviceID, "multicast-gem-id": gemPortAttribs.multicastGemID})
+ continue
+ }
+ oFsm.mutexPLastTxMeInstance.Lock()
+ meInstance, err := oFsm.pOmciCC.SendCreateMulticastGemIWTPVar(context.TODO(), oFsm.pDeviceHandler.GetOmciTimeout(),
+ true, oFsm.PAdaptFsm.CommChan, meParams)
+ if err != nil {
+ oFsm.mutexPLastTxMeInstance.Unlock()
+ logger.Errorw(ctx, "MulticastGemIWTPVar create failed, aborting UniPonAniConfigFsm!",
+ log.Fields{"device-id": oFsm.deviceID})
+ _ = oFsm.PAdaptFsm.PFsm.Event(aniEvReset)
+ return
+
+ }
+ oFsm.pLastTxMeInstance = meInstance
+ oFsm.mutexPLastTxMeInstance.Unlock()
+ //verify response
+ err = oFsm.waitforOmciResponse(ctx)
+ if err != nil {
+ logger.Errorw(ctx, "MulticastGemIWTP create failed, aborting AniConfig FSM!",
+ log.Fields{"device-id": oFsm.deviceID, "GemIndex": gemIndex})
+ _ = oFsm.PAdaptFsm.PFsm.Event(aniEvReset)
+ return
+ }
+ ipv4MulticastTable := make([]uint8, 12)
+ //Gem Port ID
+ binary.BigEndian.PutUint16(ipv4MulticastTable[0:], gemPortAttribs.multicastGemID)
+ //Secondary Key
+ binary.BigEndian.PutUint16(ipv4MulticastTable[2:], 0)
+ // Multicast IP range start This is the 224.0.0.1 address
+ binary.BigEndian.PutUint32(ipv4MulticastTable[4:], cmn.IPToInt32(net.IPv4(224, 0, 0, 0)))
+ // MulticastIp range stop
+ binary.BigEndian.PutUint32(ipv4MulticastTable[8:], cmn.IPToInt32(net.IPv4(239, 255, 255, 255)))
+
+ meIPV4MCTableParams := me.ParamData{
+ EntityID: gemPortAttribs.multicastGemID,
+ Attributes: me.AttributeValueMap{
+ "Ipv4MulticastAddressTable": ipv4MulticastTable,
+ },
+ }
+ oFsm.mutexPLastTxMeInstance.Lock()
+ meIPV4MCTableInstance, err := oFsm.pOmciCC.SendSetMulticastGemIWTPVar(context.TODO(), oFsm.pDeviceHandler.GetOmciTimeout(),
+ true, oFsm.PAdaptFsm.CommChan, meIPV4MCTableParams)
+ if err != nil {
+ oFsm.mutexPLastTxMeInstance.Unlock()
+ logger.Errorw(ctx, "MulticastGemIWTPVar set failed, aborting UniPonAniConfigFsm!",
+ log.Fields{"device-id": oFsm.deviceID})
+ _ = oFsm.PAdaptFsm.PFsm.Event(aniEvReset)
+ return
+ }
+ oFsm.pLastTxMeInstance = meIPV4MCTableInstance
+ oFsm.mutexPLastTxMeInstance.Unlock()
+
+ } else {
+ meParams := me.ParamData{
+ EntityID: gemPortAttribs.gemPortID,
+ Attributes: me.AttributeValueMap{
+ "GemPortNetworkCtpConnectivityPointer": gemPortAttribs.gemPortID, //same as EntityID, see above
+ "InterworkingOption": 5, //fixed model:: G.998 .1pMapper
+ "ServiceProfilePointer": oFsm.mapperSP0ID,
+ "InterworkingTerminationPointPointer": 0, //not used with .1PMapper Mac bridge
+ "GalProfilePointer": cmn.GalEthernetEID,
+ },
+ }
+ oFsm.mutexPLastTxMeInstance.Lock()
+ meInstance, err := oFsm.pOmciCC.SendCreateGemIWTPVar(context.TODO(), oFsm.pDeviceHandler.GetOmciTimeout(), true,
+ oFsm.PAdaptFsm.CommChan, meParams)
+ if err != nil {
+ oFsm.mutexPLastTxMeInstance.Unlock()
+ logger.Errorw(ctx, "GEMIWTPVar create failed, aborting UniPonAniConfigFsm!",
+ log.Fields{"device-id": oFsm.deviceID})
+ _ = oFsm.PAdaptFsm.PFsm.Event(aniEvReset)
+ return
+ }
+ //accept also nil as (error) return value for writing to LastTx
+ // - this avoids misinterpretation of new received OMCI messages
+ oFsm.pLastTxMeInstance = meInstance
+ oFsm.mutexPLastTxMeInstance.Unlock()
+ }
+ //verify response
+ err := oFsm.waitforOmciResponse(ctx)
+ if err != nil {
+ logger.Errorw(ctx, "GemTP create failed, aborting AniConfig FSM!",
+ log.Fields{"device-id": oFsm.deviceID, "GemIndex": gemIndex})
+ _ = oFsm.PAdaptFsm.PFsm.Event(aniEvReset)
+ return
+ }
+ } //for all GemPort's of this T-Cont
+
+ // if Config has been done for all GemPort instances let the FSM proceed
+ logger.Debugw(ctx, "GemIwTp create loop finished", log.Fields{"device-id": oFsm.deviceID})
+ _ = oFsm.PAdaptFsm.PFsm.Event(aniEvRxGemiwsResp)
+}
+
+func (oFsm *UniPonAniConfigFsm) performSettingPQs(ctx context.Context) {
+ //If upstream PQs were set before, then no need to set them again. Let state machine to proceed.
+ if oFsm.tcontSetBefore {
+ logger.Debugw(ctx, "No need to set PQs again.", log.Fields{
+ "device-id": oFsm.deviceID, "tcont": oFsm.alloc0ID,
+ "uni-id": oFsm.pOnuUniPort.UniID,
+ "techProfile-id": oFsm.techProfileID})
+ go func(aPAFsm *cmn.AdapterFsm) {
+ if aPAFsm != nil && aPAFsm.PFsm != nil {
+ _ = aPAFsm.PFsm.Event(aniEvRxPrioqsResp)
+ }
+ }(oFsm.PAdaptFsm)
+ return
+ }
+ const cu16StrictPrioWeight uint16 = 0xFFFF
+ //find all upstream PrioQueues related to this T-Cont
+ loQueueMap := ordered_map.NewOrderedMap()
+ for _, gemPortAttribs := range oFsm.gemPortAttribsSlice {
+ if gemPortAttribs.isMulticast {
+ logger.Debugw(ctx, "UniPonAniConfigFsm Port is Multicast, ignoring PQs", log.Fields{
+ "device-id": oFsm.deviceID, "GemPort": gemPortAttribs.gemPortID,
+ "prioString": gemPortAttribs.pbitString})
+ continue
+ }
+ if gemPortAttribs.qosPolicy == "WRR" {
+ if _, ok := loQueueMap.Get(gemPortAttribs.upQueueID); !ok {
+ //key does not yet exist
+ loQueueMap.Set(gemPortAttribs.upQueueID, uint16(gemPortAttribs.weight))
+ }
+ } else {
+ loQueueMap.Set(gemPortAttribs.upQueueID, cu16StrictPrioWeight) //use invalid weight value to indicate SP
+ }
+ }
+
+ trafficSchedPtrSetSupported := false
+ loOnu2g := oFsm.pOnuDB.GetMe(me.Onu2GClassID, cmn.Onu2gMeID)
+ if loOnu2g == nil {
+ logger.Errorw(ctx, "onu2g is nil, cannot read qos configuration flexibility parameter",
+ log.Fields{"device-id": oFsm.deviceID})
+ _ = oFsm.PAdaptFsm.PFsm.Event(aniEvReset)
+ return
+ }
+ returnVal := loOnu2g["QualityOfServiceQosConfigurationFlexibility"]
+ if returnVal != nil {
+ if qosCfgFlexParam, err := oFsm.pOnuDB.GetUint16Attrib(returnVal); err == nil {
+ trafficSchedPtrSetSupported = qosCfgFlexParam&bitTrafficSchedulerPtrSetPermitted == bitTrafficSchedulerPtrSetPermitted
+ logger.Debugw(ctx, "trafficSchedPtrSetSupported set",
+ log.Fields{"qosCfgFlexParam": qosCfgFlexParam, "trafficSchedPtrSetSupported": trafficSchedPtrSetSupported})
+ } else {
+ logger.Errorw(ctx, "Cannot extract qos configuration flexibility parameter",
+ log.Fields{"device-id": oFsm.deviceID})
+ _ = oFsm.PAdaptFsm.PFsm.Event(aniEvReset)
+ return
+ }
+ } else {
+ logger.Errorw(ctx, "Cannot read qos configuration flexibility parameter",
+ log.Fields{"device-id": oFsm.deviceID})
+ _ = oFsm.PAdaptFsm.PFsm.Event(aniEvReset)
+ return
+ }
+
+ //TODO: assumption here is that ONU data uses SP setting in the T-Cont and WRR in the TrafficScheduler
+ // if that is not the case, the reverse case could be checked and reacted accordingly or if the
+ // complete chain is not valid, then some error should be thrown and configuration can be aborted
+ // or even be finished without correct SP/WRR setting
+
+ //TODO: search for the (WRR)trafficScheduler related to the T-Cont of this queue
+ //By now assume fixed value 0x8000, which is the only announce BBSIM TrafficScheduler,
+ // even though its T-Cont seems to be wrong ...
+ loTrafficSchedulerEID := 0x8000
+ //for all found queues
+ iter := loQueueMap.IterFunc()
+ for kv, ok := iter(); ok; kv, ok = iter() {
+ queueIndex := (kv.Key).(uint16)
+ meParams := me.ParamData{
+ EntityID: queueIndex,
+ Attributes: make(me.AttributeValueMap),
+ }
+ if trafficSchedPtrSetSupported {
+ if (kv.Value).(uint16) == cu16StrictPrioWeight {
+ //StrictPrio indication
+ logger.Debugw(ctx, "uniPonAniConfigFsm Tx Set::PrioQueue to StrictPrio", log.Fields{
+ "EntitytId": strconv.FormatInt(int64(queueIndex), 16),
+ "device-id": oFsm.deviceID})
+ meParams.Attributes["TrafficSchedulerPointer"] = 0 //ensure T-Cont defined StrictPrio scheduling
+ } else {
+ //WRR indication
+ logger.Debugw(ctx, "uniPonAniConfigFsm Tx Set::PrioQueue to WRR", log.Fields{
+ "EntitytId": strconv.FormatInt(int64(queueIndex), 16),
+ "Weight": kv.Value,
+ "device-id": oFsm.deviceID})
+ meParams.Attributes["TrafficSchedulerPointer"] = loTrafficSchedulerEID //ensure assignment of the relevant trafficScheduler
+ meParams.Attributes["Weight"] = uint8(kv.Value.(uint16))
+ }
+ } else {
+ // setting Traffic Scheduler (TS) pointer is not supported unless we point to another TS that points to the same TCONT.
+ // For now lets use TS that is hardwired in the ONU and just update the weight in case of WRR, which in fact is all we need at the moment.
+ // The code could get unnecessarily convoluted if we provide the flexibility try to find and point to another TS that points to the same TCONT.
+ if (kv.Value).(uint16) == cu16StrictPrioWeight { // SP case, nothing to be done. Proceed to the next queue
+ logger.Debugw(ctx, "uniPonAniConfigFsm Tx Set::PrioQueue to StrictPrio, traffic sched ptr set unsupported", log.Fields{
+ "EntitytId": strconv.FormatInt(int64(queueIndex), 16),
+ "device-id": oFsm.deviceID})
+ continue
+ }
+ // WRR case, update weight.
+ logger.Debugw(ctx, "uniPonAniConfigFsm Tx Set::PrioQueue to WRR, traffic sched ptr set unsupported", log.Fields{
+ "EntitytId": strconv.FormatInt(int64(queueIndex), 16),
+ "Weight": kv.Value,
+ "device-id": oFsm.deviceID})
+ meParams.Attributes["Weight"] = uint8(kv.Value.(uint16))
+ }
+ oFsm.mutexPLastTxMeInstance.Lock()
+ meInstance, err := oFsm.pOmciCC.SendSetPrioQueueVar(log.WithSpanFromContext(context.TODO(), ctx), oFsm.pDeviceHandler.GetOmciTimeout(), true,
+ oFsm.PAdaptFsm.CommChan, meParams)
+ if err != nil {
+ oFsm.mutexPLastTxMeInstance.Unlock()
+ logger.Errorw(ctx, "PrioQueueVar set failed, aborting UniPonAniConfigFsm!",
+ log.Fields{"device-id": oFsm.deviceID})
+ _ = oFsm.PAdaptFsm.PFsm.Event(aniEvReset)
+ return
+ }
+ //accept also nil as (error) return value for writing to LastTx
+ // - this avoids misinterpretation of new received OMCI messages
+ oFsm.pLastTxMeInstance = meInstance
+ oFsm.mutexPLastTxMeInstance.Unlock()
+
+ //verify response
+ err = oFsm.waitforOmciResponse(ctx)
+ if err != nil {
+ logger.Errorw(ctx, "PrioQueue set failed, aborting AniConfig FSM!",
+ log.Fields{"device-id": oFsm.deviceID, "QueueId": strconv.FormatInt(int64(queueIndex), 16)})
+ _ = oFsm.PAdaptFsm.PFsm.Event(aniEvReset)
+ return
+ }
+
+ //TODO: In case of WRR setting of the GemPort/PrioQueue it might further be necessary to
+ // write the assigned trafficScheduler with the requested Prio to be considered in the StrictPrio scheduling
+ // of the (next upstream) assigned T-Cont, which is f(prioQueue[priority]) - in relation to other SP prioQueues
+ // not yet done because of BBSIM TrafficScheduler issues (and not done in py code as well)
+
+ } //for all upstream prioQueues
+
+ // if Config has been done for all PrioQueue instances let the FSM proceed
+ logger.Debugw(ctx, "PrioQueue set loop finished", log.Fields{"device-id": oFsm.deviceID})
+ _ = oFsm.PAdaptFsm.PFsm.Event(aniEvRxPrioqsResp)
+}
+
+func (oFsm *UniPonAniConfigFsm) waitforOmciResponse(ctx context.Context) error {
+ oFsm.mutexIsAwaitingResponse.Lock()
+ if oFsm.isCanceled {
+ // FSM already canceled before entering wait
+ logger.Debugw(ctx, "UniPonAniConfigFsm wait-for-multi-entity-response aborted (on enter)", log.Fields{"for device-id": oFsm.deviceID})
+ oFsm.mutexIsAwaitingResponse.Unlock()
+ return fmt.Errorf(cmn.CErrWaitAborted)
+ }
+ oFsm.isAwaitingResponse = true
+ oFsm.mutexIsAwaitingResponse.Unlock()
+ select {
+ // maybe be also some outside cancel (but no context modeled for the moment ...)
+ // case <-ctx.Done():
+ // logger.Infow("LockState-bridge-init message reception canceled", log.Fields{"for device-id": oFsm.deviceID})
+ case <-time.After(oFsm.pOmciCC.GetMaxOmciTimeoutWithRetries() * time.Second): //3s was detected to be to less in 8*8 bbsim test with debug Info/Debug
+ logger.Warnw(ctx, "UniPonAniConfigFsm multi entity timeout", log.Fields{"for device-id": oFsm.deviceID})
+ oFsm.mutexIsAwaitingResponse.Lock()
+ oFsm.isAwaitingResponse = false
+ oFsm.mutexIsAwaitingResponse.Unlock()
+ return fmt.Errorf("uniPonAniConfigFsm multi entity timeout %s", oFsm.deviceID)
+ case success := <-oFsm.omciMIdsResponseReceived:
+ if success {
+ logger.Debugw(ctx, "UniPonAniConfigFsm multi entity response received", log.Fields{"for device-id": oFsm.deviceID})
+ oFsm.mutexIsAwaitingResponse.Lock()
+ oFsm.isAwaitingResponse = false
+ oFsm.mutexIsAwaitingResponse.Unlock()
+ return nil
+ }
+ // waiting was aborted (probably on external request)
+ logger.Debugw(ctx, "UniPonAniConfigFsm wait-for-multi-entity-response aborted", log.Fields{"for device-id": oFsm.deviceID})
+ oFsm.mutexIsAwaitingResponse.Lock()
+ oFsm.isAwaitingResponse = false
+ oFsm.mutexIsAwaitingResponse.Unlock()
+ return fmt.Errorf(cmn.CErrWaitAborted)
+ }
+}
+
+func (oFsm *UniPonAniConfigFsm) setChanSet(flagValue bool) {
+ oFsm.mutexChanSet.Lock()
+ oFsm.chanSet = flagValue
+ oFsm.mutexChanSet.Unlock()
+}
+
+func (oFsm *UniPonAniConfigFsm) isChanSet() bool {
+ oFsm.mutexChanSet.RLock()
+ flagValue := oFsm.chanSet
+ oFsm.mutexChanSet.RUnlock()
+ return flagValue
+}