[VOL-3442] reconcile treatment for existing flow configuration

Signed-off-by: Holger Hildebrandt <holger.hildebrandt@adtran.com>
Change-Id: I9f132f565d262cf8660efae3473aa2f7983c0464
diff --git a/internal/pkg/onuadaptercore/device_handler.go b/internal/pkg/onuadaptercore/device_handler.go
index f7dad9a..9e98452 100644
--- a/internal/pkg/onuadaptercore/device_handler.go
+++ b/internal/pkg/onuadaptercore/device_handler.go
@@ -237,7 +237,7 @@
 		return pDevEntry.PDevOmciCC.receiveMessage(context.TODO(), omciMsg.Message)
 	}
 	logger.Errorw("No valid OnuDevice -aborting", log.Fields{"device-id": dh.deviceID})
-	return errors.New("no valid OnuDevice")
+	return fmt.Errorf("no valid OnuDevice: %s", dh.deviceID)
 }
 
 func (dh *deviceHandler) processInterAdapterONUIndReqMessage(msg *ic.InterAdapterMessage) error {
@@ -261,23 +261,29 @@
 		_ = dh.updateInterface(onuIndication)
 	} else {
 		logger.Errorw("unknown-onu-indication operState", log.Fields{"OnuId": onuIndication.GetOnuId()})
-		return errors.New("invalidOperState")
+		return fmt.Errorf("invalidOperState: %s, %s", onuOperstate, dh.deviceID)
 	}
 	return nil
 }
 
 func (dh *deviceHandler) processInterAdapterTechProfileDownloadReqMessage(
 	msg *ic.InterAdapterMessage) error {
+
+	pDevEntry := dh.getOnuDeviceEntry(true)
+	if pDevEntry == nil {
+		logger.Errorw("No valid OnuDevice - aborting", log.Fields{"device-id": dh.deviceID})
+		return fmt.Errorf("no valid OnuDevice: %s", dh.deviceID)
+	}
 	if dh.pOnuTP == nil {
 		//should normally not happen ...
 		logger.Warnw("onuTechProf instance not set up for DLMsg request - ignoring request",
 			log.Fields{"device-id": dh.deviceID})
-		return errors.New("techProfile DLMsg request while onuTechProf instance not setup")
+		return fmt.Errorf("techProfile DLMsg request while onuTechProf instance not setup: %s", dh.deviceID)
 	}
 	if (dh.deviceReason == "stopping-openomci") || (dh.deviceReason == "omci-admin-lock") {
 		// I've seen cases for this request, where the device was already stopped
 		logger.Warnw("TechProf stopped: device-unreachable", log.Fields{"device-id": dh.deviceID})
-		return errors.New("device-unreachable")
+		return fmt.Errorf("device-unreachable: %s", dh.deviceID)
 	}
 
 	msgBody := msg.GetBody()
@@ -289,10 +295,20 @@
 	}
 
 	// we have to lock access to TechProfile processing based on different messageType calls or
-	// even to fast subsequent calls of the same messageType
+	// even to fast subsequent calls of the same messageType as well as OnuKVStore processing due
+	// to possible concurrent access by flow processing
 	dh.pOnuTP.lockTpProcMutex()
-	// lock hangs as long as below decoupled or other related TechProfile processing is active
-	if bTpModify := dh.pOnuTP.updateOnuUniTpPath(techProfMsg.UniId, techProfMsg.Path); bTpModify {
+	defer dh.pOnuTP.unlockTpProcMutex()
+	pDevEntry.lockOnuKVStoreMutex()
+	defer pDevEntry.unlockOnuKVStoreMutex()
+
+	if techProfMsg.UniId > 255 {
+		return fmt.Errorf(fmt.Sprintf("received UniId value exceeds range: %d, device-id: %s",
+			techProfMsg.UniId, dh.deviceID))
+	}
+	uniID := uint8(techProfMsg.UniId)
+
+	if bTpModify := pDevEntry.updateOnuUniTpPath(uniID, techProfMsg.Path); bTpModify {
 		//	if there has been some change for some uni TechProfilePath
 		//in order to allow concurrent calls to other dh instances we do not wait for execution here
 		//but doing so we can not indicate problems to the caller (who does what with that then?)
@@ -305,30 +321,35 @@
 		deadline := time.Now().Add(30 * time.Second) //allowed run time to finish before execution
 		dctx, cancel := context.WithDeadline(context.Background(), deadline)
 
-		dh.pOnuTP.resetProcessingErrorIndication()
+		dh.pOnuTP.resetTpProcessingErrorIndication()
+		pDevEntry.resetKvProcessingErrorIndication()
+
 		var wg sync.WaitGroup
 		wg.Add(2) // for the 2 go routines to finish
 		// attention: deadline completion check and wg.Done is to be done in both routines
-		go dh.pOnuTP.configureUniTp(dctx, uint8(techProfMsg.UniId), techProfMsg.Path, &wg)
-		go dh.pOnuTP.updateOnuTpPathKvStore(dctx, &wg)
-		//the wait.. function is responsible for tpProcMutex.Unlock()
-		err := dh.pOnuTP.waitForTpCompletion(cancel, &wg) //wait for background process to finish and collect their result
-		return err
+		go dh.pOnuTP.configureUniTp(dctx, uniID, techProfMsg.Path, &wg)
+		go pDevEntry.updateOnuKvStore(dctx, &wg)
+		dh.waitForCompletion(cancel, &wg) //wait for background process to finish
+
+		return dh.combineErrorStrings(dh.pOnuTP.getTpProcessingErrorIndication(), pDevEntry.getKvProcessingErrorIndication())
 	}
-	// no change, nothing really to do
-	dh.pOnuTP.unlockTpProcMutex()
-	//return success
+	// no change, nothing really to do - return success
 	return nil
 }
 
 func (dh *deviceHandler) processInterAdapterDeleteGemPortReqMessage(
 	msg *ic.InterAdapterMessage) error {
 
+	pDevEntry := dh.getOnuDeviceEntry(true)
+	if pDevEntry == nil {
+		logger.Errorw("No valid OnuDevice - aborting", log.Fields{"device-id": dh.deviceID})
+		return fmt.Errorf("no valid OnuDevice: %s", dh.deviceID)
+	}
 	if dh.pOnuTP == nil {
 		//should normally not happen ...
 		logger.Warnw("onuTechProf instance not set up for DelGem request - ignoring request",
 			log.Fields{"device-id": dh.deviceID})
-		return errors.New("techProfile DelGem request while onuTechProf instance not setup")
+		return fmt.Errorf("techProfile DelGem request while onuTechProf instance not setup: %s", dh.deviceID)
 	}
 
 	msgBody := msg.GetBody()
@@ -341,28 +362,50 @@
 
 	//compare TECH_PROFILE_DOWNLOAD_REQUEST
 	dh.pOnuTP.lockTpProcMutex()
+	defer dh.pOnuTP.unlockTpProcMutex()
+	pDevEntry.lockOnuKVStoreMutex()
+	defer pDevEntry.unlockOnuKVStoreMutex()
 
-	// deadline context to ensure completion of background routines waited for
-	deadline := time.Now().Add(10 * time.Second) //allowed run time to finish before execution
-	dctx, cancel := context.WithDeadline(context.Background(), deadline)
+	if delGemPortMsg.UniId > 255 {
+		return fmt.Errorf(fmt.Sprintf("received UniId value exceeds range: %d, device-id: %s",
+			delGemPortMsg.UniId, dh.deviceID))
+	}
+	uniID := uint8(delGemPortMsg.UniId)
 
-	dh.pOnuTP.resetProcessingErrorIndication()
-	var wg sync.WaitGroup
-	wg.Add(1) // for the 1 go routine to finish
-	go dh.pOnuTP.deleteTpResource(dctx, delGemPortMsg.UniId, delGemPortMsg.TpPath,
-		cResourceGemPort, delGemPortMsg.GemPortId, &wg)
-	//the wait.. function is responsible for tpProcMutex.Unlock()
-	err := dh.pOnuTP.waitForTpCompletion(cancel, &wg) //let that also run off-line to let the IA messaging return!
-	return err
+	if bTpModify := pDevEntry.updateOnuUniTpPath(uniID, ""); bTpModify {
+		// deadline context to ensure completion of background routines waited for
+		deadline := time.Now().Add(10 * time.Second) //allowed run time to finish before execution
+		dctx, cancel := context.WithDeadline(context.Background(), deadline)
+
+		dh.pOnuTP.resetTpProcessingErrorIndication()
+		pDevEntry.resetKvProcessingErrorIndication()
+
+		var wg sync.WaitGroup
+		wg.Add(2) // for the 2 go routines to finish
+		go pDevEntry.deleteTpResource(dctx, uniID, delGemPortMsg.TpPath,
+			cResourceGemPort, delGemPortMsg.GemPortId, &wg)
+		// Removal of the tcont/alloc id mapping represents the removal of the tech profile
+		go pDevEntry.updateOnuKvStore(dctx, &wg)
+		dh.waitForCompletion(cancel, &wg) //wait for background process to finish
+
+		return dh.combineErrorStrings(dh.pOnuTP.getTpProcessingErrorIndication(), pDevEntry.getKvProcessingErrorIndication())
+	}
+	return nil
 }
 
 func (dh *deviceHandler) processInterAdapterDeleteTcontReqMessage(
 	msg *ic.InterAdapterMessage) error {
+
+	pDevEntry := dh.getOnuDeviceEntry(true)
+	if pDevEntry == nil {
+		logger.Errorw("No valid OnuDevice - aborting", log.Fields{"device-id": dh.deviceID})
+		return fmt.Errorf("no valid OnuDevice: %s", dh.deviceID)
+	}
 	if dh.pOnuTP == nil {
 		//should normally not happen ...
 		logger.Warnw("onuTechProf instance not set up for DelTcont request - ignoring request",
 			log.Fields{"device-id": dh.deviceID})
-		return errors.New("techProfile DelTcont request while onuTechProf instance not setup")
+		return fmt.Errorf("techProfile DelTcont request while onuTechProf instance not setup: %s", dh.deviceID)
 	}
 
 	msgBody := msg.GetBody()
@@ -375,24 +418,34 @@
 
 	//compare TECH_PROFILE_DOWNLOAD_REQUEST
 	dh.pOnuTP.lockTpProcMutex()
-	if bTpModify := dh.pOnuTP.updateOnuUniTpPath(delTcontMsg.UniId, ""); bTpModify {
+	defer dh.pOnuTP.unlockTpProcMutex()
+	pDevEntry.lockOnuKVStoreMutex()
+	defer pDevEntry.unlockOnuKVStoreMutex()
+
+	if delTcontMsg.UniId > 255 {
+		return fmt.Errorf(fmt.Sprintf("received UniId value exceeds range: %d, device-id: %s",
+			delTcontMsg.UniId, dh.deviceID))
+	}
+	uniID := uint8(delTcontMsg.UniId)
+
+	if bTpModify := pDevEntry.updateOnuUniTpPath(uniID, ""); bTpModify {
 		// deadline context to ensure completion of background routines waited for
 		deadline := time.Now().Add(10 * time.Second) //allowed run time to finish before execution
 		dctx, cancel := context.WithDeadline(context.Background(), deadline)
 
-		dh.pOnuTP.resetProcessingErrorIndication()
+		dh.pOnuTP.resetTpProcessingErrorIndication()
+		pDevEntry.resetKvProcessingErrorIndication()
+
 		var wg sync.WaitGroup
 		wg.Add(2) // for the 2 go routines to finish
-		go dh.pOnuTP.deleteTpResource(dctx, delTcontMsg.UniId, delTcontMsg.TpPath,
+		go pDevEntry.deleteTpResource(dctx, uniID, delTcontMsg.TpPath,
 			cResourceTcont, delTcontMsg.AllocId, &wg)
 		// Removal of the tcont/alloc id mapping represents the removal of the tech profile
-		go dh.pOnuTP.updateOnuTpPathKvStore(dctx, &wg)
-		//the wait.. function is responsible for tpProcMutex.Unlock()
-		err := dh.pOnuTP.waitForTpCompletion(cancel, &wg) //let that also run off-line to let the IA messaging return!
-		return err
+		go pDevEntry.updateOnuKvStore(dctx, &wg)
+		dh.waitForCompletion(cancel, &wg) //wait for background process to finish
+
+		return dh.combineErrorStrings(dh.pOnuTP.getTpProcessingErrorIndication(), pDevEntry.getKvProcessingErrorIndication())
 	}
-	dh.pOnuTP.unlockTpProcMutex()
-	//return success
 	return nil
 }
 
@@ -434,8 +487,8 @@
 	default:
 		{
 			logger.Errorw("inter-adapter-unhandled-type", log.Fields{
-				"device-id": dh.deviceID, "msgType": msg.Header.Type})
-			return errors.New("unimplemented")
+				"msgType": msg.Header.Type, "device-id": dh.deviceID})
+			return fmt.Errorf("inter-adapter-unhandled-type: %d, %s", msg.Header.Type, dh.deviceID)
 		}
 	}
 }
@@ -461,7 +514,7 @@
 			flowInPort := flow.GetInPort(flowItem)
 			if flowInPort == uint32(of.OfpPortNo_OFPP_INVALID) {
 				logger.Errorw("flow inPort invalid", log.Fields{"deviceID": dh.deviceID})
-				return errors.New("flow inPort invalid")
+				return fmt.Errorf("flow inPort invalid: %s", dh.deviceID)
 			} else if flowInPort == dh.ponPortNumber {
 				//this is some downstream flow
 				logger.Debugw("incremental flow ignore downstream", log.Fields{
@@ -557,64 +610,127 @@
 func (dh *deviceHandler) reconcileDeviceOnuInd() {
 	logger.Debugw("reconciling - simulate onu indication", log.Fields{"device-id": dh.deviceID})
 
-	if err := dh.pOnuTP.restoreFromOnuTpPathKvStore(context.TODO()); err != nil {
+	pDevEntry := dh.getOnuDeviceEntry(true)
+	if pDevEntry == nil {
+		logger.Errorw("No valid OnuDevice - aborting", log.Fields{"device-id": dh.deviceID})
+		return
+	}
+	if err := pDevEntry.restoreDataFromOnuKvStore(context.TODO()); err != nil {
 		logger.Errorw("reconciling - restoring OnuTp-data failed - abort", log.Fields{"err": err, "device-id": dh.deviceID})
 		dh.reconciling = false
 		return
 	}
 	var onuIndication oop.OnuIndication
-	onuIndication.IntfId = dh.pOnuTP.sOnuPersistentData.PersIntfID
-	onuIndication.OnuId = dh.pOnuTP.sOnuPersistentData.PersOnuID
-	onuIndication.OperState = dh.pOnuTP.sOnuPersistentData.PersOperState
-	onuIndication.AdminState = dh.pOnuTP.sOnuPersistentData.PersAdminState
+	onuIndication.IntfId = pDevEntry.sOnuPersistentData.PersIntfID
+	onuIndication.OnuId = pDevEntry.sOnuPersistentData.PersOnuID
+	onuIndication.OperState = pDevEntry.sOnuPersistentData.PersOperState
+	onuIndication.AdminState = pDevEntry.sOnuPersistentData.PersAdminState
 	_ = dh.createInterface(&onuIndication)
 }
 
 func (dh *deviceHandler) reconcileDeviceTechProf() {
 	logger.Debugw("reconciling - trigger tech profile config", log.Fields{"device-id": dh.deviceID})
 
-	dh.pOnuTP.lockTpProcMutex()
-	// lock hangs as long as below decoupled or other related TechProfile processing is active
-	for _, uniData := range dh.pOnuTP.sOnuPersistentData.PersUniTpPath {
-		//In order to allow concurrent calls to other dh instances we do not wait for execution here
-		//but doing so we can not indicate problems to the caller (who does what with that then?)
-		//by now we just assume straightforward successful execution
-		//TODO!!! Generally: In this scheme it would be good to have some means to indicate
-		//  possible problems to the caller later autonomously
+	pDevEntry := dh.getOnuDeviceEntry(true)
+	if pDevEntry == nil {
+		logger.Errorw("No valid OnuDevice - aborting", log.Fields{"device-id": dh.deviceID})
+		return
+	}
 
+	dh.pOnuTP.lockTpProcMutex()
+	defer dh.pOnuTP.unlockTpProcMutex()
+
+	for _, uniData := range pDevEntry.sOnuPersistentData.PersUniConfig {
 		// deadline context to ensure completion of background routines waited for
 		//20200721: 10s proved to be less in 8*8 ONU test on local vbox machine with debug, might be further adapted
 		deadline := time.Now().Add(30 * time.Second) //allowed run time to finish before execution
 		dctx, cancel := context.WithDeadline(context.Background(), deadline)
 
-		dh.pOnuTP.resetProcessingErrorIndication()
+		dh.pOnuTP.resetTpProcessingErrorIndication()
+
 		var wg sync.WaitGroup
-		wg.Add(1) // for the 1 go routines to finish
-		// attention: deadline completion check and wg.Done is to be done in both routines
-		go dh.pOnuTP.configureUniTp(dctx, uint8(uniData.PersUniID), uniData.PersTpPath, &wg)
-		//the wait.. function is responsible for tpProcMutex.Unlock()
-		_ = dh.pOnuTP.waitForTpCompletion(cancel, &wg) //wait for background process to finish and collect their result
+		wg.Add(1) // for the 1 go routine to finish
+		go dh.pOnuTP.configureUniTp(dctx, uniData.PersUniID, uniData.PersTpPath, &wg)
+		dh.waitForCompletion(cancel, &wg) //wait for background process to finish
+
+		if err := dh.pOnuTP.getTpProcessingErrorIndication(); err != nil {
+			logger.Errorw(err.Error(), log.Fields{"device-id": dh.deviceID})
+		}
+	}
+}
+
+func (dh *deviceHandler) reconcileDeviceFlowConfig() {
+	logger.Debugw("reconciling - trigger flow config", log.Fields{"device-id": dh.deviceID})
+
+	pDevEntry := dh.getOnuDeviceEntry(true)
+	if pDevEntry == nil {
+		logger.Errorw("No valid OnuDevice - aborting", log.Fields{"device-id": dh.deviceID})
 		return
 	}
-	dh.pOnuTP.unlockTpProcMutex()
-	//TODO: reset of reconciling-flag has always to be done in the last ReconcileDevice*() function
+	for _, uniData := range pDevEntry.sOnuPersistentData.PersUniConfig {
+		var uniPort *onuUniPort
+		var exist bool
+		uniNo := mkUniPortNum(dh.pOnuIndication.GetIntfId(), dh.pOnuIndication.GetOnuId(), uint32(uniData.PersUniID))
+		if uniPort, exist = dh.uniEntityMap[uniNo]; !exist {
+			logger.Errorw("onuUniPort data not found!", log.Fields{"uniNo": uniNo, "deviceID": dh.deviceID})
+			return
+		}
+		for _, flowData := range uniData.PersFlowParams {
+			if _, exist = dh.UniVlanConfigFsmMap[uniData.PersUniID]; exist {
+				if err := dh.UniVlanConfigFsmMap[uniData.PersUniID].SetUniFlowParams(flowData.TpID, uint16(flowData.MatchVid),
+					uint16(flowData.SetVid), uint8(flowData.SetPcp)); err != nil {
+					logger.Errorw(err.Error(), log.Fields{"device-id": dh.deviceID})
+				}
+
+			} else {
+				if err := dh.createVlanFilterFsm(uniPort, flowData.TpID, uint16(flowData.MatchVid), uint16(flowData.SetVid),
+					uint8(flowData.SetPcp), OmciVlanFilterDone); err != nil {
+					logger.Errorw(err.Error(), log.Fields{"device-id": dh.deviceID})
+				}
+			}
+		}
+	}
+}
+
+func (dh *deviceHandler) reconcileMetrics() {
+	logger.Debugw("reconciling - trigger metrics - to be implemented in scope of VOL-3324!", log.Fields{"device-id": dh.deviceID})
+
+	//TODO: reset of reconciling-flag has always to be done in the last reconcile*() function
 	dh.reconciling = false
 }
 
 func (dh *deviceHandler) deleteDevice(device *voltha.Device) error {
 	logger.Debugw("delete-device", log.Fields{"device-id": device.Id, "SerialNumber": device.SerialNumber})
-	if err := dh.pOnuTP.deleteOnuTpPathKvStore(context.TODO()); err != nil {
-		return err
+
+	pDevEntry := dh.getOnuDeviceEntry(true)
+	if pDevEntry == nil {
+		logger.Errorw("No valid OnuDevice - aborting", log.Fields{"device-id": dh.deviceID})
+		return fmt.Errorf("no valid OnuDevice: %s", dh.deviceID)
 	}
+	pDevEntry.lockOnuKVStoreMutex()
+	defer pDevEntry.unlockOnuKVStoreMutex()
+
+	// deadline context to ensure completion of background routines waited for
+	//20200721: 10s proved to be less in 8*8 ONU test on local vbox machine with debug, might be further adapted
+	deadline := time.Now().Add(30 * time.Second) //allowed run time to finish before execution
+	dctx, cancel := context.WithDeadline(context.Background(), deadline)
+
+	pDevEntry.resetKvProcessingErrorIndication()
+
+	var wg sync.WaitGroup
+	wg.Add(1) // for the 1 go routine to finish
+	go pDevEntry.deleteDataFromOnuKvStore(dctx, &wg)
+	dh.waitForCompletion(cancel, &wg) //wait for background process to finish
+
 	// TODO: further actions - stop metrics and FSMs, remove device ...
-	return nil
+	return pDevEntry.getKvProcessingErrorIndication()
 }
 
 func (dh *deviceHandler) rebootDevice(device *voltha.Device) error {
 	logger.Debugw("reboot-device", log.Fields{"device-id": device.Id, "SerialNumber": device.SerialNumber})
 	if device.ConnectStatus != voltha.ConnectStatus_REACHABLE {
 		logger.Errorw("device-unreachable", log.Fields{"device-id": device.Id, "SerialNumber": device.SerialNumber})
-		return errors.New("device-unreachable")
+		return fmt.Errorf("device-unreachable: %s, %s", dh.deviceID, device.SerialNumber)
 	}
 	if err := dh.pOnuOmciDevice.reboot(context.TODO()); err != nil {
 		//TODO with VOL-3045/VOL-3046: return the error and stop further processing
@@ -879,7 +995,7 @@
 // ###################################################
 // deviceHandler utility methods ##### begin #########
 
-//getOnuDeviceEntry getsthe  ONU device entry and may wait until its value is defined
+//getOnuDeviceEntry gets the ONU device entry and may wait until its value is defined
 func (dh *deviceHandler) getOnuDeviceEntry(aWait bool) *OnuDeviceEntry {
 	dh.lockDevice.RLock()
 	pOnuDeviceEntry := dh.pOnuOmciDevice
@@ -977,7 +1093,7 @@
 		}
 	} else {
 		logger.Errorw("No valid OnuDevice -aborting", log.Fields{"device-id": dh.deviceID})
-		return errors.New("no valid OnuDevice")
+		return fmt.Errorf("no valid OnuDevice: %s", dh.deviceID)
 	}
 	if !dh.reconciling {
 		if err := dh.coreProxy.DeviceReasonUpdate(context.TODO(), dh.deviceID, "starting-openomci"); err != nil {
@@ -1091,7 +1207,7 @@
 		if pMibUlFsm.Is(ulStDisabled) {
 			if err := pMibUlFsm.Event(ulEvStart); err != nil {
 				logger.Errorw("MibSyncFsm: Can't go to state starting", log.Fields{"err": err})
-				return errors.New("can't go to state starting")
+				return fmt.Errorf("can't go to state starting: %s", dh.deviceID)
 			}
 			logger.Debugw("MibSyncFsm", log.Fields{"state": string(pMibUlFsm.Current())})
 			//Determine ONU status and start/re-start MIB Synchronization tasks
@@ -1099,12 +1215,12 @@
 			if true { //TODO: insert valid check
 				if err := pMibUlFsm.Event(ulEvResetMib); err != nil {
 					logger.Errorw("MibSyncFsm: Can't go to state resetting_mib", log.Fields{"err": err})
-					return errors.New("can't go to state resetting_mib")
+					return fmt.Errorf("can't go to state resetting_mib: %s", dh.deviceID)
 				}
 			} else {
 				if err := pMibUlFsm.Event(ulEvExamineMds); err != nil {
 					logger.Errorw("MibSyncFsm: Can't go to state examine_mds", log.Fields{"err": err})
-					return errors.New("can't go to examine_mds")
+					return fmt.Errorf("can't go to examine_mds: %s", dh.deviceID)
 				}
 				logger.Debugw("state of MibSyncFsm", log.Fields{"state": string(pMibUlFsm.Current())})
 				//Examine the MIB Data Sync
@@ -1115,11 +1231,11 @@
 			}
 		} else {
 			logger.Errorw("wrong state of MibSyncFsm - want: disabled", log.Fields{"have": string(pMibUlFsm.Current())})
-			return errors.New("wrong state of MibSyncFsm")
+			return fmt.Errorf("wrong state of MibSyncFsm: %s", dh.deviceID)
 		}
 	} else {
 		logger.Errorw("MibSyncFsm invalid - cannot be executed!!", log.Fields{"device-id": dh.deviceID})
-		return errors.New("cannot execut MibSync")
+		return fmt.Errorf("can't execute MibSync: %s", dh.deviceID)
 	}
 	return nil
 }
@@ -1132,7 +1248,7 @@
 		pDevEntry := dh.getOnuDeviceEntry(false)
 		if pDevEntry == nil {
 			logger.Errorw("No valid OnuDevice -aborting", log.Fields{"device-id": dh.deviceID})
-			return errors.New("no valid OnuDevice")
+			return fmt.Errorf("no valid OnuDevice: %s", dh.deviceID)
 		}
 
 		switch dh.deviceReason {
@@ -1361,7 +1477,7 @@
 		logger.Debugw("reconciling - don't notify core that onu went to active but trigger tech profile config",
 			log.Fields{"device-id": dh.deviceID})
 		go dh.reconcileDeviceTechProf()
-		//TODO: further actions e.g. restore flows, metrics, ...
+		// reconcilement will be continued after ani config is done
 	}
 }
 
@@ -1387,6 +1503,9 @@
 		//set internal state anyway - as it was done
 		dh.deviceReason = "tech-profile-config-download-success"
 	}
+	if dh.reconciling {
+		go dh.reconcileDeviceFlowConfig()
+	}
 }
 
 func (dh *deviceHandler) processOmciVlanFilterDoneEvent(devEvent OnuDeviceEvent) {
@@ -1399,15 +1518,24 @@
 	if dh.deviceReason != "omci-flows-pushed" {
 		// which may be the case from some previous actvity on another UNI Port of the ONU
 		// or even some previous flow add activity on the same port
-		if err := dh.coreProxy.DeviceReasonUpdate(context.TODO(), dh.deviceID, "omci-flows-pushed"); err != nil {
-			logger.Errorw("error-DeviceReasonUpdate to 'omci-flows-pushed'",
-				log.Fields{"device-id": dh.deviceID, "error": err})
+		if !dh.reconciling {
+			if err := dh.coreProxy.DeviceReasonUpdate(context.TODO(), dh.deviceID, "omci-flows-pushed"); err != nil {
+				logger.Errorw("error-DeviceReasonUpdate to 'omci-flows-pushed'",
+					log.Fields{"device-id": dh.deviceID, "error": err})
+			} else {
+				logger.Infow("updated dev reason to ''omci-flows-pushed'",
+					log.Fields{"device-id": dh.deviceID})
+			}
 		} else {
-			logger.Infow("updated dev reason to ''omci-flows-pushed'",
+			logger.Debugw("reconciling - don't notify core about DeviceReasonUpdate to omci-flows-pushed",
 				log.Fields{"device-id": dh.deviceID})
 		}
 		//set internal state anyway - as it was done
 		dh.deviceReason = "omci-flows-pushed"
+
+		if dh.reconciling {
+			go dh.reconcileMetrics()
+		}
 	}
 }
 
@@ -1784,7 +1912,7 @@
 	if metadata == 0 {
 		logger.Debugw("FlowAdd invalid metadata - abort",
 			log.Fields{"device-id": dh.deviceID})
-		return errors.New("flowAdd invalid metadata")
+		return fmt.Errorf("flowAdd invalid metadata: %s", dh.deviceID)
 	}
 	loTpID := flow.GetTechProfileIDFromWriteMetaData(metadata)
 	logger.Debugw("FlowAdd TechProfileId", log.Fields{"device-id": dh.deviceID, "TP-Id": loTpID})
@@ -1807,7 +1935,7 @@
 			"match_vid": strconv.FormatInt(int64(loMatchVlan), 16)})
 		//TODO!!: Use DeviceId within the error response to rwCore
 		//  likewise also in other error response cases to calling components as requested in [VOL-3458]
-		return errors.New("flowAdd Set/Match VlanId inconsistent")
+		return fmt.Errorf("flowAdd Set/Match VlanId inconsistent: %s", dh.deviceID)
 	}
 	if loSetVlan == uint16(of.OfpVlanId_OFPVID_NONE) && loMatchVlan == uint16(of.OfpVlanId_OFPVID_PRESENT) {
 		logger.Debugw("FlowAdd vlan-any/copy", log.Fields{"device-id": dh.deviceID})
@@ -1821,7 +1949,7 @@
 		logger.Debugw("FlowAdd vlan-set", log.Fields{"device-id": dh.deviceID})
 	}
 	if _, exist := dh.UniVlanConfigFsmMap[apUniPort.uniID]; exist {
-		return dh.UniVlanConfigFsmMap[apUniPort.uniID].SetUniFlowParams(loMatchVlan, loSetVlan, loSetPcp)
+		return dh.UniVlanConfigFsmMap[apUniPort.uniID].SetUniFlowParams(loTpID, loMatchVlan, loSetVlan, loSetPcp)
 	}
 	return dh.createVlanFilterFsm(apUniPort,
 		loTpID, loMatchVlan, loSetVlan, loSetPcp, OmciVlanFilterDone)
@@ -1910,3 +2038,59 @@
 	//save to do, even if entry dows not exist
 	delete(dh.UniVlanConfigFsmMap, apUniPort.uniID)
 }
+
+//storePersUniFlowConfig updates local storage of OnuUniFlowConfig and writes it into kv-store afterwards to have it
+//available for potential reconcilement
+
+func (dh *deviceHandler) storePersUniFlowConfig(aUniID uint8, aUniVlanFlowParams *[]uniVlanFlowParams) error {
+
+	if dh.reconciling {
+		logger.Debugw("reconciling - don't store persistent UniFlowConfig", log.Fields{"device-id": dh.deviceID})
+		return nil
+	}
+	logger.Debugw("Store persistent UniFlowConfig", log.Fields{"device-id": dh.deviceID})
+
+	pDevEntry := dh.getOnuDeviceEntry(true)
+	if pDevEntry == nil {
+		logger.Errorw("No valid OnuDevice - aborting", log.Fields{"device-id": dh.deviceID})
+		return fmt.Errorf("no valid OnuDevice: %s", dh.deviceID)
+	}
+	pDevEntry.updateOnuUniFlowConfig(aUniID, aUniVlanFlowParams)
+
+	pDevEntry.lockOnuKVStoreMutex()
+	defer pDevEntry.unlockOnuKVStoreMutex()
+
+	// deadline context to ensure completion of background routines waited for
+	//20200721: 10s proved to be less in 8*8 ONU test on local vbox machine with debug, might be further adapted
+	deadline := time.Now().Add(30 * time.Second) //allowed run time to finish before execution
+	dctx, cancel := context.WithDeadline(context.Background(), deadline)
+
+	pDevEntry.resetKvProcessingErrorIndication()
+	var wg sync.WaitGroup
+	wg.Add(1) // for the 1 go routine to finish
+
+	go pDevEntry.updateOnuKvStore(dctx, &wg)
+	dh.waitForCompletion(cancel, &wg) //wait for background process to finish
+
+	return pDevEntry.getKvProcessingErrorIndication()
+}
+
+func (dh *deviceHandler) waitForCompletion(cancel context.CancelFunc, wg *sync.WaitGroup) {
+	defer cancel() //ensure termination of context (may be pro forma)
+	wg.Wait()
+	logger.Debug("WaitGroup processing completed")
+
+}
+
+func (dh *deviceHandler) combineErrorStrings(errS ...error) error {
+	var errStr string = ""
+	for _, err := range errS {
+		if err != nil {
+			errStr = errStr + err.Error() + " "
+		}
+	}
+	if errStr != "" {
+		return fmt.Errorf("%s: %s", errStr, dh.deviceID)
+	}
+	return nil
+}
diff --git a/internal/pkg/onuadaptercore/omci_vlan_config.go b/internal/pkg/onuadaptercore/omci_vlan_config.go
index a414186..34eeef6 100644
--- a/internal/pkg/onuadaptercore/omci_vlan_config.go
+++ b/internal/pkg/onuadaptercore/omci_vlan_config.go
@@ -105,15 +105,6 @@
 	vlanStResetting       = "vlanStResetting"
 )
 
-type uniVlanFlowParameter struct {
-	//use uint32 types for allowing immediate bitshifting
-	matchVid     uint32
-	matchPcp     uint32
-	tagsToRemove uint32
-	setVid       uint32
-	setPcp       uint32
-}
-
 //UniVlanConfigFsm defines the structure for the state machine to config the PON ANI ports of ONU UNI ports via OMCI
 type UniVlanConfigFsm struct {
 	pDeviceHandler              *deviceHandler
@@ -127,7 +118,7 @@
 	pAdaptFsm                   *AdapterFsm
 	acceptIncrementalEvtoOption bool
 	mutexFlowParams             sync.Mutex
-	uniFlowParamsSlice          []uniVlanFlowParameter
+	uniFlowParamsSlice          []uniVlanFlowParams
 	numUniFlows                 uint8 // expected number of flows should be less than 12
 	configuredUniFlow           uint8
 	numVlanFilterEntries        uint8
@@ -205,7 +196,7 @@
 		return nil
 	}
 
-	_ = instFsm.SetUniFlowParams(aMatchVlan, aSetVlan, aSetPcp)
+	_ = instFsm.SetUniFlowParams(aTechProfileID, aMatchVlan, aSetVlan, aSetPcp)
 
 	logger.Infow("UniVlanConfigFsm created", log.Fields{"device-id": aDeviceID,
 		"accIncrEvto": instFsm.acceptIncrementalEvtoOption})
@@ -214,37 +205,38 @@
 
 //SetUniFlowParams verifies on existence of flow parameters to be configured
 // and appends a new flow if there is space
-func (oFsm *UniVlanConfigFsm) SetUniFlowParams(aMatchVlan uint16, aSetVlan uint16, aSetPcp uint8) error {
-	loFlowParams := uniVlanFlowParameter{
-		matchVid: uint32(aMatchVlan),
-		setVid:   uint32(aSetVlan),
-		setPcp:   uint32(aSetPcp),
+func (oFsm *UniVlanConfigFsm) SetUniFlowParams(aTpID uint16, aMatchVlan uint16, aSetVlan uint16, aSetPcp uint8) error {
+	loFlowParams := uniVlanFlowParams{
+		TpID:     aTpID,
+		MatchVid: uint32(aMatchVlan),
+		SetVid:   uint32(aSetVlan),
+		SetPcp:   uint32(aSetPcp),
 	}
 	// some automatic adjustments on the filter/treat parameters as not specifically configured/ensured by flow configuration parameters
-	loFlowParams.tagsToRemove = 1            //one tag to remove as default setting
-	loFlowParams.matchPcp = cPrioDoNotFilter // do not Filter on prio as default
+	loFlowParams.TagsToRemove = 1            //one tag to remove as default setting
+	loFlowParams.MatchPcp = cPrioDoNotFilter // do not Filter on prio as default
 
-	if loFlowParams.setVid == uint32(of.OfpVlanId_OFPVID_PRESENT) {
+	if loFlowParams.SetVid == uint32(of.OfpVlanId_OFPVID_PRESENT) {
 		//then matchVlan is don't care and should be overwritten to 'transparent' here to avoid unneeded multiple flow entries
-		loFlowParams.matchVid = uint32(of.OfpVlanId_OFPVID_PRESENT)
+		loFlowParams.MatchVid = uint32(of.OfpVlanId_OFPVID_PRESENT)
 		//TODO!!: maybe be needed to be re-checked at flow deletion (but assume all flows are always deleted togehther)
 	} else {
 		if !oFsm.acceptIncrementalEvtoOption {
 			//then matchVlan is don't care and should be overwritten to 'transparent' here to avoid unneeded multiple flow entries
-			loFlowParams.matchVid = uint32(of.OfpVlanId_OFPVID_PRESENT)
+			loFlowParams.MatchVid = uint32(of.OfpVlanId_OFPVID_PRESENT)
 		}
 	}
 
-	if loFlowParams.matchVid == uint32(of.OfpVlanId_OFPVID_PRESENT) {
+	if loFlowParams.MatchVid == uint32(of.OfpVlanId_OFPVID_PRESENT) {
 		// no prio/vid filtering requested
-		loFlowParams.tagsToRemove = 0          //no tag pop action
-		loFlowParams.matchPcp = cPrioIgnoreTag // no vlan tag filtering
-		if loFlowParams.setPcp == cCopyPrioFromInner {
+		loFlowParams.TagsToRemove = 0          //no tag pop action
+		loFlowParams.MatchPcp = cPrioIgnoreTag // no vlan tag filtering
+		if loFlowParams.SetPcp == cCopyPrioFromInner {
 			//in case of no filtering and configured PrioCopy ensure default prio setting to 0
 			// which is required for stacking of untagged, but obviously also ensures prio setting for prio/singletagged
 			// might collide with NoMatchVid/CopyPrio(/setVid) setting
 			// this was some precondition setting taken over from py adapter ..
-			loFlowParams.setPcp = 0
+			loFlowParams.SetPcp = 0
 		}
 	}
 	flowEntryMatch := false
@@ -265,10 +257,18 @@
 			oFsm.uniFlowParamsSlice = append(oFsm.uniFlowParamsSlice, loFlowParams)
 			oFsm.numUniFlows++
 			logger.Debugw("UniVlanConfigFsm flow added", log.Fields{
-				"matchVid": strconv.FormatInt(int64(loFlowParams.matchVid), 16),
-				"setVid":   strconv.FormatInt(int64(loFlowParams.setVid), 16),
-				"setPcp":   loFlowParams.setPcp, "numberofFlows": oFsm.numUniFlows,
+				"MatchVid": strconv.FormatInt(int64(loFlowParams.MatchVid), 16),
+				"SetVid":   strconv.FormatInt(int64(loFlowParams.SetVid), 16),
+				"SetPcp":   loFlowParams.SetPcp, "numberofFlows": oFsm.numUniFlows,
 				"device-id": oFsm.pAdaptFsm.deviceID})
+
+			//permanently store flow config for reconcile case
+
+			if err := oFsm.pDeviceHandler.storePersUniFlowConfig(oFsm.pOnuUniPort.uniID, &oFsm.uniFlowParamsSlice); err != nil {
+				logger.Errorw(err.Error(), log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
+				return err
+			}
+
 			pConfigVlanStateBaseFsm := oFsm.pAdaptFsm.pFsm
 			if pConfigVlanStateBaseFsm.Is(vlanStConfigDone) {
 				//have to re-trigger the FSM to proceed with outstanding incremental flow configuration
@@ -321,7 +321,7 @@
 func (oFsm *UniVlanConfigFsm) enterConfigVtfd(e *fsm.Event) {
 	//mutex protection is required for possible concurrent access to FSM members
 	oFsm.mutexFlowParams.Lock()
-	if oFsm.uniFlowParamsSlice[0].setVid == uint32(of.OfpVlanId_OFPVID_PRESENT) {
+	if oFsm.uniFlowParamsSlice[0].SetVid == uint32(of.OfpVlanId_OFPVID_PRESENT) {
 		// meaning transparent setup - no specific VTFD setting required
 		oFsm.mutexFlowParams.Unlock()
 		logger.Debugw("UniVlanConfigFsm: no VTFD config required", log.Fields{
@@ -336,7 +336,7 @@
 		logger.Debugw("UniVlanConfigFsm create VTFD", log.Fields{
 			"EntitytId": strconv.FormatInt(int64(oFsm.vtfdID), 16),
 			"in state":  e.FSM.Current(), "device-id": oFsm.pAdaptFsm.deviceID})
-		oFsm.vlanFilterList[0] = uint16(oFsm.uniFlowParamsSlice[0].setVid) // setVid is assumed to be masked already by the caller to 12 bit
+		oFsm.vlanFilterList[0] = uint16(oFsm.uniFlowParamsSlice[0].SetVid) // setVid is assumed to be masked already by the caller to 12 bit
 		oFsm.mutexFlowParams.Unlock()
 		vtfdFilterList := make([]uint16, 12) //needed for parameter serialization
 		vtfdFilterList[0] = oFsm.vlanFilterList[0]
@@ -397,7 +397,7 @@
 		"device-id": oFsm.pAdaptFsm.deviceID})
 	oFsm.mutexFlowParams.Lock()
 
-	if oFsm.uniFlowParamsSlice[oFsm.configuredUniFlow].setVid == uint32(of.OfpVlanId_OFPVID_PRESENT) {
+	if oFsm.uniFlowParamsSlice[oFsm.configuredUniFlow].SetVid == uint32(of.OfpVlanId_OFPVID_PRESENT) {
 		// meaning transparent setup - no specific VTFD setting required
 		oFsm.mutexFlowParams.Unlock()
 		logger.Debugw("UniVlanConfigFsm: no VTFD config required", log.Fields{
@@ -408,7 +408,7 @@
 			logger.Debugw("UniVlanConfigFsm create VTFD", log.Fields{
 				"EntitytId": strconv.FormatInt(int64(oFsm.vtfdID), 16),
 				"in state":  e.FSM.Current(), "device-id": oFsm.pAdaptFsm.deviceID})
-			oFsm.vlanFilterList[0] = uint16(oFsm.uniFlowParamsSlice[oFsm.configuredUniFlow].setVid) // setVid is assumed to be masked already by the caller to 12 bit
+			oFsm.vlanFilterList[0] = uint16(oFsm.uniFlowParamsSlice[oFsm.configuredUniFlow].SetVid) // setVid is assumed to be masked already by the caller to 12 bit
 			oFsm.mutexFlowParams.Unlock()
 			vtfdFilterList := make([]uint16, 12) //needed for parameter serialization
 			vtfdFilterList[0] = oFsm.vlanFilterList[0]
@@ -437,7 +437,7 @@
 				"in state":  e.FSM.Current(), "device-id": oFsm.pAdaptFsm.deviceID})
 			// setVid is assumed to be masked already by the caller to 12 bit
 			oFsm.vlanFilterList[oFsm.numVlanFilterEntries] =
-				uint16(oFsm.uniFlowParamsSlice[oFsm.configuredUniFlow].setVid)
+				uint16(oFsm.uniFlowParamsSlice[oFsm.configuredUniFlow].SetVid)
 			oFsm.mutexFlowParams.Unlock()
 			vtfdFilterList := make([]uint16, 12) //needed for parameter serialization
 			for i := uint8(0); i <= oFsm.numVlanFilterEntries; i++ {
@@ -679,7 +679,7 @@
 	} //first flow element
 
 	oFsm.mutexFlowParams.Lock()
-	if oFsm.uniFlowParamsSlice[aFlowEntryNo].setVid == uint32(of.OfpVlanId_OFPVID_PRESENT) {
+	if oFsm.uniFlowParamsSlice[aFlowEntryNo].SetVid == uint32(of.OfpVlanId_OFPVID_PRESENT) {
 		//transparent transmission required
 		oFsm.mutexFlowParams.Unlock()
 		logger.Debugw("UniVlanConfigFsm Tx Set::EVTOCD single tagged transparent rule", log.Fields{
@@ -742,20 +742,20 @@
 					cDoNotFilterTPID<<cFilterTpidOffset) // Do not filter on outer TPID field
 
 			binary.BigEndian.PutUint32(sliceEvtocdRule[cFilterInnerOffset:],
-				oFsm.uniFlowParamsSlice[aFlowEntryNo].matchPcp<<cFilterPrioOffset| // either DNFonPrio or ignore tag (default) on innerVLAN
-					oFsm.uniFlowParamsSlice[aFlowEntryNo].matchVid<<cFilterVidOffset| // either DNFonVid or real filter VID
+				oFsm.uniFlowParamsSlice[aFlowEntryNo].MatchPcp<<cFilterPrioOffset| // either DNFonPrio or ignore tag (default) on innerVLAN
+					oFsm.uniFlowParamsSlice[aFlowEntryNo].MatchVid<<cFilterVidOffset| // either DNFonVid or real filter VID
 					cDoNotFilterTPID<<cFilterTpidOffset| // Do not filter on inner TPID field
 					cDoNotFilterEtherType<<cFilterEtherTypeOffset) // Do not filter of EtherType
 
 			binary.BigEndian.PutUint32(sliceEvtocdRule[cTreatOuterOffset:],
-				oFsm.uniFlowParamsSlice[aFlowEntryNo].tagsToRemove<<cTreatTTROffset| // either 1 or 0
+				oFsm.uniFlowParamsSlice[aFlowEntryNo].TagsToRemove<<cTreatTTROffset| // either 1 or 0
 					cDoNotAddPrio<<cTreatPrioOffset| // do not add outer tag
 					cDontCareVid<<cTreatVidOffset| // Outer VID don't care
 					cDontCareTpid<<cTreatTpidOffset) // Outer TPID field don't care
 
 			binary.BigEndian.PutUint32(sliceEvtocdRule[cTreatInnerOffset:],
-				oFsm.uniFlowParamsSlice[aFlowEntryNo].setPcp<<cTreatPrioOffset| // as configured in flow
-					oFsm.uniFlowParamsSlice[aFlowEntryNo].setVid<<cTreatVidOffset| //as configured in flow
+				oFsm.uniFlowParamsSlice[aFlowEntryNo].SetPcp<<cTreatPrioOffset| // as configured in flow
+					oFsm.uniFlowParamsSlice[aFlowEntryNo].SetVid<<cTreatVidOffset| //as configured in flow
 					cSetOutputTpidCopyDei<<cTreatTpidOffset) // Set TPID = 0x8100
 			oFsm.mutexFlowParams.Unlock()
 
@@ -807,7 +807,7 @@
 				binary.BigEndian.PutUint32(sliceEvtocdRule[cTreatInnerOffset:],
 					0<<cTreatPrioOffset| // vlan prio set to 0
 						//   (as done in Py code, maybe better option would be setPcp here, which still could be 0?)
-						oFsm.uniFlowParamsSlice[aFlowEntryNo].setVid<<cTreatVidOffset| // Outer VID don't care
+						oFsm.uniFlowParamsSlice[aFlowEntryNo].SetVid<<cTreatVidOffset| // Outer VID don't care
 						cSetOutputTpidCopyDei<<cTreatTpidOffset) // Set TPID = 0x8100
 
 				oFsm.mutexFlowParams.Unlock()
@@ -859,7 +859,7 @@
 				binary.BigEndian.PutUint32(sliceEvtocdRule[cTreatInnerOffset:],
 					cCopyPrioFromInner<<cTreatPrioOffset| // vlan copy from PrioTag
 						//   (as done in Py code, maybe better option would be setPcp here, which still could be PrioCopy?)
-						oFsm.uniFlowParamsSlice[aFlowEntryNo].setVid<<cTreatVidOffset| // Outer VID as configured
+						oFsm.uniFlowParamsSlice[aFlowEntryNo].SetVid<<cTreatVidOffset| // Outer VID as configured
 						cSetOutputTpidCopyDei<<cTreatTpidOffset) // Set TPID = 0x8100
 				oFsm.mutexFlowParams.Unlock()
 
diff --git a/internal/pkg/onuadaptercore/onu_device_entry.go b/internal/pkg/onuadaptercore/onu_device_entry.go
index 382984c..8c1d097 100644
--- a/internal/pkg/onuadaptercore/onu_device_entry.go
+++ b/internal/pkg/onuadaptercore/onu_device_entry.go
@@ -19,7 +19,10 @@
 
 import (
 	"context"
+	"encoding/json"
 	"errors"
+	"fmt"
+	"sync"
 	"time"
 
 	"github.com/opencord/omci-lib-go"
@@ -31,6 +34,7 @@
 	"github.com/looplab/fsm"
 	"github.com/opencord/voltha-lib-go/v3/pkg/adapters/adapterif"
 	"github.com/opencord/voltha-lib-go/v3/pkg/db"
+	"github.com/opencord/voltha-lib-go/v3/pkg/db/kvstore"
 
 	//"github.com/opencord/voltha-lib-go/v3/pkg/kafka"
 	"github.com/opencord/voltha-lib-go/v3/pkg/log"
@@ -104,6 +108,7 @@
 const (
 	cBasePathMibTemplateKvStore = "service/voltha/omci_mibs/go_templates"
 	cSuffixMibTemplateKvStore   = "%s/%s/%s"
+	cBasePathOnuKVStore         = "service/voltha/openonu"
 )
 
 // OnuDeviceEvent - event of interest to Device Adapters and OpenOMCI State Machines
@@ -188,22 +193,52 @@
 	isActive uint8
 }
 
+type uniVlanFlowParams struct {
+	TpID         uint16 `json:"tp_id"`
+	MatchVid     uint32 `json:"match_vid"` //use uint32 types for allowing immediate bitshifting
+	MatchPcp     uint32 `json:"match_pcp"`
+	TagsToRemove uint32 `json:"tags_to_revome"`
+	SetVid       uint32 `json:"set_vid"`
+	SetPcp       uint32 `json:"set_pcp"`
+}
+
+type uniPersConfig struct {
+	PersUniID      uint8               `json:"uni_id"`
+	PersTpPath     string              `json:"tp_path"`
+	PersFlowParams []uniVlanFlowParams `json:"flow_params"`
+}
+
+type onuPersistentData struct {
+	PersOnuID      uint32          `json:"onu_id"`
+	PersIntfID     uint32          `json:"intf_id"`
+	PersSnr        string          `json:"serial_number"`
+	PersAdminState string          `json:"admin_state"`
+	PersOperState  string          `json:"oper_state"`
+	PersUniConfig  []uniPersConfig `json:"uni_config"`
+}
+
 // OnuDeviceEntry - ONU device info and FSM events.
 type OnuDeviceEntry struct {
-	deviceID           string
-	baseDeviceHandler  *deviceHandler
-	coreProxy          adapterif.CoreProxy
-	adapterProxy       adapterif.AdapterProxy
-	started            bool
-	PDevOmciCC         *omciCC
-	pOnuDB             *onuDeviceDB
-	mibTemplateKVStore *db.Backend
-	vendorID           string
-	serialNumber       string
-	equipmentID        string
-	swImages           [secondSwImageMeID + 1]swImages
-	activeSwVersion    string
-	macAddress         string
+	deviceID              string
+	baseDeviceHandler     *deviceHandler
+	coreProxy             adapterif.CoreProxy
+	adapterProxy          adapterif.AdapterProxy
+	started               bool
+	PDevOmciCC            *omciCC
+	pOnuDB                *onuDeviceDB
+	mibTemplateKVStore    *db.Backend
+	sOnuPersistentData    onuPersistentData
+	onuKVStoreMutex       sync.RWMutex
+	onuKVStore            *db.Backend
+	onuKVStorePath        string
+	onuKVStoreprocResult  error //error indication of processing
+	chOnuKvProcessingStep chan uint8
+	vendorID              string
+	serialNumber          string
+	equipmentID           string
+	swImages              [secondSwImageMeID + 1]swImages
+	activeSwVersion       string
+	macAddress            string
 	//lockDeviceEntries           sync.RWMutex
 	mibDbClass    func() error
 	supportedFsms OmciDeviceFsms
@@ -235,6 +270,8 @@
 	onuDeviceEntry.coreProxy = coreProxy
 	onuDeviceEntry.adapterProxy = adapterProxy
 	onuDeviceEntry.devState = DeviceStatusInit
+	onuDeviceEntry.sOnuPersistentData.PersUniConfig = make([]uniPersConfig, 0)
+	onuDeviceEntry.chOnuKvProcessingStep = make(chan uint8)
 	onuDeviceEntry.omciRebootMessageReceivedChannel = make(chan Message, 2048)
 	//openomciagent.lockDeviceHandlersMap = sync.RWMutex{}
 	//OMCI related databases are on a per-agent basis. State machines and tasks
@@ -379,7 +416,15 @@
 
 	onuDeviceEntry.mibTemplateKVStore = onuDeviceEntry.baseDeviceHandler.setBackend(cBasePathMibTemplateKvStore)
 	if onuDeviceEntry.mibTemplateKVStore == nil {
-		logger.Errorw("Failed to setup mibTemplateKVStore", log.Fields{"device-id": deviceID})
+		logger.Errorw("Can't access mibTemplateKVStore - no backend connection to service",
+			log.Fields{"device-id": deviceID, "service": cBasePathMibTemplateKvStore})
+	}
+
+	onuDeviceEntry.onuKVStorePath = onuDeviceEntry.deviceID
+	onuDeviceEntry.onuKVStore = onuDeviceEntry.baseDeviceHandler.setBackend(cBasePathOnuKVStore)
+	if onuDeviceEntry.onuKVStore == nil {
+		logger.Errorw("Can't access onuKVStore - no backend connection to service",
+			log.Fields{"device-id": deviceID, "service": cBasePathOnuKVStore})
 	}
 
 	// Alarm Synchronization Database
@@ -479,3 +524,217 @@
 		logger.Warnw("device-event not yet handled", log.Fields{"state": devEvent})
 	}
 }
+
+func (oo *OnuDeviceEntry) restoreDataFromOnuKvStore(ctx context.Context) error {
+	if oo.onuKVStore == nil {
+		logger.Debugw("onuKVStore not set - abort", log.Fields{"device-id": oo.deviceID})
+		return fmt.Errorf(fmt.Sprintf("onuKVStore-not-set-abort-%s", oo.deviceID))
+	}
+	oo.sOnuPersistentData = onuPersistentData{0, 0, "", "", "", make([]uniPersConfig, 0)}
+	Value, err := oo.onuKVStore.Get(ctx, oo.onuKVStorePath)
+	if err == nil {
+		if Value != nil {
+			logger.Debugw("ONU-data read",
+				log.Fields{"Key": Value.Key, "device-id": oo.deviceID})
+			tmpBytes, _ := kvstore.ToByte(Value.Value)
+
+			if err = json.Unmarshal(tmpBytes, &oo.sOnuPersistentData); err != nil {
+				logger.Errorw("unable to unmarshal ONU-data", log.Fields{"error": err, "device-id": oo.deviceID})
+				return fmt.Errorf(fmt.Sprintf("unable-to-unmarshal-ONU-data-%s", oo.deviceID))
+			}
+			logger.Debugw("ONU-data", log.Fields{"sOnuPersistentData": oo.sOnuPersistentData,
+				"device-id": oo.deviceID})
+		} else {
+			logger.Errorw("no ONU-data found", log.Fields{"path": oo.onuKVStorePath, "device-id": oo.deviceID})
+			return fmt.Errorf(fmt.Sprintf("no-ONU-data-found-%s", oo.deviceID))
+		}
+	} else {
+		logger.Errorw("unable to read from KVstore", log.Fields{"device-id": oo.deviceID})
+		return fmt.Errorf(fmt.Sprintf("unable-to-read-from-KVstore-%s", oo.deviceID))
+	}
+	return nil
+}
+
+func (oo *OnuDeviceEntry) deleteDataFromOnuKvStore(ctx context.Context, wg *sync.WaitGroup) {
+	defer wg.Done()
+
+	if oo.onuKVStore == nil {
+		logger.Debugw("onuKVStore not set - abort", log.Fields{"device-id": oo.deviceID})
+		oo.onuKVStoreprocResult = errors.New("onu-data delete aborted: onuKVStore not set")
+		return
+	}
+	var processingStep uint8 = 1 // used to synchronize the different processing steps with chOnuKvProcessingStep
+	go oo.deletePersistentData(ctx, processingStep)
+	if !oo.waitForTimeoutOrCompletion(ctx, oo.chOnuKvProcessingStep, processingStep) {
+		//timeout or error detected
+		logger.Debugw("ONU-data not deleted - abort", log.Fields{"device-id": oo.deviceID})
+		oo.onuKVStoreprocResult = errors.New("onu-data delete aborted: during kv-access")
+		return
+	}
+}
+
+func (oo *OnuDeviceEntry) deletePersistentData(ctx context.Context, aProcessingStep uint8) {
+
+	logger.Debugw("delete ONU-data from KVStore", log.Fields{"device-id": oo.deviceID})
+	err := oo.onuKVStore.Delete(ctx, oo.onuKVStorePath)
+	if err != nil {
+		logger.Errorw("unable to delete in KVstore", log.Fields{"device-id": oo.deviceID, "err": err})
+		oo.chOnuKvProcessingStep <- 0 //error indication
+		return
+	}
+	oo.chOnuKvProcessingStep <- aProcessingStep //done
+}
+
+func (oo *OnuDeviceEntry) updateOnuKvStore(ctx context.Context, wg *sync.WaitGroup) {
+	defer wg.Done()
+
+	if oo.onuKVStore == nil {
+		logger.Debugw("onuKVStore not set - abort", log.Fields{"device-id": oo.deviceID})
+		oo.onuKVStoreprocResult = errors.New("onu-data update aborted: onuKVStore not set")
+		return
+	}
+	var processingStep uint8 = 1 // used to synchronize the different processing steps with chOnuKvProcessingStep
+	go oo.storeDataInOnuKvStore(ctx, processingStep)
+	if !oo.waitForTimeoutOrCompletion(ctx, oo.chOnuKvProcessingStep, processingStep) {
+		//timeout or error detected
+		logger.Debugw("ONU-data not written - abort", log.Fields{"device-id": oo.deviceID})
+		oo.onuKVStoreprocResult = errors.New("onu-data update aborted: during writing process")
+		return
+	}
+}
+
+func (oo *OnuDeviceEntry) storeDataInOnuKvStore(ctx context.Context, aProcessingStep uint8) {
+
+	//assign values which are not already present when newOnuDeviceEntry() is called
+	oo.sOnuPersistentData.PersOnuID = oo.baseDeviceHandler.pOnuIndication.OnuId
+	oo.sOnuPersistentData.PersIntfID = oo.baseDeviceHandler.pOnuIndication.IntfId
+	oo.sOnuPersistentData.PersSnr = oo.baseDeviceHandler.pOnuOmciDevice.serialNumber
+	//TODO: verify usage of these values during restart UC
+	oo.sOnuPersistentData.PersAdminState = "up"
+	oo.sOnuPersistentData.PersOperState = "active"
+
+	logger.Debugw("Update ONU-data in KVStore", log.Fields{"device-id": oo.deviceID, "sOnuPersistentData": oo.sOnuPersistentData})
+
+	Value, err := json.Marshal(oo.sOnuPersistentData)
+	if err != nil {
+		logger.Errorw("unable to marshal ONU-data", log.Fields{"sOnuPersistentData": oo.sOnuPersistentData,
+			"device-id": oo.deviceID, "err": err})
+		oo.chOnuKvProcessingStep <- 0 //error indication
+		return
+	}
+	err = oo.onuKVStore.Put(ctx, oo.onuKVStorePath, Value)
+	if err != nil {
+		logger.Errorw("unable to write ONU-data into KVstore", log.Fields{"device-id": oo.deviceID, "err": err})
+		oo.chOnuKvProcessingStep <- 0 //error indication
+		return
+	}
+	oo.chOnuKvProcessingStep <- aProcessingStep //done
+}
+
+func (oo *OnuDeviceEntry) updateOnuUniTpPath(aUniID uint8, aPathString string) bool {
+	/* within some specific InterAdapter processing request write/read access to data is ensured to be sequentially,
+	   as also the complete sequence is ensured to 'run to completion' before some new request is accepted
+	   no specific concurrency protection to sOnuPersistentData is required here
+	*/
+	for k, v := range oo.sOnuPersistentData.PersUniConfig {
+		if v.PersUniID == aUniID {
+			logger.Debugw("PersUniConfig-entry already exists", log.Fields{"device-id": oo.deviceID, "uniID": aUniID})
+			existingPath := oo.sOnuPersistentData.PersUniConfig[k].PersTpPath
+			if existingPath != aPathString {
+				if aPathString == "" {
+					//existing entry to be deleted
+					logger.Debugw("UniTp delete path value", log.Fields{"device-id": oo.deviceID, "uniID": aUniID, "path": aPathString})
+					oo.sOnuPersistentData.PersUniConfig[k].PersTpPath = ""
+				} else {
+					//existing entry to be modified
+					logger.Debugw("UniTp modify path value", log.Fields{"device-id": oo.deviceID, "uniID": aUniID, "path": aPathString})
+					oo.sOnuPersistentData.PersUniConfig[k].PersTpPath = aPathString
+				}
+				return true
+			}
+			//entry already exists
+			logger.Debugw("UniTp path already exists", log.Fields{"device-id": oo.deviceID, "uniID": aUniID, "path": aPathString})
+			return false
+		}
+	}
+	//no entry exists for uniId
+
+	if aPathString == "" {
+		//delete request in non-existing state , accept as no change
+		logger.Debugw("UniTp path already removed", log.Fields{"device-id": oo.deviceID, "uniID": aUniID})
+		return false
+	}
+	//new entry to be created
+	logger.Debugw("New UniTp path set", log.Fields{"device-id": oo.deviceID, "uniID": aUniID, "path": aPathString})
+	oo.sOnuPersistentData.PersUniConfig =
+		append(oo.sOnuPersistentData.PersUniConfig, uniPersConfig{PersUniID: aUniID, PersTpPath: aPathString, PersFlowParams: make([]uniVlanFlowParams, 0)})
+	return true
+}
+
+// deleteTpResource removes Resources from the ONU's specified Uni
+func (oo *OnuDeviceEntry) deleteTpResource(ctx context.Context,
+	aUniID uint8, aPathString string, aResource resourceEntry, aEntryID uint32,
+	wg *sync.WaitGroup) {
+	defer wg.Done()
+	logger.Debugw("this would remove TP resources from ONU's UNI", log.Fields{
+		"device-id": oo.deviceID, "uniID": aUniID, "path": aPathString, "Resource": aResource})
+	//TODO!!!
+	//delete the given resource from ONU OMCI config and data base - as background routine
+	/*
+		var processingStep uint8 = 1 // used to synchronize the different processing steps with chTpConfigProcessingStep
+		go onuTp.deleteAniResource(ctx, processingStep)
+		if !onuTP.waitForTimeoutOrCompletion(ctx, chTpConfigProcessingStep, processingStep) {
+			//timeout or error detected
+			return
+		}
+	*/
+}
+
+func (oo *OnuDeviceEntry) updateOnuUniFlowConfig(aUniID uint8, aUniVlanFlowParams *[]uniVlanFlowParams) {
+
+	for k, v := range oo.sOnuPersistentData.PersUniConfig {
+		if v.PersUniID == aUniID {
+			oo.sOnuPersistentData.PersUniConfig[k].PersFlowParams = make([]uniVlanFlowParams, len(*aUniVlanFlowParams))
+			copy(oo.sOnuPersistentData.PersUniConfig[k].PersFlowParams, *aUniVlanFlowParams)
+			return
+		}
+	}
+	//flow update was faster than tp-config - create PersUniConfig-entry
+	tmpConfig := uniPersConfig{PersUniID: aUniID, PersTpPath: "", PersFlowParams: make([]uniVlanFlowParams, len(*aUniVlanFlowParams))}
+	copy(tmpConfig.PersFlowParams, *aUniVlanFlowParams)
+	oo.sOnuPersistentData.PersUniConfig = append(oo.sOnuPersistentData.PersUniConfig, tmpConfig)
+}
+
+func (oo *OnuDeviceEntry) waitForTimeoutOrCompletion(
+	ctx context.Context, aChOnuProcessingStep <-chan uint8, aProcessingStep uint8) bool {
+	select {
+	case <-ctx.Done():
+		logger.Warnw("processing not completed in-time!",
+			log.Fields{"device-id": oo.deviceID, "error": ctx.Err()})
+		return false
+	case rxStep := <-aChOnuProcessingStep:
+		if rxStep == aProcessingStep {
+			return true
+		}
+		//all other values are not accepted - including 0 for error indication
+		logger.Warnw("Invalid processing step received: abort!",
+			log.Fields{"device-id": oo.deviceID,
+				"wantedStep": aProcessingStep, "haveStep": rxStep})
+		return false
+	}
+}
+
+func (oo *OnuDeviceEntry) resetKvProcessingErrorIndication() {
+	oo.onuKVStoreprocResult = nil
+}
+func (oo *OnuDeviceEntry) getKvProcessingErrorIndication() error {
+	return oo.onuKVStoreprocResult
+}
+
+func (oo *OnuDeviceEntry) lockOnuKVStoreMutex() {
+	oo.onuKVStoreMutex.Lock()
+}
+
+func (oo *OnuDeviceEntry) unlockOnuKVStoreMutex() {
+	oo.onuKVStoreMutex.Unlock()
+}
diff --git a/internal/pkg/onuadaptercore/onu_uni_tp.go b/internal/pkg/onuadaptercore/onu_uni_tp.go
index 4f452a7..f47025b 100644
--- a/internal/pkg/onuadaptercore/onu_uni_tp.go
+++ b/internal/pkg/onuadaptercore/onu_uni_tp.go
@@ -21,7 +21,6 @@
 	"context"
 	"encoding/json"
 	"errors"
-	"fmt"
 	"strconv"
 	"strings"
 	"sync"
@@ -33,7 +32,6 @@
 )
 
 const cBasePathTechProfileKVStore = "service/voltha/technology_profiles"
-const cBasePathOnuKVStore = "service/voltha/openonu"
 
 //definitions for TechProfileProcessing - copied from OltAdapter:openolt_flowmgr.go
 //  could perhaps be defined more globally
@@ -51,20 +49,6 @@
 	cResourceTcont   resourceEntry = 2
 )
 
-type uniPersData struct {
-	PersUniID  uint32 `json:"uni_id"`
-	PersTpPath string `json:"tp_path"`
-}
-
-type onuPersistentData struct {
-	PersOnuID      uint32        `json:"onu_id"`
-	PersIntfID     uint32        `json:"intf_id"`
-	PersSnr        string        `json:"serial_number"`
-	PersAdminState string        `json:"admin_state"`
-	PersOperState  string        `json:"oper_state"`
-	PersUniTpPath  []uniPersData `json:"uni_config"`
-}
-
 type tTechProfileIndication struct {
 	techProfileType       string
 	techProfileID         uint16
@@ -103,13 +87,8 @@
 	deviceID                 string
 	baseDeviceHandler        *deviceHandler
 	tpProcMutex              sync.RWMutex
-	mapUniTpPath             map[uint32]string
-	sOnuPersistentData       onuPersistentData
 	techProfileKVStore       *db.Backend
-	onuKVStore               *db.Backend
-	onuKVStorePath           string
 	chTpConfigProcessingStep chan uint8
-	chTpKvProcessingStep     chan uint8
 	mapUniTpIndication       map[uint8]*tTechProfileIndication //use pointer values to ease assignments to the map
 	mapPonAniConfig          map[uint8]*tMapPonAniConfig       //per UNI: use pointer values to ease assignments to the map
 	pAniConfigFsm            *uniPonAniConfigFsm
@@ -125,10 +104,7 @@
 	onuTP.deviceID = aDeviceID
 	onuTP.baseDeviceHandler = aDeviceHandler
 	onuTP.tpProcMutex = sync.RWMutex{}
-	onuTP.mapUniTpPath = make(map[uint32]string)
-	onuTP.sOnuPersistentData.PersUniTpPath = make([]uniPersData, 1)
 	onuTP.chTpConfigProcessingStep = make(chan uint8)
-	onuTP.chTpKvProcessingStep = make(chan uint8)
 	onuTP.mapUniTpIndication = make(map[uint8]*tTechProfileIndication)
 	onuTP.mapPonAniConfig = make(map[uint8]*tMapPonAniConfig)
 	onuTP.procResult = nil //default assumption processing done with success
@@ -139,12 +115,6 @@
 			log.Fields{"device-id": aDeviceID, "service": cBasePathTechProfileKVStore})
 	}
 
-	onuTP.onuKVStorePath = onuTP.deviceID
-	onuTP.onuKVStore = aDeviceHandler.setBackend(cBasePathOnuKVStore)
-	if onuTP.onuKVStore == nil {
-		logger.Errorw("Can't access onuKVStore - no backend connection to service",
-			log.Fields{"device-id": aDeviceID, "service": cBasePathOnuKVStore})
-	}
 	return &onuTP
 }
 
@@ -158,59 +128,13 @@
 	onuTP.tpProcMutex.Unlock()
 }
 
-// resetProcessingErrorIndication resets the internal error indication
+// resetTpProcessingErrorIndication resets the internal error indication
 // need to be called before evaluation of any subsequent processing (given by waitForTpCompletion())
-func (onuTP *onuUniTechProf) resetProcessingErrorIndication() {
+func (onuTP *onuUniTechProf) resetTpProcessingErrorIndication() {
 	onuTP.procResult = nil
 }
 
-// updateOnuUniTpPath verifies and updates changes in the kvStore onuUniTpPath
-func (onuTP *onuUniTechProf) updateOnuUniTpPath(aUniID uint32, aPathString string) bool {
-	/* within some specific InterAdapter processing request write/read access to data is ensured to be sequentially,
-	   as also the complete sequence is ensured to 'run to  completion' before some new request is accepted
-	   no specific concurrency protection to sOnuPersistentData is required here
-	*/
-	if existingPath, present := onuTP.mapUniTpPath[aUniID]; present {
-		// uni entry already exists
-		//logger.Debugw(" already exists", log.Fields{"for InstanceId": a_uniInstNo})
-		if existingPath != aPathString {
-			if aPathString == "" {
-				//existing entry to be deleted
-				logger.Debugw("UniTp path delete", log.Fields{
-					"device-id": onuTP.deviceID, "uniID": aUniID, "path": aPathString})
-				delete(onuTP.mapUniTpPath, aUniID)
-			} else {
-				//existing entry to be modified
-				logger.Debugw("UniTp path modify", log.Fields{
-					"device-id": onuTP.deviceID, "uniID": aUniID, "path": aPathString})
-				onuTP.mapUniTpPath[aUniID] = aPathString
-			}
-			return true
-		}
-		//entry already exists
-		logger.Debugw("UniTp path already exists", log.Fields{
-			"device-id": onuTP.deviceID, "uniID": aUniID, "path": aPathString})
-		return false
-	}
-	//uni entry does not exist
-	if aPathString == "" {
-		//delete request in non-existing state , accept as no change
-		logger.Debugw("UniTp path already removed", log.Fields{
-			"device-id": onuTP.deviceID, "uniID": aUniID})
-		return false
-	}
-	//new entry to be set
-	logger.Debugw("New UniTp path set", log.Fields{
-		"device-id": onuTP.deviceID, "uniID": aUniID, "path": aPathString})
-	onuTP.mapUniTpPath[aUniID] = aPathString
-	return true
-}
-
-func (onuTP *onuUniTechProf) waitForTpCompletion(cancel context.CancelFunc, wg *sync.WaitGroup) error {
-	defer cancel() //ensure termination of context (may be pro forma)
-	wg.Wait()
-	logger.Debug("some TechProfile Processing completed")
-	onuTP.tpProcMutex.Unlock() //allow further TP related processing
+func (onuTP *onuUniTechProf) getTpProcessingErrorIndication() error {
 	return onuTP.procResult
 }
 
@@ -306,148 +230,8 @@
 	}
 }
 
-func (onuTP *onuUniTechProf) updateOnuTpPathKvStore(ctx context.Context, wg *sync.WaitGroup) {
-	defer wg.Done()
-
-	if onuTP.onuKVStore == nil {
-		logger.Debugw("onuKVStore not set - abort", log.Fields{"device-id": onuTP.deviceID})
-		onuTP.procResult = errors.New("onu/tp-data update aborted: onuKVStore not set")
-		return
-	}
-	var processingStep uint8 = 1 // used to synchronize the different processing steps with chTpKvProcessingStep
-	go onuTP.storePersistentData(ctx, processingStep)
-	if !onuTP.waitForTimeoutOrCompletion(ctx, onuTP.chTpKvProcessingStep, processingStep) {
-		//timeout or error detected
-		logger.Debugw("ONU/TP-data not written - abort", log.Fields{"device-id": onuTP.deviceID})
-		onuTP.procResult = errors.New("onu/tp-data update aborted: during writing process")
-		return
-	}
-}
-
-func (onuTP *onuUniTechProf) restoreFromOnuTpPathKvStore(ctx context.Context) error {
-	if onuTP.onuKVStore == nil {
-		logger.Debugw("onuKVStore not set - abort", log.Fields{"device-id": onuTP.deviceID})
-		return fmt.Errorf(fmt.Sprintf("onuKVStore-not-set-abort-%s", onuTP.deviceID))
-	}
-	if err := onuTP.restorePersistentData(ctx); err != nil {
-		logger.Debugw("ONU/TP-data not read - abort", log.Fields{"device-id": onuTP.deviceID})
-		return err
-	}
-	return nil
-}
-
-func (onuTP *onuUniTechProf) deleteOnuTpPathKvStore(ctx context.Context) error {
-	if onuTP.onuKVStore == nil {
-		logger.Debugw("onuKVStore not set - abort", log.Fields{"device-id": onuTP.deviceID})
-		return fmt.Errorf(fmt.Sprintf("onuKVStore-not-set-abort-%s", onuTP.deviceID))
-	}
-	if err := onuTP.deletePersistentData(ctx); err != nil {
-		logger.Debugw("ONU/TP-data not read - abort", log.Fields{"device-id": onuTP.deviceID})
-		return err
-	}
-	return nil
-}
-
-// deleteTpResource removes Resources from the ONU's specified Uni
-func (onuTP *onuUniTechProf) deleteTpResource(ctx context.Context,
-	aUniID uint32, aPathString string, aResource resourceEntry, aEntryID uint32,
-	wg *sync.WaitGroup) {
-	defer wg.Done()
-	logger.Debugw("this would remove TP resources from ONU's UNI", log.Fields{
-		"device-id": onuTP.deviceID, "uniID": aUniID, "path": aPathString, "Resource": aResource})
-	//TODO!!!
-	//delete the given resource from ONU OMCI config and data base - as background routine
-	/*
-		var processingStep uint8 = 1 // used to synchronize the different processing steps with chTpConfigProcessingStep
-		go onuTp.deleteAniResource(ctx, processingStep)
-		if !onuTP.waitForTimeoutOrCompletion(ctx, chTpConfigProcessingStep, processingStep) {
-			//timeout or error detected
-			return
-		}
-	*/
-}
-
 /* internal methods *********************/
 
-func (onuTP *onuUniTechProf) storePersistentData(ctx context.Context, aProcessingStep uint8) {
-
-	onuTP.sOnuPersistentData.PersOnuID = onuTP.baseDeviceHandler.pOnuIndication.OnuId
-	onuTP.sOnuPersistentData.PersIntfID = onuTP.baseDeviceHandler.pOnuIndication.IntfId
-	onuTP.sOnuPersistentData.PersSnr = onuTP.baseDeviceHandler.pOnuOmciDevice.serialNumber
-	//TODO: verify usage of these values during restart UC
-	onuTP.sOnuPersistentData.PersAdminState = "up"
-	onuTP.sOnuPersistentData.PersOperState = "active"
-
-	onuTP.sOnuPersistentData.PersUniTpPath = onuTP.sOnuPersistentData.PersUniTpPath[:0]
-
-	for k, v := range onuTP.mapUniTpPath {
-		onuTP.sOnuPersistentData.PersUniTpPath =
-			append(onuTP.sOnuPersistentData.PersUniTpPath, uniPersData{PersUniID: k, PersTpPath: v})
-	}
-	logger.Debugw("Update ONU/TP-data in KVStore", log.Fields{"device-id": onuTP.deviceID, "onuTP.sOnuPersistentData": onuTP.sOnuPersistentData})
-
-	Value, err := json.Marshal(onuTP.sOnuPersistentData)
-	if err != nil {
-		logger.Errorw("unable to marshal ONU/TP-data", log.Fields{"onuTP.sOnuPersistentData": onuTP.sOnuPersistentData,
-			"device-id": onuTP.deviceID, "err": err})
-		onuTP.chTpKvProcessingStep <- 0 //error indication
-		return
-	}
-	err = onuTP.onuKVStore.Put(ctx, onuTP.onuKVStorePath, Value)
-	if err != nil {
-		logger.Errorw("unable to write ONU/TP-data into KVstore", log.Fields{"device-id": onuTP.deviceID, "err": err})
-		onuTP.chTpKvProcessingStep <- 0 //error indication
-		return
-	}
-	onuTP.chTpKvProcessingStep <- aProcessingStep //done
-}
-
-func (onuTP *onuUniTechProf) restorePersistentData(ctx context.Context) error {
-
-	onuTP.mapUniTpPath = make(map[uint32]string)
-	onuTP.sOnuPersistentData = onuPersistentData{0, 0, "", "", "", make([]uniPersData, 0)}
-
-	Value, err := onuTP.onuKVStore.Get(ctx, onuTP.onuKVStorePath)
-	if err == nil {
-		if Value != nil {
-			logger.Debugw("ONU/TP-data read",
-				log.Fields{"Key": Value.Key, "device-id": onuTP.deviceID})
-			tpTmpBytes, _ := kvstore.ToByte(Value.Value)
-
-			if err = json.Unmarshal(tpTmpBytes, &onuTP.sOnuPersistentData); err != nil {
-				logger.Errorw("unable to unmarshal ONU/TP-data", log.Fields{"error": err, "device-id": onuTP.deviceID})
-				return fmt.Errorf(fmt.Sprintf("unable-to-unmarshal-ONU/TP-data-%s", onuTP.deviceID))
-			}
-			logger.Debugw("ONU/TP-data", log.Fields{"onuTP.sOnuPersistentData": onuTP.sOnuPersistentData,
-				"device-id": onuTP.deviceID})
-
-			for _, uniData := range onuTP.sOnuPersistentData.PersUniTpPath {
-				onuTP.mapUniTpPath[uniData.PersUniID] = uniData.PersTpPath
-			}
-			logger.Debugw("TpPath map", log.Fields{"onuTP.mapUniTpPath": onuTP.mapUniTpPath,
-				"device-id": onuTP.deviceID})
-		} else {
-			logger.Errorw("no ONU/TP-data found", log.Fields{"path": onuTP.onuKVStorePath, "device-id": onuTP.deviceID})
-			return fmt.Errorf(fmt.Sprintf("no-ONU/TP-data-found-%s", onuTP.deviceID))
-		}
-	} else {
-		logger.Errorw("unable to read from KVstore", log.Fields{"device-id": onuTP.deviceID})
-		return fmt.Errorf(fmt.Sprintf("unable-to-read-from-KVstore-%s", onuTP.deviceID))
-	}
-	return nil
-}
-
-func (onuTP *onuUniTechProf) deletePersistentData(ctx context.Context) error {
-
-	logger.Debugw("delete ONU/TP-data in KVStore", log.Fields{"device-id": onuTP.deviceID})
-	err := onuTP.onuKVStore.Delete(ctx, onuTP.onuKVStorePath)
-	if err != nil {
-		logger.Errorw("unable to delete in KVstore", log.Fields{"device-id": onuTP.deviceID, "err": err})
-		return fmt.Errorf(fmt.Sprintf("unable-delete-in-KVstore-%s", onuTP.deviceID))
-	}
-	return nil
-}
-
 func (onuTP *onuUniTechProf) readAniSideConfigFromTechProfile(
 	ctx context.Context, aUniID uint8, aPathString string, aProcessingStep uint8) {
 	var tpInst tp.TechProfile
diff --git a/internal/pkg/onuadaptercore/platform.go b/internal/pkg/onuadaptercore/platform.go
index 3430247..cd4c923 100644
--- a/internal/pkg/onuadaptercore/platform.go
+++ b/internal/pkg/onuadaptercore/platform.go
@@ -128,7 +128,7 @@
 var controllerPorts = []uint32{0xfffd, 0x7ffffffd, 0xfffffffd}
 */
 
-//mkUniPortNum returns new UNIportNum based on intfID, inuID and uniID
+//mkUniPortNum returns new UNIportNum based on intfID, onuID and uniID
 func mkUniPortNum(intfID, onuID, uniID uint32) uint32 {
 	//extended for checks available in the python onu adapter:!!
 	var limit = int(intfID)