From d90d239ce83736aec65016ead30b70b065f3a4b4 Mon Sep 17 00:00:00 2001 From: Partha Dutta <51353699+dutta-partha@users.noreply.github.com> Date: Wed, 13 Jan 2021 00:47:05 +0530 Subject: [PATCH] CVL Changes #10: Custom Validation infra, Unit Test reorganization and new test cases addition (#39) Adding Custom Validation Infrastructure Unit Test files reorganization Adding Test YANG Schemas Adding Unit Test for new CVL APIs Adding Unit Test for leafref, when, must expression evaluation --- cvl/Makefile | 2 +- cvl/README.md | 18 +- cvl/conf/cvl_cfg.json | 13 +- cvl/custom_validation/common.go | 132 ++ cvl/cvl.go | 63 +- cvl/cvl_api.go | 16 +- cvl/cvl_leafref_test.go | 505 +++++++ cvl/cvl_must_test.go | 638 +++++++++ cvl/cvl_test.go | 1203 ++++++++++++----- cvl/cvl_when_test.go | 147 ++ cvl/jsondata_test.go | 2 +- cvl/testdata/acl_rule.json | 18 +- cvl/testdata/aclrule.json | 12 +- cvl/testdata/port_table.json | 96 +- cvl/testdata/schema/sonic-acl-deviation.yang | 43 - cvl/testdata/schema/sonic-acl.yang | 4 +- cvl/testdata/schema/sonic-bgp-global.yang | 289 ++++ cvl/testdata/schema/sonic-bgp-neighbor.yang | 84 -- .../schema/sonic-device-metadata.yang | 15 +- cvl/testdata/schema/sonic-ifa.yang | 87 ++ cvl/testdata/schema/sonic-igmp-snooping.yang | 242 ++++ cvl/testdata/schema/sonic-interface.yang | 116 ++ .../schema/sonic-loopback-interface.yang | 54 + cvl/testdata/schema/sonic-mirror-session.yang | 2 +- .../schema/sonic-portchannel-interface.yang | 78 +- cvl/testdata/schema/sonic-portchannel.yang | 209 ++- cvl/testdata/schema/sonic-spanning-tree.yang | 246 ++++ cvl/testdata/schema/sonic-tam.yang | 77 ++ cvl/testdata/schema/sonic-vlan-interface.yang | 97 +- cvl/testdata/schema/sonic-vlan.yang | 289 ++-- cvl/testdata/schema/sonic-vrf.yang | 62 + cvl/testdata/schema/sonic-vxlan.yang | 215 +++ cvl/tests/acl_rule.json | 2 - 33 files changed, 4238 insertions(+), 838 deletions(-) create mode 100644 cvl/custom_validation/common.go create mode 100644 cvl/cvl_leafref_test.go create mode 100644 cvl/cvl_must_test.go create mode 100644 cvl/cvl_when_test.go delete mode 100644 cvl/testdata/schema/sonic-acl-deviation.yang create mode 100644 cvl/testdata/schema/sonic-bgp-global.yang delete mode 100644 cvl/testdata/schema/sonic-bgp-neighbor.yang create mode 100644 cvl/testdata/schema/sonic-ifa.yang create mode 100644 cvl/testdata/schema/sonic-igmp-snooping.yang create mode 100644 cvl/testdata/schema/sonic-interface.yang create mode 100644 cvl/testdata/schema/sonic-loopback-interface.yang create mode 100755 cvl/testdata/schema/sonic-spanning-tree.yang create mode 100644 cvl/testdata/schema/sonic-tam.yang create mode 100644 cvl/testdata/schema/sonic-vrf.yang create mode 100644 cvl/testdata/schema/sonic-vxlan.yang diff --git a/cvl/Makefile b/cvl/Makefile index 278d97e6acfc..75e24b8d544c 100644 --- a/cvl/Makefile +++ b/cvl/Makefile @@ -55,7 +55,7 @@ schema: test-schema: | schema $(MAKE) -C testdata/schema - cp $(CVL_SCHEMA_DIR)/*.yin $(CVL_TEST_SCHEMA_DIR)/ + cp -n $(CVL_SCHEMA_DIR)/*.yin $(CVL_TEST_SCHEMA_DIR)/ tests: $(MAKE) -C tests diff --git a/cvl/README.md b/cvl/README.md index 9445cb22a80a..0e6ee34f3851 100644 --- a/cvl/README.md +++ b/cvl/README.md @@ -20,6 +20,7 @@ Below steps need to be done to enable CVL logging. 2. Change the logging flags from "false" to "true" as below: +``` { "TRACE_CACHE": "true", "TRACE_LIBYANG": "true", @@ -29,15 +30,22 @@ Below steps need to be done to enable CVL logging. "TRACE_DELETE": "true", "TRACE_SEMANTIC": "true", "TRACE_SYNTAX": "true", + "TRACE_ONERROR": "true", "__comment1__": "Set LOGTOSTDER to 'true' to log on standard error", - "LOGTOSTDERR": "true", - "__comment2__": "Display log upto INFO level", - "STDERRTHRESHOLD": "INFO", - "__comment3__": "Display log upto INFO level 8", - "VERBOSITY": "8", + "LOGTOSTDERR": "false", + "__comment2__": "Log messages to standard error at or above this severity level", + "STDERRTHRESHOLD": "ERROR", + "__comment3__": "Log to /tmp/cvl.log file", + "LOG_TO_FILE": "true", + "__comment4__": "Limit log file size in bytes, 0 means no limit, default 10MB", + "LOG_FILE_SIZE": "10485760", + "__comment5__": "Set verbosity level(1 to 8) for verbose logs", + "VERBOSITY": "0", "SKIP_VALIDATION": "false", "SKIP_SEMANTIC_VALIDATION": "false" } +``` + 3. Below environment variables need to be set at the end in /usr/bin/rest-server.sh in mgmt-framework docker. export CVL_DEBUG=1 diff --git a/cvl/conf/cvl_cfg.json b/cvl/conf/cvl_cfg.json index 1445bf3029b0..e5a51f946f69 100644 --- a/cvl/conf/cvl_cfg.json +++ b/cvl/conf/cvl_cfg.json @@ -7,13 +7,16 @@ "TRACE_DELETE": "false", "TRACE_SEMANTIC": "false", "TRACE_SYNTAX": "false", - "__comment1__": "Log trace data when error occurs", - "TRACE_ONERROR": "true", - "__comment2__": "Set LOGTOSTDER to 'true' to log on standard error", + "TRACE_ONERROR": "false", + "__comment1__": "Set LOGTOSTDER to 'true' to log on standard error", "LOGTOSTDERR": "false", - "__comment3__": "Display log upto INFO level", + "__comment2__": "Display log upto INFO level", "STDERRTHRESHOLD": "ERROR", - "__comment4__": "Display log upto INFO level 8", + "__comment3__": "Log to /tmp/cvl.log file", + "LOG_TO_FILE": "false", + "__comment4__": "Limit log file size in bytes, 0 means no limit, default 10MB", + "LOG_FILE_SIZE": "10485760", + "__comment5__": "Display log upto INFO level 8", "VERBOSITY": "0", "SKIP_VALIDATION": "false", "SKIP_SEMANTIC_VALIDATION": "false" diff --git a/cvl/custom_validation/common.go b/cvl/custom_validation/common.go new file mode 100644 index 000000000000..9625113c73c8 --- /dev/null +++ b/cvl/custom_validation/common.go @@ -0,0 +1,132 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 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 custom_validation + +import ( + "reflect" + "github.com/antchfx/xmlquery" + "github.com/go-redis/redis/v7" + "github.com/Azure/sonic-mgmt-common/cvl/internal/util" + "github.com/Azure/sonic-mgmt-common/cvl/internal/yparser" + ) + +type CustomValidation struct {} + +type CVLValidateType uint +const ( + VALIDATE_NONE CVLValidateType = iota //Data is used as dependent data + VALIDATE_SYNTAX //Syntax is checked and data is used as dependent data + VALIDATE_SEMANTICS //Semantics is checked + VALIDATE_ALL //Syntax and Semantics are checked +) + +type CVLOperation uint +const ( + OP_NONE CVLOperation = 0 //Used to just validate the config without any operation + OP_CREATE = 1 << 0//For Create operation + OP_UPDATE = 1 << 1//For Update operation + OP_DELETE = 1 << 2//For Delete operation +) + +//CVLRetCode CVL Error codes +type CVLRetCode int +const ( + CVL_SUCCESS CVLRetCode = iota + CVL_ERROR + CVL_NOT_IMPLEMENTED + CVL_INTERNAL_UNKNOWN + CVL_FAILURE + CVL_SYNTAX_ERROR = CVLRetCode(yparser.YP_SYNTAX_ERROR) + CVL_SEMANTIC_ERROR = CVLRetCode(yparser.YP_SEMANTIC_ERROR) + CVL_SYNTAX_MISSING_FIELD = CVLRetCode(yparser.YP_SYNTAX_MISSING_FIELD) + CVL_SYNTAX_INVALID_FIELD = CVLRetCode(yparser.YP_SYNTAX_INVALID_FIELD) /* Invalid Field */ + CVL_SYNTAX_INVALID_INPUT_DATA = CVLRetCode(yparser.YP_SYNTAX_INVALID_INPUT_DATA) /*Invalid Input Data */ + CVL_SYNTAX_MULTIPLE_INSTANCE = CVLRetCode(yparser.YP_SYNTAX_MULTIPLE_INSTANCE) /* Multiple Field Instances */ + CVL_SYNTAX_DUPLICATE = CVLRetCode(yparser.YP_SYNTAX_DUPLICATE) /* Duplicate Fields */ + CVL_SYNTAX_ENUM_INVALID = CVLRetCode(yparser.YP_SYNTAX_ENUM_INVALID) /* Invalid enum value */ + CVL_SYNTAX_ENUM_INVALID_NAME = CVLRetCode(yparser.YP_SYNTAX_ENUM_INVALID_NAME) /* Invalid enum name */ + CVL_SYNTAX_ENUM_WHITESPACE = CVLRetCode(yparser.YP_SYNTAX_ENUM_WHITESPACE) /* Enum name with leading/trailing whitespaces */ + CVL_SYNTAX_OUT_OF_RANGE = CVLRetCode(yparser.YP_SYNTAX_OUT_OF_RANGE) /* Value out of range/length/pattern (data) */ + CVL_SYNTAX_MINIMUM_INVALID = CVLRetCode(yparser.YP_SYNTAX_MINIMUM_INVALID) /* min-elements constraint not honored */ + CVL_SYNTAX_MAXIMUM_INVALID = CVLRetCode(yparser.YP_SYNTAX_MAXIMUM_INVALID) /* max-elements constraint not honored */ + CVL_SEMANTIC_DEPENDENT_DATA_MISSING = CVLRetCode(yparser.YP_SEMANTIC_DEPENDENT_DATA_MISSING) /* Dependent Data is missing */ + CVL_SEMANTIC_MANDATORY_DATA_MISSING = CVLRetCode(yparser.YP_SEMANTIC_MANDATORY_DATA_MISSING) /* Mandatory Data is missing */ + CVL_SEMANTIC_KEY_ALREADY_EXIST = CVLRetCode(yparser.YP_SEMANTIC_KEY_ALREADY_EXIST) /* Key already existing. */ + CVL_SEMANTIC_KEY_NOT_EXIST = CVLRetCode(yparser.YP_SEMANTIC_KEY_NOT_EXIST) /* Key is missing. */ + CVL_SEMANTIC_KEY_DUPLICATE = CVLRetCode(yparser.YP_SEMANTIC_KEY_DUPLICATE) /* Duplicate key. */ + CVL_SEMANTIC_KEY_INVALID = CVLRetCode(yparser.YP_SEMANTIC_KEY_INVALID) +) + +//CVLEditConfigData Strcture for key and data in API +type CVLEditConfigData struct { + VType CVLValidateType //Validation type + VOp CVLOperation //Operation type + Key string //Key format : "PORT|Ethernet4" + Data map[string]string //Value : {"alias": "40GE0/28", "mtu" : 9100, "admin_status": down} +} + +//CVLErrorInfo CVL Error Structure +type CVLErrorInfo struct { + TableName string /* Table having error */ + ErrCode CVLRetCode /* CVL Error return Code. */ + CVLErrDetails string /* CVL Error Message details. */ + Keys []string /* Keys of the Table having error. */ + Value string /* Field Value throwing error */ + Field string /* Field Name throwing error . */ + Msg string /* Detailed error message. */ + ConstraintErrMsg string /* Constraint error message. */ + ErrAppTag string +} + +type CustValidationCache struct { + Data interface{} +} + +//CustValidationCtxt Custom validation context passed to custom validation function +type CustValidationCtxt struct { + ReqData []CVLEditConfigData //All request data + CurCfg *CVLEditConfigData //Current request data for which validation should be done + YNodeName string //YANG node name + YNodeVal string //YANG node value, leaf-list will have "," separated value + YCur *xmlquery.Node //YANG data tree + SessCache *CustValidationCache //Session cache, can be used for storing data, persistent in session + RClient *redis.Client //Redis client +} + +//InvokeCustomValidation Common function to invoke custom validation +//TBD should we do this using GO plugin feature ? +func InvokeCustomValidation(cv *CustomValidation, name string, args... interface{}) CVLErrorInfo { + inputs := make([]reflect.Value, len(args)) + for i := range args { + inputs[i] = reflect.ValueOf(args[i]) + } + + f := reflect.ValueOf(cv).MethodByName(name) + if !f.IsNil() { + v := f.Call(inputs) + util.TRACE_LEVEL_LOG(util.TRACE_SEMANTIC, + "InvokeCustomValidation: %s(), CVLErrorInfo: %v", name, v[0]) + + return (v[0].Interface()).(CVLErrorInfo) + } + + return CVLErrorInfo{ErrCode: CVL_SUCCESS} +} + diff --git a/cvl/cvl.go b/cvl/cvl.go index cc6a1c678950..2f3f8f41646d 100644 --- a/cvl/cvl.go +++ b/cvl/cvl.go @@ -34,6 +34,8 @@ import ( "sync" "io/ioutil" "path/filepath" + custv "github.com/Azure/sonic-mgmt-common/cvl/custom_validation" + "unsafe" ) //DB number @@ -106,6 +108,7 @@ type modelTableInfo struct { whenExpr map[string][]*whenInfo tablesForMustExp map[string]CVLOperation refFromTables []tblFieldPair //list of table or table/field referring to this table + custValidation map[string]string // Map for custom validation node and function name dfltLeafVal map[string]string //map of leaf names and default value mandatoryNodes map[string]bool //map of leaf names and mandatory flag } @@ -140,6 +143,7 @@ type CVL struct { maxTableElem map[string]int //max element count per table batchLeaf []*yparser.YParserLeafValue //field name and value yv *YValidator //Custom YANG validator for validating external dependencies + custvCache custv.CustValidationCache //Custom validation cache per session } // Struct for model namepsace and prefix @@ -449,6 +453,7 @@ func storeModelInfo(modelFile string, module *yparser.YParserModule) { tInfo.redisTableSize = lInfo.RedisTableSize tInfo.keys = lInfo.Keys tInfo.mapLeaf = lInfo.MapLeaf + tInfo.custValidation = lInfo.CustValidation tInfo.mandatoryNodes = lInfo.MandatoryNodes //store default values used in must and when exp @@ -955,6 +960,62 @@ func (c *CVL) addCfgDataItem(configData *map[string]interface{}, return tblName, key } +//Perform user defined custom validation +func (c *CVL) doCustomValidation(node *xmlquery.Node, + custvCfg []custv.CVLEditConfigData, + curCustvCfg *custv.CVLEditConfigData, yangListName, + tbl, key string) CVLErrorInfo { + + cvlErrObj := CVLErrorInfo{ErrCode : CVL_SUCCESS} + + // yangListName provides the correct table name defined in sonic-yang + // For ex. VLAN_INTERFACE_LIST and VLAN_INTERFACE_IPADDR_LIST are in same container + for nodeName, custFunc := range modelInfo.tableInfo[yangListName].custValidation { + //find the node value + //node value is empty for custom validation function at list level + nodeVal := "" + if !strings.HasSuffix(nodeName, "_LIST") { + for nodeLeaf := node.FirstChild; nodeLeaf != nil; + nodeLeaf = nodeLeaf.NextSibling { + if (nodeName != nodeLeaf.Data) { + continue + } + + if (len(nodeLeaf.Attr) > 0) && + (nodeLeaf.Attr[0].Name.Local == "leaf-list") { + nodeVal = curCustvCfg.Data[nodeName] + } else { + nodeVal = nodeLeaf.FirstChild.Data + } + } + + } + + //Call custom validation functions + CVL_LOG(INFO_TRACE, "Calling custom validation function %s", custFunc) + pCustv := &custv.CustValidationCtxt{ + ReqData: custvCfg, + CurCfg: curCustvCfg, + YNodeName: nodeName, + YNodeVal: nodeVal, + YCur: node, + SessCache: &(c.custvCache), + RClient: redisClient} + + errObj := custv.InvokeCustomValidation(&custv.CustomValidation{}, + custFunc, pCustv) + + cvlErrObj = *(*CVLErrorInfo)(unsafe.Pointer(&errObj)) + + if (cvlErrObj.ErrCode != CVL_SUCCESS) { + CVL_LOG(WARNING, "Custom validation failed, Error = %v", cvlErrObj) + return cvlErrObj + } + } + + return cvlErrObj +} + // getLeafRefInfo This function returns leafrefInfo structure based on table name, // target table name and leaf node name where leafRef is present func getLeafRefInfo(tblName, fldName, targetTblName string) *leafRefInfo { @@ -978,4 +1039,4 @@ func isMandatoryTrueNode(tblName, field string) bool { } return false -} +} \ No newline at end of file diff --git a/cvl/cvl_api.go b/cvl/cvl_api.go index de5e84bbc3b9..0e80bc55cc8e 100644 --- a/cvl/cvl_api.go +++ b/cvl/cvl_api.go @@ -21,6 +21,7 @@ package cvl import ( "fmt" + "reflect" "encoding/json" "github.com/go-redis/redis/v7" toposort "github.com/philopon/go-toposort" @@ -29,7 +30,9 @@ import ( . "github.com/Azure/sonic-mgmt-common/cvl/internal/util" "strings" "github.com/antchfx/xmlquery" + "unsafe" "runtime" + custv "github.com/Azure/sonic-mgmt-common/cvl/custom_validation" "time" "sync" ) @@ -321,7 +324,7 @@ func (c *CVL) ValidateEditConfig(cfgData []CVLEditConfigData) (cvlErr CVLErrorIn caller = f.Name() } - CVL_LOG(INFO_DEBUG, "ValidateEditConfig() called from %s() : %v", caller, cfgData) + CVL_LOG(INFO_DEBUG, "ValidateEditConfig() called from %s() : %v", caller, cfgData) if SkipValidation() { CVL_LOG(INFO_TRACE, "Skipping CVL validation.") @@ -329,6 +332,8 @@ func (c *CVL) ValidateEditConfig(cfgData []CVLEditConfigData) (cvlErr CVLErrorIn } //Type cast to custom validation cfg data + sliceHeader := *(*reflect.SliceHeader)(unsafe.Pointer(&cfgData)) + custvCfg := *(*[]custv.CVLEditConfigData)(unsafe.Pointer(&sliceHeader)) c.clearTmpDbCache() //c.yv.root.FirstChild = nil @@ -544,6 +549,13 @@ func (c *CVL) ValidateEditConfig(cfgData []CVLEditConfigData) (cvlErr CVLErrorIn continue } + //Step 3.2 : Run all custom validations + cvlErrObj= c.doCustomValidation(node, custvCfg, &custvCfg[i], yangListName, + tbl, key) + if cvlErrObj.ErrCode != CVL_SUCCESS { + return cvlErrObj,cvlErrObj.ErrCode + } + //Step 3.3 : Perform semantic validation if cvlErrObj = c.validateSemantics(node, yangListName, key, &cfgData[i]); cvlErrObj.ErrCode != CVL_SUCCESS { @@ -1087,4 +1099,4 @@ func (c *CVL) GetAllReferringTables(tableName string) (map[string][]string) { } return refTbls -} +} \ No newline at end of file diff --git a/cvl/cvl_leafref_test.go b/cvl/cvl_leafref_test.go new file mode 100644 index 000000000000..8fa2a9b1a39d --- /dev/null +++ b/cvl/cvl_leafref_test.go @@ -0,0 +1,505 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 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 ( + "fmt" + "testing" + "github.com/Azure/sonic-mgmt-common/cvl" +) + +// EditConfig(Create) with chained leafref from redis +func TestValidateEditConfig_Create_Chained_Leafref_DepData_Positive(t *testing.T) { + depDataMap := map[string]interface{} { + "VLAN" : map[string]interface{} { + "Vlan100": map[string]interface{} { + "members@": "Ethernet1", + "vlanid": "100", + }, + }, + "PORT" : map[string]interface{} { + "Ethernet1" : map[string]interface{} { + "alias":"hundredGigE1", + "lanes": "81,82,83,84", + "mtu": "9100", + }, + "Ethernet2" : map[string]interface{} { + "alias":"hundredGigE1", + "lanes": "85,86,87,89", + "mtu": "9100", + }, + }, + "ACL_TABLE" : map[string]interface{} { + "TestACL1": map[string] interface{} { + "stage": "INGRESS", + "type": "L3", + "ports@":"Ethernet2", + }, + }, + } + + //Prepare data in Redis + loadConfigDB(rclient, depDataMap) + + cvSess, _ := cvl.ValidationSessOpen() + + cfgDataVlan := []cvl.CVLEditConfigData { + cvl.CVLEditConfigData { + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "VLAN_MEMBER|Vlan100|Ethernet1", + map[string]string { + "tagging_mode" : "tagged", + }, + }, + } + + _, err := cvSess.ValidateEditConfig(cfgDataVlan) + + if err != cvl.CVL_SUCCESS { //should succeed + t.Errorf("Config Validation failed.") + return + } + + cfgDataAclRule := []cvl.CVLEditConfigData { + cvl.CVLEditConfigData { + cvl.VALIDATE_ALL, + 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", + }, + }, + } + + + _, err = cvSess.ValidateEditConfig(cfgDataAclRule) + + cvl.ValidationSessClose(cvSess) + + if err != cvl.CVL_SUCCESS { //should succeed + t.Errorf("Config Validation failed.") + } + + unloadConfigDB(rclient, depDataMap) +} + +func TestValidateEditConfig_Create_Leafref_To_NonKey_Positive(t *testing.T) { + depDataMap := map[string]interface{} { + "BGP_GLOBALS" : map[string]interface{} { + "default": map[string] interface{} { + "router_id": "1.1.1.1", + "local_asn": "12338", + }, + }, + } + + //Prepare data in Redis + loadConfigDB(rclient, depDataMap) + + cfgData := []cvl.CVLEditConfigData { + cvl.CVLEditConfigData { + cvl.VALIDATE_ALL, + cvl.OP_UPDATE, + "DEVICE_METADATA|localhost", + map[string]string { + "vrf_name": "default", + "bgp_asn": "12338", + }, + }, + } + 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) +} + +func TestValidateEditConfig_Update_Leafref_To_NonKey_Negative(t *testing.T) { + depDataMap := map[string]interface{} { + "BGP_GLOBALS" : map[string]interface{} { + "default": map[string] interface{} { + "router_id": "1.1.1.1", + "local_asn": "12338", + }, + }, + } + + //Prepare data in Redis + loadConfigDB(rclient, depDataMap) + + cfgData := []cvl.CVLEditConfigData { + cvl.CVLEditConfigData { + cvl.VALIDATE_ALL, + cvl.OP_UPDATE, + "DEVICE_METADATA|localhost", + map[string]string { + "vrf_name": "default", + "bgp_asn": "17698", + }, + }, + } + 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) +} + +func TestValidateEditConfig_Create_Leafref_Multi_Key_Positive(t *testing.T) { + depDataMap := map[string]interface{} { + "ACL_TABLE" : map[string]interface{} { + "TestACL901": map[string] interface{} { + "stage": "INGRESS", + "type": "L3", + }, + "TestACL902": map[string] interface{} { + "stage": "INGRESS", + "type": "L3", + }, + }, + "ACL_RULE" : map[string]interface{} { + "TestACL901|Rule1": map[string] interface{} { + "PACKET_ACTION": "FORWARD", + "IP_TYPE": "IPV4", + "SRC_IP": "10.1.1.1/32", + "DST_IP": "20.2.2.2/32", + }, + "TestACL902|Rule1": map[string] interface{} { + "PACKET_ACTION": "FORWARD", + "IP_TYPE": "IPV4", + "SRC_IP": "10.1.1.2/32", + "DST_IP": "20.2.2.4/32", + }, + }, + } + + //Prepare data in Redis + loadConfigDB(rclient, depDataMap) + + cfgData := []cvl.CVLEditConfigData { + cvl.CVLEditConfigData { + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "TAM_INT_IFA_FLOW_TABLE|Flow_1", + map[string]string { + "acl-table-name": "TestACL901", + "acl-rule-name": "Rule1", + }, + }, + } + 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) +} + +func TestValidateEditConfig_Create_Leafref_Multi_Key_Negative(t *testing.T) { + depDataMap := map[string]interface{} { + "ACL_TABLE" : map[string]interface{} { + "TestACL901": map[string] interface{} { + "stage": "INGRESS", + "type": "L3", + }, + "TestACL902": map[string] interface{} { + "stage": "INGRESS", + "type": "L3", + }, + }, + "ACL_RULE" : map[string]interface{} { + "TestACL902|Rule1": map[string] interface{} { + "PACKET_ACTION": "FORWARD", + "IP_TYPE": "IPV4", + "SRC_IP": "10.1.1.2/32", + "DST_IP": "20.2.2.4/32", + }, + }, + } + + //Prepare data in Redis + loadConfigDB(rclient, depDataMap) + + cfgData := []cvl.CVLEditConfigData { + cvl.CVLEditConfigData { + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "TAM_INT_IFA_FLOW_TABLE|Flow_1", + map[string]string { + "acl-table-name": "TestACL901", + "acl-rule-name": "Rule1", //This is not there in above depDataMap + }, + }, + } + 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) +} + +func TestValidateEditConfig_Create_Leafref_With_Other_DataType_In_Union_Positive(t *testing.T) { + + depDataMap := map[string]interface{}{ + "STP": map[string]interface{}{ + "GLOBAL": map[string]interface{}{ + "mode": "rpvst", + }, + }, + } + + loadConfigDB(rclient, depDataMap) + cvSess, _ := cvl.ValidationSessOpen() + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "STP_PORT|StpIntf10", //Non-leafref + 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) +} + +func TestValidateEditConfig_Create_Leafref_With_Other_DataType_In_Union_Negative(t *testing.T) { + + depDataMap := map[string]interface{}{ + "STP": map[string]interface{}{ + "GLOBAL": map[string]interface{}{ + "mode": "rpvst", + }, + }, + } + + loadConfigDB(rclient, depDataMap) + cvSess, _ := cvl.ValidationSessOpen() + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "STP_PORT|Test12", //Non-leafref + 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) +} + +func TestValidateEditConfig_Create_Leafref_With_Other_DataType_In_Union_Non_Existing_Negative(t *testing.T) { + + depDataMap := map[string]interface{}{ + "STP": map[string]interface{}{ + "GLOBAL": map[string]interface{}{ + "mode": "rpvst", + }, + }, + } + + loadConfigDB(rclient, depDataMap) + cvSess, _ := cvl.ValidationSessOpen() + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "STP_PORT|Ethernet3999", //Correct PORT format but not existing + 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 fail as leafref does not exist + t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + } + + unloadConfigDB(rclient, depDataMap) +} + +func TestValidateEditConfig_Delete_Leafref(t *testing.T) { + depDataMap := map[string]interface{}{ + "PORTCHANNEL": map[string]interface{}{ + "PortChannel1": map[string]interface{}{ + "NULL": "NULL", + }, + "PortChannel2": map[string]interface{}{ + "NULL": "NULL", + }, + "PortChannel3": map[string]interface{}{ + "NULL": "NULL", + }, + "PortChannel4": map[string]interface{}{ + "NULL": "NULL", + }, + }, + "ACL_TABLE": map[string]interface{}{ + "TestACL1": map[string]interface{}{ + "type": "L3", + "stage": "INGRESS", + "ports@": "PortChannel1", + }, + "TestACL2": map[string]interface{}{ + "type": "L3", + "stage": "INGRESS", + "ports@": "PortChannel3,PortChannel4", + }, + }, + } + + loadConfigDB(rclient, depDataMap) + defer unloadConfigDB(rclient, depDataMap) + + t.Run("positive", deletePO(2, true)) + t.Run("negative", deletePO(1, false)) + t.Run("with_dep", deleteACLAndPO("TestACL1", "nil", 1, false, true)) + t.Run("with_dep_field", deleteACLAndPO("TestACL1", "", 1, false, true)) + t.Run("with_dep_update", deleteACLAndPO("TestACL2", "PortChannel4", 3, false, true)) + //t.Run("with_dep_bulk", deleteACLAndPO("TestACL1", 1, true, true)) +} + +func deletePO(poId int, expSuccess bool) func(*testing.T) { + return func (t *testing.T) { + session, _ := cvl.ValidationSessOpen() + defer cvl.ValidationSessClose(session) + validateDeletePO(t, session, nil, poId, expSuccess) + } +} + +func deleteACLAndPO(aclName, ports string, poId int, bulk, expSuccess bool) func(*testing.T) { + return func (t *testing.T) { + session, _ := cvl.ValidationSessOpen() + defer cvl.ValidationSessClose(session) + var cfgData []cvl.CVLEditConfigData + + cfgData = append(cfgData, cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_DELETE, + fmt.Sprintf("ACL_TABLE|%s", aclName), + map[string]string{ }, + }) + + if ports != "nil" { + cfgData[0].Data["ports@"] = ports + if ports != "" { + cfgData[0].VOp = cvl.OP_UPDATE + } + } + + if !bulk { + errInfo, status := session.ValidateEditConfig(cfgData) + if status != cvl.CVL_SUCCESS { + t.Errorf("ACL \"%s\" delete validation failed; %v", aclName, errInfo) + return + } + + cfgData[0].VType = cvl.VALIDATE_NONE + } + + validateDeletePO(t, session, cfgData, poId, expSuccess) + } +} + +func validateDeletePO(t *testing.T, session *cvl.CVL, cfgData []cvl.CVLEditConfigData, poId int, expSuccess bool) { + cfgData = append(cfgData, cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_DELETE, + fmt.Sprintf("PORTCHANNEL|PortChannel%d", poId), + map[string]string{ }, + }) + + errInfo, status := session.ValidateEditConfig(cfgData) + if expSuccess && status != cvl.CVL_SUCCESS { + t.Errorf("po%d delete validation failed; %v", poId, errInfo) + } + if !expSuccess && status == cvl.CVL_SUCCESS { + t.Errorf("po%d delete validation should have failed", poId) + } +} + diff --git a/cvl/cvl_must_test.go b/cvl/cvl_must_test.go new file mode 100644 index 000000000000..4f677b9217d3 --- /dev/null +++ b/cvl/cvl_must_test.go @@ -0,0 +1,638 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 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 ( + "fmt" + "testing" + "github.com/Azure/sonic-mgmt-common/cvl" +) + +func TestValidateEditConfig_Delete_Must_Check_Positive(t *testing.T) { + depDataMap := map[string]interface{} { + "PORT" : map[string]interface{} { + "Ethernet3" : map[string]interface{} { + "alias":"hundredGigE1", + "lanes": "81,82,83,84", + }, + "Ethernet5" : map[string]interface{} { + "alias":"hundredGigE1", + "lanes": "85,86,87,89", + }, + }, + "ACL_TABLE" : map[string]interface{} { + "TestACL1": map[string] interface{} { + "stage": "INGRESS", + "type": "L3", + "ports@": "Ethernet3,Ethernet5", + }, + "TestACL2": map[string] interface{} { + "stage": "INGRESS", + "type": "L3", + }, + }, + "ACL_RULE" : map[string]interface{} { + "TestACL1|Rule1": map[string] interface{} { + "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", + }, + "TestACL2|Rule2": map[string] interface{} { + "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", + }, + }, + } + + //Prepare data in Redis + loadConfigDB(rclient, depDataMap) + + cfgDataAclRule := []cvl.CVLEditConfigData { + cvl.CVLEditConfigData { + cvl.VALIDATE_ALL, + cvl.OP_DELETE, + "ACL_RULE|TestACL2|Rule2", + map[string]string { + }, + }, + } + + 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) +} + +func TestValidateEditConfig_Delete_Must_Check_Negative(t *testing.T) { + depDataMap := map[string]interface{} { + "PORT" : map[string]interface{} { + "Ethernet3" : map[string]interface{} { + "alias":"hundredGigE1", + "lanes": "81,82,83,84", + //"mtu": "9100", + }, + "Ethernet5" : map[string]interface{} { + "alias":"hundredGigE1", + "lanes": "85,86,87,89", + //"mtu": "9100", + }, + }, + "ACL_TABLE" : map[string]interface{} { + "TestACL1": map[string] interface{} { + "stage": "INGRESS", + "type": "L3", + "ports@": "Ethernet3,Ethernet5", + }, + }, + "ACL_RULE" : map[string]interface{} { + "TestACL1|Rule1": map[string] interface{} { + "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", + }, + }, + } + + //Prepare data in Redis + loadConfigDB(rclient, depDataMap) + + cfgDataAclRule := []cvl.CVLEditConfigData { + cvl.CVLEditConfigData { + cvl.VALIDATE_ALL, + cvl.OP_DELETE, + "ACL_RULE|TestACL1|Rule1", + map[string]string { + }, + }, + } + + 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) +} + +func TestValidateEditConfig_Create_ErrAppTag_In_Must_Negative(t *testing.T) { + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "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) + } + +} + +func TestValidateEditConfig_MustExp_With_Default_Value_Positive(t *testing.T) { + depDataMap := map[string]interface{} { + "VLAN" : map[string]interface{} { + "Vlan2001": map[string] interface{} { + "vlanid": "2001", + }, + }, + } + + + //Try to create er interface collding with vlan interface IP prefix + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "CFG_L2MC_TABLE|Vlan2001", + map[string]string{ + "enabled": "true", + "query-max-response-time": "25", //default query-interval = 125 + }, + }, + } + + loadConfigDB(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) + } + +} + +func TestValidateEditConfig_MustExp_With_Default_Value_Negative(t *testing.T) { + depDataMap := map[string]interface{} { + "VLAN" : map[string]interface{} { + "Vlan2002": map[string] interface{} { + "vlanid": "2002", + }, + }, + } + + + //Try to create er interface collding with vlan interface IP prefix + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "CFG_L2MC_TABLE|Vlan2002", + map[string]string{ + "enabled": "true", + "query-interval": "9", //default query-max-response-time = 10 + }, + }, + } + + loadConfigDB(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 fail %v", cvlErrInfo) + } + +} + +func TestValidateEditConfig_MustExp_Chained_Predicate_Positive(t *testing.T) { + depDataMap := map[string]interface{} { + "VLAN" : map[string]interface{} { + "Vlan701": map[string] interface{} { + "vlanid": "701", + "members@": "Ethernet20", + }, + "Vlan702": map[string] interface{} { + "vlanid": "702", + "members@": "Ethernet20,Ethernet24,Ethernet28", + }, + "Vlan703": map[string] interface{} { + "vlanid": "703", + "members@": "Ethernet20", + }, + }, + "VLAN_MEMBER" : map[string]interface{} { + "Vlan701|Ethernet20": map[string] interface{} { + "tagging_mode": "tagged", + }, + "Vlan702|Ethernet20": map[string] interface{} { + "tagging_mode": "tagged", + }, + "Vlan702|Ethernet24": map[string] interface{} { + "tagging_mode": "tagged", + }, + "Vlan702|Ethernet28": map[string] interface{} { + "tagging_mode": "tagged", + }, + "Vlan703|Ethernet20": map[string] interface{} { + "tagging_mode": "tagged", + }, + }, + "INTERFACE" : map[string]interface{} { + "Ethernet20|1.1.1.0/32": map[string] interface{} { + "NULL": "NULL", + }, + "Ethernet24|1.1.2.0/32": map[string] interface{} { + "NULL": "NULL", + }, + "Ethernet28|1.1.2.0/32": map[string] interface{} { + "NULL": "NULL", + }, + "Ethernet20|1.1.3.0/32": map[string] interface{} { + "NULL": "NULL", + }, + }, + "VLAN_INTERFACE" : map[string]interface{} { + "Vlan701|2.2.2.0/32": map[string] interface{} { + "NULL": "NULL", + }, + "Vlan701|2.2.3.0/32": map[string] interface{} { + "NULL": "NULL", + }, + "Vlan702|2.2.4.0/32": map[string] interface{} { + "NULL": "NULL", + }, + "Vlan702|2.2.5.0/32": map[string] interface{} { + "NULL": "NULL", + }, + "Vlan703|2.2.6.0/32": map[string] interface{} { + "NULL": "NULL", + }, + }, + } + + + //Try to create er interface collding with vlan interface IP prefix + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "VLAN_INTERFACE|Vlan702|1.1.2.0/32", + map[string]string{ + "NULL": "NULL", + }, + }, + } + + loadConfigDB(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) + } + +} + +func TestValidateEditConfig_MustExp_Within_Same_Table_Negative(t *testing.T) { + //Try to create + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "TAM_COLLECTOR_TABLE|Col10", + map[string]string{ + "ipaddress-type": "ipv6", //Invalid ip address type + "ipaddress": "10.101.1.2", + }, + }, + } + + 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) + } + +} + +//Check if all data is fetched for xpath without predicate +func TestValidateEditConfig_MustExp_Without_Predicate_Positive(t *testing.T) { + depDataMap := map[string]interface{} { + "VLAN" : map[string]interface{} { + "Vlan201": map[string] interface{} { + "vlanid": "201", + "members@": "Ethernet4,Ethernet8,Ethernet12,Ethernet16", + }, + "Vlan202": map[string] interface{} { + "vlanid": "202", + "members@": "Ethernet4", + }, + }, + } + + //Try to create + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "VLAN_INTERFACE|Vlan201", + map[string]string{ + "NULL": "NULL", + }, + }, + } + + loadConfigDB(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) +} + +func TestValidateEditConfig_MustExp_Non_Key_As_Predicate_Negative(t *testing.T) { + depDataMap := map[string]interface{} { + "VLAN" : map[string]interface{} { + "Vlan201": map[string] interface{} { + "vlanid": "201", + }, + "Vlan202": map[string] interface{} { + "vlanid": "202", + }, + }, + "VXLAN_TUNNEL" : map[string]interface{} { + "tun1": map[string] interface{} { + "src_ip": "10.10.1.2", + }, + }, + "VXLAN_TUNNEL_MAP" : map[string]interface{} { + "tun1|vmap1": map[string] interface{} { + "vlan": "Vlan201", + "vni": "300", + }, + }, + } + + //Try to create + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "VXLAN_TUNNEL_MAP|tun1|vmap2", + map[string]string{ + "vlan": "Vlan202", + "vni": "300", //same VNI is not valid + }, + }, + } + + loadConfigDB(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) +} + +func TestValidateEditConfig_MustExp_Non_Key_As_Predicate_In_External_Table_Positive(t *testing.T) { + depDataMap := map[string]interface{} { + "VLAN" : map[string]interface{} { + "Vlan201": map[string] interface{} { + "vlanid": "201", + }, + "Vlan202": map[string] interface{} { + "vlanid": "202", + }, + "Vlan203": map[string] interface{} { + "vlanid": "203", + }, + }, + "VXLAN_TUNNEL" : map[string]interface{} { + "tun1": map[string] interface{} { + "src_ip": "10.10.1.2", + }, + }, + "VXLAN_TUNNEL_MAP" : map[string]interface{} { + "tun1|vmap1": map[string] interface{} { + "vlan": "Vlan201", + "vni": "301", + }, + "tun1|vmap2": map[string] interface{} { + "vlan": "Vlan202", + "vni": "302", + }, + "tun1|vmap3": map[string] interface{} { + "vlan": "Vlan203", + "vni": "303", + }, + }, + } + + //Try to create + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "VRF|vrf101", + map[string]string{ + "vni": "302", + }, + }, + } + + loadConfigDB(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) +} + +func TestValidateEditConfig_MustExp_Update_Leaf_List_Positive(t *testing.T) { + depDataMap := map[string]interface{} { + "VLAN" : map[string]interface{} { + "Vlan202": map[string] interface{} { + "vlanid": "202", + }, + }, + } + + loadConfigDB(rclient, depDataMap) + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_UPDATE, + "VLAN|Vlan202", + map[string]string{ + "members@": "Ethernet4,Ethernet8", + }, + }, + } + + 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) +} + +func TestValidateEditConfig_MustExp_Add_NULL(t *testing.T) { + depDataMap := map[string]interface{} { + "INTERFACE": map[string]interface{} { + "Ethernet20": map[string] interface{} { + "unnumbered": "Loopback1", + }, + }, + "LOOPBACK_INTERFACE": map[string]interface{} { + "Loopback1": map[string] interface{} { + "NULL": "NULL", + }, + "Loopback1|1.2.3.4/32": map[string] interface{} { + "NULL": "NULL", + }, + }, + } + + loadConfigDB(rclient, depDataMap) + defer unloadConfigDB(rclient, depDataMap) + + delUnnumber := cvl.CVLEditConfigData{ + VType: cvl.VALIDATE_ALL, + VOp: cvl.OP_DELETE, + Key: "INTERFACE|Ethernet20", + Data: map[string]string{ "unnumbered": "" }, + } + + addNull := cvl.CVLEditConfigData{ + VType: cvl.VALIDATE_ALL, + VOp: cvl.OP_UPDATE, + Key: "INTERFACE|Ethernet20", + Data: map[string]string{ "NULL": "NULL" }, + } + + t.Run("before", testNullAdd(addNull, delUnnumber)) + t.Run("after", testNullAdd(delUnnumber, addNull)) +} + +func testNullAdd(data ...cvl.CVLEditConfigData) func(*testing.T) { + return func(t *testing.T) { + session, _ := cvl.ValidationSessOpen() + defer cvl.ValidationSessClose(session) + + 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) + } + + cfgData[i].VType = cvl.VALIDATE_NONE // dont validate for next op + } + } +} diff --git a/cvl/cvl_test.go b/cvl/cvl_test.go index c352fd098d82..fa37c41c7a93 100644 --- a/cvl/cvl_test.go +++ b/cvl/cvl_test.go @@ -20,18 +20,21 @@ 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" + //"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" ) type testEditCfgData struct { @@ -45,6 +48,19 @@ var rclient *redis.Client var port_map map[string]interface{} var filehandle *os.File +var loadDeviceDataMap bool +var deviceDataMap = map[string]interface{} { + "DEVICE_METADATA" : map[string]interface{} { + "localhost": map[string] interface{} { + "hwsku": "Quanta-IX8-54x", + "hostname": "sonic", + "platform": "x86_64-quanta_ix8_54x-r0", + "mac": "4c:76:25:f4:70:82", + "deployment_id": "1", + }, + }, +} + /* Dependent port channel configuration. */ var depDataMap = map[string]interface{} { "PORTCHANNEL" : map[string]interface{} { @@ -215,12 +231,80 @@ func prepareDb() { fmt.Printf("read file %v err: %v", fileName, err) } + //Load device data map on which application of deviation files depends + dm, err:= rclient.Keys("DEVICE_METADATA|localhost").Result() + if (err != nil) || (len(dm) == 0) { + loadConfigDB(rclient, deviceDataMap) + loadDeviceDataMap = true + } + port_map = loadConfig("", PortsMapByte) + portKeys, err:= rclient.Keys("PORT|*").Result() + //Load only the port config which are not there in Redis + if err == nil { + portMapKeys := port_map["PORT"].(map[string]interface{}) + for _, portKey := range portKeys { + //Delete the port key which is already there in Redis + delete(portMapKeys, portKey[len("PORTS|") - 1:]) + } + port_map["PORT"] = portMapKeys + } + loadConfigDB(rclient, port_map) loadConfigDB(rclient, depDataMap) } +//Clear all db entries which are used in the test cases. +//The list of such db should be updated here if new +//table is referred in any test case. +//The test case running may fail if tables are not cleared +//prior to starting execution of test cases. +//"DEVICE_METADATA" should not be cleaned as it is used +//during cvl package init() phase. +func clearDb() { + + tblList := []string { + "ACL_RULE", + "ACL_TABLE", + "BGP_GLOBALS", + "BUFFER_PG", + "CABLE_LENGTH", + "CFG_L2MC_TABLE", + "INTERFACE", + "MIRROR_SESSION", + "PORTCHANNEL", + "PORTCHANNEL_MEMBER", + "PORT_QOS_MAP", + "QUEUE", + "SCHEDULER", + "STP", + "STP_PORT", + "STP_VLAN", + "TAM_COLLECTOR_TABLE", + "TAM_INT_IFA_FLOW_TABLE", + "VLAN", + "VLAN_INTERFACE", + "VLAN_MEMBER", + "VRF", + "VXLAN_TUNNEL", + "VXLAN_TUNNEL_MAP", + "WRED_PROFILE", + "DSCP_TO_TC_MAP", + } + + for _, tbl := range tblList { + _, err := exec.Command("/bin/sh", "-c", + "sonic-db-cli CONFIG_DB del `sonic-db-cli CONFIG_DB keys '" + + tbl + "|*' | cut -d ' ' -f 2`").Output() + + if err != nil { + fmt.Println(err.Error()) + } + } +} + + func WriteToFile(message string) { pc := make([]uintptr, 10) runtime.Callers(2, pc) @@ -244,7 +328,7 @@ func WriteToFile(message string) { func TestMain(m *testing.M) { redisAlreadyRunning := false - pidOfRedis, err := exec.Command("/bin/pidof", "redis-server").Output() + pidOfRedis, err := exec.Command("pidof", "redis-server").Output() if err == nil && string(pidOfRedis) != "\n" { redisAlreadyRunning = true } @@ -267,6 +351,9 @@ func TestMain(m *testing.M) { } + //Clear all tables which are used for testing + clearDb() + /* Prepare the Redis database. */ prepareDb() SetTrace(true) @@ -276,6 +363,13 @@ func TestMain(m *testing.M) { unloadConfigDB(rclient, port_map) unloadConfigDB(rclient, depDataMap) + if (loadDeviceDataMap == true) { + unloadConfigDB(rclient, deviceDataMap) + } + + //Clear all tables which were used for testing + clearDb() + cvl.Finish() rclient.Close() rclient.FlushDB() @@ -494,142 +588,6 @@ func TestValidateConfig_CfgFile(t *testing.T) { cvl.ValidationSessClose(cvSess) } -func TestValidateEditConfig_Delete_Must_Check_Positive(t *testing.T) { - depDataMap := map[string]interface{} { - "PORT" : map[string]interface{} { - "Ethernet3" : map[string]interface{} { - "alias":"hundredGigE1", - "lanes": "81,82,83,84", - "mtu": "9100", - "index": "3", - }, - "Ethernet5" : map[string]interface{} { - "alias":"hundredGigE1", - "lanes": "85,86,87,89", - "mtu": "9100", - "index": "5", - }, - }, - "ACL_TABLE" : map[string]interface{} { - "TestACL1": map[string] interface{} { - "stage": "INGRESS", - "type": "L3", - "ports@": "Ethernet3,Ethernet5", - }, - "TestACL2": map[string] interface{} { - "stage": "INGRESS", - "type": "L3", - }, - }, - "ACL_RULE" : map[string]interface{} { - "TestACL1|Rule1": map[string] interface{} { - "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", - }, - "TestACL2|Rule2": map[string] interface{} { - "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", - }, - }, - } - - //Prepare data in Redis - loadConfigDB(rclient, depDataMap) - - cfgDataAclRule := []cvl.CVLEditConfigData { - cvl.CVLEditConfigData { - cvl.VALIDATE_ALL, - cvl.OP_DELETE, - "ACL_RULE|TestACL2|Rule2", - map[string]string { - }, - }, - } - - 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) -} - -/* -func TestValidateEditConfig_Delete_Must_Check_Negative(t *testing.T) { - depDataMap := map[string]interface{} { - "PORT" : map[string]interface{} { - "Ethernet3" : map[string]interface{} { - "alias":"hundredGigE1", - "lanes": "81,82,83,84", - "mtu": "9100", - }, - "Ethernet5" : map[string]interface{} { - "alias":"hundredGigE1", - "lanes": "85,86,87,89", - "mtu": "9100", - }, - }, - "ACL_TABLE" : map[string]interface{} { - "TestACL1": map[string] interface{} { - "stage": "INGRESS", - "type": "L3", - "ports@": "Ethernet3,Ethernet5", - }, - }, - "ACL_RULE" : map[string]interface{} { - "TestACL1|Rule1": map[string] interface{} { - "PACKET_ACTION": "FORWARD", - "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", - }, - }, - } - - //Prepare data in Redis - loadConfigDB(rclient, depDataMap) - - cfgDataAclRule := []cvl.CVLEditConfigData { - cvl.CVLEditConfigData { - cvl.VALIDATE_ALL, - cvl.OP_DELETE, - "ACL_RULE|TestACL1|Rule1", - map[string]string { - }, - }, - } - - 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) -} -*/ - //Validate invalid json data func TestValidateConfig_Negative(t *testing.T) { cvSess, _ := cvl.ValidationSessOpen() @@ -668,6 +626,7 @@ func TestValidateEditConfig_Delete_Semantic_ACLTableReference_Positive(t *testin "ACL_RULE": map[string]interface{} { "TestACL1005|Rule1": map[string] interface{} { "PACKET_ACTION": "FORWARD", + "IP_TYPE": "IPV4", "SRC_IP": "10.1.1.1/32", "L4_SRC_PORT": "1909", "IP_PROTOCOL": "103", @@ -724,7 +683,7 @@ func TestValidateEditConfig_Create_Syntax_Valid_FieldValue(t *testing.T) { "ACL_RULE|TestACL1|Rule1", map[string]string{ "PACKET_ACTION": "FORWARD", - "IP_TYPE": "IPV4", + "IP_TYPE": "IPV4", "SRC_IP": "10.1.1.1/32", "L4_SRC_PORT": "1909", "IP_PROTOCOL": "103", @@ -819,7 +778,7 @@ func TestValidateEditConfig_Create_Syntax_Invalid_PacketAction_Negative(t *testi "ACL_RULE|TestACL1|Rule1", map[string]string{ "PACKET_ACTION": "FORWARD777", - "IP_TYPE": "IPV4", + "IP_TYPE": "IPV4", "SRC_IP": "10.1.1.1/32", "L4_SRC_PORT": "1909", "IP_PROTOCOL": "103", @@ -864,7 +823,7 @@ func TestValidateEditConfig_Create_Syntax_Invalid_SrcPrefix_Negative(t *testing. "ACL_RULE|TestACL1|Rule1", map[string]string{ "PACKET_ACTION": "FORWARD", - "IP_TYPE": "IPV4", + "IP_TYPE": "IPV4", "SRC_IP": "10.1.1.1/3288888", "L4_SRC_PORT": "1909", "IP_PROTOCOL": "103", @@ -909,7 +868,7 @@ func TestValidateEditConfig_Create_Syntax_InvalidIPAddress_Negative(t *testing.T "ACL_RULE|TestACL1|Rule1", map[string]string{ "PACKET_ACTION": "FORWARD", - "IP_TYPE": "IPV4", + "IP_TYPE": "IPV4", "SRC_IP": "10.1a.1.1/32", "L4_SRC_PORT": "1909", "IP_PROTOCOL": "103", @@ -954,7 +913,7 @@ func TestValidateEditConfig_Create_Syntax_OutofBound_Negative(t *testing.T) { "ACL_RULE|TestACL1|Rule1", map[string]string{ "PACKET_ACTION": "FORWARD", - "IP_TYPE": "IPV4", + "IP_TYPE": "IPV4", "SRC_IP": "10.1.1.1/32", "L4_SRC_PORT": "19099090909090", "IP_PROTOCOL": "103", @@ -1000,7 +959,7 @@ func TestValidateEditConfig_Create_Syntax_InvalidProtocol_Negative(t *testing.T) "ACL_RULE|TestACL1|Rule1", map[string]string{ "PACKET_ACTION": "FORWARD", - "IP_TYPE": "IPV4", + "IP_TYPE": "IPV4", "SRC_IP": "10.1.1.1/32", "L4_SRC_PORT": "1909", "IP_PROTOCOL": "10388888", @@ -1047,7 +1006,7 @@ func TestValidateEditConfig_Create_Syntax_InvalidRange_Negative(t *testing.T) { "ACL_RULE|TestACL1|Rule1", map[string]string{ "PACKET_ACTION": "FORWARD", - "IP_TYPE": "IPV4", + "IP_TYPE": "IPV4", "SRC_IP": "10.1.1.1/32", "L4_SRC_PORT": "1909", "IP_PROTOCOL": "103", @@ -1083,7 +1042,7 @@ func TestValidateEditConfig_Create_Syntax_InvalidCharNEw_Negative(t *testing.T) "ACL_RULE|TestACL1jjjj|Rule1", map[string]string{ "PACKET_ACTION": "FORWARD", - "IP_TYPE": "IPV4", + "IP_TYPE": "IPV4", "SRC_IP": "10.1.1.1/32", "L4_SRC_PORT": "1909", "IP_PROTOCOL": "103", @@ -1124,10 +1083,10 @@ func TestValidateEditConfig_Create_Syntax_SpecialChar_Positive(t *testing.T) { cvl.CVLEditConfigData{ cvl.VALIDATE_ALL, cvl.OP_CREATE, - "ACL_RULE|TestACL1|Rule_1-2", + "ACL_RULE|TestACL1|Rule@##", map[string]string{ "PACKET_ACTION": "FORWARD", - "IP_TYPE": "IPV4", + "IP_TYPE": "IPV4", "SRC_IP": "10.1.1.1/32", "L4_SRC_PORT": "1909", "IP_PROTOCOL": "103", @@ -1160,6 +1119,7 @@ func TestValidateEditConfig_Create_Syntax_InvalidKeyName_Negative(t *testing.T) "AC&&***L_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", @@ -1202,7 +1162,7 @@ func TestValidateEditConfig_Create_Semantic_AdditionalInvalidNode_Negative(t *te "ACL_RULE|TestACL1|Rule1", map[string]string{ "PACKET_ACTION": "FORWARD", - "IP_TYPE": "IPV4", + "IP_TYPE": "IPV4", "SRC_IP": "10.1.1.1/32", "L4_SRC_PORT": "1909", "IP_PROTOCOL": "103", @@ -1228,14 +1188,16 @@ func TestValidateEditConfig_Create_Semantic_AdditionalInvalidNode_Negative(t *te unloadConfigDB(rclient, depDataMap) } +/* func TestValidateEditConfig_Create_Semantic_MissingMandatoryNode_Negative(t *testing.T) { cfgData := []cvl.CVLEditConfigData{ cvl.CVLEditConfigData{ cvl.VALIDATE_ALL, cvl.OP_CREATE, - "VLAN|Vlan101", + "VXLAN_TUNNEL|Tunnel1", map[string]string{ + "NULL": "NULL", }, }, } @@ -1252,6 +1214,7 @@ func TestValidateEditConfig_Create_Semantic_MissingMandatoryNode_Negative(t *tes t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) } } +*/ func TestValidateEditConfig_Create_Syntax_Invalid_Negative(t *testing.T) { @@ -1262,7 +1225,7 @@ func TestValidateEditConfig_Create_Syntax_Invalid_Negative(t *testing.T) { "ACL_RULERule1", map[string]string{ "PACKET_ACTION": "FORWARD", - "IP_TYPE": "IPV4", + "IP_TYPE": "IPV4", "SRC_IP": "10.1.1.1/32", "L4_SRC_PORT": "1909", "IP_PROTOCOL": "103", @@ -1295,7 +1258,7 @@ func TestValidateEditConfig_Create_Syntax_IncompleteKey_Negative(t *testing.T) { "ACL_RULE|Rule1", map[string]string{ "PACKET_ACTION": "FORWARD", - "IP_TYPE": "IPV4", + "IP_TYPE": "IPV4", "SRC_IP": "10.1.1.1/32", "L4_SRC_PORT": "1909", "IP_PROTOCOL": "103", @@ -1328,6 +1291,7 @@ func TestValidateEditConfig_Create_Syntax_InvalidKey_Negative(t *testing.T) { "|Rule1", map[string]string{ "PACKET_ACTION": "FORWARD", + "IP_TYPE": "IPV4", "SRC_IP": "10.1.1.1/32", "L4_SRC_PORT": "1909", "IP_PROTOCOL": "103", @@ -1474,6 +1438,7 @@ func TestValidateEditConfig_Delete_Syntax_InvalidKey_Negative(t *testing.T) { "|Rule1", map[string]string{ "PACKET_ACTION": "FORWARD", + "IP_TYPE": "IPV4", "SRC_IP": "10.1.1.1/32", "L4_SRC_PORT": "1909", "IP_PROTOCOL": "103", @@ -1506,6 +1471,7 @@ func TestValidateEditConfig_Update_Syntax_InvalidKey_Negative(t *testing.T) { "|Rule1", map[string]string{ "PACKET_ACTION": "FORWARD", + "IP_TYPE": "IPV4", "SRC_IP": "10.1.1.1/32", "L4_SRC_PORT": "1909", "IP_PROTOCOL": "103", @@ -1760,7 +1726,7 @@ func TestValidateEditConfig_Update_Semantic_Positive(t *testing.T) { } /* API to test edit config with valid syntax. */ -func TestValidateConfig_Update_Semantic_Vlan_Negative(t *testing.T) { +func TestValidateConfig_Semantic_Vlan_Negative(t *testing.T) { cvSess, _ := cvl.ValidationSessOpen() @@ -1808,14 +1774,12 @@ func TestValidateEditConfig_Update_Syntax_DependentData_Redis_Positive(t *testin loadConfigDB(rclient, mpi_acl_table_rule) depDataMap := map[string]interface{}{ - /* Use MIRROR session once supported --- "MIRROR_SESSION": map[string]interface{}{ "everflow2": map[string]interface{}{ "src_ip": "10.1.0.32", "dst_ip": "2.2.2.2", }, }, - */ } loadConfigDB(rclient, depDataMap) @@ -1827,10 +1791,7 @@ func TestValidateEditConfig_Update_Syntax_DependentData_Redis_Positive(t *testin cvl.OP_UPDATE, "ACL_RULE|TestACL13|Rule1", map[string]string{ - /* Use Mirror session when supported "MIRROR_ACTION": "everflow2", - */ - "PACKET_ACTION" : "FORWARD", }, }, } @@ -1986,7 +1947,7 @@ func TestValidateEditConfig_Delete_Semantic_ACLTableReference_Negative(t *testin cvl.CVLEditConfigData{ cvl.VALIDATE_ALL, cvl.OP_DELETE, - "ACL_RULE|MyACL11_ACL_IPV4|RULE_1", + "ACL_RULE|MyACLTest_ACL_IPV4|Test_1", map[string]string{}, }, } @@ -2139,13 +2100,14 @@ func TestValidateEditConfig_Create_DepData_From_Redis_Negative11(t *testing.T) { cvl.ValidationSessClose(cvSess) - if (err != cvl.CVL_SEMANTIC_DEPENDENT_DATA_MISSING) { + if err == cvl.CVL_SUCCESS { t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) } unloadConfigDB(rclient, depDataMap) } + func TestValidateEditConfig_Create_DepData_From_Redis(t *testing.T) { depDataMap := map[string]interface{}{ @@ -2276,35 +2238,6 @@ func TestValidateEditConfig_Create_Syntax_ErrAppTag_In_Pattern_Negative(t *testi } -func TestValidateEditConfig_Create_ErrAppTag_In_Must_Negative(t *testing.T) { - - cfgData := []cvl.CVLEditConfigData{ - cvl.CVLEditConfigData{ - cvl.VALIDATE_ALL, - cvl.OP_CREATE, - "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)) - - /* Compare expected error details and error tag. */ - if compareErrorDetails(cvlErrInfo, cvl.CVL_SEMANTIC_ERROR ,"vlan-invalid", "") != true { - t.Errorf("Config Validation failed -- error details %v %v", cvlErrInfo, retCode) - } - -} - /* API to test edit config with valid syntax. */ func TestValidateEditConfig_Create_Syntax_InValid_FieldValue(t *testing.T) { @@ -2353,6 +2286,7 @@ func TestValidateEditConfig_Create_Syntax_InValid_FieldValue(t *testing.T) { } } +/* //EditConfig(Create) with dependent data from redis func TestValidateEditConfig_Create_DepData_From_Redis_Negative(t *testing.T) { @@ -2398,35 +2332,26 @@ func TestValidateEditConfig_Create_DepData_From_Redis_Negative(t *testing.T) { unloadConfigDB(rclient, depDataMap) } +*/ -// EditConfig(Create) with chained leafref from redis -func TestValidateEditConfig_Create_Chained_Leafref_DepData_Positive(t *testing.T) { +//EditConfig(Delete) deleting entry already used by other table as leafref +func TestValidateEditConfig_Delete_Dep_Leafref_Negative(t *testing.T) { depDataMap := map[string]interface{} { - "VLAN" : map[string]interface{} { - "Vlan100": map[string]interface{} { - "members@": "Ethernet1", - "vlanid": "100", - }, - }, - "PORT" : map[string]interface{} { - "Ethernet1" : map[string]interface{} { - "alias":"hundredGigE1", - "lanes": "81,82,83,84", - "mtu": "9100", - "index": "1", - }, - "Ethernet2" : map[string]interface{} { - "alias":"hundredGigE1", - "lanes": "85,86,87,89", - "mtu": "9100", - "index": "2", - }, - }, "ACL_TABLE" : map[string]interface{} { "TestACL1": map[string] interface{} { "stage": "INGRESS", "type": "L3", - "ports@":"Ethernet2", + }, + }, + "ACL_RULE": map[string]interface{} { + "TestACL1|Rule1": map[string] interface{} { + "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", }, }, } @@ -2434,93 +2359,19 @@ func TestValidateEditConfig_Create_Chained_Leafref_DepData_Positive(t *testing.T //Prepare data in Redis loadConfigDB(rclient, depDataMap) - cvSess, _ := cvl.ValidationSessOpen() - cfgDataVlan := []cvl.CVLEditConfigData { cvl.CVLEditConfigData { cvl.VALIDATE_ALL, - cvl.OP_CREATE, - "VLAN_MEMBER|Vlan100|Ethernet1", - map[string]string { - "tagging_mode" : "tagged", - }, - }, - } - - _, err := cvSess.ValidateEditConfig(cfgDataVlan) - - if err != cvl.CVL_SUCCESS { //should succeed - t.Errorf("Config Validation failed.") - return - } - - cfgDataAclRule := []cvl.CVLEditConfigData { - cvl.CVLEditConfigData { - cvl.VALIDATE_ALL, - cvl.OP_CREATE, - "ACL_RULE|TestACL1|Rule1", + cvl.OP_DELETE, + "ACL_TABLE|TestACL1", 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() - _, err = cvSess.ValidateEditConfig(cfgDataAclRule) - - cvl.ValidationSessClose(cvSess) - - if err != cvl.CVL_SUCCESS { //should succeed - t.Errorf("Config Validation failed.") - } - - 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{} { - "ACL_TABLE" : map[string]interface{} { - "TestACL1": map[string] interface{} { - "stage": "INGRESS", - "type": "L3", - }, - }, - "ACL_RULE": map[string]interface{} { - "TestACL1|Rule1": map[string] interface{} { - "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", - }, - }, - } - - //Prepare data in Redis - loadConfigDB(rclient, depDataMap) - - cfgDataVlan := []cvl.CVLEditConfigData { - cvl.CVLEditConfigData { - cvl.VALIDATE_ALL, - cvl.OP_DELETE, - "ACL_TABLE|TestACL1", - map[string]string { - }, - }, - } - - cvSess, _ := cvl.ValidationSessOpen() - - cvlErrInfo, err := cvSess.ValidateEditConfig(cfgDataVlan) + cvlErrInfo, err := cvSess.ValidateEditConfig(cfgDataVlan) cvl.ValidationSessClose(cvSess) @@ -2533,67 +2384,6 @@ func TestValidateEditConfig_Delete_Dep_Leafref_Negative(t *testing.T) { unloadConfigDB(rclient, depDataMap) } -//EditConfig(Create) with chained leafref from redis -func TestValidateEditConfig_Create_Chained_Leafref_DepData_Negative(t *testing.T) { - depDataMap := map[string]interface{} { - "PORT" : map[string]interface{} { - "Ethernet3" : map[string]interface{} { - "alias":"hundredGigE1", - "lanes": "81,82,83,84", - "mtu": "9100", - "index": "3", - }, - "Ethernet5" : map[string]interface{} { - "alias":"hundredGigE1", - "lanes": "85,86,87,89", - "mtu": "9100", - "index": "5", - }, - }, - } - - //Prepare data in Redis - loadConfigDB(rclient, depDataMap) - - cfgDataAclRule := []cvl.CVLEditConfigData { - cvl.CVLEditConfigData { - cvl.VALIDATE_ALL, - cvl.OP_CREATE, - "ACL_TABLE|TestACL1", - map[string]string { - "stage": "INGRESS", - "type": "L3", - "ports@":"Ethernet2", - }, - }, - cvl.CVLEditConfigData { - cvl.VALIDATE_ALL, - 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", - }, - }, - } - - cvSess, _ := cvl.ValidationSessOpen() - - _, err := cvSess.ValidateEditConfig(cfgDataAclRule) - - cvl.ValidationSessClose(cvSess) - - if err == cvl.CVL_SUCCESS { //should not succeed - t.Errorf("Config Validation failed.") - } - - unloadConfigDB(rclient, depDataMap) -} func TestValidateEditConfig_Create_Syntax_InvalidVlanRange_Negative(t *testing.T) { cfgData := []cvl.CVLEditConfigData{ @@ -2652,13 +2442,11 @@ func TestValidateEditConfig_DepData_Through_Cache(t *testing.T) { "alias":"hundredGigE1", "lanes": "81,82,83,84", "mtu": "9100", - "index": "3", }, "Ethernet5" : map[string]interface{} { "alias":"hundredGigE1", "lanes": "85,86,87,89", "mtu": "9100", - "index": "5", }, }, } @@ -2667,7 +2455,7 @@ func TestValidateEditConfig_DepData_Through_Cache(t *testing.T) { loadConfigDB(rclient, depDataMap) //Modify entry - depDataMap = map[string]interface{} { + modDepDataMap := map[string]interface{} { "PORT" : map[string]interface{} { "Ethernet3" : map[string]interface{} { "mtu": "9200", @@ -2675,7 +2463,7 @@ func TestValidateEditConfig_DepData_Through_Cache(t *testing.T) { }, } - loadConfigDB(rclient, depDataMap) + loadConfigDB(rclient, modDepDataMap) cfgDataAclRule := []cvl.CVLEditConfigData { cvl.CVLEditConfigData { @@ -2701,6 +2489,7 @@ func TestValidateEditConfig_DepData_Through_Cache(t *testing.T) { } unloadConfigDB(rclient, depDataMap) + unloadConfigDB(rclient, modDepDataMap) } /* Delete field for an existing key.*/ @@ -2743,6 +2532,28 @@ func TestValidateEditConfig_Delete_Single_Field_Positive(t *testing.T) { unloadConfigDB(rclient, depDataMap) } +func TestValidateEditConfig_Create_Dscp_To_Tc_Map(t *testing.T) { + cfgData := []cvl.CVLEditConfigData { + cvl.CVLEditConfigData { + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "DSCP_TO_TC_MAP|AZURE", + map[string]string { + "1": "7", + "2": "8", + "3": "9", + }, + }, + } + + 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) + } +} + func TestValidateConfig_Repeated_Keys_Positive(t *testing.T) { jsonData := `{ "WRED_PROFILE": { @@ -2858,6 +2669,41 @@ func TestValidateEditConfig_Delete_Entry_Then_Dep_Leafref_Positive(t *testing.T) unloadConfigDB(rclient, depDataMap) } +/* +func TestBadSchema(t *testing.T) { + env := os.Environ() + env[0] = env[0] + " " + + if _, err := os.Stat("/usr/sbin/schema"); os.IsNotExist(err) { + //Corrupt some schema file + exec.Command("/bin/sh", "-c", "/bin/cp testdata/schema/sonic-port.yin testdata/schema/sonic-port.yin.bad" + + " && /bin/sed -i '1 a ' testdata/schema/sonic-port.yin.bad").Output() + + //Parse bad schema file + if module, _ := yparser.ParseSchemaFile("testdata/schema/sonic-port.yin.bad"); module != nil { //should fail + t.Errorf("Bad schema parsing should fail.") + } + + //Revert to + exec.Command("/bin/sh", "-c", "/bin/rm testdata/schema/sonic-port.yin.bad").Output() + } else { + //Corrupt some schema file + exec.Command("/bin/sh", "-c", "/bin/cp /usr/sbin/schema/sonic-port.yin /usr/sbin/schema/sonic-port.yin.bad" + + " && /bin/sed -i '1 a ' /usr/sbin/schema/sonic-port.yin.bad").Output() + + //Parse bad schema file + if module, _ := yparser.ParseSchemaFile("/usr/sbin/schema/sonic-port.yin.bad"); module != nil { //should fail + t.Errorf("Bad schema parsing should fail.") + } + + //Revert to + exec.Command("/bin/sh", "-c", "/bin/rm /usr/sbin/schema/sonic-port.yin.bad").Output() + } + +} +*/ + +/* func TestServicability_Debug_Trace(t *testing.T) { cvl.Debug(false) @@ -2918,7 +2764,8 @@ func TestServicability_Debug_Trace(t *testing.T) { p.Signal(syscall.SIGUSR2) } exec.Command("/bin/sh", "-c", "/bin/mv conf/cvl_cfg.json.orig conf/cvl_cfg.json").Output() -} + p.Signal(syscall.SIGUSR2) +}*/ // EditConfig(Create) with chained leafref from redis func TestValidateEditConfig_Delete_Create_Same_Entry_Positive(t *testing.T) { @@ -2934,7 +2781,6 @@ func TestValidateEditConfig_Delete_Create_Same_Entry_Positive(t *testing.T) { "alias":"hundredGigE1", "lanes": "81,82,83,84", "mtu": "9100", - "index": "1", }, }, } @@ -3011,13 +2857,11 @@ func TestValidateIncrementalConfig_Positive(t *testing.T) { "alias":"hundredGigE1", "lanes": "81,82,83,84", "mtu": "9100", - "index": "1", }, "Ethernet2" : map[string]interface{} { "alias":"hundredGigE1", "lanes": "85,86,87,89", "mtu": "9100", - "index": "2", }, }, } @@ -3191,7 +3035,6 @@ func TestValidateEditConfig_Create_Syntax_DependentData_PositivePortChannelIfNam } func TestValidateEditConfig_Create_Syntax_DependentData_NegativePortChannelEthernet(t *testing.T) { - cfgData := []cvl.CVLEditConfigData{ cvl.CVLEditConfigData{ cvl.VALIDATE_ALL, @@ -3217,11 +3060,39 @@ func TestValidateEditConfig_Create_Syntax_DependentData_NegativePortChannelEther } } +func TestValidateEditConfig_Create_Syntax_DependentData_NegativePortChannelNew(t *testing.T) { + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "VLAN|Vlan1001", + map[string]string{ + "vlanid": "1001", + "members@": "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) + } +} + func TestValidateEditConfig_Use_Updated_Data_As_Create_DependentData_Positive(t *testing.T) { depDataMap := map[string]interface{} { "VLAN" : map[string]interface{} { "Vlan201": map[string] interface{} { "vlanid": "201", + "mtu": "1700", "members@": "Ethernet8", }, }, @@ -3239,6 +3110,7 @@ func TestValidateEditConfig_Use_Updated_Data_As_Create_DependentData_Positive(t cvl.OP_UPDATE, "VLAN|Vlan201", map[string]string{ + "mtu": "1900", "members@": "Ethernet8,Ethernet12", }, }, @@ -3280,6 +3152,7 @@ func TestValidateEditConfig_Use_Updated_Data_As_Create_DependentData_Single_Call "VLAN" : map[string]interface{} { "Vlan201": map[string] interface{} { "vlanid": "201", + "mtu": "1700", "members@": "Ethernet8", }, }, @@ -3297,6 +3170,7 @@ func TestValidateEditConfig_Use_Updated_Data_As_Create_DependentData_Single_Call cvl.OP_UPDATE, "VLAN|Vlan201", map[string]string{ + "mtu": "1900", "members@": "Ethernet8,Ethernet12", }, }, @@ -3356,7 +3230,6 @@ func TestValidateEditConfig_Create_Syntax_Interface_OptionalKey_Positive(t *test cvl.OP_CREATE, "INTERFACE|Ethernet24", map[string]string{ - /*"vrf-name": "Vrf1", -- Enable once VRF YANG implemented */ }, }, } @@ -3426,3 +3299,609 @@ func TestValidateEditConfig_EmptyNode_Positive(t *testing.T) { } } + +func TestSortDepTables(t *testing.T) { + cvSess, _ := cvl.ValidationSessOpen() + + result, _ := cvSess.SortDepTables([]string{"PORT", "ACL_RULE", "ACL_TABLE"}) + + expectedResult := []string{"ACL_RULE", "ACL_TABLE", "PORT"} + + if len(expectedResult) != len(result) { + t.Errorf("Validation failed, returned value = %v", result) + return + } + + for i := 0; i < len(expectedResult) ; i++ { + if result[i] != expectedResult[i] { + t.Errorf("Validation failed, returned value = %v", result) + break + } + } + + cvl.ValidationSessClose(cvSess) +} + +func TestGetOrderedTables(t *testing.T) { + cvSess, _ := cvl.ValidationSessOpen() + + result, _ := cvSess.GetOrderedTables("sonic-vlan") + + expectedResult := []string{"VLAN_MEMBER", "VLAN"} + + if len(expectedResult) != len(result) { + t.Errorf("Validation failed, returned value = %v", result) + return + } + + for i := 0; i < len(expectedResult) ; i++ { + if result[i] != expectedResult[i] { + t.Errorf("Validation failed, returned value = %v", result) + break + } + } + + cvl.ValidationSessClose(cvSess) +} + +func TestGetOrderedDepTables(t *testing.T) { + cvSess, _ := cvl.ValidationSessOpen() + + result, _ := cvSess.GetOrderedDepTables("sonic-vlan", "VLAN") + + expectedResult := []string{"VLAN_MEMBER", "VLAN"} + + if len(expectedResult) != len(result) { + t.Errorf("Validation failed, returned value = %v", result) + return + } + + for i := 0; i < len(expectedResult) ; i++ { + if result[i] != expectedResult[i] { + t.Errorf("Validation failed, returned value = %v", result) + break + } + } + + cvl.ValidationSessClose(cvSess) +} + +func TestGetDepTables(t *testing.T) { + cvSess, _ := cvl.ValidationSessOpen() + + result, _ := cvSess.GetDepTables("sonic-acl", "ACL_RULE") + + expectedResult := []string{"ACL_RULE", "ACL_TABLE", "MIRROR_SESSION", "PORT", "PORTCHANNEL"} + + sort.Strings(result) + sort.Strings(expectedResult) + if !reflect.DeepEqual(result, expectedResult) { + t.Errorf("Validation failed, returned value = %v", result) + } + + cvl.ValidationSessClose(cvSess) +} + + +func TestGetDepDataForDelete(t *testing.T) { + depDataMap := map[string]interface{} { + "VLAN_MEMBER" : map[string]interface{} { + "Vlan21|Ethernet7": map[string] interface{} { + "tagging_mode": "tagged", + }, + "Vlan22|Ethernet7": map[string] interface{} { + "tagging_mode": "tagged", + }, + "Vlan22|Ethernet72": map[string] interface{} { + "tagging_mode": "tagged", + }, + }, + "PORTCHANNEL_MEMBER" : map[string]interface{} { + "Ch47|Ethernet7": map[string] interface{} { + "NULL": "NULL", + }, + "Ch47|Ethernet75": map[string] interface{} { + "NULL": "NULL", + }, + }, + "ACL_TABLE" : map[string]interface{} { + "TestACL1": map[string] interface{} { + "stage": "INGRESS", + "type": "L3", + "ports@": "Ethernet3,Ethernet76,Ethernet7", + }, + }, + "CFG_L2MC_STATIC_MEMBER_TABLE" : map[string]interface{} { + "Vlan24|10.1.1.1|Ethernet7": map[string] interface{} { + "NULL": "NULL", + }, + "Vlan25|10.1.1.2|Ethernet78": map[string] interface{} { + "NULL": "NULL", + }, + }, + "CFG_L2MC_MROUTER_TABLE" : map[string]interface{} { + "Vlan21|Ethernet7": map[string] interface{} { + "NULL": "NULL", + }, + }, + "MIRROR_SESSION": map[string]interface{}{ + "sess1": map[string]interface{}{ + "src_ip": "10.1.0.32", + "dst_ip": "2.2.2.2", + }, + }, + "ACL_RULE" : map[string]interface{} { + "TestACL1|Rule1": map[string] interface{} { + "PACKET_ACTION": "FORWARD", + "MIRROR_ACTION": "sess1", + }, + }, + "INTERFACE" : map[string]interface{} { + "Ethernet7": map[string] interface{} { + "vrf_name": "Vrf1", + }, + "Ethernet7|10.2.1.1/16": map[string] interface{} { + "NULL": "NULL", + }, + "Ethernet7|10.2.1.2/16": map[string] interface{} { + "NULL": "NULL", + }, + }, + } + + loadConfigDB(rclient, depDataMap) + + cvSess, _ := cvl.ValidationSessOpen() + + depEntries := cvSess.GetDepDataForDelete("PORT|Ethernet7") + + if (len(depEntries) != 9) { //9 entries to be deleted + t.Errorf("GetDepDataForDelete() failed") + } + + depEntries1 := cvSess.GetDepDataForDelete("MIRROR_SESSION|sess1") + + if (len(depEntries1) != 1) { //1 entry to be deleted + t.Errorf("GetDepDataForDelete() failed") + } + cvl.ValidationSessClose(cvSess) + + unloadConfigDB(rclient, depDataMap) +} + +func TestMaxElements_All_Entries_In_Request(t *testing.T) { + cvSess, _ := cvl.ValidationSessOpen() + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "VXLAN_TUNNEL|tun1", + map[string]string{ + "src_ip": "20.1.1.1", + }, + }, + } + + //Check addition of first element + cvlErrInfo, _ := cvSess.ValidateEditConfig(cfgData) + + cfgData1 := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "VXLAN_TUNNEL|tun2", + map[string]string{ + "src_ip": "30.1.1.1", + }, + }, + } + + //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) + } + +} + +func TestMaxElements_Entries_In_Redis(t *testing.T) { + depDataMap := map[string]interface{} { + "VXLAN_TUNNEL" : map[string]interface{} { + "tun1" : map[string]interface{} { + "src_ip": "20.1.1.1", + }, + }, + } + + 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) +} + +func TestValidateEditConfig_Two_Create_Requests_Positive(t *testing.T) { + cvSess, _ := cvl.ValidationSessOpen() + + cfgDataVlan := []cvl.CVLEditConfigData { + cvl.CVLEditConfigData { + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "VLAN|Vlan21", + map[string]string { + "vlanid": "21", + }, + }, + } + + cvlErrInfo, _ := cvSess.ValidateEditConfig(cfgDataVlan) + + if cvlErrInfo.ErrCode != cvl.CVL_SUCCESS { + cvl.ValidationSessClose(cvSess) + t.Errorf("VLAN Create : Config Validation failed") + return + } + + cfgDataVlan = []cvl.CVLEditConfigData { + cvl.CVLEditConfigData { + cvl.VALIDATE_NONE, + cvl.OP_CREATE, + "VLAN|Vlan21", + map[string]string { + "vlanid": "21", + }, + }, + cvl.CVLEditConfigData { + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "STP_VLAN|Vlan21", + map[string]string { + "enabled": "true", + "forward_delay": "15", + "hello_time": "2", + "max_age" : "20", + "priority": "327", + "vlanid": "21", + }, + }, + } + + cvlErrInfo, _ = cvSess.ValidateEditConfig(cfgDataVlan) + + cvl.ValidationSessClose(cvSess) + + if cvlErrInfo.ErrCode != cvl.CVL_SUCCESS { + t.Errorf("STP VLAN Create : Config Validation failed") + return + } +} + +func TestValidateEditConfig_Two_Delete_Requests_Positive(t *testing.T) { + depDataMap := map[string]interface{}{ + "VLAN": map[string]interface{}{ + "Vlan51": map[string]interface{}{ + "vlanid": "51", + }, + }, + "STP_VLAN": map[string]interface{}{ + "Vlan51": map[string]interface{}{ + "enabled": "true", + "forward_delay": "15", + "hello_time": "2", + "max_age" : "20", + "priority": "327", + "vlanid": "51", + }, + }, + } + + loadConfigDB(rclient, depDataMap) + + cvSess, _ := cvl.ValidationSessOpen() + + cfgDataVlan := []cvl.CVLEditConfigData { + cvl.CVLEditConfigData { + cvl.VALIDATE_ALL, + cvl.OP_DELETE, + "STP_VLAN|Vlan51", + map[string]string { + }, + }, + } + + 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 + } + + cfgDataVlan = []cvl.CVLEditConfigData { + cvl.CVLEditConfigData { + cvl.VALIDATE_NONE, + cvl.OP_DELETE, + "STP_VLAN|Vlan51", + map[string]string { + }, + }, + cvl.CVLEditConfigData { + cvl.VALIDATE_ALL, + cvl.OP_DELETE, + "VLAN|Vlan51", + map[string]string { + }, + }, + } + + cvlErrInfo, _ = cvSess.ValidateEditConfig(cfgDataVlan) + if cvlErrInfo.ErrCode != cvl.CVL_SUCCESS { + t.Errorf("VLAN delete : Config Validation failed") + } + + cvl.ValidationSessClose(cvSess) + + unloadConfigDB(rclient, depDataMap) +} + +//Check delete constraing with table having multiple keys +func TestValidateEditConfig_Multi_Delete_MultiKey_Same_Session_Positive(t *testing.T) { + depDataMap := map[string]interface{}{ + "VLAN": map[string]interface{}{ + "Vlan511": map[string]interface{}{ + "vlanid": "511", + }, + }, + "VLAN_MEMBER": map[string]interface{}{ + "Vlan511|Ethernet16": map[string]interface{}{ + "tagging_mode": "untagged", + }, + }, + "STP_VLAN_PORT": map[string]interface{}{ + "Vlan511|Ethernet16": map[string]interface{}{ + "path_cost": "200", + "priority": "128", + }, + }, + "STP_PORT": map[string]interface{}{ + "Ethernet16": map[string]interface{}{ + "bpdu_filter": "global", + "enabled": "true", + "portfast": "true", + }, + }, + } + + loadConfigDB(rclient, depDataMap) + cvSess, _ := cvl.ValidationSessOpen() + + cfgData := []cvl.CVLEditConfigData { + cvl.CVLEditConfigData { + cvl.VALIDATE_ALL, + cvl.OP_DELETE, + "STP_VLAN_PORT|Vlan511|Ethernet16", + map[string]string { + }, + }, + } + + cvlErrInfo, _ := cvSess.ValidateEditConfig(cfgData) + if cvlErrInfo.ErrCode != cvl.CVL_SUCCESS { + t.Errorf("STP_VLAN_PORT Delete: Config Validation failed") + unloadConfigDB(rclient, depDataMap) + return + } + + cfgData = []cvl.CVLEditConfigData { + cvl.CVLEditConfigData { + cvl.VALIDATE_ALL, + cvl.OP_DELETE, + "VLAN_MEMBER|Vlan511|Ethernet16", + map[string]string { + "tagging_mode": "untagged", + }, + }, + } + + cvlErrInfo, _ = cvSess.ValidateEditConfig(cfgData) + if cvlErrInfo.ErrCode != cvl.CVL_SUCCESS { + t.Errorf("VLAN_MEMBER Delete: Config Validation failed") + unloadConfigDB(rclient, depDataMap) + return + } + + cfgData = []cvl.CVLEditConfigData { + cvl.CVLEditConfigData { + cvl.VALIDATE_NONE, + cvl.OP_DELETE, + "STP_VLAN_PORT|Vlan511|Ethernet16", + map[string]string { + }, + }, + cvl.CVLEditConfigData { + cvl.VALIDATE_NONE, + cvl.OP_DELETE, + "VLAN_MEMBER|Vlan511|Ethernet16", + map[string]string { + "tagging_mode": "untagged", + }, + }, + cvl.CVLEditConfigData { + cvl.VALIDATE_ALL, + cvl.OP_DELETE, + "STP_PORT|Ethernet16", + map[string]string { + }, + }, + } + + 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) +} + +/* +func TestValidateEditConfig_Update_Leaf_List_Max_Elements_Negative(t *testing.T) { + depDataMap := map[string]interface{}{ + "VLAN": map[string]interface{} { + "Vlan801": map[string]interface{} { + "vlanid": "801", + }, + }, + "CFG_L2MC_STATIC_GROUP_TABLE": map[string]interface{} { + "Vlan801|16.2.2.1": map[string]interface{} { + "out-intf@": "Ethernet4,Ethernet8,Ethernet16", + }, + }, + } + + loadConfigDB(rclient, depDataMap) + + cvSess, _ := cvl.ValidationSessOpen() + + cfgData := []cvl.CVLEditConfigData { + cvl.CVLEditConfigData { + cvl.VALIDATE_ALL, + cvl.OP_UPDATE, + "CFG_L2MC_STATIC_GROUP_TABLE|Vlan801|16.2.2.1", + map[string]string { + "out-intf@": "Ethernet4,Ethernet8,Ethernet16,Ethernet20", + }, + }, + } + + 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) +} +*/ + +func TestValidationTimeStats(t *testing.T) { + cvl.ClearValidationTimeStats() + + stats := cvl.GetValidationTimeStats() + + if (stats.Hits != 0 || stats.Time != 0 || stats.Peak != 0) { + t.Errorf("TestValidationTimeStats : clearing stats failed") + return + } + + cvSess, _ := cvl.ValidationSessOpen() + + cfgData := []cvl.CVLEditConfigData { + cvl.CVLEditConfigData { + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "VRF|VrfTest", + map[string]string { + "fallback": "true", + }, + }, + } + + cvSess.ValidateEditConfig(cfgData) + + cvl.ValidationSessClose(cvSess) + + stats = cvl.GetValidationTimeStats() + + if (stats.Hits == 0 || stats.Time == 0 || stats.Peak == 0) { + t.Errorf("TestValidationTimeStats : getting stats failed") + return + } + + //Clear stats again and check + cvl.ClearValidationTimeStats() + + stats = cvl.GetValidationTimeStats() + + if (stats.Hits != 0 || stats.Time != 0 || stats.Peak != 0) { + t.Errorf("TestValidationTimeStats : clearing stats failed") + } +} \ No newline at end of file diff --git a/cvl/cvl_when_test.go b/cvl/cvl_when_test.go new file mode 100644 index 000000000000..89cf93700ac7 --- /dev/null +++ b/cvl/cvl_when_test.go @@ -0,0 +1,147 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 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 ( + "testing" + "github.com/Azure/sonic-mgmt-common/cvl" +) + +func TestValidateEditConfig_When_Exp_In_Choice_Negative(t *testing.T) { + + depDataMap := map[string]interface{}{ + "ACL_TABLE": map[string]interface{}{ + "TestACL1": map[string]interface{}{ + "stage": "INGRESS", + "type": "MIRROR", + }, + }, + } + + loadConfigDB(rclient, depDataMap) + + cvSess, _ := cvl.ValidationSessOpen() + cfgDataRule := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "ACL_RULE|TestACL1|Rule1", + map[string]string{ + "PACKET_ACTION": "FORWARD", + "IP_TYPE": "IPV6", + "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) +} + +func TestValidateEditConfig_When_Exp_In_Leaf_Positive(t *testing.T) { + + depDataMap := map[string]interface{}{ + "STP": map[string]interface{}{ + "GLOBAL": map[string]interface{}{ + "mode": "rpvst", + }, + }, + } + + loadConfigDB(rclient, depDataMap) + cvSess, _ := cvl.ValidationSessOpen() + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "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) +} + +func TestValidateEditConfig_When_Exp_In_Leaf_Negative(t *testing.T) { + + depDataMap := map[string]interface{}{ + "STP": map[string]interface{}{ + "GLOBAL": map[string]interface{}{ + "mode": "mstp", + }, + }, + } + + loadConfigDB(rclient, depDataMap) + cvSess, _ := cvl.ValidationSessOpen() + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "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) +} diff --git a/cvl/jsondata_test.go b/cvl/jsondata_test.go index 87fe059e2724..b0a7d309209c 100644 --- a/cvl/jsondata_test.go +++ b/cvl/jsondata_test.go @@ -27,7 +27,7 @@ var json_edit_config_create_acl_table_dependent_data = []string{`{ var json_edit_config_create_acl_rule_config_data = []string{ `{ "PACKET_ACTION": "FORWARD", - "IP_TYPE": "IPV4", + "IP_TYPE": "IPV4", "SRC_IP": "10.1.1.1/32", "L4_SRC_PORT": "1909", "IP_PROTOCOL": "103", diff --git a/cvl/testdata/acl_rule.json b/cvl/testdata/acl_rule.json index 46c5a3745af1..73b66ca366ee 100644 --- a/cvl/testdata/acl_rule.json +++ b/cvl/testdata/acl_rule.json @@ -1,10 +1,12 @@ { -"ACL_RULE": { - "TestACL13|Rule1": { - "PRIORITY": "55", - "PACKET_ACTION": "DROP", - "IP_TYPE" : "IPV4", - "L4_SRC_PORT": "0" - } - } + "ACL_RULE": { + "TestACL13|Rule1": { + "PRIORITY": "55", + "PACKET_ACTION": "DROP", + "L4_SRC_PORT": "0", + "IP_TYPE": "IPV4", + "SRC_IP": "10.1.1.1/32", + "DST_IP": "20.2.2.2/32" + } + } } diff --git a/cvl/testdata/aclrule.json b/cvl/testdata/aclrule.json index 41a768225a74..6d7dd3f20bf0 100644 --- a/cvl/testdata/aclrule.json +++ b/cvl/testdata/aclrule.json @@ -1,9 +1,9 @@ { "PACKET_ACTION": "FORWARD", - "SRC_IP": "10.1.1.1/32", - "IP_TYPE" : "IPV4", - "L4_SRC_PORT": "1909", - "IP_PROTOCOL": "103", - "DST_IP": "20.2.2.2/32", - "L4_DST_PORT_RANGE": "9000-12000" + "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" } diff --git a/cvl/testdata/port_table.json b/cvl/testdata/port_table.json index 266731a6ed0e..ec6ce5e734f9 100644 --- a/cvl/testdata/port_table.json +++ b/cvl/testdata/port_table.json @@ -2,163 +2,131 @@ "PORT": { "Ethernet0": { "alias": "fortyGigE0/0", - "lanes": "29,30,31,32", - "index": "0" + "lanes": "29,30,31,32" }, "Ethernet4": { "alias": "fortyGigE0/4", - "lanes": "25,26,27,28", - "index": "1" + "lanes": "25,26,27,28" }, "Ethernet8": { "alias": "fortyGigE0/8", - "lanes": "37,38,39,40", - "index": "2" + "lanes": "37,38,39,40" }, "Ethernet12": { "alias": "fortyGigE0/12", - "lanes": "33,34,35,36", - "index": "3" + "lanes": "33,34,35,36" }, "Ethernet16": { "alias": "fortyGigE0/16", - "lanes": "41,42,43,44", - "index": "4" + "lanes": "41,42,43,44" }, "Ethernet20": { "alias": "fortyGigE0/20", - "lanes": "45,46,47,48", - "index": "5" + "lanes": "45,46,47,48" }, "Ethernet24": { "alias": "fortyGigE0/24", - "lanes": "5,6,7,8", - "index": "6" + "lanes": "5,6,7,8" }, "Ethernet28": { "alias": "fortyGigE0/28", - "lanes": "1,2,3,4", - "index": "7" + "lanes": "1,2,3,4" }, "Ethernet32": { "alias": "fortyGigE0/32", - "lanes": "9,10,11,12", - "index": "8" + "lanes": "9,10,11,12" }, "Ethernet36": { "alias": "fortyGigE0/36", - "lanes": "13,14,15,16", - "index": "9" + "lanes": "13,14,15,16" }, "Ethernet40": { "alias": "fortyGigE0/40", - "lanes": "21,22,23,24", - "index": "10" + "lanes": "21,22,23,24" }, "Ethernet44": { "alias": "fortyGigE0/44", - "lanes": "17,18,19,20", - "index": "11" + "lanes": "17,18,19,20" }, "Ethernet48": { "alias": "fortyGigE0/48", - "lanes": "49,50,51,52", - "index": "12" + "lanes": "49,50,51,52" }, "Ethernet52": { "alias": "fortyGigE0/52", - "lanes": "53,54,55,56", - "index": "13" + "lanes": "53,54,55,56" }, "Ethernet56": { "alias": "fortyGigE0/56", - "lanes": "61,62,63,64", - "index": "14" + "lanes": "61,62,63,64" }, "Ethernet60": { "alias": "fortyGigE0/60", - "lanes": "57,58,59,60", - "index": "15" + "lanes": "57,58,59,60" }, "Ethernet64": { "alias": "fortyGigE0/64", - "lanes": "65,66,67,68", - "index": "16" + "lanes": "65,66,67,68" }, "Ethernet68": { "alias": "fortyGigE0/68", - "lanes": "69,70,71,72", - "index": "17" + "lanes": "69,70,71,72" }, "Ethernet72": { "alias": "fortyGigE0/72", - "lanes": "77,78,79,80", - "index": "18" + "lanes": "77,78,79,80" }, "Ethernet76": { "alias": "fortyGigE0/76", - "lanes": "73,74,75,76", - "index": "19" + "lanes": "73,74,75,76" }, "Ethernet80": { "alias": "fortyGigE0/80", - "lanes": "105,106,107,108", - "index": "20" + "lanes": "105,106,107,108" }, "Ethernet84": { "alias": "fortyGigE0/84", - "lanes": "109,110,111,112", - "index": "21" + "lanes": "109,110,111,112" }, "Ethernet88": { "alias": "fortyGigE0/88", - "lanes": "117,118,119,120", - "index": "22" + "lanes": "117,118,119,120" }, "Ethernet92": { "alias": "fortyGigE0/92", - "lanes": "113,114,115,116", - "index": "23" + "lanes": "113,114,115,116" }, "Ethernet96": { "alias": "fortyGigE0/96", - "lanes": "121,122,123,124", - "index": "24" + "lanes": "121,122,123,124" }, "Ethernet100": { "alias": "fortyGigE0/100", - "lanes": "125,126,127,128", - "index": "25" + "lanes": "125,126,127,128" }, "Ethernet104": { "alias": "fortyGigE0/104", - "lanes": "85,86,87,88", - "index": "26" + "lanes": "85,86,87,88" }, "Ethernet108": { "alias": "fortyGigE0/108", - "lanes": "81,82,83,84", - "index": "27" + "lanes": "81,82,83,84" }, "Ethernet112": { "alias": "fortyGigE0/112", - "lanes": "89,90,91,92", - "index": "28" + "lanes": "89,90,91,92" }, "Ethernet116": { "alias": "fortyGigE0/116", - "lanes": "93,94,95,96", - "index": "29" + "lanes": "93,94,95,96" }, "Ethernet120": { "alias": "fortyGigE0/120", - "lanes": "97,98,99,100", - "index": "30" + "lanes": "97,98,99,100" }, "Ethernet124": { "alias": "fortyGigE0/124", - "lanes": "101,102,103,104", - "index": "31" + "lanes": "101,102,103,104" } } } diff --git a/cvl/testdata/schema/sonic-acl-deviation.yang b/cvl/testdata/schema/sonic-acl-deviation.yang deleted file mode 100644 index c1d701c29d43..000000000000 --- a/cvl/testdata/schema/sonic-acl-deviation.yang +++ /dev/null @@ -1,43 +0,0 @@ -module sonic-acl-deviation { - namespace "http://github.com/Azure/sonic-acl-deviation"; - prefix acld; - yang-version 1.1; - - import sonic-acl { - prefix sacl; - } - - organization - "SONiC"; - - contact - "SONiC"; - - description - "SONIC ACL Deviations"; - - revision 2019-05-15 { - description - "Initial revision."; - } -/* - deviation /sacl:sonic-acl/sacl:ACL_TABLE/sacl:type { - deviate replace { - type enumeration { - enum MIRROR; - enum L2; - enum L3; - } - } - } - - deviation /sacl:sonic-acl/sacl:ACL_RULE/sacl:PACKET_ACTION { - deviate replace { - type enumeration { - enum FORWARD; - enum DROP; - } - } - } - */ -} diff --git a/cvl/testdata/schema/sonic-acl.yang b/cvl/testdata/schema/sonic-acl.yang index 34b416940991..135749c3da51 100644 --- a/cvl/testdata/schema/sonic-acl.yang +++ b/cvl/testdata/schema/sonic-acl.yang @@ -97,7 +97,7 @@ module sonic-acl { list ACL_RULE_LIST { key "aclname rulename"; - sonic-ext:custom-validation ValidateMaxAclRule; // Max 64K ACL rules for all platforms + //sonic-ext:custom-validation ValidateMaxAclRule; // Max 64K ACL rules for all platforms leaf aclname { type leafref { @@ -142,7 +142,7 @@ module sonic-acl { } leaf IP_TYPE { - sonic-ext:custom-validation ValidateAclRuleIPAddress; + //sonic-ext:custom-validation ValidateAclRuleIPAddress; type enumeration { enum ANY; enum IP; diff --git a/cvl/testdata/schema/sonic-bgp-global.yang b/cvl/testdata/schema/sonic-bgp-global.yang new file mode 100644 index 000000000000..f9589c529e10 --- /dev/null +++ b/cvl/testdata/schema/sonic-bgp-global.yang @@ -0,0 +1,289 @@ +module sonic-bgp-global { + namespace "http://github.com/Azure/sonic-bgp-global"; + prefix sbgpg; + yang-version 1.1; + + import sonic-vrf { + prefix svrf; + } + + import ietf-inet-types { + prefix inet; + } + + organization + "SONiC"; + + contact + "SONiC"; + + description + "SONIC BGP Global YANG"; + + revision 2019-11-20 { + description + "Added BGP fields for extensive features."; + } + + revision 2019-09-15 { + description + "Initial revision."; + } + + container sonic-bgp-global { + container BGP_GLOBALS { + list BGP_GLOBALS_LIST { + key "vrf_name"; + + leaf vrf_name { + type union { + type string { + pattern "default"; + } + type leafref { + path "/svrf:sonic-vrf/svrf:VRF/svrf:VRF_LIST/svrf:vrf_name"; + } + } + } + + leaf router_id { + type inet:ipv4-address; + } + + leaf local_asn { + type uint32; + } + + leaf always_compare_med { + type boolean; + } + + leaf load_balance_mp_relax { + type boolean; + } + + leaf graceful_restart_enable { + type boolean; + } + + leaf gr_preserve_fw_state { + type boolean; + description + "Set F-bit indication that FIB is preserved while doing Graceful Restart."; + } + + leaf gr_restart_time { + type uint16 { + range "1..3600"; + } + } + + leaf gr_stale_routes_time { + type uint16 { + range "1..3600"; + } + } + + leaf external_compare_router_id { + type boolean; + } + + leaf ignore_as_path_length { + type boolean; + } + + leaf log_nbr_state_changes { + type boolean; + } + + leaf rr_cluster_id { + type string; + } + + leaf rr_allow_out_policy { + type boolean; + } + + leaf disable_ebgp_connected_rt_check { + type boolean; + } + + leaf fast_external_failover { + type boolean; + } + + leaf network_import_check { + type boolean; + } + + leaf graceful_shutdown { + type boolean; + } + + leaf route_flap_dampen { + type boolean; + } + + leaf route_flap_dampen_half_life { + type uint8 { + range "1..45"; + } + } + + leaf route_flap_dampen_reuse_threshold { + type uint16 { + range "1..20000"; + } + } + + leaf route_flap_dampen_suppress_threshold { + type uint16 { + range "1..20000"; + } + } + + leaf route_flap_dampen_max_suppress { + type uint8 { + range "1..255"; + } + } + + leaf rr_clnt_to_clnt_reflection { + type boolean; + description + "Enable client to client route reflection."; + } + + leaf max_dynamic_neighbors { + type uint16 { + range 1..5000; + } + description + "Maximum number of BGP dynamic neighbors that can be created."; + } + + leaf read_quanta { + type uint8 { + range 1..10; + } + description + "This indicates how many packets to read from peer socket per I/O cycle"; + } + + leaf write_quanta { + type uint8 { + range 1..10; + } + description + "This indicates how many packets to write to peer socket per run"; + } + + leaf coalesce_time { + type uint32; + description + "Subgroup coalesce timer value in milli-sec"; + } + + leaf route_map_process_delay { + type uint16 { + range 0..600; + } + description + "0 disables the timer, no route updates happen when route-maps change"; + } + + leaf deterministic_med { + type boolean; + description + "Pick the best-MED path among paths advertised from the neighboring AS."; + } + + leaf med_confed { + type boolean; + description + "Compare MED among confederation paths when set to true."; + } + + leaf med_missing_as_worst { + type boolean; + description + "Treat missing MED as the least preferred one when set to true."; + } + + leaf compare_confed_as_path { + type boolean; + description + "Compare path lengths including confederation sets & sequences in selecting a route"; + } + + leaf as_path_mp_as_set { + type boolean; + description + "Generate an AS_SET."; + } + + leaf default_ipv4_unicast { + type boolean; + description + "Activate ipv4-unicast for a peer by default"; + } + + leaf default_local_preference { + type uint32; + description + "Configure default local preference value."; + } + + leaf default_show_hostname { + type boolean; + description + "Show hostname in BGP dumps."; + } + + leaf default_shutdown { + type boolean; + description + "Apply administrative shutdown to newly configured peers."; + } + + leaf default_subgroup_pkt_queue_max { + type uint8 { + range 20..100; + } + description + "Configure subgroup packet queue max."; + } + + leaf max_med_time { + type uint32{ + range 5..86400; + } + description + "Time (seconds) period for max-med"; + } + + leaf max_med_val { + type uint32; + description + "Max MED value to be used"; + } + + leaf max_delay { + type uint16 { + range 0..3600; + } + description + "Maximum delay for best path calculation."; + } + + leaf establish_wait { + type uint16 { + range 0..3600; + } + description + "Maximum delay for updates."; + } + } + } + } +} diff --git a/cvl/testdata/schema/sonic-bgp-neighbor.yang b/cvl/testdata/schema/sonic-bgp-neighbor.yang deleted file mode 100644 index 14504d6f9b83..000000000000 --- a/cvl/testdata/schema/sonic-bgp-neighbor.yang +++ /dev/null @@ -1,84 +0,0 @@ -module sonic-bgp-neighbor { - namespace "http://github.com/Azure/sonic-bgp-neighbor"; - prefix sbn; - - import ietf-inet-types { - prefix inet; - } - - import sonic-common { - prefix scommon; - } - - organization - "SONiC"; - - contact - "SONiC"; - - description - "SONIC BGP NEIGHBOR"; - - revision 2019-07-02 { - description - "Initial revision."; - } - - container sonic-bgp-neighbor { - - container BGP_NEIGHBOR { - - list BGP_NEIGHBOR_LIST { - key "ipaddress"; - - leaf ipaddress{ - type inet:ip-address; - } - - leaf rrclient { - type uint8 { - range "0..255"; - } - } - - leaf admin_status{ - type scommon:admin-status; - } - - leaf peer_addr{ - type inet:ip-address; - } - - leaf name { - type string; - } - - leaf local_addr { - type inet:ipv4-address; - } - - leaf nhopself { - type uint8 { - range "0..255"; - } - } - - leaf holdtime { - type uint8 { - range "0..255"; - } - } - - leaf asn { - type uint64; - } - - leaf keepalive { - type uint8 { - range "0..255"; - } - } - } - } - } -} diff --git a/cvl/testdata/schema/sonic-device-metadata.yang b/cvl/testdata/schema/sonic-device-metadata.yang index 014f88cd47ba..0f51095527e0 100644 --- a/cvl/testdata/schema/sonic-device-metadata.yang +++ b/cvl/testdata/schema/sonic-device-metadata.yang @@ -1,4 +1,5 @@ module sonic-device-metadata { + yang-version 1.1; namespace "http://github.com/Azure/sonic-device-metadata"; prefix sdm; @@ -10,8 +11,8 @@ module sonic-device-metadata { prefix scommon; } - import sonic-bgp-neighbor { - prefix sbn; + import sonic-bgp-global { + prefix sbg; } organization @@ -34,11 +35,19 @@ module sonic-device-metadata { list DEVICE_METADATA_LIST { key "name"; + max-elements 1; leaf name{ type string; } + leaf vrf_name { + type enumeration { + enum default; + } + default default; + } + leaf hwsku { type string; } @@ -57,7 +66,7 @@ module sonic-device-metadata { leaf bgp_asn { type leafref { - path "/sbn:sonic-bgp-neighbor/sbn:BGP_NEIGHBOR/sbn:BGP_NEIGHBOR_LIST/sbn:asn"; + path "/sbg:sonic-bgp-global/sbg:BGP_GLOBALS/sbg:BGP_GLOBALS_LIST[sbg:vrf_name=current()/../vrf_name]/sbg:local_asn"; } } diff --git a/cvl/testdata/schema/sonic-ifa.yang b/cvl/testdata/schema/sonic-ifa.yang new file mode 100644 index 000000000000..dfdc0dc0b50d --- /dev/null +++ b/cvl/testdata/schema/sonic-ifa.yang @@ -0,0 +1,87 @@ +module sonic-ifa { + namespace "http://github.com/Azure/sonic-ifa"; + prefix ifa; + + yang-version 1.1; + + import sonic-acl { + prefix acl; + } + + organization + "SONiC"; + + contact + "SONiC"; + + description + "SONIC TAM IFA"; + + revision 2019-11-12 { + description + "Initial revision."; + } + + container sonic-ifa { + + container TAM_INT_IFA_FEATURE_TABLE { + + list TAM_INT_IFA_FEATURE_TABLE_LIST { + key "name"; + + leaf name { + type enumeration { + enum feature; + } + } + + leaf enable { + type boolean; + } + } + } + + container TAM_INT_IFA_FLOW_TABLE { + + list TAM_INT_IFA_FLOW_TABLE_LIST { + key "name"; + + leaf name { + type string { + pattern '[a-zA-Z0-9]{1}([-a-zA-Z0-9_]{0,32})'; + length 1..32; + } + } + + leaf acl-table-name { + mandatory true; + type leafref { + path "/acl:sonic-acl/acl:ACL_TABLE/acl:ACL_TABLE_LIST/acl:aclname"; + } + } + + leaf acl-rule-name { + mandatory true; + type leafref { + path "/acl:sonic-acl/acl:ACL_RULE/acl:ACL_RULE_LIST[acl:aclname=current()/../acl-table-name]/acl:rulename"; + } + } + + leaf sampling-rate { + type uint16 { + range "1..10000"{ + error-app-tag "Invalid IFA flow sampling rate."; + } + } + } + + leaf collector-name { + type string { + pattern '[a-zA-Z0-9]{1}([-a-zA-Z0-9_]{0,32})'; + length 1..32; + } + } + } + } + } +} diff --git a/cvl/testdata/schema/sonic-igmp-snooping.yang b/cvl/testdata/schema/sonic-igmp-snooping.yang new file mode 100644 index 000000000000..ec04c22342a2 --- /dev/null +++ b/cvl/testdata/schema/sonic-igmp-snooping.yang @@ -0,0 +1,242 @@ +module sonic-igmp-snooping { + namespace "http://github.com/Azure/sonic-igmp-snooping"; + prefix sigmps; + yang-version 1.1; + + import ietf-inet-types { + prefix inet; + } + + import sonic-vlan { + prefix svlan; + } + + import sonic-port { + prefix prt; + } + + import sonic-portchannel { + prefix spc; + } + + organization + "BRCM"; + + contact + "BRCM"; + + description + "SONIC IGMP SNOOPING"; + + revision 2019-10-15 { + description + "Initial revision."; + } + + container sonic-igmp-snooping { + + container APP_L2MC_MEMBER_TABLE { + config false; + + list APP_L2MC_MEMBER_TABLE_LIST { + + key "vlan-name group-addr source-addr out-intf"; + + leaf vlan-name { + type string { + pattern "Vlan(409[0-5]|40[0-8][0-9]|[1-3][0-9]{3}|[1-9][0-9]{2}|[1-9][0-9]|[1-9])"; + } + } + + leaf group-addr { + type inet:ipv4-address; + } + + leaf source-addr { + type inet:ipv4-address; + } + + leaf out-intf { + type string { + pattern "Ethernet([1-3][0-9]{3}|[1-9][0-9]{2}|[1-9][0-9]|[0-9])"; + } + } + } + } + + container CFG_L2MC_STATIC_GROUP_TABLE { + + list CFG_L2MC_STATIC_GROUP_TABLE_LIST { + key "vlan-name group-addr"; + + leaf vlan-name { + type leafref { + path "/svlan:sonic-vlan/svlan:VLAN/svlan:VLAN_LIST/svlan:name"; + } + } + + leaf group-addr { + type inet:ipv4-address; + } + + leaf-list out-intf { + max-elements 3; + type union { + type leafref { + path "/prt:sonic-port/prt:PORT/prt:PORT_LIST/prt:ifname"; + } + type leafref { + path "/spc:sonic-portchannel/spc:PORTCHANNEL/spc:PORTCHANNEL_LIST/spc:name"; + } + } + } + } + } + + container CFG_L2MC_STATIC_MEMBER_TABLE { + + list CFG_L2MC_STATIC_MEMBER_TABLE_LIST { + + key "vlan-name group-addr out-intf"; + + leaf vlan-name { + type leafref { + path "/svlan:sonic-vlan/svlan:VLAN/svlan:VLAN_LIST/svlan:name"; + } + } + + leaf group-addr { + type inet:ipv4-address; + } + + leaf out-intf { + type union { + type leafref { + path "/prt:sonic-port/prt:PORT/prt:PORT_LIST/prt:ifname"; + } + type leafref { + path "/spc:sonic-portchannel/spc:PORTCHANNEL/spc:PORTCHANNEL_LIST/spc:name"; + } + } + } + } + } + + container APP_L2MC_MROUTER_TABLE { + config false; + + list APP_L2MC_MROUTER_TABLE_LIST { + + key "vlan-name port_name"; + + leaf vlan-name { + type string { + pattern "Vlan(409[0-5]|40[0-8][0-9]|[1-3][0-9]{3}|[1-9][0-9]{2}|[1-9][0-9]|[1-9])"; + } + } + + leaf port_name { + type string { + pattern "Ethernet([1-3][0-9]{3}|[1-9][0-9]{2}|[1-9][0-9]|[0-9])"; + } + } + } + } + + container CFG_L2MC_MROUTER_TABLE { + + list CFG_L2MC_MROUTER_TABLE_LIST { + + key "vlan-name mrouter-intf"; + + leaf vlan-name { + type leafref { + path "/svlan:sonic-vlan/svlan:VLAN/svlan:VLAN_LIST/svlan:name"; + } + } + + leaf mrouter-intf { + type union { + type leafref { + path "/prt:sonic-port/prt:PORT/prt:PORT_LIST/prt:ifname"; + } + type leafref { + path "/spc:sonic-portchannel/spc:PORTCHANNEL/spc:PORTCHANNEL_LIST/spc:name"; + } + } + } + } + } + + container CFG_L2MC_TABLE { + + list CFG_L2MC_TABLE_LIST { + key "vlan-name"; + + leaf vlan-name { + type leafref { + path "/svlan:sonic-vlan/svlan:VLAN/svlan:VLAN_LIST/svlan:name"; + } + } + + leaf enabled { + type boolean; + default false; + } + + leaf querier { + type boolean; + default false; + } + + leaf fast-leave { + type boolean; + default false; + } + + leaf version { + type uint8 { + range "1..3"{ + error-message "Invalid IGMP Snooping version."; + } + } + default 2; + } + + leaf query-interval { + must "current() > ../query-max-response-time" { + error-message "Invalid IGMP Snooping query interval value."; + } + + type uint32 { + range "1..18000" { + error-message "Invalid IGMP Snooping Query Interval."; + } + } + default 125; + } + + leaf last-member-query-interval { + type uint32 { + range "100..25500"{ + error-message "Invalid IGMP Snooping Last Member Query Interval."; + } + } + default 1000; + } + + leaf query-max-response-time { + must "current() < ../query-interval" { + error-message "Invalid IGMP Snooping query interval value."; + } + type uint16 { + range "1..25"{ + error-message "Invalid IGMP Snooping Query Max Response Time."; + } + } + default 10; + } + } + } + } +} diff --git a/cvl/testdata/schema/sonic-interface.yang b/cvl/testdata/schema/sonic-interface.yang new file mode 100644 index 000000000000..e80f6451b79f --- /dev/null +++ b/cvl/testdata/schema/sonic-interface.yang @@ -0,0 +1,116 @@ +module sonic-interface { + namespace "http://github.com/Azure/sonic-interface"; + prefix sint; + + import ietf-inet-types { + prefix inet; + } + + import sonic-port { + prefix prt; + } + + import sonic-extension { + prefix sonic-ext; + } + + import sonic-common { + prefix cmn; + } + + import sonic-loopback-interface { + prefix lointf; + } + + import sonic-portchannel { + prefix spc; + } + + organization + "SONiC"; + + contact + "SONiC"; + + description + "SONIC INTERFACE"; + + revision 2019-07-02 { + description + "Initial revision."; + } + + container sonic-interface { + + container INTERFACE { + + list INTERFACE_LIST { + key "portname"; + + leaf portname{ + type leafref { + path "/prt:sonic-port/prt:PORT/prt:PORT_LIST/prt:ifname"; + } + } + + leaf vrf_name { + type string; + } + + leaf unnumbered { + must "((/cmn:operation/cmn:operation != 'CREATE') and (/cmn:operation/cmn:operation != 'UPDATE')) or " + + "not(contains(/sonic-interface/INTERFACE/INTERFACE_IPADDR_LIST[portname=current()/../portname]/ip_prefix, '.'))" + { + error-message "Cannot configure IP address and IP Unnumbered interface configurations on the same interface"; + error-app-tag intf-ip-config; + } + + must "((/cmn:operation/cmn:operation != 'CREATE') and (/cmn:operation/cmn:operation != 'UPDATE')) or " + + "(count(/sonic-interface/INTERFACE/INTERFACE_LIST[portname=current()/../portname]/unnumbered) <= 1)" + { + error-message "IP Unnumbered interface configuration is already done on this interface"; + error-app-tag intf-unnum-config; + } + + must "count(/spc:sonic-portchannel/spc:PORTCHANNEL_MEMBER/spc:PORTCHANNEL_MEMBER_LIST[spc:ifname=current()/../portname]) = 0" + { + error-message "Interface configured as a member of portchannel. Cannot configure IP Unnumbered"; + error-app-tag intf-po-member; + } + + must "count(current()/../vrf_name) = 0" + { + error-message "Non default VRF binded to interface. Cannot configure IP Unnumbered"; + error-app-tag intf-non-def-vrf; + } + + must "count(/lointf:sonic-loopback-interface/lointf:LOOPBACK_INTERFACE/lointf:LOOPBACK_INTERFACE_LIST[lointf:loIfName=current()/./unnumbered]/lointf:vrf_name) = 0" + { + error-message "Non default VRF binded to Donor interface. Cannot configure IP Unnumbered"; + error-app-tag donor-intf-non-def-vrf; + } + + //sonic-ext:custom-validation ValidateIpv4UnnumIntf; + type leafref { + path "/lointf:sonic-loopback-interface/lointf:LOOPBACK_INTERFACE/lointf:LOOPBACK_INTERFACE_LIST/lointf:loIfName"; + } + } + } + + list INTERFACE_IPADDR_LIST { + key "portname ip_prefix"; + + leaf portname{ + type leafref { + path "/prt:sonic-port/prt:PORT/prt:PORT_LIST/prt:ifname"; + } + } + + leaf ip_prefix { + type inet:ip-prefix; + + } + } + } + } +} diff --git a/cvl/testdata/schema/sonic-loopback-interface.yang b/cvl/testdata/schema/sonic-loopback-interface.yang new file mode 100644 index 000000000000..a0a78ae188a7 --- /dev/null +++ b/cvl/testdata/schema/sonic-loopback-interface.yang @@ -0,0 +1,54 @@ +module sonic-loopback-interface { + namespace "http://github.com/Azure/sonic-loopback-interface"; + prefix sonic-lo-if; + + import ietf-inet-types { + prefix inet; + } + + organization + "SONiC"; + + contact + "SONiC"; + + description + "SONIC LOOPBACK INTERFACE"; + + revision 2019-07-02 { + description + "Initial revision."; + } + + container sonic-loopback-interface { + + container LOOPBACK_INTERFACE { + + list LOOPBACK_INTERFACE_LIST { + key "loIfName"; + + leaf loIfName{ + type string; + } + + leaf vrf_name { + type string; + } + } + + list LOOPBACK_INTERFACE_IPADDR_LIST { + key "loIfName ip_prefix"; + + leaf loIfName{ + type string; + } + + leaf ip_prefix { + mandatory true; + type inet:ip-prefix; + + } + } + } + } +} diff --git a/cvl/testdata/schema/sonic-mirror-session.yang b/cvl/testdata/schema/sonic-mirror-session.yang index f9e22691dd6a..ff18b5a14a53 100644 --- a/cvl/testdata/schema/sonic-mirror-session.yang +++ b/cvl/testdata/schema/sonic-mirror-session.yang @@ -13,7 +13,7 @@ module sonic-mirror-session { "SONiC"; description - "SONiC MIRROR SESSION"; + "SONIC MIRROR SESSION"; revision 2019-05-15 { description diff --git a/cvl/testdata/schema/sonic-portchannel-interface.yang b/cvl/testdata/schema/sonic-portchannel-interface.yang index 45d92787d244..c9a9054267c5 100644 --- a/cvl/testdata/schema/sonic-portchannel-interface.yang +++ b/cvl/testdata/schema/sonic-portchannel-interface.yang @@ -1,48 +1,58 @@ module sonic-portchannel-interface { - namespace "http://github.com/Azure/sonic-portchannel-interface"; - prefix spchint; + namespace "http://github.com/Azure/sonic-portchannel-interface"; + prefix spchint; - import ietf-inet-types { - prefix inet; - } + import ietf-inet-types { + prefix inet; + } - import sonic-portchannel { - prefix spc; - } + import sonic-portchannel { + prefix spc; + } - organization - "SONiC"; + organization + "SONiC"; - contact - "SONiC"; + contact + "SONiC"; - description - "SONIC PORTCHANNEL INTERFACE"; + description + "SONIC PORTCHANNEL INTERFACE"; - revision 2019-07-02 { - description - "Initial revision."; - } + revision 2019-07-02 { + description + "Initial revision."; + } - container sonic-portchannel-interface { + container sonic-portchannel-interface { - container PORTCHANNEL_INTERFACE { + container PORTCHANNEL_INTERFACE { - list PORTCHANNEL_INTERFACE_LIST { - key "pch_name ip_prefix"; + list PORTCHANNEL_INTERFACE_LIST { + key "pch_name"; - leaf pch_name{ - type leafref { - path "/spc:sonic-portchannel/spc:PORTCHANNEL/spc:PORTCHANNEL_LIST/spc:name"; - } - } + leaf pch_name{ + type leafref { + path "/spc:sonic-portchannel/spc:PORTCHANNEL/spc:PORTCHANNEL_LIST/spc:name"; + } + } - leaf ip_prefix { - mandatory true; - type inet:ip-prefix; + } + list PORTCHANNEL_INTERFACE_IPADDR_LIST { + key "pch_name ip_prefix"; - } - } - } - } + leaf pch_name{ + type leafref { + path "/spc:sonic-portchannel/spc:PORTCHANNEL/spc:PORTCHANNEL_LIST/spc:name"; + } + } + + leaf ip_prefix { + mandatory true; + type inet:ip-prefix; + + } + } + } + } } diff --git a/cvl/testdata/schema/sonic-portchannel.yang b/cvl/testdata/schema/sonic-portchannel.yang index afe308f4e5e4..c70a80deea0b 100644 --- a/cvl/testdata/schema/sonic-portchannel.yang +++ b/cvl/testdata/schema/sonic-portchannel.yang @@ -1,77 +1,136 @@ module sonic-portchannel { - namespace "http://github.com/Azure/sonic-portchannel"; - prefix spc; - - import sonic-common { - prefix scommon; - } - - import sonic-port { - prefix prt; - } - - organization - "SONiC"; - - contact - "SONiC"; - - description - "SONIC PORTCHANNEL"; - - revision 2019-05-15 { - description - "Initial revision."; - } - - container sonic-portchannel { - - container PORTCHANNEL { - - list PORTCHANNEL_LIST { - key "name"; - - max-elements 3; - - leaf name { - type string; - } - - leaf admin_status { - type scommon:admin-status; - } - - leaf mtu { - type uint16; - } - - leaf min_links { - type uint8; - } - - leaf fallback { - type boolean; - } - } - } - - container PORTCHANNEL_MEMBER { - - list PORTCHANNEL_MEMBER_LIST { - key "name ifname"; - - leaf name { - type leafref { - path "../../../PORTCHANNEL/PORTCHANNEL_LIST/name"; - } - } - - leaf ifname { - type leafref { - path "/prt:sonic-port/prt:PORT/prt:PORT_LIST/prt:ifname"; - } - } - } - } - } + namespace "http://github.com/Azure/sonic-portchannel"; + prefix spc; + + import sonic-common { + prefix scommon; + } + + import sonic-port { + prefix prt; + } + + organization + "SONiC"; + + contact + "SONiC"; + + description + "SONIC PORTCHANNEL"; + + revision 2019-05-15 { + description + "Initial revision."; + } + + container sonic-portchannel { + + container PORTCHANNEL { + + list PORTCHANNEL_LIST { + key "name"; + + leaf name { + type string; + } + + leaf admin_status { + type scommon:admin-status; + } + + leaf mtu { + type uint32 { + range "1312..9216" { + error-message "Invalid MTU value"; + error-app-tag mtu-invalid; + } + } + } + + leaf min_links { + type uint8; + } + + leaf fallback { + type boolean; + } + } + } + + container PORTCHANNEL_MEMBER { + + list PORTCHANNEL_MEMBER_LIST { + key "name ifname"; + + leaf name { + type leafref { + path "../../../PORTCHANNEL/PORTCHANNEL_LIST/name"; + } + } + + leaf ifname { + type leafref { + path "/prt:sonic-port/prt:PORT/prt:PORT_LIST/prt:ifname"; + } + } + } + } + + container LAG_TABLE { + config false; + + list LAG_TABLE_LIST { + key "lagname"; + + leaf lagname { + type string; + } + + leaf admin_status { + type scommon:admin-status; + } + + leaf mtu { + type uint32 { + range "1312..9216" { + error-message "Invalid MTU value"; + error-app-tag mtu-invalid; + } + } + } + + leaf active { + type boolean; + } + + leaf name { + type string; + } + + leaf oper_status { + type scommon:oper-status; + } + } + } + + container LAG_MEMBER_TABLE { + config false; + list LAG_MEMBER_TABLE_LIST { + key "name ifname"; + + leaf name { + type leafref { + path "../../../LAG_TABLE/LAG_TABLE_LIST/lagname"; + } + } + + leaf ifname { + type leafref { + path "/prt:sonic-port/prt:PORT/prt:PORT_LIST/prt:ifname"; + } + } + } + } + } } diff --git a/cvl/testdata/schema/sonic-spanning-tree.yang b/cvl/testdata/schema/sonic-spanning-tree.yang new file mode 100755 index 000000000000..099278ae6309 --- /dev/null +++ b/cvl/testdata/schema/sonic-spanning-tree.yang @@ -0,0 +1,246 @@ +module sonic-spanning-tree { + namespace "http://github.com/Azure/sonic-spanning-tree"; + prefix sstp; + yang-version 1.1; + + import sonic-port { + prefix prt; + } + + import sonic-vlan { + prefix svlan; + } + + organization + "SONiC"; + + contact + "SONiC"; + + + description + "SONIC Spanning-tree"; + + revision 2019-09-21 { + description + "Initial revision."; + } + + grouping vlanModeAttr { + leaf forward_delay { + type uint8 { + range "4..30" { + error-message "Invalid Forwarding Delay value."; + } + } + units seconds; + default 15; + } + + leaf hello_time { + type uint8 { + range "1..10" { + error-message "Invalid Hello Time value."; + } + } + units seconds; + default 2; + } + + leaf max_age { + type uint8 { + range "6..40" { + error-message "Invalid Maximum Age Time value."; + } + } + units seconds; + default 20; + } + + leaf priority { + type uint16 { + range "0..61440" { + error-message "Invalid Bridge Priority value."; + } + } + default 32768; + } + } + + grouping interfaceAttr { + leaf path_cost { + type uint64 { + range "1..200000000" { + error-message "Invalid Port Path Cost value."; + } + } + default 200; + } + + leaf priority { + type uint8 { + range "0..240" { + error-message "Invalid Port Priority value."; + } + } + default 128; + } + } + + container sonic-spanning-tree { + + container STP { + list STP_LIST { + max-elements 1; + key "keyleaf"; + + leaf keyleaf { + type enumeration { + enum GLOBAL; + } + } + + leaf mode { + type enumeration { + enum pvst; + enum rpvst; + enum mstp; + enum rstp; + } + } + + leaf rootguard_timeout { + type uint16 { + range "5..600" { + error-message "Invalid Root-guard Timeout value."; + } + } + units seconds; + default 30; + } + + leaf bpdu_filter { + type boolean; + } + + uses vlanModeAttr; + } + } + + container STP_VLAN { + list STP_VLAN_LIST { + key "name"; + + leaf name { + type leafref { + path "/svlan:sonic-vlan/svlan:VLAN/svlan:VLAN_LIST/svlan:name"; + } + } + + leaf vlanid { + type uint16 { + range "1..4095" { + error-message "Vlan ID out of range"; + error-app-tag vlanid-invalid; + } + } + } + + leaf enabled { + type boolean; + } + + uses vlanModeAttr; + } + } + + container STP_VLAN_PORT { + list STP_VLAN_PORT_LIST { + key "vlan-name ifname"; + + leaf vlan-name { + type leafref { + path "../../../STP_VLAN/STP_VLAN_LIST/name"; + } + } + + leaf ifname { + type leafref { + path "../../../STP_PORT/STP_PORT_LIST/ifname"; + } + } + + uses interfaceAttr; + } + } + + container STP_PORT { + list STP_PORT_LIST { + key "ifname"; + + leaf ifname { + type union { + type string { //Any other type + pattern "StpIntf[0-9]+"; + } + type leafref { + path "/prt:sonic-port/prt:PORT/prt:PORT_LIST/prt:ifname"; + } + /*type leafref { + path "/spc:sonic-portchannel/spc:PORTCHANNEL/spc:PORTCHANNEL_LIST/spc:name"; + }*/ + } + } + + leaf enabled { + type boolean; + } + + leaf root_guard { + type boolean; + } + + leaf bpdu_guard { + type boolean; + } + + leaf bpdu_filter { + type enumeration { + enum enable; + enum disable; + enum global; + } + } + + leaf bpdu_guard_do_disable { + type boolean; + } + + leaf uplink_fast { + type boolean; + } + + leaf portfast { + type boolean; + } + + uses interfaceAttr; + + // For RPVST+ + leaf edge_port { + when "../../../STP/STP_LIST[keyleaf='GLOBAL']/mode = 'rpvst'"; + type boolean; + } + + leaf link_type { + when "../../../STP/STP_LIST[keyleaf='GLOBAL']/mode = 'rpvst'"; + type enumeration { + enum auto; + enum shared; + enum point-to-point; + } + } + } + } + } +} diff --git a/cvl/testdata/schema/sonic-tam.yang b/cvl/testdata/schema/sonic-tam.yang new file mode 100644 index 000000000000..20f8c7867941 --- /dev/null +++ b/cvl/testdata/schema/sonic-tam.yang @@ -0,0 +1,77 @@ +module sonic-tam { + namespace "http://github.com/Azure/sonic-tam"; + prefix tam; + + import ietf-inet-types { + prefix inet; + } + + organization + "SONiC"; + + contact + "SONiC"; + + description + "SONIC TAM"; + + revision 2019-11-08 { + description + "Initial revision."; + } + + container sonic-tam { + + container TAM_DEVICE_TABLE { + + list TAM_DEVICE_TABLE_LIST { + key "name"; + leaf name { + type enumeration { + enum device; + } + } + + leaf deviceid { + type uint16; + default 0; + } + } + } + + container TAM_COLLECTOR_TABLE { + + list TAM_COLLECTOR_TABLE_LIST { + key "name"; + + leaf name { + type string { + pattern '[a-zA-Z0-9]{1}([-a-zA-Z0-9_]{0,32})'; + length 1..32; + } + } + + leaf ipaddress-type { + type enumeration { + enum ipv4; + enum ipv6; + } + + must "(contains(../ipaddress, ':') and current()='ipv6') or " + + "(contains(../ipaddress, '.') and current()='ipv4')" { + error-app-tag ipaddres-type-mismatch; + error-message "IP address and IP address type does not match."; + } + } + + leaf ipaddress { + type inet:ip-address; + } + + leaf port { + type inet:port-number; + } + } + } + } +} diff --git a/cvl/testdata/schema/sonic-vlan-interface.yang b/cvl/testdata/schema/sonic-vlan-interface.yang index 554feb1f58b3..06848a964c98 100644 --- a/cvl/testdata/schema/sonic-vlan-interface.yang +++ b/cvl/testdata/schema/sonic-vlan-interface.yang @@ -1,48 +1,77 @@ module sonic-vlan-interface { - namespace "http://github.com/Azure/sonic-vlan-interface"; - prefix svint; + namespace "http://github.com/Azure/sonic-vlan-interface"; + prefix sVlanIf; - import ietf-inet-types { - prefix inet; - } + import ietf-inet-types { + prefix inet; + } - import sonic-vlan { - prefix svlan; - } + import sonic-vlan { + prefix svlan; + } - organization - "SONiC"; + import sonic-common { + prefix cmn; + } - contact - "SONiC"; + import sonic-interface { + prefix sint; + } - description - "SONIC VLAN INTERFACE"; + organization + "SONiC"; - revision 2019-07-02 { - description - "Initial revision."; - } + contact + "SONiC"; - container sonic-vlan-interface { + description + "SONiC VLAN INTERFACE"; - container VLAN_INTERFACE { + revision 2019-07-02 { + description + "Initial revision."; + } - list VLAN_INTERFACE_LIST { - key "portname ip_prefix"; + container sonic-vlan-interface { - leaf portname{ - type leafref { - path "/svlan:sonic-vlan/svlan:VLAN/svlan:VLAN_LIST/svlan:name"; - } - } + container VLAN_INTERFACE { - leaf ip_prefix { - mandatory true; - type inet:ip-prefix; + list VLAN_INTERFACE_LIST { + key "vlanName"; - } - } - } - } + leaf vlanName { + type leafref { + path "/svlan:sonic-vlan/svlan:VLAN/svlan:VLAN_LIST/svlan:name"; + } + must "count(/svlan:sonic-vlan/svlan:VLAN/svlan:VLAN_LIST/svlan:members) > 0" { + error-message "No VLAN member is configured"; + error-app-tag no-vlan-member-configured; + } + } + + } + list VLAN_INTERFACE_IPADDR_LIST { + key "vlanName ip_prefix"; + + leaf vlanName{ + type leafref { + path "/svlan:sonic-vlan/svlan:VLAN/svlan:VLAN_LIST/svlan:name"; + } + must "(/cmn:operation/cmn:operation != 'CREATE') or " + + "(count(/sint:sonic-interface/sint:INTERFACE/sint:INTERFACE_IPADDR_LIST" + + "[sint:ip_prefix=current()/../ip_prefix] " + + "[sint:portname=(/svlan:sonic-vlan/svlan:VLAN_MEMBER/svlan:VLAN_MEMBER_LIST" + + "[svlan:name=current()]/svlan:ifname)]) = 0)" { + error-message "Vlan and port being member of same vlan can't have same IP prefix."; + } + } + + leaf ip_prefix { + mandatory true; + type inet:ip-prefix; + + } + } + } + } } diff --git a/cvl/testdata/schema/sonic-vlan.yang b/cvl/testdata/schema/sonic-vlan.yang index 1170960df12c..59933798e7f6 100644 --- a/cvl/testdata/schema/sonic-vlan.yang +++ b/cvl/testdata/schema/sonic-vlan.yang @@ -1,107 +1,186 @@ module sonic-vlan { - namespace "http://github.com/Azure/sonic-vlan"; - prefix svlan; - yang-version 1.1; - - import sonic-common { - prefix scommon; - } - - import sonic-port { - prefix prt; - } - - import sonic-portchannel { - prefix spc; - } - - organization - "SONiC"; - - contact - "SONiC"; - - description - "SONIC VLAN"; - - revision 2019-05-15 { - description - "Initial revision."; - } - - - container sonic-vlan { - - container VLAN { - - list VLAN_LIST { - key "name"; - must "./name = concat('Vlan', string(./vlanid))"{ - error-app-tag vlan-invalid; - } - - leaf name { - type string { - pattern "Vlan(409[0-5]|40[0-8][0-9]|[1-3][0-9]{3}|[1-9][0-9]{2}|[1-9][0-9]|[1-9])" { - error-message "Invalid Vlan name pattern"; - error-app-tag vlan-name-invalid; - } - } - } - - leaf vlanid { - mandatory true; - type uint16 { - range "1..4095" { - error-message "Vlan ID out of range"; - error-app-tag vlanid-invalid; - } - } - } - - leaf-list members { - must "count(../members[text()=/spc:sonic-portchannel/spc:PORTCHANNEL_MEMBER/" + - "spc:PORTCHANNEL_MEMBER_LIST[spc:ifname=current()]/spc:name]) = 0 and " + - "count(../members[text()=/spc:sonic-portchannel/spc:PORTCHANNEL_MEMBER/" + - "spc:PORTCHANNEL_MEMBER_LIST[spc:name=current()]/spc:ifname]) = 0 " { - error-message "A vlan interface member cannot be part of portchannel which is already a vlan member"; - } - - - type union { - type leafref { - path "/prt:sonic-port/prt:PORT/prt:PORT_LIST/prt:ifname"; - } - type leafref { - path "/spc:sonic-portchannel/spc:PORTCHANNEL/spc:PORTCHANNEL_LIST/spc:name"; - } - } - } - } - } - - container VLAN_MEMBER { - - list VLAN_MEMBER_LIST { - key "name ifname"; - - leaf name { - type leafref { - path "../../../VLAN/VLAN_LIST/name"; - } - } - - leaf ifname { - type leafref { - path "/prt:sonic-port/prt:PORT/prt:PORT_LIST/prt:ifname"; - } - } - - leaf tagging_mode { - type scommon:tagging_mode; - default tagged; - } - } - } - } + namespace "http://github.com/Azure/sonic-vlan"; + prefix svlan; + yang-version 1.1; + + import sonic-common { + prefix scommon; + } + + import sonic-port { + prefix prt; + } + + import sonic-portchannel { + prefix spc; + } + + organization + "SONiC"; + + contact + "SONiC"; + + description + "SONIC VLAN"; + + revision 2019-05-15 { + description + "Initial revision."; + } + + + container sonic-vlan { + + container VLAN { + + list VLAN_LIST { + key "name"; + must "./name = concat('Vlan', string(./vlanid))"{ + error-app-tag vlan-invalid; + } + + leaf name { + type string { + pattern "Vlan(409[0-5]|40[0-8][0-9]|[1-3][0-9]{3}|[1-9][0-9]{2}|[1-9][0-9]|[1-9])" { + error-message "Invalid Vlan name pattern"; + error-app-tag vlan-name-invalid; + } + } + } + + leaf vlanid { + mandatory true; + type uint16 { + range "1..4095" { + error-message "Vlan ID out of range"; + error-app-tag vlanid-invalid; + } + } + } + + leaf-list members { + must "count(current()/../members[text()=/spc:sonic-portchannel/spc:PORTCHANNEL_MEMBER/" + + "spc:PORTCHANNEL_MEMBER_LIST[spc:ifname=current()]/spc:name]) = 0 and " + + "count(current()/../members[text()=/spc:sonic-portchannel/spc:PORTCHANNEL_MEMBER/" + + "spc:PORTCHANNEL_MEMBER_LIST[spc:name=current()]/spc:ifname]) = 0 " { + error-message "A vlan interface member cannot be part of portchannel which is already a vlan member"; + } + + + type union { + type leafref { + path "/prt:sonic-port/prt:PORT/prt:PORT_LIST/prt:ifname"; + } + type leafref { + path "/spc:sonic-portchannel/spc:PORTCHANNEL/spc:PORTCHANNEL_LIST/spc:name"; + } + } + } + leaf mtu { + type uint32 { + range "1312..9216" { + error-message "Invalid MTU value"; + error-app-tag mtu-invalid; + } + } + } + + leaf admin_status { + type scommon:admin-status; + } + } + } + + container VLAN_MEMBER { + + list VLAN_MEMBER_LIST { + key "name ifname"; + + leaf name { + type leafref { + path "../../../VLAN/VLAN_LIST/name"; + } + } + + leaf ifname { + type union { + type leafref { + path "/prt:sonic-port/prt:PORT/prt:PORT_LIST/prt:ifname"; + } + type leafref { + path "/spc:sonic-portchannel/spc:PORTCHANNEL/spc:PORTCHANNEL_LIST/spc:name"; + } + } + } + + leaf tagging_mode { + type scommon:tagging_mode; + default tagged; + } + } + } + + + container VLAN_TABLE { + config false; + + list VLAN_TABLE_LIST { + key "name"; + must "./name = concat('Vlan', string(./vlanid))" { + error-app-tag vlan-invalid; + } + + leaf name { + type string { + pattern "Vlan(409[0-5]|40[0-8][0-9]|[1-3][0-9]{3}|[1-9][0-9]{2}|[1-9][0-9]|[1-9])" { + error-message "Invalid Vlan name pattern"; + error-app-tag vlan-name-invalid; + } + } + } + + leaf admin_status { + type scommon:admin-status; + } + + leaf mtu { + type uint32 { + range "1312..9216" { + error-message "Invalid MTU value"; + error-app-tag mtu-invalid; + } + } + } + + leaf oper_status { + type scommon:oper-status; + } + } + } + + container VLAN_MEMBER_TABLE { + config false; + + list VLAN_MEMBER_TABLE_LIST { + key "name ifname"; + + leaf name { + type leafref { + path "../../../VLAN_TABLE/VLAN_TABLE_LIST/name"; + } + } + + leaf ifname { + type leafref { + path "/prt:sonic-port/prt:PORT/prt:PORT_LIST/prt:ifname"; + } + } + + leaf tagging_mode { + type scommon:tagging_mode; + } + } + } + } } diff --git a/cvl/testdata/schema/sonic-vrf.yang b/cvl/testdata/schema/sonic-vrf.yang new file mode 100644 index 000000000000..832bffda5b25 --- /dev/null +++ b/cvl/testdata/schema/sonic-vrf.yang @@ -0,0 +1,62 @@ +module sonic-vrf { + namespace "http://github.com/Azure/sonic-vrf"; + prefix vrf; + + import sonic-vxlan { + prefix svxlan; + } + + organization + "SONiC"; + + contact + "SONiC"; + + description + "SONIC VRF"; + + revision 2019-10-30 { + description + "Initial revision."; + } + + container sonic-vrf { + container VRF { + description "Vrf configuration."; + + list VRF_LIST { + key "vrf_name"; + + leaf vrf_name { + type string; + description + "Vrf name for SONiC, must start with Vrf."; + } + + leaf fallback { + type boolean; + default false; + description + "Enalbe/disable fallback feature which is useful for specified VRF user to access internet through global/main route."; + } + + leaf vni { + type uint32 { + range "0..16777215" { + error-message "VNI ID out of range"; + error-app-tag vnid-invalid; + } + } + + must "count(/svxlan:sonic-vxlan/svxlan:VXLAN_TUNNEL_MAP/svxlan:VXLAN_TUNNEL_MAP_LIST[svxlan:vni=current()]) > 0" { + error-app-tag vnid-not-configured; + } + + default 0; + description + "VNI mapped to VRF"; + } + } + } + } +} diff --git a/cvl/testdata/schema/sonic-vxlan.yang b/cvl/testdata/schema/sonic-vxlan.yang new file mode 100644 index 000000000000..eaed620bc26e --- /dev/null +++ b/cvl/testdata/schema/sonic-vxlan.yang @@ -0,0 +1,215 @@ +module sonic-vxlan { + namespace "http://github.com/Azure/sonic-vxlan"; + prefix svxlan; + yang-version 1.1; + + import ietf-yang-types { + prefix yang; + } + + import ietf-inet-types { + prefix inet; + } + + import sonic-common { + prefix cmn; + } + + import sonic-extension { + prefix sonic-ext; + } + import sonic-vlan { + prefix svlan; + } + + + organization + "SONiC"; + + contact + "SONiC"; + + description + "SONIC VXLAN"; + + revision 2019-10-01 { + description + "Initial revision."; + } + + container sonic-vxlan { + + container VXLAN_TUNNEL { + + list VXLAN_TUNNEL_LIST { + key "name"; + max-elements 1; + + leaf name { + type string; + } + + leaf src_ip { + mandatory true; + type inet:ipv4-address; + } + } + } + + container VXLAN_TUNNEL_MAP { + + list VXLAN_TUNNEL_MAP_LIST { + key "name mapname"; + + leaf name { + type leafref { + path "../../../VXLAN_TUNNEL/VXLAN_TUNNEL_LIST/name"; + } + } + + leaf mapname { + type string; + } + leaf vlan { + mandatory true; + type leafref { + path "/svlan:sonic-vlan/svlan:VLAN/svlan:VLAN_LIST/svlan:name"; + } + + must "((/cmn:operation/cmn:operation != 'CREATE') and (/cmn:operation/cmn:operation != 'UPDATE')) or " + + "(count(../../VXLAN_TUNNEL_MAP_LIST[vlan=current()]) = 1)" { + error-app-tag not-unique-vlanid; + } + } + + leaf vni { + mandatory true; + type uint32 { + range "1..16777215" { + error-message "VNI ID out of range"; + error-app-tag vnid-invalid; + } + } + + must "((/cmn:operation/cmn:operation != 'CREATE') and (/cmn:operation/cmn:operation != 'UPDATE')) or " + + "(count(../../VXLAN_TUNNEL_MAP_LIST[vni=current()]) = 1)" { + error-app-tag not-unique-vni; + } + } + } + } + + container EVPN_NVO { + + list EVPN_NVO_LIST { + + key "name"; + max-elements 1; + + leaf name { + type string; + } + + leaf source_vtep { + mandatory true; + type leafref { + path "../../../VXLAN_TUNNEL/VXLAN_TUNNEL_LIST/name"; + } + } + } + } + container SUPPRESS_VLAN_NEIGH { + + list SUPPRESS_VLAN_NEIGH_LIST { + key "name"; + + leaf name { + type string; + } + leaf suppress { + type string; + } + + } + } + container VXLAN_TUNNEL_TABLE { + sonic-ext:db-name "STATE_DB"; + sonic-ext:key-delim ":"; + + config false; + description "state db vxlan tunnel table"; + + list VXLAN_TUNNEL_TABLE_LIST { + key "name"; + + leaf name { + type string; + } + leaf src_ip { + type string; + } + leaf dst_ip { + type string; + } + leaf tnl_src { + type string; + } + leaf operstatus { + type string; + } + } + } + container EVPN_REMOTE_VNI_TABLE { + sonic-ext:db-name "APPL_DB"; + sonic-ext:key-delim ":"; + + config false; + description "app db vxlan remote vni table"; + + list EVPN_REMOTE_VNI_TABLE_LIST { + key "vlan remote_vtep"; + + leaf vlan { + type leafref { + path "/svlan:sonic-vlan/svlan:VLAN/svlan:VLAN_LIST/svlan:name"; + } + } + leaf remote_vtep { + type inet:ipv4-address; + } + leaf vni { + type string; + } + } + } + container VXLAN_FDB_TABLE{ + sonic-ext:db-name "APPL_DB"; + sonic-ext:key-delim ":"; + + config false; + description "app db vxlan fdb table"; + + list VXLAN_FDB_TABLE_LIST { + key "vlan mac_addr"; + + leaf vlan { + type leafref { + path "/svlan:sonic-vlan/svlan:VLAN/svlan:VLAN_LIST/svlan:name"; + } + } + leaf mac_addr { + type yang:mac-address; + } + leaf remote_vtep { + type inet:ipv4-address; + } + leaf type { + type string; + } + leaf vni { + type string; + } + } + } + } +} diff --git a/cvl/tests/acl_rule.json b/cvl/tests/acl_rule.json index c26cdee0e568..cb8bf4e935fd 100644 --- a/cvl/tests/acl_rule.json +++ b/cvl/tests/acl_rule.json @@ -9,13 +9,11 @@ "TestACL11|Rule1": { "PRIORITY": "55", "PACKET_ACTION": "DROP", - "IP_TYPE" : "ANY", "L4_SRC_PORT": "0" }, "TestACL11|Rule2": { "PRIORITY": "55", "PACKET_ACTION": "DROP", - "IP_TYPE" : "ANY", "L4_SRC_PORT": "1" } }