2020package cvl
2121
2222import (
23- "strings "
23+ "fmt "
2424 "encoding/xml"
2525 "encoding/json"
26+ "strings"
27+ "regexp"
28+ "github.com/antchfx/xpath"
2629 "github.com/antchfx/xmlquery"
2730 "github.com/antchfx/jsonquery"
2831 "github.com/Azure/sonic-mgmt-common/cvl/internal/yparser"
@@ -201,16 +204,16 @@ func (c *CVL) generateYangListData(jsonNode *jsonquery.Node,
201204 var cvlErrObj CVLErrorInfo
202205
203206 tableName := jsonNode .Data
204- // c.batchLeaf = nil
205- // c.batchLeaf = make([]*yparser.YParserLeafValue, 0)
207+ c .batchLeaf = nil
208+ c .batchLeaf = make ([]* yparser.YParserLeafValue , 0 )
206209
207210 //Every Redis table is mapped as list within a container,
208211 //E.g. ACL_RULE is mapped as
209212 // container ACL_RULE { list ACL_RULE_LIST {} }
210213 var topNode * xmlquery.Node
211214
212215 if _ , exists := modelInfo .tableInfo [tableName ]; ! exists {
213- CVL_LOG (ERROR , "Failed to find schema details for table %s" , tableName )
216+ CVL_LOG (WARNING , "Failed to find schema details for table %s" , tableName )
214217 cvlErrObj .ErrCode = CVL_SYNTAX_ERROR
215218 cvlErrObj .TableName = tableName
216219 cvlErrObj .Msg = "Schema details not found"
@@ -756,7 +759,7 @@ func (c *CVL) addDepYangData(redisKeys []string, redisKeyFilter,
756759
757760 for field := redisKey .FirstChild ; field != nil ;
758761 field = field .NextSibling {
759- if (field .Data == fields ) {
762+ if (field .Data == fields && field . FirstChild != nil ) {
760763 //Single field requested
761764 singleLeaf = singleLeaf + field .FirstChild .Data + ","
762765 break
@@ -783,6 +786,217 @@ func (c *CVL) addDepYangData(redisKeys []string, redisKeyFilter,
783786 return ""
784787}
785788
789+ func compileLeafRefPath () {
790+ reMultiPred := regexp .MustCompile (`\][ ]*\[` )
791+
792+ for _ , tInfo := range modelInfo .tableInfo {
793+ if (len (tInfo .leafRef ) == 0 ) { //no leafref
794+ continue
795+ }
796+
797+ //for nodeName, leafRefArr := range tInfo.leafRef {
798+ for _ , leafRefArr := range tInfo .leafRef {
799+ for _ , leafRefArrItem := range leafRefArr {
800+ if (leafRefArrItem .path == "non-leafref" ) {
801+ //Leaf type has at-least one non-learef data type
802+ continue
803+ }
804+
805+ //first store the referred table and target node
806+ leafRefArrItem .yangListNames , leafRefArrItem .targetNodeName =
807+ getLeafRefTargetInfo (leafRefArrItem .path )
808+ //check if predicate is used in path
809+ //for complex expression, xpath engine is
810+ //used for evaluation,
811+ //else don't build expression tree,
812+ //it is handled by just checking redis entry
813+ if strings .Contains (leafRefArrItem .path , "[" ) &&
814+ strings .Contains (leafRefArrItem .path , "]" ) {
815+ //Compile the xpath in leafref
816+ tmpExp := reMultiPred .ReplaceAllString (leafRefArrItem .path , " and " )
817+ //tmpExp = nodeName + " = " + tmpExp
818+ tmpExp = "current() = " + tmpExp
819+ leafRefArrItem .exprTree = xpath .MustCompile (tmpExp )
820+ }
821+ }
822+ }
823+ }
824+ }
825+
826+ //Validate leafref
827+ //Convert leafref to must expression
828+ //type leafref { path "../../../ACL_TABLE/ACL_TABLE_LIST/aclname";} converts to
829+ // "current() = ../../../ACL_TABLE/ACL_TABLE_LIST[aclname=current()]/aclname"
830+ func (c * CVL ) validateLeafRef (node * xmlquery.Node ,
831+ tableName , key string , op CVLOperation ) (r CVLErrorInfo ) {
832+ defer func () {
833+ ret := & r
834+ CVL_LOG (INFO_API , "validateLeafRef(): table name = %s, " +
835+ "return value = %v" , tableName , * ret )
836+ }()
837+
838+ if (op == OP_DELETE ) {
839+ //No new node getting added so skip leafref validation
840+ return CVLErrorInfo {ErrCode :CVL_SUCCESS }
841+ }
842+
843+ //Set xpath callback for retreiving dependent data
844+ xpath .SetDepDataClbk (c , func (ctxt interface {}, redisKeys []string ,
845+ redisKeyFilter , keyNames , pred , fields , count string ) string {
846+ c := ctxt .(* CVL )
847+ TRACE_LOG (INFO_API , TRACE_SEMANTIC , "validateLeafRef(): calling addDepYangData()" )
848+ return c .addDepYangData (redisKeys , redisKeyFilter , keyNames , pred , fields , "" )
849+ })
850+
851+ listNode := node
852+ if (listNode == nil || listNode .FirstChild == nil ) {
853+ return CVLErrorInfo {
854+ TableName : tableName ,
855+ Keys : strings .Split (key , modelInfo .tableInfo [tableName ].redisKeyDelim ),
856+ ErrCode : CVL_SEMANTIC_ERROR ,
857+ CVLErrDetails : cvlErrorMap [CVL_SEMANTIC_ERROR ],
858+ Msg : "Failed to find YANG data for leafref expression validation" ,
859+ }
860+ }
861+
862+ tblInfo := modelInfo .tableInfo [tableName ]
863+
864+ for nodeName , leafRefs := range tblInfo .leafRef { //for each leafref node
865+
866+ //Reach to the node where leafref is present
867+ ctxNode := listNode .FirstChild
868+ for ;(ctxNode != nil ) && (ctxNode .Data != nodeName );
869+ ctxNode = ctxNode .NextSibling {
870+ }
871+
872+ if (ctxNode == nil ) {
873+ //No leafref instance present, proceed to next leafref
874+ continue
875+ }
876+
877+ //Check leafref for each leaf-list node
878+ for ;(ctxNode != nil ) && (ctxNode .Data == nodeName );
879+ ctxNode = ctxNode .NextSibling {
880+ //Load first data for each referred table.
881+ //c.yv.root has all requested data merged and any depdendent
882+ //data needed for leafref validation should be available from this.
883+
884+ leafRefSuccess := false
885+ nonLeafRefPresent := false //If leaf has non-leafref data type due to union
886+ nodeValMatchedWithLeafref := false
887+
888+ ctxtVal := ""
889+ //Get the leaf value
890+ if (ctxNode .FirstChild != nil ) {
891+ ctxtVal = ctxNode .FirstChild .Data
892+ }
893+
894+ //Excute all leafref checks, multiple leafref for unions
895+ leafRefLoop:
896+ for _ , leafRefPath := range leafRefs {
897+ if (leafRefPath .path == "non-leafref" ) {
898+ //Leaf has at-least one non-leaferf data type in union
899+ nonLeafRefPresent = true
900+ continue
901+ }
902+
903+ //Add dependent data for all referred tables
904+ for _ , refListName := range leafRefPath .yangListNames {
905+ refRedisTableName := getYangListToRedisTbl (refListName )
906+
907+ filter := ""
908+ var err error
909+ var tableKeys []string
910+ if (leafRefPath .exprTree == nil ) { //no predicate, single key case
911+ //Context node used for leafref
912+ //Keys -> ACL_TABLE|TestACL1
913+ filter = refRedisTableName +
914+ modelInfo .tableInfo [refListName ].redisKeyDelim + ctxtVal
915+ tableKeys , err = redisClient .Keys (filter ).Result ()
916+ } else {
917+ //Keys -> ACL_TABLE|*
918+ filter = refRedisTableName +
919+ modelInfo .tableInfo [refListName ].redisKeyDelim + "*"
920+ //tableKeys, _, err = redisClient.Scan(0, filter, 1).Result()
921+ tableKeys , err = redisClient .Keys (filter ).Result ()
922+ }
923+
924+ if (err != nil ) || (len (tableKeys ) == 0 ) {
925+ //There must be at least one entry in the ref table
926+ TRACE_LOG (INFO_API , TRACE_SEMANTIC , "Leafref dependent data " +
927+ "table %s, key %s not found in Redis" , refRedisTableName ,
928+ ctxtVal )
929+
930+ if (leafRefPath .exprTree == nil ) {
931+ //Check the key in request cache also
932+ if _ , exists := c.requestCache [refRedisTableName ][ctxtVal ]; exists {
933+ //no predicate and single key is referred
934+ leafRefSuccess = true
935+ break leafRefLoop
936+ } else if node := c .findYangList (refListName , ctxtVal );
937+ node != nil {
938+ //Found in the request tree
939+ leafRefSuccess = true
940+ break leafRefLoop
941+ }
942+ }
943+ continue
944+ } else {
945+ if (leafRefPath .exprTree == nil ) {
946+ //no predicate and single key is referred
947+ leafRefSuccess = true
948+ break leafRefLoop
949+ }
950+ }
951+
952+ //Now add the first data
953+ c .addDepYangData ([]string {}, tableKeys [0 ],
954+ strings .Join (modelInfo .tableInfo [refListName ].keys , "|" ),
955+ "true" , "" , "" )
956+ }
957+
958+ //Excute xpath expression for complex leafref path
959+ if xmlquery .Eval (c .yv .root , ctxNode , leafRefPath .exprTree ) {
960+ leafRefSuccess = true
961+ break leafRefLoop
962+ }
963+ } //for loop for all leafref check for a leaf - union case
964+
965+ if ! leafRefSuccess && nonLeafRefPresent && (len (leafRefs ) > 1 ) {
966+ //If union has mixed type with base and leafref type,
967+ //check if node value matched with any leafref.
968+ //If so non-existence of leafref in DB will be treated as failure.
969+ if (ctxtVal != "" ) {
970+ nodeValMatchedWithLeafref = c .yp .IsLeafrefMatchedInUnion (tblInfo .module ,
971+ fmt .Sprintf ("/%s:%s/%s/%s_LIST/%s" , tblInfo .modelName ,
972+ tblInfo .modelName , tblInfo .redisTableName ,
973+ tableName , nodeName ),
974+ ctxtVal )
975+ }
976+ }
977+
978+ if ! leafRefSuccess && (! nonLeafRefPresent || nodeValMatchedWithLeafref ) {
979+ //Return failure if none of the leafref exists
980+ return CVLErrorInfo {
981+ TableName : tableName ,
982+ Keys : strings .Split (key ,
983+ modelInfo .tableInfo [tableName ].redisKeyDelim ),
984+ ErrCode : CVL_SEMANTIC_DEPENDENT_DATA_MISSING ,
985+ CVLErrDetails : cvlErrorMap [CVL_SEMANTIC_DEPENDENT_DATA_MISSING ],
986+ ErrAppTag : "instance-required" ,
987+ ConstraintErrMsg : "No instance found for '" + ctxtVal + "'" ,
988+ }
989+ } else if ! leafRefSuccess {
990+ TRACE_LOG (INFO_API , TRACE_SEMANTIC , "validateLeafRef(): " +
991+ "Leafref dependent data not found but leaf has " +
992+ "other data type in union, returning success." )
993+ }
994+ } //for each leaf-list node
995+ }
996+
997+ return CVLErrorInfo {ErrCode :CVL_SUCCESS }
998+ }
999+
7861000//Check delete constraint for leafref if key/field is deleted
7871001func (c * CVL ) checkDeleteConstraint (cfgData []CVLEditConfigData ,
7881002 tableName , keyVal , field string ) CVLRetCode {
0 commit comments