VOL-2296 Install TrafficDescriptor ME to provide upstream rate-limiting at ONU and set traffic descriptor info into Gem Port Network CTP ME. Also, These rate-limiting value gets meter bands.

Change-Id: Ib6189d5b1e25734fff1702d3dfa16736ad0b1377
diff --git a/internal/pkg/onuadaptercore/omci_vlan_config.go b/internal/pkg/onuadaptercore/omci_vlan_config.go
index d95a8a7..4f8247a 100644
--- a/internal/pkg/onuadaptercore/omci_vlan_config.go
+++ b/internal/pkg/onuadaptercore/omci_vlan_config.go
@@ -27,6 +27,9 @@
 	"sync"
 	"time"
 
+	meters "github.com/opencord/voltha-lib-go/v5/pkg/meters"
+	"github.com/opencord/voltha-protos/v4/go/voltha"
+
 	gp "github.com/google/gopacket"
 	"github.com/looplab/fsm"
 	"github.com/opencord/omci-lib-go"
@@ -137,8 +140,9 @@
 }
 
 type uniVlanFlowParams struct {
-	CookieSlice    []uint64          `json:"cookie_slice"`
-	VlanRuleParams uniVlanRuleParams `json:"vlan_rule_params"`
+	CookieSlice    []uint64               `json:"cookie_slice"`
+	VlanRuleParams uniVlanRuleParams      `json:"vlan_rule_params"`
+	Meter          *voltha.OfpMeterConfig `json:"flow_meter"`
 }
 
 type uniRemoveVlanFlowParams struct {
@@ -168,6 +172,7 @@
 	mutexFlowParams             sync.RWMutex
 	chCookieDeleted             chan bool //channel to indicate that a specific cookie (related to the active rule) was deleted
 	actualUniVlanConfigRule     uniVlanRuleParams
+	actualUniVlanConfigMeter    *voltha.OfpMeterConfig
 	uniVlanFlowParamsSlice      []uniVlanFlowParams
 	uniRemoveFlowsSlice         []uniRemoveVlanFlowParams
 	numUniFlows                 uint8 // expected number of flows should be less than 12
@@ -194,7 +199,7 @@
 func NewUniVlanConfigFsm(ctx context.Context, apDeviceHandler *deviceHandler, apDevOmciCC *omciCC, apUniPort *onuUniPort,
 	apUniTechProf *onuUniTechProf, apOnuDB *onuDeviceDB, aTechProfileID uint8,
 	aRequestEvent OnuDeviceEvent, aName string, aCommChannel chan Message, aAcceptIncrementalEvto bool,
-	aCookieSlice []uint64, aMatchVlan uint16, aSetVlan uint16, aSetPcp uint8, lastFlowToRec bool) *UniVlanConfigFsm {
+	aCookieSlice []uint64, aMatchVlan uint16, aSetVlan uint16, aSetPcp uint8, lastFlowToRec bool, aMeter *voltha.OfpMeterConfig) *UniVlanConfigFsm {
 	instFsm := &UniVlanConfigFsm{
 		pDeviceHandler:              apDeviceHandler,
 		deviceID:                    apDeviceHandler.deviceID,
@@ -274,7 +279,7 @@
 		return nil
 	}
 
-	_ = instFsm.initUniFlowParams(ctx, aTechProfileID, aCookieSlice, aMatchVlan, aSetVlan, aSetPcp)
+	_ = instFsm.initUniFlowParams(ctx, aTechProfileID, aCookieSlice, aMatchVlan, aSetVlan, aSetPcp, aMeter)
 
 	logger.Debugw(ctx, "UniVlanConfigFsm created", log.Fields{"device-id": instFsm.deviceID,
 		"accIncrEvto": instFsm.acceptIncrementalEvtoOption})
@@ -283,7 +288,7 @@
 
 //initUniFlowParams is a simplified form of SetUniFlowParams() used for first flow parameters configuration
 func (oFsm *UniVlanConfigFsm) initUniFlowParams(ctx context.Context, aTpID uint8, aCookieSlice []uint64,
-	aMatchVlan uint16, aSetVlan uint16, aSetPcp uint8) error {
+	aMatchVlan uint16, aSetVlan uint16, aSetPcp uint8, aMeter *voltha.OfpMeterConfig) error {
 	loRuleParams := uniVlanRuleParams{
 		TpID:     aTpID,
 		MatchVid: uint32(aMatchVlan),
@@ -321,6 +326,9 @@
 	loFlowParams := uniVlanFlowParams{VlanRuleParams: loRuleParams}
 	loFlowParams.CookieSlice = make([]uint64, 0)
 	loFlowParams.CookieSlice = append(loFlowParams.CookieSlice, aCookieSlice...)
+	if aMeter != nil {
+		loFlowParams.Meter = aMeter
+	}
 
 	//no mutex protection is required for initial access and adding the first flow is always possible
 	oFsm.uniVlanFlowParamsSlice = make([]uniVlanFlowParams, 0)
@@ -330,7 +338,7 @@
 		"MatchVid":  strconv.FormatInt(int64(loRuleParams.MatchVid), 16),
 		"SetVid":    strconv.FormatInt(int64(loRuleParams.SetVid), 16),
 		"SetPcp":    loRuleParams.SetPcp,
-		"device-id": oFsm.deviceID})
+		"device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.uniID})
 	oFsm.numUniFlows = 1
 	oFsm.uniRemoveFlowsSlice = make([]uniRemoveVlanFlowParams, 0) //initially nothing to remove
 
@@ -391,7 +399,7 @@
 // ignore complexity by now
 // nolint: gocyclo
 func (oFsm *UniVlanConfigFsm) SetUniFlowParams(ctx context.Context, aTpID uint8, aCookieSlice []uint64,
-	aMatchVlan uint16, aSetVlan uint16, aSetPcp uint8, lastFlowToReconcile bool) error {
+	aMatchVlan uint16, aSetVlan uint16, aSetPcp uint8, lastFlowToReconcile bool, aMeter *voltha.OfpMeterConfig) error {
 	loRuleParams := uniVlanRuleParams{
 		TpID:     aTpID,
 		MatchVid: uint32(aMatchVlan),
@@ -401,7 +409,6 @@
 	// some automatic adjustments on the filter/treat parameters as not specifically configured/ensured by flow configuration parameters
 	loRuleParams.TagsToRemove = 1            //one tag to remove as default setting
 	loRuleParams.MatchPcp = cPrioDoNotFilter // do not Filter on prio as default
-
 	if loRuleParams.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
 		loRuleParams.MatchVid = uint32(of.OfpVlanId_OFPVID_PRESENT)
@@ -468,7 +475,10 @@
 		if storedUniFlowParams.VlanRuleParams == loRuleParams {
 			flowEntryMatch = true
 			logger.Debugw(ctx, "UniVlanConfigFsm flow setting - rule already exists", log.Fields{
-				"device-id": oFsm.deviceID})
+				"MatchVid":  strconv.FormatInt(int64(loRuleParams.MatchVid), 16),
+				"SetVid":    strconv.FormatInt(int64(loRuleParams.SetVid), 16),
+				"SetPcp":    loRuleParams.SetPcp,
+				"device-id": oFsm.deviceID, " uni-id": oFsm.pOnuUniPort.uniID})
 			var cookieMatch bool
 			for _, newCookie := range aCookieSlice { // for all cookies available in the arguments
 				cookieMatch = false
@@ -519,13 +529,16 @@
 			loFlowParams := uniVlanFlowParams{VlanRuleParams: loRuleParams}
 			loFlowParams.CookieSlice = make([]uint64, 0)
 			loFlowParams.CookieSlice = append(loFlowParams.CookieSlice, aCookieSlice...)
+			if aMeter != nil {
+				loFlowParams.Meter = aMeter
+			}
 			oFsm.uniVlanFlowParamsSlice = append(oFsm.uniVlanFlowParamsSlice, loFlowParams)
 			logger.Debugw(ctx, "UniVlanConfigFsm flow add", log.Fields{
 				"Cookies":  oFsm.uniVlanFlowParamsSlice[oFsm.numUniFlows].CookieSlice,
 				"MatchVid": strconv.FormatInt(int64(loRuleParams.MatchVid), 16),
 				"SetVid":   strconv.FormatInt(int64(loRuleParams.SetVid), 16),
 				"SetPcp":   loRuleParams.SetPcp, "numberofFlows": oFsm.numUniFlows + 1,
-				"device-id": oFsm.deviceID})
+				"device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.uniID})
 
 			oFsm.numUniFlows++
 			pConfigVlanStateBaseFsm := oFsm.pAdaptFsm.pFsm
@@ -573,6 +586,7 @@
 						return fmt.Errorf("abort UniVlanConfigFsm on add due to internal counter mismatch %s", oFsm.deviceID)
 					}
 					oFsm.actualUniVlanConfigRule = oFsm.uniVlanFlowParamsSlice[oFsm.configuredUniFlow].VlanRuleParams
+					oFsm.actualUniVlanConfigMeter = oFsm.uniVlanFlowParamsSlice[oFsm.configuredUniFlow].Meter
 					//tpId of the next rule to be configured
 					tpID := oFsm.actualUniVlanConfigRule.TpID
 					loTechProfDone := oFsm.pUniTechProf.getTechProfileDone(ctx, oFsm.pOnuUniPort.uniID, tpID)
@@ -1065,6 +1079,7 @@
 		//access to uniVlanFlowParamsSlice is done on first element only here per definition
 		//store the actual rule that shall be worked upon in the following transient states
 		oFsm.actualUniVlanConfigRule = oFsm.uniVlanFlowParamsSlice[0].VlanRuleParams
+		oFsm.actualUniVlanConfigMeter = oFsm.uniVlanFlowParamsSlice[0].Meter
 		tpID := oFsm.actualUniVlanConfigRule.TpID
 		oFsm.TpIDWaitingFor = tpID
 		loTechProfDone := oFsm.pUniTechProf.getTechProfileDone(ctx, oFsm.pOnuUniPort.uniID, uint8(tpID))
@@ -1184,6 +1199,21 @@
 				oFsm.mutexFlowParams.RLock()
 			}
 			oFsm.mutexFlowParams.RUnlock()
+			//If this first flow contains a meter, then create TD for related gems.
+			if oFsm.actualUniVlanConfigMeter != nil {
+				logger.Debugw(ctx, "Creating Traffic Descriptor", log.Fields{"device-id": oFsm.deviceID, "meter": oFsm.actualUniVlanConfigMeter})
+				for _, gemPort := range oFsm.pUniTechProf.getBidirectionalGemPortIDsForTP(ctx, oFsm.pOnuUniPort.uniID, tpID) {
+					logger.Debugw(ctx, "Creating Traffic Descriptor for gem", log.Fields{"device-id": oFsm.deviceID, "meter": oFsm.actualUniVlanConfigMeter, "gem": gemPort})
+					errCreateTrafficDescriptor := oFsm.createTrafficDescriptor(ctx, oFsm.actualUniVlanConfigMeter, tpID,
+						oFsm.pOnuUniPort.uniID, gemPort)
+					if errCreateTrafficDescriptor != nil {
+						logger.Errorw(ctx, "Create Traffic Descriptor create failed, aborting Ani Config FSM!",
+							log.Fields{"device-id": oFsm.deviceID})
+						_ = oFsm.pAdaptFsm.pFsm.Event(vlanEvReset)
+					}
+				}
+			}
+
 			//TODO Possibly insert new state for multicast --> possibly another jira/later time.
 			_ = oFsm.pAdaptFsm.pFsm.Event(vlanEvRxConfigEvtocd)
 		}
@@ -1257,6 +1287,7 @@
 			return
 		}
 		oFsm.actualUniVlanConfigRule = oFsm.uniVlanFlowParamsSlice[oFsm.configuredUniFlow].VlanRuleParams
+		oFsm.actualUniVlanConfigMeter = oFsm.uniVlanFlowParamsSlice[oFsm.configuredUniFlow].Meter
 		//tpId of the next rule to be configured
 		tpID := oFsm.actualUniVlanConfigRule.TpID
 		oFsm.TpIDWaitingFor = tpID
@@ -1447,6 +1478,19 @@
 					_ = oFsm.pAdaptFsm.pFsm.Event(vlanEvReset)
 				}
 			}
+			//If this incremental flow contains a meter, then create TD for related gems.
+			if oFsm.actualUniVlanConfigMeter != nil {
+				for _, gemPort := range oFsm.pUniTechProf.getBidirectionalGemPortIDsForTP(ctx, oFsm.pOnuUniPort.uniID, tpID) {
+					logger.Debugw(ctx, "Creating Traffic Descriptor for gem", log.Fields{"device-id": oFsm.deviceID, "meter": oFsm.actualUniVlanConfigMeter, "gem": gemPort})
+					errCreateTrafficDescriptor := oFsm.createTrafficDescriptor(ctx, oFsm.actualUniVlanConfigMeter, tpID,
+						oFsm.pOnuUniPort.uniID, gemPort)
+					if errCreateTrafficDescriptor != nil {
+						logger.Errorw(ctx, "Create Traffic Descriptor create failed, aborting Ani Config FSM!",
+							log.Fields{"device-id": oFsm.deviceID})
+						_ = oFsm.pAdaptFsm.pFsm.Event(vlanEvReset)
+					}
+				}
+			}
 			_ = oFsm.pAdaptFsm.pFsm.Event(vlanEvRxConfigEvtocd)
 		}
 	}()
@@ -1831,7 +1875,7 @@
 				if msgObj.EntityClass == oFsm.pLastTxMeInstance.GetClassID() &&
 					msgObj.EntityInstance == oFsm.pLastTxMeInstance.GetEntityID() {
 					switch oFsm.pLastTxMeInstance.GetName() {
-					case "VlanTaggingFilterData", "ExtendedVlanTaggingOperationConfigurationData", "MulticastOperationsProfile":
+					case "VlanTaggingFilterData", "ExtendedVlanTaggingOperationConfigurationData", "MulticastOperationsProfile", "GemPortNetworkCtp":
 						{ // let the MultiEntity config proceed by stopping the wait function
 							oFsm.mutexPLastTxMeInstance.RUnlock()
 							oFsm.omciMIdsResponseReceived <- true
@@ -1896,7 +1940,7 @@
 			switch oFsm.pLastTxMeInstance.GetName() {
 			case "VlanTaggingFilterData", "MulticastOperationsProfile",
 				"MulticastSubscriberConfigInfo", "MacBridgePortConfigurationData",
-				"ExtendedVlanTaggingOperationConfigurationData":
+				"ExtendedVlanTaggingOperationConfigurationData", "TrafficDescriptor":
 				{
 					oFsm.mutexPLastTxMeInstance.RUnlock()
 					if oFsm.pAdaptFsm.pFsm.Current() == vlanStConfigVtfd {
@@ -1949,7 +1993,7 @@
 		if msgObj.EntityClass == oFsm.pLastTxMeInstance.GetClassID() &&
 			msgObj.EntityInstance == oFsm.pLastTxMeInstance.GetEntityID() {
 			switch oFsm.pLastTxMeInstance.GetName() {
-			case "VlanTaggingFilterData", "ExtendedVlanTaggingOperationConfigurationData":
+			case "VlanTaggingFilterData", "ExtendedVlanTaggingOperationConfigurationData", "TrafficDescriptor":
 				{ // let the MultiEntity config proceed by stopping the wait function
 					oFsm.mutexPLastTxMeInstance.RUnlock()
 					oFsm.omciMIdsResponseReceived <- true
@@ -2919,6 +2963,86 @@
 	return nil
 }
 
+func (oFsm *UniVlanConfigFsm) createTrafficDescriptor(ctx context.Context, aMeter *voltha.OfpMeterConfig,
+	tpID uint8, uniID uint8, gemPortID uint16) error {
+	logger.Infow(ctx, "Starting create traffic descriptor", log.Fields{"device-id": oFsm.deviceID, "uniID": uniID, "tpID": tpID})
+	// uniTPKey  generate id to Traffic Descriptor ME. We need to create two of them. They should be unique. Because of that
+	// I created unique TD ID by flow direction.
+	// TODO! Traffic descriptor ME ID will check
+	trafficDescriptorID := gemPortID
+	if aMeter == nil {
+		return fmt.Errorf("meter not found %s", oFsm.deviceID)
+	}
+	trafficShapingInfo, err := meters.GetTrafficShapingInfo(ctx, aMeter)
+	if err != nil {
+		logger.Errorw(ctx, "Traffic Shaping Info get failed", log.Fields{"device-id": oFsm.deviceID})
+		return err
+	}
+	cir := trafficShapingInfo.Cir + trafficShapingInfo.Gir
+	cbs := trafficShapingInfo.Cbs
+	pir := trafficShapingInfo.Pir
+	pbs := trafficShapingInfo.Pbs
+
+	logger.Infow(ctx, "cir-pir-cbs-pbs", log.Fields{"device-id": oFsm.deviceID, "cir": cir, "pir": pir, "cbs": cbs, "pbs": pbs})
+	meParams := me.ParamData{
+		EntityID: trafficDescriptorID,
+		Attributes: me.AttributeValueMap{
+			"Cir":                  cir,
+			"Pir":                  pir,
+			"Cbs":                  cbs,
+			"Pbs":                  pbs,
+			"ColourMode":           1,
+			"IngressColourMarking": 3,
+			"EgressColourMarking":  3,
+			"MeterType":            1,
+		},
+	}
+	meInstance, errCreateTD := oFsm.pOmciCC.sendCreateTDVar(log.WithSpanFromContext(context.TODO(), ctx), oFsm.pDeviceHandler.pOpenOnuAc.omciTimeout,
+		true, oFsm.pAdaptFsm.commChan, meParams)
+	if errCreateTD != nil {
+		logger.Errorw(ctx, "Traffic Descriptor create failed", log.Fields{"device-id": oFsm.deviceID})
+		return err
+	}
+	oFsm.pLastTxMeInstance = meInstance
+	err = oFsm.waitforOmciResponse(ctx)
+	if err != nil {
+		logger.Errorw(ctx, "Traffic Descriptor create failed, aborting VlanConfig FSM!", log.Fields{"device-id": oFsm.deviceID})
+		return err
+	}
+
+	err = oFsm.setTrafficDescriptorToGemPortNWCTP(ctx, gemPortID)
+	if err != nil {
+		logger.Errorw(ctx, "Traffic Descriptor set failed to Gem Port Network CTP, aborting VlanConfig FSM!", log.Fields{"device-id": oFsm.deviceID})
+		return err
+	}
+	logger.Infow(ctx, "Set TD Info to GemPortNWCTP successfully", log.Fields{"device-id": oFsm.deviceID, "gem-port-id": gemPortID, "td-id": trafficDescriptorID})
+
+	return nil
+}
+
+func (oFsm *UniVlanConfigFsm) setTrafficDescriptorToGemPortNWCTP(ctx context.Context, gemPortID uint16) error {
+	logger.Debugw(ctx, "Starting Set Traffic Descriptor to GemPortNWCTP", log.Fields{"device-id": oFsm.deviceID, "gem-port-id": gemPortID})
+	meParams := me.ParamData{
+		EntityID: gemPortID,
+		Attributes: me.AttributeValueMap{
+			"TrafficManagementPointerForUpstream": gemPortID,
+		},
+	}
+	meInstance, err := oFsm.pOmciCC.sendSetGemNCTPVar(log.WithSpanFromContext(context.TODO(), ctx),
+		oFsm.pDeviceHandler.pOpenOnuAc.omciTimeout, true, oFsm.pAdaptFsm.commChan, meParams)
+	if err != nil {
+		logger.Errorw(ctx, "GemNCTP set failed", log.Fields{"device-id": oFsm.deviceID})
+		return err
+	}
+	oFsm.pLastTxMeInstance = meInstance
+	err = oFsm.waitforOmciResponse(ctx)
+	if err != nil {
+		logger.Errorw(ctx, "Upstream Traffic Descriptor set failed, aborting VlanConfig FSM!", log.Fields{"device-id": oFsm.deviceID})
+		return err
+	}
+	return nil
+}
+
 // IsFlowRemovePending returns true if there are pending flows to remove, else false.
 func (oFsm *UniVlanConfigFsm) IsFlowRemovePending(aFlowDeleteChannel chan<- bool) bool {
 	oFsm.mutexFlowParams.Lock()