diff --git a/translib/common_app.go b/translib/common_app.go index 5be38428d..da308db17 100644 --- a/translib/common_app.go +++ b/translib/common_app.go @@ -28,6 +28,7 @@ import ( "github.com/Azure/sonic-mgmt-common/translib/db" "github.com/Azure/sonic-mgmt-common/translib/ocbinds" + "github.com/Azure/sonic-mgmt-common/translib/path" "github.com/Azure/sonic-mgmt-common/translib/tlerr" "github.com/Azure/sonic-mgmt-common/translib/transformer" "github.com/Azure/sonic-mgmt-common/translib/utils" @@ -133,12 +134,223 @@ func (app *CommonApp) translateGet(dbs [db.MaxDB]*db.DB) error { return err } -func (app *CommonApp) translateSubscribe(req translateSubRequest) (translateSubResponse, error) { - return emptySubscribeResponse(req.path) +func (app *CommonApp) translateSubscribe(req *translateSubRequest) (*translateSubResponse, error) { + txCache := new(sync.Map) + reqIdLogStr := "subReq Id:[" + fmt.Sprintf("%v", req.ctxID) + "] : " + if log.V(4) { + log.Info(reqIdLogStr, "tranlateSubscribe:path", req.path) + } + subMode := transformer.NotificationType(req.mode) + subReqXlator, err := transformer.NewSubscribeReqXlator(req.ctxID, req.path, subMode, req.dbs, txCache) + if err != nil { + if log.V(4) { + log.Warning(reqIdLogStr, "tranlateSubscribe:Error in initializing the SubscribeReqXlator for the subscribe path request: ", req.path) + } + return nil, err + } + + if err = subReqXlator.Translate(!req.recurse); err != nil { + if log.V(4) { + log.Warning(reqIdLogStr, "translateSubscribe: Error in processing the subscribe path request: ", req.path) + } + return nil, err + } + + subsReqXlateInfo, err := subReqXlator.GetSubscribeReqXlateInfo() + if err != nil { + return nil, err + } + + if uriPath, err := ygot.PathToString(subsReqXlateInfo.TrgtPathInfo.Path); err == nil { + if log.V(4) { + log.Info(reqIdLogStr, "translateSubscribe: subsReqXlateInfo.TrgtPathInfo.path: ", uriPath) + } + } else { + if log.V(4) { + log.Warning(reqIdLogStr, "translateSubscribe: subsReqXlateInfo.TrgtPathInfo.path: Error in converting the gnmi path: ", *subsReqXlateInfo.TrgtPathInfo.Path) + } + } + + if log.V(4) { + log.Info(reqIdLogStr, "translateSubscribe: subsReqXlateInfo.TrgtPathInfo: ", subsReqXlateInfo.TrgtPathInfo.DbKeyXlateInfo) + } + ntfSubsAppInfo := new(translateSubResponse) + + for _, dbKeyInfo := range subsReqXlateInfo.TrgtPathInfo.DbKeyXlateInfo { + if log.V(4) { + log.Info(reqIdLogStr, "translateSubscribe: Target node: DbNum: ", dbKeyInfo.DbNum) + } + if dbKeyInfo.Table != nil { + if log.V(4) { + log.Info(reqIdLogStr, "Target node: pathXlateInfo.Table: ", *dbKeyInfo.Table) + } + } + if dbKeyInfo.Key != nil { + if log.V(4) { + log.Info(reqIdLogStr, "Target node: pathXlateInfo.Key: ", *dbKeyInfo.Key) + } + } + + ntfAppInfo := notificationAppInfo{ + table: dbKeyInfo.Table, + key: dbKeyInfo.Key, + dbno: dbKeyInfo.DbNum, + path: subsReqXlateInfo.TrgtPathInfo.Path, + handlerFunc: subsReqXlateInfo.TrgtPathInfo.HandlerFunc, + deleteAction: dbKeyInfo.DeleteAction, + fieldScanPattern: dbKeyInfo.FieldScanPatt, + keyGroupComps: dbKeyInfo.KeyGroupComps, + isDataSrcDynamic: subsReqXlateInfo.TrgtPathInfo.IsDataSrcDynamic, + } + + if log.V(4) { + log.Info(reqIdLogStr, "translateSubscribe: Target node: ntfAppInfo.deleteAction: ", ntfAppInfo.deleteAction) + } + + pType := subsReqXlateInfo.TrgtPathInfo.PType + if subsReqXlateInfo.TrgtPathInfo.OnChange != transformer.OnchangeEnable && dbKeyInfo.DbNum == db.CountersDB { + pType = transformer.Sample + } + + if pType == transformer.Sample { + ntfAppInfo.pType = Sample + ntfAppInfo.mInterval = subsReqXlateInfo.TrgtPathInfo.MinInterval + } else if subsReqXlateInfo.TrgtPathInfo.OnChange == transformer.OnchangeEnable || subsReqXlateInfo.TrgtPathInfo.OnChange == transformer.OnchangeDefault { + ntfAppInfo.isOnChangeSupported = true + ntfAppInfo.pType = OnChange + } + + for _, dbFldMapInfo := range dbKeyInfo.DbFldYgMapList { + if log.V(4) { + log.Info(reqIdLogStr, "translateSubscribe: Target node: RelPath: ", dbFldMapInfo.RltvPath, + "; db field YANG map: ", dbFldMapInfo.DbFldYgPathMap) + } + dbFldInfo := dbFldYgPathInfo{dbFldMapInfo.RltvPath, dbFldMapInfo.DbFldYgPathMap} + ntfAppInfo.dbFldYgPathInfoList = append(ntfAppInfo.dbFldYgPathInfoList, &dbFldInfo) + } + + if log.V(4) { + log.Info(reqIdLogStr, "translateSubscribe: target node: ntfAppInfo.path: ", ntfAppInfo.path, + "; ntfAppInfo.isOnChangeSupported: ", ntfAppInfo.isOnChangeSupported, "; ntfAppInfo.table: ", + ntfAppInfo.table, "; ntfAppInfo.key: ", ntfAppInfo.key) + } + for _, pathInfoList := range ntfAppInfo.dbFldYgPathInfoList { + if log.V(4) { + log.Info(reqIdLogStr, "translateSubscribe: target node: ntfAppInfo.dbFldYgPathInfoList entry: ", pathInfoList) + } + } + if log.V(4) { + log.Info(reqIdLogStr, "translateSubscribe: target node: ntfAppInfo.dbno: ", ntfAppInfo.dbno, + "; ntfAppInfo.mInterval: : ", ntfAppInfo.mInterval, "; ntfAppInfo.pType: ", ntfAppInfo.pType, + "; ntfAppInfo.fieldScanPattern: ", ntfAppInfo.fieldScanPattern, "; ntfAppInfo.opaque: ", ntfAppInfo.opaque, "isDataSrcDynamic: ", subsReqXlateInfo.TrgtPathInfo.IsDataSrcDynamic) + } + ntfSubsAppInfo.ntfAppInfoTrgt = append(ntfSubsAppInfo.ntfAppInfoTrgt, &ntfAppInfo) + if log.V(4) { + log.Info(reqIdLogStr, "translateSubscribe: target node ===========================================") + } + } + + for _, pathXlateInfo := range subsReqXlateInfo.ChldPathsInfo { + if uriPath, err := ygot.PathToString(pathXlateInfo.Path); err == nil { + if log.V(4) { + log.Info(reqIdLogStr, "translateSubscribe: ChldPathsInfo: path: ", uriPath) + } + } else { + log.Warning(reqIdLogStr, "translateSubscribe: ChldPathsInfo: Error in converting the gnmi path: ", *pathXlateInfo.Path) + } + if log.V(4) { + log.Info(reqIdLogStr, "translateSubscribe: ChldPathsInfo.pathXlateInfo.DbKeyXlateInfo: ", pathXlateInfo.DbKeyXlateInfo) + } + for _, dbKeyInfo := range pathXlateInfo.DbKeyXlateInfo { + if log.V(4) { + log.Info(reqIdLogStr, "translateSubscribe: child node: DbNum: ", dbKeyInfo.DbNum) + } + if dbKeyInfo.Table != nil && log.V(4) { + log.Info(reqIdLogStr, "child node: pathXlateInfo.Table: ", *dbKeyInfo.Table) + } + if dbKeyInfo.Key != nil && log.V(4) { + log.Info(reqIdLogStr, "child node: pathXlateInfo.Key: ", *dbKeyInfo.Key) + } + ntfAppInfo := notificationAppInfo{ + table: dbKeyInfo.Table, + key: dbKeyInfo.Key, + dbno: dbKeyInfo.DbNum, + path: pathXlateInfo.Path, + handlerFunc: pathXlateInfo.HandlerFunc, + deleteAction: dbKeyInfo.DeleteAction, + fieldScanPattern: dbKeyInfo.FieldScanPatt, + keyGroupComps: dbKeyInfo.KeyGroupComps, + isDataSrcDynamic: pathXlateInfo.IsDataSrcDynamic, + } + if log.V(4) { + log.Info(reqIdLogStr, "translateSubscribe: child node: ntfAppInfo.deleteAction: ", ntfAppInfo.deleteAction) + } + pType := pathXlateInfo.PType + if pathXlateInfo.OnChange != transformer.OnchangeEnable && dbKeyInfo.DbNum == db.CountersDB { + pType = transformer.Sample + } + if pType == transformer.Sample { + ntfAppInfo.pType = app.translateNotificationType(pType) + ntfAppInfo.mInterval = pathXlateInfo.MinInterval + } else if pathXlateInfo.OnChange == transformer.OnchangeEnable || pathXlateInfo.OnChange == transformer.OnchangeDefault { + ntfAppInfo.isOnChangeSupported = true + ntfAppInfo.pType = OnChange + } + for _, dbFldMapInfo := range dbKeyInfo.DbFldYgMapList { + if log.V(4) { + log.Info(reqIdLogStr, "translateSubscribe: child node: RelPath: ", dbFldMapInfo.RltvPath, + "; dbFldMapInfo.DbFldYgPathMap: ", dbFldMapInfo.DbFldYgPathMap) + } + dbFldInfo := dbFldYgPathInfo{dbFldMapInfo.RltvPath, dbFldMapInfo.DbFldYgPathMap} + ntfAppInfo.dbFldYgPathInfoList = append(ntfAppInfo.dbFldYgPathInfoList, &dbFldInfo) + } + if log.V(4) { + log.Info(reqIdLogStr, "translateSubscribe: child node: ntfAppInfo.path: ", ntfAppInfo.path, + "; ntfAppInfo.isOnChangeSupported: ", ntfAppInfo.isOnChangeSupported, "; ntfAppInfo.table: ", + ntfAppInfo.table, "; ntfAppInfo.key: ", ntfAppInfo.key) + } + for _, pathInfoList := range ntfAppInfo.dbFldYgPathInfoList { + if log.V(4) { + log.Info(reqIdLogStr, "translateSubscribe: child node: ntfAppInfo.dbFldYgPathInfoList entry: ", pathInfoList) + } + } + if log.V(4) { + log.Info(reqIdLogStr, "translateSubscribe: child node: ntfAppInfo.dbno: ", ntfAppInfo.dbno, + "; ntfAppInfo.mInterval: : ", ntfAppInfo.mInterval, "; ntfAppInfo.pType: ", ntfAppInfo.pType, + "; ntfAppInfo.fieldScanPattern: ", ntfAppInfo.fieldScanPattern, "; ntfAppInfo.opaque: ", ntfAppInfo.opaque, "; isDataSrcDynamic: ", pathXlateInfo.IsDataSrcDynamic) + } + if len(subsReqXlateInfo.TrgtPathInfo.DbKeyXlateInfo) == 0 && pathXlateInfo.TrgtNodeChld { + if log.V(4) { + log.Info(reqIdLogStr, "translateSubscribe: Added the child node notification app info into targt app info for the path: ", pathXlateInfo.Path) + } + ntfSubsAppInfo.ntfAppInfoTrgt = append(ntfSubsAppInfo.ntfAppInfoTrgt, &ntfAppInfo) + } else { + ntfSubsAppInfo.ntfAppInfoTrgtChlds = append(ntfSubsAppInfo.ntfAppInfoTrgtChlds, &ntfAppInfo) + } + if log.V(4) { + log.Info(reqIdLogStr, "translateSubscribe: child node =========================================") + } + } + } + if len(ntfSubsAppInfo.ntfAppInfoTrgt) == 0 && (!path.HasWildcardKey(subsReqXlateInfo.TrgtPathInfo.Path) && + subsReqXlateInfo.TrgtPathInfo.PType == transformer.Sample) { + ntfAppInfo := ¬ificationAppInfo{path: subsReqXlateInfo.TrgtPathInfo.Path, pType: Sample, mInterval: subsReqXlateInfo.TrgtPathInfo.MinInterval} + if log.V(4) { + log.Info(reqIdLogStr, "translateSubscribe: no table mapping: non wild card path; sample mode - notificationAppInfo:", ntfAppInfo.String()) + } + ntfSubsAppInfo.ntfAppInfoTrgt = append(ntfSubsAppInfo.ntfAppInfoTrgt, ntfAppInfo) + } + if log.V(4) { + log.Info(reqIdLogStr, "translateSubscribe: ntfSubsAppInfo: ", ntfSubsAppInfo) + } + return ntfSubsAppInfo, nil } -func (app *CommonApp) processSubscribe(req processSubRequest) (processSubResponse, error) { - return processSubResponse{}, tlerr.New("not implemented") +func (app *CommonApp) translateNotificationType(t transformer.NotificationType) NotificationType { + if t == transformer.Sample { + return Sample + } + return OnChange } func (app *CommonApp) translateAction(dbs [db.MaxDB]*db.DB) error { @@ -309,6 +521,24 @@ func (app *CommonApp) processAction(dbs [db.MaxDB]*db.DB) (ActionResponse, error return resp, err } +func (app *CommonApp) processSubscribe(param *processSubRequest) (processSubResponse, error) { + var resp processSubResponse + + subNotfRespXlator, err := transformer.NewSubscribeNotfRespXlator(param.ctxID, param.path, param.dbno, param.table, param.key, param.entry, param.dbs, param.opaque) + if err != nil { + log.Warning("processSubscribe: Error in getting the NewSubscribeNotfRespXlator; error: ", err) + return resp, err + } + if log.V(4) { + log.Info("processSubscribe: subNotfRespXlator: ", *subNotfRespXlator) + } + if resp.path, err = subNotfRespXlator.Translate(); err != nil { + log.Warning("processSubscribe: Error in translating the subscribe notification; error: ", err) + return resp, err + } + return resp, nil +} + func (app *CommonApp) translateCRUDCommon(d *db.DB, opcode int) ([]db.WatchKeys, error) { var err error var keys []db.WatchKeys diff --git a/translib/transformer/subscribe_req_xlate.go b/translib/transformer/subscribe_req_xlate.go index 367c95f14..5e5f66251 100644 --- a/translib/transformer/subscribe_req_xlate.go +++ b/translib/transformer/subscribe_req_xlate.go @@ -185,7 +185,7 @@ func (pathXltr *subscribePathXlator) setTrgtYgXpathInfo() error { func (pathXlateInfo *XfmrSubscribePathXlateInfo) addPathXlateInfo(tblSpec *db.TableSpec, dbKey *db.Key, dBNum db.DBNum) *dbTableKeyInfo { dbTblInfo := dbTableKeyInfo{Table: tblSpec, Key: dbKey, DbNum: dBNum} pathXlateInfo.DbKeyXlateInfo = append(pathXlateInfo.DbKeyXlateInfo, &dbTblInfo) - if (pathXlateInfo.ygXpathInfo.yangType == YNG_LEAF || pathXlateInfo.ygXpathInfo.yangType == YNG_LEAF_LIST) && + if (pathXlateInfo.ygXpathInfo.yangType == YANG_LEAF || pathXlateInfo.ygXpathInfo.yangType == YANG_LEAF_LIST) && pathXlateInfo.ygXpathInfo.subscriptionFlags.Has(subsDelAsUpdate) { dbTblInfo.DeleteAction = apis.InspectPathOnDelete } @@ -1285,7 +1285,7 @@ func (reqXlator *subscribeReqXlator) translateChildNodePaths(ygXpathInfo *yangXp } func (pathXlateInfo *XfmrSubscribePathXlateInfo) isSamePathXlateInfo(parentPathXlateInfo *XfmrSubscribePathXlateInfo) bool { - if (pathXlateInfo.ygXpathInfo.yangType == YNG_LEAF || pathXlateInfo.ygXpathInfo.yangType == YNG_LEAF_LIST) && + if (pathXlateInfo.ygXpathInfo.yangType == YANG_LEAF || pathXlateInfo.ygXpathInfo.yangType == YANG_LEAF_LIST) && pathXlateInfo.ygXpathInfo.subscriptionFlags.Has(subsDelAsUpdate) { return false } @@ -1432,7 +1432,7 @@ func (reqXlator *subscribeReqXlator) traverseYgXpathAndTranslate(ygXpNode *ygXpa } } - if chldNode.ygXpathInfo.yangType != YNG_LIST && parentPathXlateInfo.hasDbTableInfo() { + if chldNode.ygXpathInfo.yangType != YANG_LIST && parentPathXlateInfo.hasDbTableInfo() { // other than list node, that is for the container / leaf / leaf-list node // the db key entry of the parent list node's table db key will be used as the table // key for the container/leaf/leaf-list node's table @@ -1849,8 +1849,7 @@ func (reqXlator *subscribeReqXlator) uriToAbsolutePath(rltvUri string) (*gnmipb. } func debugPrintXPathInfo(xpathInfo *yangXpathInfo) { - // TODO:INTG_CHANGES below commented code will be reverted during the code integration - //log.Infof(" yangType: %v\r\n", getYangTypeStrId(xpathInfo.yangType)) + log.Infof(" yangType: %v\r\n", getYangTypeStrId(xpathInfo.yangType)) log.Info(" fieldName: ", xpathInfo.fieldName) if xpathInfo.nameWithMod != nil { log.Infof(" nameWithMod : %v\r\n", *xpathInfo.nameWithMod) @@ -1924,11 +1923,9 @@ func debugPrintXPathInfo(xpathInfo *yangXpathInfo) { func getYgEntry(reqLogId string, ygXpath *yangXpathInfo, ygPath string) (*yang.Entry, error) { ygEntry := ygXpath.yangEntry - // TODO:INTG_CHANGES below commented lines will be reverted during the code integration - // since this function "getYangEntryForXPath" has to be merged first - //if ygEntry == nil && (ygXpath.yangType == YNG_LEAF || ygXpath.yangType == YNG_LEAF_LIST) { - // ygEntry = getYangEntryForXPath(ygPath) - //} + if ygEntry == nil && (ygXpath.yangType == YANG_LEAF || ygXpath.yangType == YANG_LEAF_LIST) { + ygEntry = getYangEntryForXPath(ygPath) + } if ygEntry == nil { if log.V(dbLgLvl) { log.Warningf("%v : yangEntry is nil in the yangXpathInfo for the path:", reqLogId, ygPath) @@ -1938,15 +1935,6 @@ func getYgEntry(reqLogId string, ygXpath *yangXpathInfo, ygPath string) (*yang.E return ygEntry, nil } -// TODO:INTG_CHANGES below const. are temporary changes, will be removed during the code integration -// reference to YNG_LEAF_LIST, YNG_LEAF in this file will be replaced with YANG_LEAF_LIST, YANG_LEAF -const ( - YNG_LIST yangElementType = iota + 2 - YNG_CONTAINER - YNG_LEAF - YNG_LEAF_LIST -) - func (keyRslvr *DbYangKeyResolver) handleValueXfmr(xfmrName string, operation Operation, keyName string, keyVal string) (keyLeafVal string, err error) { if log.V(dbLgLvl) { log.Info(keyRslvr.reqLogId, "resolveDbKey: keyLeafRefNode xfmrValue; ", xfmrName) @@ -2107,11 +2095,9 @@ func (keyRslvr *DbYangKeyResolver) getDbYangListInfo(listName string) (*dbInfo, func getYgDbEntry(reqLogId string, ygDbInfo *dbInfo, ygPath string) (*yang.Entry, error) { ygEntry := ygDbInfo.dbEntry - // TODO:INTG_CHANGES below commented lines will be reverted during the code integration - // since this function "getYangEntryForXPath" has to be merged first - //if ygEntry == nil && (ygDbInfo.yangType == YANG_LEAF || ygDbInfo.yangType == YANG_LEAF_LIST) { - // ygEntry = getYangEntryForXPath(ygPath) - //} + if ygEntry == nil && (ygDbInfo.yangType == YANG_LEAF || ygDbInfo.yangType == YANG_LEAF_LIST) { + ygEntry = getYangEntryForXPath(ygPath) + } if ygEntry == nil { if log.V(dbLgLvl) { log.Warningf("%v : yangEntry is nil in the yangXpathInfo for the path:", reqLogId, ygPath) diff --git a/translib/transformer/subscribe_resp_xlate.go b/translib/transformer/subscribe_resp_xlate.go new file mode 100644 index 000000000..a731405f9 --- /dev/null +++ b/translib/transformer/subscribe_resp_xlate.go @@ -0,0 +1,456 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2020 Broadcom. The term Broadcom refers to Broadcom Inc. and/or // +// its subsidiaries. // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +//////////////////////////////////////////////////////////////////////////////// + +package transformer + +import ( + "fmt" + "reflect" + "strings" + "sync" + + "github.com/Azure/sonic-mgmt-common/translib/db" + "github.com/Azure/sonic-mgmt-common/translib/ocbinds" + "github.com/Azure/sonic-mgmt-common/translib/path" + "github.com/Azure/sonic-mgmt-common/translib/tlerr" + log "github.com/golang/glog" + "github.com/openconfig/gnmi/proto/gnmi" + "github.com/openconfig/ygot/ygot" +) + +type subscribeNotfRespXlator struct { + ntfXlateReq *subscribeNotfXlateReq + dbYgXlateList []*DbYgXlateInfo +} + +type subscribeNotfXlateReq struct { + path *gnmi.Path + dbNum db.DBNum + table *db.TableSpec + key *db.Key + entry *db.Value + dbs [db.MaxDB]*db.DB + opaque interface{} + reqLogId string +} + +type DbYgXlateInfo struct { + pathIdx int + ygXpathInfo *yangXpathInfo + tableName string + dbKey string + uriPath string + xlateReq *subscribeNotfXlateReq +} + +func NewSubscribeNotfRespXlator(ctxID interface{}, gPath *gnmi.Path, dbNum db.DBNum, table *db.TableSpec, key *db.Key, + entry *db.Value, dbs [db.MaxDB]*db.DB, opaque interface{}) (*subscribeNotfRespXlator, error) { + reqLogId := "subNotfReq Id:[" + fmt.Sprintf("%v", ctxID) + "] : " + + if log.V(dbLgLvl) { + log.Infof("%v NewSubscribeNotfRespXlator: table: %v, key: %v, "+ + "dbno: %v, path: %v", reqLogId, table, key, dbNum, gPath) + } + + if opaque == nil || (reflect.ValueOf(opaque).Kind() == reflect.Ptr && reflect.ValueOf(opaque).IsNil()) { + opaque = new(sync.Map) + } + + xlateReq := subscribeNotfXlateReq{gPath, dbNum, table, key, entry, dbs, opaque, reqLogId} + return &subscribeNotfRespXlator{ntfXlateReq: &xlateReq}, nil +} + +func (respXlator *subscribeNotfRespXlator) Translate() (*gnmi.Path, error) { + ntfXlateReq := respXlator.ntfXlateReq + + if log.V(dbLgLvl) { + log.Info(ntfXlateReq.reqLogId, "subscribeNotfRespXlator:Translate: path: ", ntfXlateReq.path) + } + + pathElem := respXlator.ntfXlateReq.path.Elem + + for idx := len(pathElem) - 1; idx >= 0; idx-- { + + ygPath := respXlator.getYangListPath(idx) + if log.V(dbLgLvl) { + log.Info(ntfXlateReq.reqLogId, "subscribeNotfRespXlator:Translate:ygPath: ", ygPath) + } + ygXpathInfo, err := respXlator.getYangXpathInfo(ygPath) + if err != nil { + return nil, err + } + + if log.V(dbLgLvl) { + log.Info(ntfXlateReq.reqLogId, "subscribeNotfRespXlator:Translate: ygXpathInfo: ", ygXpathInfo) + } + + // for subtree, path transformr can be present at any node level + if (len(pathElem[idx].Key) == 0 || !path.HasWildcardAtKey(respXlator.ntfXlateReq.path, idx)) && len(ygXpathInfo.xfmrPath) == 0 { + continue + } + + if len(ygXpathInfo.xfmrPath) > 0 { + err = respXlator.handlePathTransformer(ygXpathInfo, idx) + if err != nil { + return nil, err + } + err = respXlator.processDbToYangKeyXfmrList() + if err != nil { + return nil, err + } + return respXlator.ntfXlateReq.path, nil + } + if ygXpathInfo.virtualTbl != nil && (*ygXpathInfo.virtualTbl) { + log.Warning(ntfXlateReq.reqLogId, "Translate: virtual table is set to true and path transformer not found list node path: ", *respXlator.ntfXlateReq.path) + return nil, tlerr.InternalError{Format: ntfXlateReq.reqLogId + "virtual table is set to true and path transformer not found list node path", Path: ygPath} + } + if len(ygXpathInfo.xfmrFunc) == 0 && len(ygXpathInfo.xfmrKey) > 0 { + dbYgXlateInfo := &DbYgXlateInfo{pathIdx: idx, ygXpathInfo: ygXpathInfo, xlateReq: respXlator.ntfXlateReq} + dbYgXlateInfo.setUriPath() + respXlator.dbYgXlateList = append(respXlator.dbYgXlateList, dbYgXlateInfo) + // since there is no path transformer defined in the path, processing the collected db to yang key xfmrs + if err = respXlator.processDbToYangKeyXfmrList(); err != nil { + log.Warning(ntfXlateReq.reqLogId, "Translate: Error in processDbToYangKeyXfmrList for the path: ", *respXlator.ntfXlateReq.path) + return nil, err + } + } else { + if len(ygXpathInfo.xfmrFunc) > 0 { + if log.V(dbLgLvl) { + log.Warning(ntfXlateReq.reqLogId, "Translate: Could not find the path transformer for the xpath: ", ygPath) + } + } else if log.V(dbLgLvl) { + log.Warning(ntfXlateReq.reqLogId, "Translate: Could not find the DbToYangKey transformer for the xpath: ", ygPath) + } + + if log.V(dbLgLvl) { + log.Warningf("%v Translate: Attempting direct conversion from db key %v to yang key %v directly"+ + " for the path: %v", ntfXlateReq.reqLogId, respXlator.ntfXlateReq.key.Comp, pathElem[idx].Key, ygPath) + } + dbKeyComp := respXlator.ntfXlateReq.key.Comp + tblName := "" + if ygXpathInfo.tableName != nil { + tblName = *ygXpathInfo.tableName + } + + if dbInfo, ok := xDbSpecMap[tblName]; !ok || dbInfo == nil { + err = fmt.Errorf("error: direct conversion from db key to yang key is not supported for the"+ + " path %v, since there is no sonic yang model for this table %v; need path transformer to"+ + " translate key", ygXpathInfo.dbIndex, ygPath, tblName) + log.Warning(ntfXlateReq.reqLogId, err) + return nil, err + } + + dbKeyRslvr := &DbYangKeyResolver{tableName: tblName, key: respXlator.ntfXlateReq.key, + dbs: respXlator.ntfXlateReq.dbs, dbIdx: respXlator.ntfXlateReq.dbNum, uriPath: ygPath, reqLogId: respXlator.ntfXlateReq.reqLogId} + dbKeyComp, err = dbKeyRslvr.resolve(GET) + if err != nil { + return nil, tlerr.InternalError{Format: respXlator.ntfXlateReq.reqLogId + "Translate: Error: " + err.Error(), Path: ygPath} + } + //yang key can be part of the db key, where db key is from child table db key + if len(pathElem[idx].Key) > len(dbKeyComp) { + log.Warning(ntfXlateReq.reqLogId, "Translate: Could not find the path transformer or DbToYangKey transformer for the ygXpathListInfo: ", ygPath) + return nil, tlerr.InternalError{Format: ntfXlateReq.reqLogId + "Could not find the path transformer or DbToYangKey transformer", Path: ygPath} + } + dbYgListKeyNames, err := dbKeyRslvr.getMatchingDbYangListKeyNames(pathElem[idx].Key) + if err != nil { + return nil, tlerr.InternalError{Format: "Translate: Error: " + err.Error(), Path: ygPath} + } + dbKeyIdx := 0 + for _, dbKeyNm := range dbYgListKeyNames { + pathElem[idx].Key[dbKeyNm] = dbKeyComp[dbKeyIdx] + dbKeyIdx++ + } + } + } + + log.Info(ntfXlateReq.reqLogId, "subscribeNotfRespXlator: translated path: ", *respXlator.ntfXlateReq.path) + return respXlator.ntfXlateReq.path, nil +} + +func (respXlator *subscribeNotfRespXlator) handlePathTransformer(ygXpathInfo *yangXpathInfo, pathIdx int) error { + var currPath gnmi.Path + pathElems := respXlator.ntfXlateReq.path.Elem + ygSchemPath := "/" + pathElems[0].Name + currPath.Elem = append(currPath.Elem, pathElems[0]) + + for idx := 1; idx <= pathIdx; idx++ { + ygSchemPath = ygSchemPath + "/" + pathElems[idx].Name + currPath.Elem = append(currPath.Elem, pathElems[idx]) + } + + inParam := XfmrDbToYgPathParams{ + yangPath: &currPath, + subscribePath: respXlator.ntfXlateReq.path, + ygSchemaPath: ygSchemPath, + tblName: respXlator.ntfXlateReq.table.Name, + tblKeyComp: respXlator.ntfXlateReq.key.Comp, + tblEntry: respXlator.ntfXlateReq.entry, + dbNum: respXlator.ntfXlateReq.dbNum, + dbs: respXlator.ntfXlateReq.dbs, + db: respXlator.ntfXlateReq.dbs[respXlator.ntfXlateReq.dbNum], + ygPathKeys: make(map[string]string), + } + + if err := respXlator.xfmrPathHandlerFunc("DbToYangPath_"+ygXpathInfo.xfmrPath, inParam); err != nil { + return fmt.Errorf(respXlator.ntfXlateReq.reqLogId+"Error in path transformer callback : %v for"+ + " the gnmi path: %v, and the error: %v", ygXpathInfo.xfmrPath, respXlator.ntfXlateReq.path, err) + } + + if log.V(dbLgLvl) { + log.Info(respXlator.ntfXlateReq.reqLogId, "handlePathTransformer: uriPathKeysMap: ", inParam.ygPathKeys) + } + ygpath := "/" + respXlator.ntfXlateReq.path.Elem[0].Name + + for idx := 1; idx <= pathIdx; idx++ { + ygpath = ygpath + "/" + respXlator.ntfXlateReq.path.Elem[idx].Name + + if log.V(dbLgLvl) { + log.Info(respXlator.ntfXlateReq.reqLogId, "handlePathTransformer: yang map keys: yang path:", ygpath) + } + + for keyName, keyVal := range respXlator.ntfXlateReq.path.Elem[idx].Key { + if keyVal != "*" { + continue + } + if log.V(dbLgLvl) { + log.Info(respXlator.ntfXlateReq.reqLogId, "handlePathTransformer: yang map keys: yang key path:", ygpath, "/", keyName) + } + ygKeyVal, ok := inParam.ygPathKeys[ygpath+"/"+keyName] + if !ok { + return fmt.Errorf(respXlator.ntfXlateReq.reqLogId+"Error: path transformer callback (%v)"+ + " response yang key map does not have the yang key value for the yang key: %v ", + ygXpathInfo.xfmrPath, ygpath+"/"+keyName) + } + respXlator.ntfXlateReq.path.Elem[idx].Key[keyName] = ygKeyVal + } + } + + return nil +} + +func (respXlator *subscribeNotfRespXlator) xfmrPathHandlerFunc(xfmrPathFunc string, inParam XfmrDbToYgPathParams) error { + if log.V(dbLgLvl) { + log.Infof(respXlator.ntfXlateReq.reqLogId+"Received inParam %v, Path transformer function name %v", inParam, xfmrPathFunc) + } + + retVals, err := XlateFuncCall(xfmrPathFunc, inParam) + if err != nil { + return err + } + if retVals == nil || len(retVals) != PATH_XFMR_RET_ARGS { + return tlerr.InternalError{Format: "incorrect return type in the transformer call back function", Path: inParam.yangPath.String()} + } + if retVals[PATH_XFMR_RET_ERR_INDX].Interface() != nil { + if err = retVals[PATH_XFMR_RET_ERR_INDX].Interface().(error); err != nil { + return err + } + } + return nil +} + +func (respXlator *subscribeNotfRespXlator) processDbToYangKeyXfmrList() error { + for idx := len(respXlator.dbYgXlateList) - 1; idx >= 0; idx-- { + if err := respXlator.dbYgXlateList[idx].handleDbToYangKeyXlate(); err != nil { + log.Warningf(respXlator.ntfXlateReq.reqLogId+"handleDbToYangKeyXlate: Error: %v for the ygPathTmp: %v ", + err, respXlator.dbYgXlateList[idx].uriPath) + } + } + return nil +} + +func (respXlator *subscribeNotfRespXlator) getYangListPath(listIdx int) string { + ygPathTmp := "" + for idx := 0; idx <= listIdx; idx++ { + pathName := respXlator.ntfXlateReq.path.Elem[idx].Name + if idx > 0 { + pathNames := strings.Split(respXlator.ntfXlateReq.path.Elem[idx].Name, ":") + if len(pathNames) > 1 { + pathName = pathNames[1] + } + } + ygPathTmp = ygPathTmp + "/" + pathName + } + if log.V(dbLgLvl) { + log.Infof(respXlator.ntfXlateReq.reqLogId+"getYangListPath: listIdx: %v, ygPathTmp: %v ", listIdx, ygPathTmp) + } + return ygPathTmp +} + +func (dbYgXlateInfo *DbYgXlateInfo) setUriPath() { + for idx := 0; idx <= dbYgXlateInfo.pathIdx; idx++ { + dbYgXlateInfo.uriPath = dbYgXlateInfo.uriPath + "/" + dbYgXlateInfo.xlateReq.path.Elem[idx].Name + for kn, kv := range dbYgXlateInfo.xlateReq.path.Elem[idx].Key { + // not including the wildcard in the path; since it will be sent to db to yang key xfmr + if kv == "*" { + continue + } + dbYgXlateInfo.uriPath = dbYgXlateInfo.uriPath + "[" + kn + "=" + kv + "]" + } + } +} + +func (respXlator *subscribeNotfRespXlator) getYangXpathInfo(ygPath string) (*yangXpathInfo, error) { + ygXpathListInfo, ok := xYangSpecMap[ygPath] + + if !ok || ygXpathListInfo == nil { + log.Warning(respXlator.ntfXlateReq.reqLogId, "ygXpathInfo data not found in the xYangSpecMap for xpath : ", ygPath) + return nil, tlerr.InternalError{Format: respXlator.ntfXlateReq.reqLogId + "Error in processing the subscribe path", Path: ygPath} + } + if _, ygErr := getYgEntry(respXlator.ntfXlateReq.reqLogId, ygXpathListInfo, ygPath); ygErr != nil { + return nil, tlerr.NotSupportedError{Format: respXlator.ntfXlateReq.reqLogId + "Subscribe not supported", Path: ygPath} + } + return ygXpathListInfo, nil +} + +func (keyRslvr *DbYangKeyResolver) getMatchingDbYangListKeyNames(ygListKey map[string]string) ([]string, error) { + ygDbInfo, _, err := keyRslvr.getDbYangNode() + if err != nil { + log.Warning(err) + return nil, err + } + for _, listName := range ygDbInfo.listName { + _, ygDbListNode, err := keyRslvr.getDbYangListInfo(listName) + if err != nil { + log.Warning(err) + return nil, err + } + if !ygDbListNode.IsList() { + if log.V(dbLgLvl) { + log.Infof("%v DbYangKeyResolver: resolve: list name %v is not found in the xDbSpecMap as yang list node for the table: %v", keyRslvr.reqLogId, listName, keyRslvr.tableName) + } + continue + } + keyList := strings.Fields(ygDbListNode.Key) + if log.V(dbLgLvl) { + log.Info("keyList: ", keyList) + } + isMatch := true + for _, kn := range keyList { + if _, ok := ygListKey[kn]; !ok { + isMatch = false + break + } + } + if !isMatch { + continue + } + return keyList, nil + } + err = fmt.Errorf("DbYangKeyResolver: Db yang matching list node not found for the table %v for the path: %v", keyRslvr.tableName, keyRslvr.uriPath) + log.Warning(err) + return nil, err +} + +func (dbYgXlateInfo *DbYgXlateInfo) handleDbToYangKeyXlate() error { + if dbYgXlateInfo.ygXpathInfo.tableName != nil && *dbYgXlateInfo.ygXpathInfo.tableName != "NONE" { + dbYgXlateInfo.tableName = *dbYgXlateInfo.ygXpathInfo.tableName + } else if dbYgXlateInfo.ygXpathInfo.xfmrTbl == nil { + return tlerr.InternalError{Format: dbYgXlateInfo.xlateReq.reqLogId + "Could not find the table information for the path", Path: dbYgXlateInfo.uriPath} + } + tblLst, err := dbYgXlateInfo.handleTableXfmrCallback() + if err != nil { + return fmt.Errorf("%v : Error: %v - in handleDbToYangKeyXlate; table name: %v", + dbYgXlateInfo.xlateReq.reqLogId, err, *dbYgXlateInfo.ygXpathInfo.tableName) + } + if len(tblLst) == 0 { + return fmt.Errorf("%v handleDbToYangKeyXlate: Error: No tables are returned by the table "+ + "transformer: for the path: %v", dbYgXlateInfo.xlateReq.reqLogId, dbYgXlateInfo.uriPath) + } + // taking the first table, since number of keys should be same between the tables returned by table transformer + dbYgXlateInfo.tableName = tblLst[0] + if log.V(dbLgLvl) { + log.Info(dbYgXlateInfo.xlateReq.reqLogId, "handleDbToYangKeyXlate: Found table from the table transformer: table name: ", dbYgXlateInfo.tableName) + } + dbKeyRslvr := &DbYangKeyResolver{tableName: dbYgXlateInfo.tableName, key: dbYgXlateInfo.xlateReq.key, + dbs: dbYgXlateInfo.xlateReq.dbs, dbIdx: dbYgXlateInfo.xlateReq.dbNum, uriPath: dbYgXlateInfo.uriPath, reqLogId: dbYgXlateInfo.xlateReq.reqLogId} + dbTableKeyComp, err := dbKeyRslvr.resolve(GET) + if err != nil { + return tlerr.InternalError{Format: dbYgXlateInfo.xlateReq.reqLogId + "handleDbToYangKeyXlate: Error: " + err.Error() + + "; tableName: " + dbYgXlateInfo.tableName, Path: dbYgXlateInfo.uriPath} + } + if len(dbTableKeyComp) > 0 { + dbYgXlateInfo.dbKey = dbTableKeyComp[0] + for idx := 1; idx < len(dbTableKeyComp); idx++ { + dbYgXlateInfo.dbKey = dbYgXlateInfo.dbKey + dbKeyRslvr.delim + dbTableKeyComp[idx] + } + } + return dbYgXlateInfo.handleDbToYangKeyXfmr() +} + +func (dbYgXlateInfo *DbYgXlateInfo) handleDbToYangKeyXfmr() error { + dbDataMap := make(RedisDbMap) + for i := db.ApplDB; i < db.MaxDB; i++ { + dbDataMap[i] = make(map[string]map[string]db.Value) + } + inParams := formXfmrInputRequest(dbYgXlateInfo.xlateReq.dbs[dbYgXlateInfo.xlateReq.dbNum], dbYgXlateInfo.xlateReq.dbs, dbYgXlateInfo.xlateReq.dbNum, + nil, dbYgXlateInfo.uriPath, dbYgXlateInfo.uriPath, GET, dbYgXlateInfo.dbKey, &dbDataMap, nil, nil, dbYgXlateInfo.xlateReq.opaque) + + inParams.table = dbYgXlateInfo.tableName + rmap, err := keyXfmrHandlerFunc(inParams, dbYgXlateInfo.ygXpathInfo.xfmrKey) + if err != nil { + return fmt.Errorf("%v handleDbToYangKeyXfmr: error in keyXfmrHandlerFunc: %v", dbYgXlateInfo.xlateReq.reqLogId, err) + } + if log.V(dbLgLvl) { + log.Info(dbYgXlateInfo.xlateReq.reqLogId, "handleDbToYangKeyXfmr: res map: ", rmap) + } + for k, v := range rmap { + //Assuming that always the string to be passed as the value in the DbtoYang key transformer response map + dbYgXlateInfo.xlateReq.path.Elem[dbYgXlateInfo.pathIdx].Key[k] = fmt.Sprintf("%v", v) + } + + return nil +} + +func (dbYgXlateInfo *DbYgXlateInfo) handleTableXfmrCallback() ([]string, error) { + ygXpathInfo := dbYgXlateInfo.ygXpathInfo + uriPath := dbYgXlateInfo.uriPath + + if log.V(dbLgLvl) { + log.Info(dbYgXlateInfo.xlateReq.reqLogId, "handleTableXfmrCallback: ", uriPath) + } + dbs := dbYgXlateInfo.xlateReq.dbs + txCache := new(sync.Map) + currDbNum := ygXpathInfo.dbIndex + xfmrDbTblKeyCache := make(map[string]tblKeyCache) + dbDataMap := make(RedisDbMap) + for i := db.ApplDB; i < db.MaxDB; i++ { + dbDataMap[i] = make(map[string]map[string]db.Value) + } + deviceObj := ocbinds.Device{} + rootIntf := reflect.ValueOf(&deviceObj).Interface() + ygotObj := rootIntf.(ygot.GoStruct) + inParams := formXfmrInputRequest(dbs[ygXpathInfo.dbIndex], dbs, currDbNum, &ygotObj, uriPath, + uriPath, SUBSCRIBE, "", &dbDataMap, nil, nil, txCache) + tblList, tblXfmrErr := xfmrTblHandlerFunc(*ygXpathInfo.xfmrTbl, inParams, xfmrDbTblKeyCache) + if tblXfmrErr != nil { + log.Warningf("%v handleTableXfmrCallback: table transformer callback returns"+ + " error: %v for the callback %v", dbYgXlateInfo.xlateReq.reqLogId, tblXfmrErr, *ygXpathInfo.xfmrTbl) + } else if inParams.isVirtualTbl != nil && *inParams.isVirtualTbl { + if log.V(dbLgLvl) { + log.Info(dbYgXlateInfo.xlateReq.reqLogId, "handleTableXfmrCallback: virtualTbl is SET to TRUE for this table transformer callback: ", *ygXpathInfo.xfmrTbl) + } + } else { + if log.V(dbLgLvl) { + log.Infof(dbYgXlateInfo.xlateReq.reqLogId+"handleTableXfmrCallback: table list %v returned by table transformer callback: %v ", tblList, *ygXpathInfo.xfmrTbl) + } + return tblList, nil + } + + return nil, nil +} diff --git a/translib/transformer/xconst.go b/translib/transformer/xconst.go index 5589c93a6..e8cde89e5 100644 --- a/translib/transformer/xconst.go +++ b/translib/transformer/xconst.go @@ -30,6 +30,8 @@ const ( OC_MDL_PFX = "openconfig-" IETF_MDL_PFX = "ietf-" IANA_MDL_PFX = "iana-" + PATH_XFMR_RET_ARGS = 1 + PATH_XFMR_RET_ERR_INDX = 0 ) const (