[VOL-4023] openonuAdapterGo - Ani side configuration fails while waiting for FlowDeletion, and upgrade looplab/fsm

Signed-off-by: mpagenko <michael.pagenkopf@adtran.com>
Change-Id: I9053d2ee774ff46c6d3a9cff5743a141bf14ad58
diff --git a/internal/pkg/onuadaptercore/omci_ani_config.go b/internal/pkg/onuadaptercore/omci_ani_config.go
index 19167f6..c7433c8 100644
--- a/internal/pkg/onuadaptercore/omci_ani_config.go
+++ b/internal/pkg/onuadaptercore/omci_ani_config.go
@@ -127,6 +127,8 @@
 	gemPortAttribsSlice      []ponAniGemPortAttribs
 	pLastTxMeInstance        *me.ManagedEntity
 	requestEventOffset       uint8 //used to indicate ConfigDone or Removed using successor (enum)
+	isWaitingForFlowDelete   bool
+	waitFlowDeleteChannel    chan bool
 }
 
 //newUniPonAniConfigFsm is the 'constructor' for the state machine to config the PON ANI ports of ONU UNI ports via OMCI
@@ -145,6 +147,7 @@
 		chanSet:        false,
 	}
 	instFsm.uniTpKey = uniTP{uniID: apUniPort.uniID, tpID: aTechProfileID}
+	instFsm.waitFlowDeleteChannel = make(chan bool)
 
 	instFsm.pAdaptFsm = NewAdapterFsm(aName, instFsm.deviceID, aCommChannel)
 	if instFsm.pAdaptFsm == nil {
@@ -194,7 +197,7 @@
 			// exceptional treatment for all states except aniStResetting
 			{Name: aniEvReset, Src: []string{aniStStarting, aniStCreatingDot1PMapper, aniStCreatingMBPCD,
 				aniStSettingTconts, aniStCreatingGemNCTPs, aniStCreatingGemIWs, aniStSettingPQs, aniStSettingDot1PMapper,
-				aniStConfigDone, aniStRemovingGemIW, aniStRemovingGemNCTP,
+				aniStConfigDone, aniStRemovingGemIW, aniStWaitingFlowRem, aniStRemovingGemNCTP,
 				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},
@@ -212,6 +215,7 @@
 			("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_" + aniStResetTcont):          func(e *fsm.Event) { instFsm.enterResettingTcont(ctx, e) },
 			("enter_" + aniStRemDot1PMapper):      func(e *fsm.Event) { instFsm.enterRemoving1pMapper(ctx, e) },
@@ -249,6 +253,10 @@
 		//use channel to indicate that the response waiting shall be aborted
 		oFsm.omciMIdsResponseReceived <- false
 	}
+	if oFsm.isWaitingForFlowDelete {
+		//use channel to indicate that the response waiting shall be aborted
+		oFsm.waitFlowDeleteChannel <- false
+	}
 	// 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 {
@@ -672,9 +680,14 @@
 }
 
 func (oFsm *uniPonAniConfigFsm) enterRemovingGemIW(ctx context.Context, e *fsm.Event) {
-
 	oFsm.pUniTechProf.mutexTPState.Lock()
-	if oFsm.pDeviceHandler.UniVlanConfigFsmMap[oFsm.pOnuUniPort.uniID].IsFlowRemovePending() {
+	//flush the waitFlowDeleteChannel - possibly already/still set by some previous activity
+	select {
+	case <-oFsm.waitFlowDeleteChannel:
+		logger.Debug(ctx, "flushed waitFlowDeleteChannel")
+	default:
+	}
+	if oFsm.pDeviceHandler.UniVlanConfigFsmMap[oFsm.pOnuUniPort.uniID].IsFlowRemovePending(oFsm.waitFlowDeleteChannel) {
 		oFsm.pUniTechProf.mutexTPState.Unlock()
 		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})
@@ -707,6 +720,76 @@
 	oFsm.pLastTxMeInstance = meInstance
 }
 
+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(10 * 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 *AdapterFsm) {
+				if aPAFsm != nil && aPAFsm.pFsm != nil {
+					_ = oFsm.pAdaptFsm.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 *AdapterFsm) {
+					if aPAFsm != nil && aPAFsm.pFsm != nil {
+						_ = oFsm.pAdaptFsm.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 *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.Lock()
 	loGemPortID := (*(oFsm.pUniTechProf.mapRemoveGemEntry[oFsm.uniTpKey])).gemPortID