Skip to content

Commit

Permalink
CVL Changes #2: YValidator infra changes for evaluating xpath express…
Browse files Browse the repository at this point in the history
…ion (sonic-net#19)

Why I did it
Adding YValidator support so that xpath expression (must/when/leafref) can be evaluated using custom xpath engine. Custom xpath engine will be integrated in subsequent PR.

How I did it
N/A.
Please refer to change log for more details.

How to verify it
No specific test cases to be executed. Run cvl go test cases using - 'make cvl-test'.

Description for the changelog

Modifying cvl.go for adding new data structure to store cache in CVL session cache, add YValidator data type. Adding API to get Redis table name from YANG list.

Modifying cvl_api.go and cvl_cache.go due to change in session cache.

Modifying cvl_syntax.go due to change in API name.

Modifying cvl_semantics.go to add various API which can operate on YValidator nodes.
  • Loading branch information
dutta-partha authored Aug 10, 2020
1 parent f3fc40f commit 9d24a34
Show file tree
Hide file tree
Showing 5 changed files with 714 additions and 16 deletions.
55 changes: 48 additions & 7 deletions cvl/cvl.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}


Expand All @@ -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 {
Expand Down Expand Up @@ -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 {
Expand All @@ -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)
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -475,24 +510,30 @@ 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
}

//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
Expand All @@ -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]
Expand Down
8 changes: 4 additions & 4 deletions cvl/cvl_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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
}
Expand Down
8 changes: 4 additions & 4 deletions cvl/cvl_cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
}
Expand Down
Loading

0 comments on commit 9d24a34

Please sign in to comment.