blob: f5efa369e808bd57b7234644d338cb6bbea307f0 [file] [log] [blame]
divyadesaia37f78b2020-02-07 12:41:22 +00001/*
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"
serkant.uluderya5e3528d2020-05-22 19:31:07 -070021 "os"
22 "strings"
23 "time"
24
Girish Gowdra8a0bdcd2021-05-13 12:31:04 -070025 "github.com/opencord/voltha-lib-go/v5/pkg/db"
26 "github.com/opencord/voltha-lib-go/v5/pkg/db/kvstore"
27 "github.com/opencord/voltha-lib-go/v5/pkg/log"
divyadesaia37f78b2020-02-07 12:41:22 +000028)
29
30const (
serkant.uluderya5e3528d2020-05-22 19:31:07 -070031 defaultkvStoreConfigPath = "config"
serkant.uluderya7b8211e2021-02-24 16:39:18 +030032 defaultkvStoreDataPathPrefix = "service/voltha_voltha"
serkant.uluderya5e3528d2020-05-22 19:31:07 -070033 kvStorePathSeparator = "/"
divyadesaia37f78b2020-02-07 12:41:22 +000034)
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
Scott Bakered4a8e72020-04-17 11:10:20 -070042 ConfigTypeMetadata
divyadesaia37f78b2020-02-07 12:41:22 +000043 ConfigTypeKafka
Girish Kumar935f7af2020-08-18 11:59:42 +000044 ConfigTypeLogFeatures
divyadesaia37f78b2020-02-07 12:41:22 +000045)
46
47func (c ConfigType) String() string {
Girish Kumar935f7af2020-08-18 11:59:42 +000048 return [...]string{"loglevel", "metadata", "kafka", "logfeatures"}[c]
divyadesaia37f78b2020-02-07 12:41:22 +000049}
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
divyadesaid26f6b12020-03-19 06:30:28 +000060func (ce ChangeEvent) String() string {
61 return [...]string{"Put", "Delete"}[ce]
62}
63
divyadesaia37f78b2020-02-07 12:41:22 +000064// 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
Matteo Scandolo3ad5d2b2020-04-02 17:02:04 -070071// ConfigManager is a wrapper over Backend to maintain Configuration of voltha components
divyadesaia37f78b2020-02-07 12:41:22 +000072// in kvstore based persistent storage
73type ConfigManager struct {
serkant.uluderya5e3528d2020-05-22 19:31:07 -070074 Backend *db.Backend
75 KVStoreConfigPrefix string
76 KVStoreDataPathPrefix string
divyadesaia37f78b2020-02-07 12:41:22 +000077}
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
Neha Sharma96b7bf22020-06-15 10:37:32 +0000100func NewConfigManager(ctx context.Context, kvClient kvstore.Client, kvStoreType, kvStoreAddress string, kvStoreTimeout time.Duration) *ConfigManager {
serkant.uluderya5e3528d2020-05-22 19:31:07 -0700101 var kvStorePrefix string
102 if prefix, present := os.LookupEnv("KV_STORE_DATAPATH_PREFIX"); present {
103 kvStorePrefix = prefix
Neha Sharma96b7bf22020-06-15 10:37:32 +0000104 logger.Infow(ctx, "KV_STORE_DATAPATH_PREFIX env variable is set, ", log.Fields{"kvStoreDataPathPrefix": kvStorePrefix})
serkant.uluderya5e3528d2020-05-22 19:31:07 -0700105 } else {
106 kvStorePrefix = defaultkvStoreDataPathPrefix
Neha Sharma96b7bf22020-06-15 10:37:32 +0000107 logger.Infow(ctx, "KV_STORE_DATAPATH_PREFIX env variable is not set, using default", log.Fields{"kvStoreDataPathPrefix": defaultkvStoreDataPathPrefix})
serkant.uluderya5e3528d2020-05-22 19:31:07 -0700108 }
divyadesaia37f78b2020-02-07 12:41:22 +0000109 return &ConfigManager{
serkant.uluderya5e3528d2020-05-22 19:31:07 -0700110 KVStoreConfigPrefix: defaultkvStoreConfigPath,
111 KVStoreDataPathPrefix: kvStorePrefix,
Matteo Scandolo3ad5d2b2020-04-02 17:02:04 -0700112 Backend: &db.Backend{
divyadesaia37f78b2020-02-07 12:41:22 +0000113 Client: kvClient,
114 StoreType: kvStoreType,
Neha Sharma3f221ae2020-04-29 19:02:12 +0000115 Address: kvStoreAddress,
divyadesaia37f78b2020-02-07 12:41:22 +0000116 Timeout: kvStoreTimeout,
serkant.uluderya5e3528d2020-05-22 19:31:07 -0700117 PathPrefix: kvStorePrefix,
divyadesaia37f78b2020-02-07 12:41:22 +0000118 },
119 }
120}
121
divyadesaid26f6b12020-03-19 06:30:28 +0000122// RetrieveComponentList list the component Names for which loglevel is stored in kvstore
123func (c *ConfigManager) RetrieveComponentList(ctx context.Context, configType ConfigType) ([]string, error) {
serkant.uluderya5e3528d2020-05-22 19:31:07 -0700124 data, err := c.Backend.List(ctx, c.KVStoreConfigPrefix)
divyadesaid26f6b12020-03-19 06:30:28 +0000125 if err != nil {
126 return nil, err
127 }
128
Matteo Scandolo3ad5d2b2020-04-02 17:02:04 -0700129 // Looping through the data recieved from the Backend for config
divyadesaid26f6b12020-03-19 06:30:28 +0000130 // 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
serkant.uluderya5e3528d2020-05-22 19:31:07 -0700134 pathPrefix := c.KVStoreDataPathPrefix + kvStorePathSeparator + c.KVStoreConfigPrefix + kvStorePathSeparator
divyadesaid26f6b12020-03-19 06:30:28 +0000135 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
divyadesaia37f78b2020-02-07 12:41:22 +0000151// 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()
serkant.uluderya5e3528d2020-05-22 19:31:07 -0700167 return c.cManager.KVStoreConfigPrefix + kvStorePathSeparator +
divyadesaia37f78b2020-02-07 12:41:22 +0000168 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
Neha Sharma96b7bf22020-06-15 10:37:32 +0000180 logger.Debugw(ctx, "monitoring-for-config-change", log.Fields{"key": key})
divyadesaia37f78b2020-02-07 12:41:22 +0000181
182 c.changeEventChan = make(chan *ConfigChangeEvent, 1)
183
Matteo Scandolo3ad5d2b2020-04-02 17:02:04 -0700184 c.kvStoreEventChan = c.cManager.Backend.CreateWatch(ctx, key, true)
divyadesaia37f78b2020-02-07 12:41:22 +0000185
Neha Sharma96b7bf22020-06-15 10:37:32 +0000186 go c.processKVStoreWatchEvents(ctx)
divyadesaia37f78b2020-02-07 12:41:22 +0000187
188 return c.changeEventChan
189}
190
Matteo Scandolo3ad5d2b2020-04-02 17:02:04 -0700191// processKVStoreWatchEvents process event channel recieved from the Backend for any ChangeType
divyadesaia37f78b2020-02-07 12:41:22 +0000192// It checks for the EventType is valid or not.For the valid EventTypes creates ConfigChangeEvent and send it on channel
Neha Sharma96b7bf22020-06-15 10:37:32 +0000193func (c *ComponentConfig) processKVStoreWatchEvents(ctx context.Context) {
divyadesaia37f78b2020-02-07 12:41:22 +0000194
195 ccKeyPrefix := c.makeConfigPath()
divyadesaid26f6b12020-03-19 06:30:28 +0000196
Neha Sharma96b7bf22020-06-15 10:37:32 +0000197 logger.Debugw(ctx, "processing-kvstore-event-change", log.Fields{"key-prefix": ccKeyPrefix})
divyadesaid26f6b12020-03-19 06:30:28 +0000198
Matteo Scandolo3ad5d2b2020-04-02 17:02:04 -0700199 ccPathPrefix := c.cManager.Backend.PathPrefix + ccKeyPrefix + kvStorePathSeparator
divyadesaid26f6b12020-03-19 06:30:28 +0000200
divyadesaia37f78b2020-02-07 12:41:22 +0000201 for watchResp := range c.kvStoreEventChan {
202
203 if watchResp.EventType == kvstore.CONNECTIONDOWN || watchResp.EventType == kvstore.UNKNOWN {
Neha Sharma96b7bf22020-06-15 10:37:32 +0000204 logger.Warnw(ctx, "received-invalid-change-type-in-watch-channel-from-kvstore", log.Fields{"change-type": watchResp.EventType})
divyadesaia37f78b2020-02-07 12:41:22 +0000205 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
divyadesaid26f6b12020-03-19 06:30:28 +0000220// 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
Neha Sharma96b7bf22020-06-15 10:37:32 +0000224 logger.Debugw(ctx, "retrieving-config", log.Fields{"key": key})
divyadesaid26f6b12020-03-19 06:30:28 +0000225
Matteo Scandolo3ad5d2b2020-04-02 17:02:04 -0700226 if kvpair, err := c.cManager.Backend.Get(ctx, key); err != nil {
divyadesaid26f6b12020-03-19 06:30:28 +0000227 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), "\"")
Neha Sharma96b7bf22020-06-15 10:37:32 +0000234 logger.Debugw(ctx, "retrieved-config", log.Fields{"key": key, "value": value})
divyadesaid26f6b12020-03-19 06:30:28 +0000235 return value, nil
236 }
237}
238
divyadesaia37f78b2020-02-07 12:41:22 +0000239func (c *ComponentConfig) RetrieveAll(ctx context.Context) (map[string]string, error) {
240 key := c.makeConfigPath()
241
Neha Sharma96b7bf22020-06-15 10:37:32 +0000242 logger.Debugw(ctx, "retreiving-list", log.Fields{"key": key})
divyadesaid26f6b12020-03-19 06:30:28 +0000243
Matteo Scandolo3ad5d2b2020-04-02 17:02:04 -0700244 data, err := c.cManager.Backend.List(ctx, key)
divyadesaia37f78b2020-02-07 12:41:22 +0000245 if err != nil {
246 return nil, err
247 }
248
Matteo Scandolo3ad5d2b2020-04-02 17:02:04 -0700249 // Looping through the data recieved from the Backend for the given key
divyadesaia37f78b2020-02-07 12:41:22 +0000250 // 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)
Matteo Scandolo3ad5d2b2020-04-02 17:02:04 -0700254 ccPathPrefix := c.cManager.Backend.PathPrefix + kvStorePathSeparator + key + kvStorePathSeparator
divyadesaia37f78b2020-02-07 12:41:22 +0000255 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
divyadesaid26f6b12020-03-19 06:30:28 +0000262func (c *ComponentConfig) Save(ctx context.Context, configKey string, configValue string) error {
divyadesaia37f78b2020-02-07 12:41:22 +0000263 key := c.makeConfigPath() + "/" + configKey
264
Neha Sharma96b7bf22020-06-15 10:37:32 +0000265 logger.Debugw(ctx, "saving-config", log.Fields{"key": key, "value": configValue})
divyadesaia37f78b2020-02-07 12:41:22 +0000266
267 //save the data for update config
Matteo Scandolo3ad5d2b2020-04-02 17:02:04 -0700268 if err := c.cManager.Backend.Put(ctx, key, configValue); err != nil {
divyadesaia37f78b2020-02-07 12:41:22 +0000269 return err
270 }
271 return nil
272}
273
divyadesaid26f6b12020-03-19 06:30:28 +0000274func (c *ComponentConfig) Delete(ctx context.Context, configKey string) error {
divyadesaia37f78b2020-02-07 12:41:22 +0000275 //construct key using makeConfigPath
276 key := c.makeConfigPath() + "/" + configKey
277
Neha Sharma96b7bf22020-06-15 10:37:32 +0000278 logger.Debugw(ctx, "deleting-config", log.Fields{"key": key})
divyadesaia37f78b2020-02-07 12:41:22 +0000279 //delete the config
Matteo Scandolo3ad5d2b2020-04-02 17:02:04 -0700280 if err := c.cManager.Backend.Delete(ctx, key); err != nil {
divyadesaia37f78b2020-02-07 12:41:22 +0000281 return err
282 }
283 return nil
284}