Read list type of environment variables
[ric-plt/sdlgo.git] / internal / sdlgoredis / sdlgosentinel.go
1 /*
2    Copyright (c) 2021 AT&T Intellectual Property.
3    Copyright (c) 2018-2022 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         MasterName string
35         NodeCnt    string
36         IredisSentinelClient
37 }
38
39 type IredisSentinelClient interface {
40         Master(ctx context.Context, name string) *redis.StringStringMapCmd
41         Slaves(ctx context.Context, name string) *redis.SliceCmd
42         Sentinels(ctx context.Context, name string) *redis.SliceCmd
43 }
44
45 type RedisSentinelCreateCb func(addr, sentinelPort, masterName, nodeCnt string) *Sentinel
46
47 func newRedisSentinel(addr, sentinelPort, masterName, nodeCnt string) *Sentinel {
48         redisAddress := addr + ":" + sentinelPort
49         return &Sentinel{
50                 ctx: context.Background(),
51                 IredisSentinelClient: redis.NewSentinelClient(&redis.Options{
52                         Addr:       redisAddress,
53                         Password:   "", // no password set
54                         DB:         0,  // use default DB
55                         PoolSize:   20,
56                         MaxRetries: 2,
57                 }),
58                 MasterName: masterName,
59                 NodeCnt:    nodeCnt,
60         }
61 }
62
63 func (s *Sentinel) GetDbState() (*DbState, error) {
64         state := new(DbState)
65         pState, pErr := s.getPrimaryDbState()
66         rState, rErr := s.getReplicasState()
67         sState, sErr := s.getSentinelsState()
68         state.PrimaryDbState = *pState
69         state.ReplicasDbState = rState
70         state.SentinelsDbState = sState
71
72         cnt, err := strconv.Atoi(s.NodeCnt)
73         if err != nil {
74                 state.Err = fmt.Errorf("Sentinel DBAAS_NODE_COUNT configuration value '%s' conversion to integer failed", s.NodeCnt)
75                 return state, state.Err
76         }
77         state.ConfigNodeCnt = cnt
78
79         if pErr != nil {
80                 return state, pErr
81         }
82         if rErr != nil {
83                 return state, rErr
84         }
85         return state, sErr
86 }
87
88 func (s *Sentinel) getPrimaryDbState() (*PrimaryDbState, error) {
89         state := new(PrimaryDbState)
90         redisVal, redisErr := s.Master(s.ctx, s.MasterName).Result()
91         if redisErr == nil {
92                 state.Fields.Ip = redisVal["ip"]
93                 state.Fields.Port = redisVal["port"]
94                 state.Fields.Flags = redisVal["flags"]
95                 state.Fields.Role = redisVal["role-reported"]
96         }
97         state.Err = redisErr
98         return state, redisErr
99 }
100
101 func (s *Sentinel) getReplicasState() (*ReplicasDbState, error) {
102         states := new(ReplicasDbState)
103         states.States = make([]*ReplicaDbState, 0)
104
105         redisVal, redisErr := s.Slaves(s.ctx, s.MasterName).Result()
106         if redisErr == nil {
107                 for _, redisReplica := range redisVal {
108                         replicaState := readReplicaState(redisReplica.([]interface{}))
109                         states.States = append(states.States, replicaState)
110                 }
111         }
112         states.Err = redisErr
113         return states, redisErr
114 }
115
116 func readReplicaState(redisReplicas []interface{}) *ReplicaDbState {
117         state := new(ReplicaDbState)
118         for i := 0; i < len(redisReplicas); i += 2 {
119                 if redisReplicas[i].(string) == "ip" {
120                         state.Fields.Ip = redisReplicas[i+1].(string)
121                 } else if redisReplicas[i].(string) == "port" {
122                         state.Fields.Port = redisReplicas[i+1].(string)
123                 } else if redisReplicas[i].(string) == "flags" {
124                         state.Fields.Flags = redisReplicas[i+1].(string)
125                 } else if redisReplicas[i].(string) == "role-reported" {
126                         state.Fields.Role = redisReplicas[i+1].(string)
127                 } else if redisReplicas[i].(string) == "master-link-status" {
128                         state.Fields.PrimaryLinkStatus = redisReplicas[i+1].(string)
129                 }
130         }
131         return state
132 }
133
134 func (s *Sentinel) getSentinelsState() (*SentinelsDbState, error) {
135         states := new(SentinelsDbState)
136         states.States = make([]*SentinelDbState, 0)
137
138         redisVal, redisErr := s.Sentinels(s.ctx, s.MasterName).Result()
139         if redisErr == nil {
140                 for _, redisSentinel := range redisVal {
141                         sentinelState := readSentinelState(redisSentinel.([]interface{}))
142                         // Ignore a sentinel entry with zero port, because missing of fix
143                         // for the Redis Bug #9240.
144                         if sentinelState.Fields.Port != "0" {
145                                 states.States = append(states.States, sentinelState)
146                         }
147                 }
148         }
149         states.Err = redisErr
150         return states, redisErr
151 }
152
153 func readSentinelState(redisSentinels []interface{}) *SentinelDbState {
154         state := new(SentinelDbState)
155         for i := 0; i < len(redisSentinels); i += 2 {
156                 if redisSentinels[i].(string) == "ip" {
157                         state.Fields.Ip = redisSentinels[i+1].(string)
158                 } else if redisSentinels[i].(string) == "port" {
159                         state.Fields.Port = redisSentinels[i+1].(string)
160                 } else if redisSentinels[i].(string) == "flags" {
161                         state.Fields.Flags = redisSentinels[i+1].(string)
162                 }
163         }
164         return state
165 }