Skip to content

Commit e57c46e

Browse files
Implement translib backend logic for the gNSI Pathz
1 parent 71b3577 commit e57c46e

File tree

7 files changed

+794
-6
lines changed

7 files changed

+794
-6
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
module openconfig-system-annot {
2+
3+
yang-version "1";
4+
5+
namespace "http://openconfig.net/yang/openconfig-system-annot";
6+
prefix "oc-sys-annot";
7+
8+
import openconfig-system { prefix oc-sys; }
9+
import openconfig-yang-types { prefix oc-yang; }
10+
import openconfig-system { prefix oc-sys; }
11+
import gnsi-credentialz { prefix gnsi-credz; }
12+
import gnsi-pathz { prefix gnsi-pathz; }
13+
import sonic-extensions {prefix sonic-ext; }
14+
import openconfig-system-grpc { prefix oc-sys-grpc; }
15+
16+
deviation /oc-sys:system/oc-sys-grpc:grpc-servers {
17+
deviate add {
18+
sonic-ext:key-transformer "grpc_server_key_xfmr";
19+
sonic-ext:subtree-transformer "grpc_server_xfmr";
20+
}
21+
}
22+
23+
deviation /oc-sys:system/gnsi-pathz:gnmi-pathz-policies {
24+
deviate add {
25+
sonic-ext:key-transformer "pathz_policies_key_xfmr";
26+
sonic-ext:subtree-transformer "pathz_policies_xfmr";
27+
}
28+
}
29+
}

translib/db/db_redis_opts.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ func adjustRedisOpts(dbOpt *Options) *redis.Options {
144144
return &redisOpts
145145
}
146146

147-
func init() {
147+
func initializeRedisOpts() {
148148
flag.StringVar(&goRedisOpts, "go_redis_opts", "", "Options for go-redis")
149149
}
150150

translib/db/rcm.go

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
package db
2+
3+
import (
4+
"flag"
5+
"fmt"
6+
"sync"
7+
"sync/atomic"
8+
9+
"github.com/go-redis/redis/v7"
10+
log "github.com/golang/glog"
11+
)
12+
13+
var usePools = flag.Bool("use_connection_pools", true, "use connection pools for Redis Clients")
14+
15+
const (
16+
POOL_SIZE = 25
17+
)
18+
19+
var rcm *redisClientManager
20+
var initMu = &sync.Mutex{}
21+
22+
type redisClientManager struct {
23+
// clients holds one Redis Client for each DBNum
24+
clients [MaxDB + 1]*redis.Client
25+
mu *sync.RWMutex
26+
curTransactionalClients atomic.Int32
27+
totalPoolClientsRequested atomic.Uint64
28+
totalTransactionalClientsRequested atomic.Uint64
29+
}
30+
31+
type RedisCounters struct {
32+
CurTransactionalClients uint32 // The number of transactional clients currently opened.
33+
TotalPoolClientsRequested uint64 // The total number of Redis Clients using a connection pool requested.
34+
TotalTransactionalClientsRequested uint64 // The total number of Transactional Redis Clients requested.
35+
PoolStatsPerDB map[string]*redis.PoolStats // The pool counters for each Redis Client in the cache.
36+
}
37+
38+
func init() {
39+
initializeRedisOpts()
40+
initializeRedisClientManager()
41+
}
42+
43+
func initializeRedisClientManager() {
44+
initMu.Lock()
45+
defer initMu.Unlock()
46+
if rcm != nil {
47+
return
48+
}
49+
rcm = &redisClientManager{
50+
clients: [MaxDB + 1]*redis.Client{},
51+
mu: &sync.RWMutex{},
52+
curTransactionalClients: atomic.Int32{},
53+
totalPoolClientsRequested: atomic.Uint64{},
54+
totalTransactionalClientsRequested: atomic.Uint64{},
55+
}
56+
rcm.mu.Lock()
57+
defer rcm.mu.Unlock()
58+
for dbNum := DBNum(0); dbNum < MaxDB; dbNum++ {
59+
if len(getDBInstName(dbNum)) == 0 {
60+
continue
61+
}
62+
// Create a Redis Client for each database.
63+
rcm.clients[int(dbNum)] = createRedisClient(dbNum, POOL_SIZE)
64+
}
65+
}
66+
67+
func createRedisClient(db DBNum, poolSize int) *redis.Client {
68+
opts := adjustRedisOpts(&Options{DBNo: db})
69+
opts.PoolSize = poolSize
70+
client := redis.NewClient(opts)
71+
if _, err := client.Ping().Result(); err != nil {
72+
log.V(0).Infof("RCM error during Redis Client creation for DBNum=%v: %v", db, err)
73+
}
74+
return client
75+
}
76+
77+
func createRedisClientWithOpts(opts *redis.Options) *redis.Client {
78+
client := redis.NewClient(opts)
79+
if _, err := client.Ping().Result(); err != nil {
80+
log.V(0).Infof("RCM error during Redis Client creation for DBNum=%v: %v", opts.DB, err)
81+
}
82+
return client
83+
}
84+
85+
func getClient(db DBNum) *redis.Client {
86+
rcm.mu.RLock()
87+
defer rcm.mu.RUnlock()
88+
return rcm.clients[int(db)]
89+
}
90+
91+
// RedisClient will return a Redis Client that can be used for non-transactional Redis operations.
92+
// The client returned by this function is shared among many DB readers/writers and uses
93+
// a connection pool. For transactional Redis operations, please use GetRedisClientForTransaction().
94+
func RedisClient(db DBNum) *redis.Client {
95+
if rcm == nil {
96+
initializeRedisClientManager()
97+
}
98+
if !(*usePools) { // Connection Pooling is disabled.
99+
return TransactionalRedisClient(db)
100+
}
101+
if len(getDBInstName(db)) == 0 {
102+
log.V(0).Infof("Invalid DBNum requested: %v", db)
103+
return nil
104+
}
105+
rcm.totalPoolClientsRequested.Add(1)
106+
rc := getClient(db)
107+
if rc == nil {
108+
log.V(0).Infof("RCM Redis client for DBNum=%v is nil!", db)
109+
rcm.mu.Lock()
110+
defer rcm.mu.Unlock()
111+
if rc = rcm.clients[int(db)]; rc != nil {
112+
return rc
113+
}
114+
rc = createRedisClient(db, POOL_SIZE)
115+
rcm.clients[int(db)] = rc
116+
}
117+
return rc
118+
}
119+
120+
// TransactionalRedisClient will create and return a unique Redis client. This client can be used
121+
// for transactional operations. These operations include MULTI, PSUBSCRIBE (PubSub), and SCAN. This
122+
// client must be closed using CloseRedisClient when it is no longer needed.
123+
func TransactionalRedisClient(db DBNum) *redis.Client {
124+
if rcm == nil {
125+
initializeRedisClientManager()
126+
}
127+
if len(getDBInstName(db)) == 0 {
128+
log.V(0).Infof("Invalid DBNum requested: %v", db)
129+
return nil
130+
}
131+
rcm.totalTransactionalClientsRequested.Add(1)
132+
client := createRedisClient(db, 1)
133+
rcm.curTransactionalClients.Add(1)
134+
return client
135+
}
136+
137+
func TransactionalRedisClientWithOpts(opts *redis.Options) *redis.Client {
138+
if rcm == nil {
139+
initializeRedisClientManager()
140+
}
141+
rcm.totalTransactionalClientsRequested.Add(1)
142+
opts.PoolSize = 1
143+
client := createRedisClientWithOpts(opts)
144+
rcm.curTransactionalClients.Add(1)
145+
return client
146+
}
147+
148+
// CloseUniqueRedisClient will close the Redis client that is passed in.
149+
func CloseRedisClient(rc *redis.Client) error {
150+
if rcm == nil {
151+
return fmt.Errorf("RCM is nil when trying to close Redis Client: %v", rc)
152+
}
153+
if rc == nil {
154+
return nil
155+
}
156+
// Closing a Redis Client with a connection pool is a no-op because these clients need to stay open.
157+
if !IsTransactionalClient(rc) {
158+
return nil
159+
}
160+
if err := rc.Close(); err != nil {
161+
return err
162+
}
163+
rcm.curTransactionalClients.Add(-1)
164+
return nil
165+
}
166+
167+
// IsTransactionalClient returns true if rc is a transactional client and false otherwise.
168+
func IsTransactionalClient(rc *redis.Client) bool {
169+
if rc == nil {
170+
return false
171+
}
172+
return rc.Options().PoolSize == 1
173+
}
174+
175+
// RedisClientManagerCounters returns the counters stored in the RCM.
176+
func RedisClientManagerCounters() *RedisCounters {
177+
if rcm == nil {
178+
initializeRedisClientManager()
179+
}
180+
counters := &RedisCounters{
181+
CurTransactionalClients: uint32(rcm.curTransactionalClients.Load()),
182+
TotalPoolClientsRequested: rcm.totalPoolClientsRequested.Load(),
183+
TotalTransactionalClientsRequested: rcm.totalTransactionalClientsRequested.Load(),
184+
PoolStatsPerDB: map[string]*redis.PoolStats{},
185+
}
186+
rcm.mu.RLock()
187+
defer rcm.mu.RUnlock()
188+
for db, client := range rcm.clients {
189+
dbName := getDBInstName(DBNum(db))
190+
if dbName == "" || client == nil {
191+
continue
192+
}
193+
counters.PoolStatsPerDB[dbName] = client.PoolStats()
194+
}
195+
return counters
196+
}

translib/tlerr/tlerr.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ package tlerr
3131

3232
import (
3333
// "fmt"
34+
"errors"
3435
"github.com/Azure/sonic-mgmt-common/cvl"
36+
"github.com/golang/glog"
3537
"golang.org/x/text/language"
3638
"golang.org/x/text/message"
3739
// "errors"
@@ -190,3 +192,32 @@ type TranslibBusy struct {
190192
func (e TranslibBusy) Error() string {
191193
return p.Sprintf("Translib Busy")
192194
}
195+
196+
func IsTranslibRedisClientEntryNotExist(err error) bool {
197+
switch err.(type) {
198+
case *TranslibRedisClientEntryNotExist, TranslibRedisClientEntryNotExist:
199+
return true
200+
}
201+
return false
202+
}
203+
204+
// isDBEntryNotExistError returns `true` if `err` is (or wraps around) an error
205+
// of type `TranslibRedisClientEntryNotExist`.
206+
func isDBEntryNotExistError(err error) bool {
207+
if IsTranslibRedisClientEntryNotExist(err) {
208+
return true
209+
}
210+
pdberr := &TranslibRedisClientEntryNotExist{}
211+
return errors.As(err, &TranslibRedisClientEntryNotExist{}) || errors.As(err, &pdberr)
212+
}
213+
214+
// ErrorSeverity based on `err` calculates the VLOG level.
215+
func ErrorSeverity(err error) glog.Level {
216+
if err == nil {
217+
return 3
218+
}
219+
if isDBEntryNotExistError(err) {
220+
return 3
221+
}
222+
return 0
223+
}

translib/transformer/subscribe_req_xlate.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -478,7 +478,7 @@ func (pathXltr *subscribePathXlator) handleSubtreeNodeXlate() error {
478478
log.Info(pathXltr.subReq.reqLogId, "handleSubtreeNodeXlate: handleSubtreeNodeXlate: uriSubtree: ", uriSubtree)
479479
}
480480

481-
subInParam := XfmrSubscInParams{uriSubtree, pathXltr.subReq.dbs, make(RedisDbMap), TRANSLATE_SUBSCRIBE}
481+
subInParam := XfmrSubscInParams{uri: uriSubtree, requestURI: pathXltr.subReq.reqUri, dbs: pathXltr.subReq.dbs, dbDataMap: make(RedisDbMap), subscProc: TRANSLATE_SUBSCRIBE}
482482
subOutPram, subErr := xfmrSubscSubtreeHandler(subInParam, ygXpathInfo.xfmrFunc)
483483

484484
if log.V(dbLgLvl) {

translib/transformer/xfmr_interface.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,11 @@ type notificationOpts struct {
7676

7777
// XfmrSubscInParams represents input to subscribe subtree callbacks - request uri, DBs info access-pointers, DB info for request uri and subscription process type from translib.
7878
type XfmrSubscInParams struct {
79-
uri string
80-
dbs [db.MaxDB]*db.DB
81-
dbDataMap RedisDbMap
82-
subscProc SubscProcType
79+
uri string
80+
requestURI string
81+
dbs [db.MaxDB]*db.DB
82+
dbDataMap RedisDbMap
83+
subscProc SubscProcType
8384
}
8485

8586
// XfmrSubscOutParams represents output from subscribe subtree callback - DB data for request uri, Need cache, OnChange, subscription preference and interval.

0 commit comments

Comments
 (0)