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/configs/software-image.img b/configs/software-image.img
index 7880fd7..86a570b 100644
--- a/configs/software-image.img
+++ b/configs/software-image.img
@@ -1,4 +1,4 @@
-# This is a file to mimic an image software download via OMCI.
+BBSM_IMG_00002# This is a file to mimic an image software download via OMCI.
 # BBSim doesn't care about the size of the file, but we need to know the source so we can compare.
 
 Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam a scelerisque nibh. Donec vitae imperdiet nulla, non hendrerit est. Pellentesque turpis leo, aliquam a libero vitae, malesuada eleifend velit. Quisque non ornare neque. Maecenas vulputate justo vitae vehicula porta. Proin pellentesque lacus non neque bibendum vehicula. Vestibulum sollicitudin, nisl quis semper hendrerit, dolor lacus dapibus dolor, ut volutpat enim augue at magna. Vestibulum neque urna, sagittis eget dui vel, pulvinar varius magna. Vestibulum condimentum iaculis urna ac congue. Proin fringilla id felis pellentesque rutrum. Phasellus eu sodales eros. Nullam laoreet blandit lacus. Nulla pellentesque euismod dolor tempus congue. Donec vel dui varius, maximus enim eget, molestie orci.
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)
+				}
 			}
 		})
 	}