Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CVL Changes #3: Multi-db instance support #20

Merged
merged 6 commits into from
Sep 30, 2020
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
18 changes: 7 additions & 11 deletions cvl/cvl_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ package cvl
import (
"fmt"
"encoding/json"
"github.com/go-redis/redis"
"path/filepath"
"github.com/Azure/sonic-mgmt-common/cvl/internal/yparser"
. "github.com/Azure/sonic-mgmt-common/cvl/internal/util"
Expand Down Expand Up @@ -135,6 +134,13 @@ func Initialize() CVLRetCode {
return CVL_SUCCESS
}

//Initialize redis Client
redisClient = NewDbClient("CONFIG_DB")

if (redisClient == nil) {
CVL_LOG(FATAL, "Unable to connect to Redis Config DB Server")
return CVL_ERROR
}
//Scan schema directory to get all schema files
modelFiles, err := filepath.Glob(CVL_SCHEMA + "/*.yin")
if err != nil {
Expand Down Expand Up @@ -169,16 +175,6 @@ func Initialize() CVLRetCode {

//Initialize redis Client

redisClient = redis.NewClient(&redis.Options{
Addr: ":6379",
Password: "", // no password set
DB: int(CONFIG_DB), // use APP DB
})

if (redisClient == nil) {
CVL_LOG(FATAL, "Unable to connect with Redis Config DB")
return CVL_ERROR
}

//Load lua script into redis
loadLuaScript()
Expand Down
15 changes: 5 additions & 10 deletions cvl/cvl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,17 +196,12 @@ func compareErrorDetails(cvlErr cvl.CVLErrorInfo, expCode cvl.CVLRetCode, errApp
}

func getConfigDbClient() *redis.Client {
rclient := redis.NewClient(&redis.Options{
Network: "tcp",
Addr: "localhost:6379",
Password: "", // no password set
DB: 4,
DialTimeout: 0,
})
_, err := rclient.Ping().Result()
if err != nil {
fmt.Printf("failed to connect to redis server %v", err)
rclient := NewDbClient("CONFIG_DB")

if rclient == nil {
panic("Unable to connect to Redis Config DB Server")
}

return rclient
}

Expand Down
188 changes: 188 additions & 0 deletions cvl/internal/util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,15 @@ import (
"syscall"
"strings"
"flag"
"github.com/go-redis/redis"
log "github.com/golang/glog"
)

var CVL_SCHEMA string = "/usr/sbin/schema/"
var CVL_CFG_FILE string = "/usr/sbin/cvl_cfg.json"
const SONIC_DB_CONFIG_FILE string = "/var/run/redis/sonic-db/database_config.json"
const ENV_VAR_SONIC_DB_CONFIG_FILE = "DB_CONFIG_PATH"
var sonic_db_config = make(map[string]interface{})

//package init function
func init() {
Expand All @@ -44,6 +48,9 @@ func init() {
if (os.Getenv("CVL_CFG_FILE") != "") {
CVL_CFG_FILE = os.Getenv("CVL_CFG_FILE")
}

//Initialize DB settings
dbCfgInit()
}

var cvlCfgMap map[string]string
Expand Down Expand Up @@ -253,3 +260,184 @@ func SkipSemanticValidation() bool {

return false
}

//Function to read Redis DB configuration from file.
//In absence of the file, it uses default config for CONFIG_DB
//so that CVL UT will pass in development environment.
func dbCfgInit() {
defaultDBConfig := `{

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use sonic_db_config/db_config.go ?

We could move that to here and update as needed. So we can have all the APIs that parse and use this file, in one place.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thought of so, but wanted keep CVL as independent of telemetry package.

Are you suggesting to import "sonic-telemetry/dbconfig" in this file and create a wrapper layer for any additional logic ?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better to keep the common stuff in one place, so everyone shares the updated code.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renuka, we cannot do that as telemetry code today is dependent on mgmt-common and CVL resides inside the mgmt-common. We need to do the other way around all the telemetry DB access stuff move to mgmt-common to make everything reside in a common place.

"INSTANCES": {
"redis":{
"hostname" : "127.0.0.1",
"port" : 6379
}
},
"DATABASES" : {
"CONFIG_DB" : {
"id" : 4,
"separator": "|",
"instance" : "redis"
},
"STATE_DB" : {
"id" : 6,
"separator": "|",
"instance" : "redis"
}
}
}`

dbCfgFile := ""

//Check if multi-db config file is present
if _, errF := os.Stat(SONIC_DB_CONFIG_FILE); !os.IsNotExist(errF) {
dbCfgFile = SONIC_DB_CONFIG_FILE
} else {
//Check if multi-db config file is specified in environment
if fileName := os.Getenv(ENV_VAR_SONIC_DB_CONFIG_FILE); fileName != "" {
if _, errF := os.Stat(fileName); !os.IsNotExist(errF) {
dbCfgFile = fileName
}
}
}

if dbCfgFile != "" {
//Read from multi-db config file
data, err := ioutil.ReadFile(dbCfgFile)
if err != nil {
panic(err)
} else {
err = json.Unmarshal([]byte(data), &sonic_db_config)
if err != nil {
panic(err)
}
}
} else {
//No multi-db config file is present.
//Use default config for CONFIG_DB setting, this avoids CVL UT failure
//in absence of at multi-db config file
err := json.Unmarshal([]byte(defaultDBConfig), &sonic_db_config)
if err != nil {
panic(err)
}
}
}

//Get list of DB
func getDbList()(map[string]interface{}) {
db_list, ok := sonic_db_config["DATABASES"].(map[string]interface{})
if !ok {
panic(fmt.Errorf("DATABASES' is not valid key in %s!",
SONIC_DB_CONFIG_FILE))
}
return db_list
}

//Get DB instance based on given DB name
func getDbInst(dbName string)(map[string]interface{}) {
db, ok := sonic_db_config["DATABASES"].(map[string]interface{})[dbName]
if !ok {
panic(fmt.Errorf("database name '%v' is not valid in %s !",
dbName, SONIC_DB_CONFIG_FILE))
}
inst_name, ok := db.(map[string]interface{})["instance"]
if !ok {
panic(fmt.Errorf("'instance' is not a valid field in %s !",
SONIC_DB_CONFIG_FILE))
}
inst, ok := sonic_db_config["INSTANCES"].(map[string]interface{})[inst_name.(string)]
if !ok {
panic(fmt.Errorf("instance name '%v' is not valid in %s !",
inst_name, SONIC_DB_CONFIG_FILE))
}
return inst.(map[string]interface{})
}

//GetDbSeparator Get DB separator based on given DB name
func GetDbSeparator(dbName string)(string) {
db_list := getDbList()
separator, ok := db_list[dbName].(map[string]interface{})["separator"]
if !ok {
panic(fmt.Errorf("'separator' is not a valid field in %s !",
SONIC_DB_CONFIG_FILE))
}
return separator.(string)
}

//GetDbId Get DB id on given db name
func GetDbId(dbName string)(int) {
db_list := getDbList()
id, ok := db_list[dbName].(map[string]interface{})["id"]
if !ok {
panic(fmt.Errorf("'id' is not a valid field in %s !",
SONIC_DB_CONFIG_FILE))
}
return int(id.(float64))
}

//GetDbSock Get DB socket path
func GetDbSock(dbName string)(string) {
inst := getDbInst(dbName)
unix_socket_path, ok := inst["unix_socket_path"]
if !ok {
CVL_LEVEL_LOG(INFO, "'unix_socket_path' is not " +
"a valid field in %s !", SONIC_DB_CONFIG_FILE)

return ""
}

return unix_socket_path.(string)
}

//GetDbTcpAddr Get DB TCP endpoint
func GetDbTcpAddr(dbName string)(string) {
inst := getDbInst(dbName)
hostname, ok := inst["hostname"]
if !ok {
panic(fmt.Errorf("'hostname' is not a valid field in %s !",
SONIC_DB_CONFIG_FILE))
}

port, ok1 := inst["port"]
if !ok1 {
panic(fmt.Errorf("'port' is not a valid field in %s !",
SONIC_DB_CONFIG_FILE))
}

return fmt.Sprintf("%v:%v", hostname, port)
}

//NewDbClient Get new redis client
func NewDbClient(dbName string) *redis.Client {
var redisClient *redis.Client = nil

//Try unix domain socket first
if dbSock := GetDbSock(dbName); dbSock != "" {
redisClient = redis.NewClient(&redis.Options{
Network: "unix",
Addr: dbSock,
Password: "",
DB: GetDbId(dbName),
})
} else {
//Otherwise, use TCP socket
redisClient = redis.NewClient(&redis.Options{
Network: "tcp",
Addr: GetDbTcpAddr(dbName),
Password: "",
DB: GetDbId(dbName),
})
}

if (redisClient == nil) {
return nil
}

//Check the connectivity
_, err := redisClient.Ping().Result()
if err != nil {
CVL_LEVEL_LOG(ERROR, "Failed to connect to Redis server %v", err)
return nil
}

return redisClient
}