diff --git a/cvl/cvl.go b/cvl/cvl.go index 98922aa4b0ef..881a84744da8 100644 --- a/cvl/cvl.go +++ b/cvl/cvl.go @@ -78,6 +78,7 @@ type modelTableInfo struct { //multiple leafref possible for union mustExp map[string]string tablesForMustExp map[string]CVLOperation + dfltLeafVal map[string]string //map of leaf names and default value } @@ -94,14 +95,21 @@ type CVLErrorInfo struct { ErrAppTag string } +// Struct for request data and YANG data +type requestCacheType struct { + reqData CVLEditConfigData + yangData *xmlquery.Node +} + type CVL struct { redisClient *redis.Client yp *yparser.YParser tmpDbCache map[string]interface{} //map of table storing map of key-value pair - requestCache map[string]map[string][]CVLEditConfigData //Cache of validated data, - //might be used as dependent data in next request + requestCache map[string]map[string][]*requestCacheType//Cache of validated data, + //per table, per key. Can be used as dependent data in next request batchLeaf string chkLeafRefWithOthCache bool + yv *YValidator //Custom YANG validator for validating external dependencies } type modelNamespace struct { @@ -205,6 +213,11 @@ func Debug(on bool) { yparser.Debug(on) } +// isLeafListNode checks if the xml node represents a leaf-list field +func isLeafListNode(node *xmlquery.Node) bool { + return len(node.Attr) != 0 && node.Attr[0].Name.Local == "leaf-list" +} + //Get attribute value of xml node func getXmlNodeAttr(node *xmlquery.Node, attrName string) string { for _, attr := range node.Attr { @@ -216,6 +229,14 @@ func getXmlNodeAttr(node *xmlquery.Node, attrName string) string { return "" } +// getNodeName returns database field name for the xml node. +func getNodeName(node *xmlquery.Node) string { + if isLeafListNode(node) { + return node.Data + "@" + } + return node.Data +} + //Store useful schema data during initialization func storeModelInfo(modelFile string, module *yparser.YParserModule) { //such model info can be maintained in C code and fetched from there f, err := os.Open(CVL_SCHEMA + modelFile) @@ -394,6 +415,20 @@ func storeModelInfo(modelFile string, module *yparser.YParserModule) { //such mo } } +// Get YANG list to Redis table name +func getYangListToRedisTbl(yangListName string) string { + if (strings.HasSuffix(yangListName, "_LIST")) { + yangListName = yangListName[0:len(yangListName) - len("_LIST")] + } + tInfo, exists := modelInfo.tableInfo[yangListName] + + if exists && (tInfo.redisTableName != "") { + return tInfo.redisTableName + } + + return yangListName +} + //Find the tables names in must expression, these tables data need to be fetched //during semantic validation func addTableNamesForMustExp() { @@ -475,16 +510,22 @@ func splitRedisKey(key string) (string, string) { return tblName, key[prefixLen:] } -//Get the YANG list name from Redis key +//Get the YANG list name from Redis key and table name //This just returns same YANG list name as Redis table name //when 1:1 mapping is there. For one Redis table to //multiple YANG list, it returns appropriate YANG list name //INTERFACE:Ethernet12 returns ==> INTERFACE //INTERFACE:Ethernet12:1.1.1.0/32 ==> INTERFACE_IPADDR -func getRedisKeyToYangList(tableName, key string) string { +func getRedisTblToYangList(tableName, key string) (yangList string) { + defer func() { + pYangList := &yangList + TRACE_LOG(INFO_API, TRACE_SYNTAX, "Got YANG list '%s' " + + "from Redis Table '%s', Key '%s'", *pYangList, tableName, key) + }() + mapArr, exists := modelInfo.redisTableToYangList[tableName] - if exists == false { + if !exists || (len(mapArr) == 1) { //no map or only one //1:1 mapping case return tableName } @@ -492,7 +533,7 @@ func getRedisKeyToYangList(tableName, key string) string { //As of now determine the mapping based on number of keys var foundIdx int = -1 numOfKeys := 1 //Assume only one key initially - for keyDelim, _ := range modelInfo.allKeyDelims { + for keyDelim := range modelInfo.allKeyDelims { foundIdx = strings.Index(key, keyDelim) if (foundIdx >= 0) { //Matched with key delim @@ -505,7 +546,7 @@ func getRedisKeyToYangList(tableName, key string) string { //Check which list has number of keys as 'numOfKeys' for i := 0; i < len(mapArr); i++ { tblInfo, exists := modelInfo.tableInfo[mapArr[i]] - if exists == true { + if exists { if (len(tblInfo.keys) == numOfKeys) { //Found the YANG list matching the number of keys return mapArr[i] diff --git a/cvl/cvl_api.go b/cvl/cvl_api.go index 719467f31736..ad4af71e6328 100644 --- a/cvl/cvl_api.go +++ b/cvl/cvl_api.go @@ -195,7 +195,7 @@ func Finish() { func ValidationSessOpen() (*CVL, CVLRetCode) { cvl := &CVL{} cvl.tmpDbCache = make(map[string]interface{}) - cvl.requestCache = make(map[string]map[string][]CVLEditConfigData) + cvl.requestCache = make(map[string]map[string][]*requestCacheType) cvl.yp = &yparser.YParser{} if (cvl == nil || cvl.yp == nil) { @@ -324,10 +324,10 @@ func (c *CVL) ValidateEditConfig(cfgData []CVLEditConfigData) (CVLErrorInfo, CVL reqTbl, exists := c.requestCache[tbl] if (exists == false) { //Create new table key data - reqTbl = make(map[string][]CVLEditConfigData) + reqTbl = make(map[string][]*requestCacheType) } cfgDataItemArr, _ := reqTbl[key] - cfgDataItemArr = append(cfgDataItemArr, cfgDataItem) + cfgDataItemArr = append(cfgDataItemArr, &requestCacheType{cfgDataItem, nil}) reqTbl[key] = cfgDataItemArr c.requestCache[tbl] = reqTbl @@ -431,7 +431,7 @@ func (c *CVL) ValidateEditConfig(cfgData []CVLEditConfigData) (CVLErrorInfo, CVL deletedInSameSession := false if tbl != "" && key != "" { for _, cachedCfgData := range c.requestCache[tbl][key] { - if cachedCfgData.VOp == OP_DELETE { + if cachedCfgData.reqData.VOp == OP_DELETE { deletedInSameSession = true break } diff --git a/cvl/cvl_cache.go b/cvl/cvl_cache.go index 66cc22bc7928..ab2792df679c 100644 --- a/cvl/cvl_cache.go +++ b/cvl/cvl_cache.go @@ -46,10 +46,10 @@ func (c *CVL) fetchDataFromRequestCache(tableName string, key string) (d map[str if (cfgDataArr != nil) { for _, cfgReqData := range cfgDataArr { //Delete request doesn't have depedent data - if (cfgReqData.VOp == OP_CREATE) { - return cfgReqData.Data, false - } else if (cfgReqData.VOp == OP_UPDATE) { - return cfgReqData.Data, true + if (cfgReqData.reqData.VOp == OP_CREATE) { + return cfgReqData.reqData.Data, false + } else if (cfgReqData.reqData.VOp == OP_UPDATE) { + return cfgReqData.reqData.Data, true } } } diff --git a/cvl/cvl_semantics.go b/cvl/cvl_semantics.go index b7eab1f539e5..94270a16c111 100644 --- a/cvl/cvl_semantics.go +++ b/cvl/cvl_semantics.go @@ -21,7 +21,9 @@ package cvl import ( "strings" + "encoding/xml" "github.com/antchfx/xmlquery" + "github.com/antchfx/jsonquery" "github.com/Azure/sonic-mgmt-common/cvl/internal/yparser" //lint:ignore ST1001 This is safe to dot import for util package . "github.com/Azure/sonic-mgmt-common/cvl/internal/util" @@ -34,6 +36,661 @@ type YValidator struct { current *xmlquery.Node //Current position } +//Generate leaf/leaf-list YANG data +func (c *CVL) generateYangLeafData(tableName string, jsonNode *jsonquery.Node, +parent *xmlquery.Node) CVLRetCode { + + //Traverse fields + for jsonFieldNode := jsonNode.FirstChild; jsonFieldNode!= nil; + jsonFieldNode = jsonFieldNode.NextSibling { + //Add fields as leaf to the list + if (jsonFieldNode.Type == jsonquery.ElementNode && + jsonFieldNode.FirstChild != nil && + jsonFieldNode.FirstChild.Type == jsonquery.TextNode) { + + if (len(modelInfo.tableInfo[tableName].mapLeaf) == 2) {//mapping should have two leaf always + //Values should be stored inside another list as map table + listNode := c.addYangNode(tableName, parent, tableName, "") //Add the list to the top node + c.addYangNode(tableName, + listNode, modelInfo.tableInfo[tableName].mapLeaf[0], + jsonFieldNode.Data) + + c.addYangNode(tableName, + listNode, modelInfo.tableInfo[tableName].mapLeaf[1], + jsonFieldNode.FirstChild.Data) + + } else { + //check if it is hash-ref, then need to add only key from "TABLE|k1" + hashRefMatch := reHashRef.FindStringSubmatch(jsonFieldNode.FirstChild.Data) + + if len(hashRefMatch) == 3 { + c.addYangNode(tableName, + parent, jsonFieldNode.Data, + hashRefMatch[2]) //take hashref key value + } else { + c.addYangNode(tableName, + parent, jsonFieldNode.Data, + jsonFieldNode.FirstChild.Data) + } + } + + } else if (jsonFieldNode.Type == jsonquery.ElementNode && + jsonFieldNode.FirstChild != nil && + jsonFieldNode.FirstChild.Type == jsonquery.ElementNode) { + //Array data e.g. VLAN members@ or 'ports@' + for arrayNode:=jsonFieldNode.FirstChild; arrayNode != nil; + arrayNode = arrayNode.NextSibling { + + node := c.addYangNode(tableName, + parent, jsonFieldNode.Data, + arrayNode.FirstChild.Data) + + //mark these nodes as leaf-list + addAttrNode(node, "leaf-list", "") + } + } + } + + //Add all the default nodes required for must and when exps evaluation + for nodeName, valStr := range modelInfo.tableInfo[tableName].dfltLeafVal { + //Check if default node is already present in data + var child *xmlquery.Node + for child = parent.FirstChild; child != nil; child = child.NextSibling { + if (child.Data == nodeName) { + break + } + } + + if (child != nil) { + //node is already present, skip adding it + continue + } + + valArr := strings.Split(valStr, ",") + for idx := 0; idx < len(valArr); idx ++ { + node := c.addYangNode(tableName, + parent, nodeName, valArr[idx]) + + //mark these nodes as leaf-list + if (len(valArr) > 1) { + addAttrNode(node, "leaf-list", "") + } + } + } + + return CVL_SUCCESS +} + +//Add attribute YANG node +func addAttrNode(n *xmlquery.Node, key, val string) { + var attr xml.Attr = xml.Attr { + Name: xml.Name{Local: key}, + Value: val, + } + + n.Attr = append(n.Attr, attr) +} + +func getAttrNodeVal(node *xmlquery.Node, name string) string { + if (node == nil) { + return "" + } + + if len(node.Attr) == 0 { + return "" + } + + for idx := 0; idx < len(node.Attr); idx++ { + if (node.Attr[idx].Name.Local == name) { + return node.Attr[idx].Value + } + } + + return "" +} + +//Add YANG node with or without parent, with or without value +func (c *CVL) addYangNode(tableName string, parent *xmlquery.Node, + name string, value string) *xmlquery.Node { + + //Create the node + node := &xmlquery.Node{Parent: parent, Data: name, + Type: xmlquery.ElementNode} + + //Set prefix from parent + if (parent != nil) { + node.Prefix = parent.Prefix + } + + if (value != "") { + //Create the value node + textNode := &xmlquery.Node{Data: value, Type: xmlquery.TextNode} + node.FirstChild = textNode + node.LastChild = textNode + } + + if (parent == nil) { + //Creating top node + return node + } + + if parent.FirstChild == nil { + //Create as first child + parent.FirstChild = node + parent.LastChild = node + + } else { + //Append as sibling + tmp := parent.LastChild + tmp.NextSibling = node + node.PrevSibling = tmp + parent.LastChild = node + } + + return node +} + +//Generate YANG list data along with top container, +//table container. +//If needed, stores the list pointer against each request table/key +//in requestCahce so that YANG data can be reached +//directly on given table/key +func (c *CVL) generateYangListData(jsonNode *jsonquery.Node, + storeInReqCache bool)(*xmlquery.Node, CVLErrorInfo) { + var cvlErrObj CVLErrorInfo + + tableName := jsonNode.Data + //c.batchLeaf = nil + //c.batchLeaf = make([]*yparser.YParserLeafValue, 0) + + //Every Redis table is mapped as list within a container, + //E.g. ACL_RULE is mapped as + // container ACL_RULE { list ACL_RULE_LIST {} } + var topNode *xmlquery.Node + + if _, exists := modelInfo.tableInfo[tableName]; !exists { + CVL_LOG(ERROR, "Failed to find schema details for table %s", tableName) + cvlErrObj.ErrCode = CVL_SYNTAX_ERROR + cvlErrObj.TableName = tableName + cvlErrObj.Msg ="Schema details not found" + return nil, cvlErrObj + } + + // Add top most container e.g. 'container sonic-acl {...}' + topNode = c.addYangNode(tableName, nil, modelInfo.tableInfo[tableName].modelName, "") + //topNode.Prefix = modelInfo.modelNs[modelInfo.tableInfo[tableName].modelName].prefix + topNode.Prefix = modelInfo.tableInfo[tableName].modelName + topNode.NamespaceURI = modelInfo.modelNs[modelInfo.tableInfo[tableName].modelName].ns + + //Add the container node for each list + //e.g. 'container ACL_TABLE { list ACL_TABLE_LIST ...} + listConatinerNode := c.addYangNode(tableName, topNode, tableName, "") + + //Traverse each key instance + keyPresent := false + for jsonNode = jsonNode.FirstChild; jsonNode != nil; jsonNode = jsonNode.NextSibling { + //store the redis key + redisKey := jsonNode.Data + + //Mark at least one key is present + keyPresent = true + + //For each field check if is key + //If it is key, create list as child of top container + // Get all key name/value pairs + if yangListName := getRedisTblToYangList(tableName, redisKey); yangListName!= "" { + tableName = yangListName + } + keyValuePair := getRedisToYangKeys(tableName, redisKey) + keyCompCount := len(keyValuePair) + totalKeyComb := 1 + var keyIndices []int + + //Find number of all key combinations + //Each key can have one or more key values, which results in nk1 * nk2 * nk2 combinations + idx := 0 + for i := range keyValuePair { + totalKeyComb = totalKeyComb * len(keyValuePair[i].values) + keyIndices = append(keyIndices, 0) + } + + for ; totalKeyComb > 0 ; totalKeyComb-- { + //Get the YANG list name from Redis table name + //Ideally they are same except when one Redis table is split + //into multiple YANG lists + + //Add table i.e. create list element + listNode := c.addYangNode(tableName, listConatinerNode, tableName + "_LIST", "") //Add the list to the top node + addAttrNode(listNode, "key", redisKey) + + if storeInReqCache { + //store the list pointer in requestCache against the table/key + reqCache, exists := c.requestCache[tableName][redisKey] + if exists { + //Store same list instance in all requests under same table/key + for idx := 0; idx < len(reqCache); idx++ { + if (reqCache[idx].yangData == nil) { + //Save the YANG data tree for using it later + reqCache[idx].yangData = listNode + } + } + } + } + + //For each key combination + //Add keys as leaf to the list + for idx = 0; idx < keyCompCount; idx++ { + c.addYangNode(tableName, listNode, keyValuePair[idx].key, + keyValuePair[idx].values[keyIndices[idx]]) + } + + //Get all fields under the key field and add them as children of the list + c.generateYangLeafData(tableName, jsonNode, listNode) + + //Check which key elements left after current key element + var next int = keyCompCount - 1 + for ((next > 0) && ((keyIndices[next] +1) >= len(keyValuePair[next].values))) { + next-- + } + //No more combination possible + if (next < 0) { + break + } + + keyIndices[next]++ + + //Reset indices for all other key elements + for idx = next+1; idx < keyCompCount; idx++ { + keyIndices[idx] = 0 + } + } + } + + if !keyPresent { + return nil, cvlErrObj + } + + return topNode, cvlErrObj +} + +//Append given children to destNode +func (c *CVL) appendSubtree(dest, src *xmlquery.Node) CVLRetCode { + if (dest == nil || src == nil) { + return CVL_FAILURE + } + + var lastSibling *xmlquery.Node = nil + + for srcNode := src; srcNode != nil ; srcNode = srcNode.NextSibling { + //set parent for all nodes + srcNode.Parent = dest + lastSibling = srcNode + } + + if (dest.LastChild == nil) { + //No sibling in dest yet + dest.FirstChild = src + dest.LastChild = lastSibling + } else { + //Append to the last sibling + dest.LastChild.NextSibling = src + src.PrevSibling = dest.LastChild + dest.LastChild = lastSibling + } + + return CVL_SUCCESS +} + +//Return subtree after detaching from parent +func (c *CVL) detachSubtree(parent *xmlquery.Node) *xmlquery.Node { + + child := parent.FirstChild + + if (child != nil) { + //set children to nil + parent.FirstChild = nil + parent.LastChild = nil + } else { + //No children + return nil + } + + //Detach all children from parent + for node := child; node != nil; node = node.NextSibling { + node.Parent = nil + } + + return child +} + +//Detach a node from its parent +func (c *CVL) detachNode(node *xmlquery.Node) CVLRetCode { + if (node == nil) { + return CVL_FAILURE + } + + //get the parent node + parent := node.Parent + + if (parent == nil) { + //Already detached node + return CVL_SUCCESS + } + + //adjust siblings + if (parent.FirstChild == node && parent.LastChild == node) { + //this is the only node + parent.FirstChild = nil + parent.LastChild = nil + } else if (parent.FirstChild == node) { + //first child, set new first child + parent.FirstChild = node.NextSibling + node.NextSibling.PrevSibling = nil + } else { + node.PrevSibling.NextSibling = node.NextSibling + if (node.NextSibling != nil) { + //if remaining sibling + node.NextSibling.PrevSibling = node.PrevSibling + } else { + //this is last child getting detached, + //so set lastChild as node's prevSibling + parent.LastChild = node.PrevSibling + } + } + + //detach from parent and siblings + node.Parent = nil + node.PrevSibling = nil + node.NextSibling = nil + + return CVL_SUCCESS +} + +//Delete all leaf-list nodes in destination +//Leaf-list should be replaced from source +//destination +func (c *CVL) deleteDestLeafList(dest *xmlquery.Node) { + + TRACE_LOG(INFO_API, TRACE_CACHE, "Updating leaf-list by " + + "removing and then adding leaf-list") + + //find start and end of dest leaf list + leafListName := dest.Data + for node := dest; node != nil; { + tmpNextNode := node.NextSibling + + if (node.Data == leafListName) { + c.detachNode(node) + node = tmpNextNode + continue + } else { + //no more leaflist node + break + } + } +} + +// deleteLeafNodes removes specified child nodes from an xml node topNode +func (c *CVL) deleteLeafNodes(topNode *xmlquery.Node, cfgData map[string]string) { + for node := topNode.FirstChild; node != nil; { + if _, found := cfgData[getNodeName(node)]; found { + tmpNode := node.NextSibling + c.detachNode(node) + node = tmpNode + } else { + node = node.NextSibling + } + } +} + +//Check if the given list src node already exists in dest node +func (c *CVL) checkIfListNodeExists(dest, src *xmlquery.Node) *xmlquery.Node { + if (dest == nil) || (src == nil) { + return nil + } + + tableName := getYangListToRedisTbl(src.Data) + redisKey := getAttrNodeVal(src, "key") + + if (tableName == "" || redisKey == "") { + return nil + } + + entry, exists := c.requestCache[tableName][redisKey] + if !exists || (len(entry) == 0) { + return nil + } + + //CREATE/UPDATE/DELETE request for same table/key points to + //same yang list in request cache + yangList := entry[0].yangData + + if (yangList == nil || yangList.Parent == nil) { + //Source node does not exist in destination + return nil + } + + if (dest.Parent == yangList.Parent) { + //Same parent means yang list already exists in destination tree + return yangList + } + + return nil +} + +//Merge YANG data recursively from dest to src +//Leaf-list is always replaced and appeneded at +//the end of list's children +func (c *CVL) mergeYangData(dest, src *xmlquery.Node) CVLRetCode { + if (dest == nil) || (src == nil) { + return CVL_FAILURE + } + + TRACE_LOG(INFO_API, (TRACE_SYNTAX | TRACE_SEMANTIC), + "Merging YANG data %s %s", dest.Data, src.Data) + + if (dest.Type == xmlquery.TextNode) && (src.Type == xmlquery.TextNode) { + //handle leaf node by updating value + dest.Data = src.Data + return CVL_SUCCESS + } + + srcNode := src + + destLeafListDeleted := make(map[string]bool) + for srcNode != nil { + //Find all source nodes and attach to the matching destination node + ret := CVL_FAILURE + //TRACE_LOG((TRACE_SYNTAX | TRACE_SEMANTIC), "MergeData : src loop\n") +destLoop: + destNode := dest + for ; destNode != nil; destNode = destNode.NextSibling { + //TRACE_LOG((TRACE_SYNTAX | TRACE_SEMANTIC), "MergeData : dest loop\n") + if (destNode.Data != srcNode.Data) { + //Can proceed to subtree only if current node name matches + continue + } + + if (strings.HasSuffix(destNode.Data, "_LIST")){ + //Check if src list node already exists in destination + tmpNode := c.checkIfListNodeExists(destNode, srcNode) + if tmpNode != nil { + destNode = tmpNode + } else { + destNode = tmpNode + break + } + //find exact match for list instance + //check with key value, stored in attribute + /*if (len(destNode.Attr) == 0) || (len(srcNode.Attr) == 0) || + (destNode.Attr[0].Value != srcNode.Attr[0].Value) { + //move to next list + continue + }*/ + } else if (len(destNode.Attr) > 0) && (len(srcNode.Attr) > 0) && + (destNode.Attr[0].Name.Local == "leaf-list") && + (srcNode.Attr[0].Name.Local == "leaf-list") { // attribute has type + + delFlag, exists := destLeafListDeleted[srcNode.Data] + + if !exists || !delFlag { + //Replace all leaf-list nodes from destination first + c.deleteDestLeafList(destNode) + destLeafListDeleted[srcNode.Data] = true + //Note that 'dest' still points to list keys + //even though all leaf-list might have been deleted + //as we never delete key while merging + goto destLoop + } else { + //if all dest leaflist deleted, + //just break to add all leaflist + destNode = nil + break + } + } + + //Go to their children + ret = c.mergeYangData(destNode.FirstChild, srcNode.FirstChild) + + //Node matched break now + break + + } //dest node loop + + if (ret == CVL_FAILURE) { + if (destNode == nil) { + //destNode == nil -> node not found + //detach srcNode and append to dest + tmpNextSrcNode := srcNode.NextSibling + if CVL_SUCCESS == c.detachNode(srcNode) { + if (len(srcNode.Attr) > 0) && + (srcNode.Attr[0].Name.Local == "leaf-list") { + //set the flag so that we don't delete leaf-list + //from destNode further + destLeafListDeleted[srcNode.Data] = true + } + c.appendSubtree(dest.Parent, srcNode) + } + srcNode = tmpNextSrcNode + continue + } else { + //subtree merge failure ,, append subtree + subTree := c.detachSubtree(srcNode) + if (subTree != nil) { + c.appendSubtree(destNode, subTree) + } + } + } + + srcNode = srcNode.NextSibling + } //src node loop + + return CVL_SUCCESS +} + +func (c *CVL) findYangList(tableName string, redisKey string) *xmlquery.Node { + origCurrent := c.yv.current + tmpCurrent := c.moveToYangList(tableName, redisKey) + c.yv.current = origCurrent + + return tmpCurrent +} + +//Locate YANG list instance in root for given table name and key +func (c *CVL) moveToYangList(tableName string, redisKey string) *xmlquery.Node { + + var nodeTbl *xmlquery.Node = nil + + redisTableName := getYangListToRedisTbl(tableName) + modelName := modelInfo.tableInfo[tableName].modelName + + //move to the model first + for node := c.yv.root.FirstChild; node != nil; node = node.NextSibling { + if (node.Data != modelName) { + continue + } + + //Move to container + for nodeTbl = node.FirstChild; nodeTbl != nil; nodeTbl = nodeTbl.NextSibling { + if (nodeTbl.Data == redisTableName) { + break + } + } + + break + } + + if (nodeTbl == nil) { + TRACE_LOG(INFO_API, TRACE_SEMANTIC, "YANG data for table %s, key %s is not present in YANG tree", + tableName, redisKey) + return nil + } + + //Move to list + listName := tableName + "_LIST" + for nodeList := nodeTbl.FirstChild; nodeList != nil; nodeList = nodeList.NextSibling { + if (nodeList.Data != listName) { + continue + } + + c.yv.current = nodeList + //if no key specified or no other instance exists, + //just return the first list instance + if (redisKey == "" || nodeList.NextSibling == nil) { + return c.yv.current + } + + for ; (nodeList != nil); nodeList = nodeList.NextSibling { + if (len(nodeList.Attr) > 0) && + (nodeList.Attr[0].Value == redisKey) { + c.yv.current = nodeList + return nodeList + } + } + } + + CVL_LOG(WARNING, "No list entry matched, unable to find YANG data for table %s, key %s", + tableName, redisKey) + return nil +} + +//Set operation node value based on operation in request received +func (c *CVL) setOperation(op CVLOperation) { + + var node *xmlquery.Node + + for node = c.yv.root.FirstChild; node != nil; node = node.NextSibling { + if (node.Data == "operation") { + break + } + } + + //Add the operation container + if (node == nil) { + node = c.addYangNode("", c.yv.root, "operation", "") + node.Prefix = "sonic-common" //"cmn" + //modelInfo.modelNs["sonic-common"].prefix + node.NamespaceURI = modelInfo.modelNs["sonic-common"].ns + } + + opNode := node.FirstChild + if opNode == nil { + node.Prefix = "sonic-common"//"cmn" + opNode = c.addYangNode("", node, "operation", "NONE") + } + + switch op { + case OP_CREATE: + opNode.FirstChild.Data = "CREATE" + case OP_UPDATE: + opNode.FirstChild.Data = "UPDATE" + case OP_DELETE: + opNode.FirstChild.Data = "DELETE" + default: + opNode.FirstChild.Data = "NONE" + } +} + //Check delete constraint for leafref if key/field is deleted func (c *CVL) checkDeleteConstraint(cfgData []CVLEditConfigData, tableName, keyVal, field string) CVLRetCode { diff --git a/cvl/cvl_syntax.go b/cvl/cvl_syntax.go index 7cc812e0374a..9dfa8d98ead3 100644 --- a/cvl/cvl_syntax.go +++ b/cvl/cvl_syntax.go @@ -136,7 +136,7 @@ func (c *CVL) generateTableData(config bool, jsonNode *jsonquery.Node)(*yparser. //For each field check if is key //If it is key, create list as child of top container // Get all key name/value pairs - if yangListName := getRedisKeyToYangList(tableName, jsonNode.Data); yangListName!= "" { + if yangListName := getRedisTblToYangList(tableName, jsonNode.Data); yangListName!= "" { tableName = yangListName } keyValuePair := getRedisToYangKeys(tableName, jsonNode.Data)