Converting Software Image Hash to hex string
Returning image version based on the first 14 bytes of the image file. D

Change-Id: I5ea94be5e841cd1a845e7d523cf0e3666983d4aa
diff --git a/internal/bbsim/devices/onu.go b/internal/bbsim/devices/onu.go
index 84706fd..af135de 100644
--- a/internal/bbsim/devices/onu.go
+++ b/internal/bbsim/devices/onu.go
@@ -127,6 +127,9 @@
 	ImageSoftwareReceivedSections int
 	ActiveImageEntityId           uint16
 	CommittedImageEntityId        uint16
+	StandbyImageVersion           string
+	ActiveImageVersion            string
+	CommittedImageVersion         string
 	OmciResponseRate              uint8
 	OmciMsgCounter                uint8
 
@@ -163,10 +166,14 @@
 		MibDataSync:                   0,
 		ImageSoftwareExpectedSections: 0, // populated during OMCI StartSoftwareDownloadRequest
 		ImageSoftwareReceivedSections: 0,
-		ActiveImageEntityId:           0, // when we start the SoftwareImage with ID 0 is active and committed
-		CommittedImageEntityId:        0,
-		OmciResponseRate:              olt.OmciResponseRate,
-		OmciMsgCounter:                0,
+		//TODO this needs reworking, it's always 0 or 1, possibly base all on the version
+		ActiveImageEntityId:    0, // when we start the SoftwareImage with ID 0 is active and committed
+		CommittedImageEntityId: 0,
+		StandbyImageVersion:    "BBSM_IMG_00000",
+		ActiveImageVersion:     "BBSM_IMG_00001",
+		CommittedImageVersion:  "BBSM_IMG_00001",
+		OmciResponseRate:       olt.OmciResponseRate,
+		OmciMsgCounter:         0,
 	}
 	o.SerialNumber = NewSN(olt.ID, pon.ID, id)
 	// NOTE this state machine is used to track the operational
@@ -782,7 +789,8 @@
 		responsePkt, _ = omcilib.CreateMibUploadNextResponse(msg.OmciPkt, msg.OmciMsg, o.MibDataSync, o.MibDb)
 	case omci.GetRequestType:
 		onuDown := o.OperState.Current() == "down"
-		responsePkt, _ = omcilib.CreateGetResponse(msg.OmciPkt, msg.OmciMsg, o.SerialNumber, o.MibDataSync, o.ActiveImageEntityId, o.CommittedImageEntityId, onuDown)
+		responsePkt, _ = omcilib.CreateGetResponse(msg.OmciPkt, msg.OmciMsg, o.SerialNumber, o.MibDataSync, o.ActiveImageEntityId,
+			o.CommittedImageEntityId, o.StandbyImageVersion, o.ActiveImageVersion, o.CommittedImageVersion, onuDown)
 	case omci.SetRequestType:
 		success := true
 		msgObj, _ := omcilib.ParseSetRequest(msg.OmciPkt)
@@ -1011,6 +1019,10 @@
 				"SectionNumber":  msgObj.SectionNumber,
 				"SectionData":    msgObj.SectionData,
 			}).Trace("received-download-section-request")
+			//Extracting the first 14 bytes to use as a version for this image.
+			if o.ImageSoftwareReceivedSections == 0 {
+				o.StandbyImageVersion = string(msgObj.SectionData[0:14])
+			}
 			o.ImageSoftwareReceivedSections++
 			if o.InternalState.Current() != OnuStateImageDownloadInProgress {
 				if err := o.InternalState.Event(OnuTxProgressImageDownload); err != nil {
@@ -1101,6 +1113,9 @@
 			}
 			if msgObj, err := omcilib.ParseActivateSoftwareRequest(msg.OmciPkt); err == nil {
 				o.ActiveImageEntityId = msgObj.EntityInstance
+				previousActiveImage := o.ActiveImageVersion
+				o.ActiveImageVersion = o.StandbyImageVersion
+				o.StandbyImageVersion = previousActiveImage
 			} else {
 				onuLogger.Errorf("something-went-wrong-while-activating: %s", err)
 			}
@@ -1134,7 +1149,11 @@
 				// TODO validate that the image to commit is:
 				// - active
 				// - not already committed
+				o.ActiveImageEntityId = msgObj.EntityInstance
 				o.CommittedImageEntityId = msgObj.EntityInstance
+				//committed becomes standby
+				o.StandbyImageVersion = o.CommittedImageVersion
+				o.CommittedImageVersion = o.ActiveImageVersion
 			} else {
 				onuLogger.Errorf("something-went-wrong-while-committing: %s", err)
 			}
diff --git a/internal/common/omci/get.go b/internal/common/omci/get.go
index 73e53ac..e50c47a 100644
--- a/internal/common/omci/get.go
+++ b/internal/common/omci/get.go
@@ -45,7 +45,9 @@
 	return msgObj, nil
 }
 
-func CreateGetResponse(omciPkt gopacket.Packet, omciMsg *omci.OMCI, onuSn *openolt.SerialNumber, mds uint8, activeImageEntityId uint16, committedImageEntityId uint16, onuDown bool) ([]byte, error) {
+func CreateGetResponse(omciPkt gopacket.Packet, omciMsg *omci.OMCI, onuSn *openolt.SerialNumber, mds uint8,
+	activeImageEntityId uint16, committedImageEntityId uint16, standbyImageVersion string, activeImageVersion string,
+	committedImageVersion string, onuDown bool) ([]byte, error) {
 
 	msgObj, err := ParseGetRequest(omciPkt)
 
@@ -66,7 +68,8 @@
 	case me.OnuGClassID:
 		response = createOnugResponse(msgObj.AttributeMask, msgObj.EntityInstance, onuSn)
 	case me.SoftwareImageClassID:
-		response = createSoftwareImageResponse(msgObj.AttributeMask, msgObj.EntityInstance, activeImageEntityId, committedImageEntityId)
+		response = createSoftwareImageResponse(msgObj.AttributeMask, msgObj.EntityInstance,
+			activeImageEntityId, committedImageEntityId, standbyImageVersion, activeImageVersion, committedImageVersion)
 	case me.IpHostConfigDataClassID:
 		response = createIpHostResponse(msgObj.AttributeMask, msgObj.EntityInstance)
 	case me.UniGClassID:
@@ -200,20 +203,35 @@
 	//}
 }
 
-func createSoftwareImageResponse(attributeMask uint16, entityInstance uint16, activeImageEntityId uint16, committedImageEntityId uint16) *omci.GetResponse {
+func createSoftwareImageResponse(attributeMask uint16, entityInstance uint16, activeImageEntityId uint16,
+	committedImageEntityId uint16, standbyImageVersion string, activeImageVersion string, committedImageVersion string) *omci.GetResponse {
 
 	omciLogger.WithFields(log.Fields{
 		"EntityInstance": entityInstance,
+		"AttributeMask":  attributeMask,
 	}).Trace("received-get-software-image-request")
 
 	// Only one image can be active and committed
 	committed := 0
 	active := 0
+	version := standbyImageVersion
 	if entityInstance == activeImageEntityId {
 		active = 1
+		version = activeImageVersion
 	}
 	if entityInstance == committedImageEntityId {
 		committed = 1
+		version = committedImageVersion
+	}
+
+	imageHash, err := hex.DecodeString(hex.EncodeToString([]byte(version)))
+	if err != nil {
+		omciLogger.WithFields(log.Fields{
+			"entityId":  entityInstance,
+			"active":    active,
+			"committed": committed,
+			"err":       err,
+		}).Error("cannot-generate-image-hash")
 	}
 
 	// NOTE that we need send the response for the correct ME Instance or the adapter won't process it
@@ -224,12 +242,12 @@
 		},
 		Attributes: me.AttributeValueMap{
 			"ManagedEntityId": 0,
-			"Version":         ToOctets("00000000000001", 14),
+			"Version":         ToOctets(version, 14),
 			"IsCommitted":     committed,
 			"IsActive":        active,
 			"IsValid":         1,
-			"ProductCode":     ToOctets("product-code", 25),
-			"ImageHash":       ToOctets("broadband-sim", 16),
+			"ProductCode":     ToOctets("BBSIM-ONU", 25),
+			"ImageHash":       imageHash,
 		},
 		Result:        me.Success,
 		AttributeMask: attributeMask,
diff --git a/internal/common/omci/get_test.go b/internal/common/omci/get_test.go
index 0d9663f..f9cdac4 100644
--- a/internal/common/omci/get_test.go
+++ b/internal/common/omci/get_test.go
@@ -24,6 +24,7 @@
 	me "github.com/opencord/omci-lib-go/generated"
 	"github.com/opencord/voltha-protos/v4/go/openolt"
 	"gotest.tools/assert"
+	"reflect"
 	"testing"
 )
 
@@ -108,6 +109,26 @@
 			getArgs{createEthernetPerformanceMonitoringHistoryDataResponse(32768, 10), 2},
 			getWant{2, map[string]interface{}{"ManagedEntityId": uint16(10)}},
 		},
+		{"getSoftwareImageResponse",
+			getArgs{createSoftwareImageResponse(61440, 0, 1, 1, "BBSM_IMG_00000", "BBSM_IMG_00001", "BBSM_IMG_00001"), 2},
+			getWant{2, map[string]interface{}{"IsCommitted": uint8(0), "IsActive": uint8(0)}},
+		},
+		{"getSoftwareImageResponseActiveCommitted",
+			getArgs{createSoftwareImageResponse(61440, 1, 1, 1, "BBSM_IMG_00000", "BBSM_IMG_00001", "BBSM_IMG_00001"), 2},
+			getWant{2, map[string]interface{}{"IsCommitted": uint8(1), "IsActive": uint8(1)}},
+		},
+		{"getSoftwareImageResponseVersion",
+			getArgs{createSoftwareImageResponse(61440, 1, 1, 1, "BBSM_IMG_00000", "BBSM_IMG_00001", "BBSM_IMG_00001"), 2},
+			getWant{2, map[string]interface{}{"Version": ToOctets("BBSM_IMG_00001", 14)}},
+		},
+		{"getSoftwareImageResponseProductCode",
+			getArgs{createSoftwareImageResponse(2048, 1, 1, 1, "BBSM_IMG_00000", "BBSM_IMG_00001", "BBSM_IMG_00001"), 2},
+			getWant{2, map[string]interface{}{"ProductCode": ToOctets("BBSIM-ONU", 25)}},
+		},
+		{"getSoftwareImageResponseActiveImageHash",
+			getArgs{createSoftwareImageResponse(1024, 1, 1, 1, "BBSM_IMG_00000", "BBSM_IMG_00001", "BBSM_IMG_00001"), 2},
+			getWant{2, map[string]interface{}{"ImageHash": ToOctets("BBSM_IMG_00001", 25)}},
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
@@ -135,7 +156,18 @@
 
 			for k, v := range tt.want.attributes {
 				attr := getResponseLayer.Attributes[k]
-				assert.Equal(t, attr, v)
+				attrValue := reflect.ValueOf(attr)
+				if attrValue.Kind() == reflect.Slice {
+					// it the attribute is a list, iterate and compare single values
+					expectedValue := reflect.ValueOf(v)
+					for i := 0; i < attrValue.Len(); i++ {
+						assert.Equal(t, attrValue.Index(i).Interface(), expectedValue.Index(i).Interface(),
+							fmt.Sprintf("Attribute %s does not match, expected: %s, received %s", k, v, attr))
+					}
+				} else {
+					// if it's not a list just compare
+					assert.Equal(t, attr, v)
+				}
 			}
 		})
 	}