From 4bb02f5acbba49f2cd7be7f34583968a5ce30f8a Mon Sep 17 00:00:00 2001 From: Dong Zhang <41927498+dzhangalibaba@users.noreply.github.com> Date: Thu, 21 Nov 2019 22:52:11 -0800 Subject: [PATCH] [multi-DB] Part 5: Golang API changes and replacement (#24) * [multi-DB] Part 5: Golang API changes and replacement * try sudo * fix build issue which not using committed codes * add GetSeparator and dynamiclly update Target_value/name * fix typo * using GetDbId() * using go modules to manage local pkgs --- Makefile | 14 +- dialout/dialout_client/dialout_client.go | 11 +- dialout/dialout_client/dialout_client_test.go | 16 +-- gnmi_server/server_test.go | 16 +-- proto/sonic.pb.go | 41 ++---- sonic_data_client/db_client.go | 17 +-- sonic_db_config/db_config.go | 135 ++++++++++++++++++ testdata/database_config.json | 57 ++++++++ 8 files changed, 237 insertions(+), 70 deletions(-) create mode 100644 sonic_db_config/db_config.go create mode 100644 testdata/database_config.json diff --git a/Makefile b/Makefile index 6a51f9324..79856774a 100644 --- a/Makefile +++ b/Makefile @@ -3,16 +3,20 @@ export GOPATH=/tmp/go endif INSTALL := /usr/bin/install +DBDIR := /var/run/redis/sonic-db/ all: sonic-telemetry sonic-telemetry: - /usr/local/go/bin/go get -v github.com/Azure/sonic-telemetry/telemetry - /usr/local/go/bin/go get -v github.com/Azure/sonic-telemetry/dialout/dialout_client_cli + /usr/local/go/bin/go mod init github.com/Azure/sonic-telemetry + /usr/local/go/bin/go install github.com/Azure/sonic-telemetry/telemetry + /usr/local/go/bin/go install github.com/Azure/sonic-telemetry/dialout/dialout_client_cli check: - /usr/local/go/bin/go get -v -t github.com/Azure/sonic-telemetry/gnmi_server/... - /usr/local/go/bin/go test -v ${GOPATH}/src/github.com/Azure/sonic-telemetry/gnmi_server + sudo mkdir -p ${DBDIR} + sudo cp ./testdata/database_config.json ${DBDIR} + /usr/local/go/bin/go test -v github.com/Azure/sonic-telemetry/gnmi_server + /usr/local/go/bin/go test -v github.com/Azure/sonic-telemetry/dialout/dialout_client install: $(INSTALL) -D ${GOPATH}/bin/telemetry $(DESTDIR)/usr/sbin/telemetry @@ -24,4 +28,6 @@ deinstall: clean: rm -fr ${GOPATH} + rm -rf go.mod + rm -rf go.sum diff --git a/dialout/dialout_client/dialout_client.go b/dialout/dialout_client/dialout_client.go index f530a0f21..66280915a 100644 --- a/dialout/dialout_client/dialout_client.go +++ b/dialout/dialout_client/dialout_client.go @@ -7,6 +7,7 @@ import ( "fmt" spb "github.com/Azure/sonic-telemetry/proto" sdc "github.com/Azure/sonic-telemetry/sonic_data_client" + sdcfg "github.com/Azure/sonic-telemetry/sonic_db_config" "github.com/go-redis/redis" log "github.com/golang/glog" gpb "github.com/openconfig/gnmi/proto/gnmi" @@ -641,23 +642,23 @@ func processTelemetryClientConfig(ctx context.Context, redisDb *redis.Client, ke // read configDB data for telemetry client and start publishing service for client subscription func DialOutRun(ctx context.Context, ccfg *ClientConfig) error { clientCfg = ccfg - dbn := spb.Target_value["CONFIG_DB"] + dbn := sdcfg.GetDbId("CONFIG_DB") var redisDb *redis.Client if sdc.UseRedisLocalTcpPort == false { redisDb = redis.NewClient(&redis.Options{ Network: "unix", - Addr: sdc.Default_REDIS_UNIXSOCKET, + Addr: sdcfg.GetDbSock("CONFIG_DB"), Password: "", // no password set - DB: int(dbn), + DB: dbn, DialTimeout: 0, }) } else { redisDb = redis.NewClient(&redis.Options{ Network: "tcp", - Addr: sdc.Default_REDIS_LOCAL_TCP_PORT, + Addr: sdcfg.GetDbTcpAddr("CONFIG_DB"), Password: "", // no password set - DB: int(dbn), + DB: dbn, DialTimeout: 0, }) } diff --git a/dialout/dialout_client/dialout_client_test.go b/dialout/dialout_client/dialout_client_test.go index b94963be0..1a8b76ebd 100644 --- a/dialout/dialout_client/dialout_client_test.go +++ b/dialout/dialout_client/dialout_client_test.go @@ -29,8 +29,8 @@ import ( "time" sds "github.com/Azure/sonic-telemetry/dialout/dialout_server" - spb "github.com/Azure/sonic-telemetry/proto" sdc "github.com/Azure/sonic-telemetry/sonic_data_client" + sdcfg "github.com/Azure/sonic-telemetry/sonic_db_config" gclient "github.com/openconfig/gnmi/client/gnmi" ) @@ -95,12 +95,11 @@ func runServer(t *testing.T, s *sds.Server) { } func getRedisClient(t *testing.T) *redis.Client { - dbn := spb.Target_value["COUNTERS_DB"] rclient := redis.NewClient(&redis.Options{ Network: "tcp", - Addr: "localhost:6379", + Addr: sdcfg.GetDbTcpAddr("COUNTERS_DB"), Password: "", // no password set - DB: int(dbn), + DB: sdcfg.GetDbId("COUNTERS_DB"), DialTimeout: 0, }) _, err := rclient.Ping().Result() @@ -125,12 +124,11 @@ func exe_cmd(t *testing.T, cmd string) { } func getConfigDbClient(t *testing.T) *redis.Client { - dbn := spb.Target_value["CONFIG_DB"] rclient := redis.NewClient(&redis.Options{ Network: "tcp", - Addr: "localhost:6379", + Addr: sdcfg.GetDbTcpAddr("CONFIG_DB"), Password: "", // no password set - DB: int(dbn), + DB: sdcfg.GetDbId("CONFIG_DB"), DialTimeout: 0, }) _, err := rclient.Ping().Result() @@ -157,7 +155,7 @@ func loadConfigDB(t *testing.T, rclient *redis.Client, mpi map[string]interface{ func prepareConfigDb(t *testing.T) { rclient := getConfigDbClient(t) defer rclient.Close() - rclient.FlushDb() + rclient.FlushDB() fileName := "../../testdata/COUNTERS_PORT_ALIAS_MAP.txt" countersPortAliasMapByte, err := ioutil.ReadFile(fileName) @@ -179,7 +177,7 @@ func prepareConfigDb(t *testing.T) { func prepareDb(t *testing.T) { rclient := getRedisClient(t) defer rclient.Close() - rclient.FlushDb() + rclient.FlushDB() //Enable keysapce notification os.Setenv("PATH", "$PATH:/usr/bin:/sbin:/bin:/usr/local/bin:/usr/local/Cellar/redis/4.0.8/bin") cmd := exec.Command("redis-cli", "config", "set", "notify-keyspace-events", "KEA") diff --git a/gnmi_server/server_test.go b/gnmi_server/server_test.go index 262d12017..1fcb4920c 100644 --- a/gnmi_server/server_test.go +++ b/gnmi_server/server_test.go @@ -26,8 +26,8 @@ import ( "testing" "time" // Register supported client types. - spb "github.com/Azure/sonic-telemetry/proto" sdc "github.com/Azure/sonic-telemetry/sonic_data_client" + sdcfg "github.com/Azure/sonic-telemetry/sonic_db_config" gclient "github.com/jipanyang/gnmi/client/gnmi" ) @@ -155,12 +155,11 @@ func runServer(t *testing.T, s *Server) { } func getRedisClient(t *testing.T) *redis.Client { - dbn := spb.Target_value["COUNTERS_DB"] rclient := redis.NewClient(&redis.Options{ Network: "tcp", - Addr: "localhost:6379", + Addr: sdcfg.GetDbTcpAddr("COUNTERS_DB"), Password: "", // no password set - DB: int(dbn), + DB: sdcfg.GetDbId("COUNTERS_DB"), DialTimeout: 0, }) _, err := rclient.Ping().Result() @@ -171,12 +170,11 @@ func getRedisClient(t *testing.T) *redis.Client { } func getConfigDbClient(t *testing.T) *redis.Client { - dbn := spb.Target_value["CONFIG_DB"] rclient := redis.NewClient(&redis.Options{ Network: "tcp", - Addr: "localhost:6379", + Addr: sdcfg.GetDbTcpAddr("CONFIG_DB"), Password: "", // no password set - DB: int(dbn), + DB: sdcfg.GetDbId("CONFIG_DB"), DialTimeout: 0, }) _, err := rclient.Ping().Result() @@ -203,7 +201,7 @@ func loadConfigDB(t *testing.T, rclient *redis.Client, mpi map[string]interface{ func prepareConfigDb(t *testing.T) { rclient := getConfigDbClient(t) defer rclient.Close() - rclient.FlushDb() + rclient.FlushDB() fileName := "../testdata/COUNTERS_PORT_ALIAS_MAP.txt" countersPortAliasMapByte, err := ioutil.ReadFile(fileName) @@ -225,7 +223,7 @@ func prepareConfigDb(t *testing.T) { func prepareDb(t *testing.T) { rclient := getRedisClient(t) defer rclient.Close() - rclient.FlushDb() + rclient.FlushDB() //Enable keysapce notification os.Setenv("PATH", "/usr/bin:/sbin:/bin:/usr/local/bin") cmd := exec.Command("redis-cli", "config", "set", "notify-keyspace-events", "KEA") diff --git a/proto/sonic.pb.go b/proto/sonic.pb.go index 5004a6204..d2af6b680 100644 --- a/proto/sonic.pb.go +++ b/proto/sonic.pb.go @@ -7,6 +7,7 @@ import proto "github.com/golang/protobuf/proto" import fmt "fmt" import math "math" import _ "github.com/openconfig/gnmi/proto/gnmi" +import sdcfg "github.com/Azure/sonic-telemetry/sonic_db_config" // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -16,40 +17,10 @@ var _ = math.Inf // target - the name of the target for which the path is a member. Only set in prefix for a path. type Target int32 -const ( - Target_APPL_DB Target = 0 - Target_ASIC_DB Target = 1 - Target_COUNTERS_DB Target = 2 - Target_LOGLEVEL_DB Target = 3 - Target_CONFIG_DB Target = 4 - // PFC_WD_DB shares the the same db number with FLEX_COUNTER_DB - Target_PFC_WD_DB Target = 5 - Target_FLEX_COUNTER_DB Target = 5 - Target_STATE_DB Target = 6 - // For none-DB data - Target_OTHERS Target = 100 -) - var Target_name = map[int32]string{ - 0: "APPL_DB", - 1: "ASIC_DB", - 2: "COUNTERS_DB", - 3: "LOGLEVEL_DB", - 4: "CONFIG_DB", - 5: "PFC_WD_DB", - // Duplicate value: 5: "FLEX_COUNTER_DB", - 6: "STATE_DB", 100: "OTHERS", } var Target_value = map[string]int32{ - "APPL_DB": 0, - "ASIC_DB": 1, - "COUNTERS_DB": 2, - "LOGLEVEL_DB": 3, - "CONFIG_DB": 4, - "PFC_WD_DB": 5, - "FLEX_COUNTER_DB": 5, - "STATE_DB": 6, "OTHERS": 100, } @@ -58,7 +29,17 @@ func (x Target) String() string { } func (Target) EnumDescriptor() ([]byte, []int) { return fileDescriptor1, []int{0} } +func setTarget() { + db_list := sdcfg.GetDbList() + for dbname, v := range db_list { + id := v.(map[string]interface{})["id"].(float64) + Target_value[dbname] = int32(id) + Target_name[int32(id)] = dbname + } +} + func init() { + setTarget() proto.RegisterEnum("gnmi.sonic.Target", Target_name, Target_value) } diff --git a/sonic_data_client/db_client.go b/sonic_data_client/db_client.go index f2b93a5b0..28e109661 100644 --- a/sonic_data_client/db_client.go +++ b/sonic_data_client/db_client.go @@ -15,6 +15,7 @@ import ( log "github.com/golang/glog" spb "github.com/Azure/sonic-telemetry/proto" + sdcfg "github.com/Azure/sonic-telemetry/sonic_db_config" "github.com/go-redis/redis" gnmipb "github.com/openconfig/gnmi/proto/gnmi" "github.com/workiva/go-datastructures/queue" @@ -24,8 +25,6 @@ const ( // indentString represents the default indentation string used for // JSON. Two spaces are used here. indentString string = " " - Default_REDIS_UNIXSOCKET string = "/var/run/redis/redis.sock" - Default_REDIS_LOCAL_TCP_PORT string = "localhost:6379" ) // Client defines a set of methods which every client must implement. @@ -304,15 +303,7 @@ func GetTableKeySeparator(target string) (string, error) { return "", fmt.Errorf("%v not a valid path target", target) } - var separator string - switch target { - case "CONFIG_DB": - separator = "|" - case "STATE_DB": - separator = "|" - default: - separator = ":" - } + var separator string = sdcfg.GetDbSeparator(target) return separator, nil } @@ -325,7 +316,7 @@ func useRedisTcpClient() { if UseRedisLocalTcpPort { redisDb = redis.NewClient(&redis.Options{ Network: "tcp", - Addr: Default_REDIS_LOCAL_TCP_PORT, + Addr: sdcfg.GetDbTcpAddr(dbName), Password: "", // no password set DB: int(dbn), DialTimeout: 0, @@ -345,7 +336,7 @@ func init() { redisDb = redis.NewClient(&redis.Options{ Network: "unix", - Addr: Default_REDIS_UNIXSOCKET, + Addr: sdcfg.GetDbSock(dbName), Password: "", // no password set DB: int(dbn), DialTimeout: 0, diff --git a/sonic_db_config/db_config.go b/sonic_db_config/db_config.go new file mode 100644 index 000000000..c097359bc --- /dev/null +++ b/sonic_db_config/db_config.go @@ -0,0 +1,135 @@ +// Package dbconfig provides a generic functions for parsing sonic database config file in system +package dbconfig + +import ( + "encoding/json" + "fmt" + "strconv" + io "io/ioutil" +) + +const ( + SONIC_DB_CONFIG_FILE string = "/var/run/redis/sonic-db/database_config.json" +) + +var sonic_db_config = make(map[string]interface{}) +var sonic_db_init bool + +func GetDbList()(map[string]interface{}) { + if !sonic_db_init { + DbInit() + } + db_list, ok := sonic_db_config["DATABASES"].(map[string]interface{}) + if !ok { + panic(fmt.Errorf("DATABASES' is not valid key in database_config.json file!")) + } + return db_list +} + +func GetDbInst(db_name string)(map[string]interface{}) { + if !sonic_db_init { + DbInit() + } + db, ok := sonic_db_config["DATABASES"].(map[string]interface{})[db_name] + if !ok { + panic(fmt.Errorf("database name '%v' is not valid in database_config.json file!", db_name)) + } + inst_name, ok := db.(map[string]interface{})["instance"] + if !ok { + panic(fmt.Errorf("'instance' is not a valid field in database_config.json 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 database_config.json file!", inst_name)) + } + return inst.(map[string]interface{}) +} + +func GetDbSeparator(db_name string)(string) { + if !sonic_db_init { + DbInit() + } + db_list := GetDbList() + separator, ok := db_list[db_name].(map[string]interface{})["separator"] + if !ok { + panic(fmt.Errorf("'separator' is not a valid field in database_config.json file!")) + } + return separator.(string) +} + +func GetDbId(db_name string)(int) { + if !sonic_db_init { + DbInit() + } + db_list := GetDbList() + id, ok := db_list[db_name].(map[string]interface{})["id"] + if !ok { + panic(fmt.Errorf("'id' is not a valid field in database_config.json file!")) + } + return int(id.(float64)) +} + +func GetDbSock(db_name string)(string) { + if !sonic_db_init { + DbInit() + } + inst := GetDbInst(db_name) + unix_socket_path, ok := inst["unix_socket_path"] + if !ok { + panic(fmt.Errorf("'unix_socket_path' is not a valid field in database_config.json file!")) + } + return unix_socket_path.(string) +} + +func GetDbHostName(db_name string)(string) { + if !sonic_db_init { + DbInit() + } + inst := GetDbInst(db_name) + hostname, ok := inst["hostname"] + if !ok { + panic(fmt.Errorf("'hostname' is not a valid field in database_config.json file!")) + } + return hostname.(string) +} + +func GetDbPort(db_name string)(int) { + if !sonic_db_init { + DbInit() + } + inst := GetDbInst(db_name) + port, ok := inst["port"] + if !ok { + panic(fmt.Errorf("'port' is not a valid field in database_config.json file!")) + } + return int(port.(float64)) +} + +func GetDbTcpAddr(db_name string)(string) { + if !sonic_db_init { + DbInit() + } + hostname := GetDbHostName(db_name) + port := GetDbPort(db_name) + return hostname + ":" + strconv.Itoa(port) +} + +func DbInit() { + if sonic_db_init { + return + } + data, err := io.ReadFile(SONIC_DB_CONFIG_FILE) + if err != nil { + panic(err) + } else { + err = json.Unmarshal([]byte(data), &sonic_db_config) + if err != nil { + panic(err) + } + sonic_db_init = true + } +} + +func init() { + sonic_db_init = false +} diff --git a/testdata/database_config.json b/testdata/database_config.json new file mode 100644 index 000000000..b86ae11bb --- /dev/null +++ b/testdata/database_config.json @@ -0,0 +1,57 @@ +{ + "INSTANCES": { + "redis":{ + "hostname" : "127.0.0.1", + "port" : 6379, + "unix_socket_path" : "/var/run/redis/redis.sock" + } + }, + "DATABASES" : { + "APPL_DB" : { + "id" : 0, + "separator": ":", + "instance" : "redis" + }, + "ASIC_DB" : { + "id" : 1, + "separator": ":", + "instance" : "redis" + }, + "COUNTERS_DB" : { + "id" : 2, + "separator": ":", + "instance" : "redis" + }, + "LOGLEVEL_DB" : { + "id" : 3, + "separator": ":", + "instance" : "redis" + }, + "CONFIG_DB" : { + "id" : 4, + "separator": "|", + "instance" : "redis" + }, + "PFC_WD_DB" : { + "id" : 5, + "separator": ":", + "instance" : "redis" + }, + "FLEX_COUNTER_DB" : { + "id" : 5, + "separator": ":", + "instance" : "redis" + }, + "STATE_DB" : { + "id" : 6, + "separator": "|", + "instance" : "redis" + }, + "SNMP_OVERLAY_DB" : { + "id" : 7, + "separator": "|", + "instance" : "redis" + } + }, + "VERSION" : "1.0" +}