VOL-3052 Onu Software preliminary upgrade test from Adapter to ONU, version 1.2.5-dev168

Signed-off-by: mpagenko <michael.pagenkopf@adtran.com>
Change-Id: I5f48645fa8b392c2c79f13cf8fd0d2369bfd5ca5
diff --git a/internal/pkg/onuadaptercore/device_handler.go b/internal/pkg/onuadaptercore/device_handler.go
index c55afd3..27d04dc 100644
--- a/internal/pkg/onuadaptercore/device_handler.go
+++ b/internal/pkg/onuadaptercore/device_handler.go
@@ -19,7 +19,6 @@
 
 import (
 	"context"
-	"encoding/hex"
 	"errors"
 	"fmt"
 	"strconv"
@@ -102,6 +101,7 @@
 	cAniConfigFsm
 	cUniVlanConfigFsm
 	cL2PmFsm
+	cOnuUpgradeFsm
 )
 
 type omciIdleCheckStruct struct {
@@ -117,6 +117,7 @@
 	cAniConfigFsm:     {(*deviceHandler).isAniConfigFsmInOmciIdleState, cAniFsmIdleState},
 	cUniVlanConfigFsm: {(*deviceHandler).isUniVlanConfigFsmInOmciIdleState, cVlanFsmIdleState},
 	cL2PmFsm:          {(*deviceHandler).isFsmInOmciIdleStateDefault, cL2PmFsmIdleState},
+	cOnuUpgradeFsm:    {(*deviceHandler).isFsmInOmciIdleStateDefault, cOnuUpgradeFsmIdleState},
 }
 
 const (
@@ -203,6 +204,8 @@
 	mutexKvStoreContext        sync.Mutex
 	lockVlanConfig             sync.RWMutex
 	UniVlanConfigFsmMap        map[uint8]*UniVlanConfigFsm
+	lockUpgradeFsm             sync.RWMutex
+	pOnuUpradeFsm              *OnuUpgradeFsm
 	reconciling                bool
 	mutexReconcilingFlag       sync.RWMutex
 	chReconcilingFinished      chan bool //channel to indicate that reconciling has been finished
@@ -233,6 +236,7 @@
 	//TODO initialize the support classes.
 	dh.uniEntityMap = make(map[uint32]*onuUniPort)
 	dh.lockVlanConfig = sync.RWMutex{}
+	dh.lockUpgradeFsm = sync.RWMutex{}
 	dh.UniVlanConfigFsmMap = make(map[uint8]*UniVlanConfigFsm)
 	dh.reconciling = false
 	dh.chReconcilingFinished = make(chan bool)
@@ -319,10 +323,12 @@
 		return err
 	}
 
+	/* msg print moved symmetrically to omci_cc, if wanted here as additional debug, than perhaps only based on additional debug setting!
 	//assuming omci message content is hex coded!
 	// with restricted output of 16(?) bytes would be ...omciMsg.Message[:16]
 	logger.Debugw(ctx, "inter-adapter-recv-omci", log.Fields{
 		"device-id": dh.deviceID, "RxOmciMessage": hex.EncodeToString(omciMsg.Message)})
+	*/
 	pDevEntry := dh.getOnuDeviceEntry(ctx, true)
 	if pDevEntry != nil {
 		if pDevEntry.PDevOmciCC != nil {
@@ -965,11 +971,47 @@
 }
 
 //doOnuSwUpgrade initiates the SW download transfer to the ONU and on success activates the (inactive) image
-func (dh *deviceHandler) doOnuSwUpgrade(ctx context.Context, apImageDsc *voltha.ImageDownload) error {
-	logger.Warnw(ctx, "onuSwUpgrade not yet implemented in deviceHandler", log.Fields{
+func (dh *deviceHandler) doOnuSwUpgrade(ctx context.Context, apImageDsc *voltha.ImageDownload,
+	apDownloadManager *adapterDownloadManager) error {
+	logger.Debugw(ctx, "onuSwUpgrade requested", log.Fields{
 		"device-id": dh.deviceID, "image-name": (*apImageDsc).Name})
-	//return success to comfort the core processing during integration
-	return nil
+
+	var err error
+	if dh.ReadyForSpecificOmciConfig {
+		dh.lockUpgradeFsm.Lock()
+		defer dh.lockUpgradeFsm.Unlock()
+		if dh.pOnuUpradeFsm == nil {
+			err = dh.createOnuUpgradeFsm(ctx, OmciOnuSwUpgradeDone)
+			if err == nil {
+				if err = dh.pOnuUpradeFsm.SetDownloadParams(ctx, apImageDsc, apDownloadManager); err != nil {
+					logger.Errorw(ctx, "onu upgrade fsm could not set parameters", log.Fields{
+						"device-id": dh.deviceID, "error": err})
+				}
+			} else {
+				logger.Errorw(ctx, "onu upgrade fsm could not be created", log.Fields{
+					"device-id": dh.deviceID, "error": err})
+			}
+		} else { //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})
+				}
+				err = fmt.Errorf("aborted Onu SW upgrade but not automatically started, try again, device-id: %s", dh.deviceID)
+				//TODO!!!: wait for 'ready' to start and configure - see above SetDownloadParams()
+				// for now a second start of download should work again
+			} else { //should never occur
+				logger.Errorw(ctx, "onu upgrade fsm inconsistent setup", log.Fields{
+					"device-id": dh.deviceID})
+				err = fmt.Errorf("onu upgrade fsm inconsistent setup, baseFsm invalid for device-id: %s", dh.deviceID)
+			}
+		}
+	} else {
+		logger.Errorw(ctx, "Start Onu SW upgrade rejected: no active OMCI connection", log.Fields{"device-id": dh.deviceID})
+	}
+	return err
 }
 
 //  deviceHandler methods that implement the adapters interface requests## end #########
@@ -1601,6 +1643,15 @@
 		dh.stopAlarmManager <- true
 	}
 
+	//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)
+	dh.lockUpgradeFsm.RLock()
+	if dh.pOnuUpradeFsm != nil {
+		_ = dh.pOnuUpradeFsm.pAdaptFsm.pFsm.Event(upgradeEvReset)
+	}
+	dh.lockUpgradeFsm.RUnlock()
+
 	return nil
 }
 
@@ -2108,6 +2159,58 @@
 	}
 }
 
+// createOnuUpgradeFsm initializes and runs the Onu Software upgrade FSM
+func (dh *deviceHandler) createOnuUpgradeFsm(ctx context.Context, devEvent OnuDeviceEvent) error {
+	//in here lockUpgradeFsm is already locked
+	chUpgradeFsm := make(chan Message, 2048)
+	var sFsmName = "OnuSwUpgradeFSM"
+	logger.Debugw(ctx, "create OnuSwUpgradeFSM", log.Fields{"device-id": dh.deviceID})
+	pDevEntry := dh.getOnuDeviceEntry(ctx, true)
+	if pDevEntry == nil {
+		logger.Errorw(ctx, "no valid OnuDevice -abort", log.Fields{"device-id": dh.deviceID})
+		return fmt.Errorf(fmt.Sprintf("no valid OnuDevice - abort for device-id: %s", dh.device.Id))
+	}
+	dh.pOnuUpradeFsm = NewOnuUpgradeFsm(ctx, dh, pDevEntry.PDevOmciCC, pDevEntry.pOnuDB, devEvent,
+		sFsmName, chUpgradeFsm)
+	if dh.pOnuUpradeFsm != nil {
+		pUpgradeStatemachine := dh.pOnuUpradeFsm.pAdaptFsm.pFsm
+		if pUpgradeStatemachine != nil {
+			if pUpgradeStatemachine.Is(upgradeStDisabled) {
+				if err := pUpgradeStatemachine.Event(upgradeEvStart); err != nil {
+					logger.Errorw(ctx, "OnuSwUpgradeFSM: can't start", log.Fields{"err": err})
+					// maybe try a FSM reset and then again ... - TODO!!!
+					return fmt.Errorf(fmt.Sprintf("OnuSwUpgradeFSM could not be started for device-id: %s", dh.device.Id))
+				}
+				/***** LockStateFSM started */
+				logger.Debugw(ctx, "OnuSwUpgradeFSM started", log.Fields{
+					"state": pUpgradeStatemachine.Current(), "device-id": dh.deviceID})
+			} else {
+				logger.Errorw(ctx, "wrong state of OnuSwUpgradeFSM to start - want: disabled", log.Fields{
+					"have": pUpgradeStatemachine.Current(), "device-id": dh.deviceID})
+				// maybe try a FSM reset and then again ... - TODO!!!
+				return fmt.Errorf(fmt.Sprintf("OnuSwUpgradeFSM could not be started for device-id: %s, wrong internal state", dh.device.Id))
+			}
+		} else {
+			logger.Errorw(ctx, "OnuSwUpgradeFSM internal FSM invalid - cannot be executed!!", log.Fields{"device-id": dh.deviceID})
+			// maybe try a FSM reset and then again ... - TODO!!!
+			return fmt.Errorf(fmt.Sprintf("OnuSwUpgradeFSM internal FSM could not be created for device-id: %s", dh.device.Id))
+		}
+	} else {
+		logger.Errorw(ctx, "OnuSwUpgradeFSM could not be created  - abort", log.Fields{"device-id": dh.deviceID})
+		return fmt.Errorf(fmt.Sprintf("OnuSwUpgradeFSM could not be created - abort for device-id: %s", dh.device.Id))
+	}
+	return nil
+}
+
+// removeOnuUpgradeFsm clears the Onu Software upgrade FSM
+func (dh *deviceHandler) removeOnuUpgradeFsm(ctx context.Context) {
+	logger.Debugw(ctx, "remove OnuSwUpgradeFSM StateMachine", log.Fields{
+		"device-id": dh.deviceID})
+	dh.lockUpgradeFsm.Lock()
+	defer dh.lockUpgradeFsm.Unlock()
+	dh.pOnuUpradeFsm = nil //resource clearing is left to garbage collector
+}
+
 //setBackend provides a DB backend for the specified path on the existing KV client
 func (dh *deviceHandler) setBackend(ctx context.Context, aBasePathKvStore string) *db.Backend {
 
@@ -2833,6 +2936,12 @@
 				return true //FSM not active - so there is no activity on omci
 			}
 		}
+	case cOnuUpgradeFsm:
+		{
+			dh.lockUpgradeFsm.RLock()
+			defer dh.lockUpgradeFsm.RUnlock()
+			pFsm = dh.pOnuUpradeFsm.pAdaptFsm.pFsm
+		}
 	default:
 		{
 			logger.Errorw(ctx, "invalid stateMachine selected for idle check", log.Fields{