[VOL-4532] Remove duplicate maps in FlowManager and ResourceManager

Change-Id: I0a0fee7dbd3b3a25f2f0eee062bf565ba3212df3
diff --git a/internal/pkg/core/device_handler.go b/internal/pkg/core/device_handler.go
index a8c33f0..c17d92e 100644
--- a/internal/pkg/core/device_handler.go
+++ b/internal/pkg/core/device_handler.go
@@ -1052,7 +1052,7 @@
 					}
 					logger.Debugw(ctx, "publish-pon-metrics", log.Fields{"pon-port": port.Label})
 
-					onuGemInfoLst := dh.flowMgr[intfID].getOnuGemInfoList(ctx)
+					onuGemInfoLst := dh.resourceMgr[intfID].GetOnuGemInfoList(ctx)
 					if len(onuGemInfoLst) > 0 {
 						go dh.portStats.collectOnuAndGemStats(ctx, onuGemInfoLst)
 					}
@@ -1333,7 +1333,7 @@
 
 func (dh *DeviceHandler) activateONU(ctx context.Context, intfID uint32, onuID int64, serialNum *oop.SerialNumber, serialNumber string) error {
 	logger.Debugw(ctx, "activate-onu", log.Fields{"intf-id": intfID, "onu-id": onuID, "serialNum": serialNum, "serialNumber": serialNumber, "device-id": dh.device.Id, "OmccEncryption": dh.openOLT.config.OmccEncryption})
-	if err := dh.flowMgr[intfID].AddOnuInfoToFlowMgrCacheAndKvStore(ctx, intfID, uint32(onuID), serialNumber); err != nil {
+	if err := dh.resourceMgr[intfID].AddNewOnuGemInfoToCacheAndKvStore(ctx, intfID, uint32(onuID), serialNumber); err != nil {
 		return olterrors.NewErrAdapter("onu-activate-failed", log.Fields{"onu": onuID, "intf-id": intfID}, err)
 	}
 	var pir uint32 = 1000000
@@ -2037,7 +2037,7 @@
 		var ponPort uint32
 		for ponPort = 0; ponPort < dh.totalPonPorts; ponPort++ {
 			var err error
-			onuGemData := dh.flowMgr[ponPort].getOnuGemInfoList(ctx)
+			onuGemData := dh.resourceMgr[ponPort].GetOnuGemInfoList(ctx)
 			for i, onu := range onuGemData {
 				logger.Debugw(ctx, "onu-data", log.Fields{"onu": onu})
 				if err = dh.clearUNIData(ctx, &onuGemData[i]); err != nil {
@@ -2487,7 +2487,7 @@
 		for _, gem := range onuGem.GemPorts {
 			_ = dh.resourceMgr[intfID].DeleteFlowIDsForGem(ctx, intfID, gem)
 		}
-		if err := dh.flowMgr[intfID].RemoveOnuInfoFromFlowMgrCacheAndKvStore(ctx, intfID, onuID); err != nil {
+		if err := dh.resourceMgr[intfID].DelOnuGemInfo(ctx, intfID, onuID); err != nil {
 			logger.Warnw(ctx, "persistence-update-onu-gem-info-failed", log.Fields{
 				"intf-id":    intfID,
 				"onu-device": onu,
diff --git a/internal/pkg/core/device_handler_test.go b/internal/pkg/core/device_handler_test.go
index 9c5961c..867f821 100644
--- a/internal/pkg/core/device_handler_test.go
+++ b/internal/pkg/core/device_handler_test.go
@@ -233,9 +233,7 @@
 		dh.flowMgr[i].grpMgr = dh.groupMgr
 		dh.flowMgr[i].resourceMgr = dh.resourceMgr[i]
 		dh.flowMgr[i].techprofile = mocks.MockTechProfile{}
-		dh.flowMgr[i].gemToFlowIDs = make(map[uint32][]uint64)
 		dh.flowMgr[i].packetInGemPort = make(map[resourcemanager.PacketInInfoKey]uint32)
-		dh.flowMgr[i].flowIDToGems = make(map[uint64][]uint32)
 
 		dh.resourceMgr[i].TechprofileRef = dh.flowMgr[i].techprofile
 
@@ -253,7 +251,6 @@
 			dh.flowMgr[i].flowHandlerRoutineActive[j] = true
 			go dh.flowMgr[i].perOnuFlowHandlerRoutine(j, dh.flowMgr[i].incomingFlows[j], dh.flowMgr[i].stopFlowHandlerRoutine[j])
 		}
-		dh.flowMgr[i].onuGemInfoMap = make(map[uint32]*resourcemanager.OnuGemInfo)
 	}
 	dh.Client = &mocks.MockOpenoltClient{}
 	dh.eventMgr = &OpenOltEventMgr{eventProxy: &mocks.MockEventProxy{}, handler: dh}
diff --git a/internal/pkg/core/openolt_flowmgr.go b/internal/pkg/core/openolt_flowmgr.go
index 818b167..81f7fcb 100644
--- a/internal/pkg/core/openolt_flowmgr.go
+++ b/internal/pkg/core/openolt_flowmgr.go
@@ -204,20 +204,9 @@
 	grpMgr        *OpenOltGroupMgr
 	resourceMgr   *rsrcMgr.OpenOltResourceMgr
 
-	gemToFlowIDs    map[uint32][]uint64 // gem port id to flow ids
-	gemToFlowIDsKey sync.RWMutex        // lock to be used to access the gemToFlowIDs map
-
 	packetInGemPort     map[rsrcMgr.PacketInInfoKey]uint32 //packet in gem port local cache
 	packetInGemPortLock sync.RWMutex
 
-	// TODO create a type rsrcMgr.OnuGemInfos to be used instead of []rsrcMgr.OnuGemInfo
-	onuGemInfoMap map[uint32]*rsrcMgr.OnuGemInfo //onu, gem and uni info local cache -> map of onuID to OnuGemInfo
-	// We need to have a global lock on the onuGemInfo map
-	onuGemInfoLock sync.RWMutex
-
-	flowIDToGems     map[uint64][]uint32
-	flowIDToGemsLock sync.RWMutex
-
 	// Slice of channels. Each channel in slice, index by ONU ID, queues flows per ONU.
 	// A go routine per ONU, waits on the unique channel (indexed by ONU ID) for incoming flows (add/remove)
 	incomingFlows            []chan flowControlBlock
@@ -239,9 +228,7 @@
 		logger.Errorw(ctx, "error-while-populating-tech-profile-mgr", log.Fields{"err": err})
 		return nil
 	}
-	flowMgr.gemToFlowIDs = make(map[uint32][]uint64)
 	flowMgr.packetInGemPort = make(map[rsrcMgr.PacketInInfoKey]uint32)
-	flowMgr.flowIDToGems = make(map[uint64][]uint32)
 
 	// Create a slice of buffered channels for handling concurrent flows per ONU.
 	// The additional entry (+1) is to handle the NNI trap flows on a separate channel from individual ONUs channel
@@ -257,20 +244,6 @@
 		flowMgr.flowHandlerRoutineActive[i] = true
 		go flowMgr.perOnuFlowHandlerRoutine(i, flowMgr.incomingFlows[i], flowMgr.stopFlowHandlerRoutine[i])
 	}
-	flowMgr.onuGemInfoMap = make(map[uint32]*rsrcMgr.OnuGemInfo)
-	//Load the onugem info cache from kv store on flowmanager start
-	onuIDStart := flowMgr.deviceHandler.deviceInfo.OnuIdStart
-	onuIDEnd := flowMgr.deviceHandler.deviceInfo.OnuIdEnd
-	for onuID := onuIDStart; onuID <= onuIDEnd; onuID++ {
-		// check for a valid serial number in onuGem as GetOnuGemInfo can return nil error in case of nothing found in the path.
-		onugem, err := rMgr.GetOnuGemInfo(ctx, ponPortIdx, onuID)
-		if err == nil && onugem != nil && onugem.SerialNumber != "" {
-			flowMgr.onuGemInfoMap[onuID] = onugem
-		}
-	}
-
-	//Load flowID list per gem map And gemIDs per flow per interface from the kvstore.
-	flowMgr.loadFlowIDsForGemAndGemIDsForFlow(ctx)
 
 	//load interface to multicast queue map from kv store
 	flowMgr.grpMgr.LoadInterfaceToMulticastQueueMap(ctx)
@@ -281,11 +254,11 @@
 func (f *OpenOltFlowMgr) registerFlow(ctx context.Context, flowFromCore *ofp.OfpFlowStats, deviceFlow *openoltpb2.Flow) error {
 	if !deviceFlow.ReplicateFlow && deviceFlow.GemportId > 0 {
 		// Flow is not replicated in this case, we need to register the flow for a single gem-port
-		return f.registerFlowIDForGemAndGemIDForFlow(ctx, uint32(deviceFlow.AccessIntfId), uint32(deviceFlow.GemportId), flowFromCore)
+		return f.resourceMgr.RegisterFlowIDForGem(ctx, uint32(deviceFlow.AccessIntfId), uint32(deviceFlow.GemportId), flowFromCore)
 	} else if deviceFlow.ReplicateFlow && len(deviceFlow.PbitToGemport) > 0 {
 		// Flow is replicated in this case. We need to register the flow for all the gem-ports it is replicated to.
 		for _, gemPort := range deviceFlow.PbitToGemport {
-			if err := f.registerFlowIDForGemAndGemIDForFlow(ctx, uint32(deviceFlow.AccessIntfId), gemPort, flowFromCore); err != nil {
+			if err := f.resourceMgr.RegisterFlowIDForGem(ctx, uint32(deviceFlow.AccessIntfId), gemPort, flowFromCore); err != nil {
 				return err
 			}
 		}
@@ -293,31 +266,6 @@
 	return nil
 }
 
-func (f *OpenOltFlowMgr) registerFlowIDForGemAndGemIDForFlow(ctx context.Context, accessIntfID uint32, gemPortID uint32, flowFromCore *ofp.OfpFlowStats) error {
-	// update gem->flows map
-	f.gemToFlowIDsKey.Lock()
-	flowIDList, ok := f.gemToFlowIDs[gemPortID]
-	if !ok {
-		flowIDList = []uint64{flowFromCore.Id}
-	} else {
-		flowIDList = appendUnique64bit(flowIDList, flowFromCore.Id)
-	}
-	f.gemToFlowIDs[gemPortID] = flowIDList
-	f.gemToFlowIDsKey.Unlock()
-
-	// update flow->gems map
-	f.flowIDToGemsLock.Lock()
-	if _, ok := f.flowIDToGems[flowFromCore.Id]; !ok {
-		f.flowIDToGems[flowFromCore.Id] = []uint32{gemPortID}
-	} else {
-		f.flowIDToGems[flowFromCore.Id] = appendUnique32bit(f.flowIDToGems[flowFromCore.Id], gemPortID)
-	}
-	f.flowIDToGemsLock.Unlock()
-
-	// update the flowids for a gem to the KVstore
-	return f.resourceMgr.UpdateFlowIDsForGem(ctx, accessIntfID, gemPortID, flowIDList)
-}
-
 func (f *OpenOltFlowMgr) processAddFlow(ctx context.Context, intfID uint32, onuID uint32, uniID uint32, portNo uint32,
 	classifierInfo map[string]interface{}, actionInfo map[string]interface{}, flow *ofp.OfpFlowStats, TpID uint32,
 	UsMeterID uint32, DsMeterID uint32, flowMetadata *ofp.FlowMetadata) error {
@@ -985,15 +933,17 @@
 			"device-id":   f.deviceHandler.device.Id})
 	/* Update the allocated alloc_id and gem_port_id for the ONU/UNI to KV store  */
 	if err := f.resourceMgr.UpdateAllocIdsForOnu(ctx, intfID, onuID, uniID, allocID); err != nil {
-		logger.Errorw(ctx, "error-while-uploading-allocid-to-kv-store", log.Fields{"device-id": f.deviceHandler.device.Id})
+		logger.Errorw(ctx, "error-while-uploading-allocid-to-kv-store", log.Fields{"device-id": f.deviceHandler.device.Id, "onuID": onuID, "allocID": allocID})
 	}
 	if err := f.resourceMgr.UpdateGEMPortIDsForOnu(ctx, intfID, onuID, uniID, gemPortIDs); err != nil {
-		logger.Errorw(ctx, "error-while-uploading-gemports-to-kv-store", log.Fields{"device-id": f.deviceHandler.device.Id})
+		logger.Errorw(ctx, "error-while-uploading-gemports-to-kv-store", log.Fields{"device-id": f.deviceHandler.device.Id, "onuID": onuID, "gemPort": gemPortIDs})
 	}
 
 	logger.Infow(ctx, "stored-tconts-and-gem-into-kv-store-successfully", log.Fields{"device-id": f.deviceHandler.device.Id})
 	for _, gemPort := range gemPortIDs {
-		f.addGemPortToOnuInfoMap(ctx, intfID, onuID, gemPort)
+		if err := f.resourceMgr.AddGemToOnuGemInfo(ctx, intfID, onuID, gemPort); err != nil {
+			logger.Errorw(ctx, "error-while-uploading-onugeminfos-to-kv-store", log.Fields{"device-id": f.deviceHandler.device.Id, "onuID": onuID, "gemPort": gemPort})
+		}
 	}
 }
 
@@ -1911,48 +1861,6 @@
 	return nil
 }
 
-// Once the gemport is released for a given onu, it also has to be cleared from local cache
-// which was used for deriving the gemport->logicalPortNo during packet-in.
-// Otherwise stale info continues to exist after gemport is freed and wrong logicalPortNo
-// is conveyed to ONOS during packet-in OF message.
-func (f *OpenOltFlowMgr) deleteGemPortFromLocalCache(ctx context.Context, intfID uint32, onuID uint32, gemPortID uint32) {
-	logger.Infow(ctx, "deleting-gem-from-local-cache",
-		log.Fields{
-			"gem-port-id": gemPortID,
-			"intf-id":     intfID,
-			"onu-id":      onuID,
-			"device-id":   f.deviceHandler.device.Id})
-	f.onuGemInfoLock.RLock()
-	onugem, ok := f.onuGemInfoMap[onuID]
-	f.onuGemInfoLock.RUnlock()
-	if !ok {
-		logger.Warnw(ctx, "onu gem info already cleared from cache", log.Fields{
-			"gem-port-id": gemPortID,
-			"intf-id":     intfID,
-			"onu-id":      onuID,
-			"device-id":   f.deviceHandler.device.Id})
-		return
-	}
-deleteLoop:
-	for j, gem := range onugem.GemPorts {
-		// If the gemport is found, delete it from local cache.
-		if gem == gemPortID {
-			onugem.GemPorts = append(onugem.GemPorts[:j], onugem.GemPorts[j+1:]...)
-			f.onuGemInfoLock.Lock()
-			f.onuGemInfoMap[onuID] = onugem
-			f.onuGemInfoLock.Unlock()
-			logger.Infow(ctx, "removed-gemport-from-local-cache",
-				log.Fields{
-					"intf-id":           intfID,
-					"onu-id":            onuID,
-					"deletedgemport-id": gemPortID,
-					"gemports":          onugem.GemPorts,
-					"device-id":         f.deviceHandler.device.Id})
-			break deleteLoop
-		}
-	}
-}
-
 //clearResources clears pon resources in kv store and the device
 // nolint: gocyclo
 func (f *OpenOltFlowMgr) clearResources(ctx context.Context, intfID uint32, onuID int32, uniID int32,
@@ -1988,20 +1896,15 @@
 	case *tp_pb.TechProfileInstance:
 		for _, gemPort := range techprofileInst.UpstreamGemPortAttributeList {
 			gemPortID := gemPort.GemportId
-			used := f.isGemPortUsedByAnotherFlow(gemPortID, flowID)
+			used := f.resourceMgr.IsGemPortUsedByAnotherFlow(gemPortID, flowID)
 			if used {
-				f.gemToFlowIDsKey.RLock()
-				flowIDs := f.gemToFlowIDs[gemPortID]
-				f.gemToFlowIDsKey.RUnlock()
-
+				flowIDs, err := f.resourceMgr.GetFlowIDsForGem(ctx, intfID, gemPortID)
+				if err != nil {
+					return err
+				}
 				for i, flowIDinMap := range flowIDs {
 					if flowIDinMap == flowID {
 						flowIDs = append(flowIDs[:i], flowIDs[i+1:]...)
-						f.gemToFlowIDsKey.Lock()
-						f.gemToFlowIDs[gemPortID] = flowIDs
-						f.gemToFlowIDsKey.Unlock()
-						// everytime gemToFlowIDs cache is updated the same should be updated
-						// in kv store by calling UpdateFlowIDsForGem
 						if err := f.resourceMgr.UpdateFlowIDsForGem(ctx, intfID, gemPortID, flowIDs); err != nil {
 							return err
 						}
@@ -2036,16 +1939,9 @@
 
 		for _, gemPort := range techprofileInst.UpstreamGemPortAttributeList {
 			gemPortID := gemPort.GemportId
-			f.deleteGemPortFromLocalCache(ctx, intfID, uint32(onuID), gemPortID)
 			_ = f.resourceMgr.RemoveGemFromOnuGemInfo(ctx, intfID, uint32(onuID), gemPortID) // ignore error and proceed.
 
-			if err := f.resourceMgr.DeleteFlowIDsForGem(ctx, intfID, gemPortID); err == nil {
-				//everytime an entry is deleted from gemToFlowIDs cache, the same should be updated in kv as well
-				// by calling DeleteFlowIDsForGem
-				f.gemToFlowIDsKey.Lock()
-				delete(f.gemToFlowIDs, gemPortID)
-				f.gemToFlowIDsKey.Unlock()
-			} else {
+			if err := f.resourceMgr.DeleteFlowIDsForGem(ctx, intfID, gemPortID); err != nil {
 				logger.Errorw(ctx, "error-removing-flow-ids-of-gem-port",
 					log.Fields{
 						"err":        err,
@@ -2214,11 +2110,6 @@
 		return err
 	}
 
-	// Delete the flow-id to gemport list entry from the map now the flow is deleted.
-	f.flowIDToGemsLock.Lock()
-	delete(f.flowIDToGems, flow.Id)
-	f.flowIDToGemsLock.Unlock()
-
 	if err = f.clearResources(ctx, Intf, onuID, uniID, flow.Id, portNum, tpID, true); err != nil {
 		logger.Errorw(ctx, "failed-to-clear-resources-for-flow", log.Fields{
 			"flow-id":   flow.Id,
@@ -2440,24 +2331,6 @@
 	}
 
 	f.resourceMgr.AddUniPortToOnuInfo(ctx, intfID, onuID, portNo)
-	// also update flowmgr cache
-	f.onuGemInfoLock.Lock()
-	onugem, ok := f.onuGemInfoMap[onuID]
-	if ok {
-		found := false
-		for _, uni := range onugem.UniPorts {
-			if uni == portNo {
-				found = true
-				break
-			}
-		}
-		if !found {
-			onugem.UniPorts = append(onugem.UniPorts, portNo)
-			f.onuGemInfoMap[onuID] = onugem
-			logger.Infow(ctx, "added uni port to onugem cache", log.Fields{"uni": portNo})
-		}
-	}
-	f.onuGemInfoLock.Unlock()
 
 	tpID, err := getTpIDFromFlow(ctx, flow)
 	if err != nil {
@@ -2596,118 +2469,6 @@
 	return nil
 }
 
-//AddOnuInfoToFlowMgrCacheAndKvStore function adds onu info to cache and kvstore
-func (f *OpenOltFlowMgr) AddOnuInfoToFlowMgrCacheAndKvStore(ctx context.Context, intfID uint32, onuID uint32, serialNum string) error {
-
-	f.onuGemInfoLock.RLock()
-	_, ok := f.onuGemInfoMap[onuID]
-	f.onuGemInfoLock.RUnlock()
-	// If the ONU already exists in onuGemInfo list, nothing to do
-	if ok {
-		logger.Debugw(ctx, "onu-id-already-exists-in-cache",
-			log.Fields{"onuID": onuID,
-				"serialNum": serialNum})
-		return nil
-	}
-
-	onuGemInfo := rsrcMgr.OnuGemInfo{OnuID: onuID, SerialNumber: serialNum, IntfID: intfID}
-	f.onuGemInfoLock.Lock()
-	f.onuGemInfoMap[onuID] = &onuGemInfo
-	f.onuGemInfoLock.Unlock()
-	if err := f.resourceMgr.AddOnuGemInfo(ctx, intfID, onuID, onuGemInfo); err != nil {
-		return err
-	}
-	logger.Infow(ctx, "added-onuinfo",
-		log.Fields{
-			"intf-id":    intfID,
-			"onu-id":     onuID,
-			"serial-num": serialNum,
-			"onu":        onuGemInfo,
-			"device-id":  f.deviceHandler.device.Id})
-	return nil
-}
-
-//RemoveOnuInfoFromFlowMgrCacheAndKvStore function adds onu info to cache and kvstore
-func (f *OpenOltFlowMgr) RemoveOnuInfoFromFlowMgrCacheAndKvStore(ctx context.Context, intfID uint32, onuID uint32) error {
-
-	f.onuGemInfoLock.Lock()
-	delete(f.onuGemInfoMap, onuID)
-	f.onuGemInfoLock.Unlock()
-
-	if err := f.resourceMgr.DelOnuGemInfo(ctx, intfID, onuID); err != nil {
-		return err
-	}
-	logger.Infow(ctx, "deleted-onuinfo",
-		log.Fields{
-			"intf-id":   intfID,
-			"onu-id":    onuID,
-			"device-id": f.deviceHandler.device.Id})
-	return nil
-}
-
-//addGemPortToOnuInfoMap function adds GEMport to ONU map
-func (f *OpenOltFlowMgr) addGemPortToOnuInfoMap(ctx context.Context, intfID uint32, onuID uint32, gemPort uint32) {
-
-	logger.Infow(ctx, "adding-gem-to-onu-info-map",
-		log.Fields{
-			"gem-port-id": gemPort,
-			"intf-id":     intfID,
-			"onu-id":      onuID,
-			"device-id":   f.deviceHandler.device.Id})
-	f.onuGemInfoLock.RLock()
-	onugem, ok := f.onuGemInfoMap[onuID]
-	f.onuGemInfoLock.RUnlock()
-	if !ok {
-		logger.Warnw(ctx, "onu gem info is missing", log.Fields{
-			"gem-port-id": gemPort,
-			"intf-id":     intfID,
-			"onu-id":      onuID,
-			"device-id":   f.deviceHandler.device.Id})
-		return
-	}
-
-	if onugem.OnuID == onuID {
-		// check if gem already exists , else update the cache and kvstore
-		for _, gem := range onugem.GemPorts {
-			if gem == gemPort {
-				logger.Debugw(ctx, "gem-already-in-cache-no-need-to-update-cache-and-kv-store",
-					log.Fields{
-						"gem":       gemPort,
-						"device-id": f.deviceHandler.device.Id})
-				return
-			}
-		}
-		onugem.GemPorts = append(onugem.GemPorts, gemPort)
-		f.onuGemInfoLock.Lock()
-		f.onuGemInfoMap[onuID] = onugem
-		f.onuGemInfoLock.Unlock()
-		logger.Debugw(ctx, "updated onu gem info from cache", log.Fields{"onugem": onugem})
-	} else {
-		logger.Warnw(ctx, "mismatched onu id", log.Fields{
-			"gem-port-id": gemPort,
-			"intf-id":     intfID,
-			"onu-id":      onuID,
-			"device-id":   f.deviceHandler.device.Id})
-		return
-	}
-	err := f.resourceMgr.AddGemToOnuGemInfo(ctx, intfID, onuID, gemPort)
-	if err != nil {
-		logger.Errorw(ctx, "failed-to-add-gem-to-onu",
-			log.Fields{
-				"intf-id":   intfID,
-				"onu-id":    onuID,
-				"gemPort":   gemPort,
-				"device-id": f.deviceHandler.device.Id})
-		return
-	}
-	logger.Infow(ctx, "gem-added-to-onu-info-map",
-		log.Fields{
-			"gem-port-id": gemPort,
-			"intf-id":     intfID,
-			"onu-id":      onuID,
-			"device-id":   f.deviceHandler.device.Id})
-}
-
 //GetLogicalPortFromPacketIn function computes logical port UNI/NNI port from packet-in indication and returns the same
 func (f *OpenOltFlowMgr) GetLogicalPortFromPacketIn(ctx context.Context, packetIn *openoltpb2.PacketIndication) (uint32, error) {
 	var logicalPortNum uint32
@@ -3128,20 +2889,6 @@
 	return nil
 }
 
-func (f *OpenOltFlowMgr) isGemPortUsedByAnotherFlow(gemPortID uint32, flowID uint64) bool {
-	f.gemToFlowIDsKey.RLock()
-	flowIDList := f.gemToFlowIDs[gemPortID]
-	f.gemToFlowIDsKey.RUnlock()
-	if len(flowIDList) > 0 {
-		for _, id := range flowIDList {
-			if flowID != id {
-				return true
-			}
-		}
-	}
-	return false
-}
-
 func (f *OpenOltFlowMgr) isAllocUsedByAnotherUNI(ctx context.Context, sq schedQueue) bool {
 	tpInst := sq.tpInst.(*tp_pb.TechProfileInstance)
 	if tpInst.InstanceControl.Onu == "single-instance" && sq.direction == tp_pb.Direction_UPSTREAM {
@@ -3357,15 +3104,6 @@
 	return uint32(TpID), nil
 }
 
-func appendUnique64bit(slice []uint64, item uint64) []uint64 {
-	for _, sliceElement := range slice {
-		if sliceElement == item {
-			return slice
-		}
-	}
-	return append(slice, item)
-}
-
 func appendUnique32bit(slice []uint32, item uint32) []uint32 {
 	for _, sliceElement := range slice {
 		if sliceElement == item {
@@ -3466,32 +3204,6 @@
 	return 0, 0, nil
 }
 
-func (f *OpenOltFlowMgr) loadFlowIDsForGemAndGemIDsForFlow(ctx context.Context) {
-	logger.Debug(ctx, "loadFlowIDsForGemAndGemIDsForFlow - start")
-	f.onuGemInfoLock.RLock()
-	f.gemToFlowIDsKey.Lock()
-	f.flowIDToGemsLock.Lock()
-	for _, og := range f.onuGemInfoMap {
-		for _, gem := range og.GemPorts {
-			flowIDs, err := f.resourceMgr.GetFlowIDsForGem(ctx, f.ponPortIdx, gem)
-			if err == nil {
-				f.gemToFlowIDs[gem] = flowIDs
-				for _, flowID := range flowIDs {
-					if _, ok := f.flowIDToGems[flowID]; !ok {
-						f.flowIDToGems[flowID] = []uint32{gem}
-					} else {
-						f.flowIDToGems[flowID] = appendUnique32bit(f.flowIDToGems[flowID], gem)
-					}
-				}
-			}
-		}
-	}
-	f.flowIDToGemsLock.Unlock()
-	f.gemToFlowIDsKey.Unlock()
-	f.onuGemInfoLock.RUnlock()
-	logger.Debug(ctx, "loadFlowIDsForGemAndGemIDsForFlow - end")
-}
-
 //clearMulticastFlowFromResourceManager  removes a multicast flow from the KV store and
 // clears resources reserved for this multicast flow
 func (f *OpenOltFlowMgr) clearMulticastFlowFromResourceManager(ctx context.Context, flow *ofp.OfpFlowStats) error {
@@ -3548,16 +3260,6 @@
 	}, nil
 }
 
-func (f *OpenOltFlowMgr) getOnuGemInfoList(ctx context.Context) []rsrcMgr.OnuGemInfo {
-	var onuGemInfoLst []rsrcMgr.OnuGemInfo
-	f.onuGemInfoLock.RLock()
-	defer f.onuGemInfoLock.RUnlock()
-	for _, v := range f.onuGemInfoMap {
-		onuGemInfoLst = append(onuGemInfoLst, *v)
-	}
-	return onuGemInfoLst
-}
-
 // revertTechProfileInstance is called when CreateScheduler or CreateQueues request fails
 func (f *OpenOltFlowMgr) revertTechProfileInstance(ctx context.Context, sq schedQueue) {
 
diff --git a/internal/pkg/core/openolt_flowmgr_test.go b/internal/pkg/core/openolt_flowmgr_test.go
index 5f64a71..a880297 100644
--- a/internal/pkg/core/openolt_flowmgr_test.go
+++ b/internal/pkg/core/openolt_flowmgr_test.go
@@ -21,8 +21,6 @@
 	"context"
 	"encoding/hex"
 	"fmt"
-	"reflect"
-	"strconv"
 	"sync"
 	"testing"
 	"time"
@@ -676,154 +674,6 @@
 	}
 }
 
-func TestOpenOltFlowMgr_UpdateOnuInfo(t *testing.T) {
-	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
-	defer cancel()
-
-	wg := sync.WaitGroup{}
-
-	intfCount := NumPonPorts
-	onuCount := OnuIDEnd - OnuIDStart + 1
-
-	for i := 0; i < intfCount; i++ {
-		for j := 1; j <= onuCount; j++ {
-			wg.Add(1)
-			go func(i uint32, j uint32) {
-				// TODO: actually verify success
-				_ = flowMgr[i].AddOnuInfoToFlowMgrCacheAndKvStore(ctx, i, i, fmt.Sprintf("onu-%d", i))
-				wg.Done()
-			}(uint32(i), uint32(j))
-		}
-
-	}
-
-	wg.Wait()
-}
-
-func TestOpenOltFlowMgr_addGemPortToOnuInfoMap(t *testing.T) {
-	intfNum := NumPonPorts
-	onuNum := OnuIDEnd - OnuIDStart + 1
-
-	// clean the flowMgr
-	for i := 0; i < intfNum; i++ {
-		flowMgr[i].onuGemInfoMap = make(map[uint32]*rsrcMgr.OnuGemInfo)
-	}
-
-	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
-	defer cancel()
-
-	// Create OnuInfo
-	for i := 0; i < intfNum; i++ {
-		for o := 1; o <= onuNum; o++ {
-			// TODO: actually verify success
-			_ = flowMgr[i].AddOnuInfoToFlowMgrCacheAndKvStore(ctx, uint32(i), uint32(o), fmt.Sprintf("i%do%d", i, o-1))
-		}
-	}
-
-	// Add gemPorts to OnuInfo in parallel threads
-	wg := sync.WaitGroup{}
-	for o := 1; o <= onuNum; o++ {
-		for i := 0; i < intfNum; i++ {
-			wg.Add(1)
-			go func(intfId uint32, onuId uint32) {
-				gemID, _ := strconv.Atoi(fmt.Sprintf("90%d%d", intfId, onuId-1))
-
-				flowMgr[intfId].addGemPortToOnuInfoMap(ctx, intfId, onuId, uint32(gemID))
-				wg.Done()
-			}(uint32(i), uint32(o))
-		}
-	}
-
-	wg.Wait()
-
-	// check that each entry of onuGemInfoMap has the correct number of ONUs
-	for i := 0; i < intfNum; i++ {
-		lenofOnu := len(flowMgr[i].onuGemInfoMap)
-		if onuNum != lenofOnu {
-			t.Errorf("onuGemInfoMap length is not as expected len = %d, want %d", lenofOnu, onuNum)
-		}
-
-		for o := 1; o <= onuNum; o++ {
-			lenOfGemPorts := len(flowMgr[i].onuGemInfoMap[uint32(o)].GemPorts)
-			// check that each onuEntry has 1 gemPort
-			if lenOfGemPorts != 1 {
-				t.Errorf("Expected 1 GemPort per ONU, found %d", lenOfGemPorts)
-			}
-
-			// check that the value of the gemport is correct
-			gemID, _ := strconv.Atoi(fmt.Sprintf("90%d%d", i, o-1))
-			currentValue := flowMgr[i].onuGemInfoMap[uint32(o)].GemPorts[0]
-			if uint32(gemID) != currentValue {
-				t.Errorf("Expected GemPort value to be %d, found %d", gemID, currentValue)
-			}
-		}
-	}
-}
-
-func TestOpenOltFlowMgr_deleteGemPortFromLocalCache(t *testing.T) {
-	// Create fresh flowMgr instance
-	flowMgr = newMockFlowmgr()
-	type args struct {
-		intfID                uint32
-		onuID                 uint32
-		gemPortIDs            []uint32
-		gemPortIDsToBeDeleted []uint32
-		gemPortIDsRemaining   []uint32
-		serialNum             string
-		finalLength           int
-	}
-	tests := []struct {
-		name string
-		args args
-	}{
-		// Add/Delete single gem port
-		{"DeleteGemPortFromLocalCache1", args{0, 1, []uint32{1}, []uint32{1}, []uint32{}, "onu1", 0}},
-		// Delete all gemports
-		{"DeleteGemPortFromLocalCache2", args{0, 1, []uint32{1, 2, 3, 4}, []uint32{1, 2, 3, 4}, []uint32{}, "onu1", 0}},
-		// Try to delete when there is no gem port
-		{"DeleteGemPortFromLocalCache3", args{0, 1, []uint32{}, []uint32{1, 2}, nil, "onu1", 0}},
-		// Try to delete non-existent gem port
-		{"DeleteGemPortFromLocalCache4", args{0, 1, []uint32{1}, []uint32{2}, []uint32{1}, "onu1", 1}},
-		// Try to delete two of the gem ports
-		{"DeleteGemPortFromLocalCache5", args{0, 1, []uint32{1, 2, 3, 4}, []uint32{2, 4}, []uint32{1, 3}, "onu1", 2}},
-	}
-	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
-	defer cancel()
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			if err := flowMgr[tt.args.intfID].RemoveOnuInfoFromFlowMgrCacheAndKvStore(ctx, tt.args.intfID, tt.args.onuID); err != nil {
-				t.Errorf("failed to remove onu")
-			}
-			if err := flowMgr[tt.args.intfID].AddOnuInfoToFlowMgrCacheAndKvStore(ctx, tt.args.intfID, tt.args.onuID, tt.args.serialNum); err != nil {
-				t.Errorf("failed to add onu")
-			}
-			for _, gemPort := range tt.args.gemPortIDs {
-				flowMgr[tt.args.intfID].addGemPortToOnuInfoMap(ctx, tt.args.intfID, tt.args.onuID, gemPort)
-			}
-			for _, gemPortDeleted := range tt.args.gemPortIDsToBeDeleted {
-				flowMgr[tt.args.intfID].deleteGemPortFromLocalCache(ctx, tt.args.intfID, tt.args.onuID, gemPortDeleted)
-			}
-			lenofGemPorts := 0
-			gP, ok := flowMgr[tt.args.intfID].onuGemInfoMap[tt.args.onuID]
-			if ok {
-				lenofGemPorts = len(gP.GemPorts)
-			}
-			if lenofGemPorts != tt.args.finalLength {
-				t.Errorf("GemPorts length is not as expected len = %d, want %d", lenofGemPorts, tt.args.finalLength)
-			}
-			gP, ok = flowMgr[tt.args.intfID].onuGemInfoMap[tt.args.onuID]
-			var gemPorts []uint32
-			if ok {
-				gemPorts = gP.GemPorts
-			}
-			if !reflect.DeepEqual(tt.args.gemPortIDsRemaining, gemPorts) {
-				t.Errorf("GemPorts are not as expected = %v, want %v", gemPorts, tt.args.gemPortIDsRemaining)
-			}
-
-		})
-	}
-}
-
 func TestOpenOltFlowMgr_GetLogicalPortFromPacketIn(t *testing.T) {
 	type args struct {
 		packetIn *openoltpb2.PacketIndication
diff --git a/internal/pkg/resourcemanager/resourcemanager.go b/internal/pkg/resourcemanager/resourcemanager.go
index 476bbf5..cfc755e 100755
--- a/internal/pkg/resourcemanager/resourcemanager.go
+++ b/internal/pkg/resourcemanager/resourcemanager.go
@@ -244,7 +244,9 @@
 	}
 
 	ResourceMgr.InitLocalCache()
-
+	if err := ResourceMgr.LoadLocalCacheFromKVStore(ctx, PonIntfID); err != nil {
+		logger.Error(ctx, "failed-to-load-local-cache-from-kvstore")
+	}
 	logger.Info(ctx, "Initialization of  resource manager success!")
 	return &ResourceMgr
 }
@@ -263,6 +265,52 @@
 	rsrcMgr.groupInfo = make(map[string]*GroupInfo)
 }
 
+//LoadLocalCacheFromKVStore loads local maps
+func (rsrcMgr *OpenOltResourceMgr) LoadLocalCacheFromKVStore(ctx context.Context, PonIntfID uint32) error {
+
+	//List all the keys for OnuGemInfo
+	prefixPath := fmt.Sprintf(OnuGemInfoPathPathPrefix, PonIntfID)
+	keys, err := rsrcMgr.KVStore.List(ctx, prefixPath)
+	logger.Debug(ctx, "load-local-cache-from-KV-store-started")
+	if err != nil {
+		logger.Errorf(ctx, "failed-to-list-keys-from-path-%s", prefixPath)
+		return err
+	}
+	for path := range keys {
+		var Val []byte
+		var onugem OnuGemInfo
+		// Get rid of the path prefix
+		stringToBeReplaced := rsrcMgr.KVStore.PathPrefix + "/"
+		replacedWith := ""
+		path = strings.Replace(path, stringToBeReplaced, replacedWith, 1)
+
+		value, err := rsrcMgr.KVStore.Get(ctx, path)
+		if err != nil {
+			logger.Errorw(ctx, "failed-to-get-from-kv-store", log.Fields{"path": path})
+			return err
+		} else if value == nil {
+			logger.Debug(ctx, "no-onugeminfo-for-path", log.Fields{"path": path})
+			continue
+		}
+		if Val, err = kvstore.ToByte(value.Value); err != nil {
+			logger.Error(ctx, "failed-to-covert-to-byte-array")
+			return err
+		}
+		if err = json.Unmarshal(Val, &onugem); err != nil {
+			logger.Error(ctx, "failed-to-unmarshall")
+			return err
+		}
+		logger.Debugw(ctx, "found-onugeminfo-from-path", log.Fields{"path": path, "onuGemInfo": onugem})
+
+		rsrcMgr.onuGemInfoLock.Lock()
+		rsrcMgr.onuGemInfo[path] = &onugem
+		rsrcMgr.onuGemInfoLock.Unlock()
+
+	}
+	logger.Debug(ctx, "load-local-cache-from-KV-store-finished")
+	return nil
+}
+
 // InitializeDeviceResourceRangeAndPool initializes the resource range pool according to the sharing type, then apply
 // device specific information. If KV doesn't exist
 // or is broader than the device, the device's information will
@@ -948,6 +996,36 @@
 	return &onugem, nil
 }
 
+//AddNewOnuGemInfoToCacheAndKvStore function adds a new  onu gem info to cache and kvstore
+func (rsrcMgr *OpenOltResourceMgr) AddNewOnuGemInfoToCacheAndKvStore(ctx context.Context, intfID uint32, onuID uint32, serialNum string) error {
+
+	Path := fmt.Sprintf(OnuGemInfoPath, intfID, onuID)
+
+	rsrcMgr.onuGemInfoLock.Lock()
+	_, ok := rsrcMgr.onuGemInfo[Path]
+	rsrcMgr.onuGemInfoLock.Unlock()
+
+	// If the ONU already exists in onuGemInfo list, nothing to do
+	if ok {
+		logger.Debugw(ctx, "onu-id-already-exists-in-cache", log.Fields{"onuID": onuID, "serialNum": serialNum})
+		return nil
+	}
+
+	onuGemInfo := OnuGemInfo{OnuID: onuID, SerialNumber: serialNum, IntfID: intfID}
+
+	if err := rsrcMgr.AddOnuGemInfo(ctx, intfID, onuID, onuGemInfo); err != nil {
+		return err
+	}
+	logger.Infow(ctx, "added-onuinfo",
+		log.Fields{
+			"intf-id":    intfID,
+			"onu-id":     onuID,
+			"serial-num": serialNum,
+			"onu":        onuGemInfo,
+			"device-id":  rsrcMgr.DeviceID})
+	return nil
+}
+
 // AddOnuGemInfo adds onu info on to the kvstore per interface
 func (rsrcMgr *OpenOltResourceMgr) AddOnuGemInfo(ctx context.Context, intfID uint32, onuID uint32, onuGem OnuGemInfo) error {
 
@@ -965,7 +1043,7 @@
 		logger.Errorf(ctx, "Failed to update resource %s", Path)
 		return err
 	}
-	logger.Debugw(ctx, "added onu gem info to store", log.Fields{"onuGemInfo": onuGem})
+	logger.Debugw(ctx, "added onu gem info to store", log.Fields{"onuGemInfo": onuGem, "Path": Path})
 
 	//update cache
 	rsrcMgr.onuGemInfoLock.Lock()
@@ -1173,6 +1251,34 @@
 	return flowIDs, nil
 }
 
+// IsGemPortUsedByAnotherFlow returns true if given gem is used by another flow
+func (rsrcMgr *OpenOltResourceMgr) IsGemPortUsedByAnotherFlow(gemPortID uint32, flowID uint64) bool {
+	rsrcMgr.flowIDsForGemLock.RLock()
+	flowIDList := rsrcMgr.flowIDsForGem[gemPortID]
+	rsrcMgr.flowIDsForGemLock.RUnlock()
+	for _, id := range flowIDList {
+		if flowID != id {
+			return true
+		}
+	}
+	return false
+}
+
+// RegisterFlowIDForGem updates both cache and KV store for flowIDsForGem map
+func (rsrcMgr *OpenOltResourceMgr) RegisterFlowIDForGem(ctx context.Context, accessIntfID uint32, gemPortID uint32, flowFromCore *ofp.OfpFlowStats) error {
+	// get from cache
+	rsrcMgr.flowIDsForGemLock.RLock()
+	flowIDs, ok := rsrcMgr.flowIDsForGem[gemPortID]
+	rsrcMgr.flowIDsForGemLock.RUnlock()
+	if !ok {
+		flowIDs = []uint64{flowFromCore.Id}
+	} else {
+		flowIDs = appendUnique64bit(flowIDs, flowFromCore.Id)
+	}
+	// update the flowids for a gem to the KVstore
+	return rsrcMgr.UpdateFlowIDsForGem(ctx, accessIntfID, gemPortID, flowIDs)
+}
+
 //UpdateFlowIDsForGem updates flow id per gemport
 func (rsrcMgr *OpenOltResourceMgr) UpdateFlowIDsForGem(ctx context.Context, intf uint32, gem uint32, flowIDs []uint64) error {
 	var val []byte
@@ -1424,6 +1530,17 @@
 	return false, groupInfo, nil
 }
 
+// GetOnuGemInfoList returns all gems in the onuGemInfo map
+func (rsrcMgr *OpenOltResourceMgr) GetOnuGemInfoList(ctx context.Context) []OnuGemInfo {
+	var onuGemInfoLst []OnuGemInfo
+	rsrcMgr.onuGemInfoLock.RLock()
+	defer rsrcMgr.onuGemInfoLock.RUnlock()
+	for _, v := range rsrcMgr.onuGemInfo {
+		onuGemInfoLst = append(onuGemInfoLst, *v)
+	}
+	return onuGemInfoLst
+}
+
 // toByte converts an interface value to a []byte.  The interface should either be of
 // a string type or []byte.  Otherwise, an error is returned.
 func toByte(value interface{}) ([]byte, error) {
@@ -1436,3 +1553,12 @@
 		return nil, fmt.Errorf("unexpected-type-%T", t)
 	}
 }
+
+func appendUnique64bit(slice []uint64, item uint64) []uint64 {
+	for _, sliceElement := range slice {
+		if sliceElement == item {
+			return slice
+		}
+	}
+	return append(slice, item)
+}
diff --git a/internal/pkg/resourcemanager/resourcemanager_test.go b/internal/pkg/resourcemanager/resourcemanager_test.go
index 5392eb7..85a5811 100644
--- a/internal/pkg/resourcemanager/resourcemanager_test.go
+++ b/internal/pkg/resourcemanager/resourcemanager_test.go
@@ -27,6 +27,7 @@
 	"context"
 	"encoding/json"
 	"errors"
+	"fmt"
 	"reflect"
 	"strconv"
 	"strings"
@@ -493,6 +494,162 @@
 	}
 }
 
+func TestOpenOltResourceMgr_deleteGemPort(t *testing.T) {
+
+	type args struct {
+		intfID                uint32
+		onuID                 uint32
+		gemPortIDs            []uint32
+		gemPortIDsToBeDeleted []uint32
+		gemPortIDsRemaining   []uint32
+		serialNum             string
+		finalLength           int
+	}
+	tests := []struct {
+		name   string
+		fields *fields
+		args   args
+	}{
+		// Add/Delete single gem port
+		{"DeleteGemPortFromLocalCache1", getResMgr(), args{0, 1, []uint32{1}, []uint32{1}, []uint32{}, "onu1", 0}},
+		// Delete all gemports
+		{"DeleteGemPortFromLocalCache2", getResMgr(), args{0, 1, []uint32{1, 2, 3, 4}, []uint32{1, 2, 3, 4}, []uint32{}, "onu1", 0}},
+		// Try to delete when there is no gem port
+		{"DeleteGemPortFromLocalCache3", getResMgr(), args{0, 1, []uint32{}, []uint32{1, 2}, nil, "onu1", 0}},
+		// Try to delete non-existent gem port
+		{"DeleteGemPortFromLocalCache4", getResMgr(), args{0, 1, []uint32{1}, []uint32{2}, []uint32{1}, "onu1", 1}},
+		// Try to delete two of the gem ports
+		{"DeleteGemPortFromLocalCache5", getResMgr(), args{0, 1, []uint32{1, 2, 3, 4}, []uint32{2, 4}, []uint32{1, 3}, "onu1", 2}},
+	}
+	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+	defer cancel()
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			RsrcMgr := testResMgrObject(tt.fields)
+			if err := RsrcMgr.DelOnuGemInfo(ctx, tt.args.intfID, tt.args.onuID); err != nil {
+				t.Errorf("failed to remove onu")
+			}
+			if err := RsrcMgr.AddNewOnuGemInfoToCacheAndKvStore(ctx, tt.args.intfID, tt.args.onuID, tt.args.serialNum); err != nil {
+				t.Errorf("failed to add onu")
+			}
+			for _, gemPort := range tt.args.gemPortIDs {
+				if err := RsrcMgr.AddGemToOnuGemInfo(ctx, tt.args.intfID, tt.args.onuID, gemPort); err != nil {
+					t.Errorf("failed to add gem to onu")
+				}
+			}
+			for _, gemPortDeleted := range tt.args.gemPortIDsToBeDeleted {
+				if err := RsrcMgr.RemoveGemFromOnuGemInfo(ctx, tt.args.intfID, tt.args.onuID, gemPortDeleted); err != nil {
+					t.Errorf("failed to remove gem from onu")
+				}
+			}
+			lenofGemPorts := 0
+			gP, err := RsrcMgr.GetOnuGemInfo(ctx, tt.args.intfID, tt.args.onuID)
+			if err != nil || gP == nil {
+				t.Errorf("failed to get onuGemInfo")
+			}
+			var gemPorts []uint32
+
+			lenofGemPorts = len(gP.GemPorts)
+			gemPorts = gP.GemPorts
+
+			if lenofGemPorts != tt.args.finalLength {
+				t.Errorf("GemPorts length is not as expected len = %d, want %d", lenofGemPorts, tt.args.finalLength)
+			}
+
+			if !reflect.DeepEqual(tt.args.gemPortIDsRemaining, gemPorts) {
+				t.Errorf("GemPorts are not as expected = %v, want %v", gemPorts, tt.args.gemPortIDsRemaining)
+			}
+		})
+	}
+}
+
+func TestOpenOltResourceMgr_AddNewOnuGemInfo(t *testing.T) {
+
+	type args struct {
+		PONIntfID uint32
+		OnuCount  uint32
+	}
+	tests := []struct {
+		name   string
+		fields *fields
+		args   args
+		want   error
+	}{
+		{"AddNewOnuGemInfoForIntf-0", getResMgr(), args{0, 32}, nil},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			RsrcMgr := testResMgrObject(tt.fields)
+			ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+			defer cancel()
+			for j := 1; j <= int(tt.args.OnuCount); j++ {
+				go func(i uint32, j uint32) {
+					// TODO: actually verify success
+					_ = RsrcMgr.AddNewOnuGemInfoToCacheAndKvStore(ctx, i, i, fmt.Sprintf("onu-%d", i))
+				}(tt.args.PONIntfID, uint32(j))
+			}
+		})
+	}
+}
+
+func TestOpenOltFlowMgr_addGemPortToOnuInfoMap(t *testing.T) {
+
+	type args struct {
+		intfID              uint32
+		onuID               uint32
+		gemPortIDs          []uint32
+		gemPortIDsRemaining []uint32
+		serialNum           string
+		finalLength         int
+	}
+	tests := []struct {
+		name   string
+		fields *fields
+		args   args
+	}{
+		// Add single gem port
+		{"addGemPortToOnuInfoMap1", getResMgr(), args{0, 1, []uint32{1}, []uint32{1}, "onu1", 1}},
+		// Delete all gemports
+		{"addGemPortToOnuInfoMap2", getResMgr(), args{0, 1, []uint32{1, 2, 3, 4}, []uint32{1, 2, 3, 4}, "onu1", 4}},
+		// Do not add any gemport
+		{"addGemPortToOnuInfoMap3", getResMgr(), args{0, 1, []uint32{}, nil, "onu1", 0}},
+	}
+	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+	defer cancel()
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			RsrcMgr := testResMgrObject(tt.fields)
+			if err := RsrcMgr.DelOnuGemInfo(ctx, tt.args.intfID, tt.args.onuID); err != nil {
+				t.Errorf("failed to remove onu")
+			}
+			if err := RsrcMgr.AddNewOnuGemInfoToCacheAndKvStore(ctx, tt.args.intfID, tt.args.onuID, tt.args.serialNum); err != nil {
+				t.Errorf("failed to add onu")
+			}
+			for _, gemPort := range tt.args.gemPortIDs {
+				if err := RsrcMgr.AddGemToOnuGemInfo(ctx, tt.args.intfID, tt.args.onuID, gemPort); err != nil {
+					t.Errorf("failed to add gem to onu")
+				}
+			}
+
+			lenofGemPorts := 0
+			gP, err := RsrcMgr.GetOnuGemInfo(ctx, tt.args.intfID, tt.args.onuID)
+
+			var gemPorts []uint32
+			if err == nil && gP != nil {
+				lenofGemPorts = len(gP.GemPorts)
+				gemPorts = gP.GemPorts
+			}
+			if lenofGemPorts != tt.args.finalLength {
+				t.Errorf("GemPorts length is not as expected len = %d, want %d", lenofGemPorts, tt.args.finalLength)
+			}
+
+			if !reflect.DeepEqual(tt.args.gemPortIDsRemaining, gemPorts) {
+				t.Errorf("GemPorts are not as expected = %v, want %v", gemPorts, tt.args.gemPortIDsRemaining)
+			}
+		})
+	}
+}
+
 func TestOpenOltResourceMgr_GetCurrentGEMPortIDsForOnu(t *testing.T) {
 	type args struct {
 		intfID uint32