Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ __pycache__
*.yin
*.tree
translib/ocbinds/ocbinds.go
models/yang/*.md
.idea
6 changes: 4 additions & 2 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,10 @@ stages:
STATUS=0
DEBDIR=$(realpath debian/sonic-mgmt-common)

[[ -f tools/test/database_config.json ]] && \
export DB_CONFIG_PATH=${PWD}/tools/test/database_config.json
# Update unixsocket path in database_config.json
tools/test/dbconfig.py -o build/tests/database_config.json
export DB_CONFIG_PATH=${PWD}/build/tests/database_config.json

# Run CVL tests

pushd build/tests/cvl
Expand Down
65 changes: 41 additions & 24 deletions cvl/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -18,76 +18,93 @@
################################################################################

GO?=go
TOPDIR ?= ..
BUILD_DIR:=$(TOPDIR)/build/cvl
FORMAT_CHECK = $(BUILD_DIR)/.formatcheck
SRC_FILES=$(shell find . -name '*.go' | grep -v '_test.go' | grep -v '/tests/')
TEST_FILES=$(wildcard *_test.go)
TOP_DIR := ..
BUILD_DIR:=$(TOP_DIR)/build/cvl
FORMAT_CHECK = $(BUILD_DIR)/.formatcheck
CVL_TEST_DIR = $(TOPDIR)/build/tests/cvl
CVL_TEST_BIN = $(CVL_TEST_DIR)/cvl.test
CVL_TEST_SCHEMA_DIR = $(CVL_TEST_DIR)/testdata/schema
CVL_TEST_CONFIG = $(CVL_TEST_DIR)/cvl_cfg.json
CVL_TEST_DB_CONFIG = $(CVL_TEST_DIR)/database_config.json

CVL_SCHEMA_DIR = $(BUILD_DIR)/schema
CVL_SCHEMA = $(CVL_SCHEMA_DIR)/.done
SONIC_YANG_DIR = $(TOP_DIR)/build/yang/sonic
CVL_SCHEMA = $(CVL_SCHEMA_DIR)/.done
SONIC_YANG_DIR = $(TOPDIR)/build/yang/sonic
SONIC_YANG_FILES = $(shell find $(SONIC_YANG_DIR) -name '*.yang')
YANG_SRC_DIR = ../models/yang

CVL_TEST_DIR = $(TOP_DIR)/build/tests/cvl
CVL_TEST_BIN = $(CVL_TEST_DIR)/cvl.test
CVL_TEST_SCHEMA_DIR = $(CVL_TEST_DIR)/testdata/schema
CVL_TEST_SCHEMA = $(CVL_TEST_SCHEMA_DIR)/.done
CVL_TEST_YANGS = $(wildcard testdata/schema/*.yang)
CVL_TEST_YANGS += $(wildcard $(YANG_SRC_DIR)/sonic/common/*.yang)
SONIC_YANG_COMMON := $(TOPDIR)/models/yang/sonic/common
CVL_TEST_SCHEMA := $(CVL_TEST_SCHEMA_DIR)/.done
CVL_TEST_YANGS = $(shell find testdata/schema -name '*.yang')
CVL_TEST_YANGS += $(wildcard $(SONIC_YANG_COMMON)/*.yang)

DEFAULT_TARGETS = $(CVL_SCHEMA) $(FORMAT_CHECK)
ifdef DEBUG
GOFLAGS += -gcflags="all=-N -l"
endif

ifeq ($(NO_TEST_BINS),)
DEFAULT_TARGETS += $(CVL_TEST_BIN)
DEFAULT_TARGETS += $(CVL_TEST_BIN) $(CVL_TEST_SCHEMA)
endif

all: $(DEFAULT_TARGETS)

.SECONDEXPANSION:

.PRECIOUS: %/.
%/.:
mkdir -p $@

$(CVL_TEST_BIN): $(TEST_FILES) $(SRC_FILES) $(CVL_TEST_SCHEMA)
cp -r testdata/*.json $(@D)/testdata
$(GO) test -mod=vendor -cover -coverpkg=../cvl,../cvl/internal/util,../cvl/internal/yparser -c ../cvl -o $@
.SECONDEXPANSION:

.PHONY: schema
schema: $(CVL_SCHEMA)

$(CVL_SCHEMA): $(SONIC_YANG_FILES) | $$(@D)/.
$(TOP_DIR)/tools/pyang/generate_yin.py \
tools/generate_yin.py \
--path=$(SONIC_YANG_DIR) \
--path=$(YANG_SRC_DIR)/common \
--out-dir=$(@D)
touch $@

$(CVL_TEST_BIN): $(TEST_FILES) $(SRC_FILES) | $$(@D)/testdata/.
cp -r testdata/*.json $(@D)/testdata
$(GO) test -mod=vendor -tags=test -cover -coverpkg=../cvl,../cvl/internal/util,../cvl/internal/yparser -c ../cvl -o $@

.PHONY: test-schema
test-schema: $(CVL_TEST_SCHEMA)

$(CVL_TEST_SCHEMA): $(CVL_TEST_YANGS) | $$(@D)/.
$(TOP_DIR)/tools/pyang/generate_yin.py \
tools/generate_yin.py \
--path=testdata/schema \
--path=$(YANG_SRC_DIR)/common \
--path=$(YANG_SRC_DIR)/sonic/common \
--out-dir=$(@D)
touch $@

gotest: $(CVL_TEST_SCHEMA)
CVL_CFG_FILE=$(abspath .)/conf/cvl_cfg.json \
CVL_SCHEMA_PATH=$(abspath $(CVL_TEST_SCHEMA_DIR)) \
$(CVL_TEST_CONFIG): conf/cvl_cfg.json
sed -E 's/((TRACE|LOG).*)\"false\"/\1\"true\"/' conf/cvl_cfg.json > $@

$(CVL_TEST_DB_CONFIG): $(TOPDIR)/tools/test/database_config.json
$(TOPDIR)/tools/test/dbconfig.py -o $@

gotest: $(CVL_TEST_SCHEMA) $(CVL_TEST_CONFIG) $(CVL_TEST_DB_CONFIG)
CVL_CFG_FILE=$(abspath $(CVL_TEST_CONFIG)) \
CVL_SCHEMA_PATH=$(CVL_TEST_SCHEMA_DIR) \
DB_CONFIG_PATH=$(abspath $(CVL_TEST_DB_CONFIG)) \
tests/run_test.sh

$(FORMAT_CHECK): $(SRC_FILES) $(TEST_FILES) | $$(@D)/.
$(TOP_DIR)/tools/test/format-check.sh \
$(TOPDIR)/tools/test/format-check.sh \
--log=$(@D)/formatcheck.log \
$?
touch $@

clean:
$(RM) -r $(CVL_TEST_DIR) $(BUILD_DIR)
make -C tests clean
$(RM) -r $(BUILD_DIR)
$(RM) -r $(wildcard $(PKG_BUILD_DIR)/*/cvl)
$(RM) -r $(CVL_TEST_DIR)

cleanall:clean

64 changes: 64 additions & 0 deletions cvl/common/db_utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package common

// KeyMatch checks if the value matches a key pattern.
// vIndex and pIndex are start positions of value and pattern strings to match.
// Mimics redis pattern matcher - i.e, glob like pattern matcher which
// matches '/' against wildcard.
// Supports '*' and '?' wildcards with '\' as the escape character.
// '*' matches any char sequence or none; '?' matches exactly one char.
// Character classes are not supported (redis supports it).
func KeyMatch(value, pattern string) bool {
return keyMatch(value, 0, pattern, 0)
}

func keyMatch(value string, vIndex int, pattern string, pIndex int) bool {
for pIndex < len(pattern) {
switch pattern[pIndex] {
case '*':
// Skip successive *'s in the pattern
pIndex++
for pIndex < len(pattern) && pattern[pIndex] == '*' {
pIndex++
}
// Pattern ends with *. Its a match always
if pIndex == len(pattern) {
return true
}
// Try to match remaining pattern with every value substring
for ; vIndex < len(value); vIndex++ {
if keyMatch(value, vIndex, pattern, pIndex) {
return true
}
}
// No match for remaining pattern
return false

case '?':
// Accept any char.. there should be at least one
if vIndex >= len(value) {
return false
}
vIndex++
pIndex++

case '\\':
// Do not treat \ as escape char if it is the last pattern char.
// Redis commands behave this way.
if pIndex+1 < len(pattern) {
pIndex++
}
fallthrough

default:
if vIndex >= len(value) || pattern[pIndex] != value[vIndex] {
return false
}
vIndex++
pIndex++
}
}

// All pattern chars have been compared.
// It is a match if all value chars have been exhausted too.
return (vIndex == len(value))
}
38 changes: 31 additions & 7 deletions cvl/custom_validation/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@
package custom_validation

import (
"fmt"
"reflect"

"github.com/Azure/sonic-mgmt-common/cvl/common"
"github.com/Azure/sonic-mgmt-common/cvl/internal/util"
"github.com/Azure/sonic-mgmt-common/cvl/internal/yparser"
"github.com/antchfx/xmlquery"
"github.com/go-redis/redis/v7"
)

type CustomValidation struct{}
Expand Down Expand Up @@ -81,10 +81,11 @@ const (

// 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}
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}
ReplaceOp bool
}

// CVLErrorInfo CVL Error Structure
Expand All @@ -101,7 +102,8 @@ type CVLErrorInfo struct {
}

type CustValidationCache struct {
Data interface{}
Data map[string]interface{}
Hint map[string]interface{}
}

// CustValidationCtxt Custom validation context passed to custom validation function
Expand All @@ -112,7 +114,7 @@ type CustValidationCtxt struct {
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
RClient common.DBAccess //Db access interface
}

// Search criteria for advanced lookup through DBAccess APIs
Expand All @@ -127,6 +129,8 @@ func InvokeCustomValidation(cv *CustomValidation, name string, args ...interface
}

f := reflect.ValueOf(cv).MethodByName(name)
util.TRACE_LEVEL_LOG(util.TRACE_SEMANTIC,
"customFuncName: %s()", name)
if !f.IsNil() {
v := f.Call(inputs)
util.TRACE_LEVEL_LOG(util.TRACE_SEMANTIC,
Expand All @@ -137,3 +141,23 @@ func InvokeCustomValidation(cv *CustomValidation, name string, args ...interface

return CVLErrorInfo{ErrCode: CVL_SUCCESS}
}

func (err CVLErrorInfo) String() string {
var s string
if CVL_SUCCESS == err.ErrCode {
s = "Success"
} else {
s = fmt.Sprintf("ErrCode[%v]: ErrDetails[%s], Msg[%s], ConstraintErrMsg[%s], Table[%s:%v], Field[%s:%s]", err.ErrCode, err.CVLErrDetails, err.Msg, err.ConstraintErrMsg, err.TableName, err.Keys, err.Field, err.Value)
}

return s
}

func (vc *CustValidationCtxt) ContainsAnyFields(fields ...string) bool {
for _, f := range fields {
if _, ok := vc.CurCfg.Data[f]; ok {
return true
}
}
return false
}
37 changes: 37 additions & 0 deletions cvl/custom_validation/test_validations.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
////////////////////////////////////////////////////////////////////////////////
// //
// 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. //
// //
////////////////////////////////////////////////////////////////////////////////

//go:build test
// +build test

package custom_validation

func (t *CustomValidation) ValidateIfExtraFieldValidationCalled(
vc *CustValidationCtxt) CVLErrorInfo {
vc.SessCache.Hint["ExtraFieldValidationCalled"] = true
return CVLErrorInfo{ErrCode: CVL_SUCCESS}

}

func (t *CustomValidation) ValidateIfListLevelValidationCalled(
vc *CustValidationCtxt) CVLErrorInfo {
vc.SessCache.Hint["ListLevelValidationCalled"] = true
return CVLErrorInfo{ErrCode: CVL_SUCCESS}

}
Loading