VOL-3052 Onu Software preliminary upgrade extensions with internal test, version 1.2.5-dev171

Signed-off-by: mpagenko <michael.pagenkopf@adtran.com>
Change-Id: I23ee1a14af635def33b565444f1a1f81370a86a7
diff --git a/internal/pkg/onuadaptercore/mib_sync.go b/internal/pkg/onuadaptercore/mib_sync.go
index 0fcf7f9..0c71848 100644
--- a/internal/pkg/onuadaptercore/mib_sync.go
+++ b/internal/pkg/onuadaptercore/mib_sync.go
@@ -103,7 +103,7 @@
 
 func (oo *OnuDeviceEntry) enterGettingFirstSwVersionState(ctx context.Context, e *fsm.Event) {
 	logger.Debugw(ctx, "MibSync FSM", log.Fields{"Start getting IsActive and Version of first SW-image in State": e.FSM.Current(), "device-id": oo.deviceID})
-	requestedAttributes := me.AttributeValueMap{"IsActive": 0, "Version": ""}
+	requestedAttributes := me.AttributeValueMap{"IsCommitted": 0, "IsActive": 0, "Version": ""}
 	meInstance := oo.PDevOmciCC.sendGetMe(log.WithSpanFromContext(context.TODO(), ctx), me.SoftwareImageClassID, firstSwImageMeID, requestedAttributes, ConstDefaultOmciTimeout, true, oo.pMibUploadFsm.commChan)
 	//accept also nil as (error) return value for writing to LastTx
 	//  - this avoids misinterpretation of new received OMCI messages
@@ -112,7 +112,7 @@
 
 func (oo *OnuDeviceEntry) enterGettingSecondSwVersionState(ctx context.Context, e *fsm.Event) {
 	logger.Debugw(ctx, "MibSync FSM", log.Fields{"Start getting IsActive and Version of second SW-image in State": e.FSM.Current(), "device-id": oo.deviceID})
-	requestedAttributes := me.AttributeValueMap{"IsActive": 0, "Version": ""}
+	requestedAttributes := me.AttributeValueMap{"IsCommitted": 0, "IsActive": 0, "Version": ""}
 	meInstance := oo.PDevOmciCC.sendGetMe(log.WithSpanFromContext(context.TODO(), ctx), me.SoftwareImageClassID, secondSwImageMeID, requestedAttributes, ConstDefaultOmciTimeout, true, oo.pMibUploadFsm.commChan)
 	//accept also nil as (error) return value for writing to LastTx
 	//  - this avoids misinterpretation of new received OMCI messages
@@ -130,10 +130,11 @@
 
 func (oo *OnuDeviceEntry) enterGettingMibTemplate(ctx context.Context, e *fsm.Event) {
 
-	for i := firstSwImageMeID; i <= secondSwImageMeID; i++ {
-		if oo.swImages[i].isActive > 0 {
-			oo.activeSwVersion = oo.swImages[i].version
-		}
+	if oo.onuSwImageIndications.activeEntityEntry.valid {
+		oo.activeSwVersion = oo.onuSwImageIndications.activeEntityEntry.version
+	} else {
+		logger.Errorw(ctx, "get-mib-template: no active SW version found, working with empty SW version, which might be untrustworthy",
+			log.Fields{"device-id": oo.deviceID})
 	}
 
 	meStoredFromTemplate := false
@@ -477,22 +478,15 @@
 				_ = oo.pMibUploadFsm.pFsm.Event(ulEvGetFirstSwVersion)
 				return nil
 			case "SoftwareImage":
-				if entityID <= secondSwImageMeID {
-					oo.swImages[entityID].version = trimStringFromInterface(meAttributes["Version"])
-					oo.swImages[entityID].isActive = meAttributes["IsActive"].(uint8)
-					logger.Debugw(ctx, "MibSync FSM - GetResponse Data for SoftwareImage - Version/IsActive",
-						log.Fields{"device-id": oo.deviceID, "entityID": entityID,
-							"version": oo.swImages[entityID].version, "isActive": oo.swImages[entityID].isActive})
-				} else {
-					err = fmt.Errorf("mibSync FSM - Failed to GetResponse Data for SoftwareImage: %s", oo.deviceID)
+				if entityID > secondSwImageMeID {
+					logger.Errorw(ctx, "mibSync FSM - Failed to GetResponse Data for SoftwareImage with expected EntityId",
+						log.Fields{"device-id": oo.deviceID, "entity-ID": entityID})
+					return fmt.Errorf("mibSync FSM - SwResponse Data with unexpected EntityId: %s %x",
+						oo.deviceID, entityID)
 				}
-				if firstSwImageMeID == entityID {
-					_ = oo.pMibUploadFsm.pFsm.Event(ulEvGetSecondSwVersion)
-					return nil
-				} else if secondSwImageMeID == entityID {
-					_ = oo.pMibUploadFsm.pFsm.Event(ulEvGetMacAddress)
-					return nil
-				}
+				// need to use function for go lint complexity
+				oo.handleSwImageIndications(ctx, entityID, meAttributes)
+				return nil
 			case "IpHostConfigData":
 				macBytes, _ := me.InterfaceToOctets(meAttributes["MacAddress"])
 				if omciMacAddressLen == len(macBytes) {
@@ -523,6 +517,67 @@
 	return err
 }
 
+func (oo *OnuDeviceEntry) handleSwImageIndications(ctx context.Context, entityID uint16, meAttributes me.AttributeValueMap) {
+	imageIsCommitted := meAttributes["IsCommitted"].(uint8)
+	imageIsActive := meAttributes["IsActive"].(uint8)
+	imageVersion := trimStringFromInterface(meAttributes["Version"])
+	logger.Infow(ctx, "MibSync FSM - GetResponse Data for SoftwareImage",
+		log.Fields{"device-id": oo.deviceID, "entityID": entityID,
+			"version": imageVersion, "isActive": imageIsActive, "isCommitted": imageIsCommitted, "SNR": oo.serialNumber})
+	if firstSwImageMeID == entityID {
+		//always accept the state of the first image (2nd image info should not yet be available)
+		if imageIsActive == swIsActive {
+			oo.onuSwImageIndications.activeEntityEntry.entityID = entityID
+			oo.onuSwImageIndications.activeEntityEntry.valid = true
+			oo.onuSwImageIndications.activeEntityEntry.version = imageVersion
+			oo.onuSwImageIndications.activeEntityEntry.isCommitted = imageIsCommitted
+		} else {
+			oo.onuSwImageIndications.inactiveEntityEntry.entityID = entityID
+			oo.onuSwImageIndications.inactiveEntityEntry.valid = true
+			oo.onuSwImageIndications.inactiveEntityEntry.version = imageVersion
+			oo.onuSwImageIndications.inactiveEntityEntry.isCommitted = imageIsCommitted
+		}
+		_ = oo.pMibUploadFsm.pFsm.Event(ulEvGetSecondSwVersion)
+		return
+	} else if secondSwImageMeID == entityID {
+		//2nd image info might conflict with first image info, in which case we priorize first image info!
+		if imageIsActive == swIsActive { //2nd image reported to be active
+			if oo.onuSwImageIndications.activeEntityEntry.valid {
+				//conflict exists - state of first image is left active
+				logger.Warnw(ctx, "mibSync FSM - both ONU images are reported as active - assuming 2nd to be inactive",
+					log.Fields{"device-id": oo.deviceID})
+				oo.onuSwImageIndications.inactiveEntityEntry.entityID = entityID
+				oo.onuSwImageIndications.inactiveEntityEntry.valid = true ////to indicate that at least something has been reported
+				oo.onuSwImageIndications.inactiveEntityEntry.version = imageVersion
+				oo.onuSwImageIndications.inactiveEntityEntry.isCommitted = imageIsCommitted
+			} else { //first image inactive, this one active
+				oo.onuSwImageIndications.activeEntityEntry.entityID = entityID
+				oo.onuSwImageIndications.activeEntityEntry.valid = true
+				oo.onuSwImageIndications.activeEntityEntry.version = imageVersion
+				oo.onuSwImageIndications.activeEntityEntry.isCommitted = imageIsCommitted
+			}
+		} else { //2nd image reported to be inactive
+			if oo.onuSwImageIndications.inactiveEntityEntry.valid {
+				//conflict exists - both images inactive - regard it as ONU failure and assume first image to be active
+				logger.Warnw(ctx, "mibSync FSM - both ONU images are reported as inactive, defining first to be active",
+					log.Fields{"device-id": oo.deviceID})
+				oo.onuSwImageIndications.activeEntityEntry.entityID = firstSwImageMeID
+				oo.onuSwImageIndications.activeEntityEntry.valid = true //to indicate that at least something has been reported
+				//copy active commit/version from the previously stored inactive position
+				oo.onuSwImageIndications.activeEntityEntry.version = oo.onuSwImageIndications.inactiveEntityEntry.version
+				oo.onuSwImageIndications.activeEntityEntry.isCommitted = oo.onuSwImageIndications.inactiveEntityEntry.isCommitted
+			}
+			//in any case we indicate (and possibly overwrite) the second image indications as inactive
+			oo.onuSwImageIndications.inactiveEntityEntry.entityID = entityID
+			oo.onuSwImageIndications.inactiveEntityEntry.valid = true
+			oo.onuSwImageIndications.inactiveEntityEntry.version = imageVersion
+			oo.onuSwImageIndications.inactiveEntityEntry.isCommitted = imageIsCommitted
+		}
+		_ = oo.pMibUploadFsm.pFsm.Event(ulEvGetMacAddress)
+		return
+	}
+}
+
 func (oo *OnuDeviceEntry) handleOmciMessage(ctx context.Context, msg OmciMessage) {
 	logger.Debugw(ctx, "MibSync Msg", log.Fields{"OmciMessage received for device-id": oo.deviceID,
 		"msgType": msg.OmciMsg.MessageType, "msg": msg})
@@ -718,3 +773,31 @@
 		logger.Warnw(ctx, "wrong state for MDS evaluation!", log.Fields{"state": oo.pMibUploadFsm.pFsm.Current(), "device-id": oo.deviceID})
 	}
 }
+
+//GetActiveImageMeID returns the Omci MeId of the active ONU image together with error code for validity
+func (oo *OnuDeviceEntry) GetActiveImageMeID(ctx context.Context) (uint16, error) {
+	if oo.onuSwImageIndications.activeEntityEntry.valid {
+		return oo.onuSwImageIndications.activeEntityEntry.entityID, nil
+	}
+	return 0xFFFF, fmt.Errorf("no valid active image found: %s", oo.deviceID)
+}
+
+//GetInactiveImageMeID returns the Omci MeId of the inactive ONU image together with error code for validity
+func (oo *OnuDeviceEntry) GetInactiveImageMeID(ctx context.Context) (uint16, error) {
+	if oo.onuSwImageIndications.inactiveEntityEntry.valid {
+		return oo.onuSwImageIndications.inactiveEntityEntry.entityID, nil
+	}
+	return 0xFFFF, fmt.Errorf("no valid inactive image found: %s", oo.deviceID)
+}
+
+//IsImageToBeCommitted returns true if the active image is still uncommitted
+func (oo *OnuDeviceEntry) IsImageToBeCommitted(ctx context.Context, aImageID uint16) bool {
+	if oo.onuSwImageIndications.activeEntityEntry.valid {
+		if oo.onuSwImageIndications.activeEntityEntry.entityID == aImageID {
+			if oo.onuSwImageIndications.activeEntityEntry.isCommitted == swIsUncommitted {
+				return true
+			}
+		}
+	}
+	return false //all other case are treated as 'nothing to commit
+}