From 4cfc8825063a8f8de0a978dabeb65fff5dc3b6c9 Mon Sep 17 00:00:00 2001 From: Sachin Holla <51310506+sachinholla@users.noreply.github.com> Date: Tue, 19 Sep 2023 03:04:47 +0530 Subject: [PATCH] CVL error reporting enhancements (#97) 1) Enhanced cvl syntax validator to return consistent error response when data validation fails. All syntax errors will have table, keys, and error message filled. A validation error will also include errored field name and value. ConstraintErrMsg and ErrAppTag are filled if the sonic yang node has error-message and error-app-tag values. 2) Libyang is setup to fill the node path in ly_err_first() response. Attempt to resolve table name, key values and field name from this error path. For sonic yangs, the libynag returned paths will be like "/sonic-port:sonic-port/PORT/PORT_LIST[name='Ethernet0']/mtu". Elem[1] will be the table name, elem[2] predicates will be the keys and elem[3] will be the field name. 3) Try to resolve table, key and fied info from the error message if libyang did not return an error path. If not possible, table and key info are filled from the validation request data. 4) Enable error message prefixing in the cvl schema generator, which adds a "[Error]" prefix to all the error-message strings in the schema file. This helps cvl to differentiate user defined erorr messages from linyang's messages (ly_err_item struct has a single field for both). Cvl removes this prefix before filling it in ConstraintErrMsg field. 5) Modified existing test cases to verify all fields of CVLErrorInfo instead of just success/error check Signed-off-by: Sachin Holla --- cvl/Makefile | 2 - cvl/cvl.go | 28 +- cvl/cvl_api.go | 14 + cvl/cvl_error_test.go | 85 ++ cvl/cvl_leafref_test.go | 142 +-- cvl/cvl_must_test.go | 210 ++-- cvl/cvl_syntax.go | 4 +- cvl/cvl_test.go | 1474 +++++++++------------------- cvl/cvl_when_test.go | 58 +- cvl/internal/util/util.go | 2 +- cvl/internal/yparser/ly_path.go | 132 +++ cvl/internal/yparser/yparser.go | 135 +-- cvl/testdata/schema/sonic-acl.yang | 1 + 13 files changed, 933 insertions(+), 1354 deletions(-) create mode 100644 cvl/cvl_error_test.go create mode 100644 cvl/internal/yparser/ly_path.go 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; }