blob: c48940761bc3fbf7b7342b839d82e267cc628877 [file] [log] [blame]
khenaidood948f772021-08-11 17:49:24 -04001/*
2 * Copyright 2018-present Open Networking Foundation
3
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7
8 * http://www.apache.org/licenses/LICENSE-2.0
9
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package config
17
18import (
19 "context"
20 "fmt"
21 "os"
22 "strings"
23 "time"
24
25 "github.com/opencord/voltha-lib-go/v7/pkg/db"
26 "github.com/opencord/voltha-lib-go/v7/pkg/db/kvstore"
27 "github.com/opencord/voltha-lib-go/v7/pkg/log"
28)
29
30const (
31 defaultkvStoreConfigPath = "config"
32 defaultkvStoreDataPathPrefix = "service/voltha/voltha1_voltha1"
33 kvStorePathSeparator = "/"
34)
35
36// ConfigType represents the type for which config is created inside the kvstore
37// For example, loglevel
38type ConfigType int
39
40const (
41 ConfigTypeLogLevel ConfigType = iota
42 ConfigTypeMetadata
43 ConfigTypeKafka
44 ConfigTypeLogFeatures
45)
46
47func (c ConfigType) String() string {
48 return [...]string{"loglevel", "metadata", "kafka", "logfeatures"}[c]
49}
50
51// ChangeEvent represents the event recieved from watch
52// For example, Put Event
53type ChangeEvent int
54
55const (
56 Put ChangeEvent = iota
57 Delete
58)
59
60func (ce ChangeEvent) String() string {
61 return [...]string{"Put", "Delete"}[ce]
62}
63
64// ConfigChangeEvent represents config for the events recieved from watch
65// For example,ChangeType is Put ,ConfigAttribute default
66type ConfigChangeEvent struct {
67 ChangeType ChangeEvent
68 ConfigAttribute string
69}
70
71// ConfigManager is a wrapper over Backend to maintain Configuration of voltha components
72// in kvstore based persistent storage
73type ConfigManager struct {
74 Backend *db.Backend
75 KVStoreConfigPrefix string
76 KVStoreDataPathPrefix string
77}
78
79// ComponentConfig represents a category of configuration for a specific VOLTHA component type
80// stored in a persistent storage pointed to by Config Manager
81// For example, one ComponentConfig instance will be created for loglevel config type for rw-core
82// component while another ComponentConfig instance will refer to connection config type for same
83// rw-core component. So, there can be multiple ComponentConfig instance created per component
84// pointing to different category of configuration.
85//
86// Configuration pointed to be by ComponentConfig is stored in kvstore as a list of key/value pairs
87// under the hierarchical tree with following base path
88// <Backend Prefix Path>/<Config Prefix>/<Component Name>/<Config Type>/
89//
90// For example, rw-core ComponentConfig for loglevel config entries will be stored under following path
91// /voltha/service/config/rw-core/loglevel/
92type ComponentConfig struct {
93 cManager *ConfigManager
94 componentLabel string
95 configType ConfigType
96 changeEventChan chan *ConfigChangeEvent
97 kvStoreEventChan chan *kvstore.Event
98}
99
100func NewConfigManager(ctx context.Context, kvClient kvstore.Client, kvStoreType, kvStoreAddress string, kvStoreTimeout time.Duration) *ConfigManager {
101 var kvStorePrefix string
102 if prefix, present := os.LookupEnv("KV_STORE_DATAPATH_PREFIX"); present {
103 kvStorePrefix = prefix
104 logger.Infow(ctx, "KV_STORE_DATAPATH_PREFIX env variable is set, ", log.Fields{"kvStoreDataPathPrefix": kvStorePrefix})
105 } else {
106 kvStorePrefix = defaultkvStoreDataPathPrefix
107 logger.Infow(ctx, "KV_STORE_DATAPATH_PREFIX env variable is not set, using default", log.Fields{"kvStoreDataPathPrefix": defaultkvStoreDataPathPrefix})
108 }
109 return &ConfigManager{
110 KVStoreConfigPrefix: defaultkvStoreConfigPath,
111 KVStoreDataPathPrefix: kvStorePrefix,
112 Backend: &db.Backend{
113 Client: kvClient,
114 StoreType: kvStoreType,
115 Address: kvStoreAddress,
116 Timeout: kvStoreTimeout,
117 PathPrefix: kvStorePrefix,
118 },
119 }
120}
121
122// RetrieveComponentList list the component Names for which loglevel is stored in kvstore
123func (c *ConfigManager) RetrieveComponentList(ctx context.Context, configType ConfigType) ([]string, error) {
124 data, err := c.Backend.List(ctx, c.KVStoreConfigPrefix)
125 if err != nil {
126 return nil, err
127 }
128
129 // Looping through the data recieved from the Backend for config
130 // Trimming and Splitting the required key and value from data and storing as componentName,PackageName and Level
131 // For Example, recieved key would be <Backend Prefix Path>/<Config Prefix>/<Component Name>/<Config Type>/default and value \"DEBUG\"
132 // Then in default will be stored as PackageName,componentName as <Component Name> and DEBUG will be stored as value in List struct
133 ccPathPrefix := kvStorePathSeparator + configType.String() + kvStorePathSeparator
134 pathPrefix := c.KVStoreDataPathPrefix + kvStorePathSeparator + c.KVStoreConfigPrefix + kvStorePathSeparator
135 var list []string
136 keys := make(map[string]interface{})
137 for attr := range data {
138 cname := strings.TrimPrefix(attr, pathPrefix)
139 cName := strings.SplitN(cname, ccPathPrefix, 2)
140 if len(cName) != 2 {
141 continue
142 }
143 if _, exist := keys[cName[0]]; !exist {
144 keys[cName[0]] = nil
145 list = append(list, cName[0])
146 }
147 }
148 return list, nil
149}
150
151// Initialize the component config
152func (cm *ConfigManager) InitComponentConfig(componentLabel string, configType ConfigType) *ComponentConfig {
153
154 return &ComponentConfig{
155 componentLabel: componentLabel,
156 configType: configType,
157 cManager: cm,
158 changeEventChan: nil,
159 kvStoreEventChan: nil,
160 }
161
162}
163
164func (c *ComponentConfig) makeConfigPath() string {
165
166 cType := c.configType.String()
167 return c.cManager.KVStoreConfigPrefix + kvStorePathSeparator +
168 c.componentLabel + kvStorePathSeparator + cType
169}
170
171// MonitorForConfigChange watch on the subkeys for the given key
172// Any changes to the subkeys for the given key will return an event channel
173// Then Event channel will be processed and new event channel with required values will be created and return
174// For example, rw-core will be watching on <Backend Prefix Path>/<Config Prefix>/<Component Name>/<Config Type>/
175// will return an event channel for PUT,DELETE eventType.
176// Then values from event channel will be processed and stored in kvStoreEventChan.
177func (c *ComponentConfig) MonitorForConfigChange(ctx context.Context) chan *ConfigChangeEvent {
178 key := c.makeConfigPath()
179
180 logger.Debugw(ctx, "monitoring-for-config-change", log.Fields{"key": key})
181
182 c.changeEventChan = make(chan *ConfigChangeEvent, 1)
183
184 c.kvStoreEventChan = c.cManager.Backend.CreateWatch(ctx, key, true)
185
186 go c.processKVStoreWatchEvents(ctx)
187
188 return c.changeEventChan
189}
190
191// processKVStoreWatchEvents process event channel recieved from the Backend for any ChangeType
192// It checks for the EventType is valid or not.For the valid EventTypes creates ConfigChangeEvent and send it on channel
193func (c *ComponentConfig) processKVStoreWatchEvents(ctx context.Context) {
194
195 ccKeyPrefix := c.makeConfigPath()
196
197 logger.Debugw(ctx, "processing-kvstore-event-change", log.Fields{"key-prefix": ccKeyPrefix})
198
199 ccPathPrefix := c.cManager.Backend.PathPrefix + ccKeyPrefix + kvStorePathSeparator
200
201 for watchResp := range c.kvStoreEventChan {
202
203 if watchResp.EventType == kvstore.CONNECTIONDOWN || watchResp.EventType == kvstore.UNKNOWN {
204 logger.Warnw(ctx, "received-invalid-change-type-in-watch-channel-from-kvstore", log.Fields{"change-type": watchResp.EventType})
205 continue
206 }
207
208 // populating the configAttribute from the received Key
209 // For Example, Key received would be <Backend Prefix Path>/<Config Prefix>/<Component Name>/<Config Type>/default
210 // Storing default in configAttribute variable
211 ky := fmt.Sprintf("%s", watchResp.Key)
212
213 c.changeEventChan <- &ConfigChangeEvent{
214 ChangeType: ChangeEvent(watchResp.EventType),
215 ConfigAttribute: strings.TrimPrefix(ky, ccPathPrefix),
216 }
217 }
218}
219
220// Retrieves value of a specific config key. Value of key is returned in String format
221func (c *ComponentConfig) Retrieve(ctx context.Context, configKey string) (string, error) {
222 key := c.makeConfigPath() + "/" + configKey
223
224 logger.Debugw(ctx, "retrieving-config", log.Fields{"key": key})
225
226 if kvpair, err := c.cManager.Backend.Get(ctx, key); err != nil {
227 return "", err
228 } else {
229 if kvpair == nil {
230 return "", fmt.Errorf("config-key-does-not-exist : %s", key)
231 }
232
233 value := strings.Trim(fmt.Sprintf("%s", kvpair.Value), "\"")
234 logger.Debugw(ctx, "retrieved-config", log.Fields{"key": key, "value": value})
235 return value, nil
236 }
237}
238
239func (c *ComponentConfig) RetrieveAll(ctx context.Context) (map[string]string, error) {
240 key := c.makeConfigPath()
241
242 logger.Debugw(ctx, "retreiving-list", log.Fields{"key": key})
243
244 data, err := c.cManager.Backend.List(ctx, key)
245 if err != nil {
246 return nil, err
247 }
248
249 // Looping through the data recieved from the Backend for the given key
250 // Trimming the required key and value from data and storing as key/value pair
251 // For Example, recieved key would be <Backend Prefix Path>/<Config Prefix>/<Component Name>/<Config Type>/default and value \"DEBUG\"
252 // Then in default will be stored as key and DEBUG will be stored as value in map[string]string
253 res := make(map[string]string)
254 ccPathPrefix := c.cManager.Backend.PathPrefix + kvStorePathSeparator + key + kvStorePathSeparator
255 for attr, val := range data {
256 res[strings.TrimPrefix(attr, ccPathPrefix)] = strings.Trim(fmt.Sprintf("%s", val.Value), "\"")
257 }
258
259 return res, nil
260}
261
262func (c *ComponentConfig) Save(ctx context.Context, configKey string, configValue string) error {
263 key := c.makeConfigPath() + "/" + configKey
264
265 logger.Debugw(ctx, "saving-config", log.Fields{"key": key, "value": configValue})
266
267 //save the data for update config
268 if err := c.cManager.Backend.Put(ctx, key, configValue); err != nil {
269 return err
270 }
271 return nil
272}
273
274func (c *ComponentConfig) Delete(ctx context.Context, configKey string) error {
275 //construct key using makeConfigPath
276 key := c.makeConfigPath() + "/" + configKey
277
278 logger.Debugw(ctx, "deleting-config", log.Fields{"key": key})
279 //delete the config
280 if err := c.cManager.Backend.Delete(ctx, key); err != nil {
281 return err
282 }
283 return nil
284}