From bcf7a04903cba3a63c55a9e5a2f867c5902e55a6 Mon Sep 17 00:00:00 2001 From: Abhishek Dosi Date: Wed, 21 Apr 2021 22:31:16 +0000 Subject: [PATCH 01/13] Enhanced Teleemtry to support multi-asic Signed-off-by: Abhishek Dosi --- gnmi_server/client_subscribe.go | 2 +- gnmi_server/server.go | 16 +- gnmi_server/server_test.go | 11 +- sonic_data_client/db_client.go | 129 +++++++---- sonic_data_client/virtual_db.go | 304 +++++++++++++++----------- sonic_db_config/db_config.go | 370 ++++++++++++++++++++++---------- 6 files changed, 530 insertions(+), 302 deletions(-) diff --git a/gnmi_server/client_subscribe.go b/gnmi_server/client_subscribe.go index c1bf22e0e..7a954d833 100644 --- a/gnmi_server/client_subscribe.go +++ b/gnmi_server/client_subscribe.go @@ -123,7 +123,7 @@ func (c *Client) Run(stream gnmipb.GNMI_SubscribeServer) (err error) { if target == "OTHERS" { dc, err = sdc.NewNonDbClient(paths, prefix) - } else if isTargetDb(target) == true { + } else if _,ok,_,_ := sdc.IsTargetDb(target); ok { dc, err = sdc.NewDbClient(paths, prefix) } else { /* For any other target or no target create new Transl Client. */ diff --git a/gnmi_server/server.go b/gnmi_server/server.go index 4b0a9e365..4f024fe57 100644 --- a/gnmi_server/server.go +++ b/gnmi_server/server.go @@ -1,5 +1,4 @@ package gnmi - import ( "errors" "fmt" @@ -303,7 +302,7 @@ func (s *Server) Get(ctx context.Context, req *gnmipb.GetRequest) (*gnmipb.GetRe if target == "OTHERS" { dc, err = sdc.NewNonDbClient(paths, prefix) - } else if isTargetDb(target) == true { + } else if _,ok,_,_ := sdc.IsTargetDb(target); ok { dc, err = sdc.NewDbClient(paths, prefix) } else { /* If no prefix target is specified create new Transl Data Client . */ @@ -438,17 +437,4 @@ func (s *Server) Capabilities(ctx context.Context, req *gnmipb.CapabilityRequest Extension: exts}, nil } -func isTargetDb ( target string) (bool) { - isDbClient := false - dbTargetSupported := []string { "APPL_DB", "ASIC_DB" , "COUNTERS_DB", "LOGLEVEL_DB", "CONFIG_DB", "PFC_WD_DB", "FLEX_COUNTER_DB", "STATE_DB"} - - for _, name := range dbTargetSupported { - if target == name { - isDbClient = true - return isDbClient - } - } - - return isDbClient -} diff --git a/gnmi_server/server_test.go b/gnmi_server/server_test.go index 07a6c880a..43af96b4c 100644 --- a/gnmi_server/server_test.go +++ b/gnmi_server/server_test.go @@ -2,7 +2,6 @@ package gnmi // server_test covers gNMI get, subscribe (stream and poll) test // Prerequisite: redis-server should be running. - import ( "crypto/tls" "encoding/json" @@ -233,7 +232,7 @@ func runServer(t *testing.T, s *Server) { func getRedisClientN(t *testing.T, n int) *redis.Client { rclient := redis.NewClient(&redis.Options{ Network: "tcp", - Addr: sdcfg.GetDbTcpAddr("COUNTERS_DB"), + Addr: sdcfg.GetDbTcpAddr("COUNTERS_DB", sdcfg.GetDbDefaultNamespace()), Password: "", // no password set DB: n, DialTimeout: 0, @@ -248,9 +247,9 @@ func getRedisClientN(t *testing.T, n int) *redis.Client { func getRedisClient(t *testing.T) *redis.Client { rclient := redis.NewClient(&redis.Options{ Network: "tcp", - Addr: sdcfg.GetDbTcpAddr("COUNTERS_DB"), + Addr: sdcfg.GetDbTcpAddr("COUNTERS_DB", sdcfg.GetDbDefaultNamespace()), Password: "", // no password set - DB: sdcfg.GetDbId("COUNTERS_DB"), + DB: sdcfg.GetDbId("COUNTERS_DB", sdcfg.GetDbDefaultNamespace()), DialTimeout: 0, }) _, err := rclient.Ping().Result() @@ -263,9 +262,9 @@ func getRedisClient(t *testing.T) *redis.Client { func getConfigDbClient(t *testing.T) *redis.Client { rclient := redis.NewClient(&redis.Options{ Network: "tcp", - Addr: sdcfg.GetDbTcpAddr("CONFIG_DB"), + Addr: sdcfg.GetDbTcpAddr("CONFIG_DB", sdcfg.GetDbDefaultNamespace()), Password: "", // no password set - DB: sdcfg.GetDbId("CONFIG_DB"), + DB: sdcfg.GetDbId("CONFIG_DB", sdcfg.GetDbDefaultNamespace()), DialTimeout: 0, }) _, err := rclient.Ping().Result() diff --git a/sonic_data_client/db_client.go b/sonic_data_client/db_client.go index 68fab01e3..6ca7e9055 100644 --- a/sonic_data_client/db_client.go +++ b/sonic_data_client/db_client.go @@ -62,7 +62,7 @@ type Stream interface { var UseRedisLocalTcpPort bool = false // redis client connected to each DB -var Target2RedisDb = make(map[string]*redis.Client) +var Target2RedisDb = make(map[string]map[string]*redis.Client) // MinSampleInterval is the lowest sampling interval for streaming subscriptions. // Any non-zero value that less than this threshold is considered invalid argument. @@ -75,11 +75,12 @@ var IntervalTicker = func(interval time.Duration) <-chan time.Time { } type tablePath struct { - dbName string - tableName string - tableKey string - delimitor string - field string + dbNamespace string + dbName string + tableName string + tableKey string + delimitor string + field string // path name to be used in json data which may be different // from the real data path. Ex. in Counters table, real tableKey // is oid:0x####, while key name like Ethernet## may be put @@ -367,33 +368,66 @@ func ValToResp(val Value) (*gnmipb.SubscribeResponse, error) { } } -func GetTableKeySeparator(target string) (string, error) { +func GetTableKeySeparator(target string, ns string) (string, error) { _, ok := spb.Target_value[target] if !ok { log.V(1).Infof(" %v not a valid path target", target) return "", fmt.Errorf("%v not a valid path target", target) } - var separator string = sdcfg.GetDbSeparator(target) + var separator string = sdcfg.GetDbSeparator(target, ns) return separator, nil } +func GetRedisClientsForDb(target string) (map[string]*redis.Client) { + redis_client_map := make(map[string]*redis.Client) + if sdcfg.CheckDbMultiNamespace() { + ns_list, _ := sdcfg.GetDbNonDefaultNamespaces() + for _, ns := range ns_list { + redis_client_map[ns] = Target2RedisDb[ns][target] + } + + } else { + ns := sdcfg.GetDbDefaultNamespace() + redis_client_map[ns] = Target2RedisDb[ns][target] + } + return redis_client_map +} +func IsTargetDb ( target string) (string, bool, string, bool) { + targetname := strings.Split(target, "/") + dbName:= targetname[0] + dbNameSpaceExist := false + dbNamespace := sdcfg.GetDbDefaultNamespace() + + if len(targetname) > 1 { + dbNamespace = targetname[1] + dbNameSpaceExist = true + } + for name, _ := range spb.Target_value { + if name == dbName { + return dbName, true, dbNamespace, dbNameSpaceExist + } + } + return dbName, false, dbNamespace, dbNameSpaceExist +} // For testing only func useRedisTcpClient() { for dbName, dbn := range spb.Target_value { if dbName != "OTHERS" { // DB connector for direct redis operation - var redisDb *redis.Client if UseRedisLocalTcpPort { - redisDb = redis.NewClient(&redis.Options{ - Network: "tcp", - Addr: sdcfg.GetDbTcpAddr(dbName), - Password: "", // no password set - DB: int(dbn), - DialTimeout: 0, - }) + for _, dbNamespace := range(sdcfg.GetDbAllNamespaces()) { + var redisDb *redis.Client + redisDb = redis.NewClient(&redis.Options{ + Network: "tcp", + Addr: sdcfg.GetDbTcpAddr(dbName, dbNamespace), + Password: "", // no password set + DB: int(dbn), + DialTimeout: 0, + }) + Target2RedisDb[dbNamespace][dbName] = redisDb + } } - Target2RedisDb[dbName] = redisDb } } } @@ -403,16 +437,18 @@ func init() { for dbName, dbn := range spb.Target_value { if dbName != "OTHERS" { // DB connector for direct redis operation - var redisDb *redis.Client - - redisDb = redis.NewClient(&redis.Options{ - Network: "unix", - Addr: sdcfg.GetDbSock(dbName), - Password: "", // no password set - DB: int(dbn), - DialTimeout: 0, - }) - Target2RedisDb[dbName] = redisDb + + for _, dbNamespace := range(sdcfg.GetDbAllNamespaces()) { + var redisDb *redis.Client + redisDb = redis.NewClient(&redis.Options{ + Network: "unix", + Addr: sdcfg.GetDbSock(dbName, dbNamespace), + Password: "", // no password set + DB: int(dbn), + DialTimeout: 0, + }) + Target2RedisDb[dbNamespace][dbName] = redisDb + } } } } @@ -447,10 +483,16 @@ func populateDbtablePath(prefix, path *gnmipb.Path, pathG2S *map[*gnmipb.Path][] var tblPath tablePath target := prefix.GetTarget() - // Verify it is a valid db name - redisDb, ok := Target2RedisDb[target] + targetDbName, targetDbNameValid, targetDbNameSpace, targetDbNameSpaceExist := IsTargetDb(target) + + // Verify Namespace is valid + dbNamespace, ok := sdcfg.GetDbNamespaceFromTarget(targetDbNameSpace) if !ok { - return fmt.Errorf("Invalid target name %v", target) + return fmt.Errorf("Invalid target dbNameSpace %v", targetDbNameSpace) + } + // Verify it is a valid db name + if !targetDbNameValid { + return fmt.Errorf("Invalid target dbName %v", targetDbName) } fullPath := path @@ -458,8 +500,8 @@ func populateDbtablePath(prefix, path *gnmipb.Path, pathG2S *map[*gnmipb.Path][] fullPath = gnmiFullPath(prefix, path) } - stringSlice := []string{target} - separator, _ := GetTableKeySeparator(target) + stringSlice := []string{targetDbName} + separator, _ := GetTableKeySeparator(targetDbName, dbNamespace) elems := fullPath.GetElem() if elems != nil { for i, elem := range elems { @@ -477,14 +519,17 @@ func populateDbtablePath(prefix, path *gnmipb.Path, pathG2S *map[*gnmipb.Path][] // First lookup the Virtual path to Real path mapping tree // The path from gNMI might not be real db path if tblPaths, err := lookupV2R(stringSlice); err == nil { + if targetDbNameSpaceExist { + return fmt.Errorf("Target having %v namespace is not supported for V2R Dataset", dbNamespace) + } (*pathG2S)[path] = tblPaths log.V(5).Infof("v2r from %v to %+v ", stringSlice, tblPaths) return nil } else { log.V(5).Infof("v2r lookup failed for %v %v", stringSlice, err) } - - tblPath.dbName = target + tblPath.dbNamespace = dbNamespace + tblPath.dbName = targetDbName tblPath.tableName = stringSlice[1] tblPath.delimitor = separator @@ -493,6 +538,12 @@ func populateDbtablePath(prefix, path *gnmipb.Path, pathG2S *map[*gnmipb.Path][] mappedKey = stringSlice[2] } + redisDb, ok := Target2RedisDb[tblPath.dbNamespace][tblPath.dbName] + if !ok { + return fmt.Errorf("Redis Client not present for dbName %v dbNamespace", targetDbName, dbNamespace) + } + + // The expect real db path could be in one of the formats: // <1> DB Table // <2> DB Table Key @@ -596,7 +647,7 @@ func emitJSON(v *map[string]interface{}) ([]byte, error) { // If only table name provided in the tablePath, find all keys in the table, otherwise // Use tableName + tableKey as key to get all field value paires func tableData2Msi(tblPath *tablePath, useKey bool, op *string, msi *map[string]interface{}) error { - redisDb := Target2RedisDb[tblPath.dbName] + redisDb := Target2RedisDb[tblPath.dbNamespace][tblPath.dbName] var pattern string var dbkeys []string @@ -678,7 +729,7 @@ func tableData2TypedValue(tblPaths []tablePath, op *string) (*gnmipb.TypedValue, var useKey bool msi := make(map[string]interface{}) for _, tblPath := range tblPaths { - redisDb := Target2RedisDb[tblPath.dbName] + redisDb := Target2RedisDb[tblPath.dbNamespace][tblPath.dbName] if tblPath.jsonField == "" { // Not asked to include field in json value, which means not wildcard query // table path includes table, key and field @@ -750,7 +801,7 @@ func dbFieldMultiSubscribe(c *DbClient, gnmiPath *gnmipb.Path, onChange bool, in key = tblPath.tableName } // run redis get directly for field value - redisDb := Target2RedisDb[tblPath.dbName] + redisDb := Target2RedisDb[tblPath.dbNamespace][tblPath.dbName] val, err := redisDb.HGet(key, tblPath.field).Result() if err == redis.Nil { if tblPath.jsonField != "" { @@ -836,7 +887,7 @@ func dbFieldSubscribe(c *DbClient, gnmiPath *gnmipb.Path, onChange bool, interva tblPaths := c.pathG2S[gnmiPath] tblPath := tblPaths[0] // run redis get directly for field value - redisDb := Target2RedisDb[tblPath.dbName] + redisDb := Target2RedisDb[tblPath.dbNamespace][tblPath.dbName] var key string if tblPath.tableKey != "" { @@ -1066,7 +1117,7 @@ func dbTableKeySubscribe(c *DbClient, gnmiPath *gnmipb.Path, interval time.Durat prefixLen = len(pattern) pattern += "*" } - redisDb := Target2RedisDb[tblPath.dbName] + redisDb := Target2RedisDb[tblPath.dbNamespace][tblPath.dbName] pubsub := redisDb.PSubscribe(pattern) defer pubsub.Close() diff --git a/sonic_data_client/virtual_db.go b/sonic_data_client/virtual_db.go index 7b1592cf7..8f6b13df0 100644 --- a/sonic_data_client/virtual_db.go +++ b/sonic_data_client/virtual_db.go @@ -38,6 +38,8 @@ var ( alias2nameMap = make(map[string]string) // Alias translation: from sonic interface name to vendor port name name2aliasMap = make(map[string]string) + // Map of sonic interface name to namespace + port2namespaceMap = make(map[string]string) // SONiC interface name to their PFC-WD enabled queues, then to oid map countersPfcwdNameMap = make(map[string]map[string]string) @@ -98,14 +100,13 @@ func initCountersPortNameMap() error { func initAliasMap() error { var err error if len(alias2nameMap) == 0 { - alias2nameMap, name2aliasMap, err = getAliasMap() + alias2nameMap, name2aliasMap, port2namespaceMap, err = getAliasMap() if err != nil { return err } } return nil } - func initCountersPfcwdNameMap() error { var err error if len(countersPfcwdNameMap) == 0 { @@ -122,145 +123,160 @@ func getPfcwdMap() (map[string]map[string]string, error) { var pfcwdName_map = make(map[string]map[string]string) dbName := "CONFIG_DB" - separator, _ := GetTableKeySeparator(dbName) - redisDb, _ := Target2RedisDb[dbName] - _, err := redisDb.Ping().Result() - if err != nil { - log.V(1).Infof("Can not connect to %v, err: %v", dbName, err) - return nil, err - } - - keyName := fmt.Sprintf("PFC_WD_TABLE%v*", separator) - resp, err := redisDb.Keys(keyName).Result() - if err != nil { - log.V(1).Infof("redis get keys failed for %v, key = %v, err: %v", dbName, keyName, err) - return nil, err - } - - if len(resp) == 0 { - // PFC WD service not enabled on device - log.V(1).Infof("PFC WD not enabled on device") - return nil, nil - } - - for _, key := range resp { - name := key[13:] - pfcwdName_map[name] = make(map[string]string) - } - - // Get Queue indexes that are enabled with PFC-WD - keyName = "PORT_QOS_MAP*" - resp, err = redisDb.Keys(keyName).Result() - if err != nil { - log.V(1).Infof("redis get keys failed for %v, key = %v, err: %v", dbName, keyName, err) - return nil, err - } - if len(resp) == 0 { - log.V(1).Infof("PFC WD not enabled on device") - return nil, nil - } - qos_key := resp[0] - - fieldName := "pfc_enable" - priorities, err := redisDb.HGet(qos_key, fieldName).Result() - if err != nil { - log.V(1).Infof("redis get field failed for %v, key = %v, field = %v, err: %v", dbName, qos_key, fieldName, err) - return nil, err - } - - keyName = fmt.Sprintf("MAP_PFC_PRIORITY_TO_QUEUE%vAZURE", separator) - pfc_queue_map, err := redisDb.HGetAll(keyName).Result() - if err != nil { - log.V(1).Infof("redis get fields failed for %v, key = %v, err: %v", dbName, keyName, err) - return nil, err - } - - var indices []string - for _, p := range strings.Split(priorities, ",") { - _, ok := pfc_queue_map[p] - if !ok { - log.V(1).Infof("Missing mapping between PFC priority %v to queue", p) - } else { - indices = append(indices, pfc_queue_map[p]) - } - } - - if len(countersQueueNameMap) == 0 { - log.V(1).Infof("COUNTERS_QUEUE_NAME_MAP is empty") - return nil, nil - } - - var queue_key string - queue_separator, _ := GetTableKeySeparator("COUNTERS_DB") - for port, _ := range pfcwdName_map { - for _, indice := range indices { - queue_key = port + queue_separator + indice - oid, ok := countersQueueNameMap[queue_key] - if !ok { - return nil, fmt.Errorf("key %v not exists in COUNTERS_QUEUE_NAME_MAP", queue_key) - } - pfcwdName_map[port][queue_key] = oid - } - } + for namespace, redisDb := range(GetRedisClientsForDb(dbName)) { + separator, _ := GetTableKeySeparator(dbName, namespace) + _, err := redisDb.Ping().Result() + if err != nil { + log.V(1).Infof("Can not connect to %v, err: %v", dbName, err) + return nil, err + } + + keyName := fmt.Sprintf("PFC_WD_TABLE%v*", separator) + resp, err := redisDb.Keys(keyName).Result() + if err != nil { + log.V(1).Infof("redis get keys failed for %v, key = %v, err: %v", dbName, keyName, err) + return nil, err + } + + if len(resp) == 0 { + // PFC WD service not enabled on device + log.V(1).Infof("PFC WD not enabled on device") + return nil, nil + } + + for _, key := range resp { + name := key[13:] + pfcwdName_map[name] = make(map[string]string) + } + + // Get Queue indexes that are enabled with PFC-WD + keyName = "PORT_QOS_MAP*" + resp, err = redisDb.Keys(keyName).Result() + if err != nil { + log.V(1).Infof("redis get keys failed for %v, key = %v, err: %v", dbName, keyName, err) + return nil, err + } + if len(resp) == 0 { + log.V(1).Infof("PFC WD not enabled on device") + return nil, nil + } + qos_key := resp[0] + + fieldName := "pfc_enable" + priorities, err := redisDb.HGet(qos_key, fieldName).Result() + if err != nil { + log.V(1).Infof("redis get field failed for %v, key = %v, field = %v, err: %v", dbName, qos_key, fieldName, err) + return nil, err + } + + keyName = fmt.Sprintf("MAP_PFC_PRIORITY_TO_QUEUE%vAZURE", separator) + pfc_queue_map, err := redisDb.HGetAll(keyName).Result() + if err != nil { + log.V(1).Infof("redis get fields failed for %v, key = %v, err: %v", dbName, keyName, err) + return nil, err + } + + var indices []string + for _, p := range strings.Split(priorities, ",") { + _, ok := pfc_queue_map[p] + if !ok { + log.V(1).Infof("Missing mapping between PFC priority %v to queue", p) + } else { + indices = append(indices, pfc_queue_map[p]) + } + } + + if len(countersQueueNameMap) == 0 { + log.V(1).Infof("COUNTERS_QUEUE_NAME_MAP is empty") + return nil, nil + } + + var queue_key string + queue_separator, _ := GetTableKeySeparator("COUNTERS_DB", namespace) + for port, _ := range pfcwdName_map { + for _, indice := range indices { + queue_key = port + queue_separator + indice + oid, ok := countersQueueNameMap[queue_key] + if !ok { + return nil, fmt.Errorf("key %v not exists in COUNTERS_QUEUE_NAME_MAP", queue_key) + } + pfcwdName_map[port][queue_key] = oid + } + } +} log.V(6).Infof("countersPfcwdNameMap: %v", pfcwdName_map) return pfcwdName_map, nil } -// Get the mapping between sonic interface name and vendor alias -func getAliasMap() (map[string]string, map[string]string, error) { +// Get the mapping between sonic interface name and vendor alias and sonic-interface to namespace map +func getAliasMap() (map[string]string, map[string]string, map[string]string, error) { var alias2name_map = make(map[string]string) var name2alias_map = make(map[string]string) + var port2namespace_map = make(map[string]string) dbName := "CONFIG_DB" - separator, _ := GetTableKeySeparator(dbName) - redisDb, _ := Target2RedisDb[dbName] - _, err := redisDb.Ping().Result() - if err != nil { - log.V(1).Infof("Can not connect to %v, err: %v", dbName, err) - return nil, nil, err - } + for namespace, redisDb := range(GetRedisClientsForDb(dbName)) { + separator, _ := GetTableKeySeparator(dbName, namespace) + _, err := redisDb.Ping().Result() + if err != nil { + log.V(1).Infof("Can not connect to %v, err: %v", dbName, err) + return nil, nil, nil, err + } - keyName := fmt.Sprintf("PORT%v*", separator) - resp, err := redisDb.Keys(keyName).Result() - if err != nil { - log.V(1).Infof("redis get keys failed for %v, key = %v, err: %v", dbName, keyName, err) - return nil, nil, err - } - for _, key := range resp { - alias, err := redisDb.HGet(key, "alias").Result() + keyName := fmt.Sprintf("PORT%v*", separator) + resp, err := redisDb.Keys(keyName).Result() if err != nil { - log.V(1).Infof("redis get field alias failed for %v, key = %v, err: %v", dbName, key, err) - // clear aliasMap - alias2name_map = make(map[string]string) - name2alias_map = make(map[string]string) - return nil, nil, err + log.V(1).Infof("redis get keys failed for %v, key = %v, err: %v", dbName, keyName, err) + return nil, nil, nil, err + } + for _, key := range resp { + alias, err := redisDb.HGet(key, "alias").Result() + if err != nil { + log.V(1).Infof("redis get field alias failed for %v, key = %v, err: %v", dbName, key, err) + // clear aliasMap + alias2name_map = make(map[string]string) + name2alias_map = make(map[string]string) + port2namespace_map = make(map[string]string) + return nil, nil, nil, err + } + alias2name_map[alias] = key[5:] + name2alias_map[key[5:]] = alias + port2namespace_map[key[5:]] = namespace } - alias2name_map[alias] = key[5:] - name2alias_map[key[5:]] = alias } log.V(6).Infof("alias2nameMap: %v", alias2name_map) log.V(6).Infof("name2aliasMap: %v", name2alias_map) - return alias2name_map, name2alias_map, nil + log.V(6).Infof("port2namespaceMap: %v", port2namespace_map) + return alias2name_map, name2alias_map, port2namespace_map, nil +} +// Ref: https://stackoverflow.com/questions/12172215/merging-maps-in-go +func addmap(a map[string]string, b map[string]string) { + for k,v := range b { + a[k] = v + } } // Get the mapping between objects in counters DB, Ex. port name to oid in "COUNTERS_PORT_NAME_MAP" table. // Aussuming static port name to oid map in COUNTERS table func getCountersMap(tableName string) (map[string]string, error) { - redisDb, _ := Target2RedisDb["COUNTERS_DB"] - fv, err := redisDb.HGetAll(tableName).Result() - if err != nil { - log.V(2).Infof("redis HGetAll failed for COUNTERS_DB, tableName: %s", tableName) - return nil, err + counter_map := make(map[string]string) + dbName := "COUNTERS_DB" + for _, redisDb := range(GetRedisClientsForDb(dbName)) { + fv, err := redisDb.HGetAll(tableName).Result() + if err != nil { + log.V(2).Infof("redis HGetAll failed for COUNTERS_DB, tableName: %s", tableName) + return nil, err + } + addmap(counter_map, fv) + log.V(6).Infof("tableName: %s, map %v", tableName, fv) } - log.V(6).Infof("tableName: %s, map %v", tableName, fv) - return fv, nil + return counter_map, nil } // Populate real data paths from paths like // [COUNTER_DB COUNTERS Ethernet*] or [COUNTER_DB COUNTERS Ethernet68] func v2rEthPortStats(paths []string) ([]tablePath, error) { - separator, _ := GetTableKeySeparator(paths[DbIdx]) var tblPaths []tablePath if strings.HasSuffix(paths[KeyIdx], "*") { // All Ethernet ports for port, oid := range countersPortNameMap { @@ -271,8 +287,13 @@ func v2rEthPortStats(paths []string) ([]tablePath, error) { log.V(2).Infof("%v does not have a vendor alias", port) oport = port } - + namespace, ok := port2namespaceMap[port] + if !ok { + return nil, fmt.Errorf("%v does not have namespace associated", port) + } + separator, _ := GetTableKeySeparator(paths[DbIdx], namespace) tblPath := tablePath{ + dbNamespace: namespace, dbName: paths[DbIdx], tableName: paths[TblIdx], tableKey: oid, @@ -292,7 +313,13 @@ func v2rEthPortStats(paths []string) ([]tablePath, error) { if !ok { return nil, fmt.Errorf("%v not a valid sonic interface. Vendor alias is %v", name, alias) } + namespace, ok := port2namespaceMap[name] + if !ok { + return nil, fmt.Errorf("%v does not have namespace associated", name) + } + separator, _ := GetTableKeySeparator(paths[DbIdx], namespace) tblPaths = []tablePath{{ + dbNamespace: namespace, dbName: paths[DbIdx], tableName: paths[TblIdx], tableKey: oid, @@ -310,7 +337,6 @@ func v2rEthPortStats(paths []string) ([]tablePath, error) { // Ex. [COUNTER_DB COUNTERS Ethernet68 SAI_PORT_STAT_PFC_0_RX_PKTS] // case of "*" field could be covered in v2rEthPortStats() func v2rEthPortFieldStats(paths []string) ([]tablePath, error) { - separator, _ := GetTableKeySeparator(paths[DbIdx]) var tblPaths []tablePath if strings.HasSuffix(paths[KeyIdx], "*") { for port, oid := range countersPortNameMap { @@ -321,8 +347,13 @@ func v2rEthPortFieldStats(paths []string) ([]tablePath, error) { log.V(2).Infof("%v dose not have a vendor alias", port) oport = port } - + namespace, ok := port2namespaceMap[port] + if !ok { + return nil, fmt.Errorf("%v does not have namespace associated", port) + } + separator, _ := GetTableKeySeparator(paths[DbIdx], namespace) tblPath := tablePath{ + dbNamespace: namespace, dbName: paths[DbIdx], tableName: paths[TblIdx], tableKey: oid, @@ -344,7 +375,13 @@ func v2rEthPortFieldStats(paths []string) ([]tablePath, error) { if !ok { return nil, fmt.Errorf(" %v not a valid sonic interface. Vendor alias is %v ", name, alias) } + namespace, ok := port2namespaceMap[name] + if !ok { + return nil, fmt.Errorf("%v does not have namespace associated", name) + } + separator, _ := GetTableKeySeparator(paths[DbIdx], namespace) tblPaths = []tablePath{{ + dbNamespace: namespace, dbName: paths[DbIdx], tableName: paths[TblIdx], tableKey: oid, @@ -359,12 +396,16 @@ func v2rEthPortFieldStats(paths []string) ([]tablePath, error) { // Populate real data paths from paths like // [COUNTER_DB COUNTERS Ethernet* Pfcwd] or [COUNTER_DB COUNTERS Ethernet68 Pfcwd] func v2rEthPortPfcwdStats(paths []string) ([]tablePath, error) { - separator, _ := GetTableKeySeparator(paths[DbIdx]) var tblPaths []tablePath if strings.HasSuffix(paths[KeyIdx], "*") { // Pfcwd on all Ethernet ports - for _, pfcqueues := range countersPfcwdNameMap { + for port, pfcqueues := range countersPfcwdNameMap { + namespace, ok := port2namespaceMap[port] + if !ok { + return nil, fmt.Errorf("%v does not have namespace associated", port) + } + separator, _ := GetTableKeySeparator(paths[DbIdx], namespace) for pfcque, oid := range pfcqueues { - // pfcque is in format of "Interface:12" + // pfcque is in format of "Interface:12" names := strings.Split(pfcque, separator) var oname string if alias, ok := name2aliasMap[names[0]]; ok { @@ -375,6 +416,7 @@ func v2rEthPortPfcwdStats(paths []string) ([]tablePath, error) { } que := strings.Join([]string{oname, names[1]}, separator) tblPath := tablePath{ + dbNamespace: namespace, dbName: paths[DbIdx], tableName: paths[TblIdx], tableKey: oid, @@ -390,10 +432,15 @@ func v2rEthPortPfcwdStats(paths []string) ([]tablePath, error) { if val, ok := alias2nameMap[alias]; ok { name = val } - _, ok := countersPortNameMap[name] + namespace, ok := port2namespaceMap[name] + if !ok { + return nil, fmt.Errorf("%v does not have namespace associated", name) + } + _, ok = countersPortNameMap[name] if !ok { return nil, fmt.Errorf("%v not a valid SONiC interface. Vendor alias is %v", name, alias) } + separator, _ := GetTableKeySeparator(paths[DbIdx], namespace) pfcqueues, ok := countersPfcwdNameMap[name] if ok { @@ -402,6 +449,7 @@ func v2rEthPortPfcwdStats(paths []string) ([]tablePath, error) { names := strings.Split(pfcque, separator) que := strings.Join([]string{alias, names[1]}, separator) tblPath := tablePath{ + dbNamespace: namespace, dbName: paths[DbIdx], tableName: paths[TblIdx], tableKey: oid, @@ -419,7 +467,7 @@ func v2rEthPortPfcwdStats(paths []string) ([]tablePath, error) { // Populate real data paths from paths like // [COUNTER_DB COUNTERS Ethernet* Queues] or [COUNTER_DB COUNTERS Ethernet68 Queues] func v2rEthPortQueStats(paths []string) ([]tablePath, error) { - separator, _ := GetTableKeySeparator(paths[DbIdx]) + separator, _ := GetTableKeySeparator(paths[DbIdx], "") var tblPaths []tablePath if strings.HasSuffix(paths[KeyIdx], "*") { // queues on all Ethernet ports for que, oid := range countersQueueNameMap { @@ -432,8 +480,13 @@ func v2rEthPortQueStats(paths []string) ([]tablePath, error) { log.V(2).Infof(" %v dose not have a vendor alias", names[0]) oname = names[0] } + namespace, ok := port2namespaceMap[names[0]] + if !ok { + return nil, fmt.Errorf("%v does not have namespace associated", names[0]) + } que = strings.Join([]string{oname, names[1]}, separator) tblPath := tablePath{ + dbNamespace: namespace, dbName: paths[DbIdx], tableName: paths[TblIdx], tableKey: oid, @@ -448,6 +501,10 @@ func v2rEthPortQueStats(paths []string) ([]tablePath, error) { if val, ok := alias2nameMap[alias]; ok { name = val } + namespace, ok := port2namespaceMap[name] + if !ok { + return nil, fmt.Errorf("%v does not have namespace associated", name) + } for que, oid := range countersQueueNameMap { //que is in format of "Ethernet64:12" names := strings.Split(que, separator) @@ -456,6 +513,7 @@ func v2rEthPortQueStats(paths []string) ([]tablePath, error) { } que = strings.Join([]string{alias, names[1]}, separator) tblPath := tablePath{ + dbNamespace: namespace, dbName: paths[DbIdx], tableName: paths[TblIdx], tableKey: oid, diff --git a/sonic_db_config/db_config.go b/sonic_db_config/db_config.go index c097359bc..f8b1ed087 100644 --- a/sonic_db_config/db_config.go +++ b/sonic_db_config/db_config.go @@ -1,135 +1,269 @@ -// Package dbconfig provides a generic functions for parsing sonic database config file in system +//Package dbconfig provides a generic functions for parsing sonic database config file in system +//package main package dbconfig import ( - "encoding/json" - "fmt" - "strconv" - io "io/ioutil" + "encoding/json" + "errors" + "fmt" + io "io/ioutil" + "os" + "path/filepath" + "strconv" ) const ( - SONIC_DB_CONFIG_FILE string = "/var/run/redis/sonic-db/database_config.json" + SONIC_DB_GLOBAL_CONFIG_FILE string = "/var/run/redis/sonic-db/database_globale.json" + SONIC_DB_CONFIG_FILE string = "/var/run/redis/sonic-db/database_config.json" + SONIC_DEFAULT_NAMESPACE string = "" ) -var sonic_db_config = make(map[string]interface{}) +var sonic_db_config = make(map[string]map[string]interface{}) var sonic_db_init bool +var sonic_db_multi_namespace 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 GetDbDefaultNamespace() string { + return SONIC_DEFAULT_NAMESPACE +} +func CheckDbMultiNamespace() bool { + if !sonic_db_init { + DbInit() + } + return sonic_db_multi_namespace +} +func GetDbNonDefaultNamespaces() ([]string, int) { + if !sonic_db_init { + DbInit() + } + ns_list := make([]string, 0, len(sonic_db_config)) + for ns := range sonic_db_config { + if ns == SONIC_DEFAULT_NAMESPACE { + continue + } + ns_list = append(ns_list, ns) + } + return ns_list, len(ns_list) +} +func GetDbAllNamespaces() ([]string) { + if !sonic_db_init { + DbInit() + } + ns_list := make([]string, len(sonic_db_config)) + i := 0 + for ns := range sonic_db_config { + ns_list[i] = ns + i++ + } + return ns_list +} + +func GetDbNamespaceFromTarget(target string) (string, bool) { + if target == GetDbDefaultNamespace() { + return target, true + } + ns_list,_ := GetDbNonDefaultNamespaces() + for _,ns := range(ns_list) { + if target == ns { + return target, true + } + } + return "", false +} +func GetDbList(ns string) map[string]interface{} { + if !sonic_db_init { + DbInit() + } + db_list, ok := sonic_db_config[ns]["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, ns string) map[string]interface{} { + if !sonic_db_init { + DbInit() + } + db, ok := sonic_db_config[ns]["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[ns]["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, ns string) string { + if !sonic_db_init { + DbInit() + } + db_list := GetDbList(ns) + 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, ns string) int { + if !sonic_db_init { + DbInit() + } + db_list := GetDbList(ns) + 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, ns string) string { + if !sonic_db_init { + DbInit() + } + inst := GetDbInst(db_name, ns) + 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, ns string) string { + if !sonic_db_init { + DbInit() + } + inst := GetDbInst(db_name, ns) + 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, ns string) int { + if !sonic_db_init { + DbInit() + } + inst := GetDbInst(db_name, ns) + 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, ns string) string { + if !sonic_db_init { + DbInit() + } + hostname := GetDbHostName(db_name, ns) + port := GetDbPort(db_name, ns) + return hostname + ":" + strconv.Itoa(port) +} + +func fetchValue(value interface{}) { // pass the interface value from the struct in this function as it will run recursively to get the value. + switch value.(type) { + case string: + fmt.Printf("%v is an string \n ", value.(string)) + case bool: + fmt.Printf("%v is bool \n ", value.(bool)) + case float64: + fmt.Printf("%v is float64 \n ", value.(float64)) + case []interface{}: + fmt.Printf("%v is a slice of interface \n ", value) + for _, v := range value.([]interface{}) { + fetchValue(v) + } + case map[string]interface{}: + fmt.Printf("%v is a map \n ", value) + for _, v := range value.(map[string]interface{}) { + fetchValue(v) + } + default: + fmt.Printf("%v is unknown \n ", value) + } +} +func DbGetNamespaceAndConfigFile(ns_to_cfgfile_map map[string]string) { + data, err := io.ReadFile(SONIC_DB_GLOBAL_CONFIG_FILE) + if err == nil { + //Ref:https://stackoverflow.com/questions/18537257/how-to-get-the-directory-of-the-currently-running-file + dir, err := filepath.Abs(filepath.Dir(SONIC_DB_GLOBAL_CONFIG_FILE)) + if err != nil { + panic(err) + } + var sonic_db_global_config = make(map[string]interface{}) + err = json.Unmarshal([]byte(data), &sonic_db_global_config) + if err != nil { + panic(err) + } + var db_include_file string + for _, entry := range sonic_db_global_config["INCLUDES"].([]interface{}) { + ns, ok := entry.(map[string]interface{})["namespace"] + if !ok { + ns = SONIC_DEFAULT_NAMESPACE + } + _, ok = ns_to_cfgfile_map[ns.(string)] + if ok { + panic(fmt.Errorf("Global Database config file is not valid(multiple include for same namespace!")) + } + //Ref:https://www.geeksforgeeks.org/filepath-join-function-in-golang-with-examples/ + db_include_file = filepath.Join(dir, entry.(map[string]interface{})["include"].(string)) + ns_to_cfgfile_map[ns.(string)] = db_include_file + } + if len(ns_to_cfgfile_map) > 1 { + sonic_db_multi_namespace = true + } else { + sonic_db_multi_namespace = false + } + + } else if errors.Is(err, os.ErrNotExist) { + // Ref: https://stackoverflow.com/questions/23452157/how-do-i-check-for-specific-types-of-error-among-those-returned-by-ioutil-readfi + ns_to_cfgfile_map[SONIC_DEFAULT_NAMESPACE] = SONIC_DB_CONFIG_FILE + sonic_db_multi_namespace = false + } else { + panic(err) + } } 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 - } + if sonic_db_init { + return + } + ns_to_cfgfile_map := make(map[string]string) + // Ref: https://stackoverflow.com/questions/14928826/passing-pointers-to-maps-in-golang + DbGetNamespaceAndConfigFile(ns_to_cfgfile_map) + for ns, db_cfg_file := range ns_to_cfgfile_map { + data, err := io.ReadFile(db_cfg_file) + if err != nil { + panic(err) + } + db_config := make(map[string]interface{}) + err = json.Unmarshal([]byte(data), &db_config) + if err != nil { + panic(err) + } + sonic_db_config[ns] = db_config + } + sonic_db_init = true } func init() { - sonic_db_init = false + sonic_db_init = false } + +/* +func main() { + DbInit(); + fmt.Println(GetDbSock("CONFIG_DB", "asic0")) + fmt.Println(GetDbSock("CONFIG_DB", "")) + fmt.Println(GetDbSeparator("CONFIG_DB", "asic0")) + //GetDbList("asic0"); + } +*/ From 255b63dcebfebc3622bfdc2a598f89815fb2f4f4 Mon Sep 17 00:00:00 2001 From: Abhishek Dosi Date: Thu, 22 Apr 2021 00:19:45 +0000 Subject: [PATCH 02/13] Fix the map of Target2Redis Signed-off-by: Abhishek Dosi --- sonic_data_client/db_client.go | 72 +++++++++++++++++----------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/sonic_data_client/db_client.go b/sonic_data_client/db_client.go index 6ca7e9055..d5f4b7ef9 100644 --- a/sonic_data_client/db_client.go +++ b/sonic_data_client/db_client.go @@ -385,7 +385,6 @@ func GetRedisClientsForDb(target string) (map[string]*redis.Client) { for _, ns := range ns_list { redis_client_map[ns] = Target2RedisDb[ns][target] } - } else { ns := sdcfg.GetDbDefaultNamespace() redis_client_map[ns] = Target2RedisDb[ns][target] @@ -412,45 +411,46 @@ func IsTargetDb ( target string) (string, bool, string, bool) { } // For testing only func useRedisTcpClient() { - for dbName, dbn := range spb.Target_value { - if dbName != "OTHERS" { - // DB connector for direct redis operation - if UseRedisLocalTcpPort { - for _, dbNamespace := range(sdcfg.GetDbAllNamespaces()) { - var redisDb *redis.Client - redisDb = redis.NewClient(&redis.Options{ - Network: "tcp", - Addr: sdcfg.GetDbTcpAddr(dbName, dbNamespace), - Password: "", // no password set - DB: int(dbn), - DialTimeout: 0, - }) - Target2RedisDb[dbNamespace][dbName] = redisDb - } - } - } - } + if UseRedisLocalTcpPort { + for _, dbNamespace := range(sdcfg.GetDbAllNamespaces()) { + Target2RedisDb[dbNamespace] = make(map[string]*redis.Client) + for dbName, dbn := range spb.Target_value { + if dbName != "OTHERS" { + // DB connector for direct redis operation + var redisDb *redis.Client + redisDb = redis.NewClient(&redis.Options{ + Network: "tcp", + Addr: sdcfg.GetDbTcpAddr(dbName, dbNamespace), + Password: "", // no password set + DB: int(dbn), + DialTimeout: 0, + }) + Target2RedisDb[dbNamespace][dbName] = redisDb + } + } + } + } } // Client package prepare redis clients to all DBs automatically func init() { - for dbName, dbn := range spb.Target_value { - if dbName != "OTHERS" { - // DB connector for direct redis operation - - for _, dbNamespace := range(sdcfg.GetDbAllNamespaces()) { - var redisDb *redis.Client - redisDb = redis.NewClient(&redis.Options{ - Network: "unix", - Addr: sdcfg.GetDbSock(dbName, dbNamespace), - Password: "", // no password set - DB: int(dbn), - DialTimeout: 0, - }) - Target2RedisDb[dbNamespace][dbName] = redisDb - } - } - } + for _, dbNamespace := range(sdcfg.GetDbAllNamespaces()) { + Target2RedisDb[dbNamespace] = make(map[string]*redis.Client) + for dbName, dbn := range spb.Target_value { + if dbName != "OTHERS" { + // DB connector for direct redis operation + var redisDb *redis.Client + redisDb = redis.NewClient(&redis.Options{ + Network: "unix", + Addr: sdcfg.GetDbSock(dbName, dbNamespace), + Password: "", // no password set + DB: int(dbn), + DialTimeout: 0, + }) + Target2RedisDb[dbNamespace][dbName] = redisDb + } + } + } } // gnmiFullPath builds the full path from the prefix and path. From a7e3e01a9e7cf0edfa3057f9ca3f673a55860ea7 Mon Sep 17 00:00:00 2001 From: Abhishek Dosi Date: Sun, 25 Apr 2021 00:06:08 +0000 Subject: [PATCH 03/13] Added Unit test cases for sonic_db_config package for single and multi namespace/asic. Signed-off-by: Abhishek Dosi --- sonic_db_config/db_config.go | 26 +++---- sonic_db_config/db_config_test.go | 115 ++++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+), 18 deletions(-) create mode 100644 sonic_db_config/db_config_test.go diff --git a/sonic_db_config/db_config.go b/sonic_db_config/db_config.go index f8b1ed087..26414b31e 100644 --- a/sonic_db_config/db_config.go +++ b/sonic_db_config/db_config.go @@ -13,7 +13,7 @@ import ( ) const ( - SONIC_DB_GLOBAL_CONFIG_FILE string = "/var/run/redis/sonic-db/database_globale.json" + SONIC_DB_GLOBAL_CONFIG_FILE string = "/var/run/redis/sonic-db/database_global.json" SONIC_DB_CONFIG_FILE string = "/var/run/redis/sonic-db/database_config.json" SONIC_DEFAULT_NAMESPACE string = "" ) @@ -29,7 +29,7 @@ func CheckDbMultiNamespace() bool { if !sonic_db_init { DbInit() } - return sonic_db_multi_namespace + return sonic_db_multi_namespace } func GetDbNonDefaultNamespaces() ([]string, int) { if !sonic_db_init { @@ -40,11 +40,11 @@ func GetDbNonDefaultNamespaces() ([]string, int) { if ns == SONIC_DEFAULT_NAMESPACE { continue } - ns_list = append(ns_list, ns) + ns_list = append(ns_list, ns) } return ns_list, len(ns_list) } -func GetDbAllNamespaces() ([]string) { +func GetDbAllNamespaces() []string { if !sonic_db_init { DbInit() } @@ -61,8 +61,8 @@ func GetDbNamespaceFromTarget(target string) (string, bool) { if target == GetDbDefaultNamespace() { return target, true } - ns_list,_ := GetDbNonDefaultNamespaces() - for _,ns := range(ns_list) { + ns_list, _ := GetDbNonDefaultNamespaces() + for _, ns := range ns_list { if target == ns { return target, true } @@ -75,7 +75,7 @@ func GetDbList(ns string) map[string]interface{} { } db_list, ok := sonic_db_config[ns]["DATABASES"].(map[string]interface{}) if !ok { - panic(fmt.Errorf("DATABASES' is not valid key in database_config.json file!")) + panic(fmt.Errorf("DATABASES' is not valid key in database_config.json file for namespace %q!", ns)) } return db_list } @@ -254,16 +254,6 @@ func DbInit() { sonic_db_init = true } -func init() { +func Init() { sonic_db_init = false } - -/* -func main() { - DbInit(); - fmt.Println(GetDbSock("CONFIG_DB", "asic0")) - fmt.Println(GetDbSock("CONFIG_DB", "")) - fmt.Println(GetDbSeparator("CONFIG_DB", "asic0")) - //GetDbList("asic0"); - } -*/ diff --git a/sonic_db_config/db_config_test.go b/sonic_db_config/db_config_test.go new file mode 100644 index 000000000..792a38500 --- /dev/null +++ b/sonic_db_config/db_config_test.go @@ -0,0 +1,115 @@ +package dbconfig + +import ( + "io" + "os" + "testing" +) + +func setupMultiNamespace(t *testing.T) func() { + err := os.MkdirAll("/var/run/redis0/sonic-db/", 0755) + if err != nil { + t.Fatalf("err: %s", err) + } + srcFileName := [2]string{"../testdata/database_global.json", "../testdata/database_config_asic0.json"} + dstFileName := [2]string{SONIC_DB_GLOBAL_CONFIG_FILE, "/var/run/redis0/sonic-db/database_config_asic0.json"} + for i := 0; i < len(srcFileName); i++ { + sourceFileStat, err := os.Stat(srcFileName[i]) + if err != nil { + t.Fatalf("err: %s", err) + } + + if !sourceFileStat.Mode().IsRegular() { + t.Fatalf("err: %s", err) + } + + source, err := os.Open(srcFileName[i]) + if err != nil { + t.Fatalf("err: %s", err) + } + defer source.Close() + + destination, err := os.Create(dstFileName[i]) + if err != nil { + t.Fatalf("err: %s", err) + } + defer destination.Close() + _, err = io.Copy(destination, source) + if err != nil { + t.Fatalf("err: %s", err) + } + } + /* https://github.com/golang/go/issues/32111 */ + return func() { + err := os.Remove(SONIC_DB_GLOBAL_CONFIG_FILE) + if err != nil { + t.Fatalf("err: %s", err) + } + err = os.RemoveAll("/var/run/redis0") + if err != nil { + t.Fatalf("err: %s", err) + } + } +} +func TestGetDb(t *testing.T) { + t.Run("Id", func(t *testing.T) { + db_id := GetDbId("CONFIG_DB", GetDbDefaultNamespace()) + if db_id != 4 { + t.Fatalf(`Id("") = %d, want 4, error`, db_id) + } + }) + t.Run("Sock", func(t *testing.T) { + sock_path := GetDbSock("CONFIG_DB", GetDbDefaultNamespace()) + if sock_path != "/var/run/redis/redis.sock" { + t.Fatalf(`Sock("") = %q, want "", error`, sock_path) + } + }) + t.Run("AllNamespaces", func(t *testing.T) { + ns_list := GetDbAllNamespaces() + if len(ns_list) != 1 { + t.Fatalf(`AllNamespaces("") = %q, want "1", error`, len(ns_list)) + } + if !(ns_list[0] == GetDbDefaultNamespace()) { + t.Fatalf(`AllNamespaces("") = %q, want default, error`, ns_list[0]) + } + }) + t.Run("TcpAddr", func(t *testing.T) { + tcp_addr := GetDbTcpAddr("CONFIG_DB", GetDbDefaultNamespace()) + if tcp_addr != "127.0.0.1:6379" { + t.Fatalf(`TcpAddr("") = %q, want 127.0.0.1:6379, error`, tcp_addr) + } + }) +} +func TestGetDbMultiNs(t *testing.T) { + Init() + cleanupMultiNamespace := setupMultiNamespace(t) + /* https://www.gopherguides.com/articles/test-cleanup-in-go-1-14*/ + t.Cleanup(cleanupMultiNamespace) + t.Run("Id", func(t *testing.T) { + db_id := GetDbId("CONFIG_DB", "asic0") + if db_id != 4 { + t.Fatalf(`Id("") = %d, want 4, error`, db_id) + } + }) + t.Run("Sock", func(t *testing.T) { + sock_path := GetDbSock("CONFIG_DB", "asic0") + if sock_path != "/var/run/redis0/redis.sock" { + t.Fatalf(`Sock("") = %q, want "", error`, sock_path) + } + }) + t.Run("AllNamespaces", func(t *testing.T) { + ns_list := GetDbAllNamespaces() + if len(ns_list) != 2 { + t.Fatalf(`AllNamespaces("") = %q, want "2", error`, len(ns_list)) + } + if !((ns_list[0] == GetDbDefaultNamespace() && ns_list[1] == "asic0") || (ns_list[0] == "asic0" && ns_list[1] == GetDbDefaultNamespace())) { + t.Fatalf(`AllNamespaces("") = %q %q, want default and asic0, error`, ns_list[0], ns_list[1]) + } + }) + t.Run("TcpAddr", func(t *testing.T) { + tcp_addr := GetDbTcpAddr("CONFIG_DB", "asic0") + if tcp_addr != "127.0.0.1:6379" { + t.Fatalf(`TcpAddr("") = %q, want 127.0.0.1:6379, error`, tcp_addr) + } + }) +} From 657e372be3ac85b42e642b59d0e7e2cf6b2ea687 Mon Sep 17 00:00:00 2001 From: Abhishek Dosi Date: Sun, 25 Apr 2021 23:36:15 +0000 Subject: [PATCH 04/13] More changes for unit-test Signed-off-by: Abhishek Dosi --- sonic_db_config/db_config_test.go | 62 ++++++----------------------- test_utils/test_utils.go | 54 +++++++++++++++++++++++++ testdata/database_config_asic0.json | 57 ++++++++++++++++++++++++++ testdata/database_global.json | 12 ++++++ 4 files changed, 136 insertions(+), 49 deletions(-) create mode 100644 test_utils/test_utils.go create mode 100644 testdata/database_config_asic0.json create mode 100644 testdata/database_global.json diff --git a/sonic_db_config/db_config_test.go b/sonic_db_config/db_config_test.go index 792a38500..debfbb27a 100644 --- a/sonic_db_config/db_config_test.go +++ b/sonic_db_config/db_config_test.go @@ -1,56 +1,11 @@ package dbconfig import ( - "io" - "os" + "github.com/Azure/sonic-telemetry/test_utils" "testing" -) - -func setupMultiNamespace(t *testing.T) func() { - err := os.MkdirAll("/var/run/redis0/sonic-db/", 0755) - if err != nil { - t.Fatalf("err: %s", err) - } - srcFileName := [2]string{"../testdata/database_global.json", "../testdata/database_config_asic0.json"} - dstFileName := [2]string{SONIC_DB_GLOBAL_CONFIG_FILE, "/var/run/redis0/sonic-db/database_config_asic0.json"} - for i := 0; i < len(srcFileName); i++ { - sourceFileStat, err := os.Stat(srcFileName[i]) - if err != nil { - t.Fatalf("err: %s", err) - } - if !sourceFileStat.Mode().IsRegular() { - t.Fatalf("err: %s", err) - } - - source, err := os.Open(srcFileName[i]) - if err != nil { - t.Fatalf("err: %s", err) - } - defer source.Close() +) - destination, err := os.Create(dstFileName[i]) - if err != nil { - t.Fatalf("err: %s", err) - } - defer destination.Close() - _, err = io.Copy(destination, source) - if err != nil { - t.Fatalf("err: %s", err) - } - } - /* https://github.com/golang/go/issues/32111 */ - return func() { - err := os.Remove(SONIC_DB_GLOBAL_CONFIG_FILE) - if err != nil { - t.Fatalf("err: %s", err) - } - err = os.RemoveAll("/var/run/redis0") - if err != nil { - t.Fatalf("err: %s", err) - } - } -} func TestGetDb(t *testing.T) { t.Run("Id", func(t *testing.T) { db_id := GetDbId("CONFIG_DB", GetDbDefaultNamespace()) @@ -82,9 +37,18 @@ func TestGetDb(t *testing.T) { } func TestGetDbMultiNs(t *testing.T) { Init() - cleanupMultiNamespace := setupMultiNamespace(t) + err := test_utils.SetupMultiNamespace() + if err != nil { + t.Fatalf("error Setting up MultiNamespace files with err %T", err) + } + /* https://www.gopherguides.com/articles/test-cleanup-in-go-1-14*/ - t.Cleanup(cleanupMultiNamespace) + t.Cleanup(func() { + if err:= test_utils.CleanUpMultiNamespace(); err != nil { + t.Fatalf("error Cleaning up MultiNamespace files with err %T", err) + + } + }) t.Run("Id", func(t *testing.T) { db_id := GetDbId("CONFIG_DB", "asic0") if db_id != 4 { diff --git a/test_utils/test_utils.go b/test_utils/test_utils.go new file mode 100644 index 000000000..0e7e7eced --- /dev/null +++ b/test_utils/test_utils.go @@ -0,0 +1,54 @@ +package test_utils +import ( + "io" + "os" +) + + +func SetupMultiNamespace() (error) { + err := os.MkdirAll("/var/run/redis0/sonic-db/", 0755) + if err != nil { + return err + } + srcFileName := [2]string{"../testdata/database_global.json", "../testdata/database_config_asic0.json"} + dstFileName := [2]string{"/var/run/redis/sonic-db/database_global.json", "/var/run/redis0/sonic-db/database_config_asic0.json"} + for i := 0; i < len(srcFileName); i++ { + sourceFileStat, err := os.Stat(srcFileName[i]) + if err != nil { + return err + } + + if !sourceFileStat.Mode().IsRegular() { + return err + } + + source, err := os.Open(srcFileName[i]) + if err != nil { + return err + } + defer source.Close() + + destination, err := os.Create(dstFileName[i]) + if err != nil { + return err + } + defer destination.Close() + _, err = io.Copy(destination, source) + if err != nil { + return err + } + } + return nil +} + +func CleanUpMultiNamespace() error { + err := os.Remove("/var/run/redis/sonic-db/database_global.json") + if err != nil { + return err + } + err = os.RemoveAll("/var/run/redis0") + if err != nil { + return err + } + return nil +} diff --git a/testdata/database_config_asic0.json b/testdata/database_config_asic0.json new file mode 100644 index 000000000..3d9823e33 --- /dev/null +++ b/testdata/database_config_asic0.json @@ -0,0 +1,57 @@ +{ + "INSTANCES": { + "redis":{ + "hostname" : "127.0.0.1", + "port" : 6379, + "unix_socket_path" : "/var/run/redis0/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" +} diff --git a/testdata/database_global.json b/testdata/database_global.json new file mode 100644 index 000000000..5cb179782 --- /dev/null +++ b/testdata/database_global.json @@ -0,0 +1,12 @@ +{ + "INCLUDES" : [ + { + "include" : "../../redis/sonic-db/database_config.json" + }, + { + "namespace" : "asic0", + "include" : "../../redis0/sonic-db/database_config_asic0.json" + } + ], + "VERSION" : "1.0" +} From f5d7b39a6e1804d598d9cfaa5599e36e977ee0e3 Mon Sep 17 00:00:00 2001 From: Abhishek Dosi Date: Tue, 27 Apr 2021 00:00:47 +0000 Subject: [PATCH 05/13] Added Unit test for multi-asic gnmi-server. Signed-off-by: Abhishek Dosi --- dialout/dialout_client/dialout_client.go | 10 +- dialout/dialout_client/dialout_client_test.go | 8 +- gnmi_server/server_test.go | 113 ++++++++++++++---- test_utils/test_utils.go | 3 + 4 files changed, 99 insertions(+), 35 deletions(-) diff --git a/dialout/dialout_client/dialout_client.go b/dialout/dialout_client/dialout_client.go index 451a4c10c..e927a4e9c 100644 --- a/dialout/dialout_client/dialout_client.go +++ b/dialout/dialout_client/dialout_client.go @@ -462,7 +462,7 @@ func setupDestGroupClients(ctx context.Context, destGroupName string) { // start/stop/update telemetry publist client as requested // TODO: more validation on db data func processTelemetryClientConfig(ctx context.Context, redisDb *redis.Client, key string, op string) error { - separator, _ := sdc.GetTableKeySeparator("CONFIG_DB") + separator, _ := sdc.GetTableKeySeparator("CONFIG_DB", sdcfg.GetDbDefaultNamespace()) tableKey := "TELEMETRY_CLIENT" + separator + key fv, err := redisDb.HGetAll(tableKey).Result() if err != nil { @@ -642,13 +642,13 @@ 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 := sdcfg.GetDbId("CONFIG_DB") + dbn := sdcfg.GetDbId("CONFIG_DB", sdcfg.GetDbDefaultNamespace()) var redisDb *redis.Client if sdc.UseRedisLocalTcpPort == false { redisDb = redis.NewClient(&redis.Options{ Network: "unix", - Addr: sdcfg.GetDbSock("CONFIG_DB"), + Addr: sdcfg.GetDbSock("CONFIG_DB", sdcfg.GetDbDefaultNamespace()), Password: "", // no password set DB: dbn, DialTimeout: 0, @@ -656,14 +656,14 @@ func DialOutRun(ctx context.Context, ccfg *ClientConfig) error { } else { redisDb = redis.NewClient(&redis.Options{ Network: "tcp", - Addr: sdcfg.GetDbTcpAddr("CONFIG_DB"), + Addr: sdcfg.GetDbTcpAddr("CONFIG_DB", sdcfg.GetDbDefaultNamespace()), Password: "", // no password set DB: dbn, DialTimeout: 0, }) } - separator, _ := sdc.GetTableKeySeparator("CONFIG_DB") + separator, _ := sdc.GetTableKeySeparator("CONFIG_DB", sdcfg.GetDbDefaultNamespace()) pattern := "__keyspace@" + strconv.Itoa(int(dbn)) + "__:TELEMETRY_CLIENT" + separator prefixLen := len(pattern) pattern += "*" diff --git a/dialout/dialout_client/dialout_client_test.go b/dialout/dialout_client/dialout_client_test.go index 1a8b76ebd..531d0d98e 100644 --- a/dialout/dialout_client/dialout_client_test.go +++ b/dialout/dialout_client/dialout_client_test.go @@ -97,9 +97,9 @@ func runServer(t *testing.T, s *sds.Server) { func getRedisClient(t *testing.T) *redis.Client { rclient := redis.NewClient(&redis.Options{ Network: "tcp", - Addr: sdcfg.GetDbTcpAddr("COUNTERS_DB"), + Addr: sdcfg.GetDbTcpAddr("COUNTERS_DB", sdcfg.GetDbDefaultNamespace()), Password: "", // no password set - DB: sdcfg.GetDbId("COUNTERS_DB"), + DB: sdcfg.GetDbId("COUNTERS_DB", sdcfg.GetDbDefaultNamespace()), DialTimeout: 0, }) _, err := rclient.Ping().Result() @@ -126,9 +126,9 @@ func exe_cmd(t *testing.T, cmd string) { func getConfigDbClient(t *testing.T) *redis.Client { rclient := redis.NewClient(&redis.Options{ Network: "tcp", - Addr: sdcfg.GetDbTcpAddr("CONFIG_DB"), + Addr: sdcfg.GetDbTcpAddr("CONFIG_DB", sdcfg.GetDbDefaultNamespace()), Password: "", // no password set - DB: sdcfg.GetDbId("CONFIG_DB"), + DB: sdcfg.GetDbId("CONFIG_DB", sdcfg.GetDbDefaultNamespace()), DialTimeout: 0, }) _, err := rclient.Ping().Result() diff --git a/gnmi_server/server_test.go b/gnmi_server/server_test.go index 43af96b4c..4392f3ddb 100644 --- a/gnmi_server/server_test.go +++ b/gnmi_server/server_test.go @@ -41,6 +41,7 @@ import ( gclient "github.com/jipanyang/gnmi/client/gnmi" "github.com/jipanyang/gnxi/utils/xpath" gnoi_system_pb "github.com/openconfig/gnoi/system" + "github.com/Azure/sonic-telemetry/test_utils" ) var clientTypes = []string{gclient.Type} @@ -229,10 +230,10 @@ func runServer(t *testing.T, s *Server) { //t.Log("Exiting RPC server on address", s.Address()) } -func getRedisClientN(t *testing.T, n int) *redis.Client { +func getRedisClientN(t *testing.T, n int, namespace string) *redis.Client { rclient := redis.NewClient(&redis.Options{ Network: "tcp", - Addr: sdcfg.GetDbTcpAddr("COUNTERS_DB", sdcfg.GetDbDefaultNamespace()), + Addr: sdcfg.GetDbTcpAddr("COUNTERS_DB", namespace), Password: "", // no password set DB: n, DialTimeout: 0, @@ -244,12 +245,13 @@ func getRedisClientN(t *testing.T, n int) *redis.Client { return rclient } -func getRedisClient(t *testing.T) *redis.Client { +func getRedisClient(t *testing.T, namespace string) *redis.Client { + rclient := redis.NewClient(&redis.Options{ Network: "tcp", - Addr: sdcfg.GetDbTcpAddr("COUNTERS_DB", sdcfg.GetDbDefaultNamespace()), + Addr: sdcfg.GetDbTcpAddr("COUNTERS_DB", namespace), Password: "", // no password set - DB: sdcfg.GetDbId("COUNTERS_DB", sdcfg.GetDbDefaultNamespace()), + DB: sdcfg.GetDbId("COUNTERS_DB", namespace), DialTimeout: 0, }) _, err := rclient.Ping().Result() @@ -259,12 +261,13 @@ func getRedisClient(t *testing.T) *redis.Client { return rclient } -func getConfigDbClient(t *testing.T) *redis.Client { +func getConfigDbClient(t *testing.T, namespace string) *redis.Client { + rclient := redis.NewClient(&redis.Options{ Network: "tcp", - Addr: sdcfg.GetDbTcpAddr("CONFIG_DB", sdcfg.GetDbDefaultNamespace()), + Addr: sdcfg.GetDbTcpAddr("CONFIG_DB", namespace), Password: "", // no password set - DB: sdcfg.GetDbId("CONFIG_DB", sdcfg.GetDbDefaultNamespace()), + DB: sdcfg.GetDbId("CONFIG_DB", namespace), DialTimeout: 0, }) _, err := rclient.Ping().Result() @@ -288,8 +291,8 @@ func loadConfigDB(t *testing.T, rclient *redis.Client, mpi map[string]interface{ } } -func prepareConfigDb(t *testing.T) { - rclient := getConfigDbClient(t) +func prepareConfigDb(t *testing.T, namespace string) { + rclient := getConfigDbClient(t, namespace) defer rclient.Close() rclient.FlushDB() @@ -310,8 +313,8 @@ func prepareConfigDb(t *testing.T) { loadConfigDB(t, rclient, mpi_pfcwd_map) } -func prepareDb(t *testing.T) { - rclient := getRedisClient(t) +func prepareDb(t *testing.T, namespace string) { + rclient := getRedisClient(t, namespace) defer rclient.Close() rclient.FlushDB() //Enable keysapce notification @@ -393,11 +396,11 @@ func prepareDb(t *testing.T) { loadDB(t, rclient, mpi_counter) // Load CONFIG_DB for alias translation - prepareConfigDb(t) + prepareConfigDb(t, namespace) } func prepareDbTranslib(t *testing.T) { - rclient := getRedisClient(t) + rclient := getRedisClient(t, sdcfg.GetDbDefaultNamespace()) rclient.FlushDB() rclient.Close() @@ -417,7 +420,7 @@ func prepareDbTranslib(t *testing.T) { t.Fatalf("read file %v err: %v", fileName, err) } for n, v := range rj { - rclient := getRedisClientN(t, n) + rclient := getRedisClientN(t, n, sdcfg.GetDbDefaultNamespace()) loadDBNotStrict(t, rclient, v) rclient.Close() } @@ -684,13 +687,7 @@ func TestGnmiSet(t *testing.T) { s.s.Stop() } -func TestGnmiGet(t *testing.T) { - //t.Log("Start server") - s := createServer(t, 8081) - go runServer(t, s) - - prepareDb(t) - +func runGnmiTestGet(t *testing.T) { //t.Log("Start gNMI client") tlsConfig := &tls.Config{InsecureSkipVerify: true} opts := []grpc.DialOption{grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig))} @@ -943,9 +940,46 @@ func TestGnmiGet(t *testing.T) { runTestGet(t, ctx, gClient, td.pathTarget, td.textPbPath, td.wantRetCode, td.wantRespVal, td.valTest) }) } + +} + + +func TestGnmiGet(t *testing.T) { + //t.Log("Start server") + s := createServer(t, 8081) + go runServer(t, s) + + prepareDb(t, sdcfg.GetDbDefaultNamespace()) + + runGnmiTestGet(t) + s.s.Stop() } +func TestGnmiGetMultiNs(t *testing.T) { + sdcfg.Init() + err := test_utils.SetupMultiNamespace() + if err != nil { + t.Fatalf("error Setting up MultiNamespace files with err %T", err) + } + + /* https://www.gopherguides.com/articles/test-cleanup-in-go-1-14*/ + t.Cleanup(func() { + if err:= test_utils.CleanUpMultiNamespace(); err != nil { + t.Fatalf("error Cleaning up MultiNamespace files with err %T", err) + + } + }) + //t.Log("Start server") + s := createServer(t, 8081) + go runServer(t, s) + + prepareDb(t, test_utils.GetMultiNsNamespace()) + + runGnmiTestGet(t) + + s.s.Stop() +} func TestGnmiGetTranslib(t *testing.T) { //t.Log("Start server") s := createServer(t, 8081) @@ -1101,7 +1135,7 @@ type tablePathValue struct { // runTestSubscribe subscribe DB path in stream mode or poll mode. // The return code and response value are compared with expected code and value. -func runTestSubscribe(t *testing.T) { +func runTestSubscribe(t *testing.T, namespace string) { fileName := "../testdata/COUNTERS_PORT_NAME_MAP.txt" countersPortNameMapByte, err := ioutil.ReadFile(fileName) if err != nil { @@ -2068,10 +2102,10 @@ func runTestSubscribe(t *testing.T) { }, } - rclient := getRedisClient(t) + rclient := getRedisClient(t, namespace) defer rclient.Close() for _, tt := range tests { - prepareDb(t) + prepareDb(t, namespace) // Extra db preparation for this test case for _, prepare := range tt.prepares { switch prepare.op { @@ -2082,6 +2116,7 @@ func runTestSubscribe(t *testing.T) { } } + sdcIntervalTicker := sdc.IntervalTicker intervalTickerChan := make(chan time.Time) if tt.generateIntervals { sdc.IntervalTicker = func(interval time.Duration) <-chan time.Time { @@ -2164,6 +2199,9 @@ func runTestSubscribe(t *testing.T) { t.Errorf("unexpected updates:\n%s", diff) } }) + if tt.generateIntervals { + sdc.IntervalTicker = sdcIntervalTicker + } } } @@ -2171,7 +2209,30 @@ func TestGnmiSubscribe(t *testing.T) { s := createServer(t, 8081) go runServer(t, s) - runTestSubscribe(t) + runTestSubscribe(t, sdcfg.GetDbDefaultNamespace()) + + s.s.Stop() +} +func TestGnmiSubscribeMultiNs(t *testing.T) { + sdcfg.Init() + err := test_utils.SetupMultiNamespace() + if err != nil { + t.Fatalf("error Setting up MultiNamespace files with err %T", err) + } + + /* https://www.gopherguides.com/articles/test-cleanup-in-go-1-14*/ + t.Cleanup(func() { + if err:= test_utils.CleanUpMultiNamespace(); err != nil { + t.Fatalf("error Cleaning up MultiNamespace files with err %T", err) + + } + }) + + + s := createServer(t, 8081) + go runServer(t, s) + + runTestSubscribe(t, test_utils.GetMultiNsNamespace()) s.s.Stop() } diff --git a/test_utils/test_utils.go b/test_utils/test_utils.go index 0e7e7eced..53faf02a9 100644 --- a/test_utils/test_utils.go +++ b/test_utils/test_utils.go @@ -52,3 +52,6 @@ func CleanUpMultiNamespace() error { } return nil } +func GetMultiNsNamespace() string { + return "asic0" +} From cf2407dae946f323d7b50c121e64a9ce34ccda5e Mon Sep 17 00:00:00 2001 From: Abhishek Dosi Date: Tue, 27 Apr 2021 18:01:58 +0000 Subject: [PATCH 06/13] More changes and added new test cases to quert non Counter DB Signed-off-by: Abhishek Dosi --- Makefile | 2 ++ gnmi_server/server_test.go | 42 +++++++++++++++++++++++++++++++--- sonic_data_client/db_client.go | 4 ++++ 3 files changed, 45 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index c4c20c637..24c4e6709 100644 --- a/Makefile +++ b/Makefile @@ -60,12 +60,14 @@ check: sudo cp ./testdata/database_config.json ${DBDIR} sudo mkdir -p /usr/models/yang || true sudo find $(MGMT_COMMON_DIR)/models -name '*.yang' -exec cp {} /usr/models/yang/ \; + -$(GO) test -v github.com/Azure/sonic-telemetry/sonic_db_config -$(GO) test -mod=vendor $(BLD_FLAGS) -v github.com/Azure/sonic-telemetry/gnmi_server -$(GO) test -mod=vendor $(BLD_FLAGS) -v github.com/Azure/sonic-telemetry/dialout/dialout_client clean: $(RM) -r build $(RM) -r vendor + $(RM) -r ${DBDIR} $(TELEMETRY_TEST_BIN): $(TEST_FILES) $(SRC_FILES) mkdir -p $(@D) diff --git a/gnmi_server/server_test.go b/gnmi_server/server_test.go index 4392f3ddb..79cfaa1cd 100644 --- a/gnmi_server/server_test.go +++ b/gnmi_server/server_test.go @@ -312,6 +312,12 @@ func prepareConfigDb(t *testing.T, namespace string) { mpi_pfcwd_map := loadConfig(t, "", configPfcwdByte) loadConfigDB(t, rclient, mpi_pfcwd_map) } +func prepareStateDb(t *testing.T, namespace string) { + rclient := getRedisClientN(t, 6, namespace) + defer rclient.Close() + rclient.FlushDB() + rclient.HSet("SWITCH_CAPABILITY|switch", "test_field", "test_value") +} func prepareDb(t *testing.T, namespace string) { rclient := getRedisClient(t, namespace) @@ -397,6 +403,9 @@ func prepareDb(t *testing.T, namespace string) { // Load CONFIG_DB for alias translation prepareConfigDb(t, namespace) + + //Load STATE_DB to test non V2R dataset + prepareStateDb(t, namespace) } func prepareDbTranslib(t *testing.T) { @@ -687,7 +696,7 @@ func TestGnmiSet(t *testing.T) { s.s.Stop() } -func runGnmiTestGet(t *testing.T) { +func runGnmiTestGet(t *testing.T, namespace string) { //t.Log("Start gNMI client") tlsConfig := &tls.Config{InsecureSkipVerify: true} opts := []grpc.DialOption{grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig))} @@ -745,6 +754,12 @@ func runGnmiTestGet(t *testing.T) { t.Fatalf("read file %v err: %v", fileName, err) } + stateDBPath := "STATE_DB" + + if namespace != sdcfg.GetDbDefaultNamespace() { + stateDBPath = "STATE_DB" + "/" + namespace + } + type testCase struct { desc string pathTarget string @@ -814,6 +829,7 @@ func runGnmiTestGet(t *testing.T) { `, wantRetCode: codes.OK, wantRespVal: countersPortNameMapByte, + valTest: true, }, { desc: "get COUNTERS:Ethernet68", pathTarget: "COUNTERS_DB", @@ -823,6 +839,7 @@ func runGnmiTestGet(t *testing.T) { `, wantRetCode: codes.OK, wantRespVal: countersEthernet68Byte, + valTest: true, }, { desc: "get COUNTERS:Ethernet68 SAI_PORT_STAT_PFC_7_RX_PKTS", pathTarget: "COUNTERS_DB", @@ -833,6 +850,7 @@ func runGnmiTestGet(t *testing.T) { `, wantRetCode: codes.OK, wantRespVal: "2", + valTest: true, }, { desc: "get COUNTERS:Ethernet68 Pfcwd", pathTarget: "COUNTERS_DB", @@ -843,6 +861,7 @@ func runGnmiTestGet(t *testing.T) { `, wantRetCode: codes.OK, wantRespVal: countersEthernet68PfcwdByte, + valTest: true, }, { desc: "get COUNTERS (use vendor alias):Ethernet68/1", pathTarget: "COUNTERS_DB", @@ -852,6 +871,7 @@ func runGnmiTestGet(t *testing.T) { `, wantRetCode: codes.OK, wantRespVal: countersEthernet68Byte, + valTest: true, }, { desc: "get COUNTERS (use vendor alias):Ethernet68/1 SAI_PORT_STAT_PFC_7_RX_PKTS", pathTarget: "COUNTERS_DB", @@ -862,6 +882,7 @@ func runGnmiTestGet(t *testing.T) { `, wantRetCode: codes.OK, wantRespVal: "2", + valTest: true, }, { desc: "get COUNTERS (use vendor alias):Ethernet68/1 Pfcwd", pathTarget: "COUNTERS_DB", @@ -872,6 +893,7 @@ func runGnmiTestGet(t *testing.T) { `, wantRetCode: codes.OK, wantRespVal: countersEthernet68PfcwdAliasByte, + valTest: true, }, { desc: "get COUNTERS:Ethernet*", pathTarget: "COUNTERS_DB", @@ -881,6 +903,7 @@ func runGnmiTestGet(t *testing.T) { `, wantRetCode: codes.OK, wantRespVal: countersEthernetWildcardByte, + valTest: true, }, { desc: "get COUNTERS:Ethernet* SAI_PORT_STAT_PFC_7_RX_PKTS", pathTarget: "COUNTERS_DB", @@ -891,6 +914,7 @@ func runGnmiTestGet(t *testing.T) { `, wantRetCode: codes.OK, wantRespVal: countersEthernetWildcardPfcByte, + valTest: true, }, { desc: "get COUNTERS:Ethernet* Pfcwd", pathTarget: "COUNTERS_DB", @@ -901,7 +925,19 @@ func runGnmiTestGet(t *testing.T) { `, wantRetCode: codes.OK, wantRespVal: countersEthernetWildcardPfcwdByte, + valTest: true, + }, { + desc: "get State DB Data for SWITCH_CAPABILITY switch", + pathTarget: stateDBPath, + textPbPath: ` + elem: + elem: + `, + valTest: true, + wantRetCode: codes.OK, + wantRespVal: []byte(`{"test_field": "test_value"}`), }, + // Happy path createBuildVersionTestCase( "get osversion/build", // query path @@ -951,7 +987,7 @@ func TestGnmiGet(t *testing.T) { prepareDb(t, sdcfg.GetDbDefaultNamespace()) - runGnmiTestGet(t) + runGnmiTestGet(t, sdcfg.GetDbDefaultNamespace()) s.s.Stop() } @@ -976,7 +1012,7 @@ func TestGnmiGetMultiNs(t *testing.T) { prepareDb(t, test_utils.GetMultiNsNamespace()) - runGnmiTestGet(t) + runGnmiTestGet(t, test_utils.GetMultiNsNamespace()) s.s.Stop() } diff --git a/sonic_data_client/db_client.go b/sonic_data_client/db_client.go index d5f4b7ef9..6ba9c9269 100644 --- a/sonic_data_client/db_client.go +++ b/sonic_data_client/db_client.go @@ -397,6 +397,10 @@ func IsTargetDb ( target string) (string, bool, string, bool) { dbNameSpaceExist := false dbNamespace := sdcfg.GetDbDefaultNamespace() + if len(targetname) > 2 { + return dbName, false, dbNamespace, dbNameSpaceExist + } + if len(targetname) > 1 { dbNamespace = targetname[1] dbNameSpaceExist = true From c8476c78083d3d32ab682a9f94ca8f3cb124ad5d Mon Sep 17 00:00:00 2001 From: Abhishek Dosi Date: Tue, 27 Apr 2021 18:14:48 +0000 Subject: [PATCH 07/13] Updated using go fmt Signed-off-by: Abhishek Dosi --- dialout/dialout_client/dialout_client.go | 2 +- gnmi_server/client_subscribe.go | 2 +- gnmi_server/server.go | 313 ++++++++-------- gnmi_server/server_test.go | 436 +++++++++++------------ sonic_data_client/db_client.go | 102 +++--- sonic_data_client/virtual_db.go | 225 ++++++------ sonic_db_config/db_config_test.go | 3 +- test_utils/test_utils.go | 6 +- 8 files changed, 539 insertions(+), 550 deletions(-) diff --git a/dialout/dialout_client/dialout_client.go b/dialout/dialout_client/dialout_client.go index e927a4e9c..7e46cdf9d 100644 --- a/dialout/dialout_client/dialout_client.go +++ b/dialout/dialout_client/dialout_client.go @@ -8,11 +8,11 @@ import ( 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/Workiva/go-datastructures/queue" "github.com/go-redis/redis" log "github.com/golang/glog" gpb "github.com/openconfig/gnmi/proto/gnmi" "github.com/openconfig/ygot/ygot" - "github.com/Workiva/go-datastructures/queue" "golang.org/x/net/context" "google.golang.org/grpc" "google.golang.org/grpc/credentials" diff --git a/gnmi_server/client_subscribe.go b/gnmi_server/client_subscribe.go index 7a954d833..1c2d6f313 100644 --- a/gnmi_server/client_subscribe.go +++ b/gnmi_server/client_subscribe.go @@ -123,7 +123,7 @@ func (c *Client) Run(stream gnmipb.GNMI_SubscribeServer) (err error) { if target == "OTHERS" { dc, err = sdc.NewNonDbClient(paths, prefix) - } else if _,ok,_,_ := sdc.IsTargetDb(target); ok { + } else if _, ok, _, _ := sdc.IsTargetDb(target); ok { dc, err = sdc.NewDbClient(paths, prefix) } else { /* For any other target or no target create new Transl Client. */ diff --git a/gnmi_server/server.go b/gnmi_server/server.go index 4f024fe57..f21f8d68a 100644 --- a/gnmi_server/server.go +++ b/gnmi_server/server.go @@ -1,28 +1,29 @@ package gnmi + import ( + "bytes" "errors" "fmt" - "net" - "strings" - "sync" + "github.com/Azure/sonic-mgmt-common/translib" "github.com/Azure/sonic-telemetry/common_utils" + spb "github.com/Azure/sonic-telemetry/proto" + spb_gnoi "github.com/Azure/sonic-telemetry/proto/gnoi" + spb_jwt_gnoi "github.com/Azure/sonic-telemetry/proto/gnoi/jwt" + sdc "github.com/Azure/sonic-telemetry/sonic_data_client" log "github.com/golang/glog" + "github.com/golang/protobuf/proto" + gnmipb "github.com/openconfig/gnmi/proto/gnmi" + gnmi_extpb "github.com/openconfig/gnmi/proto/gnmi_ext" + gnoi_system_pb "github.com/openconfig/gnoi/system" "golang.org/x/net/context" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/peer" "google.golang.org/grpc/reflection" "google.golang.org/grpc/status" - "github.com/golang/protobuf/proto" - gnoi_system_pb "github.com/openconfig/gnoi/system" - sdc "github.com/Azure/sonic-telemetry/sonic_data_client" - gnmipb "github.com/openconfig/gnmi/proto/gnmi" - gnmi_extpb "github.com/openconfig/gnmi/proto/gnmi_ext" - spb_gnoi "github.com/Azure/sonic-telemetry/proto/gnoi" - spb "github.com/Azure/sonic-telemetry/proto" - "github.com/Azure/sonic-mgmt-common/translib" - spb_jwt_gnoi "github.com/Azure/sonic-telemetry/proto/gnoi/jwt" - "bytes" + "net" + "strings" + "sync" ) var ( @@ -40,81 +41,81 @@ type Server struct { clients map[string]*Client } type AuthTypes map[string]bool + // Config is a collection of values for Server type Config struct { // Port for the Server to listen on. If 0 or unset the Server will pick a port // for this Server. - Port int64 + Port int64 UserAuth AuthTypes } var AuthLock sync.Mutex func (i AuthTypes) String() string { - if i["none"] { - return "" - } - b := new(bytes.Buffer) - for key, value := range i { - if value { - fmt.Fprintf(b, "%s ", key) - } - } - return b.String() + if i["none"] { + return "" + } + b := new(bytes.Buffer) + for key, value := range i { + if value { + fmt.Fprintf(b, "%s ", key) + } + } + return b.String() } func (i AuthTypes) Any() bool { - if i["none"] { - return false - } - for _, value := range i { - if value { - return true - } - } - return false + if i["none"] { + return false + } + for _, value := range i { + if value { + return true + } + } + return false } func (i AuthTypes) Enabled(mode string) bool { - if i["none"] { - return false - } - if value, exist := i[mode]; exist && value { - return true - } - return false + if i["none"] { + return false + } + if value, exist := i[mode]; exist && value { + return true + } + return false } func (i AuthTypes) Set(mode string) error { - modes := strings.Split(mode, ",") - for _, m := range modes { - m = strings.Trim(m, " ") - if m == "none" || m == "" { - i["none"] = true - return nil - } - - if _, exist := i[m]; !exist { - return fmt.Errorf("Expecting one or more of 'cert', 'password' or 'jwt'") - } - i[m] = true - } - return nil + modes := strings.Split(mode, ",") + for _, m := range modes { + m = strings.Trim(m, " ") + if m == "none" || m == "" { + i["none"] = true + return nil + } + + if _, exist := i[m]; !exist { + return fmt.Errorf("Expecting one or more of 'cert', 'password' or 'jwt'") + } + i[m] = true + } + return nil } func (i AuthTypes) Unset(mode string) error { - modes := strings.Split(mode, ",") - for _, m := range modes { - m = strings.Trim(m, " ") - if _, exist := i[m]; !exist { - return fmt.Errorf("Expecting one or more of 'cert', 'password' or 'jwt'") - } - i[m] = false - } - return nil + modes := strings.Split(mode, ",") + for _, m := range modes { + m = strings.Trim(m, " ") + if _, exist := i[m]; !exist { + return fmt.Errorf("Expecting one or more of 'cert', 'password' or 'jwt'") + } + i[m] = false + } + return nil } - // New returns an initialized Server. func NewServer(config *Config, opts []grpc.ServerOption) (*Server, error) { if config == nil { @@ -167,7 +168,7 @@ func (srv *Server) Port() int64 { return srv.config.Port } -func authenticate(UserAuth AuthTypes, ctx context.Context) (context.Context,error) { +func authenticate(UserAuth AuthTypes, ctx context.Context) (context.Context, error) { var err error success := false rc, ctx := common_utils.GetContext(ctx) @@ -184,13 +185,13 @@ func authenticate(UserAuth AuthTypes, ctx context.Context) (context.Context,erro } } if !success && UserAuth.Enabled("jwt") { - _,ctx,err = JwtAuthenAndAuthor(ctx) + _, ctx, err = JwtAuthenAndAuthor(ctx) if err == nil { success = true } } if !success && UserAuth.Enabled("cert") { - ctx,err = ClientCertAuthenAndAuthor(ctx) + ctx, err = ClientCertAuthenAndAuthor(ctx) if err == nil { success = true } @@ -199,20 +200,19 @@ func authenticate(UserAuth AuthTypes, ctx context.Context) (context.Context,erro //Allow for future authentication mechanisms here... if !success { - return ctx,status.Error(codes.Unauthenticated, "Unauthenticated") - } + return ctx, status.Error(codes.Unauthenticated, "Unauthenticated") + } - return ctx,nil + return ctx, nil } // Subscribe implements the gNMI Subscribe RPC. func (s *Server) Subscribe(stream gnmipb.GNMI_SubscribeServer) error { ctx := stream.Context() - ctx, err := authenticate(s.config.UserAuth, ctx) - if err != nil { - return err - } - + ctx, err := authenticate(s.config.UserAuth, ctx) + if err != nil { + return err + } pr, ok := peer.FromContext(ctx) if !ok { @@ -269,10 +269,10 @@ func (s *Server) checkEncodingAndModel(encoding gnmipb.Encoding, models []*gnmip // Get implements the Get RPC in gNMI spec. func (s *Server) Get(ctx context.Context, req *gnmipb.GetRequest) (*gnmipb.GetResponse, error) { - ctx, err := authenticate(s.config.UserAuth, ctx) - if err != nil { - return nil, err - } + ctx, err := authenticate(s.config.UserAuth, ctx) + if err != nil { + return nil, err + } if req.GetType() != gnmipb.GetRequest_ALL { return nil, status.Errorf(codes.Unimplemented, "unsupported request type: %s", gnmipb.GetRequest_DataType_name[int32(req.GetType())]) @@ -285,24 +285,24 @@ func (s *Server) Get(ctx context.Context, req *gnmipb.GetRequest) (*gnmipb.GetRe var target string prefix := req.GetPrefix() if prefix == nil { - return nil, status.Error(codes.Unimplemented, "No target specified in prefix") + return nil, status.Error(codes.Unimplemented, "No target specified in prefix") } else { - target = prefix.GetTarget() - if target == "" { - return nil, status.Error(codes.Unimplemented, "Empty target data not supported yet") - } + target = prefix.GetTarget() + if target == "" { + return nil, status.Error(codes.Unimplemented, "Empty target data not supported yet") + } } paths := req.GetPath() extensions := req.GetExtension() - target = prefix.GetTarget() + target = prefix.GetTarget() log.V(5).Infof("GetRequest paths: %v", paths) var dc sdc.Client if target == "OTHERS" { dc, err = sdc.NewNonDbClient(paths, prefix) - } else if _,ok,_,_ := sdc.IsTargetDb(target); ok { + } else if _, ok, _, _ := sdc.IsTargetDb(target); ok { dc, err = sdc.NewDbClient(paths, prefix) } else { /* If no prefix target is specified create new Transl Data Client . */ @@ -334,68 +334,65 @@ func (s *Server) Get(ctx context.Context, req *gnmipb.GetRequest) (*gnmipb.GetRe return &gnmipb.GetResponse{Notification: notifications}, nil } -func (s *Server) Set(ctx context.Context,req *gnmipb.SetRequest) (*gnmipb.SetResponse, error) { - if !READ_WRITE_MODE { - return nil, grpc.Errorf(codes.Unimplemented, "Telemetry is in read-only mode") - } - ctx, err := authenticate(s.config.UserAuth, ctx) - if err != nil { - return nil, err - } - var results []*gnmipb.UpdateResult - - /* Fetch the prefix. */ - prefix := req.GetPrefix() - extensions := req.GetExtension() - /* Create Transl client. */ - dc, _ := sdc.NewTranslClient(prefix, nil, ctx, extensions) - - /* DELETE */ - for _, path := range req.GetDelete() { - log.V(2).Infof("Delete path: %v", path) - - res := gnmipb.UpdateResult{ - Path: path, - Op: gnmipb.UpdateResult_DELETE, - } - - /* Add to Set response results. */ - results = append(results, &res) - +func (s *Server) Set(ctx context.Context, req *gnmipb.SetRequest) (*gnmipb.SetResponse, error) { + if !READ_WRITE_MODE { + return nil, grpc.Errorf(codes.Unimplemented, "Telemetry is in read-only mode") + } + ctx, err := authenticate(s.config.UserAuth, ctx) + if err != nil { + return nil, err + } + var results []*gnmipb.UpdateResult + + /* Fetch the prefix. */ + prefix := req.GetPrefix() + extensions := req.GetExtension() + /* Create Transl client. */ + dc, _ := sdc.NewTranslClient(prefix, nil, ctx, extensions) + + /* DELETE */ + for _, path := range req.GetDelete() { + log.V(2).Infof("Delete path: %v", path) + + res := gnmipb.UpdateResult{ + Path: path, + Op: gnmipb.UpdateResult_DELETE, } - - /* REPLACE */ - for _, path := range req.GetReplace(){ - log.V(2).Infof("Replace path: %v ", path) - - res := gnmipb.UpdateResult{ - Path: path.GetPath(), - Op: gnmipb.UpdateResult_REPLACE, - } - /* Add to Set response results. */ - results = append(results, &res) + + /* Add to Set response results. */ + results = append(results, &res) + + } + + /* REPLACE */ + for _, path := range req.GetReplace() { + log.V(2).Infof("Replace path: %v ", path) + + res := gnmipb.UpdateResult{ + Path: path.GetPath(), + Op: gnmipb.UpdateResult_REPLACE, } - - - /* UPDATE */ - for _, path := range req.GetUpdate(){ - log.V(2).Infof("Update path: %v ", path) - - res := gnmipb.UpdateResult{ - Path: path.GetPath(), - Op: gnmipb.UpdateResult_UPDATE, - } - /* Add to Set response results. */ - results = append(results, &res) + /* Add to Set response results. */ + results = append(results, &res) + } + + /* UPDATE */ + for _, path := range req.GetUpdate() { + log.V(2).Infof("Update path: %v ", path) + + res := gnmipb.UpdateResult{ + Path: path.GetPath(), + Op: gnmipb.UpdateResult_UPDATE, } - err = dc.Set(req.GetDelete(), req.GetReplace(), req.GetUpdate()) - - - - return &gnmipb.SetResponse{ - Prefix: req.GetPrefix(), - Response: results, - }, err + /* Add to Set response results. */ + results = append(results, &res) + } + err = dc.Set(req.GetDelete(), req.GetReplace(), req.GetUpdate()) + + return &gnmipb.SetResponse{ + Prefix: req.GetPrefix(), + Response: results, + }, err } @@ -405,7 +402,7 @@ func (s *Server) Capabilities(ctx context.Context, req *gnmipb.CapabilityRequest return nil, err } extensions := req.GetExtension() - dc, _ := sdc.NewTranslClient(nil , nil, ctx, extensions) + dc, _ := sdc.NewTranslClient(nil, nil, ctx, extensions) /* Fetch the client capabitlities. */ supportedModels := dc.Capabilities() @@ -413,28 +410,26 @@ func (s *Server) Capabilities(ctx context.Context, req *gnmipb.CapabilityRequest for index, model := range supportedModels { suppModels[index] = &gnmipb.ModelData{ - Name: model.Name, - Organization: model.Organization, - Version: model.Version, + Name: model.Name, + Organization: model.Organization, + Version: model.Version, } } sup_bver := spb.SupportedBundleVersions{ BundleVersion: translib.GetYangBundleVersion().String(), - BaseVersion: translib.GetYangBaseVersion().String(), + BaseVersion: translib.GetYangBaseVersion().String(), } sup_msg, _ := proto.Marshal(&sup_bver) ext := gnmi_extpb.Extension{} - ext.Ext = &gnmi_extpb.Extension_RegisteredExt { - RegisteredExt: &gnmi_extpb.RegisteredExtension { - Id: spb.SUPPORTED_VERSIONS_EXT, + ext.Ext = &gnmi_extpb.Extension_RegisteredExt{ + RegisteredExt: &gnmi_extpb.RegisteredExtension{ + Id: spb.SUPPORTED_VERSIONS_EXT, Msg: sup_msg}} exts := []*gnmi_extpb.Extension{&ext} - return &gnmipb.CapabilityResponse{SupportedModels: suppModels, - SupportedEncodings: supportedEncodings, - GNMIVersion: "0.7.0", - Extension: exts}, nil + return &gnmipb.CapabilityResponse{SupportedModels: suppModels, + SupportedEncodings: supportedEncodings, + GNMIVersion: "0.7.0", + Extension: exts}, nil } - - diff --git a/gnmi_server/server_test.go b/gnmi_server/server_test.go index 79cfaa1cd..db651045f 100644 --- a/gnmi_server/server_test.go +++ b/gnmi_server/server_test.go @@ -38,10 +38,10 @@ import ( sgpb "github.com/Azure/sonic-telemetry/proto/gnoi" sdc "github.com/Azure/sonic-telemetry/sonic_data_client" sdcfg "github.com/Azure/sonic-telemetry/sonic_db_config" + "github.com/Azure/sonic-telemetry/test_utils" gclient "github.com/jipanyang/gnmi/client/gnmi" "github.com/jipanyang/gnxi/utils/xpath" gnoi_system_pb "github.com/openconfig/gnoi/system" - "github.com/Azure/sonic-telemetry/test_utils" ) var clientTypes = []string{gclient.Type} @@ -86,22 +86,22 @@ func loadDBNotStrict(t *testing.T, rclient *redis.Client, mpi map[string]interfa } func createServer(t *testing.T, port int64) *Server { - certificate, err := testcert.NewCert() - if err != nil { - t.Errorf("could not load server key pair: %s", err) - } - tlsCfg := &tls.Config{ - ClientAuth: tls.RequestClientCert, - Certificates: []tls.Certificate{certificate}, - } - - opts := []grpc.ServerOption{grpc.Creds(credentials.NewTLS(tlsCfg))} - cfg := &Config{Port: port} - s, err := NewServer(cfg, opts) - if err != nil { - t.Errorf("Failed to create gNMI server: %v", err) - } - return s + certificate, err := testcert.NewCert() + if err != nil { + t.Errorf("could not load server key pair: %s", err) + } + tlsCfg := &tls.Config{ + ClientAuth: tls.RequestClientCert, + Certificates: []tls.Certificate{certificate}, + } + + opts := []grpc.ServerOption{grpc.Creds(credentials.NewTLS(tlsCfg))} + cfg := &Config{Port: port} + s, err := NewServer(cfg, opts) + if err != nil { + t.Errorf("Failed to create gNMI server: %v", err) + } + return s } // runTestGet requests a path from the server by Get grpc call, and compares if @@ -261,7 +261,7 @@ func getRedisClient(t *testing.T, namespace string) *redis.Client { return rclient } -func getConfigDbClient(t *testing.T, namespace string) *redis.Client { +func getConfigDbClient(t *testing.T, namespace string) *redis.Client { rclient := redis.NewClient(&redis.Options{ Network: "tcp", @@ -316,7 +316,7 @@ func prepareStateDb(t *testing.T, namespace string) { rclient := getRedisClientN(t, 6, namespace) defer rclient.Close() rclient.FlushDB() - rclient.HSet("SWITCH_CAPABILITY|switch", "test_field", "test_value") + rclient.HSet("SWITCH_CAPABILITY|switch", "test_field", "test_value") } func prepareDb(t *testing.T, namespace string) { @@ -404,8 +404,8 @@ func prepareDb(t *testing.T, namespace string) { // Load CONFIG_DB for alias translation prepareConfigDb(t, namespace) - //Load STATE_DB to test non V2R dataset - prepareStateDb(t, namespace) + //Load STATE_DB to test non V2R dataset + prepareStateDb(t, namespace) } func prepareDbTranslib(t *testing.T) { @@ -754,11 +754,11 @@ func runGnmiTestGet(t *testing.T, namespace string) { t.Fatalf("read file %v err: %v", fileName, err) } - stateDBPath := "STATE_DB" + stateDBPath := "STATE_DB" - if namespace != sdcfg.GetDbDefaultNamespace() { - stateDBPath = "STATE_DB" + "/" + namespace - } + if namespace != sdcfg.GetDbDefaultNamespace() { + stateDBPath = "STATE_DB" + "/" + namespace + } type testCase struct { desc string @@ -935,7 +935,7 @@ func runGnmiTestGet(t *testing.T, namespace string) { `, valTest: true, wantRetCode: codes.OK, - wantRespVal: []byte(`{"test_field": "test_value"}`), + wantRespVal: []byte(`{"test_field": "test_value"}`), }, // Happy path @@ -979,7 +979,6 @@ func runGnmiTestGet(t *testing.T, namespace string) { } - func TestGnmiGet(t *testing.T) { //t.Log("Start server") s := createServer(t, 8081) @@ -1000,7 +999,7 @@ func TestGnmiGetMultiNs(t *testing.T) { /* https://www.gopherguides.com/articles/test-cleanup-in-go-1-14*/ t.Cleanup(func() { - if err:= test_utils.CleanUpMultiNamespace(); err != nil { + if err := test_utils.CleanUpMultiNamespace(); err != nil { t.Fatalf("error Cleaning up MultiNamespace files with err %T", err) } @@ -2258,13 +2257,12 @@ func TestGnmiSubscribeMultiNs(t *testing.T) { /* https://www.gopherguides.com/articles/test-cleanup-in-go-1-14*/ t.Cleanup(func() { - if err:= test_utils.CleanUpMultiNamespace(); err != nil { + if err := test_utils.CleanUpMultiNamespace(); err != nil { t.Fatalf("error Cleaning up MultiNamespace files with err %T", err) } }) - s := createServer(t, 8081) go runServer(t, s) @@ -2308,206 +2306,202 @@ func TestCapabilities(t *testing.T) { } func TestGNOI(t *testing.T) { - s := createServer(t, 8086) - go runServer(t, s) - defer s.s.Stop() - - // prepareDb(t) - - //t.Log("Start gNMI client") - tlsConfig := &tls.Config{InsecureSkipVerify: true} - opts := []grpc.DialOption{grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig))} - - //targetAddr := "30.57.185.38:8080" - targetAddr := "127.0.0.1:8086" - conn, err := grpc.Dial(targetAddr, opts...) - if err != nil { - t.Fatalf("Dialing to %q failed: %v", targetAddr, err) - } - defer conn.Close() - - - ctx, cancel := context.WithTimeout(context.Background(), 240*time.Second) - defer cancel() - - t.Run("SystemTime", func(t *testing.T) { - sc := gnoi_system_pb.NewSystemClient(conn) - resp,err := sc.Time(ctx, new(gnoi_system_pb.TimeRequest)) - if err != nil { - t.Fatal(err.Error()) - } - ctime := uint64(time.Now().UnixNano()) - if ctime - resp.Time < 0 || ctime - resp.Time > 1e9 { - t.Fatalf("Invalid System Time %d", resp.Time) - } - }) - t.Run("SonicShowTechsupport", func(t *testing.T) { - sc := sgpb.NewSonicServiceClient(conn) - rtime := time.Now().AddDate(0,-1,0) - req := &sgpb.TechsupportRequest { - Input: &sgpb.TechsupportRequest_Input{ - Date: rtime.Format("20060102_150405"), - }, - } - resp,err := sc.ShowTechsupport(ctx, req) - if err != nil { - t.Fatal(err.Error()) - } - - if len(resp.Output.OutputFilename) == 0 { - t.Fatalf("Invalid Output Filename: %s", resp.Output.OutputFilename) - } - }) - - type configData struct { - source string - destination string - overwrite bool - status int32 - } - - var cfg_data = []configData { - configData{"running-configuration", "startup-configuration", false, 0}, - configData{"running-configuration", "file://etc/sonic/config_db_test.json", false, 0}, - configData{"file://etc/sonic/config_db_test.json", "running-configuration", false, 0}, - configData{"startup-configuration", "running-configuration", false, 0}, - configData{"file://etc/sonic/config_db_3.json", "running-configuration", false, 1}} - - for _,v := range cfg_data { - - t.Run("SonicCopyConfig", func(t *testing.T) { - sc := sgpb.NewSonicServiceClient(conn) - req := &sgpb.CopyConfigRequest { - Input: &sgpb.CopyConfigRequest_Input{ - Source: v.source, - Destination: v.destination, - Overwrite: v.overwrite, - }, - } - t.Logf("source: %s dest: %s overwrite: %t", v.source, v.destination, v.overwrite) - resp, err := sc.CopyConfig(ctx, req) + s := createServer(t, 8086) + go runServer(t, s) + defer s.s.Stop() + + // prepareDb(t) + + //t.Log("Start gNMI client") + tlsConfig := &tls.Config{InsecureSkipVerify: true} + opts := []grpc.DialOption{grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig))} + + //targetAddr := "30.57.185.38:8080" + targetAddr := "127.0.0.1:8086" + conn, err := grpc.Dial(targetAddr, opts...) if err != nil { - t.Fatal(err.Error()) + t.Fatalf("Dialing to %q failed: %v", targetAddr, err) } - if resp.Output.Status != v.status { - t.Fatalf("Copy Failed: status %d, %s", resp.Output.Status, resp.Output.StatusDetail) + defer conn.Close() + + ctx, cancel := context.WithTimeout(context.Background(), 240*time.Second) + defer cancel() + + t.Run("SystemTime", func(t *testing.T) { + sc := gnoi_system_pb.NewSystemClient(conn) + resp, err := sc.Time(ctx, new(gnoi_system_pb.TimeRequest)) + if err != nil { + t.Fatal(err.Error()) + } + ctime := uint64(time.Now().UnixNano()) + if ctime-resp.Time < 0 || ctime-resp.Time > 1e9 { + t.Fatalf("Invalid System Time %d", resp.Time) + } + }) + t.Run("SonicShowTechsupport", func(t *testing.T) { + sc := sgpb.NewSonicServiceClient(conn) + rtime := time.Now().AddDate(0, -1, 0) + req := &sgpb.TechsupportRequest{ + Input: &sgpb.TechsupportRequest_Input{ + Date: rtime.Format("20060102_150405"), + }, + } + resp, err := sc.ShowTechsupport(ctx, req) + if err != nil { + t.Fatal(err.Error()) + } + + if len(resp.Output.OutputFilename) == 0 { + t.Fatalf("Invalid Output Filename: %s", resp.Output.OutputFilename) + } + }) + + type configData struct { + source string + destination string + overwrite bool + status int32 + } + + var cfg_data = []configData{ + configData{"running-configuration", "startup-configuration", false, 0}, + configData{"running-configuration", "file://etc/sonic/config_db_test.json", false, 0}, + configData{"file://etc/sonic/config_db_test.json", "running-configuration", false, 0}, + configData{"startup-configuration", "running-configuration", false, 0}, + configData{"file://etc/sonic/config_db_3.json", "running-configuration", false, 1}} + + for _, v := range cfg_data { + + t.Run("SonicCopyConfig", func(t *testing.T) { + sc := sgpb.NewSonicServiceClient(conn) + req := &sgpb.CopyConfigRequest{ + Input: &sgpb.CopyConfigRequest_Input{ + Source: v.source, + Destination: v.destination, + Overwrite: v.overwrite, + }, + } + t.Logf("source: %s dest: %s overwrite: %t", v.source, v.destination, v.overwrite) + resp, err := sc.CopyConfig(ctx, req) + if err != nil { + t.Fatal(err.Error()) + } + if resp.Output.Status != v.status { + t.Fatalf("Copy Failed: status %d, %s", resp.Output.Status, resp.Output.StatusDetail) + } + }) } - }) - } } func TestBundleVersion(t *testing.T) { - s := createServer(t, 8087) - go runServer(t, s) - defer s.s.Stop() - - // prepareDb(t) - - //t.Log("Start gNMI client") - tlsConfig := &tls.Config{InsecureSkipVerify: true} - opts := []grpc.DialOption{grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig))} - - //targetAddr := "30.57.185.38:8080" - targetAddr := "127.0.0.1:8087" - conn, err := grpc.Dial(targetAddr, opts...) - if err != nil { - t.Fatalf("Dialing to %q failed: %v", targetAddr, err) - } - defer conn.Close() - - gClient := pb.NewGNMIClient(conn) - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - t.Run("Invalid Bundle Version Format", func(t *testing.T) { - var pbPath *pb.Path - pbPath, err := xpath.ToGNMIPath("openconfig-interfaces:interfaces/interface[name=Ethernet0]/config") - prefix := pb.Path{Target: "OC-YANG"} - if err != nil { - t.Fatalf("error in unmarshaling path: %v", err) - } - bundleVersion := "50.0.0" - bv, err := proto.Marshal(&spb.BundleVersion{ - Version: bundleVersion, - }) - if err != nil { - t.Fatalf("%v", err) - } - req := &pb.GetRequest{ - Path: []*pb.Path{pbPath}, - Prefix: &prefix, - Encoding: pb.Encoding_JSON_IETF, - } - req.Extension = append(req.Extension, &ext_pb.Extension{ - Ext: &ext_pb.Extension_RegisteredExt { - RegisteredExt: &ext_pb.RegisteredExtension { - Id: spb.BUNDLE_VERSION_EXT, - Msg: bv, - }}}) - - - - _, err = gClient.Get(ctx, req) - gotRetStatus, ok := status.FromError(err) - if !ok { - t.Fatal("got a non-grpc error from grpc call") - } - if gotRetStatus.Code() != codes.NotFound { - t.Log("err: ", err) - t.Fatalf("got return code %v, want %v", gotRetStatus.Code(), codes.OK) - } - }) + s := createServer(t, 8087) + go runServer(t, s) + defer s.s.Stop() + + // prepareDb(t) + + //t.Log("Start gNMI client") + tlsConfig := &tls.Config{InsecureSkipVerify: true} + opts := []grpc.DialOption{grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig))} + + //targetAddr := "30.57.185.38:8080" + targetAddr := "127.0.0.1:8087" + conn, err := grpc.Dial(targetAddr, opts...) + if err != nil { + t.Fatalf("Dialing to %q failed: %v", targetAddr, err) + } + defer conn.Close() + + gClient := pb.NewGNMIClient(conn) + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + t.Run("Invalid Bundle Version Format", func(t *testing.T) { + var pbPath *pb.Path + pbPath, err := xpath.ToGNMIPath("openconfig-interfaces:interfaces/interface[name=Ethernet0]/config") + prefix := pb.Path{Target: "OC-YANG"} + if err != nil { + t.Fatalf("error in unmarshaling path: %v", err) + } + bundleVersion := "50.0.0" + bv, err := proto.Marshal(&spb.BundleVersion{ + Version: bundleVersion, + }) + if err != nil { + t.Fatalf("%v", err) + } + req := &pb.GetRequest{ + Path: []*pb.Path{pbPath}, + Prefix: &prefix, + Encoding: pb.Encoding_JSON_IETF, + } + req.Extension = append(req.Extension, &ext_pb.Extension{ + Ext: &ext_pb.Extension_RegisteredExt{ + RegisteredExt: &ext_pb.RegisteredExtension{ + Id: spb.BUNDLE_VERSION_EXT, + Msg: bv, + }}}) + + _, err = gClient.Get(ctx, req) + gotRetStatus, ok := status.FromError(err) + if !ok { + t.Fatal("got a non-grpc error from grpc call") + } + if gotRetStatus.Code() != codes.NotFound { + t.Log("err: ", err) + t.Fatalf("got return code %v, want %v", gotRetStatus.Code(), codes.OK) + } + }) } func TestBulkSet(t *testing.T) { - s := createServer(t, 8088) - go runServer(t, s) - defer s.s.Stop() - - // prepareDb(t) - - //t.Log("Start gNMI client") - tlsConfig := &tls.Config{InsecureSkipVerify: true} - opts := []grpc.DialOption{grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig))} - - //targetAddr := "30.57.185.38:8080" - targetAddr := "127.0.0.1:8088" - conn, err := grpc.Dial(targetAddr, opts...) - if err != nil { - t.Fatalf("Dialing to %q failed: %v", targetAddr, err) - } - defer conn.Close() - - gClient := pb.NewGNMIClient(conn) - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - t.Run("Set Multiple mtu", func(t *testing.T) { - pbPath1, _ := xpath.ToGNMIPath("openconfig-interfaces:interfaces/interface[name=Ethernet0]/config/mtu") - v := &pb.TypedValue{ - Value: &pb.TypedValue_JsonIetfVal{JsonIetfVal: []byte("{\"mtu\": 9104}")}} - update1 := &pb.Update { - Path: pbPath1, - Val: v, - } - pbPath2, _ := xpath.ToGNMIPath("openconfig-interfaces:interfaces/interface[name=Ethernet4]/config/mtu") - v2 := &pb.TypedValue{ - Value: &pb.TypedValue_JsonIetfVal{JsonIetfVal: []byte("{\"mtu\": 9105}")}} - update2 := &pb.Update { - Path: pbPath2, - Val: v2, - } - - - req := &pb.SetRequest{ - Update: []*pb.Update{update1, update2}, - } - - _, err = gClient.Set(ctx, req) - _, ok := status.FromError(err) - if !ok { - t.Fatal("got a non-grpc error from grpc call") - } - - }) + s := createServer(t, 8088) + go runServer(t, s) + defer s.s.Stop() + + // prepareDb(t) + + //t.Log("Start gNMI client") + tlsConfig := &tls.Config{InsecureSkipVerify: true} + opts := []grpc.DialOption{grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig))} + + //targetAddr := "30.57.185.38:8080" + targetAddr := "127.0.0.1:8088" + conn, err := grpc.Dial(targetAddr, opts...) + if err != nil { + t.Fatalf("Dialing to %q failed: %v", targetAddr, err) + } + defer conn.Close() + + gClient := pb.NewGNMIClient(conn) + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + t.Run("Set Multiple mtu", func(t *testing.T) { + pbPath1, _ := xpath.ToGNMIPath("openconfig-interfaces:interfaces/interface[name=Ethernet0]/config/mtu") + v := &pb.TypedValue{ + Value: &pb.TypedValue_JsonIetfVal{JsonIetfVal: []byte("{\"mtu\": 9104}")}} + update1 := &pb.Update{ + Path: pbPath1, + Val: v, + } + pbPath2, _ := xpath.ToGNMIPath("openconfig-interfaces:interfaces/interface[name=Ethernet4]/config/mtu") + v2 := &pb.TypedValue{ + Value: &pb.TypedValue_JsonIetfVal{JsonIetfVal: []byte("{\"mtu\": 9105}")}} + update2 := &pb.Update{ + Path: pbPath2, + Val: v2, + } + + req := &pb.SetRequest{ + Update: []*pb.Update{update1, update2}, + } + + _, err = gClient.Set(ctx, req) + _, ok := status.FromError(err) + if !ok { + t.Fatal("got a non-grpc error from grpc call") + } + + }) } diff --git a/sonic_data_client/db_client.go b/sonic_data_client/db_client.go index 6ba9c9269..fed5e07b7 100644 --- a/sonic_data_client/db_client.go +++ b/sonic_data_client/db_client.go @@ -378,12 +378,12 @@ func GetTableKeySeparator(target string, ns string) (string, error) { var separator string = sdcfg.GetDbSeparator(target, ns) return separator, nil } -func GetRedisClientsForDb(target string) (map[string]*redis.Client) { +func GetRedisClientsForDb(target string) map[string]*redis.Client { redis_client_map := make(map[string]*redis.Client) - if sdcfg.CheckDbMultiNamespace() { + if sdcfg.CheckDbMultiNamespace() { ns_list, _ := sdcfg.GetDbNonDefaultNamespaces() for _, ns := range ns_list { - redis_client_map[ns] = Target2RedisDb[ns][target] + redis_client_map[ns] = Target2RedisDb[ns][target] } } else { ns := sdcfg.GetDbDefaultNamespace() @@ -391,70 +391,71 @@ func GetRedisClientsForDb(target string) (map[string]*redis.Client) { } return redis_client_map } -func IsTargetDb ( target string) (string, bool, string, bool) { +func IsTargetDb(target string) (string, bool, string, bool) { targetname := strings.Split(target, "/") - dbName:= targetname[0] + dbName := targetname[0] dbNameSpaceExist := false dbNamespace := sdcfg.GetDbDefaultNamespace() if len(targetname) > 2 { - return dbName, false, dbNamespace, dbNameSpaceExist + return dbName, false, dbNamespace, dbNameSpaceExist } if len(targetname) > 1 { dbNamespace = targetname[1] dbNameSpaceExist = true } - for name, _ := range spb.Target_value { - if name == dbName { - return dbName, true, dbNamespace, dbNameSpaceExist - } - } + for name, _ := range spb.Target_value { + if name == dbName { + return dbName, true, dbNamespace, dbNameSpaceExist + } + } - return dbName, false, dbNamespace, dbNameSpaceExist + return dbName, false, dbNamespace, dbNameSpaceExist } + // For testing only func useRedisTcpClient() { - if UseRedisLocalTcpPort { - for _, dbNamespace := range(sdcfg.GetDbAllNamespaces()) { - Target2RedisDb[dbNamespace] = make(map[string]*redis.Client) - for dbName, dbn := range spb.Target_value { - if dbName != "OTHERS" { - // DB connector for direct redis operation - var redisDb *redis.Client - redisDb = redis.NewClient(&redis.Options{ - Network: "tcp", - Addr: sdcfg.GetDbTcpAddr(dbName, dbNamespace), - Password: "", // no password set - DB: int(dbn), - DialTimeout: 0, - }) - Target2RedisDb[dbNamespace][dbName] = redisDb - } - } - } - } + if UseRedisLocalTcpPort { + for _, dbNamespace := range sdcfg.GetDbAllNamespaces() { + Target2RedisDb[dbNamespace] = make(map[string]*redis.Client) + for dbName, dbn := range spb.Target_value { + if dbName != "OTHERS" { + // DB connector for direct redis operation + var redisDb *redis.Client + redisDb = redis.NewClient(&redis.Options{ + Network: "tcp", + Addr: sdcfg.GetDbTcpAddr(dbName, dbNamespace), + Password: "", // no password set + DB: int(dbn), + DialTimeout: 0, + }) + Target2RedisDb[dbNamespace][dbName] = redisDb + } + } + } + } } // Client package prepare redis clients to all DBs automatically func init() { - for _, dbNamespace := range(sdcfg.GetDbAllNamespaces()) { - Target2RedisDb[dbNamespace] = make(map[string]*redis.Client) - for dbName, dbn := range spb.Target_value { - if dbName != "OTHERS" { - // DB connector for direct redis operation - var redisDb *redis.Client - redisDb = redis.NewClient(&redis.Options{ - Network: "unix", - Addr: sdcfg.GetDbSock(dbName, dbNamespace), - Password: "", // no password set - DB: int(dbn), - DialTimeout: 0, - }) - Target2RedisDb[dbNamespace][dbName] = redisDb - } - } - } + for _, dbNamespace := range sdcfg.GetDbAllNamespaces() { + Target2RedisDb[dbNamespace] = make(map[string]*redis.Client) + for dbName, dbn := range spb.Target_value { + if dbName != "OTHERS" { + // DB connector for direct redis operation + var redisDb *redis.Client + redisDb = redis.NewClient(&redis.Options{ + Network: "unix", + Addr: sdcfg.GetDbSock(dbName, dbNamespace), + Password: "", // no password set + DB: int(dbn), + DialTimeout: 0, + }) + Target2RedisDb[dbNamespace][dbName] = redisDb + } + } + } } // gnmiFullPath builds the full path from the prefix and path. @@ -532,7 +533,7 @@ func populateDbtablePath(prefix, path *gnmipb.Path, pathG2S *map[*gnmipb.Path][] } else { log.V(5).Infof("v2r lookup failed for %v %v", stringSlice, err) } - tblPath.dbNamespace = dbNamespace + tblPath.dbNamespace = dbNamespace tblPath.dbName = targetDbName tblPath.tableName = stringSlice[1] tblPath.delimitor = separator @@ -547,7 +548,6 @@ func populateDbtablePath(prefix, path *gnmipb.Path, pathG2S *map[*gnmipb.Path][] return fmt.Errorf("Redis Client not present for dbName %v dbNamespace", targetDbName, dbNamespace) } - // The expect real db path could be in one of the formats: // <1> DB Table // <2> DB Table Key @@ -1215,7 +1215,7 @@ func dbTableKeySubscribe(c *DbClient, gnmiPath *gnmipb.Path, interval time.Durat } } -func (c *DbClient) Set(delete []*gnmipb.Path, replace []*gnmipb.Update, update []*gnmipb.Update) error { +func (c *DbClient) Set(delete []*gnmipb.Path, replace []*gnmipb.Update, update []*gnmipb.Update) error { return nil } func (c *DbClient) Capabilities() []gnmipb.ModelData { diff --git a/sonic_data_client/virtual_db.go b/sonic_data_client/virtual_db.go index 8f6b13df0..c14b1d4ec 100644 --- a/sonic_data_client/virtual_db.go +++ b/sonic_data_client/virtual_db.go @@ -123,87 +123,87 @@ func getPfcwdMap() (map[string]map[string]string, error) { var pfcwdName_map = make(map[string]map[string]string) dbName := "CONFIG_DB" - for namespace, redisDb := range(GetRedisClientsForDb(dbName)) { - separator, _ := GetTableKeySeparator(dbName, namespace) - _, err := redisDb.Ping().Result() - if err != nil { - log.V(1).Infof("Can not connect to %v, err: %v", dbName, err) - return nil, err - } - - keyName := fmt.Sprintf("PFC_WD_TABLE%v*", separator) - resp, err := redisDb.Keys(keyName).Result() - if err != nil { - log.V(1).Infof("redis get keys failed for %v, key = %v, err: %v", dbName, keyName, err) - return nil, err - } - - if len(resp) == 0 { - // PFC WD service not enabled on device - log.V(1).Infof("PFC WD not enabled on device") - return nil, nil - } - - for _, key := range resp { - name := key[13:] - pfcwdName_map[name] = make(map[string]string) - } - - // Get Queue indexes that are enabled with PFC-WD - keyName = "PORT_QOS_MAP*" - resp, err = redisDb.Keys(keyName).Result() - if err != nil { - log.V(1).Infof("redis get keys failed for %v, key = %v, err: %v", dbName, keyName, err) - return nil, err - } - if len(resp) == 0 { - log.V(1).Infof("PFC WD not enabled on device") - return nil, nil - } - qos_key := resp[0] - - fieldName := "pfc_enable" - priorities, err := redisDb.HGet(qos_key, fieldName).Result() - if err != nil { - log.V(1).Infof("redis get field failed for %v, key = %v, field = %v, err: %v", dbName, qos_key, fieldName, err) - return nil, err - } - - keyName = fmt.Sprintf("MAP_PFC_PRIORITY_TO_QUEUE%vAZURE", separator) - pfc_queue_map, err := redisDb.HGetAll(keyName).Result() - if err != nil { - log.V(1).Infof("redis get fields failed for %v, key = %v, err: %v", dbName, keyName, err) - return nil, err - } - - var indices []string - for _, p := range strings.Split(priorities, ",") { - _, ok := pfc_queue_map[p] - if !ok { - log.V(1).Infof("Missing mapping between PFC priority %v to queue", p) - } else { - indices = append(indices, pfc_queue_map[p]) - } - } - - if len(countersQueueNameMap) == 0 { - log.V(1).Infof("COUNTERS_QUEUE_NAME_MAP is empty") - return nil, nil - } - - var queue_key string - queue_separator, _ := GetTableKeySeparator("COUNTERS_DB", namespace) - for port, _ := range pfcwdName_map { - for _, indice := range indices { - queue_key = port + queue_separator + indice - oid, ok := countersQueueNameMap[queue_key] - if !ok { - return nil, fmt.Errorf("key %v not exists in COUNTERS_QUEUE_NAME_MAP", queue_key) - } - pfcwdName_map[port][queue_key] = oid - } - } -} + for namespace, redisDb := range GetRedisClientsForDb(dbName) { + separator, _ := GetTableKeySeparator(dbName, namespace) + _, err := redisDb.Ping().Result() + if err != nil { + log.V(1).Infof("Can not connect to %v, err: %v", dbName, err) + return nil, err + } + + keyName := fmt.Sprintf("PFC_WD_TABLE%v*", separator) + resp, err := redisDb.Keys(keyName).Result() + if err != nil { + log.V(1).Infof("redis get keys failed for %v, key = %v, err: %v", dbName, keyName, err) + return nil, err + } + + if len(resp) == 0 { + // PFC WD service not enabled on device + log.V(1).Infof("PFC WD not enabled on device") + return nil, nil + } + + for _, key := range resp { + name := key[13:] + pfcwdName_map[name] = make(map[string]string) + } + + // Get Queue indexes that are enabled with PFC-WD + keyName = "PORT_QOS_MAP*" + resp, err = redisDb.Keys(keyName).Result() + if err != nil { + log.V(1).Infof("redis get keys failed for %v, key = %v, err: %v", dbName, keyName, err) + return nil, err + } + if len(resp) == 0 { + log.V(1).Infof("PFC WD not enabled on device") + return nil, nil + } + qos_key := resp[0] + + fieldName := "pfc_enable" + priorities, err := redisDb.HGet(qos_key, fieldName).Result() + if err != nil { + log.V(1).Infof("redis get field failed for %v, key = %v, field = %v, err: %v", dbName, qos_key, fieldName, err) + return nil, err + } + + keyName = fmt.Sprintf("MAP_PFC_PRIORITY_TO_QUEUE%vAZURE", separator) + pfc_queue_map, err := redisDb.HGetAll(keyName).Result() + if err != nil { + log.V(1).Infof("redis get fields failed for %v, key = %v, err: %v", dbName, keyName, err) + return nil, err + } + + var indices []string + for _, p := range strings.Split(priorities, ",") { + _, ok := pfc_queue_map[p] + if !ok { + log.V(1).Infof("Missing mapping between PFC priority %v to queue", p) + } else { + indices = append(indices, pfc_queue_map[p]) + } + } + + if len(countersQueueNameMap) == 0 { + log.V(1).Infof("COUNTERS_QUEUE_NAME_MAP is empty") + return nil, nil + } + + var queue_key string + queue_separator, _ := GetTableKeySeparator("COUNTERS_DB", namespace) + for port, _ := range pfcwdName_map { + for _, indice := range indices { + queue_key = port + queue_separator + indice + oid, ok := countersQueueNameMap[queue_key] + if !ok { + return nil, fmt.Errorf("key %v not exists in COUNTERS_QUEUE_NAME_MAP", queue_key) + } + pfcwdName_map[port][queue_key] = oid + } + } + } log.V(6).Infof("countersPfcwdNameMap: %v", pfcwdName_map) return pfcwdName_map, nil @@ -216,7 +216,7 @@ func getAliasMap() (map[string]string, map[string]string, map[string]string, err var port2namespace_map = make(map[string]string) dbName := "CONFIG_DB" - for namespace, redisDb := range(GetRedisClientsForDb(dbName)) { + for namespace, redisDb := range GetRedisClientsForDb(dbName) { separator, _ := GetTableKeySeparator(dbName, namespace) _, err := redisDb.Ping().Result() if err != nil { @@ -250,11 +250,12 @@ func getAliasMap() (map[string]string, map[string]string, map[string]string, err log.V(6).Infof("port2namespaceMap: %v", port2namespace_map) return alias2name_map, name2alias_map, port2namespace_map, nil } + // Ref: https://stackoverflow.com/questions/12172215/merging-maps-in-go func addmap(a map[string]string, b map[string]string) { - for k,v := range b { - a[k] = v - } + for k, v := range b { + a[k] = v + } } // Get the mapping between objects in counters DB, Ex. port name to oid in "COUNTERS_PORT_NAME_MAP" table. @@ -262,14 +263,14 @@ func addmap(a map[string]string, b map[string]string) { func getCountersMap(tableName string) (map[string]string, error) { counter_map := make(map[string]string) dbName := "COUNTERS_DB" - for _, redisDb := range(GetRedisClientsForDb(dbName)) { + for _, redisDb := range GetRedisClientsForDb(dbName) { fv, err := redisDb.HGetAll(tableName).Result() if err != nil { log.V(2).Infof("redis HGetAll failed for COUNTERS_DB, tableName: %s", tableName) return nil, err } - addmap(counter_map, fv) - log.V(6).Infof("tableName: %s, map %v", tableName, fv) + addmap(counter_map, fv) + log.V(6).Infof("tableName: %s, map %v", tableName, fv) } return counter_map, nil } @@ -291,9 +292,9 @@ func v2rEthPortStats(paths []string) ([]tablePath, error) { if !ok { return nil, fmt.Errorf("%v does not have namespace associated", port) } - separator, _ := GetTableKeySeparator(paths[DbIdx], namespace) + separator, _ := GetTableKeySeparator(paths[DbIdx], namespace) tblPath := tablePath{ - dbNamespace: namespace, + dbNamespace: namespace, dbName: paths[DbIdx], tableName: paths[TblIdx], tableKey: oid, @@ -317,13 +318,13 @@ func v2rEthPortStats(paths []string) ([]tablePath, error) { if !ok { return nil, fmt.Errorf("%v does not have namespace associated", name) } - separator, _ := GetTableKeySeparator(paths[DbIdx], namespace) + separator, _ := GetTableKeySeparator(paths[DbIdx], namespace) tblPaths = []tablePath{{ - dbNamespace: namespace, - dbName: paths[DbIdx], - tableName: paths[TblIdx], - tableKey: oid, - delimitor: separator, + dbNamespace: namespace, + dbName: paths[DbIdx], + tableName: paths[TblIdx], + tableKey: oid, + delimitor: separator, }} } log.V(6).Infof("v2rEthPortStats: %v", tblPaths) @@ -351,9 +352,9 @@ func v2rEthPortFieldStats(paths []string) ([]tablePath, error) { if !ok { return nil, fmt.Errorf("%v does not have namespace associated", port) } - separator, _ := GetTableKeySeparator(paths[DbIdx], namespace) + separator, _ := GetTableKeySeparator(paths[DbIdx], namespace) tblPath := tablePath{ - dbNamespace: namespace, + dbNamespace: namespace, dbName: paths[DbIdx], tableName: paths[TblIdx], tableKey: oid, @@ -379,14 +380,14 @@ func v2rEthPortFieldStats(paths []string) ([]tablePath, error) { if !ok { return nil, fmt.Errorf("%v does not have namespace associated", name) } - separator, _ := GetTableKeySeparator(paths[DbIdx], namespace) + separator, _ := GetTableKeySeparator(paths[DbIdx], namespace) tblPaths = []tablePath{{ - dbNamespace: namespace, - dbName: paths[DbIdx], - tableName: paths[TblIdx], - tableKey: oid, - field: paths[FieldIdx], - delimitor: separator, + dbNamespace: namespace, + dbName: paths[DbIdx], + tableName: paths[TblIdx], + tableKey: oid, + field: paths[FieldIdx], + delimitor: separator, }} } log.V(6).Infof("v2rEthPortFieldStats: %+v", tblPaths) @@ -399,13 +400,13 @@ func v2rEthPortPfcwdStats(paths []string) ([]tablePath, error) { var tblPaths []tablePath if strings.HasSuffix(paths[KeyIdx], "*") { // Pfcwd on all Ethernet ports for port, pfcqueues := range countersPfcwdNameMap { - namespace, ok := port2namespaceMap[port] - if !ok { - return nil, fmt.Errorf("%v does not have namespace associated", port) - } - separator, _ := GetTableKeySeparator(paths[DbIdx], namespace) + namespace, ok := port2namespaceMap[port] + if !ok { + return nil, fmt.Errorf("%v does not have namespace associated", port) + } + separator, _ := GetTableKeySeparator(paths[DbIdx], namespace) for pfcque, oid := range pfcqueues { - // pfcque is in format of "Interface:12" + // pfcque is in format of "Interface:12" names := strings.Split(pfcque, separator) var oname string if alias, ok := name2aliasMap[names[0]]; ok { @@ -440,7 +441,7 @@ func v2rEthPortPfcwdStats(paths []string) ([]tablePath, error) { if !ok { return nil, fmt.Errorf("%v not a valid SONiC interface. Vendor alias is %v", name, alias) } - separator, _ := GetTableKeySeparator(paths[DbIdx], namespace) + separator, _ := GetTableKeySeparator(paths[DbIdx], namespace) pfcqueues, ok := countersPfcwdNameMap[name] if ok { diff --git a/sonic_db_config/db_config_test.go b/sonic_db_config/db_config_test.go index debfbb27a..381f1f6b7 100644 --- a/sonic_db_config/db_config_test.go +++ b/sonic_db_config/db_config_test.go @@ -3,7 +3,6 @@ package dbconfig import ( "github.com/Azure/sonic-telemetry/test_utils" "testing" - ) func TestGetDb(t *testing.T) { @@ -44,7 +43,7 @@ func TestGetDbMultiNs(t *testing.T) { /* https://www.gopherguides.com/articles/test-cleanup-in-go-1-14*/ t.Cleanup(func() { - if err:= test_utils.CleanUpMultiNamespace(); err != nil { + if err := test_utils.CleanUpMultiNamespace(); err != nil { t.Fatalf("error Cleaning up MultiNamespace files with err %T", err) } diff --git a/test_utils/test_utils.go b/test_utils/test_utils.go index 53faf02a9..1a83ad9c9 100644 --- a/test_utils/test_utils.go +++ b/test_utils/test_utils.go @@ -1,11 +1,11 @@ package test_utils + import ( "io" "os" ) - -func SetupMultiNamespace() (error) { +func SetupMultiNamespace() error { err := os.MkdirAll("/var/run/redis0/sonic-db/", 0755) if err != nil { return err @@ -53,5 +53,5 @@ func CleanUpMultiNamespace() error { return nil } func GetMultiNsNamespace() string { - return "asic0" + return "asic0" } From e2f527add97db05f48992372bcbc794e9b03a715 Mon Sep 17 00:00:00 2001 From: Abhishek Dosi Date: Tue, 27 Apr 2021 18:23:08 +0000 Subject: [PATCH 08/13] Some cleanup Signed-off-by: Abhishek Dosi --- sonic_db_config/db_config.go | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/sonic_db_config/db_config.go b/sonic_db_config/db_config.go index 26414b31e..f06899d26 100644 --- a/sonic_db_config/db_config.go +++ b/sonic_db_config/db_config.go @@ -168,28 +168,6 @@ func GetDbTcpAddr(db_name string, ns string) string { return hostname + ":" + strconv.Itoa(port) } -func fetchValue(value interface{}) { // pass the interface value from the struct in this function as it will run recursively to get the value. - switch value.(type) { - case string: - fmt.Printf("%v is an string \n ", value.(string)) - case bool: - fmt.Printf("%v is bool \n ", value.(bool)) - case float64: - fmt.Printf("%v is float64 \n ", value.(float64)) - case []interface{}: - fmt.Printf("%v is a slice of interface \n ", value) - for _, v := range value.([]interface{}) { - fetchValue(v) - } - case map[string]interface{}: - fmt.Printf("%v is a map \n ", value) - for _, v := range value.(map[string]interface{}) { - fetchValue(v) - } - default: - fmt.Printf("%v is unknown \n ", value) - } -} func DbGetNamespaceAndConfigFile(ns_to_cfgfile_map map[string]string) { data, err := io.ReadFile(SONIC_DB_GLOBAL_CONFIG_FILE) if err == nil { From a3840953e2dd5ff4b39434b276ca208b331897d1 Mon Sep 17 00:00:00 2001 From: Abhishek Dosi Date: Tue, 27 Apr 2021 19:14:21 +0000 Subject: [PATCH 09/13] Added sudo to the Go test Signed-off-by: Abhishek Dosi --- Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 24c4e6709..e7cede393 100644 --- a/Makefile +++ b/Makefile @@ -60,9 +60,9 @@ check: sudo cp ./testdata/database_config.json ${DBDIR} sudo mkdir -p /usr/models/yang || true sudo find $(MGMT_COMMON_DIR)/models -name '*.yang' -exec cp {} /usr/models/yang/ \; - -$(GO) test -v github.com/Azure/sonic-telemetry/sonic_db_config - -$(GO) test -mod=vendor $(BLD_FLAGS) -v github.com/Azure/sonic-telemetry/gnmi_server - -$(GO) test -mod=vendor $(BLD_FLAGS) -v github.com/Azure/sonic-telemetry/dialout/dialout_client + -sudo $(GO) test -v github.com/Azure/sonic-telemetry/sonic_db_config + -sudo $(GO) test -mod=vendor $(BLD_FLAGS) -v github.com/Azure/sonic-telemetry/gnmi_server + -sudo $(GO) test -mod=vendor $(BLD_FLAGS) -v github.com/Azure/sonic-telemetry/dialout/dialout_client clean: $(RM) -r build From ee0d75f129e2b0032f3cb81ce7985e3ed95aaddb Mon Sep 17 00:00:00 2001 From: Abhishek Dosi Date: Wed, 28 Apr 2021 18:22:48 +0000 Subject: [PATCH 10/13] Address Review Comments Signed-off-by: Abhishek Dosi --- Makefile | 2 +- sonic_data_client/db_client.go | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index e7cede393..dcf12ad8a 100644 --- a/Makefile +++ b/Makefile @@ -67,7 +67,7 @@ check: clean: $(RM) -r build $(RM) -r vendor - $(RM) -r ${DBDIR} + sudo $(RM) -r ${DBDIR} $(TELEMETRY_TEST_BIN): $(TEST_FILES) $(SRC_FILES) mkdir -p $(@D) diff --git a/sonic_data_client/db_client.go b/sonic_data_client/db_client.go index fed5e07b7..235afbab5 100644 --- a/sonic_data_client/db_client.go +++ b/sonic_data_client/db_client.go @@ -378,6 +378,7 @@ func GetTableKeySeparator(target string, ns string) (string, error) { var separator string = sdcfg.GetDbSeparator(target, ns) return separator, nil } + func GetRedisClientsForDb(target string) map[string]*redis.Client { redis_client_map := make(map[string]*redis.Client) if sdcfg.CheckDbMultiNamespace() { @@ -391,6 +392,10 @@ func GetRedisClientsForDb(target string) map[string]*redis.Client { } return redis_client_map } + +// This function get target present in GNMI Request and +// returns: 1. DbName (string) 2. Is DbName valid (bool) +// 3. DbNamespace (string) 4. Is DbNamespace present in Target func IsTargetDb(target string) (string, bool, string, bool) { targetname := strings.Split(target, "/") dbName := targetname[0] @@ -1218,6 +1223,7 @@ func dbTableKeySubscribe(c *DbClient, gnmiPath *gnmipb.Path, interval time.Durat func (c *DbClient) Set(delete []*gnmipb.Path, replace []*gnmipb.Update, update []*gnmipb.Update) error { return nil } + func (c *DbClient) Capabilities() []gnmipb.ModelData { return nil } From d37904e13094b2fcbb4bce32dbe4c76108c74a6d Mon Sep 17 00:00:00 2001 From: Abhishek Dosi Date: Tue, 4 May 2021 19:42:43 +0000 Subject: [PATCH 11/13] Address Review Comments Signed-off-by: Abhishek Dosi --- sonic_data_client/db_client.go | 13 +++++++------ sonic_data_client/virtual_db.go | 22 +++++++++++----------- sonic_db_config/db_config.go | 8 ++++---- 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/sonic_data_client/db_client.go b/sonic_data_client/db_client.go index 235afbab5..830807488 100644 --- a/sonic_data_client/db_client.go +++ b/sonic_data_client/db_client.go @@ -395,7 +395,7 @@ func GetRedisClientsForDb(target string) map[string]*redis.Client { // This function get target present in GNMI Request and // returns: 1. DbName (string) 2. Is DbName valid (bool) -// 3. DbNamespace (string) 4. Is DbNamespace present in Target +// 3. DbNamespace (string) 4. Is DbNamespace present in Target (bool) func IsTargetDb(target string) (string, bool, string, bool) { targetname := strings.Split(target, "/") dbName := targetname[0] @@ -403,6 +403,7 @@ func IsTargetDb(target string) (string, bool, string, bool) { dbNamespace := sdcfg.GetDbDefaultNamespace() if len(targetname) > 2 { + log.V(1).Infof("target format is not correct") return dbName, false, dbNamespace, dbNameSpaceExist } @@ -494,16 +495,16 @@ func populateDbtablePath(prefix, path *gnmipb.Path, pathG2S *map[*gnmipb.Path][] target := prefix.GetTarget() targetDbName, targetDbNameValid, targetDbNameSpace, targetDbNameSpaceExist := IsTargetDb(target) + // Verify it is a valid db name + if !targetDbNameValid { + return fmt.Errorf("Invalid target dbName %v", targetDbName) + } // Verify Namespace is valid dbNamespace, ok := sdcfg.GetDbNamespaceFromTarget(targetDbNameSpace) if !ok { return fmt.Errorf("Invalid target dbNameSpace %v", targetDbNameSpace) } - // Verify it is a valid db name - if !targetDbNameValid { - return fmt.Errorf("Invalid target dbName %v", targetDbName) - } fullPath := path if prefix != nil { @@ -550,7 +551,7 @@ func populateDbtablePath(prefix, path *gnmipb.Path, pathG2S *map[*gnmipb.Path][] redisDb, ok := Target2RedisDb[tblPath.dbNamespace][tblPath.dbName] if !ok { - return fmt.Errorf("Redis Client not present for dbName %v dbNamespace", targetDbName, dbNamespace) + return fmt.Errorf("Redis Client not present for dbName %v dbNamespace %v", targetDbName, dbNamespace) } // The expect real db path could be in one of the formats: diff --git a/sonic_data_client/virtual_db.go b/sonic_data_client/virtual_db.go index c14b1d4ec..b92020454 100644 --- a/sonic_data_client/virtual_db.go +++ b/sonic_data_client/virtual_db.go @@ -127,14 +127,14 @@ func getPfcwdMap() (map[string]map[string]string, error) { separator, _ := GetTableKeySeparator(dbName, namespace) _, err := redisDb.Ping().Result() if err != nil { - log.V(1).Infof("Can not connect to %v, err: %v", dbName, err) + log.V(1).Infof("Can not connect to %v in namsespace %v, err: %v", dbName, namespace, err) return nil, err } keyName := fmt.Sprintf("PFC_WD_TABLE%v*", separator) resp, err := redisDb.Keys(keyName).Result() if err != nil { - log.V(1).Infof("redis get keys failed for %v, key = %v, err: %v", dbName, keyName, err) + log.V(1).Infof("redis get keys failed for %v in namsepace %v, key = %v, err: %v", dbName, namespace, keyName, err) return nil, err } @@ -153,7 +153,7 @@ func getPfcwdMap() (map[string]map[string]string, error) { keyName = "PORT_QOS_MAP*" resp, err = redisDb.Keys(keyName).Result() if err != nil { - log.V(1).Infof("redis get keys failed for %v, key = %v, err: %v", dbName, keyName, err) + log.V(1).Infof("redis get keys failed for %v in namespace %v, key = %v, err: %v", dbName, namespace, keyName, err) return nil, err } if len(resp) == 0 { @@ -165,14 +165,14 @@ func getPfcwdMap() (map[string]map[string]string, error) { fieldName := "pfc_enable" priorities, err := redisDb.HGet(qos_key, fieldName).Result() if err != nil { - log.V(1).Infof("redis get field failed for %v, key = %v, field = %v, err: %v", dbName, qos_key, fieldName, err) + log.V(1).Infof("redis get field failed for %v in namsepace %v, key = %v, field = %v, err: %v", dbName, namespace, qos_key, fieldName, err) return nil, err } keyName = fmt.Sprintf("MAP_PFC_PRIORITY_TO_QUEUE%vAZURE", separator) pfc_queue_map, err := redisDb.HGetAll(keyName).Result() if err != nil { - log.V(1).Infof("redis get fields failed for %v, key = %v, err: %v", dbName, keyName, err) + log.V(1).Infof("redis get fields failed for %v in namsepace %v, key = %v, err: %v", dbName, namespace, keyName, err) return nil, err } @@ -220,20 +220,20 @@ func getAliasMap() (map[string]string, map[string]string, map[string]string, err separator, _ := GetTableKeySeparator(dbName, namespace) _, err := redisDb.Ping().Result() if err != nil { - log.V(1).Infof("Can not connect to %v, err: %v", dbName, err) + log.V(1).Infof("Can not connect to %v, in namsepace %v, err: %v", dbName, namespace, err) return nil, nil, nil, err } keyName := fmt.Sprintf("PORT%v*", separator) resp, err := redisDb.Keys(keyName).Result() if err != nil { - log.V(1).Infof("redis get keys failed for %v, key = %v, err: %v", dbName, keyName, err) + log.V(1).Infof("redis get keys failed for %v in namsepace %v, key = %v, err: %v", dbName, namespace, keyName, err) return nil, nil, nil, err } for _, key := range resp { alias, err := redisDb.HGet(key, "alias").Result() if err != nil { - log.V(1).Infof("redis get field alias failed for %v, key = %v, err: %v", dbName, key, err) + log.V(1).Infof("redis get field alias failed for %v in namsepace %v, key = %v, err: %v", dbName, namespace, key, err) // clear aliasMap alias2name_map = make(map[string]string) name2alias_map = make(map[string]string) @@ -263,14 +263,14 @@ func addmap(a map[string]string, b map[string]string) { func getCountersMap(tableName string) (map[string]string, error) { counter_map := make(map[string]string) dbName := "COUNTERS_DB" - for _, redisDb := range GetRedisClientsForDb(dbName) { + for namespace, redisDb := range GetRedisClientsForDb(dbName) { fv, err := redisDb.HGetAll(tableName).Result() if err != nil { - log.V(2).Infof("redis HGetAll failed for COUNTERS_DB, tableName: %s", tableName) + log.V(2).Infof("redis HGetAll failed for COUNTERS_DB in namespace %v, tableName: %s", namespace, tableName) return nil, err } addmap(counter_map, fv) - log.V(6).Infof("tableName: %s, map %v", tableName, fv) + log.V(6).Infof("tableName: %s in namespace %v, map %v", tableName, namespace, fv) } return counter_map, nil } diff --git a/sonic_db_config/db_config.go b/sonic_db_config/db_config.go index f06899d26..7f88f14b7 100644 --- a/sonic_db_config/db_config.go +++ b/sonic_db_config/db_config.go @@ -75,7 +75,7 @@ func GetDbList(ns string) map[string]interface{} { } db_list, ok := sonic_db_config[ns]["DATABASES"].(map[string]interface{}) if !ok { - panic(fmt.Errorf("DATABASES' is not valid key in database_config.json file for namespace %q!", ns)) + panic(fmt.Errorf("DATABASES' is not valid key in database_config.json file for namespace `%v` !", ns)) } return db_list } @@ -86,15 +86,15 @@ func GetDbInst(db_name string, ns string) map[string]interface{} { } db, ok := sonic_db_config[ns]["DATABASES"].(map[string]interface{})[db_name] if !ok { - panic(fmt.Errorf("database name '%v' is not valid in database_config.json file!", db_name)) + panic(fmt.Errorf("database name '%v' is not valid in database_config.json file for namespace `%v`!", db_name, ns)) } inst_name, ok := db.(map[string]interface{})["instance"] if !ok { - panic(fmt.Errorf("'instance' is not a valid field in database_config.json file!")) + panic(fmt.Errorf("'instance' is not a valid field in database_config.json file for namespace `%v`!", ns)) } inst, ok := sonic_db_config[ns]["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)) + panic(fmt.Errorf("instance name '%v' is not valid in database_config.json file for namespace `%v`!", inst_name, ns)) } return inst.(map[string]interface{}) } From 78620e53c30ac9e6d4175d8d29fd356c2abb8279 Mon Sep 17 00:00:00 2001 From: Abhishek Dosi Date: Tue, 1 Jun 2021 19:29:02 +0000 Subject: [PATCH 12/13] Address Review Comments. Signed-off-by: Abhishek Dosi --- sonic_data_client/db_client.go | 37 +++++++++++++++---------------- sonic_db_config/db_config.go | 11 +++++---- sonic_db_config/db_config_test.go | 6 ++--- 3 files changed, 26 insertions(+), 28 deletions(-) diff --git a/sonic_data_client/db_client.go b/sonic_data_client/db_client.go index 830807488..b8fcab00e 100644 --- a/sonic_data_client/db_client.go +++ b/sonic_data_client/db_client.go @@ -382,7 +382,7 @@ func GetTableKeySeparator(target string, ns string) (string, error) { func GetRedisClientsForDb(target string) map[string]*redis.Client { redis_client_map := make(map[string]*redis.Client) if sdcfg.CheckDbMultiNamespace() { - ns_list, _ := sdcfg.GetDbNonDefaultNamespaces() + ns_list := sdcfg.GetDbNonDefaultNamespaces() for _, ns := range ns_list { redis_client_map[ns] = Target2RedisDb[ns][target] } @@ -422,22 +422,22 @@ func IsTargetDb(target string) (string, bool, string, bool) { // For testing only func useRedisTcpClient() { - if UseRedisLocalTcpPort { - for _, dbNamespace := range sdcfg.GetDbAllNamespaces() { - Target2RedisDb[dbNamespace] = make(map[string]*redis.Client) - for dbName, dbn := range spb.Target_value { - if dbName != "OTHERS" { - // DB connector for direct redis operation - var redisDb *redis.Client - redisDb = redis.NewClient(&redis.Options{ - Network: "tcp", - Addr: sdcfg.GetDbTcpAddr(dbName, dbNamespace), - Password: "", // no password set - DB: int(dbn), - DialTimeout: 0, - }) - Target2RedisDb[dbNamespace][dbName] = redisDb - } + if !UseRedisLocalTcpPort { + return + } + for _, dbNamespace := range sdcfg.GetDbAllNamespaces() { + Target2RedisDb[dbNamespace] = make(map[string]*redis.Client) + for dbName, dbn := range spb.Target_value { + if dbName != "OTHERS" { + // DB connector for direct redis operation + redisDb := redis.NewClient(&redis.Options{ + Network: "tcp", + Addr: sdcfg.GetDbTcpAddr(dbName, dbNamespace), + Password: "", // no password set + DB: int(dbn), + DialTimeout: 0, + }) + Target2RedisDb[dbNamespace][dbName] = redisDb } } } @@ -450,8 +450,7 @@ func init() { for dbName, dbn := range spb.Target_value { if dbName != "OTHERS" { // DB connector for direct redis operation - var redisDb *redis.Client - redisDb = redis.NewClient(&redis.Options{ + redisDb := redis.NewClient(&redis.Options{ Network: "unix", Addr: sdcfg.GetDbSock(dbName, dbNamespace), Password: "", // no password set diff --git a/sonic_db_config/db_config.go b/sonic_db_config/db_config.go index 7f88f14b7..2e37d9f6c 100644 --- a/sonic_db_config/db_config.go +++ b/sonic_db_config/db_config.go @@ -31,7 +31,7 @@ func CheckDbMultiNamespace() bool { } return sonic_db_multi_namespace } -func GetDbNonDefaultNamespaces() ([]string, int) { +func GetDbNonDefaultNamespaces() []string { if !sonic_db_init { DbInit() } @@ -42,7 +42,7 @@ func GetDbNonDefaultNamespaces() ([]string, int) { } ns_list = append(ns_list, ns) } - return ns_list, len(ns_list) + return ns_list } func GetDbAllNamespaces() []string { if !sonic_db_init { @@ -61,7 +61,7 @@ func GetDbNamespaceFromTarget(target string) (string, bool) { if target == GetDbDefaultNamespace() { return target, true } - ns_list, _ := GetDbNonDefaultNamespaces() + ns_list := GetDbNonDefaultNamespaces() for _, ns := range ns_list { if target == ns { return target, true @@ -176,12 +176,11 @@ func DbGetNamespaceAndConfigFile(ns_to_cfgfile_map map[string]string) { if err != nil { panic(err) } - var sonic_db_global_config = make(map[string]interface{}) + sonic_db_global_config := make(map[string]interface{}) err = json.Unmarshal([]byte(data), &sonic_db_global_config) if err != nil { panic(err) } - var db_include_file string for _, entry := range sonic_db_global_config["INCLUDES"].([]interface{}) { ns, ok := entry.(map[string]interface{})["namespace"] if !ok { @@ -192,7 +191,7 @@ func DbGetNamespaceAndConfigFile(ns_to_cfgfile_map map[string]string) { panic(fmt.Errorf("Global Database config file is not valid(multiple include for same namespace!")) } //Ref:https://www.geeksforgeeks.org/filepath-join-function-in-golang-with-examples/ - db_include_file = filepath.Join(dir, entry.(map[string]interface{})["include"].(string)) + db_include_file := filepath.Join(dir, entry.(map[string]interface{})["include"].(string)) ns_to_cfgfile_map[ns.(string)] = db_include_file } if len(ns_to_cfgfile_map) > 1 { diff --git a/sonic_db_config/db_config_test.go b/sonic_db_config/db_config_test.go index 381f1f6b7..ae2e31af5 100644 --- a/sonic_db_config/db_config_test.go +++ b/sonic_db_config/db_config_test.go @@ -15,7 +15,7 @@ func TestGetDb(t *testing.T) { t.Run("Sock", func(t *testing.T) { sock_path := GetDbSock("CONFIG_DB", GetDbDefaultNamespace()) if sock_path != "/var/run/redis/redis.sock" { - t.Fatalf(`Sock("") = %q, want "", error`, sock_path) + t.Fatalf(`Sock("") = %q, want "/var/run/redis/redis.sock", error`, sock_path) } }) t.Run("AllNamespaces", func(t *testing.T) { @@ -23,7 +23,7 @@ func TestGetDb(t *testing.T) { if len(ns_list) != 1 { t.Fatalf(`AllNamespaces("") = %q, want "1", error`, len(ns_list)) } - if !(ns_list[0] == GetDbDefaultNamespace()) { + if ns_list[0] != GetDbDefaultNamespace() { t.Fatalf(`AllNamespaces("") = %q, want default, error`, ns_list[0]) } }) @@ -57,7 +57,7 @@ func TestGetDbMultiNs(t *testing.T) { t.Run("Sock", func(t *testing.T) { sock_path := GetDbSock("CONFIG_DB", "asic0") if sock_path != "/var/run/redis0/redis.sock" { - t.Fatalf(`Sock("") = %q, want "", error`, sock_path) + t.Fatalf(`Sock("") = %q, want "/var/run/redis0/redis.sock", error`, sock_path) } }) t.Run("AllNamespaces", func(t *testing.T) { From 6a3f4332b1ff6d3e02ad10050ad15d04f1bb5e45 Mon Sep 17 00:00:00 2001 From: Abhishek Dosi Date: Fri, 11 Jun 2021 21:24:00 +0000 Subject: [PATCH 13/13] Make sure for V2R dataset if namespace is present in counter db path then always return error. Signed-off-by: Abhishek Dosi --- gnmi_server/server_test.go | 171 +++++++++++++++++---------------- sonic_data_client/db_client.go | 38 ++++---- 2 files changed, 110 insertions(+), 99 deletions(-) diff --git a/gnmi_server/server_test.go b/gnmi_server/server_test.go index db651045f..7a7f58d49 100644 --- a/gnmi_server/server_test.go +++ b/gnmi_server/server_test.go @@ -815,128 +815,137 @@ func runGnmiTestGet(t *testing.T, namespace string) { `, wantRetCode: codes.Unimplemented, }, { - desc: "Get valid but non-existing node", - pathTarget: "COUNTERS_DB", + desc: "Test passing asic in path for V2R Dataset Target", + pathTarget: "COUNTER_DB" + "/" + namespace, textPbPath: ` + elem: + elem: + `, + wantRetCode: codes.NotFound, + }, + { + desc: "Get valid but non-existing node", + pathTarget: "COUNTERS_DB", + textPbPath: ` elem: `, - wantRetCode: codes.NotFound, - }, { - desc: "Get COUNTERS_PORT_NAME_MAP", - pathTarget: "COUNTERS_DB", - textPbPath: ` + wantRetCode: codes.NotFound, + }, { + desc: "Get COUNTERS_PORT_NAME_MAP", + pathTarget: "COUNTERS_DB", + textPbPath: ` elem: `, - wantRetCode: codes.OK, - wantRespVal: countersPortNameMapByte, - valTest: true, - }, { - desc: "get COUNTERS:Ethernet68", - pathTarget: "COUNTERS_DB", - textPbPath: ` + wantRetCode: codes.OK, + wantRespVal: countersPortNameMapByte, + valTest: true, + }, { + desc: "get COUNTERS:Ethernet68", + pathTarget: "COUNTERS_DB", + textPbPath: ` elem: elem: `, - wantRetCode: codes.OK, - wantRespVal: countersEthernet68Byte, - valTest: true, - }, { - desc: "get COUNTERS:Ethernet68 SAI_PORT_STAT_PFC_7_RX_PKTS", - pathTarget: "COUNTERS_DB", - textPbPath: ` + wantRetCode: codes.OK, + wantRespVal: countersEthernet68Byte, + valTest: true, + }, { + desc: "get COUNTERS:Ethernet68 SAI_PORT_STAT_PFC_7_RX_PKTS", + pathTarget: "COUNTERS_DB", + textPbPath: ` elem: elem: elem: `, - wantRetCode: codes.OK, - wantRespVal: "2", - valTest: true, - }, { - desc: "get COUNTERS:Ethernet68 Pfcwd", - pathTarget: "COUNTERS_DB", - textPbPath: ` + wantRetCode: codes.OK, + wantRespVal: "2", + valTest: true, + }, { + desc: "get COUNTERS:Ethernet68 Pfcwd", + pathTarget: "COUNTERS_DB", + textPbPath: ` elem: elem: elem: `, - wantRetCode: codes.OK, - wantRespVal: countersEthernet68PfcwdByte, - valTest: true, - }, { - desc: "get COUNTERS (use vendor alias):Ethernet68/1", - pathTarget: "COUNTERS_DB", - textPbPath: ` + wantRetCode: codes.OK, + wantRespVal: countersEthernet68PfcwdByte, + valTest: true, + }, { + desc: "get COUNTERS (use vendor alias):Ethernet68/1", + pathTarget: "COUNTERS_DB", + textPbPath: ` elem: elem: `, - wantRetCode: codes.OK, - wantRespVal: countersEthernet68Byte, - valTest: true, - }, { - desc: "get COUNTERS (use vendor alias):Ethernet68/1 SAI_PORT_STAT_PFC_7_RX_PKTS", - pathTarget: "COUNTERS_DB", - textPbPath: ` + wantRetCode: codes.OK, + wantRespVal: countersEthernet68Byte, + valTest: true, + }, { + desc: "get COUNTERS (use vendor alias):Ethernet68/1 SAI_PORT_STAT_PFC_7_RX_PKTS", + pathTarget: "COUNTERS_DB", + textPbPath: ` elem: elem: elem: `, - wantRetCode: codes.OK, - wantRespVal: "2", - valTest: true, - }, { - desc: "get COUNTERS (use vendor alias):Ethernet68/1 Pfcwd", - pathTarget: "COUNTERS_DB", - textPbPath: ` + wantRetCode: codes.OK, + wantRespVal: "2", + valTest: true, + }, { + desc: "get COUNTERS (use vendor alias):Ethernet68/1 Pfcwd", + pathTarget: "COUNTERS_DB", + textPbPath: ` elem: elem: elem: `, - wantRetCode: codes.OK, - wantRespVal: countersEthernet68PfcwdAliasByte, - valTest: true, - }, { - desc: "get COUNTERS:Ethernet*", - pathTarget: "COUNTERS_DB", - textPbPath: ` + wantRetCode: codes.OK, + wantRespVal: countersEthernet68PfcwdAliasByte, + valTest: true, + }, { + desc: "get COUNTERS:Ethernet*", + pathTarget: "COUNTERS_DB", + textPbPath: ` elem: elem: `, - wantRetCode: codes.OK, - wantRespVal: countersEthernetWildcardByte, - valTest: true, - }, { - desc: "get COUNTERS:Ethernet* SAI_PORT_STAT_PFC_7_RX_PKTS", - pathTarget: "COUNTERS_DB", - textPbPath: ` + wantRetCode: codes.OK, + wantRespVal: countersEthernetWildcardByte, + valTest: true, + }, { + desc: "get COUNTERS:Ethernet* SAI_PORT_STAT_PFC_7_RX_PKTS", + pathTarget: "COUNTERS_DB", + textPbPath: ` elem: elem: elem: `, - wantRetCode: codes.OK, - wantRespVal: countersEthernetWildcardPfcByte, - valTest: true, - }, { - desc: "get COUNTERS:Ethernet* Pfcwd", - pathTarget: "COUNTERS_DB", - textPbPath: ` + wantRetCode: codes.OK, + wantRespVal: countersEthernetWildcardPfcByte, + valTest: true, + }, { + desc: "get COUNTERS:Ethernet* Pfcwd", + pathTarget: "COUNTERS_DB", + textPbPath: ` elem: elem: elem: `, - wantRetCode: codes.OK, - wantRespVal: countersEthernetWildcardPfcwdByte, - valTest: true, - }, { - desc: "get State DB Data for SWITCH_CAPABILITY switch", - pathTarget: stateDBPath, - textPbPath: ` + wantRetCode: codes.OK, + wantRespVal: countersEthernetWildcardPfcwdByte, + valTest: true, + }, { + desc: "get State DB Data for SWITCH_CAPABILITY switch", + pathTarget: stateDBPath, + textPbPath: ` elem: elem: `, - valTest: true, - wantRetCode: codes.OK, - wantRespVal: []byte(`{"test_field": "test_value"}`), - }, + valTest: true, + wantRetCode: codes.OK, + wantRespVal: []byte(`{"test_field": "test_value"}`), + }, // Happy path createBuildVersionTestCase( diff --git a/sonic_data_client/db_client.go b/sonic_data_client/db_client.go index b8fcab00e..ab3840316 100644 --- a/sonic_data_client/db_client.go +++ b/sonic_data_client/db_client.go @@ -129,24 +129,6 @@ func NewDbClient(paths []*gnmipb.Path, prefix *gnmipb.Path) (Client, error) { if UseRedisLocalTcpPort { useRedisTcpClient() } - if prefix.GetTarget() == "COUNTERS_DB" { - err = initCountersPortNameMap() - if err != nil { - return nil, err - } - err = initCountersQueueNameMap() - if err != nil { - return nil, err - } - err = initAliasMap() - if err != nil { - return nil, err - } - err = initCountersPfcwdNameMap() - if err != nil { - return nil, err - } - } client.prefix = prefix client.pathG2S = make(map[*gnmipb.Path][]tablePath) @@ -505,6 +487,26 @@ func populateDbtablePath(prefix, path *gnmipb.Path, pathG2S *map[*gnmipb.Path][] return fmt.Errorf("Invalid target dbNameSpace %v", targetDbNameSpace) } + if targetDbName == "COUNTERS_DB" { + err := initCountersPortNameMap() + if err != nil { + return err + } + err = initCountersQueueNameMap() + if err != nil { + return err + } + err = initAliasMap() + if err != nil { + return err + } + err = initCountersPfcwdNameMap() + if err != nil { + return err + } + } + + fullPath := path if prefix != nil { fullPath = gnmiFullPath(prefix, path)