diff --git a/cvl/Makefile b/cvl/Makefile index 730b8def19e8..6a24219aee84 100644 --- a/cvl/Makefile +++ b/cvl/Makefile @@ -58,7 +58,6 @@ schema: $(CVL_SCHEMA) $(CVL_SCHEMA): $(SONIC_YANG_FILES) | $$(@D)/. $(TOP_DIR)/tools/pyang/generate_yin.py \ - --no-err-prefix \ --path=$(SONIC_YANG_DIR) \ --path=$(YANG_SRC_DIR)/common \ --out-dir=$(@D) @@ -69,7 +68,6 @@ test-schema: $(CVL_TEST_SCHEMA) $(CVL_TEST_SCHEMA): $(CVL_TEST_YANGS) | $$(@D)/. $(TOP_DIR)/tools/pyang/generate_yin.py \ - --no-err-prefix \ --path=testdata/schema \ --path=$(YANG_SRC_DIR)/common \ --path=$(YANG_SRC_DIR)/sonic/common \ diff --git a/cvl/cvl.go b/cvl/cvl.go index edc9a5e606f3..e2fc558e2aa5 100644 --- a/cvl/cvl.go +++ b/cvl/cvl.go @@ -670,6 +670,13 @@ func splitRedisKey(key string) (string, string) { return tblName, key[prefixLen:] } +func splitKeyComponents(table, keyComps string) []string { + if m := modelInfo.tableInfo[table]; m != nil { + return strings.Split(keyComps, m.redisKeyDelim) + } + return nil +} + //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 @@ -878,12 +885,27 @@ func (c *CVL) validate (data *yparser.YParserNode) CVLRetCode { return CVL_SUCCESS } -func createCVLErrObj(errObj yparser.YParserError) CVLErrorInfo { +func createCVLErrObj(errObj yparser.YParserError, srcNode *jsonquery.Node) CVLErrorInfo { + errCode := CVLRetCode(errObj.ErrCode) + if errObj.ErrCode == yparser.YP_INTERNAL_UNKNOWN { + errCode = CVL_INTERNAL_UNKNOWN + } + + // YParserError may not contain table or key info when creating a field (libyang issue) + // Fill the missing info from source json data tree + if srcNode != nil && srcNode.Parent != nil { + if len(errObj.TableName) == 0 { + errObj.TableName = srcNode.Parent.Data + errObj.Keys = splitKeyComponents(errObj.TableName, srcNode.Data) + } else if len(errObj.Keys) == 0 && errObj.TableName == srcNode.Parent.Data { + errObj.Keys = splitKeyComponents(errObj.TableName, srcNode.Data) + } + } cvlErrObj := CVLErrorInfo { TableName : errObj.TableName, - ErrCode : CVLRetCode(errObj.ErrCode), - CVLErrDetails : cvlErrorMap[CVLRetCode(errObj.ErrCode)], + ErrCode : errCode, + CVLErrDetails : cvlErrorMap[errCode], Keys : errObj.Keys, Value : errObj.Value, Field : errObj.Field, diff --git a/cvl/cvl_api.go b/cvl/cvl_api.go index 1032e45e2426..16f240a488b0 100644 --- a/cvl/cvl_api.go +++ b/cvl/cvl_api.go @@ -379,6 +379,8 @@ func (c *CVL) ValidateEditConfig(cfgData []CVLEditConfigData) (cvlErr CVLErrorIn //Check max-element constraint if ret := c.checkMaxElemConstraint(OP_CREATE, tbl); ret != CVL_SUCCESS { cvlErrObj.ErrCode = CVL_SYNTAX_ERROR + cvlErrObj.TableName = tbl + cvlErrObj.Keys = splitKeyComponents(tbl, key) cvlErrObj.ErrAppTag = "too-many-elements" cvlErrObj.Msg = "Max elements limit reached" cvlErrObj.CVLErrDetails = cvlErrorMap[cvlErrObj.ErrCode] @@ -399,6 +401,9 @@ func (c *CVL) ValidateEditConfig(cfgData []CVLEditConfigData) (cvlErr CVLErrorIn for field := range cfgData[i].Data { if (c.checkDeleteConstraint(cfgData, tbl, key, field) != CVL_SUCCESS) { cvlErrObj.ErrCode = CVL_SEMANTIC_ERROR + cvlErrObj.TableName = tbl + cvlErrObj.Keys = splitKeyComponents(tbl, key) + cvlErrObj.Field = field cvlErrObj.Msg = "Validation failed for Delete operation, given instance is in use" cvlErrObj.CVLErrDetails = cvlErrorMap[cvlErrObj.ErrCode] cvlErrObj.ErrAppTag = "instance-in-use" @@ -411,6 +416,7 @@ func (c *CVL) ValidateEditConfig(cfgData []CVLEditConfigData) (cvlErr CVLErrorIn cvlErrObj.ErrCode = CVL_SEMANTIC_ERROR cvlErrObj.Msg = "Mandatory field getting deleted" cvlErrObj.TableName = tbl + cvlErrObj.Keys = splitKeyComponents(tbl, key) cvlErrObj.Field = field cvlErrObj.CVLErrDetails = cvlErrorMap[cvlErrObj.ErrCode] cvlErrObj.ErrAppTag = "mandatory-field-delete" @@ -427,6 +433,8 @@ func (c *CVL) ValidateEditConfig(cfgData []CVLEditConfigData) (cvlErr CVLErrorIn //Now check delete constraints if (c.checkDeleteConstraint(cfgData, tbl, key, "") != CVL_SUCCESS) { cvlErrObj.ErrCode = CVL_SEMANTIC_ERROR + cvlErrObj.TableName = tbl + cvlErrObj.Keys = splitKeyComponents(tbl, key) cvlErrObj.Msg = "Validation failed for Delete operation, given instance is in use" cvlErrObj.CVLErrDetails = cvlErrorMap[cvlErrObj.ErrCode] cvlErrObj.ErrAppTag = "instance-in-use" @@ -498,6 +506,8 @@ func (c *CVL) ValidateEditConfig(cfgData []CVLEditConfigData) (cvlErr CVLErrorIn CVL_LOG(WARNING, "\nValidateEditConfig(): Key = %s already exists", cfgData[i].Key) cvlErrObj.ErrCode = CVL_SEMANTIC_KEY_ALREADY_EXIST cvlErrObj.CVLErrDetails = cvlErrorMap[cvlErrObj.ErrCode] + cvlErrObj.TableName = tbl + cvlErrObj.Keys = splitKeyComponents(tbl, key) return cvlErrObj, CVL_SEMANTIC_KEY_ALREADY_EXIST } else { @@ -514,6 +524,8 @@ func (c *CVL) ValidateEditConfig(cfgData []CVLEditConfigData) (cvlErr CVLErrorIn CVL_LOG(WARNING, "\nValidateEditConfig(): Key = %s does not exist", cfgData[i].Key) cvlErrObj.ErrCode = CVL_SEMANTIC_KEY_NOT_EXIST cvlErrObj.CVLErrDetails = cvlErrorMap[cvlErrObj.ErrCode] + cvlErrObj.TableName = tbl + cvlErrObj.Keys = splitKeyComponents(tbl, key) return cvlErrObj, CVL_SEMANTIC_KEY_NOT_EXIST } @@ -530,6 +542,8 @@ func (c *CVL) ValidateEditConfig(cfgData []CVLEditConfigData) (cvlErr CVLErrorIn CVL_LOG(WARNING, "\nValidateEditConfig(): Key = %s does not exist", cfgData[i].Key) cvlErrObj.ErrCode = CVL_SEMANTIC_KEY_NOT_EXIST cvlErrObj.CVLErrDetails = cvlErrorMap[cvlErrObj.ErrCode] + cvlErrObj.TableName = tbl + cvlErrObj.Keys = splitKeyComponents(tbl, key) return cvlErrObj, CVL_SEMANTIC_KEY_NOT_EXIST } diff --git a/cvl/cvl_error_test.go b/cvl/cvl_error_test.go new file mode 100644 index 000000000000..2d36606eb194 --- /dev/null +++ b/cvl/cvl_error_test.go @@ -0,0 +1,85 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2023 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 cvl_test + +import ( + "reflect" + "strings" + "testing" + + "github.com/Azure/sonic-mgmt-common/cvl" +) + +const ( + CVL_SUCCESS = cvl.CVL_SUCCESS + CVL_SYNTAX_ERROR = cvl.CVL_SYNTAX_ERROR + CVL_SEMANTIC_ERROR = cvl.CVL_SEMANTIC_ERROR + CVL_SYNTAX_MAXIMUM_INVALID = cvl.CVL_SYNTAX_MAXIMUM_INVALID + CVL_SYNTAX_MINIMUM_INVALID = cvl.CVL_SYNTAX_MINIMUM_INVALID +) + +var Success = CVLErrorInfo{ErrCode: CVL_SUCCESS} + +func compareErr(val, exp CVLErrorInfo) bool { + return (val.ErrCode == exp.ErrCode) && + (len(exp.TableName) == 0 || val.TableName == exp.TableName) && + (len(exp.Keys) == 0 || reflect.DeepEqual(val.Keys, exp.Keys)) && + (len(exp.Field) == 0 || val.Field == exp.Field) && + (len(exp.Value) == 0 || val.Value == exp.Value) && + (len(exp.Msg) == 0 || val.Msg == exp.Msg) && + (len(exp.CVLErrDetails) == 0 || val.CVLErrDetails == exp.CVLErrDetails) && + (len(exp.ConstraintErrMsg) == 0 || val.ConstraintErrMsg == exp.ConstraintErrMsg) && + (len(exp.ErrAppTag) == 0 || val.ErrAppTag == exp.ErrAppTag) +} + +func verifyErr(t *testing.T, res, exp CVLErrorInfo) { + t.Helper() + expandMessagePatterns(&exp) + if !compareErr(res, exp) { + t.Fatalf("CVLErrorInfo verification failed\nExpected: %#v\nReceived: %#v", exp, res) + } +} + +func verifyValidateEditConfig(t *testing.T, data []CVLEditConfigData, exp CVLErrorInfo) { + t.Helper() + c := NewTestSession(t) + res, _ := c.ValidateEditConfig(data) + verifyErr(t, res, exp) +} + +func expandMessagePatterns(ex *CVLErrorInfo) { + switch ex.Msg { + case invalidValueErrMessage: + ex.Msg = strings.ReplaceAll(ex.Msg, "{{field}}", ex.Field) + ex.Msg = strings.ReplaceAll(ex.Msg, "{{value}}", ex.Value) + ex.Msg = strings.TrimSuffix(ex.Msg, " \"\"") // if value is empty + case unknownFieldErrMessage: + ex.Msg = strings.ReplaceAll(ex.Msg, "{{field}}", ex.Field) + } +} + +const ( + invalidValueErrMessage = "Field \"{{field}}\" has invalid value \"{{value}}\"" + unknownFieldErrMessage = "Unknown field \"{{field}}\"" + genericValueErrMessage = "Data validation failed" + mustExpressionErrMessage = "Must expression validation failed" + whenExpressionErrMessage = "When expression validation failed" + instanceInUseErrMessage = "Validation failed for Delete operation, given instance is in use" +) diff --git a/cvl/cvl_leafref_test.go b/cvl/cvl_leafref_test.go index 8fa2a9b1a39d..b19e76a43f10 100644 --- a/cvl/cvl_leafref_test.go +++ b/cvl/cvl_leafref_test.go @@ -57,8 +57,9 @@ func TestValidateEditConfig_Create_Chained_Leafref_DepData_Positive(t *testing.T //Prepare data in Redis loadConfigDB(rclient, depDataMap) + defer unloadConfigDB(rclient, depDataMap) - cvSess, _ := cvl.ValidationSessOpen() + cvSess := NewTestSession(t) cfgDataVlan := []cvl.CVLEditConfigData { cvl.CVLEditConfigData { @@ -71,12 +72,9 @@ func TestValidateEditConfig_Create_Chained_Leafref_DepData_Positive(t *testing.T }, } - _, err := cvSess.ValidateEditConfig(cfgDataVlan) + errInfo, _ := cvSess.ValidateEditConfig(cfgDataVlan) + verifyErr(t, errInfo, Success) - if err != cvl.CVL_SUCCESS { //should succeed - t.Errorf("Config Validation failed.") - return - } cfgDataAclRule := []cvl.CVLEditConfigData { cvl.CVLEditConfigData { @@ -95,16 +93,8 @@ func TestValidateEditConfig_Create_Chained_Leafref_DepData_Positive(t *testing.T }, } - - _, err = cvSess.ValidateEditConfig(cfgDataAclRule) - - cvl.ValidationSessClose(cvSess) - - if err != cvl.CVL_SUCCESS { //should succeed - t.Errorf("Config Validation failed.") - } - - unloadConfigDB(rclient, depDataMap) + errInfo, _ = cvSess.ValidateEditConfig(cfgDataAclRule) + verifyErr(t, errInfo, Success) } func TestValidateEditConfig_Create_Leafref_To_NonKey_Positive(t *testing.T) { @@ -119,6 +109,7 @@ func TestValidateEditConfig_Create_Leafref_To_NonKey_Positive(t *testing.T) { //Prepare data in Redis loadConfigDB(rclient, depDataMap) + defer unloadConfigDB(rclient, depDataMap) cfgData := []cvl.CVLEditConfigData { cvl.CVLEditConfigData { @@ -131,17 +122,8 @@ func TestValidateEditConfig_Create_Leafref_To_NonKey_Positive(t *testing.T) { }, }, } - cvSess, _ := cvl.ValidationSessOpen() - - _, err := cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - if err != cvl.CVL_SUCCESS { //should not succeed - t.Errorf("Leafref to non key : Config Validation failed.") - } - unloadConfigDB(rclient, depDataMap) + verifyValidateEditConfig(t, cfgData, Success) } func TestValidateEditConfig_Update_Leafref_To_NonKey_Negative(t *testing.T) { @@ -156,6 +138,7 @@ func TestValidateEditConfig_Update_Leafref_To_NonKey_Negative(t *testing.T) { //Prepare data in Redis loadConfigDB(rclient, depDataMap) + defer unloadConfigDB(rclient, depDataMap) cfgData := []cvl.CVLEditConfigData { cvl.CVLEditConfigData { @@ -168,17 +151,16 @@ func TestValidateEditConfig_Update_Leafref_To_NonKey_Negative(t *testing.T) { }, }, } - cvSess, _ := cvl.ValidationSessOpen() - _, err := cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - if err == cvl.CVL_SUCCESS { //should not succeed - t.Errorf("Leafref to non key : Config Validation failed.") - } - - unloadConfigDB(rclient, depDataMap) + verifyValidateEditConfig(t, cfgData, CVLErrorInfo{ + ErrCode: cvl.CVL_SEMANTIC_DEPENDENT_DATA_MISSING, + TableName: "DEVICE_METADATA", + Keys: []string{"localhost"}, + //Field: "bgp_asn", /* BUG: cvl does not fill field & value */ + //Value: "17698", + ConstraintErrMsg: "No instance found for '17698'", + ErrAppTag: "instance-required", + }) } func TestValidateEditConfig_Create_Leafref_Multi_Key_Positive(t *testing.T) { @@ -211,6 +193,7 @@ func TestValidateEditConfig_Create_Leafref_Multi_Key_Positive(t *testing.T) { //Prepare data in Redis loadConfigDB(rclient, depDataMap) + defer unloadConfigDB(rclient, depDataMap) cfgData := []cvl.CVLEditConfigData { cvl.CVLEditConfigData { @@ -223,17 +206,8 @@ func TestValidateEditConfig_Create_Leafref_Multi_Key_Positive(t *testing.T) { }, }, } - cvSess, _ := cvl.ValidationSessOpen() - _, err := cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - if err != cvl.CVL_SUCCESS { //should succeed - t.Errorf("Leafref to unique key in multiple keys: Config Validation failed.") - } - - unloadConfigDB(rclient, depDataMap) + verifyValidateEditConfig(t, cfgData, Success) } func TestValidateEditConfig_Create_Leafref_Multi_Key_Negative(t *testing.T) { @@ -260,6 +234,7 @@ func TestValidateEditConfig_Create_Leafref_Multi_Key_Negative(t *testing.T) { //Prepare data in Redis loadConfigDB(rclient, depDataMap) + defer unloadConfigDB(rclient, depDataMap) cfgData := []cvl.CVLEditConfigData { cvl.CVLEditConfigData { @@ -272,18 +247,16 @@ func TestValidateEditConfig_Create_Leafref_Multi_Key_Negative(t *testing.T) { }, }, } - cvSess, _ := cvl.ValidationSessOpen() - - _, err := cvSess.ValidateEditConfig(cfgData) - cvl.ValidationSessClose(cvSess) - - if err == cvl.CVL_SUCCESS { - //should not succeed - t.Errorf("Leafref to unique key in multiple keys: Config Validation failed.") - } - - unloadConfigDB(rclient, depDataMap) + verifyValidateEditConfig(t, cfgData, CVLErrorInfo{ + ErrCode: cvl.CVL_SEMANTIC_DEPENDENT_DATA_MISSING, + TableName: "TAM_INT_IFA_FLOW_TABLE", + Keys: []string{"Flow_1"}, + // Field: "acl-rule-name", /* BUG: cvl does not fill field & value */ + // Value: "Rule1", + ConstraintErrMsg: "No instance found for 'Rule1'", + ErrAppTag: "instance-required", + }) } func TestValidateEditConfig_Create_Leafref_With_Other_DataType_In_Union_Positive(t *testing.T) { @@ -297,7 +270,7 @@ func TestValidateEditConfig_Create_Leafref_With_Other_DataType_In_Union_Positive } loadConfigDB(rclient, depDataMap) - cvSess, _ := cvl.ValidationSessOpen() + defer unloadConfigDB(rclient, depDataMap) cfgData := []cvl.CVLEditConfigData{ cvl.CVLEditConfigData{ @@ -312,17 +285,7 @@ func TestValidateEditConfig_Create_Leafref_With_Other_DataType_In_Union_Positive }, } - - cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - if err != cvl.CVL_SUCCESS { - //Should succeed - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - } - - unloadConfigDB(rclient, depDataMap) + verifyValidateEditConfig(t, cfgData, Success) } func TestValidateEditConfig_Create_Leafref_With_Other_DataType_In_Union_Negative(t *testing.T) { @@ -336,7 +299,7 @@ func TestValidateEditConfig_Create_Leafref_With_Other_DataType_In_Union_Negative } loadConfigDB(rclient, depDataMap) - cvSess, _ := cvl.ValidationSessOpen() + defer unloadConfigDB(rclient, depDataMap) cfgData := []cvl.CVLEditConfigData{ cvl.CVLEditConfigData{ @@ -351,17 +314,14 @@ func TestValidateEditConfig_Create_Leafref_With_Other_DataType_In_Union_Negative }, } - - cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - if err == cvl.CVL_SUCCESS { - //Should succeed - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - } - - unloadConfigDB(rclient, depDataMap) + verifyValidateEditConfig(t, cfgData, CVLErrorInfo{ + ErrCode: CVL_SYNTAX_ERROR, + TableName: "STP_PORT", + Keys: []string{"Test12"}, + Field: "ifname", + Value: "Test12", + Msg: invalidValueErrMessage, + }) } func TestValidateEditConfig_Create_Leafref_With_Other_DataType_In_Union_Non_Existing_Negative(t *testing.T) { @@ -375,7 +335,7 @@ func TestValidateEditConfig_Create_Leafref_With_Other_DataType_In_Union_Non_Exis } loadConfigDB(rclient, depDataMap) - cvSess, _ := cvl.ValidationSessOpen() + defer unloadConfigDB(rclient, depDataMap) cfgData := []cvl.CVLEditConfigData{ cvl.CVLEditConfigData{ @@ -390,17 +350,13 @@ func TestValidateEditConfig_Create_Leafref_With_Other_DataType_In_Union_Non_Exis }, } - - cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - if err == cvl.CVL_SUCCESS { - //Should fail as leafref does not exist - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - } - - unloadConfigDB(rclient, depDataMap) + verifyValidateEditConfig(t, cfgData, CVLErrorInfo{ + ErrCode: cvl.CVL_SEMANTIC_DEPENDENT_DATA_MISSING, + TableName: "STP_PORT", + Keys: []string{"Ethernet3999"}, + ConstraintErrMsg: "No instance found for 'Ethernet3999'", + ErrAppTag: "instance-required", + }) } func TestValidateEditConfig_Delete_Leafref(t *testing.T) { diff --git a/cvl/cvl_must_test.go b/cvl/cvl_must_test.go index 4f677b9217d3..89684d6df066 100644 --- a/cvl/cvl_must_test.go +++ b/cvl/cvl_must_test.go @@ -20,7 +20,6 @@ package cvl_test import ( - "fmt" "testing" "github.com/Azure/sonic-mgmt-common/cvl" ) @@ -72,6 +71,7 @@ func TestValidateEditConfig_Delete_Must_Check_Positive(t *testing.T) { //Prepare data in Redis loadConfigDB(rclient, depDataMap) + defer unloadConfigDB(rclient, depDataMap) cfgDataAclRule := []cvl.CVLEditConfigData { cvl.CVLEditConfigData { @@ -83,17 +83,7 @@ func TestValidateEditConfig_Delete_Must_Check_Positive(t *testing.T) { }, } - cvSess, _ := cvl.ValidationSessOpen() - - cvlErrObj, err := cvSess.ValidateEditConfig(cfgDataAclRule) - - cvl.ValidationSessClose(cvSess) - - if err != cvl.CVL_SUCCESS { //should not succeed - t.Errorf("Config Validation failed. %v", cvlErrObj) - } - - unloadConfigDB(rclient, depDataMap) + verifyValidateEditConfig(t, cfgDataAclRule, Success) } func TestValidateEditConfig_Delete_Must_Check_Negative(t *testing.T) { @@ -132,6 +122,7 @@ func TestValidateEditConfig_Delete_Must_Check_Negative(t *testing.T) { //Prepare data in Redis loadConfigDB(rclient, depDataMap) + defer unloadConfigDB(rclient, depDataMap) cfgDataAclRule := []cvl.CVLEditConfigData { cvl.CVLEditConfigData { @@ -143,17 +134,15 @@ func TestValidateEditConfig_Delete_Must_Check_Negative(t *testing.T) { }, } - cvSess, _ := cvl.ValidationSessOpen() - - cvlErrObj, err := cvSess.ValidateEditConfig(cfgDataAclRule) - - cvl.ValidationSessClose(cvSess) - - if err == cvl.CVL_SUCCESS { //should not succeed - t.Errorf("Config Validation failed. %v", cvlErrObj) - } - - unloadConfigDB(rclient, depDataMap) + verifyValidateEditConfig(t, cfgDataAclRule, CVLErrorInfo{ + ErrCode: CVL_SEMANTIC_ERROR, + TableName: "ACL_RULE", + Keys: []string{"TestACL1", "Rule1"}, + Field: "aclname", + Value: "TestACL1", + Msg: mustExpressionErrMessage, + ConstraintErrMsg: "Ports are already bound to this rule.", + }) } func TestValidateEditConfig_Create_ErrAppTag_In_Must_Negative(t *testing.T) { @@ -165,23 +154,17 @@ func TestValidateEditConfig_Create_ErrAppTag_In_Must_Negative(t *testing.T) { "VLAN|Vlan1001", map[string]string{ "vlanid": "102", - "members@": "Ethernet24,Ethernet8", }, }, } - cvSess, _ := cvl.ValidationSessOpen() - - cvlErrInfo, retCode := cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) - - if retCode == cvl.CVL_SUCCESS { - t.Errorf("Config Validation failed -- error details %v %v", cvlErrInfo, retCode) - } - + verifyValidateEditConfig(t, cfgData, CVLErrorInfo{ + ErrCode: CVL_SEMANTIC_ERROR, + TableName: "VLAN", + //Keys: []string{"Vlan1001"}, <<<< BUG: key is not filled if must expr is defined on list + Msg: mustExpressionErrMessage, + ErrAppTag: "vlan-invalid", + }) } func TestValidateEditConfig_MustExp_With_Default_Value_Positive(t *testing.T) { @@ -208,21 +191,9 @@ func TestValidateEditConfig_MustExp_With_Default_Value_Positive(t *testing.T) { } loadConfigDB(rclient, depDataMap) + defer unloadConfigDB(rclient, depDataMap) - cvSess, _ := cvl.ValidationSessOpen() - - //Try to add second element - cvlErrInfo, _ := cvSess.ValidateEditConfig(cfgData) - - - unloadConfigDB(rclient, depDataMap) - - cvl.ValidationSessClose(cvSess) - - if cvlErrInfo.ErrCode != cvl.CVL_SUCCESS { - t.Errorf("CFG_L2MC_TABLE creation should succeed %v", cvlErrInfo) - } - + verifyValidateEditConfig(t, cfgData, Success) } func TestValidateEditConfig_MustExp_With_Default_Value_Negative(t *testing.T) { @@ -249,21 +220,29 @@ func TestValidateEditConfig_MustExp_With_Default_Value_Negative(t *testing.T) { } loadConfigDB(rclient, depDataMap) + defer unloadConfigDB(rclient, depDataMap) - cvSess, _ := cvl.ValidationSessOpen() + cvSess := NewTestSession(t) //Try to add second element cvlErrInfo, _ := cvSess.ValidateEditConfig(cfgData) + // Both query-interval and query-max-response-time have must expressions checking each other.. + // Order of evaluation is random + expField, expValue := "query-interval", "9" + if cvlErrInfo.Field == "query-max-response-time" { + expField, expValue = "query-max-response-time", "10" + } - unloadConfigDB(rclient, depDataMap) - - cvl.ValidationSessClose(cvSess) - - if cvlErrInfo.ErrCode == cvl.CVL_SUCCESS { - t.Errorf("CFG_L2MC_TABLE creation should fail %v", cvlErrInfo) - } - + verifyErr(t, cvlErrInfo, CVLErrorInfo{ + ErrCode: CVL_SEMANTIC_ERROR, + TableName: "CFG_L2MC_TABLE", + Keys: []string{"Vlan2002"}, + Field: expField, // "query-interval" or "query-max-response-time" + Value: expValue, // "9" or "10" + Msg: mustExpressionErrMessage, + ConstraintErrMsg: "Invalid IGMP Snooping query interval value.", + }) } func TestValidateEditConfig_MustExp_Chained_Predicate_Positive(t *testing.T) { @@ -346,21 +325,17 @@ func TestValidateEditConfig_MustExp_Chained_Predicate_Positive(t *testing.T) { } loadConfigDB(rclient, depDataMap) + defer unloadConfigDB(rclient, depDataMap) - cvSess, _ := cvl.ValidationSessOpen() - - //Try to add second element - cvlErrInfo, _ := cvSess.ValidateEditConfig(cfgData) - - - unloadConfigDB(rclient, depDataMap) - - cvl.ValidationSessClose(cvSess) - - if cvlErrInfo.ErrCode == cvl.CVL_SUCCESS { - t.Errorf("INTERFACE creating failed failed -- error details %v", cvlErrInfo) - } - + verifyValidateEditConfig(t, cfgData, CVLErrorInfo{ + ErrCode: CVL_SEMANTIC_ERROR, + //TableName: "VLAN_INTERFACE", <<< BUG: cvl returns VLAN_INTERFACE_IPADDR + Keys: []string{"Vlan702", "1.1.2.0/32"}, + Field: "vlanName", + Value: "Vlan702", + Msg: mustExpressionErrMessage, + ConstraintErrMsg: "Vlan and port being member of same vlan can't have same IP prefix.", + }) } func TestValidateEditConfig_MustExp_Within_Same_Table_Negative(t *testing.T) { @@ -377,16 +352,16 @@ func TestValidateEditConfig_MustExp_Within_Same_Table_Negative(t *testing.T) { }, } - cvSess, _ := cvl.ValidationSessOpen() - - cvlErrInfo, _ := cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - if cvlErrInfo.ErrCode == cvl.CVL_SUCCESS { - t.Errorf("TAM_COLLECTOR_TABLE creation should fail, %v", cvlErrInfo) - } - + verifyValidateEditConfig(t, cfgData, CVLErrorInfo{ + ErrCode: CVL_SEMANTIC_ERROR, + TableName: "TAM_COLLECTOR_TABLE", + Keys: []string{"Col10"}, + Field: "ipaddress-type", + Value: "ipv6", + Msg: mustExpressionErrMessage, + ConstraintErrMsg: "IP address and IP address type does not match.", + ErrAppTag: "ipaddres-type-mismatch", + }) } //Check if all data is fetched for xpath without predicate @@ -417,19 +392,9 @@ func TestValidateEditConfig_MustExp_Without_Predicate_Positive(t *testing.T) { } loadConfigDB(rclient, depDataMap) + defer unloadConfigDB(rclient, depDataMap) - cvSess, _ := cvl.ValidationSessOpen() - - cvlErrInfo, _ := cvSess.ValidateEditConfig(cfgData) - cvlErrInfo, _ = cvSess.ValidateEditConfig(cfgData) //second time call should succeed also - - cvl.ValidationSessClose(cvSess) - - if cvlErrInfo.ErrCode != cvl.CVL_SUCCESS { - t.Errorf("No predicate - config validation should succeed, %v", cvlErrInfo) - } - - unloadConfigDB(rclient, depDataMap) + verifyValidateEditConfig(t, cfgData, Success) } func TestValidateEditConfig_MustExp_Non_Key_As_Predicate_Negative(t *testing.T) { @@ -469,20 +434,17 @@ func TestValidateEditConfig_MustExp_Non_Key_As_Predicate_Negative(t *testing.T) } loadConfigDB(rclient, depDataMap) + defer unloadConfigDB(rclient, depDataMap) - cvSess, _ := cvl.ValidationSessOpen() - - cvlErrInfo, _ := cvSess.ValidateEditConfig(cfgData)//should fail - cvlErrInfo, _ = cvSess.ValidateEditConfig(cfgData) //should fail again - cvlErrInfo, _ = cvSess.ValidateEditConfig(cfgData) //should fail again - - cvl.ValidationSessClose(cvSess) - - if cvlErrInfo.ErrCode == cvl.CVL_SUCCESS { - t.Errorf("Non key as predicate - config validation should fail, %v", cvlErrInfo) - } - - unloadConfigDB(rclient, depDataMap) + verifyValidateEditConfig(t, cfgData, CVLErrorInfo{ + ErrCode: CVL_SEMANTIC_ERROR, + TableName: "VXLAN_TUNNEL_MAP", + Keys: []string{"tun1", "vmap2"}, + Field: "vni", + Value: "300", + Msg: mustExpressionErrMessage, + ErrAppTag: "not-unique-vni", + }) } func TestValidateEditConfig_MustExp_Non_Key_As_Predicate_In_External_Table_Positive(t *testing.T) { @@ -532,18 +494,9 @@ func TestValidateEditConfig_MustExp_Non_Key_As_Predicate_In_External_Table_Posit } loadConfigDB(rclient, depDataMap) + defer unloadConfigDB(rclient, depDataMap) - cvSess, _ := cvl.ValidationSessOpen() - - cvlErrInfo, _ := cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - if cvlErrInfo.ErrCode != cvl.CVL_SUCCESS { - t.Errorf("Non key as predicate in external table - config validation should succeed, %v", cvlErrInfo) - } - - unloadConfigDB(rclient, depDataMap) + verifyValidateEditConfig(t, cfgData, Success) } func TestValidateEditConfig_MustExp_Update_Leaf_List_Positive(t *testing.T) { @@ -556,6 +509,7 @@ func TestValidateEditConfig_MustExp_Update_Leaf_List_Positive(t *testing.T) { } loadConfigDB(rclient, depDataMap) + defer unloadConfigDB(rclient, depDataMap) cfgData := []cvl.CVLEditConfigData{ cvl.CVLEditConfigData{ @@ -568,17 +522,7 @@ func TestValidateEditConfig_MustExp_Update_Leaf_List_Positive(t *testing.T) { }, } - cvSess, _ := cvl.ValidationSessOpen() - - cvlErrInfo, retCode := cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - if retCode != cvl.CVL_SUCCESS { - t.Errorf("Config Validation failed -- error details %v %v", cvlErrInfo, retCode) - } - - unloadConfigDB(rclient, depDataMap) + verifyValidateEditConfig(t, cfgData, Success) } func TestValidateEditConfig_MustExp_Add_NULL(t *testing.T) { @@ -627,10 +571,8 @@ func testNullAdd(data ...cvl.CVLEditConfigData) func(*testing.T) { var cfgData []cvl.CVLEditConfigData for i, d := range data { cfgData = append(cfgData, d) - errInfo, status := session.ValidateEditConfig(cfgData) - if status != cvl.CVL_SUCCESS { - t.Fatalf("unexpetced error: %v", errInfo) - } + errInfo, _ := session.ValidateEditConfig(cfgData) + verifyErr(t, errInfo, Success) cfgData[i].VType = cvl.VALIDATE_NONE // dont validate for next op } diff --git a/cvl/cvl_syntax.go b/cvl/cvl_syntax.go index 397dba147a19..f60ae86a14c7 100644 --- a/cvl/cvl_syntax.go +++ b/cvl/cvl_syntax.go @@ -137,7 +137,7 @@ parent *yparser.YParserNode, multileaf *[]*yparser.YParserLeafValue) CVLErrorInf jsonFieldNode.FirstChild.Data, &batchInnerListLeaf) if errObj := c.yp.AddMultiLeafNodes(modelInfo.tableInfo[tableName].module, listNode, batchInnerListLeaf); errObj.ErrCode != yparser.YP_SUCCESS { - cvlErrObj = createCVLErrObj(errObj) + cvlErrObj = createCVLErrObj(errObj, jsonNode) CVL_LOG(WARNING, "Failed to create innner list leaf nodes, data = %v", batchInnerListLeaf) return cvlErrObj } @@ -266,7 +266,7 @@ func (c *CVL) generateTableData(config bool, jsonNode *jsonquery.Node)(*yparser. TRACE_LOG(TRACE_CACHE, "Starting batch leaf creation - %v\n", c.batchLeaf) //process batch leaf creation if errObj := c.yp.AddMultiLeafNodes(modelInfo.tableInfo[tableName].module, listNode, c.batchLeaf); errObj.ErrCode != yparser.YP_SUCCESS { - cvlErrObj = createCVLErrObj(errObj) + cvlErrObj = createCVLErrObj(errObj, jsonNode) CVL_LOG(WARNING, "Failed to create leaf nodes, data = %v", c.batchLeaf) return nil, cvlErrObj } diff --git a/cvl/cvl_test.go b/cvl/cvl_test.go index 3e6b10f2a620..c446bdf74450 100644 --- a/cvl/cvl_test.go +++ b/cvl/cvl_test.go @@ -20,21 +20,33 @@ package cvl_test import ( - "github.com/Azure/sonic-mgmt-common/cvl" "encoding/json" "fmt" - "github.com/go-redis/redis/v7" "io/ioutil" "os" "os/exec" "reflect" "sort" "strings" - //"syscall" "testing" - "runtime" + + "github.com/Azure/sonic-mgmt-common/cvl" . "github.com/Azure/sonic-mgmt-common/cvl/internal/util" - //"github.com/Azure/sonic-mgmt-common/cvl/internal/yparser" + "github.com/go-redis/redis/v7" +) + +// type aliases +type CVLEditConfigData = cvl.CVLEditConfigData +type CVLErrorInfo = cvl.CVLErrorInfo +type CVLRetCode = cvl.CVLRetCode + +// enum aliases +const ( + VALIDATE_NONE = cvl.VALIDATE_NONE + VALIDATE_ALL = cvl.VALIDATE_ALL + OP_CREATE = cvl.OP_CREATE + OP_UPDATE = cvl.OP_UPDATE + OP_DELETE = cvl.OP_DELETE ) type testEditCfgData struct { @@ -46,7 +58,6 @@ type testEditCfgData struct { var rclient *redis.Client var port_map map[string]interface{} -var filehandle *os.File var loadDeviceDataMap bool var deviceDataMap = map[string]interface{} { @@ -72,6 +83,10 @@ var depDataMap = map[string]interface{} { "admin_status": "up", "mtu": "9100", }, + "PortChannel003": map[string]interface{}{ + "admin_status": "up", + "mtu": "9100", + }, }, "PORTCHANNEL_MEMBER": map[string]interface{} { "PortChannel001|Ethernet4": map[string] interface{} { @@ -202,15 +217,6 @@ func loadConfigDB(rclient *redis.Client, mpi map[string]interface{}) { } } -func compareErrorDetails(cvlErr cvl.CVLErrorInfo, expCode cvl.CVLRetCode, errAppTag string, constraintmsg string) bool { - - if ((cvlErr.ErrCode == expCode) && ((cvlErr.ErrAppTag == errAppTag) || (cvlErr.ConstraintErrMsg == constraintmsg))) { - return true - } - - return false -} - func getConfigDbClient() *redis.Client { rclient := NewDbClient("CONFIG_DB") @@ -304,26 +310,6 @@ func clearDb() { } } - -func WriteToFile(message string) { - pc := make([]uintptr, 10) - runtime.Callers(2, pc) - f := runtime.FuncForPC(pc[0]) - - message = f.Name()+ "\n" + message - - if _, err := filehandle.Write([]byte(message)); err != nil { - fmt.Println("Unable to write to cvl test log file") - } - - message = "\n-------------------------------------------------\n" - - - if _, err := filehandle.Write([]byte(message)); err != nil { - fmt.Println("Unable to write to cvl test log file") - } -} - /* Setup before starting of test. */ func TestMain(m *testing.M) { @@ -342,15 +328,6 @@ func TestMain(m *testing.M) { } - os.Remove("testdata/cvl_test_details.log") - - filehandle, err = os.OpenFile("testdata/cvl_test_details.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) - - if err != nil { - fmt.Println("Could not open the log file for writing.") - } - - //Clear all tables which are used for testing clearDb() @@ -374,10 +351,6 @@ func TestMain(m *testing.M) { rclient.Close() rclient.FlushDB() - if err := filehandle.Close(); err != nil { - //log.Fatal(err) - } - if (redisAlreadyRunning == false) { //If Redis was not already running, close the instance that we ran _, err := exec.Command("/bin/sh", "-c", "sudo /etc/init.d/redis-server stop").Output() @@ -388,7 +361,6 @@ func TestMain(m *testing.M) { } os.Exit(code) - } //Test Initialize() API @@ -417,6 +389,16 @@ func TestFinish(t *testing.T) { cvl.Initialize() } +func NewTestSession(t *testing.T) *cvl.CVL { + t.Helper() + c, status := cvl.ValidationSessOpen() + if status != CVL_SUCCESS { + t.Fatalf("ValidationSessOpen failed; err=%v", status) + } + t.Cleanup(func() { cvl.ValidationSessClose(c) }) + return c +} + /* ValidateEditConfig with user input in file . */ func TestValidateEditConfig_CfgFile(t *testing.T) { @@ -675,6 +657,7 @@ func TestValidateEditConfig_Create_Syntax_Valid_FieldValue(t *testing.T) { } loadConfigDB(rclient, depDataMap) + defer unloadConfigDB(rclient, depDataMap) cfgData := []cvl.CVLEditConfigData{ cvl.CVLEditConfigData{ @@ -693,18 +676,7 @@ func TestValidateEditConfig_Create_Syntax_Valid_FieldValue(t *testing.T) { }, } - cvSess, _ := cvl.ValidationSessOpen() - - cvlErrInfo, retCode := cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - if retCode != cvl.CVL_SUCCESS { - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - } - - unloadConfigDB(rclient, depDataMap) - + verifyValidateEditConfig(t, cfgData, Success) } /* API to test edit config with invalid field value. */ @@ -723,16 +695,16 @@ func TestValidateEditConfig_Create_Syntax_CableLength(t *testing.T) { }, } - cvSess, _ := cvl.ValidationSessOpen() - - cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - if err == cvl.CVL_SUCCESS { - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - } - + verifyValidateEditConfig(t, cfgData, CVLErrorInfo{ + ErrCode: CVL_SYNTAX_ERROR, + TableName: "CABLE_LENGTH", + Keys: []string{"AZURE"}, + Field: "port", + Value: "", // BUG: cvl is not filling value "PortChannel16" + Msg: invalidValueErrMessage, + ConstraintErrMsg: "Invalid interface name", + ErrAppTag: "interface-name-invalid", + }) } /* API to test edit config with invalid field value. */ @@ -746,16 +718,14 @@ func TestValidateEditConfig_Create_Syntax_Invalid_FieldValue(t *testing.T) { }, } - cvSess, _ := cvl.ValidationSessOpen() - - cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - if err == cvl.CVL_SUCCESS { - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - } - + verifyValidateEditConfig(t, cfgData, CVLErrorInfo{ + ErrCode: CVL_SYNTAX_ERROR, + TableName: "ACL_TABLE", + Keys: []string{"TestACL1"}, + Field: "type", + Value: "junk", + Msg: invalidValueErrMessage, + }) } /* API to test edit config with valid syntax. */ @@ -770,6 +740,7 @@ func TestValidateEditConfig_Create_Syntax_Invalid_PacketAction_Negative(t *testi } loadConfigDB(rclient, depDataMap) + defer unloadConfigDB(rclient, depDataMap) cfgData := []cvl.CVLEditConfigData{ cvl.CVLEditConfigData{ @@ -788,19 +759,14 @@ func TestValidateEditConfig_Create_Syntax_Invalid_PacketAction_Negative(t *testi }, } - cvSess, _ := cvl.ValidationSessOpen() - - cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) - - if err == cvl.CVL_SUCCESS { - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - } - unloadConfigDB(rclient, depDataMap) - + verifyValidateEditConfig(t, cfgData, CVLErrorInfo{ + ErrCode: CVL_SYNTAX_ERROR, + TableName: "ACL_RULE", + Keys: []string{"TestACL1", "Rule1"}, + Field: "PACKET_ACTION", + Value: "FORWARD777", + Msg: invalidValueErrMessage, + }) } /* API to test edit config with valid syntax. */ @@ -815,6 +781,7 @@ func TestValidateEditConfig_Create_Syntax_Invalid_SrcPrefix_Negative(t *testing. } loadConfigDB(rclient, depDataMap) + defer unloadConfigDB(rclient, depDataMap) cfgData := []cvl.CVLEditConfigData{ cvl.CVLEditConfigData{ @@ -833,19 +800,14 @@ func TestValidateEditConfig_Create_Syntax_Invalid_SrcPrefix_Negative(t *testing. }, } - cvSess, _ := cvl.ValidationSessOpen() - - cvl.ValidationSessClose(cvSess) - - cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) - - WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) - - if err == cvl.CVL_SUCCESS { - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - } - unloadConfigDB(rclient, depDataMap) - + verifyValidateEditConfig(t, cfgData, CVLErrorInfo{ + ErrCode: CVL_SYNTAX_ERROR, + TableName: "ACL_RULE", + Keys: []string{"TestACL1", "Rule1"}, + Field: "SRC_IP", + Value: "10.1.1.1/3288888", + Msg: invalidValueErrMessage, + }) } /* API to test edit config with valid syntax. */ @@ -861,6 +823,8 @@ func TestValidateEditConfig_Create_Syntax_InvalidIPAddress_Negative(t *testing.T } loadConfigDB(rclient, depDataMap) + defer unloadConfigDB(rclient, depDataMap) + cfgData := []cvl.CVLEditConfigData{ cvl.CVLEditConfigData{ cvl.VALIDATE_ALL, @@ -878,19 +842,14 @@ func TestValidateEditConfig_Create_Syntax_InvalidIPAddress_Negative(t *testing.T }, } - cvSess, _ := cvl.ValidationSessOpen() - - cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) - - if err == cvl.CVL_SUCCESS { - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - } - unloadConfigDB(rclient, depDataMap) - + verifyValidateEditConfig(t, cfgData, CVLErrorInfo{ + ErrCode: CVL_SYNTAX_ERROR, + TableName: "ACL_RULE", + Keys: []string{"TestACL1", "Rule1"}, + Field: "SRC_IP", + Value: "10.1a.1.1/32", + Msg: invalidValueErrMessage, + }) } /* API to test edit config with valid syntax. */ @@ -905,6 +864,7 @@ func TestValidateEditConfig_Create_Syntax_OutofBound_Negative(t *testing.T) { } loadConfigDB(rclient, depDataMap) + defer unloadConfigDB(rclient, depDataMap) cfgData := []cvl.CVLEditConfigData{ cvl.CVLEditConfigData{ @@ -923,19 +883,14 @@ func TestValidateEditConfig_Create_Syntax_OutofBound_Negative(t *testing.T) { }, } - cvSess, _ := cvl.ValidationSessOpen() - - cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) - - if err == cvl.CVL_SUCCESS { - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - } - unloadConfigDB(rclient, depDataMap) - + verifyValidateEditConfig(t, cfgData, CVLErrorInfo{ + ErrCode: CVL_SYNTAX_ERROR, + TableName: "ACL_RULE", + Keys: []string{"TestACL1", "Rule1"}, + Field: "L4_SRC_PORT", + Value: "19099090909090", + Msg: invalidValueErrMessage, + }) } /* API to test edit config with valid syntax. */ @@ -950,7 +905,7 @@ func TestValidateEditConfig_Create_Syntax_InvalidProtocol_Negative(t *testing.T) } loadConfigDB(rclient, depDataMap) - + defer unloadConfigDB(rclient, depDataMap) cfgData := []cvl.CVLEditConfigData{ cvl.CVLEditConfigData{ @@ -969,19 +924,14 @@ func TestValidateEditConfig_Create_Syntax_InvalidProtocol_Negative(t *testing.T) }, } - cvSess, _ := cvl.ValidationSessOpen() - - cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) - - if err == cvl.CVL_SUCCESS { - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - } - - unloadConfigDB(rclient, depDataMap) + verifyValidateEditConfig(t, cfgData, CVLErrorInfo{ + ErrCode: CVL_SYNTAX_ERROR, + TableName: "ACL_RULE", + Keys: []string{"TestACL1", "Rule1"}, + Field: "IP_PROTOCOL", + Value: "10388888", + Msg: invalidValueErrMessage, + }) } /* API to test edit config with valid syntax. */ @@ -998,6 +948,7 @@ func TestValidateEditConfig_Create_Syntax_InvalidRange_Negative(t *testing.T) { } loadConfigDB(rclient, depDataMap) + defer unloadConfigDB(rclient, depDataMap) cfgData := []cvl.CVLEditConfigData{ cvl.CVLEditConfigData{ @@ -1016,19 +967,14 @@ func TestValidateEditConfig_Create_Syntax_InvalidRange_Negative(t *testing.T) { }, } - cvSess, _ := cvl.ValidationSessOpen() - - cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) - - if err == cvl.CVL_SUCCESS { - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - } - unloadConfigDB(rclient, depDataMap) - + verifyValidateEditConfig(t, cfgData, CVLErrorInfo{ + ErrCode: CVL_SYNTAX_ERROR, + TableName: "ACL_RULE", + Keys: []string{"TestACL1", "Rule1"}, + Field: "L4_DST_PORT_RANGE", + Value: "777779000-12000", + Msg: invalidValueErrMessage, + }) } /* API to test edit config with valid syntax. */ @@ -1052,18 +998,15 @@ func TestValidateEditConfig_Create_Syntax_InvalidCharNEw_Negative(t *testing.T) }, } - cvSess, _ := cvl.ValidationSessOpen() - - cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) - - if err == cvl.CVL_SUCCESS { - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - } - + verifyValidateEditConfig(t, cfgData, CVLErrorInfo{ + ErrCode: cvl.CVL_SEMANTIC_DEPENDENT_DATA_MISSING, + TableName: "ACL_RULE", + Keys: []string{"TestACL1jjjj", "Rule1"}, + // Field: "aclname", /* BUG: cvl is not filling Field & Value */ + // Value: "TestACL1jjjj", + ConstraintErrMsg: "No instance found for 'TestACL1jjjj'", + ErrAppTag: "instance-required", + }) } func TestValidateEditConfig_Create_Syntax_SpecialChar_Positive(t *testing.T) { @@ -1077,7 +1020,7 @@ func TestValidateEditConfig_Create_Syntax_SpecialChar_Positive(t *testing.T) { } loadConfigDB(rclient, depDataMap) - + defer unloadConfigDB(rclient, depDataMap) cfgData := []cvl.CVLEditConfigData{ cvl.CVLEditConfigData{ @@ -1096,18 +1039,7 @@ func TestValidateEditConfig_Create_Syntax_SpecialChar_Positive(t *testing.T) { }, } - cvSessNew, _ := cvl.ValidationSessOpen() - - cvlErrInfo, err := cvSessNew.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSessNew) - - if err != cvl.CVL_SUCCESS { //Should succeed - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - } - - unloadConfigDB(rclient, depDataMap) - + verifyValidateEditConfig(t, cfgData, Success) } func TestValidateEditConfig_Create_Syntax_InvalidKeyName_Negative(t *testing.T) { @@ -1129,18 +1061,10 @@ func TestValidateEditConfig_Create_Syntax_InvalidKeyName_Negative(t *testing.T) }, } - cvSess, _ := cvl.ValidationSessOpen() - - cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) - - if err == cvl.CVL_SUCCESS { - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - } - + verifyValidateEditConfig(t, cfgData, CVLErrorInfo{ + ErrCode: CVL_SYNTAX_ERROR, + Msg: "Invalid table or key for AC&&***L_RULE|TestACL1|Rule1", + }) } func TestValidateEditConfig_Create_Semantic_AdditionalInvalidNode_Negative(t *testing.T) { @@ -1173,19 +1097,13 @@ func TestValidateEditConfig_Create_Semantic_AdditionalInvalidNode_Negative(t *te }, } - cvSess, _ := cvl.ValidationSessOpen() - - cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) - - if err == cvl.CVL_SUCCESS { - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - } - - unloadConfigDB(rclient, depDataMap) + verifyValidateEditConfig(t, cfgData, CVLErrorInfo{ + ErrCode: CVL_SYNTAX_ERROR, + TableName: "ACL_RULE", + Keys: []string{"TestACL1", "Rule1"}, + Field: "extra", + Msg: unknownFieldErrMessage, + }) } func TestValidateEditConfig_Create_Semantic_MissingMandatoryNode_Negative(t *testing.T) { @@ -1201,17 +1119,13 @@ func TestValidateEditConfig_Create_Semantic_MissingMandatoryNode_Negative(t *tes }, } - cvSess, _ := cvl.ValidationSessOpen() - - cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) - - if err == cvl.CVL_SUCCESS { - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - } + verifyValidateEditConfig(t, cfgData, CVLErrorInfo{ + ErrCode: cvl.CVL_SYNTAX_MISSING_FIELD, + TableName: "VXLAN_TUNNEL", + Keys: []string{"Tunnel1"}, + Field: "src_ip", + Msg: invalidValueErrMessage, + }) } func TestValidateEditConfig_Create_Syntax_Invalid_Negative(t *testing.T) { @@ -1233,18 +1147,10 @@ func TestValidateEditConfig_Create_Syntax_Invalid_Negative(t *testing.T) { }, } - cvSess, _ := cvl.ValidationSessOpen() - - cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) - - if err == cvl.CVL_SUCCESS { - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - } - + verifyValidateEditConfig(t, cfgData, CVLErrorInfo{ + ErrCode: CVL_SYNTAX_ERROR, + Msg: "Invalid table or key for ACL_RULERule1", + }) } func TestValidateEditConfig_Create_Syntax_IncompleteKey_Negative(t *testing.T) { @@ -1266,18 +1172,12 @@ func TestValidateEditConfig_Create_Syntax_IncompleteKey_Negative(t *testing.T) { }, } - cvSess, _ := cvl.ValidationSessOpen() - - cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) - - if err == cvl.CVL_SUCCESS { - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - } - + verifyValidateEditConfig(t, cfgData, CVLErrorInfo{ + ErrCode: cvl.CVL_SYNTAX_MISSING_FIELD, + TableName: "ACL_RULE", + Field: "aclname", + Msg: invalidValueErrMessage, + }) } func TestValidateEditConfig_Create_Syntax_InvalidKey_Negative(t *testing.T) { @@ -1299,18 +1199,10 @@ func TestValidateEditConfig_Create_Syntax_InvalidKey_Negative(t *testing.T) { }, } - cvSess, _ := cvl.ValidationSessOpen() - - cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) - - if err == cvl.CVL_SUCCESS { - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - } - + verifyValidateEditConfig(t, cfgData, CVLErrorInfo{ + ErrCode: CVL_SYNTAX_ERROR, + Msg: "Invalid table or key for |Rule1", + }) } /* @@ -1446,18 +1338,10 @@ func TestValidateEditConfig_Delete_Syntax_InvalidKey_Negative(t *testing.T) { }, } - cvSess, _ := cvl.ValidationSessOpen() - - cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) - - if err == cvl.CVL_SUCCESS { - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - } - + verifyValidateEditConfig(t, cfgData, CVLErrorInfo{ + ErrCode: CVL_SYNTAX_ERROR, + Msg: "Invalid table or key for |Rule1", + }) } func TestValidateEditConfig_Update_Syntax_InvalidKey_Negative(t *testing.T) { @@ -1479,21 +1363,13 @@ func TestValidateEditConfig_Update_Syntax_InvalidKey_Negative(t *testing.T) { }, } - cvSess, _ := cvl.ValidationSessOpen() - - cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) + verifyValidateEditConfig(t, cfgData, CVLErrorInfo{ + ErrCode: CVL_SYNTAX_ERROR, + Msg: "Invalid table or key for |Rule1", + }) +} - cvl.ValidationSessClose(cvSess) - - WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) - - if err == cvl.CVL_SUCCESS { - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - } - -} - -func TestValidateEditConfig_Delete_InvalidKey_Negative(t *testing.T) { +func TestValidateEditConfig_Delete_InvalidKey_Negative(t *testing.T) { cfgData := []cvl.CVLEditConfigData{ cvl.CVLEditConfigData{ @@ -1501,29 +1377,17 @@ func TestValidateEditConfig_Delete_InvalidKey_Negative(t *testing.T) { cvl.OP_DELETE, "ACL_RULE|TestACL1:Rule1", map[string]string{ - "PACKET_ACTION": "FORWARD", - "IP_TYPE": "IPV4", - "SRC_IP": "10.1.1.1/32", - "L4_SRC_PORT": "1909", - "IP_PROTOCOL": "103", - "DST_IP": "20.2.2.2/32", - "L4_DST_PORT_RANGE": "9000-12000", + "PACKET_ACTION": "", }, }, } - cvSess, _ := cvl.ValidationSessOpen() - - cvlErrObj, err := cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrObj)) - - if err == cvl.CVL_SUCCESS { - t.Errorf("Config Validation failed -- error details %v", cvlErrObj) - } - + verifyValidateEditConfig(t, cfgData, CVLErrorInfo{ + ErrCode: cvl.CVL_SYNTAX_MISSING_FIELD, + TableName: "ACL_RULE", + Field: "aclname", + Msg: invalidValueErrMessage, + }) } func TestValidateEditConfig_Update_Semantic_Invalid_Key_Negative(t *testing.T) { @@ -1545,17 +1409,12 @@ func TestValidateEditConfig_Update_Semantic_Invalid_Key_Negative(t *testing.T) { }, } - cvSess, _ := cvl.ValidationSessOpen() - - cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) - - if err == cvl.CVL_SUCCESS { - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - } - + verifyValidateEditConfig(t, cfgData, CVLErrorInfo{ + ErrCode: cvl.CVL_SYNTAX_MISSING_FIELD, + TableName: "ACL_RULE", + Field: "aclname", + Msg: invalidValueErrMessage, + }) } func TestValidateEditConfig_Delete_Semantic_Positive(t *testing.T) { @@ -1569,6 +1428,7 @@ func TestValidateEditConfig_Delete_Semantic_Positive(t *testing.T) { } loadConfigDB(rclient, depDataMap) + defer unloadConfigDB(rclient, depDataMap) cfgData := []cvl.CVLEditConfigData{ cvl.CVLEditConfigData{ @@ -1579,18 +1439,7 @@ func TestValidateEditConfig_Delete_Semantic_Positive(t *testing.T) { }, } - cvSess, _ := cvl.ValidationSessOpen() - - cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - if err != cvl.CVL_SUCCESS { - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - } - - unloadConfigDB(rclient, depDataMap) - + verifyValidateEditConfig(t, cfgData, Success) } func TestValidateEditConfig_Delete_Semantic_KeyNotExisting_Negative(t *testing.T) { @@ -1604,18 +1453,11 @@ func TestValidateEditConfig_Delete_Semantic_KeyNotExisting_Negative(t *testing.T }, } - cvSess, _ := cvl.ValidationSessOpen() - - cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) - - if err == cvl.CVL_SUCCESS { - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - } - + verifyValidateEditConfig(t, cfgData, CVLErrorInfo{ + ErrCode: cvl.CVL_SEMANTIC_KEY_NOT_EXIST, + TableName: "MIRROR_SESSION", + Keys: []string{"everflow0"}, + }) } func TestValidateEditConfig_Update_Semantic_MissingKey_Negative(t *testing.T) { @@ -1631,18 +1473,11 @@ func TestValidateEditConfig_Update_Semantic_MissingKey_Negative(t *testing.T) { }, } - cvSess, _ := cvl.ValidationSessOpen() - - cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) - - if err == cvl.CVL_SUCCESS { - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - } - + verifyValidateEditConfig(t, cfgData, CVLErrorInfo{ + ErrCode: cvl.CVL_SEMANTIC_KEY_NOT_EXIST, + TableName: "ACL_RULE", + Keys: []string{"TestACL177", "Rule1"}, + }) } func TestValidateEditConfig_Create_Duplicate_Key_Negative(t *testing.T) { @@ -1657,6 +1492,7 @@ func TestValidateEditConfig_Create_Duplicate_Key_Negative(t *testing.T) { //Load same key in DB loadConfigDB(rclient, depDataMap) + defer unloadConfigDB(rclient, depDataMap) cfgData := []cvl.CVLEditConfigData{ cvl.CVLEditConfigData{ @@ -1670,18 +1506,11 @@ func TestValidateEditConfig_Create_Duplicate_Key_Negative(t *testing.T) { }, } - cvSess, _ := cvl.ValidationSessOpen() - - cvlErrInfo, retCode := cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) - - if retCode == cvl.CVL_SUCCESS { - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - } - - unloadConfigDB(rclient, depDataMap) + verifyValidateEditConfig(t, cfgData, CVLErrorInfo{ + ErrCode: cvl.CVL_SEMANTIC_KEY_ALREADY_EXIST, + TableName: "ACL_TABLE", + Keys: []string{"TestACL100"}, + }) } /* API to test edit config with valid syntax. */ @@ -1696,6 +1525,7 @@ func TestValidateEditConfig_Update_Semantic_Positive(t *testing.T) { mpi_acl_table_map := loadConfig("", aclTableMapByte) loadConfigDB(rclient, mpi_acl_table_map) + defer unloadConfigDB(rclient, mpi_acl_table_map) cfgData := []cvl.CVLEditConfigData{ cvl.CVLEditConfigData{ @@ -1709,18 +1539,7 @@ func TestValidateEditConfig_Update_Semantic_Positive(t *testing.T) { }, } - cvSess, _ := cvl.ValidationSessOpen() - - cvlErrInfo, retCode := cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - if retCode != cvl.CVL_SUCCESS { - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - } - - unloadConfigDB(rclient, mpi_acl_table_map) - + verifyValidateEditConfig(t, cfgData, Success) } /* API to test edit config with valid syntax. */ @@ -1760,6 +1579,7 @@ func TestValidateEditConfig_Update_Syntax_DependentData_Redis_Positive(t *testin mpi_acl_table_map := loadConfig("", aclTableMapByte) loadConfigDB(rclient, mpi_acl_table_map) + defer unloadConfigDB(rclient, mpi_acl_table_map) // Create ACL Rule. fileName = "testdata/acl_rule.json" @@ -1770,6 +1590,7 @@ func TestValidateEditConfig_Update_Syntax_DependentData_Redis_Positive(t *testin mpi_acl_table_rule := loadConfig("", aclTableMapRule) loadConfigDB(rclient, mpi_acl_table_rule) + defer unloadConfigDB(rclient, mpi_acl_table_rule) depDataMap := map[string]interface{}{ "MIRROR_SESSION": map[string]interface{}{ @@ -1781,6 +1602,7 @@ func TestValidateEditConfig_Update_Syntax_DependentData_Redis_Positive(t *testin } loadConfigDB(rclient, depDataMap) + defer unloadConfigDB(rclient, depDataMap) /* ACL and Rule name pre-created . */ cfgData := []cvl.CVLEditConfigData{ @@ -1794,20 +1616,7 @@ func TestValidateEditConfig_Update_Syntax_DependentData_Redis_Positive(t *testin }, } - cvSess, _ := cvl.ValidationSessOpen() - - cvlErrInfo, retCode := cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - if retCode != cvl.CVL_SUCCESS { - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - } - - unloadConfigDB(rclient, mpi_acl_table_map) - unloadConfigDB(rclient, mpi_acl_table_rule) - unloadConfigDB(rclient, depDataMap) - + verifyValidateEditConfig(t, cfgData, Success) } func TestValidateEditConfig_Update_Syntax_DependentData_Invalid_Op_Seq(t *testing.T) { @@ -1848,16 +1657,11 @@ func TestValidateEditConfig_Update_Syntax_DependentData_Invalid_Op_Seq(t *testin }, } - cvSess, _ := cvl.ValidationSessOpen() - - cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - if err == cvl.CVL_SUCCESS { //Validation should fail - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - } - + verifyValidateEditConfig(t, cfgData, CVLErrorInfo{ + ErrCode: cvl.CVL_SEMANTIC_KEY_NOT_EXIST, + TableName: "ACL_RULE", + Keys: []string{"TestACL1", "Rule1"}, + }) } func TestValidateEditConfig_Update_Syntax_DependentData_Redis_Negative(t *testing.T) { @@ -1874,18 +1678,11 @@ func TestValidateEditConfig_Update_Syntax_DependentData_Redis_Negative(t *testin }, } - cvSess, _ := cvl.ValidationSessOpen() - - cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) - - if err == cvl.CVL_SUCCESS { - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - } - + verifyValidateEditConfig(t, cfgData, CVLErrorInfo{ + ErrCode: cvl.CVL_SEMANTIC_KEY_NOT_EXIST, + TableName: "ACL_RULE", + Keys: []string{"TestACL1", "Rule1"}, + }) } /* Create with User provided dependent data. */ @@ -1904,13 +1701,10 @@ func TestValidateEditConfig_Create_Syntax_DependentData_Redis_Positive(t *testin }, } - cvSess, _ := cvl.ValidationSessOpen() - - cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) + cvSess := NewTestSession(t) - if err != cvl.CVL_SUCCESS { - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - } + cvlErrInfo, _ := cvSess.ValidateEditConfig(cfgData) + verifyErr(t, cvlErrInfo, Success) cfgData = []cvl.CVLEditConfigData{ cvl.CVLEditConfigData{ @@ -1929,13 +1723,8 @@ func TestValidateEditConfig_Create_Syntax_DependentData_Redis_Positive(t *testin }, } - cvlErrInfo, err = cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - if err != cvl.CVL_SUCCESS { - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - } + cvlErrInfo, _ = cvSess.ValidateEditConfig(cfgData) + verifyErr(t, cvlErrInfo, Success) } /* Delete Non-Existing Key.*/ @@ -1950,23 +1739,16 @@ func TestValidateEditConfig_Delete_Semantic_ACLTableReference_Negative(t *testin }, } - cvSess, _ := cvl.ValidationSessOpen() - - cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) - - if err == cvl.CVL_SUCCESS { - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - } - + verifyValidateEditConfig(t, cfgData, CVLErrorInfo{ + ErrCode: cvl.CVL_SEMANTIC_KEY_NOT_EXIST, + TableName: "ACL_RULE", + Keys: []string{"MyACLTest_ACL_IPV4", "Test_1"}, + }) } func TestValidateEditConfig_Create_Dependent_CacheData(t *testing.T) { - cvSess, _ := cvl.ValidationSessOpen() + cvSess := NewTestSession(t) //Create ACL rule cfgDataAcl := []cvl.CVLEditConfigData{ @@ -1981,7 +1763,8 @@ func TestValidateEditConfig_Create_Dependent_CacheData(t *testing.T) { }, } - cvlErrInfo, err1 := cvSess.ValidateEditConfig(cfgDataAcl) + cvlErrInfo, _ := cvSess.ValidateEditConfig(cfgDataAcl) + verifyErr(t, cvlErrInfo, Success) //Create ACL rule cfgDataRule := []cvl.CVLEditConfigData{ @@ -2001,12 +1784,8 @@ func TestValidateEditConfig_Create_Dependent_CacheData(t *testing.T) { }, } - cvlErrInfo, err2 := cvSess.ValidateEditConfig(cfgDataRule) - - if err1 != cvl.CVL_SUCCESS || err2 != cvl.CVL_SUCCESS { - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - } - cvl.ValidationSessClose(cvSess) + cvlErrInfo, _ = cvSess.ValidateEditConfig(cfgDataRule) + verifyErr(t, cvlErrInfo, Success) } func TestValidateEditConfig_Create_DepData_In_MultiSess(t *testing.T) { @@ -2071,9 +1850,8 @@ func TestValidateEditConfig_Create_DepData_From_Redis_Negative11(t *testing.T) { } loadConfigDB(rclient, depDataMap) + defer unloadConfigDB(rclient, depDataMap) - //Create ACL rule - Session 2 - cvSess, _ := cvl.ValidationSessOpen() cfgDataRule := []cvl.CVLEditConfigData{ cvl.CVLEditConfigData{ cvl.VALIDATE_ALL, @@ -2091,18 +1869,13 @@ func TestValidateEditConfig_Create_DepData_From_Redis_Negative11(t *testing.T) { }, } - cvlErrInfo, err := cvSess.ValidateEditConfig(cfgDataRule) - - WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) - - - cvl.ValidationSessClose(cvSess) - - if err == cvl.CVL_SUCCESS { - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - } - - unloadConfigDB(rclient, depDataMap) + verifyValidateEditConfig(t, cfgDataRule, CVLErrorInfo{ + ErrCode: cvl.CVL_SEMANTIC_DEPENDENT_DATA_MISSING, + TableName: "ACL_RULE", + Keys: []string{"TestACL188", "Rule1"}, + ConstraintErrMsg: "No instance found for 'TestACL188'", + ErrAppTag: "instance-required", + }) } @@ -2118,9 +1891,8 @@ func TestValidateEditConfig_Create_DepData_From_Redis(t *testing.T) { } loadConfigDB(rclient, depDataMap) + defer unloadConfigDB(rclient, depDataMap) - //Create ACL rule - Session 2 - cvSess, _ := cvl.ValidationSessOpen() cfgDataRule := []cvl.CVLEditConfigData{ cvl.CVLEditConfigData{ cvl.VALIDATE_ALL, @@ -2138,16 +1910,7 @@ func TestValidateEditConfig_Create_DepData_From_Redis(t *testing.T) { }, } - - cvlErrInfo, err := cvSess.ValidateEditConfig(cfgDataRule) - - cvl.ValidationSessClose(cvSess) - - if err != cvl.CVL_SUCCESS { - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - } - - unloadConfigDB(rclient, depDataMap) + verifyValidateEditConfig(t, cfgDataRule, Success) } func TestValidateEditConfig_Create_Syntax_ErrAppTag_In_Range_Negative(t *testing.T) { @@ -2163,22 +1926,19 @@ func TestValidateEditConfig_Create_Syntax_ErrAppTag_In_Range_Negative(t *testing }, } - cvSess, _ := cvl.ValidationSessOpen() - - cvlErrInfo, retCode := cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) - - /* Compare expected error details and error tag. */ - if compareErrorDetails(cvlErrInfo, cvl.CVL_SYNTAX_ERROR, "vlanid-invalid", "") != true { - t.Errorf("Config Validation failed -- error details %v %v", cvlErrInfo, retCode) - } - + verifyValidateEditConfig(t, cfgData, CVLErrorInfo{ + ErrCode: CVL_SYNTAX_ERROR, + TableName: "VLAN", + Keys: []string{"Vlan701"}, + Field: "vlanid", + Msg: invalidValueErrMessage, + ConstraintErrMsg: "Vlan ID out of range", + ErrAppTag: "vlanid-invalid", + }) } func TestValidateEditConfig_Create_Syntax_ErrAppTag_In_Length_Negative(t *testing.T) { + longText := "A12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" cfgData := []cvl.CVLEditConfigData{ cvl.CVLEditConfigData{ @@ -2188,24 +1948,20 @@ func TestValidateEditConfig_Create_Syntax_ErrAppTag_In_Length_Negative(t *testin map[string]string{ "stage": "INGRESS", "type": "MIRROR", - "policy_desc": "A12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890", + "policy_desc": longText, }, }, } - cvSess, _ := cvl.ValidationSessOpen() - - cvlErrInfo, retCode := cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) - - /* Compare expected error details and error tag. */ - if compareErrorDetails(cvlErrInfo, cvl.CVL_SYNTAX_ERROR, "policy-desc-invalid-length", "") != true { - t.Errorf("Config Validation failed -- error details %v %v", cvlErrInfo, retCode) - } - + verifyValidateEditConfig(t, cfgData, CVLErrorInfo{ + ErrCode: CVL_SYNTAX_ERROR, + TableName: "ACL_TABLE", + Keys: []string{"TestACL1"}, + Field: "policy_desc", + Value: longText, + Msg: invalidValueErrMessage, + ErrAppTag: "policy-desc-invalid-length", + }) } func TestValidateEditConfig_Create_Syntax_ErrAppTag_In_Pattern_Negative(t *testing.T) { @@ -2221,117 +1977,17 @@ func TestValidateEditConfig_Create_Syntax_ErrAppTag_In_Pattern_Negative(t *testi }, } - cvSess, _ := cvl.ValidationSessOpen() - - cvlErrInfo, retCode := cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) - - /* Compare expected error details and error tag. */ - if compareErrorDetails(cvlErrInfo, cvl.CVL_SYNTAX_ERROR, "vlan-name-invalid", "") != true { - t.Errorf("Config Validation failed -- error details %v %v", cvlErrInfo, retCode) - } - + verifyValidateEditConfig(t, cfgData, CVLErrorInfo{ + ErrCode: CVL_SYNTAX_ERROR, + TableName: "VLAN", + Keys: []string{"Vlan5001"}, + Field: "name", + Msg: invalidValueErrMessage, + ConstraintErrMsg: "Invalid Vlan name pattern", + ErrAppTag: "vlan-name-invalid", + }) } -/* API to test edit config with valid syntax. */ -func TestValidateEditConfig_Create_Syntax_InValid_FieldValue(t *testing.T) { - - cfgData := []cvl.CVLEditConfigData{ - cvl.CVLEditConfigData{ - cvl.VALIDATE_NONE, - cvl.OP_UPDATE, - "ACL_TABLE|TestACL1", - map[string]string{ - "stage": "INGRESS", - "type": "MIRROR", - }, - }, - cvl.CVLEditConfigData{ - cvl.VALIDATE_NONE, - cvl.OP_CREATE, - "ACL_RULE|TestACL1|Rule1", - map[string]string{ - "PACKET_ACTION": "FORWARD", - "IP_TYPE": "IPV4", - "SRC_IP": "10.1.1.1/32", - "L4_SRC_PORT": "1909", - "IP_PROTOCOL": "103", - "DST_IP": "20.2.2.2/32", - "L4_DST_PORT_RANGE": "9000-12000", - }, - }, - cvl.CVLEditConfigData{ - cvl.VALIDATE_ALL, - cvl.OP_DELETE, - "ACL_RULE|TestACL1", - map[string]string{}, - }, - } - - cvSess, _ := cvl.ValidationSessOpen() - - cvlErrInfo, retCode := cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) - - if retCode == cvl.CVL_SUCCESS { - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - } -} - -/* -//EditConfig(Create) with dependent data from redis -func TestValidateEditConfig_Create_DepData_From_Redis_Negative(t *testing.T) { - - depDataMap := map[string]interface{} { - "ACL_TABLE" : map[string]interface{} { - "TestACL1": map[string] interface{} { - "stage": "INGRESS", - "type": "MIRROR", - }, - }, - } - - loadConfigDB(rclient, depDataMap) - - cfgDataRule := []cvl.CVLEditConfigData { - cvl.CVLEditConfigData { - cvl.VALIDATE_ALL, - cvl.OP_CREATE, - "ACL_RULE|TestACL2|Rule1", - map[string]string { - "PACKET_ACTION": "FORWARD", - "IP_TYPE": "IPV4", - "SRC_IP": "10.1.1.1/32", - "L4_SRC_PORT": "1909", - "IP_PROTOCOL": "103", - "DST_IP": "20.2.2.2/32", - "L4_DST_PORT_RANGE": "9000-12000", - }, - }, - } - - cvSess, _ := cvl.ValidationSessOpen() - - cvlErrInfo, err := cvSess.ValidateEditConfig(cfgDataRule) - - cvl.ValidationSessClose(cvSess) - - WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) - - if err == cvl.CVL_SUCCESS { //should not succeed - t.Errorf("Config Validation should fail.") - } - - unloadConfigDB(rclient, depDataMap) -} -*/ - //EditConfig(Delete) deleting entry already used by other table as leafref func TestValidateEditConfig_Delete_Dep_Leafref_Negative(t *testing.T) { depDataMap := map[string]interface{} { @@ -2356,6 +2012,7 @@ func TestValidateEditConfig_Delete_Dep_Leafref_Negative(t *testing.T) { //Prepare data in Redis loadConfigDB(rclient, depDataMap) + defer unloadConfigDB(rclient, depDataMap) cfgDataVlan := []cvl.CVLEditConfigData { cvl.CVLEditConfigData { @@ -2367,46 +2024,78 @@ func TestValidateEditConfig_Delete_Dep_Leafref_Negative(t *testing.T) { }, } - cvSess, _ := cvl.ValidationSessOpen() - - cvlErrInfo, err := cvSess.ValidateEditConfig(cfgDataVlan) - - cvl.ValidationSessClose(cvSess) - - WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) - - if err == cvl.CVL_SUCCESS { //should be semantic failure - t.Errorf("Config Validation failed.") - } - - unloadConfigDB(rclient, depDataMap) -} - -func TestValidateEditConfig_Create_Syntax_InvalidVlanRange_Negative(t *testing.T) { - - cfgData := []cvl.CVLEditConfigData{ - cvl.CVLEditConfigData{ - cvl.VALIDATE_ALL, - cvl.OP_CREATE, - "VLAN|Vlan5002", - map[string]string{ - "vlanid": "6002", - }, - }, - } - - cvSess, _ := cvl.ValidationSessOpen() - - cvlErrInfo, retCode := cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) - - if retCode == cvl.CVL_SUCCESS { //should not succeed - t.Errorf("Config Validation failed with details %v.", cvlErrInfo) - } - + verifyValidateEditConfig(t, cfgDataVlan, CVLErrorInfo{ + ErrCode: CVL_SEMANTIC_ERROR, + TableName: "ACL_TABLE", + Keys: []string{"TestACL1"}, + Msg: instanceInUseErrMessage, + ErrAppTag: "instance-in-use", + }) +} + +func TestValidateEditConfig_Create_Syntax_RangeValidation(t *testing.T) { + t.Run("success", func(tt *testing.T) { + data := []CVLEditConfigData{{ + VType: VALIDATE_ALL, + VOp: OP_CREATE, + Key: "PORTCHANNEL|PortChannel100", + Data: map[string]string{"mtu": "5555", "admin_status": "up"}, + }} + verifyValidateEditConfig(tt, data, Success) + }) + + t.Run("failure_with_errmsg", func(tt *testing.T) { + data := []CVLEditConfigData{{ + VType: VALIDATE_ALL, + VOp: OP_CREATE, + Key: "PORTCHANNEL|PortChannel100", + Data: map[string]string{"mtu": "1", "admin_status": "up"}, + }} + verifyValidateEditConfig(tt, data, CVLErrorInfo{ + ErrCode: CVL_SYNTAX_ERROR, + TableName: "PORTCHANNEL", + Keys: []string{"PortChannel100"}, + Field: "mtu", + Msg: invalidValueErrMessage, + ConstraintErrMsg: "Invalid MTU value", + ErrAppTag: "mtu-invalid", + }) + }) + + t.Run("failure_no_errmsg", func(tt *testing.T) { + data := []CVLEditConfigData{{ + VType: VALIDATE_ALL, + VOp: OP_CREATE, + Key: "ACL_RULE|ONE|rule100", + Data: map[string]string{"PRIORITY": "65535", "IP_PROTOCOL": "4444"}, + }} + verifyValidateEditConfig(tt, data, CVLErrorInfo{ + ErrCode: CVL_SYNTAX_ERROR, + TableName: "ACL_RULE", + Keys: []string{"ONE", "rule100"}, + Field: "IP_PROTOCOL", + Value: "4444", + Msg: invalidValueErrMessage, + }) + }) + + t.Run("failure_datatype_err", func(tt *testing.T) { + data := []CVLEditConfigData{{ + VType: VALIDATE_ALL, + VOp: OP_CREATE, + Key: "PORTCHANNEL|PortChannel100", + Data: map[string]string{"mtu": "xyz"}, // mtu is not a number + }} + verifyValidateEditConfig(tt, data, CVLErrorInfo{ + // Range will not be evaluated if the value is not a number.. hence generic error + ErrCode: CVL_SYNTAX_ERROR, + TableName: "PORTCHANNEL", + Keys: []string{"PortChannel100"}, + Field: "mtu", + Value: "xyz", + Msg: invalidValueErrMessage, + }) + }) } //Test Initialize() API @@ -2451,6 +2140,7 @@ func TestValidateEditConfig_DepData_Through_Cache(t *testing.T) { //Prepare data in Redis loadConfigDB(rclient, depDataMap) + defer unloadConfigDB(rclient, depDataMap) //Modify entry modDepDataMap := map[string]interface{} { @@ -2462,6 +2152,7 @@ func TestValidateEditConfig_DepData_Through_Cache(t *testing.T) { } loadConfigDB(rclient, modDepDataMap) + defer unloadConfigDB(rclient, modDepDataMap) cfgDataAclRule := []cvl.CVLEditConfigData { cvl.CVLEditConfigData { @@ -2476,18 +2167,7 @@ func TestValidateEditConfig_DepData_Through_Cache(t *testing.T) { }, } - cvSess, _ := cvl.ValidationSessOpen() - - _, err := cvSess.ValidateEditConfig(cfgDataAclRule) - - cvl.ValidationSessClose(cvSess) - - if err != cvl.CVL_SUCCESS { //should succeed - t.Errorf("Config Validation failed.") - } - - unloadConfigDB(rclient, depDataMap) - unloadConfigDB(rclient, modDepDataMap) + verifyValidateEditConfig(t, cfgDataAclRule, Success) } /* Delete field for an existing key.*/ @@ -2505,29 +2185,20 @@ func TestValidateEditConfig_Delete_Single_Field_Positive(t *testing.T) { //Prepare data in Redis loadConfigDB(rclient, depDataMap) + defer unloadConfigDB(rclient, depDataMap) cfgData := []cvl.CVLEditConfigData{ - cvl.CVLEditConfigData{ - cvl.VALIDATE_ALL, - cvl.OP_DELETE, - "ACL_TABLE|TestACL1", - map[string]string{ - "policy_desc":"Test ACL desc", - }, - }, - } - - cvSess, _ := cvl.ValidationSessOpen() - - cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - if err != cvl.CVL_SUCCESS { - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_DELETE, + "ACL_TABLE|TestACL1", + map[string]string{ + "policy_desc":"Test ACL desc", + }, + }, } - unloadConfigDB(rclient, depDataMap) + verifyValidateEditConfig(t, cfgData, Success) } func TestValidateEditConfig_Create_Dscp_To_Tc_Map(t *testing.T) { @@ -2544,12 +2215,7 @@ func TestValidateEditConfig_Create_Dscp_To_Tc_Map(t *testing.T) { }, } - cvSess, _ := cvl.ValidationSessOpen() - cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) - cvl.ValidationSessClose(cvSess) - if err != cvl.CVL_SUCCESS { - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - } + verifyValidateEditConfig(t, cfgData, Success) } func TestValidateConfig_Repeated_Keys_Positive(t *testing.T) { @@ -2622,8 +2288,9 @@ func TestValidateEditConfig_Delete_Entry_Then_Dep_Leafref_Positive(t *testing.T) //Prepare data in Redis loadConfigDB(rclient, depDataMap) + defer unloadConfigDB(rclient, depDataMap) - cvSess, _ := cvl.ValidationSessOpen() + cvSess := NewTestSession(t) cfgDataAcl := []cvl.CVLEditConfigData { cvl.CVLEditConfigData { @@ -2635,7 +2302,8 @@ func TestValidateEditConfig_Delete_Entry_Then_Dep_Leafref_Positive(t *testing.T) }, } - cvlErrInfo, err := cvSess.ValidateEditConfig(cfgDataAcl) + cvlErrInfo, _ := cvSess.ValidateEditConfig(cfgDataAcl) + verifyErr(t, cvlErrInfo, Success) cfgDataAcl = []cvl.CVLEditConfigData { cvl.CVLEditConfigData { @@ -2654,17 +2322,8 @@ func TestValidateEditConfig_Delete_Entry_Then_Dep_Leafref_Positive(t *testing.T) }, } - cvlErrInfo, err = cvSess.ValidateEditConfig(cfgDataAcl) - - cvl.ValidationSessClose(cvSess) - - WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) - - if err != cvl.CVL_SUCCESS { //should be success - t.Errorf("Config Validation failed.") - } - - unloadConfigDB(rclient, depDataMap) + cvlErrInfo, _ = cvSess.ValidateEditConfig(cfgDataAcl) + verifyErr(t, cvlErrInfo, Success) } /* @@ -2785,8 +2444,9 @@ func TestValidateEditConfig_Delete_Create_Same_Entry_Positive(t *testing.T) { //Prepare data in Redis loadConfigDB(rclient, depDataMap) + defer unloadConfigDB(rclient, depDataMap) - cvSess, _ := cvl.ValidationSessOpen() + cvSess := NewTestSession(t) cfgDataVlan := []cvl.CVLEditConfigData { cvl.CVLEditConfigData { @@ -2798,7 +2458,8 @@ func TestValidateEditConfig_Delete_Create_Same_Entry_Positive(t *testing.T) { }, } - _, err1 := cvSess.ValidateEditConfig(cfgDataVlan) + res, _ := cvSess.ValidateEditConfig(cfgDataVlan) + verifyErr(t, res, Success) //Same entry getting created again cfgDataVlan = []cvl.CVLEditConfigData { @@ -2812,17 +2473,8 @@ func TestValidateEditConfig_Delete_Create_Same_Entry_Positive(t *testing.T) { }, } - _, err2 := cvSess.ValidateEditConfig(cfgDataVlan) - - if err1 != cvl.CVL_SUCCESS || err2 != cvl.CVL_SUCCESS { //should succeed - t.Errorf("Config Validation failed.") - return - } - - - cvl.ValidationSessClose(cvSess) - - unloadConfigDB(rclient, depDataMap) + res, _ = cvSess.ValidateEditConfig(cfgDataVlan) + verifyErr(t, res, Success) } func TestValidateStartupConfig_Positive(t *testing.T) { @@ -2940,8 +2592,7 @@ func TestValidateEditConfig_Two_Updates_Positive(t *testing.T) { //Prepare data in Redis loadConfigDB(rclient, depDataMap) - - cvSess, _ := cvl.ValidationSessOpen() + defer unloadConfigDB(rclient, depDataMap) cfgDataAcl := []cvl.CVLEditConfigData { cvl.CVLEditConfigData { @@ -2962,19 +2613,9 @@ func TestValidateEditConfig_Two_Updates_Positive(t *testing.T) { }, } - cvlErrInfo, err := cvSess.ValidateEditConfig(cfgDataAcl) - - cvl.ValidationSessClose(cvSess) - - WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) - - if err != cvl.CVL_SUCCESS { //should be success - t.Errorf("Config Validation failed.") - } - - unloadConfigDB(rclient, depDataMap) - + verifyValidateEditConfig(t, cfgDataAcl, Success) } + func TestValidateEditConfig_Create_Syntax_DependentData_PositivePortChannel(t *testing.T) { cfgData := []cvl.CVLEditConfigData{ @@ -2989,18 +2630,7 @@ func TestValidateEditConfig_Create_Syntax_DependentData_PositivePortChannel(t *t }, } - cvSess, _ := cvl.ValidationSessOpen() - - cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) - - if err != cvl.CVL_SUCCESS { - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - } - + verifyValidateEditConfig(t, cfgData, Success) } @@ -3018,18 +2648,7 @@ func TestValidateEditConfig_Create_Syntax_DependentData_PositivePortChannelIfNam }, } - cvSess, _ := cvl.ValidationSessOpen() - - cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) - - if err != cvl.CVL_SUCCESS { - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - } - + verifyValidateEditConfig(t, cfgData, Success) } func TestValidateEditConfig_Create_Syntax_DependentData_NegativePortChannelEthernet(t *testing.T) { @@ -3045,17 +2664,15 @@ func TestValidateEditConfig_Create_Syntax_DependentData_NegativePortChannelEther }, } - cvSess, _ := cvl.ValidationSessOpen() - - cvlErrInfo, _ := cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) - - if cvlErrInfo.ErrCode == cvl.CVL_SUCCESS { - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - } + verifyValidateEditConfig(t, cfgData, CVLErrorInfo{ + ErrCode: CVL_SEMANTIC_ERROR, + TableName: "VLAN", + Keys: []string{"Vlan1001"}, + Field: "members", + Value: "PortChannel001", + Msg: mustExpressionErrMessage, + ConstraintErrMsg: "A vlan interface member cannot be part of portchannel which is already a vlan member", + }) } func TestValidateEditConfig_Create_Syntax_DependentData_NegativePortChannelNew(t *testing.T) { @@ -3067,22 +2684,20 @@ func TestValidateEditConfig_Create_Syntax_DependentData_NegativePortChannelNew(t "VLAN|Vlan1001", map[string]string{ "vlanid": "1001", - "members@": "Ethernet12,PortChannel001", + "members@": "PortChannel003,Ethernet12,PortChannel001", }, }, } - cvSess, _ := cvl.ValidationSessOpen() - - cvlErrInfo, _ := cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) - - if cvlErrInfo.ErrCode == cvl.CVL_SUCCESS { - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - } + verifyValidateEditConfig(t, cfgData, CVLErrorInfo{ + ErrCode: CVL_SEMANTIC_ERROR, + TableName: "VLAN", + Keys: []string{"Vlan1001"}, + Field: "members", + //Value: "Ethernet12", <<< BUG: cvl always fills 1st instance, even thought it was ok + Msg: mustExpressionErrMessage, + ConstraintErrMsg: "A vlan interface member cannot be part of portchannel which is already a vlan member", + }) } func TestValidateEditConfig_Use_Updated_Data_As_Create_DependentData_Positive(t *testing.T) { @@ -3098,9 +2713,9 @@ func TestValidateEditConfig_Use_Updated_Data_As_Create_DependentData_Positive(t //Prepare data in Redis loadConfigDB(rclient, depDataMap) + defer unloadConfigDB(rclient, depDataMap) - cvSess, _ := cvl.ValidationSessOpen() - + cvSess := NewTestSession(t) cfgData := []cvl.CVLEditConfigData{ cvl.CVLEditConfigData{ @@ -3115,11 +2730,7 @@ func TestValidateEditConfig_Use_Updated_Data_As_Create_DependentData_Positive(t } cvlErrInfo, _ := cvSess.ValidateEditConfig(cfgData) - if cvlErrInfo.ErrCode != cvl.CVL_SUCCESS { - unloadConfigDB(rclient, depDataMap) - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - return - } + verifyErr(t, cvlErrInfo, Success) cfgData = []cvl.CVLEditConfigData{ cvl.CVLEditConfigData{ @@ -3133,16 +2744,7 @@ func TestValidateEditConfig_Use_Updated_Data_As_Create_DependentData_Positive(t } cvlErrInfo, _ = cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - unloadConfigDB(rclient, depDataMap) - - WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) - - if cvlErrInfo.ErrCode != cvl.CVL_SUCCESS { - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - } + verifyErr(t, cvlErrInfo, Success) } func TestValidateEditConfig_Use_Updated_Data_As_Create_DependentData_Single_Call_Positive(t *testing.T) { @@ -3158,9 +2760,7 @@ func TestValidateEditConfig_Use_Updated_Data_As_Create_DependentData_Single_Call //Prepare data in Redis loadConfigDB(rclient, depDataMap) - - cvSess, _ := cvl.ValidationSessOpen() - + defer unloadConfigDB(rclient, depDataMap) cfgData := []cvl.CVLEditConfigData{ cvl.CVLEditConfigData{ @@ -3182,17 +2782,7 @@ func TestValidateEditConfig_Use_Updated_Data_As_Create_DependentData_Single_Call }, } - cvlErrInfo, _ := cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - unloadConfigDB(rclient, depDataMap) - - WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) - - if cvlErrInfo.ErrCode != cvl.CVL_SUCCESS { - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - } + verifyValidateEditConfig(t, cfgData, Success) } func TestValidateEditConfig_Create_Syntax_Interface_AllKeys_Positive(t *testing.T) { @@ -3207,17 +2797,7 @@ func TestValidateEditConfig_Create_Syntax_Interface_AllKeys_Positive(t *testing. }, } - cvSess, _ := cvl.ValidationSessOpen() - - cvlErrInfo, _ := cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) - - if cvlErrInfo.ErrCode != cvl.CVL_SUCCESS { - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - } + verifyValidateEditConfig(t, cfgData, Success) } func TestValidateEditConfig_Create_Syntax_Interface_OptionalKey_Positive(t *testing.T) { @@ -3232,17 +2812,7 @@ func TestValidateEditConfig_Create_Syntax_Interface_OptionalKey_Positive(t *test }, } - cvSess, _ := cvl.ValidationSessOpen() - - cvlErrInfo, _ := cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) - - if cvlErrInfo.ErrCode != cvl.CVL_SUCCESS { - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - } + verifyValidateEditConfig(t, cfgData, Success) } func TestValidateEditConfig_Create_Syntax_Interface_IncorrectKey_Negative(t *testing.T) { @@ -3257,23 +2827,18 @@ func TestValidateEditConfig_Create_Syntax_Interface_IncorrectKey_Negative(t *tes }, } - cvSess, _ := cvl.ValidationSessOpen() - - cvlErrInfo, _ := cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) - - if cvlErrInfo.ErrCode == cvl.CVL_SUCCESS { - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - } + verifyValidateEditConfig(t, cfgData, CVLErrorInfo{ + ErrCode: CVL_SYNTAX_ERROR, + TableName: "INTERFACE", + Keys: []string{"10.0.0.0/31"}, + Field: "portname", + Msg: invalidValueErrMessage, + ConstraintErrMsg: "Invalid interface name", + ErrAppTag: "interface-name-invalid", + }) } func TestValidateEditConfig_EmptyNode_Positive(t *testing.T) { - cvSess, _ := cvl.ValidationSessOpen() - - cfgData := []cvl.CVLEditConfigData{ cvl.CVLEditConfigData{ cvl.VALIDATE_ALL, @@ -3286,16 +2851,7 @@ func TestValidateEditConfig_EmptyNode_Positive(t *testing.T) { }, } - cvlErrInfo, _ := cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) - - if cvlErrInfo.ErrCode != cvl.CVL_SUCCESS { - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - } - + verifyValidateEditConfig(t, cfgData, Success) } func TestSortDepTables(t *testing.T) { @@ -3468,7 +3024,7 @@ func TestGetDepDataForDelete(t *testing.T) { } func TestMaxElements_All_Entries_In_Request(t *testing.T) { - cvSess, _ := cvl.ValidationSessOpen() + cvSess := NewTestSession(t) cfgData := []cvl.CVLEditConfigData{ cvl.CVLEditConfigData{ @@ -3482,7 +3038,8 @@ func TestMaxElements_All_Entries_In_Request(t *testing.T) { } //Check addition of first element - cvlErrInfo, _ := cvSess.ValidateEditConfig(cfgData) + cvlErrInfo, _ := cvSess.ValidateEditConfig(cfgData) + verifyErr(t, cvlErrInfo, Success) cfgData1 := []cvl.CVLEditConfigData{ cvl.CVLEditConfigData{ @@ -3497,14 +3054,14 @@ func TestMaxElements_All_Entries_In_Request(t *testing.T) { //Try to validate addition of second element cvlErrInfo, _ = cvSess.ValidateEditConfig(cfgData1) - - cvl.ValidationSessClose(cvSess) - - //Should fail as "VXLAN_TUNNEL" has max-elements as '1' - if cvlErrInfo.ErrCode == cvl.CVL_SUCCESS { - t.Errorf("VXLAN_TUNNEL Config Validation failed -- error details %v", cvlErrInfo) - } - + verifyErr(t, cvlErrInfo, CVLErrorInfo{ + ErrCode: CVL_SYNTAX_ERROR, + TableName: "VXLAN_TUNNEL", + Keys: []string{"tun2"}, + Msg: "Max elements limit reached", + ConstraintErrMsg: "Max elements limit 1 reached", + ErrAppTag: "too-many-elements", + }) } func TestMaxElements_Entries_In_Redis(t *testing.T) { @@ -3517,81 +3074,64 @@ func TestMaxElements_Entries_In_Redis(t *testing.T) { } loadConfigDB(rclient, depDataMap) - - cvSess, _ := cvl.ValidationSessOpen() - - cfgData := []cvl.CVLEditConfigData{ - cvl.CVLEditConfigData{ - cvl.VALIDATE_ALL, - cvl.OP_CREATE, - "VXLAN_TUNNEL|tun2", - map[string]string{ - "src_ip": "30.1.1.1", - }, - }, - } - - //Check addition of second element - cvlErrInfo, _ := cvSess.ValidateEditConfig(cfgData) - - - cvl.ValidationSessClose(cvSess) - - //Should fail as "VXLAN_TUNNEL" has max-elements as '1' - if cvlErrInfo.ErrCode == cvl.CVL_SUCCESS { - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - unloadConfigDB(rclient, depDataMap) - return - } - - cfgData1 := []cvl.CVLEditConfigData{ - cvl.CVLEditConfigData{ - cvl.VALIDATE_ALL, - cvl.OP_DELETE, - "VXLAN_TUNNEL|tun1", - map[string]string{ - }, - }, - } - - //Delete the existing entry, should succeed - cvlErrInfo, _ = cvSess.ValidateEditConfig(cfgData1) - if cvlErrInfo.ErrCode != cvl.CVL_SUCCESS { - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - unloadConfigDB(rclient, depDataMap) - return - } - - cfgData1 = []cvl.CVLEditConfigData{ - cvl.CVLEditConfigData{ - cvl.VALIDATE_NONE, - cvl.OP_DELETE, - "VXLAN_TUNNEL|tun1", - map[string]string{ - "src_ip": "20.1.1.1", - }, - }, - cvl.CVLEditConfigData{ - cvl.VALIDATE_ALL, - cvl.OP_CREATE, - "VXLAN_TUNNEL|tun2", - map[string]string{ - "src_ip": "30.1.1.1", - }, - }, - } - - //Check validation of new entry, should succeed now - cvlErrInfo, _ = cvSess.ValidateEditConfig(cfgData1) - if cvlErrInfo.ErrCode != cvl.CVL_SUCCESS { - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - } - - unloadConfigDB(rclient, depDataMap) + defer unloadConfigDB(rclient, depDataMap) + + t.Run("create_new", func(t *testing.T) { + cfgData := []CVLEditConfigData{{ + VType: VALIDATE_ALL, + VOp: OP_CREATE, + Key: "VXLAN_TUNNEL|tun2", + Data: map[string]string{ + "src_ip": "30.1.1.1", + }, + }} + + verifyValidateEditConfig(t, cfgData, CVLErrorInfo{ + ErrCode: CVL_SYNTAX_ERROR, + TableName: "VXLAN_TUNNEL", + Keys: []string{"tun2"}, + Msg: "Max elements limit reached", + ConstraintErrMsg: "Max elements limit 1 reached", + ErrAppTag: "too-many-elements", + }) + }) + + t.Run("delete_and_create", func(t *testing.T) { + cvSess := NewTestSession(t) + + cfgData1 := []CVLEditConfigData{{ + VType: VALIDATE_ALL, + VOp: OP_DELETE, + Key: "VXLAN_TUNNEL|tun1", + Data: map[string]string{}, + }} + + //Delete the existing entry, should succeed + cvlErrInfo, _ := cvSess.ValidateEditConfig(cfgData1) + verifyErr(t, cvlErrInfo, Success) + + cfgData1 = []CVLEditConfigData{{ + VType: VALIDATE_NONE, + VOp: OP_DELETE, + Key: "VXLAN_TUNNEL|tun1", + Data: map[string]string{}, + }, { + VType: VALIDATE_ALL, + VOp: OP_CREATE, + Key: "VXLAN_TUNNEL|tun2", + Data: map[string]string{ + "src_ip": "30.1.1.1", + }, + }} + + //Check validation of new entry, should succeed now + cvlErrInfo, _ = cvSess.ValidateEditConfig(cfgData1) + verifyErr(t, cvlErrInfo, Success) + }) } func TestValidateEditConfig_Two_Create_Requests_Positive(t *testing.T) { - cvSess, _ := cvl.ValidationSessOpen() + cvSess := NewTestSession(t) cfgDataVlan := []cvl.CVLEditConfigData { cvl.CVLEditConfigData { @@ -3605,12 +3145,7 @@ func TestValidateEditConfig_Two_Create_Requests_Positive(t *testing.T) { } cvlErrInfo, _ := cvSess.ValidateEditConfig(cfgDataVlan) - - if cvlErrInfo.ErrCode != cvl.CVL_SUCCESS { - cvl.ValidationSessClose(cvSess) - t.Errorf("VLAN Create : Config Validation failed") - return - } + verifyErr(t, cvlErrInfo, Success) cfgDataVlan = []cvl.CVLEditConfigData { cvl.CVLEditConfigData { @@ -3637,13 +3172,7 @@ func TestValidateEditConfig_Two_Create_Requests_Positive(t *testing.T) { } cvlErrInfo, _ = cvSess.ValidateEditConfig(cfgDataVlan) - - cvl.ValidationSessClose(cvSess) - - if cvlErrInfo.ErrCode != cvl.CVL_SUCCESS { - t.Errorf("STP VLAN Create : Config Validation failed") - return - } + verifyErr(t, cvlErrInfo, Success) } func TestValidateEditConfig_Two_Delete_Requests_Positive(t *testing.T) { @@ -3652,7 +3181,7 @@ func TestValidateEditConfig_Two_Delete_Requests_Positive(t *testing.T) { "Vlan51": map[string]interface{}{ "vlanid": "51", }, - }, + }, "STP_VLAN": map[string]interface{}{ "Vlan51": map[string]interface{}{ "enabled": "true", @@ -3666,8 +3195,9 @@ func TestValidateEditConfig_Two_Delete_Requests_Positive(t *testing.T) { } loadConfigDB(rclient, depDataMap) + defer unloadConfigDB(rclient, depDataMap) - cvSess, _ := cvl.ValidationSessOpen() + cvSess := NewTestSession(t) cfgDataVlan := []cvl.CVLEditConfigData { cvl.CVLEditConfigData { @@ -3680,12 +3210,7 @@ func TestValidateEditConfig_Two_Delete_Requests_Positive(t *testing.T) { } cvlErrInfo, _ := cvSess.ValidateEditConfig(cfgDataVlan) - if cvlErrInfo.ErrCode != cvl.CVL_SUCCESS { - cvl.ValidationSessClose(cvSess) - unloadConfigDB(rclient, depDataMap) - t.Errorf("STP VLAN delete : Config Validation failed") - return - } + verifyErr(t, cvlErrInfo, Success) cfgDataVlan = []cvl.CVLEditConfigData { cvl.CVLEditConfigData { @@ -3705,13 +3230,7 @@ func TestValidateEditConfig_Two_Delete_Requests_Positive(t *testing.T) { } cvlErrInfo, _ = cvSess.ValidateEditConfig(cfgDataVlan) - if cvlErrInfo.ErrCode != cvl.CVL_SUCCESS { - t.Errorf("VLAN delete : Config Validation failed") - } - - cvl.ValidationSessClose(cvSess) - - unloadConfigDB(rclient, depDataMap) + verifyErr(t, cvlErrInfo, Success) } //Check delete constraing with table having multiple keys @@ -3743,7 +3262,8 @@ func TestValidateEditConfig_Multi_Delete_MultiKey_Same_Session_Positive(t *testi } loadConfigDB(rclient, depDataMap) - cvSess, _ := cvl.ValidationSessOpen() + defer unloadConfigDB(rclient, depDataMap) + cvSess := NewTestSession(t) cfgData := []cvl.CVLEditConfigData { cvl.CVLEditConfigData { @@ -3756,11 +3276,7 @@ func TestValidateEditConfig_Multi_Delete_MultiKey_Same_Session_Positive(t *testi } cvlErrInfo, _ := cvSess.ValidateEditConfig(cfgData) - if cvlErrInfo.ErrCode != cvl.CVL_SUCCESS { - t.Errorf("STP_VLAN_PORT Delete: Config Validation failed") - unloadConfigDB(rclient, depDataMap) - return - } + verifyErr(t, cvlErrInfo, Success) cfgData = []cvl.CVLEditConfigData { cvl.CVLEditConfigData { @@ -3774,11 +3290,7 @@ func TestValidateEditConfig_Multi_Delete_MultiKey_Same_Session_Positive(t *testi } cvlErrInfo, _ = cvSess.ValidateEditConfig(cfgData) - if cvlErrInfo.ErrCode != cvl.CVL_SUCCESS { - t.Errorf("VLAN_MEMBER Delete: Config Validation failed") - unloadConfigDB(rclient, depDataMap) - return - } + verifyErr(t, cvlErrInfo, Success) cfgData = []cvl.CVLEditConfigData { cvl.CVLEditConfigData { @@ -3806,15 +3318,7 @@ func TestValidateEditConfig_Multi_Delete_MultiKey_Same_Session_Positive(t *testi } cvlErrInfo, _ = cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - if cvlErrInfo.ErrCode != cvl.CVL_SUCCESS { - t.Errorf("STP_PORT Delete: Config Validation failed") - return - } - - unloadConfigDB(rclient, depDataMap) + verifyErr(t, cvlErrInfo, Success) } func TestValidateEditConfig_Update_Leaf_List_Max_Elements_Negative(t *testing.T) { @@ -3832,8 +3336,7 @@ func TestValidateEditConfig_Update_Leaf_List_Max_Elements_Negative(t *testing.T) } loadConfigDB(rclient, depDataMap) - - cvSess, _ := cvl.ValidationSessOpen() + defer unloadConfigDB(rclient, depDataMap) cfgData := []cvl.CVLEditConfigData { cvl.CVLEditConfigData { @@ -3846,16 +3349,13 @@ func TestValidateEditConfig_Update_Leaf_List_Max_Elements_Negative(t *testing.T) }, } - cvlErrInfo, _ := cvSess.ValidateEditConfig(cfgData) - - if cvlErrInfo.ErrCode == cvl.CVL_SUCCESS { - cvl.ValidationSessClose(cvSess) - t.Errorf("CFG_L2MC_STATIC_GROUP_TABLE Update : Config Validation failed") - return - } - - cvl.ValidationSessClose(cvSess) - unloadConfigDB(rclient, depDataMap) + verifyValidateEditConfig(t, cfgData, CVLErrorInfo{ + ErrCode: CVL_SYNTAX_MAXIMUM_INVALID, + TableName: "CFG_L2MC_STATIC_GROUP_TABLE", + Keys: []string{"Vlan801", "16.2.2.1"}, + Field: "out-intf", + CVLErrDetails: "max-elements constraint not honored", + }) } func TestValidationTimeStats(t *testing.T) { diff --git a/cvl/cvl_when_test.go b/cvl/cvl_when_test.go index 89cf93700ac7..9c4b8a9fb46a 100644 --- a/cvl/cvl_when_test.go +++ b/cvl/cvl_when_test.go @@ -36,8 +36,8 @@ func TestValidateEditConfig_When_Exp_In_Choice_Negative(t *testing.T) { } loadConfigDB(rclient, depDataMap) + defer unloadConfigDB(rclient, depDataMap) - cvSess, _ := cvl.ValidationSessOpen() cfgDataRule := []cvl.CVLEditConfigData{ cvl.CVLEditConfigData{ cvl.VALIDATE_ALL, @@ -49,23 +49,19 @@ func TestValidateEditConfig_When_Exp_In_Choice_Negative(t *testing.T) { "SRC_IP": "10.1.1.1/32", //Invalid field "L4_SRC_PORT": "1909", "IP_PROTOCOL": "103", - "DST_IP": "20.2.2.2/32", //Invalid field "L4_DST_PORT_RANGE": "9000-12000", }, }, } - - cvlErrInfo, err := cvSess.ValidateEditConfig(cfgDataRule) - - cvl.ValidationSessClose(cvSess) - - if err == cvl.CVL_SUCCESS { - //Should fail - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - } - - unloadConfigDB(rclient, depDataMap) + verifyValidateEditConfig(t, cfgDataRule, CVLErrorInfo{ + ErrCode: CVL_SEMANTIC_ERROR, + TableName: "ACL_RULE", + //Keys: []string{"TestACL1", "Rule1"}, <<< BUG: cvl is not populating the key + Field: "SRC_IP", + Value: "10.1.1.1/32", + Msg: whenExpressionErrMessage, + }) } func TestValidateEditConfig_When_Exp_In_Leaf_Positive(t *testing.T) { @@ -79,7 +75,7 @@ func TestValidateEditConfig_When_Exp_In_Leaf_Positive(t *testing.T) { } loadConfigDB(rclient, depDataMap) - cvSess, _ := cvl.ValidationSessOpen() + defer unloadConfigDB(rclient, depDataMap) cfgData := []cvl.CVLEditConfigData{ cvl.CVLEditConfigData{ @@ -94,17 +90,7 @@ func TestValidateEditConfig_When_Exp_In_Leaf_Positive(t *testing.T) { }, } - - cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - if err != cvl.CVL_SUCCESS { - //Should succeed - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - } - - unloadConfigDB(rclient, depDataMap) + verifyValidateEditConfig(t, cfgData, Success) } func TestValidateEditConfig_When_Exp_In_Leaf_Negative(t *testing.T) { @@ -118,7 +104,7 @@ func TestValidateEditConfig_When_Exp_In_Leaf_Negative(t *testing.T) { } loadConfigDB(rclient, depDataMap) - cvSess, _ := cvl.ValidationSessOpen() + defer unloadConfigDB(rclient, depDataMap) cfgData := []cvl.CVLEditConfigData{ cvl.CVLEditConfigData{ @@ -127,21 +113,17 @@ func TestValidateEditConfig_When_Exp_In_Leaf_Negative(t *testing.T) { "STP_PORT|Ethernet4", map[string]string{ "enabled": "true", - "edge_port": "true", "link_type": "shared", }, }, } - - cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) - - cvl.ValidationSessClose(cvSess) - - if err == cvl.CVL_SUCCESS { - //Should succeed - t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) - } - - unloadConfigDB(rclient, depDataMap) + verifyValidateEditConfig(t, cfgData, CVLErrorInfo{ + ErrCode: CVL_SEMANTIC_ERROR, + TableName: "STP_PORT", + Keys: []string{"Ethernet4"}, + Field: "link_type", + Value: "shared", + Msg: whenExpressionErrMessage, + }) } diff --git a/cvl/internal/util/util.go b/cvl/internal/util/util.go index 209ca2127522..e8beceb3270e 100644 --- a/cvl/internal/util/util.go +++ b/cvl/internal/util/util.go @@ -30,7 +30,7 @@ static void customLogCb(LY_LOG_LEVEL level, const char* msg, const char* path) { } static void ly_set_log_callback(int enable) { - ly_set_log_clb(customLogCb, 0); + ly_set_log_clb(customLogCb, 1); if (enable == 1) { ly_verb(LY_LLDBG); } else { diff --git a/cvl/internal/yparser/ly_path.go b/cvl/internal/yparser/ly_path.go new file mode 100644 index 000000000000..a282e127e503 --- /dev/null +++ b/cvl/internal/yparser/ly_path.go @@ -0,0 +1,132 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2023 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 yparser + +import ( + "regexp" + "strings" +) + +// parseLyPath parses a libyang formatted path; extracts table name, key components +// and field name from it. Path should represent a sonic yang node. Path elements +// are interpreted as follows: +// +// /sonic-port:sonic-port/PORT/PORT_LIST[name='Ethernet0']/mtu +// ↑ ↑ ↑ +// table name key comp field name +// +// Special case :- single elem path is considered as field name. +// Libyang does not report the full path when a leaf creation fails. So, 1 elem path +// can either represent a top container or a leaf node. But it cannot be the former +// due to our usage. +func parseLyPath(p string) (table string, keys []string, field string) { + var elem []lyPathElem + for i := 0; i < len(p); { + pe, n := parseFirstElem(p[i:]) + elem = append(elem, pe) + i += n + } + // elem[1] is the container representing table name + if len(elem) > 1 { + table = elem[1].name + } + // elem[2] is the list node, with optional key predicates + if len(elem) > 2 { + keys = elem[2].vals + } + // Last elem represents db field. Single elem path is also considered as db field! + if len(elem) == 1 || len(elem) > 3 { + field = elem[len(elem)-1].name + } + return +} + +// parseFirstElem parses the first element from a libyang path. +func parseFirstElem(p string) (elem lyPathElem, n int) { + i, size := 0, len(p) + if n < size && p[n] == '/' { + i, n = 1, 1 // skip the optional '/' prefix + } + for n < size && p[n] != '[' && p[n] != '/' { + n++ // locate next predicate or path sep, whichever comes first + } + // Collect the element name.. + elem.name = p[i:n] + if k := strings.IndexByte(elem.name, ':'); k >= 0 { + elem.name = elem.name[k+1:] + } + // Parse each predicate section `[name='value']` and collect the values + for n < size && p[n] == '[' { + m := lyPredicatePattern.FindStringSubmatch(p[n:]) + if len(m) != 3 { + elem.vals = nil + return elem, size // invalid path, skip everything + } + if v := m[2]; len(v) >= 2 { // remove surrounding quotes + elem.vals = append(elem.vals, v[1:len(v)-1]) + } + n += len(m[0]) + } + // Next char must be a path separator + if n < size && p[n] != '/' { + n = size // invalid path, skip everything + } + return +} + +// lyPathElem represents a path element +type lyPathElem struct { + name string // element name + vals []string // key values +} + +// lyPredicatePattern is the regex to match a libyang formatted predicate section. +// Expects either `[name='value']` or `[name="value"]` syntax. +// Match group 1 will be the name and group 2 will be the quoted value. +// Libyang uses single quotes if the value does not contain any single quotes; +// otherwise uses double quotes. But, libyang does not insert escape chars when +// value contains both single and double quotes, resulting in a vague format. +// Such predicates cannot be parsed. +var lyPredicatePattern = regexp.MustCompile(`^\[([^=]*)=('[^']*'|"[^"]*")]`) + +// Regex patterns to extract target node name and value from libyang error message. +// Example messages: +// - Invalid value "9999999" in "vlanid" element +// - Missing required element "vlanid" in "VLAN_LIST" +// - Value "xyz" does not satisfy the constraint "Ethernet([1-3][0-9]{3}|[1-9][0-9]{2}|[1-9][0-9]|[0-9])" (range, length, or pattern) +// - Leafref "/sonic-port:sonic-port/sonic-port:PORT/sonic-port:ifname" of value "Ethernet668" points to a non-existing leaf +// - Failed to find "extra" as a sibling to "sonic-acl:aclname" +var ( + lyBadValue = regexp.MustCompile(`[Vv]alue "([^"]*)" `) + lyElemPrefix = regexp.MustCompile(`[Ee]lement "([^"]*)"`) + lyElemSuffix = regexp.MustCompile(`"([^"]*)" element`) + lyUnknownElem = regexp.MustCompile(`Failed to find "([^"]*)" `) +) + +// parseLyMessage matches a libyang returned log message using given +// regex patterns and returns the first matched group. +func parseLyMessage(s string, regex ...*regexp.Regexp) string { + for _, ex := range regex { + if m := ex.FindStringSubmatch(s); len(m) > 1 { + return m[1] + } + } + return "" +} diff --git a/cvl/internal/yparser/yparser.go b/cvl/internal/yparser/yparser.go index a8cb93adddb6..5f63c532cacf 100644 --- a/cvl/internal/yparser/yparser.go +++ b/cvl/internal/yparser/yparser.go @@ -22,12 +22,14 @@ package yparser /* Yang parser using libyang library */ import ( - "os" "fmt" + "os" + "strconv" "strings" + "unsafe" + //lint:ignore ST1001 This is safe to dot import for util package . "github.com/Azure/sonic-mgmt-common/cvl/internal/util" - "unsafe" ) /* @@ -331,6 +333,9 @@ const ( YP_OP_DELETE ) +// cvl-yin generator adds this prefix to all user defined error messages. +const customErrorPrefix = "[Error]" + var yparserInitialized bool = false func TRACE_LOG(tracelevel CVLTraceLevel, fmtStr string, args ...interface{}) { @@ -596,7 +601,6 @@ func getErrorDetails() YParserError { var errMessage string var ElemName string var errText string - var msg string var ypErrCode YParserRetCode = YP_INTERNAL_UNKNOWN var errMsg, errPath, errAppTag string @@ -606,14 +610,7 @@ func getErrorDetails() YParserError { if (ypErrFirst == nil) { return YParserError { - TableName : errtableName, ErrCode : ypErrCode, - Keys : key, - Value : ElemVal, - Field : ElemName, - Msg : errMessage, - ErrTxt: errText, - ErrAppTag: errAppTag, } } @@ -630,94 +627,44 @@ func getErrorDetails() YParserError { errAppTag = C.GoString(ypErrFirst.prev.apptag) } + // Try to resolve table, keys and field name from the error path. + errtableName, key, ElemName = parseLyPath(errPath) - /* Example error messages. - 1. Leafref "/sonic-port:sonic-port/sonic-port:PORT/sonic-port:ifname" of value "Ethernet668" points to a non-existing leaf. - (path: /sonic-interface:sonic-interface/INTERFACE[portname='Ethernet668'][ip_prefix='10.0.0.0/31']/portname) - 2. A vlan interface member cannot be part of portchannel which is already a vlan member - (path: /sonic-vlan:sonic-vlan/VLAN[name='Vlan1001']/members[.='Ethernet8']) - 3. Value "ch1" does not satisfy the constraint "Ethernet([1-3][0-9]{3}|[1-9][0-9]{2}|[1-9][0-9]|[0-9])" (range, length, or pattern). - (path: /sonic-vlan:sonic-vlan/VLAN[name='Vlan1001']/members[.='ch1'])*/ - - - /* Fetch the TABLE Name which are in CAPS. */ - resultTable := strings.SplitN(errPath, "[", 2) - if (len(resultTable) >= 2) { - resultTab := strings.Split(resultTable[0], "/") - errtableName = resultTab[len(resultTab) -1] - - /* Fetch the Error Elem Name. */ - resultElem := strings.Split(resultTable[1], "/") - ElemName = resultElem[len(resultElem) -1] + if !strings.HasPrefix(errMsg, customErrorPrefix) { + // libyang generated error message.. try to extract the field value & name + ElemVal = parseLyMessage(errMsg, lyBadValue) + if len(ElemName) == 0 { // if not resolved from path + ElemName = parseLyMessage(errMsg, lyElemPrefix, lyElemSuffix) + } + } else { + errText = errMsg[len(customErrorPrefix):] } - /* Fetch the invalid field name. */ - // errMsg is like: Invalid value "Port1" in "dst_port" element. - result := strings.Split(errMsg, "\"") - if (len(result) > 1) { - for i := range result { - if (strings.Contains(result[i], "value")) || - (strings.Contains(result[i], "Value")) { - ElemVal = result[i+1] - } - - if (strings.Contains(result[i], "element") || strings.Contains(result[i], "Element")) && (i > 0) { - ElemName = result[i-1] + switch C.ly_errno { + case C.LY_EVALID: + // validation failure + ypErrCode = translateLYErrToYParserErr(int(ypErrFirst.prev.vecode)) + if len(ElemName) != 0 { + errMessage = "Field \"" + ElemName + "\" has invalid value" + if len(ElemVal) != 0 { + errMessage += " " + strconv.Quote(ElemVal) } + } else { + errMessage = "Data validation failed" } - } else if (len(result) == 1) { - /* Custom contraint error message like in must statement. - This can be used by App to display to user. - */ - errText = errMsg - } - - // Find key elements - resultKey := strings.Split(errPath, "=") - for i := range resultKey { - if (strings.Contains(resultKey[i], "]")) { - newRes := strings.Split(resultKey[i], "]") - key = append(key, newRes[0]) - } - } - - /* Form the error message. */ - msg = "[" - for _, elem := range key { - msg = msg + elem + " " - } - msg = msg + "]" - - /* For non-constraint related errors , print below error message. */ - if (len(result) > 1) { - errMessage = errtableName + " with keys" + msg + " has field " + - ElemName + " with invalid value " + ElemVal - }else { - /* Dependent data validation error. */ - errMessage = "Dependent data validation failed for table " + - errtableName + " with keys" + msg - } - - - if (C.ly_errno == C.LY_EVALID) { //Validation failure - ypErrCode = translateLYErrToYParserErr(int(ypErrFirst.prev.vecode)) - } else { - switch (C.ly_errno) { - case C.LY_EMEM: - errText = "Memory allocation failure" - - case C.LY_ESYS: - errText = "System call failure" - - case C.LY_EINVAL: - errText = "Invalid value" - - case C.LY_EINT: - errText = "Internal error" - - case C.LY_EPLUGIN: - errText = "Error reported by a plugin" + case C.LY_EINVAL: + // invalid node. With our usage it will be the field name. + ypErrCode = YP_SYNTAX_ERROR + if field := parseLyMessage(errMsg, lyUnknownElem); len(field) != 0 { + ElemName = field + errMessage = "Unknown field \"" + field + "\"" + } else { + errMessage = "Invalid value" } + case C.LY_EMEM: + errMessage = "Resources exhausted" + default: + errMessage = "Internal error" } errObj := YParserError { @@ -853,7 +800,7 @@ func getModelChildInfo(l *YParserListInfo, node *C.struct_lys_node, exp.ErrCode = C.GoString(must[idx].eapptag) } if (must[idx].emsg != nil) { - exp.ErrStr = C.GoString(must[idx].emsg) + exp.ErrStr = strings.TrimPrefix(C.GoString(must[idx].emsg), customErrorPrefix) } l.XpathExpr[leafName] = append(l.XpathExpr[leafName], @@ -957,7 +904,7 @@ func GetModelListInfo(module *YParserModule) []*YParserListInfo { exp.ErrCode = C.GoString(must[idx].eapptag) } if (must[idx].emsg != nil) { - exp.ErrStr = C.GoString(must[idx].emsg) + exp.ErrStr = strings.TrimPrefix(C.GoString(must[idx].emsg), customErrorPrefix) } l.XpathExpr[listName] = append(l.XpathExpr[listName], diff --git a/cvl/testdata/schema/sonic-acl.yang b/cvl/testdata/schema/sonic-acl.yang index 135749c3da51..6b1e82e712c3 100644 --- a/cvl/testdata/schema/sonic-acl.yang +++ b/cvl/testdata/schema/sonic-acl.yang @@ -149,6 +149,7 @@ module sonic-acl { enum IPV4; enum IPV4ANY; enum NON_IPV4; + enum IPV6; enum IPV6ANY; enum NON_IPV6; }