[VOL-3604] flow/techProfile handling after ONU reboot/down/disable must be revised -> version 0.1.13-dev137

Signed-off-by: mpagenko <michael.pagenkopf@adtran.com>
Change-Id: I8bd846170d62f15c3a83ce6b10911707e0cd6176
diff --git a/internal/pkg/onuadaptercore/omci_vlan_config.go b/internal/pkg/onuadaptercore/omci_vlan_config.go
index b8605e0..2fbc5db 100644
--- a/internal/pkg/onuadaptercore/omci_vlan_config.go
+++ b/internal/pkg/onuadaptercore/omci_vlan_config.go
@@ -84,6 +84,7 @@
 	vlanEvRxConfigVtfd    = "vlanEvRxConfigVtfd"
 	vlanEvRxConfigEvtocd  = "vlanEvRxConfigEvtocd"
 	vlanEvIncrFlowConfig  = "vlanEvIncrFlowConfig"
+	vlanEvRenew           = "vlanEvRenew"
 	vlanEvRemFlowConfig   = "vlanEvRemFlowConfig"
 	vlanEvRemFlowDone     = "vlanEvRemFlowDone"
 	vlanEvFlowDataRemoved = "vlanEvFlowDataRemoved"
@@ -150,6 +151,7 @@
 	vtfdID                      uint16
 	evtocdID                    uint16
 	pLastTxMeInstance           *me.ManagedEntity
+	requestEventOffset          uint8
 }
 
 //NewUniVlanConfigFsm is the 'constructor' for the state machine to config the PON ANI ports
@@ -190,6 +192,7 @@
 			{Name: vlanEvRxConfigEvtocd, Src: []string{vlanStConfigEvtocd, vlanStConfigIncrFlow},
 				Dst: vlanStConfigDone},
 			{Name: vlanEvIncrFlowConfig, Src: []string{vlanStConfigDone}, Dst: vlanStConfigIncrFlow},
+			{Name: vlanEvRenew, Src: []string{vlanStConfigIncrFlow}, Dst: vlanStStarting},
 			{Name: vlanEvRemFlowConfig, Src: []string{vlanStConfigDone}, Dst: vlanStRemoveFlow},
 			{Name: vlanEvRemFlowDone, Src: []string{vlanStRemoveFlow}, Dst: vlanStCleanupDone},
 			{Name: vlanEvFlowDataRemoved, Src: []string{vlanStCleanupDone}, Dst: vlanStConfigDone},
@@ -403,7 +406,25 @@
 				"device-id": oFsm.deviceID, "flow-number": oFsm.numUniFlows})
 			return fmt.Errorf(" UniVlanConfigFsm flow limit exceeded %s", oFsm.deviceID)
 		}
-	} //new flow
+	} else {
+		// no activity within the FSM for OMCI processing, the deviceReason may be updated immediately
+		if oFsm.numUniFlows == oFsm.configuredUniFlow {
+			//all requested rules really have been configured
+			// state transition notification is checked in deviceHandler
+			if oFsm.pDeviceHandler != nil {
+				//also the related TechProfile was already configured
+				logger.Debugw("UniVlanConfigFsm rule already set - send immediate add-success event for reason update", log.Fields{
+					"device-id": oFsm.deviceID})
+				go oFsm.pDeviceHandler.deviceProcStatusUpdate(oFsm.requestEvent)
+			}
+		} else {
+			//  avoid device reason update as the rule config connected to this flow may still be in progress
+			//  and the device reason should only be updated on success of rule config
+			logger.Debugw("UniVlanConfigFsm rule already set but configuration ongoing, suppress early add-success event for reason update",
+				log.Fields{"device-id": oFsm.deviceID,
+					"NumberofRules": oFsm.numUniFlows, "Configured rules": oFsm.configuredUniFlow})
+		}
+	}
 
 	if !flowEntryMatch || flowCookieModify { // some change was done to the flow entries
 		//permanently store flow config for reconcile case
@@ -475,20 +496,26 @@
 					} // if not in the appropriate state a new entry will be automatically considered later
 					//   when the configDone state is reached
 				} else {
-					logger.Debugw("UniVlanConfigFsm flow removal - flow persists with other cookies", log.Fields{
-						"device-id": oFsm.deviceID})
 					//cut off the requested cookie by slicing out this element
 					oFsm.uniVlanFlowParamsSlice[flow].CookieSlice = append(
 						oFsm.uniVlanFlowParamsSlice[flow].CookieSlice[:i],
 						oFsm.uniVlanFlowParamsSlice[flow].CookieSlice[i+1:]...)
-					logger.Debugw("UniVlanConfigFsm flow removal - still valid cookies for this flow", log.Fields{
+					// no activity within the FSM for OMCI processing, the deviceReason may be updated immediately
+					// state transition notification is checked in deviceHandler
+					if oFsm.pDeviceHandler != nil {
+						//making use of the add->remove successor enum assumption/definition
+						go oFsm.pDeviceHandler.deviceProcStatusUpdate(OnuDeviceEvent((uint8(oFsm.requestEvent) + 1)))
+					}
+					logger.Debugw("UniVlanConfigFsm flow removal - rule persists with still valid cookies", log.Fields{
 						"device-id": oFsm.deviceID, "cookies": oFsm.uniVlanFlowParamsSlice[flow].CookieSlice})
 				}
 
 				//permanently store the modified flow config for reconcile case
-				if err := oFsm.pDeviceHandler.storePersUniFlowConfig(oFsm.pOnuUniPort.uniID, &oFsm.uniVlanFlowParamsSlice); err != nil {
-					logger.Errorw(err.Error(), log.Fields{"device-id": oFsm.deviceID})
-					return err
+				if oFsm.pDeviceHandler != nil {
+					if err := oFsm.pDeviceHandler.storePersUniFlowConfig(oFsm.pOnuUniPort.uniID, &oFsm.uniVlanFlowParamsSlice); err != nil {
+						logger.Errorw(err.Error(), log.Fields{"device-id": oFsm.deviceID})
+						return err
+					}
 				}
 
 				break //found the cookie - no further search for this requested cookie
@@ -502,6 +529,12 @@
 		logger.Warnw("UniVlanConfigFsm flow removal - remove-cookie not found", log.Fields{
 			"device-id": oFsm.deviceID, "remove-cookie": aCookie})
 		// but accept the request with success as no such cookie (flow) does exist
+		// no activity within the FSM for OMCI processing, the deviceReason may be updated immediately
+		// state transition notification is checked in deviceHandler
+		if oFsm.pDeviceHandler != nil {
+			//making use of the add->remove successor enum assumption/definition
+			go oFsm.pDeviceHandler.deviceProcStatusUpdate(OnuDeviceEvent((uint8(oFsm.requestEvent) + 1)))
+		}
 		return nil
 	} //unknown cookie
 
@@ -588,6 +621,7 @@
 func (oFsm *UniVlanConfigFsm) enterConfigEvtocd(e *fsm.Event) {
 	logger.Debugw("UniVlanConfigFsm - start config EVTOCD loop", log.Fields{
 		"in state": e.FSM.Current(), "device-id": oFsm.deviceID})
+	oFsm.requestEventOffset = 0 //0 offset for last flow-add activity
 	go oFsm.performConfigEvtocdEntries(0)
 }
 
@@ -617,7 +651,8 @@
 	// it might appear that some flows are requested also after 'flowPushed' event has been generated ...
 	// state transition notification is checked in deviceHandler
 	if oFsm.pDeviceHandler != nil {
-		oFsm.pDeviceHandler.deviceProcStatusUpdate(oFsm.requestEvent)
+		//making use of the add->remove successor enum assumption/definition
+		go oFsm.pDeviceHandler.deviceProcStatusUpdate(OnuDeviceEvent((uint8(oFsm.requestEvent) + oFsm.requestEventOffset)))
 	}
 }
 
@@ -627,6 +662,18 @@
 		"device-id": oFsm.deviceID})
 	oFsm.mutexFlowParams.Lock()
 
+	if oFsm.configuredUniFlow == 0 {
+		oFsm.mutexFlowParams.Unlock()
+		// this is a restart with a complete new flow, we can re-use the initial flow config control
+		// including the check, if the related techProfile is (still) available (probably also removed in between)
+		// calling some FSM event must be decoupled
+		pConfigVlanStateBaseFsm := oFsm.pAdaptFsm.pFsm
+		go func(a_pBaseFsm *fsm.FSM) {
+			_ = a_pBaseFsm.Event(vlanEvRenew)
+		}(pConfigVlanStateBaseFsm)
+		return
+	}
+
 	if oFsm.uniVlanFlowParamsSlice[oFsm.configuredUniFlow].VlanRuleParams.SetVid == uint32(of.OfpVlanId_OFPVID_PRESENT) {
 		// meaning transparent setup - no specific VTFD setting required
 		oFsm.mutexFlowParams.Unlock()
@@ -697,10 +744,14 @@
 		if err != nil {
 			logger.Errorw("VTFD create/set failed, aborting VlanConfig FSM!",
 				log.Fields{"device-id": oFsm.deviceID})
-			_ = oFsm.pAdaptFsm.pFsm.Event(vlanEvReset)
+			pConfigVlanStateBaseFsm := oFsm.pAdaptFsm.pFsm
+			go func(a_pBaseFsm *fsm.FSM) {
+				_ = a_pBaseFsm.Event(vlanEvReset)
+			}(pConfigVlanStateBaseFsm)
 			return
 		}
 	}
+	oFsm.requestEventOffset = 0 //0 offset for last flow-add activity
 	go oFsm.performConfigEvtocdEntries(oFsm.configuredUniFlow)
 }
 
@@ -710,6 +761,8 @@
 		"in state": e.FSM.Current(), "with last cookie": oFsm.uniRemoveFlowsSlice[0].cookie,
 		"device-id": oFsm.deviceID})
 
+	pConfigVlanStateBaseFsm := oFsm.pAdaptFsm.pFsm
+	loAllowSpecificOmciConfig := oFsm.pDeviceHandler.ReadyForSpecificOmciConfig
 	loVlanEntryClear := uint8(0)
 	loVlanEntryRmPos := uint8(0x80) //with indication 'invalid' in bit 7
 	//shallow copy is sufficient as no reference variables are used within struct
@@ -732,10 +785,15 @@
 			logger.Debugw("UniVlanConfigFsm: VTFD delete (no more vlan filters)",
 				log.Fields{"current vlan list": oFsm.vlanFilterList,
 					"in state": e.FSM.Current(), "device-id": oFsm.deviceID})
-			loVlanEntryClear = 1 //full VlanFilter clear request
-			meInstance := oFsm.pOmciCC.sendDeleteVtfd(context.TODO(), ConstDefaultOmciTimeout, true,
-				oFsm.pAdaptFsm.commChan, oFsm.vtfdID)
-			oFsm.pLastTxMeInstance = meInstance
+			loVlanEntryClear = 1           //full VlanFilter clear request
+			if loAllowSpecificOmciConfig { //specific OMCI config is expected to work acc. to the device state
+				meInstance := oFsm.pOmciCC.sendDeleteVtfd(context.TODO(), ConstDefaultOmciTimeout, true,
+					oFsm.pAdaptFsm.commChan, oFsm.vtfdID)
+				oFsm.pLastTxMeInstance = meInstance
+			} else {
+				logger.Debugw("UniVlanConfigFsm delete VTFD OMCI handling skipped based on device state", log.Fields{
+					"device-id": oFsm.deviceID, "device-state": oFsm.pDeviceHandler.deviceReason})
+			}
 		} else {
 			//many VTFD already should exists - find and remove the one concerned by the actual remove rule
 			//  by updating the VTFD per set command with new valid list
@@ -764,29 +822,39 @@
 					"EntitytId":     strconv.FormatInt(int64(oFsm.vtfdID), 16),
 					"new vlan list": vtfdFilterList, "device-id": oFsm.deviceID})
 
-				meParams := me.ParamData{
-					EntityID: oFsm.vtfdID,
-					Attributes: me.AttributeValueMap{
-						"VlanFilterList":  vtfdFilterList,
-						"NumberOfEntries": (oFsm.numVlanFilterEntries - 1), //one element less
-					},
+				if loAllowSpecificOmciConfig { //specific OMCI config is expected to work acc. to the device state
+					meParams := me.ParamData{
+						EntityID: oFsm.vtfdID,
+						Attributes: me.AttributeValueMap{
+							"VlanFilterList":  vtfdFilterList,
+							"NumberOfEntries": (oFsm.numVlanFilterEntries - 1), //one element less
+						},
+					}
+					meInstance := oFsm.pOmciCC.sendSetVtfdVar(context.TODO(), ConstDefaultOmciTimeout, true,
+						oFsm.pAdaptFsm.commChan, meParams)
+					oFsm.pLastTxMeInstance = meInstance
+				} else {
+					logger.Debugw("UniVlanConfigFsm set VTFD OMCI handling skipped based on device state", log.Fields{
+						"device-id": oFsm.deviceID, "device-state": oFsm.pDeviceHandler.deviceReason})
 				}
-				meInstance := oFsm.pOmciCC.sendSetVtfdVar(context.TODO(), ConstDefaultOmciTimeout, true,
-					oFsm.pAdaptFsm.commChan, meParams)
-				oFsm.pLastTxMeInstance = meInstance
 			} else {
 				logger.Warnw("UniVlanConfigFsm: requested VLAN for removal not found in list - ignore and continue (no VTFD set)",
 					log.Fields{"device-id": oFsm.deviceID})
 			}
 		}
 		if loVlanEntryClear > 0 {
-			//waiting on response
-			err := oFsm.waitforOmciResponse()
-			if err != nil {
-				logger.Errorw("VTFD delete/reset failed, aborting VlanConfig FSM!",
-					log.Fields{"device-id": oFsm.deviceID})
-				_ = oFsm.pAdaptFsm.pFsm.Event(vlanEvReset)
-				return
+			if loAllowSpecificOmciConfig { //specific OMCI config is expected to work acc. to the device state
+				//waiting on response
+				err := oFsm.waitforOmciResponse()
+				if err != nil {
+					logger.Errorw("VTFD delete/reset failed, aborting VlanConfig FSM!",
+						log.Fields{"device-id": oFsm.deviceID})
+					// calling some FSM event must be decoupled
+					go func(a_pBaseFsm *fsm.FSM) {
+						_ = a_pBaseFsm.Event(vlanEvReset)
+					}(pConfigVlanStateBaseFsm)
+					return
+				}
 			}
 
 			if loVlanEntryClear == 1 {
@@ -803,7 +871,17 @@
 		}
 	}
 
-	go oFsm.removeEvtocdEntries(loRuleParams)
+	if loAllowSpecificOmciConfig { //specific OMCI config is expected to work acc. to the device state
+		go oFsm.removeEvtocdEntries(loRuleParams)
+	} else {
+		// OMCI processing is not done, expectation is to have the ONU in some basic config state accordingly
+		logger.Debugw("UniVlanConfigFsm remove EVTOCD OMCI handling skipped based on device state", log.Fields{
+			"device-id": oFsm.deviceID})
+		// calling some FSM event must be decoupled
+		go func(a_pBaseFsm *fsm.FSM) {
+			_ = a_pBaseFsm.Event(vlanEvRemFlowDone)
+		}(pConfigVlanStateBaseFsm)
+	}
 }
 
 func (oFsm *UniVlanConfigFsm) enterVlanCleanupDone(e *fsm.Event) {
@@ -825,6 +903,7 @@
 	}
 	oFsm.mutexFlowParams.Unlock()
 
+	oFsm.requestEventOffset = 1 //offset 1 for last flow-remove activity
 	//return to the basic config verification state
 	pConfigVlanStateAFsm := oFsm.pAdaptFsm
 	if pConfigVlanStateAFsm != nil {
@@ -1282,7 +1361,7 @@
 		}
 	}
 
-	// if Config has been done for all GemPort instances let the FSM proceed
+	// if Config has been done for all EVTOCD entries let the FSM proceed
 	logger.Debugw("EVTOCD set loop finished", log.Fields{"device-id": oFsm.deviceID})
 	oFsm.configuredUniFlow++ // one (more) flow configured
 	_ = oFsm.pAdaptFsm.pFsm.Event(vlanEvRxConfigEvtocd)
@@ -1483,7 +1562,7 @@
 		}
 	}
 
-	// if Config has been done for all GemPort instances let the FSM proceed
+	// if Config has been done for all EVTOCD entries let the FSM proceed
 	logger.Debugw("EVTOCD filter remove loop finished", log.Fields{"device-id": oFsm.deviceID})
 	_ = oFsm.pAdaptFsm.pFsm.Event(vlanEvRemFlowDone)
 }