ONU SW upgrade API change - step 1 to allow for straightforward upgrade (with activate/commit options)

Signed-off-by: mpagenko <michael.pagenkopf@adtran.com>
Change-Id: I6808f8f41db40faa060ed7198025abdda8506ae7
diff --git a/internal/pkg/onuadaptercore/device_handler.go b/internal/pkg/onuadaptercore/device_handler.go
index 07fa67b..fc29ac2 100644
--- a/internal/pkg/onuadaptercore/device_handler.go
+++ b/internal/pkg/onuadaptercore/device_handler.go
@@ -1100,6 +1100,182 @@
 	return err
 }
 
+//onuSwUpgradeAfterDownload initiates the SW download transfer to the ONU with activate and commit options
+// after the OnuImage has been downloaded to the adapter, called in background
+func (dh *deviceHandler) onuSwUpgradeAfterDownload(ctx context.Context, apImageRequest *voltha.DeviceImageDownloadRequest,
+	apDownloadManager *fileDownloadManager, aImageIdentifier string) {
+
+	var err error
+	pDevEntry := dh.getOnuDeviceEntry(ctx, true)
+	if pDevEntry == nil {
+		logger.Errorw(ctx, "start Onu SW upgrade rejected: no valid OnuDevice", log.Fields{"device-id": dh.deviceID})
+		return
+	}
+
+	var inactiveImageID uint16
+	if inactiveImageID, err = pDevEntry.GetInactiveImageMeID(ctx); err == nil {
+		logger.Debugw(ctx, "onuSwUpgrade requested", log.Fields{
+			"device-id": dh.deviceID, "image-version": apImageRequest.Image.Version, "to onu-image": inactiveImageID})
+		dh.lockUpgradeFsm.Lock()
+		defer dh.lockUpgradeFsm.Unlock()
+		if dh.pOnuUpradeFsm == nil {
+			//OmciOnuSwUpgradeDone could be used to create some Kafka event with information on upgrade completion,
+			//  but none yet defined
+			err = dh.createOnuUpgradeFsm(ctx, pDevEntry, OmciOnuSwUpgradeDone)
+			if err == nil {
+				if err = dh.pOnuUpradeFsm.SetDownloadParamsAfterDownload(ctx, inactiveImageID,
+					apImageRequest, apDownloadManager, aImageIdentifier, dh.pOpenOnuAc.dlToOnuTimeout4M); err != nil {
+					logger.Errorw(ctx, "onu upgrade fsm could not set parameters", log.Fields{
+						"device-id": dh.deviceID, "error": err})
+					return
+				}
+			} else {
+				logger.Errorw(ctx, "onu upgrade fsm could not be created", log.Fields{
+					"device-id": dh.deviceID, "error": err})
+			}
+			return
+		}
+		//OnuSw upgrade already running - restart (with possible abort of running)
+		logger.Debugw(ctx, "Onu SW upgrade already running - abort", log.Fields{"device-id": dh.deviceID})
+		pUpgradeStatemachine := dh.pOnuUpradeFsm.pAdaptFsm.pFsm
+		if pUpgradeStatemachine != nil {
+			if err = pUpgradeStatemachine.Event(upgradeEvAbort); err != nil {
+				logger.Errorw(ctx, "onu upgrade fsm could not abort a running processing", log.Fields{
+					"device-id": dh.deviceID, "error": err})
+				return
+			}
+			//TODO!!!: wait for 'ready' to start and configure - see above SetDownloadParams()
+			// for now a second start of download should work again - must still be initiated by user
+		} else { //should never occur
+			logger.Errorw(ctx, "onu upgrade fsm inconsistent setup", log.Fields{
+				"device-id": dh.deviceID})
+		}
+		return
+	}
+	logger.Errorw(ctx, "start Onu SW upgrade rejected: no inactive image", log.Fields{
+		"device-id": dh.deviceID, "error": err})
+}
+
+//onuSwActivateRequest ensures activation of the requested image with commit options
+func (dh *deviceHandler) onuSwActivateRequest(ctx context.Context, aVersion string, aCommitRequest bool) {
+	var err error
+	//SW activation for the ONU image may have two use cases, one of them is selected here according to following prioritization:
+	//  1.) activation of the image for a started upgrade process (in case the running upgrade runs on the requested image)
+	//  2.) activation of the inactive image
+
+	pDevEntry := dh.getOnuDeviceEntry(ctx, true)
+	if pDevEntry == nil {
+		logger.Errorw(ctx, "Onu image activation rejected: no valid OnuDevice", log.Fields{"device-id": dh.deviceID})
+		return
+	}
+	dh.lockUpgradeFsm.RLock()
+	if dh.pOnuUpradeFsm != nil {
+		dh.lockUpgradeFsm.RUnlock()
+		onuVolthaDevice, getErr := dh.coreProxy.GetDevice(log.WithSpanFromContext(context.TODO(), ctx),
+			dh.deviceID, dh.deviceID)
+		if getErr != nil || onuVolthaDevice == nil {
+			logger.Errorw(ctx, "Failed to fetch Onu device for image activation", log.Fields{"device-id": dh.deviceID, "err": getErr})
+			return
+		}
+		//  use the OnuVendor identification from this device for the internal unique name
+		imageIdentifier := onuVolthaDevice.VendorId + aVersion //head on vendor ID of the ONU
+		// 1.) check a started upgrade process and rely the activation request to it
+		if err = dh.pOnuUpradeFsm.SetActivationParamsRunning(ctx, imageIdentifier, aCommitRequest); err != nil {
+			logger.Errorw(ctx, "onu upgrade fsm did not accept activation while running", log.Fields{
+				"device-id": dh.deviceID, "error": err})
+		} else {
+			logger.Debugw(ctx, "image activation acknowledged by onu upgrade processing", log.Fields{
+				"device-id": dh.deviceID, "image-id": imageIdentifier})
+		}
+		//if some ONU upgrade is ongoing we do not accept some explicit ONU image-version related activation
+		//   (even though parameter setting is not accepted)
+		return
+	} //else
+	dh.lockUpgradeFsm.RUnlock()
+
+	// 2.) check if requested image-version equals the inactive one and start its activation
+	//   (image version is not [yet] checked - would be possible, but with increased effort ...)
+	var inactiveImageID uint16
+	if inactiveImageID, err = pDevEntry.GetInactiveImageMeID(ctx); err != nil || inactiveImageID > 1 {
+		logger.Errorw(ctx, "get inactive image failed", log.Fields{
+			"device-id": dh.deviceID, "err": err, "image-id": inactiveImageID})
+		return
+	}
+	err = dh.createOnuUpgradeFsm(ctx, pDevEntry, OmciOnuSwUpgradeDone)
+	if err == nil {
+		if err = dh.pOnuUpradeFsm.SetActivationParamsStart(ctx, aVersion,
+			inactiveImageID, aCommitRequest); err != nil {
+			logger.Errorw(ctx, "onu upgrade fsm did not accept activation to start", log.Fields{
+				"device-id": dh.deviceID, "error": err})
+			return
+		}
+		logger.Debugw(ctx, "inactive image activation acknowledged by onu upgrade", log.Fields{
+			"device-id": dh.deviceID, "image-version": aVersion})
+		return
+	} //else
+	logger.Errorw(ctx, "onu upgrade fsm could not be created", log.Fields{
+		"device-id": dh.deviceID, "error": err})
+}
+
+//onuSwCommitRequest ensures commitment of the requested image
+func (dh *deviceHandler) onuSwCommitRequest(ctx context.Context, aVersion string) {
+	var err error
+	//SW commitment for the ONU image may have two use cases, one of them is selected here according to following prioritization:
+	//  1.) commitment of the image for a started upgrade process (in case the running upgrade runs on the requested image)
+	//  2.) commitment of the active image
+
+	pDevEntry := dh.getOnuDeviceEntry(ctx, true)
+	if pDevEntry == nil {
+		logger.Errorw(ctx, "Onu image commitment rejected: no valid OnuDevice", log.Fields{"device-id": dh.deviceID})
+		return
+	}
+	dh.lockUpgradeFsm.RLock()
+	if dh.pOnuUpradeFsm != nil {
+		dh.lockUpgradeFsm.RUnlock()
+		onuVolthaDevice, getErr := dh.coreProxy.GetDevice(log.WithSpanFromContext(context.TODO(), ctx),
+			dh.deviceID, dh.deviceID)
+		if getErr != nil || onuVolthaDevice == nil {
+			logger.Errorw(ctx, "Failed to fetch Onu device for image commitment", log.Fields{"device-id": dh.deviceID, "err": getErr})
+			return
+		}
+		//  use the OnuVendor identification from this device for the internal unique name
+		imageIdentifier := onuVolthaDevice.VendorId + aVersion //head on vendor ID of the ONU
+		// 1.) check a started upgrade process and rely the commitment request to it
+		if err = dh.pOnuUpradeFsm.SetCommitmentParamsRunning(ctx, imageIdentifier); err != nil {
+			logger.Errorw(ctx, "onu upgrade fsm did not accept commitment while running", log.Fields{
+				"device-id": dh.deviceID, "error": err})
+		} else {
+			logger.Debugw(ctx, "image commitment acknowledged by onu upgrade processing", log.Fields{
+				"device-id": dh.deviceID, "image-id": imageIdentifier})
+		}
+		//if some ONU upgrade is ongoing we do not accept some explicit ONU image-version related commitment
+		//   (even though parameter setting is not accepted)
+		return
+	} //else
+	dh.lockUpgradeFsm.RUnlock()
+
+	// 2.) check if requested image-version equals the inactive one and start its commitment
+	var activeImageID uint16
+	if activeImageID, err = pDevEntry.GetActiveImageMeID(ctx); err != nil || activeImageID > 1 {
+		logger.Errorw(ctx, "get active image failed", log.Fields{
+			"device-id": dh.deviceID, "err": err, "image-id": activeImageID})
+		return
+	}
+	err = dh.createOnuUpgradeFsm(ctx, pDevEntry, OmciOnuSwUpgradeDone)
+	if err == nil {
+		if err = dh.pOnuUpradeFsm.SetCommitmentParamsStart(ctx, aVersion, activeImageID); err != nil {
+			logger.Errorw(ctx, "onu upgrade fsm did not accept commitment to start", log.Fields{
+				"device-id": dh.deviceID, "error": err})
+			return
+		}
+		logger.Debugw(ctx, "active image commitment acknowledged by onu upgrade", log.Fields{
+			"device-id": dh.deviceID, "image-version": aVersion})
+		return
+	} //else
+	logger.Errorw(ctx, "onu upgrade fsm could not be created", log.Fields{
+		"device-id": dh.deviceID, "error": err})
+}
+
 //  deviceHandler methods that implement the adapters interface requests## end #########
 // #####################################################################################
 
@@ -1731,18 +1907,10 @@
 	}
 
 	//reset a possibly running upgrade FSM
-	// specific here: If the FSM is in upgradeStWaitForCommit, it is left there for possibly later commit
-	// this possibly also refers later to (not yet existing) upgradeStWaitForActivate (with ctl API changes)
+	//  (note the Upgrade FSM may stay alive e.g. in state upgradeStWaitForCommit to endure the ONU reboot)
 	dh.lockUpgradeFsm.RLock()
 	if dh.pOnuUpradeFsm != nil {
-		pUpgradeStatemachine := dh.pOnuUpradeFsm.pAdaptFsm.pFsm
-		if pUpgradeStatemachine != nil {
-			if pUpgradeStatemachine.Is(upgradeStWaitEndDL) {
-				dh.pOnuUpradeFsm.chReceiveExpectedResponse <- false //which aborts the FSM (activate was not yet sent)
-			}
-			_ = pUpgradeStatemachine.Event(upgradeEvReset) //anyway and for all other states
-		}
-		//else the FSM seems already to be in some released state
+		dh.pOnuUpradeFsm.CancelProcessing(ctx)
 	}
 	dh.lockUpgradeFsm.RUnlock()