Bump Redis client version to v8.11.4
[ric-plt/sdlgo.git] / internal / sdlgoredis / sdlgosentinel.go
1 /*
2    Copyright (c) 2021 AT&T Intellectual Property.
3    Copyright (c) 2018-2021 Nokia.
4
5    Licensed under the Apache License, Version 2.0 (the "License");
6    you may not use this file except in compliance with the License.
7    You may obtain a copy of the License at
8
9        http://www.apache.org/licenses/LICENSE-2.0
10
11    Unless required by applicable law or agreed to in writing, software
12    distributed under the License is distributed on an "AS IS" BASIS,
13    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14    See the License for the specific language governing permissions and
15    limitations under the License.
16 */
17
18 /*
19  * This source code is part of the near-RT RIC (RAN Intelligent Controller)
20  * platform project (RICP).
21  */
22
23 package sdlgoredis
24
25 import (
26         "context"
27         "fmt"
28         "github.com/go-redis/redis/v8"
29         "strconv"
30 )
31
32 type Sentinel struct {
33         ctx context.Context
34         IredisSentinelClient
35         Cfg *Config
36 }
37
38 type IredisSentinelClient interface {
39         Master(ctx context.Context, name string) *redis.StringStringMapCmd
40         Slaves(ctx context.Context, name string) *redis.SliceCmd
41         Sentinels(ctx context.Context, name string) *redis.SliceCmd
42 }
43
44 type RedisSentinelCreateCb func(cfg *Config, addr string) *Sentinel
45
46 func newRedisSentinel(cfg *Config, addr string) *Sentinel {
47         redisAddress := addr + ":" + cfg.sentinelPort
48         return &Sentinel{
49                 ctx: context.Background(),
50                 IredisSentinelClient: redis.NewSentinelClient(&redis.Options{
51                         Addr:       redisAddress,
52                         Password:   "", // no password set
53                         DB:         0,  // use default DB
54                         PoolSize:   20,
55                         MaxRetries: 2,
56                 }),
57                 Cfg: cfg,
58         }
59 }
60
61 func (s *Sentinel) GetDbState() (*DbState, error) {
62         state := new(DbState)
63         pState, pErr := s.getPrimaryDbState()
64         rState, rErr := s.getReplicasState()
65         sState, sErr := s.getSentinelsState()
66         state.PrimaryDbState = *pState
67         state.ReplicasDbState = rState
68         state.SentinelsDbState = sState
69
70         cnt, err := strconv.Atoi(s.Cfg.nodeCnt)
71         if err != nil {
72                 state.Err = fmt.Errorf("Sentinel DBAAS_NODE_COUNT configuration value '%s' conversion to integer failed", s.Cfg.nodeCnt)
73                 return state, state.Err
74         }
75         state.ConfigNodeCnt = cnt
76
77         if pErr != nil {
78                 return state, pErr
79         }
80         if rErr != nil {
81                 return state, rErr
82         }
83         return state, sErr
84 }
85
86 func (s *Sentinel) getPrimaryDbState() (*PrimaryDbState, error) {
87         state := new(PrimaryDbState)
88         redisVal, redisErr := s.Master(s.ctx, s.Cfg.masterName).Result()
89         if redisErr == nil {
90                 state.Fields.Ip = redisVal["ip"]
91                 state.Fields.Port = redisVal["port"]
92                 state.Fields.Flags = redisVal["flags"]
93                 state.Fields.Role = redisVal["role-reported"]
94         }
95         state.Err = redisErr
96         return state, redisErr
97 }
98
99 func (s *Sentinel) getReplicasState() (*ReplicasDbState, error) {
100         states := new(ReplicasDbState)
101         states.States = make([]*ReplicaDbState, 0)
102
103         redisVal, redisErr := s.Slaves(s.ctx, s.Cfg.masterName).Result()
104         if redisErr == nil {
105                 for _, redisReplica := range redisVal {
106                         replicaState := readReplicaState(redisReplica.([]interface{}))
107                         states.States = append(states.States, replicaState)
108                 }
109         }
110         states.Err = redisErr
111         return states, redisErr
112 }
113
114 func readReplicaState(redisReplicas []interface{}) *ReplicaDbState {
115         state := new(ReplicaDbState)
116         for i := 0; i < len(redisReplicas); i += 2 {
117                 if redisReplicas[i].(string) == "ip" {
118                         state.Fields.Ip = redisReplicas[i+1].(string)
119                 } else if redisReplicas[i].(string) == "port" {
120                         state.Fields.Port = redisReplicas[i+1].(string)
121                 } else if redisReplicas[i].(string) == "flags" {
122                         state.Fields.Flags = redisReplicas[i+1].(string)
123                 } else if redisReplicas[i].(string) == "role-reported" {
124                         state.Fields.Role = redisReplicas[i+1].(string)
125                 } else if redisReplicas[i].(string) == "master-link-status" {
126                         state.Fields.PrimaryLinkStatus = redisReplicas[i+1].(string)
127                 }
128         }
129         return state
130 }
131
132 func (s *Sentinel) getSentinelsState() (*SentinelsDbState, error) {
133         states := new(SentinelsDbState)
134         states.States = make([]*SentinelDbState, 0)
135
136         redisVal, redisErr := s.Sentinels(s.ctx, s.Cfg.masterName).Result()
137         if redisErr == nil {
138                 for _, redisSentinel := range redisVal {
139                         sentinelState := readSentinelState(redisSentinel.([]interface{}))
140                         // Ignore a sentinel entry with zero port, because missing of fix
141                         // for the Redis Bug #9240.
142                         if sentinelState.Fields.Port != "0" {
143                                 states.States = append(states.States, sentinelState)
144                         }
145                 }
146         }
147         states.Err = redisErr
148         return states, redisErr
149 }
150
151 func readSentinelState(redisSentinels []interface{}) *SentinelDbState {
152         state := new(SentinelDbState)
153         for i := 0; i < len(redisSentinels); i += 2 {
154                 if redisSentinels[i].(string) == "ip" {
155                         state.Fields.Ip = redisSentinels[i+1].(string)
156                 } else if redisSentinels[i].(string) == "port" {
157                         state.Fields.Port = redisSentinels[i+1].(string)
158                 } else if redisSentinels[i].(string) == "flags" {
159                         state.Fields.Flags = redisSentinels[i+1].(string)
160                 }
161         }
162         return state
163 }