blob: 9f08b0ddeb1053859bea8df50db505a1e7cb1382 [file] [log] [blame]
divyadesai8bf96862020-02-07 12:24:26 +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"
21 "github.com/opencord/voltha-lib-go/v3/pkg/db"
22 "github.com/opencord/voltha-lib-go/v3/pkg/db/kvstore"
23 "github.com/opencord/voltha-lib-go/v3/pkg/log"
24 "strings"
25)
26
27const (
28 defaultkvStoreConfigPath = "config"
Girish Kumara8ef6332020-03-18 18:35:43 +000029 kvStoreDataPathPrefix = "service/voltha"
divyadesai8bf96862020-02-07 12:24:26 +000030 kvStorePathSeparator = "/"
31)
32
33// ConfigType represents the type for which config is created inside the kvstore
34// For example, loglevel
35type ConfigType int
36
37const (
38 ConfigTypeLogLevel ConfigType = iota
39 ConfigTypeKafka
40)
41
42func (c ConfigType) String() string {
43 return [...]string{"loglevel", "kafka"}[c]
44}
45
46// ChangeEvent represents the event recieved from watch
47// For example, Put Event
48type ChangeEvent int
49
50const (
51 Put ChangeEvent = iota
52 Delete
53)
54
Girish Kumar21972fb2020-03-13 13:27:47 +000055func (ce ChangeEvent) String() string {
56 return [...]string{"Put", "Delete"}[ce]
57}
58
divyadesai8bf96862020-02-07 12:24:26 +000059// ConfigChangeEvent represents config for the events recieved from watch
60// For example,ChangeType is Put ,ConfigAttribute default
61type ConfigChangeEvent struct {
62 ChangeType ChangeEvent
63 ConfigAttribute string
64}
65
66// ConfigManager is a wrapper over backend to maintain Configuration of voltha components
67// in kvstore based persistent storage
68type ConfigManager struct {
69 backend *db.Backend
70 KvStoreConfigPrefix string
71}
72
73// ComponentConfig represents a category of configuration for a specific VOLTHA component type
74// stored in a persistent storage pointed to by Config Manager
75// For example, one ComponentConfig instance will be created for loglevel config type for rw-core
76// component while another ComponentConfig instance will refer to connection config type for same
77// rw-core component. So, there can be multiple ComponentConfig instance created per component
78// pointing to different category of configuration.
79//
80// Configuration pointed to be by ComponentConfig is stored in kvstore as a list of key/value pairs
81// under the hierarchical tree with following base path
82// <Backend Prefix Path>/<Config Prefix>/<Component Name>/<Config Type>/
83//
84// For example, rw-core ComponentConfig for loglevel config entries will be stored under following path
85// /voltha/service/config/rw-core/loglevel/
86type ComponentConfig struct {
87 cManager *ConfigManager
88 componentLabel string
89 configType ConfigType
90 changeEventChan chan *ConfigChangeEvent
91 kvStoreEventChan chan *kvstore.Event
92}
93
94func NewConfigManager(kvClient kvstore.Client, kvStoreType, kvStoreHost string, kvStorePort, kvStoreTimeout int) *ConfigManager {
95
96 return &ConfigManager{
97 KvStoreConfigPrefix: defaultkvStoreConfigPath,
98 backend: &db.Backend{
99 Client: kvClient,
100 StoreType: kvStoreType,
101 Host: kvStoreHost,
102 Port: kvStorePort,
103 Timeout: kvStoreTimeout,
104 PathPrefix: kvStoreDataPathPrefix,
105 },
106 }
107}
108
divyadesai42bcb672020-03-04 11:40:53 +0000109// RetrieveComponentList list the component Names for which loglevel is stored in kvstore
110func (c *ConfigManager) RetrieveComponentList(ctx context.Context, configType ConfigType) ([]string, error) {
111 data, err := c.backend.List(ctx, c.KvStoreConfigPrefix)
112 if err != nil {
113 return nil, err
114 }
115
116 // Looping through the data recieved from the backend for config
117 // Trimming and Splitting the required key and value from data and storing as componentName,PackageName and Level
118 // For Example, recieved key would be <Backend Prefix Path>/<Config Prefix>/<Component Name>/<Config Type>/default and value \"DEBUG\"
119 // Then in default will be stored as PackageName,componentName as <Component Name> and DEBUG will be stored as value in List struct
120 ccPathPrefix := kvStorePathSeparator + configType.String() + kvStorePathSeparator
121 pathPrefix := kvStoreDataPathPrefix + kvStorePathSeparator + c.KvStoreConfigPrefix + kvStorePathSeparator
122 var list []string
123 keys := make(map[string]interface{})
124 for attr := range data {
125 cname := strings.TrimPrefix(attr, pathPrefix)
126 cName := strings.SplitN(cname, ccPathPrefix, 2)
127 if len(cName) != 2 {
128 continue
129 }
130 if _, exist := keys[cName[0]]; !exist {
131 keys[cName[0]] = nil
132 list = append(list, cName[0])
133 }
134 }
135 return list, nil
136}
137
divyadesai8bf96862020-02-07 12:24:26 +0000138// Initialize the component config
139func (cm *ConfigManager) InitComponentConfig(componentLabel string, configType ConfigType) *ComponentConfig {
140
141 return &ComponentConfig{
142 componentLabel: componentLabel,
143 configType: configType,
144 cManager: cm,
145 changeEventChan: nil,
146 kvStoreEventChan: nil,
147 }
148
149}
150
151func (c *ComponentConfig) makeConfigPath() string {
152
153 cType := c.configType.String()
154 return c.cManager.KvStoreConfigPrefix + kvStorePathSeparator +
155 c.componentLabel + kvStorePathSeparator + cType
156}
157
158// MonitorForConfigChange watch on the subkeys for the given key
159// Any changes to the subkeys for the given key will return an event channel
160// Then Event channel will be processed and new event channel with required values will be created and return
161// For example, rw-core will be watching on <Backend Prefix Path>/<Config Prefix>/<Component Name>/<Config Type>/
162// will return an event channel for PUT,DELETE eventType.
163// Then values from event channel will be processed and stored in kvStoreEventChan.
164func (c *ComponentConfig) MonitorForConfigChange(ctx context.Context) chan *ConfigChangeEvent {
165 key := c.makeConfigPath()
166
Girish Kumare6f45e82020-03-20 10:46:54 +0000167 logger.Debugw("monitoring-for-config-change", log.Fields{"key": key})
divyadesai8bf96862020-02-07 12:24:26 +0000168
169 c.changeEventChan = make(chan *ConfigChangeEvent, 1)
170
171 c.kvStoreEventChan = c.cManager.backend.CreateWatch(ctx, key, true)
172
173 go c.processKVStoreWatchEvents()
174
175 return c.changeEventChan
176}
177
178// processKVStoreWatchEvents process event channel recieved from the backend for any ChangeType
179// It checks for the EventType is valid or not.For the valid EventTypes creates ConfigChangeEvent and send it on channel
180func (c *ComponentConfig) processKVStoreWatchEvents() {
181
182 ccKeyPrefix := c.makeConfigPath()
divyadesai42bcb672020-03-04 11:40:53 +0000183
Girish Kumare6f45e82020-03-20 10:46:54 +0000184 logger.Debugw("processing-kvstore-event-change", log.Fields{"key-prefix": ccKeyPrefix})
divyadesai42bcb672020-03-04 11:40:53 +0000185
divyadesai8bf96862020-02-07 12:24:26 +0000186 ccPathPrefix := c.cManager.backend.PathPrefix + ccKeyPrefix + kvStorePathSeparator
divyadesai42bcb672020-03-04 11:40:53 +0000187
divyadesai8bf96862020-02-07 12:24:26 +0000188 for watchResp := range c.kvStoreEventChan {
189
190 if watchResp.EventType == kvstore.CONNECTIONDOWN || watchResp.EventType == kvstore.UNKNOWN {
Girish Kumare6f45e82020-03-20 10:46:54 +0000191 logger.Warnw("received-invalid-change-type-in-watch-channel-from-kvstore", log.Fields{"change-type": watchResp.EventType})
divyadesai8bf96862020-02-07 12:24:26 +0000192 continue
193 }
194
195 // populating the configAttribute from the received Key
196 // For Example, Key received would be <Backend Prefix Path>/<Config Prefix>/<Component Name>/<Config Type>/default
197 // Storing default in configAttribute variable
198 ky := fmt.Sprintf("%s", watchResp.Key)
199
200 c.changeEventChan <- &ConfigChangeEvent{
201 ChangeType: ChangeEvent(watchResp.EventType),
202 ConfigAttribute: strings.TrimPrefix(ky, ccPathPrefix),
203 }
204 }
205}
206
Girish Kumar78cdb4f2020-03-16 13:39:42 +0000207// Retrieves value of a specific config key. Value of key is returned in String format
208func (c *ComponentConfig) Retrieve(ctx context.Context, configKey string) (string, error) {
209 key := c.makeConfigPath() + "/" + configKey
210
Girish Kumare6f45e82020-03-20 10:46:54 +0000211 logger.Debugw("retrieving-config", log.Fields{"key": key})
Girish Kumar78cdb4f2020-03-16 13:39:42 +0000212
213 if kvpair, err := c.cManager.backend.Get(ctx, key); err != nil {
214 return "", err
215 } else {
216 if kvpair == nil {
217 return "", fmt.Errorf("config-key-does-not-exist : %s", key)
218 }
219
220 value := strings.Trim(fmt.Sprintf("%s", kvpair.Value), "\"")
Girish Kumare6f45e82020-03-20 10:46:54 +0000221 logger.Debugw("retrieved-config", log.Fields{"key": key, "value": value})
Girish Kumar78cdb4f2020-03-16 13:39:42 +0000222 return value, nil
223 }
224}
225
divyadesai8bf96862020-02-07 12:24:26 +0000226func (c *ComponentConfig) RetrieveAll(ctx context.Context) (map[string]string, error) {
227 key := c.makeConfigPath()
228
Girish Kumare6f45e82020-03-20 10:46:54 +0000229 logger.Debugw("retreiving-list", log.Fields{"key": key})
divyadesai42bcb672020-03-04 11:40:53 +0000230
divyadesai8bf96862020-02-07 12:24:26 +0000231 data, err := c.cManager.backend.List(ctx, key)
232 if err != nil {
233 return nil, err
234 }
235
236 // Looping through the data recieved from the backend for the given key
237 // Trimming the required key and value from data and storing as key/value pair
238 // For Example, recieved key would be <Backend Prefix Path>/<Config Prefix>/<Component Name>/<Config Type>/default and value \"DEBUG\"
239 // Then in default will be stored as key and DEBUG will be stored as value in map[string]string
240 res := make(map[string]string)
241 ccPathPrefix := c.cManager.backend.PathPrefix + kvStorePathSeparator + key + kvStorePathSeparator
242 for attr, val := range data {
243 res[strings.TrimPrefix(attr, ccPathPrefix)] = strings.Trim(fmt.Sprintf("%s", val.Value), "\"")
244 }
245
246 return res, nil
247}
248
divyadesai42bcb672020-03-04 11:40:53 +0000249func (c *ComponentConfig) Save(ctx context.Context, configKey string, configValue string) error {
divyadesai8bf96862020-02-07 12:24:26 +0000250 key := c.makeConfigPath() + "/" + configKey
251
Girish Kumare6f45e82020-03-20 10:46:54 +0000252 logger.Debugw("saving-config", log.Fields{"key": key, "value": configValue})
divyadesai8bf96862020-02-07 12:24:26 +0000253
254 //save the data for update config
255 if err := c.cManager.backend.Put(ctx, key, configValue); err != nil {
256 return err
257 }
258 return nil
259}
260
divyadesai42bcb672020-03-04 11:40:53 +0000261func (c *ComponentConfig) Delete(ctx context.Context, configKey string) error {
divyadesai8bf96862020-02-07 12:24:26 +0000262 //construct key using makeConfigPath
263 key := c.makeConfigPath() + "/" + configKey
264
Girish Kumare6f45e82020-03-20 10:46:54 +0000265 logger.Debugw("deleting-config", log.Fields{"key": key})
divyadesai8bf96862020-02-07 12:24:26 +0000266 //delete the config
267 if err := c.cManager.backend.Delete(ctx, key); err != nil {
268 return err
269 }
270 return nil
271}