[VOL-4796] openonuAdapterGo: Robustness - verification/correlation of ONU capabilities against configuration requirements

BBSIM mib templates adapted to the increased number of UpstreamPriorityQueues reported during BBSIM mibUpload (patch https://gerrit.opencord.org/c/bbsim/+/33457).

Change-Id: I752986335df99b6d5d5de9423ae25dcbfd828229
diff --git a/internal/pkg/core/device_handler.go b/internal/pkg/core/device_handler.go
index 9ad2b7d..744834a 100755
--- a/internal/pkg/core/device_handler.go
+++ b/internal/pkg/core/device_handler.go
@@ -419,6 +419,15 @@
 		case *ia.TechProfileDownloadMessage_TpInstance:
 			logger.Debugw(ctx, "onu-uni-tp-path-modified", log.Fields{"device-id": dh.DeviceID,
 				"uniID": uniID, "tp-path": techProfMsg.TpInstancePath, "tpID": tpID})
+
+			err = dh.CheckAvailableOnuCapabilities(ctx, pDevEntry, *tpInst.TpInstance)
+			if err != nil {
+				logger.Errorw(ctx, "error-checking-available-onu-capabilities-stopping-device",
+					log.Fields{"device-id": dh.DeviceID, "err": err, "tp-path": techProfMsg.TpInstancePath})
+				// stopping all further processing
+				_ = dh.UpdateInterface(ctx)
+				return err
+			}
 			//	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?)
@@ -2752,7 +2761,7 @@
 	dh.chUniVlanConfigReconcilingDone = make(chan uint16, uniCnt)
 	for i := 0; i < int(uniCnt); i++ {
 		dh.flowCbChan[i] = make(chan FlowCb, dh.pOpenOnuAc.config.MaxConcurrentFlowsPerUni)
-		dh.stopFlowMonitoringRoutine[i] = make(chan bool)
+		dh.stopFlowMonitoringRoutine[i] = make(chan bool, 1)
 	}
 }
 
@@ -4423,6 +4432,58 @@
 	return err
 }
 
+func (dh *deviceHandler) CheckAvailableOnuCapabilities(ctx context.Context, pDevEntry *mib.OnuDeviceEntry, tpInst tech_profile.TechProfileInstance) error {
+	// Check if there are additional TCONT instances necessary/available
+	pDevEntry.MutexPersOnuConfig.Lock()
+	if _, ok := pDevEntry.SOnuPersistentData.PersTcontMap[uint16(tpInst.UsScheduler.AllocId)]; !ok {
+		numberOfTcontMapEntries := len(pDevEntry.SOnuPersistentData.PersTcontMap)
+		pDevEntry.MutexPersOnuConfig.Unlock()
+		numberOfTcontDbInsts := pDevEntry.GetOnuDB().GetNumberOfInst(me.TContClassID)
+		logger.Debugw(ctx, "checking available TCONT instances",
+			log.Fields{"device-id": dh.DeviceID, "numberOfTcontMapEntries": numberOfTcontMapEntries, "numberOfTcontDbInsts": numberOfTcontDbInsts})
+		if numberOfTcontMapEntries >= numberOfTcontDbInsts {
+			logger.Errorw(ctx, "configuration exceeds ONU capabilities - running out of TCONT instances: send ONU device event!",
+				log.Fields{"device-id": dh.device.Id})
+			pDevEntry.SendOnuDeviceEvent(ctx, cmn.OnuConfigFailureMissingTcont, cmn.OnuConfigFailureMissingTcontDesc)
+			return fmt.Errorf(fmt.Sprintf("configuration exceeds ONU capabilities - running out of TCONT instances: device-id: %s", dh.DeviceID))
+		}
+	} else {
+		pDevEntry.MutexPersOnuConfig.Unlock()
+	}
+	// Check if there are enough PrioQueue instances available
+	if dh.pOnuTP != nil {
+		var numberOfUsPrioQueueDbInsts int
+
+		queueInstKeys := pDevEntry.GetOnuDB().GetSortedInstKeys(ctx, me.PriorityQueueClassID)
+		for _, mgmtEntityID := range queueInstKeys {
+			if mgmtEntityID >= 0x8000 && mgmtEntityID <= 0xFFFF {
+				numberOfUsPrioQueueDbInsts++
+			}
+		}
+		// Check if there is an upstream PriorityQueue instance available for each Gem port
+		numberOfConfiguredGemPorts := dh.pOnuTP.GetNumberOfConfiguredUsGemPorts(ctx)
+		logger.Debugw(ctx, "checking available upstream PriorityQueue instances",
+			log.Fields{"device-id": dh.DeviceID,
+				"numberOfConfiguredGemPorts": numberOfConfiguredGemPorts,
+				"tpInst.NumGemPorts":         tpInst.NumGemPorts,
+				"numberOfUsPrioQueueDbInsts": numberOfUsPrioQueueDbInsts})
+
+		if numberOfConfiguredGemPorts+int(tpInst.NumGemPorts) > numberOfUsPrioQueueDbInsts {
+			logger.Errorw(ctx, "configuration exceeds ONU capabilities - running out of upstream PrioQueue instances: send ONU device event!",
+				log.Fields{"device-id": dh.device.Id})
+			pDevEntry.SendOnuDeviceEvent(ctx, cmn.OnuConfigFailureMissingUsPriorityQueue, cmn.OnuConfigFailureMissingUsPriorityQueueDesc)
+			return fmt.Errorf(fmt.Sprintf("configuration exceeds ONU capabilities - running out of upstream PrioQueue instances: device-id: %s", dh.DeviceID))
+		}
+		// Downstream PrioQueue instances are evaluated in accordance with ONU MIB upload data in function UniPonAniConfigFsm::prepareAndEnterConfigState().
+		// In case of missing downstream PrioQueues the attribute "Priority queue pointer for downstream" of ME "GEM port network CTP" will be set to "0",
+		// which then alternatively activates the queuing mechanisms of the ONU (refer to Rec. ITU-T G.988 chapter 9.2.3).
+	} else {
+		logger.Warnw(ctx, "onuTechProf instance not set up - check for PriorityQueue instances skipped!",
+			log.Fields{"device-id": dh.DeviceID})
+	}
+	return nil
+}
+
 // GetDeviceID - TODO: add comment
 func (dh *deviceHandler) GetDeviceID() string {
 	return dh.DeviceID