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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions show_client/system_health_cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"strings"

sdc "github.com/sonic-net/sonic-gnmi/sonic_data_client"
sdcfg "github.com/sonic-net/sonic-gnmi/sonic_db_config"

log "github.com/golang/glog"
"google.golang.org/grpc/codes"
Expand Down Expand Up @@ -161,6 +162,26 @@ func getSystemHealthDpu(options sdc.OptionMap) ([]byte, error) {

// Get data from chassis database using GetMapFromQueries
func getChassisDataDirect(moduleName string) (map[string]interface{}, error) {
// Check whether CHASSIS_STATE_DB is present in the runtime database config.
// On non-chassis platform, CHASSIS_STATE_DB won't be present in database_config.json.
// Check Db exists before querying.

dbList, err := sdcfg.GetDbList(sdcfg.SONIC_DEFAULT_NAMESPACE)
if err != nil {
return nil, fmt.Errorf("getChassisDataDirect: failed to get DB list: %w", err)
}
chassisStateDBPresent := false
for _, db := range dbList {
if db == "CHASSIS_STATE_DB" {
chassisStateDBPresent = true
break
}
}
if !chassisStateDBPresent {
log.V(2).Infof("getChassisDataDirect: CHASSIS_STATE_DB not present on this platform, skipping")
return make(map[string]interface{}), nil
}

var queries [][]string
if moduleName != "" && moduleName != "all" {
queries = [][]string{
Expand Down
117 changes: 109 additions & 8 deletions sonic_data_client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -833,18 +833,34 @@ func saveAndResetTarget2RedisDb() func() {
return func() { Target2RedisDb = orig }
}

// allRuntimeDbNames returns all DB names from spb.Target_value except OTHERS,
// suitable for mocking sdcfg.GetDbList in initRedisDbClients tests.
func allRuntimeDbNames() []string {
names := make([]string, 0, len(spb.Target_value))
for name := range spb.Target_value {
if name != "OTHERS" {
names = append(names, name)
}
}
return names
}

func TestInitRedisDbClients(t *testing.T) {
ns := ""

t.Run("SkipUnavailableDb", func(t *testing.T) {
defer saveAndResetTarget2RedisDb()()

getDbSockCalls := 0
patches := gomonkey.ApplyFunc(sdcfg.GetDbAllNamespaces, func() ([]string, error) {
return []string{ns}, nil
patches := gomonkey.ApplyFunc(sdcfg.GetDbList, func(_ string) ([]string, error) {
return allRuntimeDbNames(), nil
})
defer patches.Reset()

patches.ApplyFunc(sdcfg.GetDbAllNamespaces, func() ([]string, error) {
return []string{ns}, nil
})

patches.ApplyFunc(sdcfg.GetDbSock, func(dbName string, _ string) (string, error) {
getDbSockCalls++
if dbName == "CHASSIS_STATE_DB" {
Expand Down Expand Up @@ -875,11 +891,15 @@ func TestInitRedisDbClients(t *testing.T) {
t.Run("AllDbsAvailable", func(t *testing.T) {
defer saveAndResetTarget2RedisDb()()

patches := gomonkey.ApplyFunc(sdcfg.GetDbAllNamespaces, func() ([]string, error) {
return []string{ns}, nil
patches := gomonkey.ApplyFunc(sdcfg.GetDbList, func(_ string) ([]string, error) {
return allRuntimeDbNames(), nil
})
defer patches.Reset()

patches.ApplyFunc(sdcfg.GetDbAllNamespaces, func() ([]string, error) {
return []string{ns}, nil
})

patches.ApplyFunc(sdcfg.GetDbSock, func(_ string, _ string) (string, error) {
return "/var/run/redis/redis.sock", nil
})
Expand All @@ -906,11 +926,15 @@ func TestInitRedisDbClients(t *testing.T) {
t.Run("GetDbAllNamespacesFails", func(t *testing.T) {
defer saveAndResetTarget2RedisDb()()

patches := gomonkey.ApplyFunc(sdcfg.GetDbAllNamespaces, func() ([]string, error) {
return nil, fmt.Errorf("namespace retrieval failed")
patches := gomonkey.ApplyFunc(sdcfg.GetDbList, func(_ string) ([]string, error) {
return allRuntimeDbNames(), nil
})
defer patches.Reset()

patches.ApplyFunc(sdcfg.GetDbAllNamespaces, func() ([]string, error) {
return nil, fmt.Errorf("namespace retrieval failed")
})

initRedisDbClients()

if len(Target2RedisDb) != 0 {
Expand All @@ -926,11 +950,15 @@ func TestInitRedisDbClients(t *testing.T) {
"ASIC_DB": true,
}

patches := gomonkey.ApplyFunc(sdcfg.GetDbAllNamespaces, func() ([]string, error) {
return []string{ns}, nil
patches := gomonkey.ApplyFunc(sdcfg.GetDbList, func(_ string) ([]string, error) {
return allRuntimeDbNames(), nil
})
defer patches.Reset()

patches.ApplyFunc(sdcfg.GetDbAllNamespaces, func() ([]string, error) {
return []string{ns}, nil
})

patches.ApplyFunc(sdcfg.GetDbSock, func(dbName string, _ string) (string, error) {
if failingDbs[dbName] {
return "", fmt.Errorf("DB %s not found", dbName)
Expand All @@ -955,6 +983,79 @@ func TestInitRedisDbClients(t *testing.T) {
}
}
})

t.Run("GetDbListFails", func(t *testing.T) {
defer saveAndResetTarget2RedisDb()()

patches := gomonkey.ApplyFunc(sdcfg.GetDbList, func(_ string) ([]string, error) {
return nil, fmt.Errorf("DB list retrieval failed")
})
defer patches.Reset()

initRedisDbClients()

if len(Target2RedisDb) != 0 {
t.Errorf("Expected Target2RedisDb to be empty when GetDbList fails, got %d entries", len(Target2RedisDb))
}
})

t.Run("GetDbListEmpty", func(t *testing.T) {
defer saveAndResetTarget2RedisDb()()

patches := gomonkey.ApplyFunc(sdcfg.GetDbList, func(_ string) ([]string, error) {
return []string{}, nil
})
defer patches.Reset()

initRedisDbClients()

if len(Target2RedisDb) != 0 {
t.Errorf("Expected Target2RedisDb to be empty when DB list is empty, got %d entries", len(Target2RedisDb))
}
})

t.Run("RuntimeDbSetFilters", func(t *testing.T) {
defer saveAndResetTarget2RedisDb()()

// Return only a subset of DBs — CHASSIS_STATE_DB is absent from runtime config
patches := gomonkey.ApplyFunc(sdcfg.GetDbList, func(_ string) ([]string, error) {
return []string{"CONFIG_DB", "APPL_DB", "STATE_DB"}, nil
})
defer patches.Reset()

patches.ApplyFunc(sdcfg.GetDbAllNamespaces, func() ([]string, error) {
return []string{ns}, nil
})

getDbSockCalls := 0
patches.ApplyFunc(sdcfg.GetDbSock, func(_ string, _ string) (string, error) {
getDbSockCalls++
return "/var/run/redis/redis.sock", nil
})

initRedisDbClients()

nsMap, ok := Target2RedisDb[ns]
if !ok {
t.Fatal("Expected namespace to exist in Target2RedisDb")
}
// DBs not in the runtime list should be filtered out
for _, dbName := range []string{"CHASSIS_STATE_DB", "ASIC_DB", "COUNTERS_DB"} {
if _, exists := nsMap[dbName]; exists {
t.Errorf("%s should have been filtered by runtimeDbSet", dbName)
}
}
// DBs in the runtime list should be present
for _, dbName := range []string{"CONFIG_DB", "APPL_DB", "STATE_DB"} {
if _, exists := nsMap[dbName]; !exists {
t.Errorf("Expected %s to be initialized", dbName)
}
}
// GetDbSock should only be called for DBs in the runtime set
if getDbSockCalls != 3 {
t.Errorf("Expected GetDbSock to be called 3 times, got %d", getDbSockCalls)
}
})
}

func TestUseRedisTcpClient(t *testing.T) {
Expand Down
29 changes: 29 additions & 0 deletions sonic_data_client/db_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,28 @@ func init() {
}

func initRedisDbClients() {
// Build the set of DBs present in the runtime database_config.json so
// we can skip DBs absent on this platform before calling GetDbSock, which would otherwise
// log ERR for every missing DB.
// Note: GetDbList always returns the default namespace DB list.
// This is safe because all namespaces carry the same DB names; they differ only in
// socket paths. GetDbList and GetDbAllNamespaces share the same DbInit gate,
// so if GetDbList fails GetDbAllNamespaces would fail too;
// returning early here avoids redundant failures.
runtimeDbList, err := sdcfg.GetDbList(sdcfg.SONIC_DEFAULT_NAMESPACE)
if err != nil {
log.Errorf("initRedisDbClients: failed to get runtime DB list: %v", err)
return
}
if len(runtimeDbList) == 0 {
log.Errorf("initRedisDbClients: runtime DB list is empty, database config may be corrupt")
return
}
runtimeDbSet := make(map[string]bool, len(runtimeDbList))
for _, db := range runtimeDbList {
runtimeDbSet[db] = true
}

AllNamespaces, err := sdcfg.GetDbAllNamespaces()
if err != nil {
log.Errorf("init error: %v", err)
Expand All @@ -583,6 +605,13 @@ func initRedisDbClients() {
Target2RedisDb[dbNamespace] = make(map[string]*redis.Client)
for dbName, dbn := range spb.Target_value {
if dbName != "OTHERS" {
// Skip DBs not present in the runtime database_config.json.
// runtimeDbSet is non-empty here (guaranteed by the early return
// above), so a missing key safely returns false in Go.
if !runtimeDbSet[dbName] {
log.V(2).Infof("initRedisDbClients: skipping %s, not in runtime DB config", dbName)
continue
}
addr, err := sdcfg.GetDbSock(dbName, dbNamespace)
if err != nil {
log.Warningf("Skipping %s in namespace %s: %v", dbName, dbNamespace, err)
Expand Down
Loading