VOL-2191: Implement the right interpretation of instance-control
attribute from tech-profile
Bumping version to 2.2.15
Change-Id: If802034af753a7872db684da2cc4f824968bd34c
diff --git a/.gitignore b/.gitignore
index 1c464a6..f296715 100644
--- a/.gitignore
+++ b/.gitignore
@@ -55,3 +55,6 @@
# test output
tests/results
+
+# sca-report
+sca-report
diff --git a/VERSION b/VERSION
index b57fd8c..5bd8c54 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-2.2.14-dev
+2.2.15
diff --git a/pkg/techprofile/config.go b/pkg/techprofile/config.go
index 9c64bd8..b1a8ac5 100644
--- a/pkg/techprofile/config.go
+++ b/pkg/techprofile/config.go
@@ -90,12 +90,10 @@
DefaultTPName string
TPVersion int
NumGemPorts uint32
- NumTconts uint32
DefaultPbits []string
LogLevel int
DefaultTechProfileID uint32
DefaultNumGemPorts uint32
- DefaultNumTconts uint32
}
func NewTechProfileFlags(KVStoreType string, KVStoreHost string, KVStorePort int) *TechProfileFlags {
@@ -113,7 +111,6 @@
TPInstanceKVPath: defaultTPInstanceKVPath,
DefaultTechProfileID: DEFAULT_TECH_PROFILE_TABLE_ID,
DefaultNumGemPorts: defaultGemportsCount,
- DefaultNumTconts: defaultNumTconts,
DefaultPbits: []string{defaultPbits},
LogLevel: defaultLogLevel,
}
diff --git a/pkg/techprofile/tech_profile.go b/pkg/techprofile/tech_profile.go
index 92569f0..8d391eb 100644
--- a/pkg/techprofile/tech_profile.go
+++ b/pkg/techprofile/tech_profile.go
@@ -20,6 +20,7 @@
"encoding/json"
"errors"
"fmt"
+ "regexp"
"strconv"
"github.com/opencord/voltha-lib-go/v2/pkg/db"
@@ -96,6 +97,9 @@
3: "WRed",
}
+// Required uniPortName format
+var uniPortNameFormat = regexp.MustCompile(`^pon-{[0-9]+}/onu-{[0-9]+}/uni-{[0-9]+}$`)
+
/*
type InferredAdditionBWIndication int32
@@ -218,7 +222,6 @@
ProfileType string `json:"profile_type"`
Version int `json:"version"`
NumGemPorts uint32 `json:"num_gem_ports"`
- NumTconts uint32 `json:"num_of_tconts"`
InstanceCtrl InstanceControl `json:"instance_control"`
UsScheduler iScheduler `json:"us_scheduler"`
DsScheduler iScheduler `json:"ds_scheduler"`
@@ -287,18 +290,51 @@
var KvTpIns TechProfile
var resPtr *TechProfile = &KvTpIns
var err error
- /*path := t.GetTechProfileInstanceKVPath(techProfiletblID, uniPortName)*/
- log.Infow("Getting tech profile instance from KV store", log.Fields{"path": path})
- kvresult, err := t.config.KVBackend.Get(path)
+ var tp *DefaultTechProfile = nil
+ var kvResult *kvstore.KVPair
+ if tp = t.getTPFromKVStore(techProfiletblID); tp != nil {
+ if err := t.validateInstanceControlAttr(tp.InstanceCtrl); err != nil {
+ return nil, errors.New("invalid-instance-ctl-attr")
+ }
+ } else {
+ return nil, errors.New("tp-not-found-on-kv-store")
+ }
+ if tp.InstanceCtrl.Onu == "multi-instance" {
+ // When InstanceCtrl.Onu is "multi-instance" there can be multiple instance of the same
+ // TP across different UNIs. We either find a pre-existing TP Instance on that UNI or
+ // create a new one.
+ log.Infow("Getting tech profile instance from KV store", log.Fields{"path": path})
+ kvResult, err = t.config.KVBackend.Get(path)
+ } else { // "single-instance"
+ // When InstanceCtrl.Onu is "single-instance" there can be only one instance of the
+ // TP across all UNIs. The TP instances for the given TP ID will have the same alloc_id,
+ // but different gemport-ids (per UNI).
+ // We do the following
+ // 1. Find a pre-existing TP Instance for the given TP ID and on the given UNI.
+ // If exists, return, else step 2.
+ // 2. Find TP instance for the given TP ID and on any other UNI on that ONU.
+ // If exists, make a copy of the TP instance, replace the gem-port IDs, place it
+ // in the the current UNIs TP instance path.
+ // If no other UNI have TP instance too, then return nil (a new TP instance will
+ // get created for the given TP ID).
+ kvResult, err = t.config.KVBackend.Get(path)
+ if kvResult == nil {
+ if resPtr, err = t.findAndAssignTpInstance(path); resPtr != nil {
+ log.Infow("successfully-found-and-assigned-tp-instance", log.Fields{"tpPath": path})
+ return resPtr, err
+ }
+ }
+ }
+
if err != nil {
log.Errorw("Error while fetching tech-profile instance from KV backend", log.Fields{"key": path})
return nil, err
}
- if kvresult == nil {
+ if kvResult == nil {
log.Infow("Tech profile does not exist in KV store", log.Fields{"key": path})
resPtr = nil
} else {
- if value, err := kvstore.ToByte(kvresult.Value); err == nil {
+ if value, err := kvstore.ToByte(kvResult.Value); err == nil {
if err = json.Unmarshal(value, resPtr); err != nil {
log.Errorw("Error while unmarshal KV result", log.Fields{"key": path, "value": value})
}
@@ -343,6 +379,13 @@
func (t *TechProfileMgr) CreateTechProfInstance(techProfiletblID uint32, uniPortName string, intfId uint32) *TechProfile {
var tpInstance *TechProfile
log.Infow("Creating tech profile instance ", log.Fields{"tableid": techProfiletblID, "uni": uniPortName, "intId": intfId})
+
+ // Make sure the uniPortName is as per format pon-{[0-9]+}/onu-{[0-9]+}/uni-{[0-9]+}
+ if !uniPortNameFormat.Match([]byte(uniPortName)) {
+ log.Errorw("uni-port-name-not-confirming-to-format", log.Fields{"uniPortName": uniPortName})
+ return nil
+ }
+
tp := t.getTPFromKVStore(techProfiletblID)
if tp != nil {
log.Infow("Creating tech profile instance with profile from KV store", log.Fields{"tpid": techProfiletblID})
@@ -350,7 +393,7 @@
tp = t.getDefaultTechProfile()
log.Infow("Creating tech profile instance with default values", log.Fields{"tpid": techProfiletblID})
}
- tpInstance = t.allocateTPInstance(uniPortName, tp, intfId, t.config.DefaultNumTconts)
+ tpInstance = t.allocateTPInstance(uniPortName, tp, intfId)
if err := t.addTechProfInstanceToKVStore(techProfiletblID, uniPortName, tpInstance); err != nil {
log.Errorw("Error in adding tech profile instance to KV ", log.Fields{"tableid": techProfiletblID, "uni": uniPortName})
return nil
@@ -365,7 +408,26 @@
return t.config.KVBackend.Delete(path)
}
-func (t *TechProfileMgr) allocateTPInstance(uniPortName string, tp *DefaultTechProfile, intfId uint32, numOfTconts uint32) *TechProfile {
+func (t *TechProfileMgr) validateInstanceControlAttr(instCtl InstanceControl) error {
+ if instCtl.Onu != "single-instance" && instCtl.Onu != "multi-instance" {
+ log.Errorw("invalid-onu-instance-control-attribute", log.Fields{"onu-inst": instCtl.Onu})
+ return errors.New("invalid-onu-instance-ctl-attr")
+ }
+
+ if instCtl.Uni != "single-instance" && instCtl.Uni != "multi-instance" {
+ log.Errorw("invalid-uni-instance-control-attribute", log.Fields{"uni-inst": instCtl.Uni})
+ return errors.New("invalid-uni-instance-ctl-attr")
+ }
+
+ if instCtl.Uni == "multi-instance" {
+ log.Error("uni-multi-instance-tp-not-supported")
+ return errors.New("uni-multi-instance-tp-not-supported")
+ }
+
+ return nil
+}
+
+func (t *TechProfileMgr) allocateTPInstance(uniPortName string, tp *DefaultTechProfile, intfId uint32) *TechProfile {
var usGemPortAttributeList []iGemPortAttribute
var dsGemPortAttributeList []iGemPortAttribute
@@ -373,13 +435,16 @@
var gemPorts []uint32
var err error
- log.Infow("Allocating TechProfileMgr instance from techprofile template", log.Fields{"uniPortName": uniPortName, "intfId": intfId, "numOfTconts": numOfTconts, "numGem": tp.NumGemPorts})
- if numOfTconts > 1 {
- log.Errorw("Multiple Tconts not supported currently", log.Fields{"uniPortName": uniPortName, "intfId": intfId})
+ log.Infow("Allocating TechProfileMgr instance from techprofile template", log.Fields{"uniPortName": uniPortName, "intfId": intfId, "numGem": tp.NumGemPorts})
+
+ err = t.validateInstanceControlAttr(tp.InstanceCtrl)
+ if err != nil {
+ log.Error("invalid-tp-instance-control-attributes")
return nil
}
- if tcontIDs, err = t.resourceMgr.GetResourceID(intfId, t.resourceMgr.GetResourceTypeAllocID(), numOfTconts); err != nil {
- log.Errorw("Error getting alloc id from rsrcrMgr", log.Fields{"intfId": intfId, "numTconts": numOfTconts})
+
+ if tcontIDs, err = t.resourceMgr.GetResourceID(intfId, t.resourceMgr.GetResourceTypeAllocID(), 1); err != nil {
+ log.Errorw("Error getting alloc id from rsrcrMgr", log.Fields{"intfId": intfId})
return nil
}
log.Debugw("Num GEM ports in TP:", log.Fields{"NumGemPorts": tp.NumGemPorts})
@@ -416,7 +481,6 @@
ProfileType: tp.ProfileType,
Version: tp.Version,
NumGemPorts: tp.NumGemPorts,
- NumTconts: numOfTconts,
InstanceCtrl: tp.InstanceCtrl,
UsScheduler: iScheduler{
AllocID: tcontIDs[0],
@@ -436,6 +500,85 @@
DownstreamGemPortAttributeList: dsGemPortAttributeList}
}
+// findAndAssignTpInstance finds out if there is another TpInstance for an ONU on a different
+// uni port for the same TP ID.
+// If it finds one:
+// 1. It will make a copy of the TpInstance
+// 2. Retain the AllocID
+// 3. Replace the GemPort IDs
+// 4. Copy the new TpInstance on the given tpPath
+// ** NOTE ** : This is to be used only when the instance control attribute is as below
+// uni: single-instance, onu: single-instance
+func (t *TechProfileMgr) findAndAssignTpInstance(tpPath string) (*TechProfile, error) {
+ var tpInst TechProfile
+ var foundValidTpInst = false
+ var gemPortIDs []uint32
+ var intfID uint64
+ var err error
+
+ // For example:
+ // tpPath like "service/voltha/technology_profiles/xgspon/64/pon-{0}/onu-{1}/uni-{1}"
+ // is broken into ["service/voltha/technology_profiles/xgspon/64/pon-{0}/onu-{1}" ""]
+ uniPathSlice := regexp.MustCompile(`/uni-{[0-9]+}$`).Split(tpPath, 2)
+ kvPairs, _ := t.config.KVBackend.List(uniPathSlice[0])
+
+ // Find the PON interface ID from the TP Path
+ var tpPathRgx = regexp.MustCompile(`pon-{([0-9]+)}`)
+ intfStrMatch := tpPathRgx.FindStringSubmatch(tpPath)
+ if intfStrMatch == nil {
+ log.Error("could-not-find-pon-intf-id-in-tp-path")
+ return nil, errors.New("could-not-find-pon-intf-id-in-tp-path")
+ } else {
+ if intfID, err = strconv.ParseUint(intfStrMatch[1], 10, 64); err != nil {
+ log.Errorw("error-converting-pon-intfid-str-to-unint", log.Fields{"intfIdStr": intfStrMatch[1]})
+ return nil, errors.New("error-converting-pon-intfid-str-to-unint")
+ }
+ }
+
+ // Find a valid TP Instance among all the UNIs of that ONU for the given TP ID
+ for keyPath, kvPair := range kvPairs {
+ if value, err := kvstore.ToByte(kvPair.Value); err == nil {
+ if err = json.Unmarshal(value, &tpInst); err != nil {
+ log.Errorw("error-unmarshal-kv-pair", log.Fields{"keyPath": keyPath, "value": value})
+ return nil, errors.New("error-unmarshal-kv-pair")
+ } else {
+ log.Debugw("found-valid-tp-instance-on-another-uni", log.Fields{"keyPath": keyPath})
+ foundValidTpInst = true
+ break
+ }
+ }
+ }
+ if foundValidTpInst {
+ // Get new GemPort IDs
+ if gemPortIDs, err = t.resourceMgr.GetResourceID(uint32(intfID), t.resourceMgr.GetResourceTypeGemPortID(), tpInst.NumGemPorts); err != nil {
+ log.Errorw("gem-port-assignment-failed", log.Fields{"intfId": intfID, "numGemports": tpInst.NumGemPorts})
+ return nil, errors.New("gem-port-assignment-failed")
+ }
+ // Update the new GemPort IDs to the TpInstance
+ for i := 0; i < int(tpInst.NumGemPorts); i++ {
+ tpInst.DownstreamGemPortAttributeList[i].GemportID = gemPortIDs[i]
+ tpInst.UpstreamGemPortAttributeList[i].GemportID = gemPortIDs[i]
+ }
+
+ tpInstanceJson, err := json.Marshal(tpInst)
+ if err == nil {
+ // Backend will convert JSON byte array into string format
+ log.Debugw("store-tp-instance", log.Fields{"tpPath": tpPath, "val": tpInstanceJson})
+ if err = t.config.KVBackend.Put(tpPath, tpInstanceJson); err != nil {
+ return nil, errors.New("error-store-instance-on-kv")
+ }
+ // We have succesfully placed the new TP Instance if we land here.
+ log.Debugw("successfully-placed-single-instance-tp-for-tp-path", log.Fields{"tpPath": tpPath})
+ return &tpInst, nil
+ } else {
+ log.Errorw("error-marshal-tp-to-json", log.Fields{"tpPath": tpPath, "tpInstance": tpInst})
+ return nil, errors.New("error-marshal-tp-to-json")
+ }
+ }
+ log.Debug("no-pre-existing-tp-instance-found-on-another-uni")
+ return nil, nil
+}
+
func (t *TechProfileMgr) getDefaultTechProfile() *DefaultTechProfile {
var usGemPortAttributeList []GemPortAttribute
@@ -690,3 +833,25 @@
log.Errorw("No-GemportId-Found-For-Pcp", log.Fields{"pcpVlan": pbit})
return 0
}
+
+// FindAllTpInstances returns all TechProfile instances for a given TechProfile table-id, pon interface ID and onu ID.
+func (t *TechProfileMgr) FindAllTpInstances(techProfiletblID uint32, ponIntf uint32, onuID uint32) []TechProfile {
+ var tp TechProfile
+ onuTpInstancePath := fmt.Sprintf("%d/%s/pon-{%d}/onu-{%d}", techProfiletblID, t.resourceMgr.GetTechnology(), ponIntf, onuID)
+
+ if kvPairs, _ := t.config.KVBackend.List(onuTpInstancePath); kvPairs != nil {
+ tpInstances := make([]TechProfile, 0, len(kvPairs))
+ for kvPath, kvPair := range kvPairs {
+ if value, err := kvstore.ToByte(kvPair.Value); err == nil {
+ if err = json.Unmarshal(value, &tp); err != nil {
+ log.Errorw("error-unmarshal-kv-pair", log.Fields{"kvPath": kvPath, "value": value})
+ continue
+ } else {
+ tpInstances = append(tpInstances, tp)
+ }
+ }
+ }
+ return tpInstances
+ }
+ return nil
+}
diff --git a/pkg/techprofile/tech_profile_if.go b/pkg/techprofile/tech_profile_if.go
index 3267759..a77ea45 100644
--- a/pkg/techprofile/tech_profile_if.go
+++ b/pkg/techprofile/tech_profile_if.go
@@ -34,4 +34,5 @@
ShapingCfg *tp_pb.TrafficShapingInfo) *tp_pb.TrafficScheduler
GetTrafficQueues(tp *TechProfile, Dir tp_pb.Direction) []*tp_pb.TrafficQueue
GetGemportIDForPbit(tp *TechProfile, Dir tp_pb.Direction, pbit uint32) uint32
+ FindAllTpInstances(techProfiletblID uint32, ponIntf uint32, onuID uint32) []TechProfile
}