blob: 441c48836b32e4d2647eb3b501f04ca1266b0c21 [file] [log] [blame]
divyadesai81bb7ba2020-03-11 11:45:23 +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
27func init() {
28 _, err := log.AddPackage(log.JSON, log.FatalLevel, nil)
29 if err != nil {
30 log.Errorw("unable-to-register-package-to-the-log-map", log.Fields{"error": err})
31 }
32}
33
34const (
35 defaultkvStoreConfigPath = "config"
divyadesai52dc0882020-03-19 06:38:11 +000036 kvStoreDataPathPrefix = "service/voltha"
divyadesai81bb7ba2020-03-11 11:45:23 +000037 kvStorePathSeparator = "/"
38)
39
40// ConfigType represents the type for which config is created inside the kvstore
41// For example, loglevel
42type ConfigType int
43
44const (
45 ConfigTypeLogLevel ConfigType = iota
46 ConfigTypeKafka
47)
48
49func (c ConfigType) String() string {
50 return [...]string{"loglevel", "kafka"}[c]
51}
52
53// ChangeEvent represents the event recieved from watch
54// For example, Put Event
55type ChangeEvent int
56
57const (
58 Put ChangeEvent = iota
59 Delete
60)
61
divyadesai52dc0882020-03-19 06:38:11 +000062func (ce ChangeEvent) String() string {
63 return [...]string{"Put", "Delete"}[ce]
64}
65
divyadesai81bb7ba2020-03-11 11:45:23 +000066// ConfigChangeEvent represents config for the events recieved from watch
67// For example,ChangeType is Put ,ConfigAttribute default
68type ConfigChangeEvent struct {
69 ChangeType ChangeEvent
70 ConfigAttribute string
71}
72
73// ConfigManager is a wrapper over backend to maintain Configuration of voltha components
74// in kvstore based persistent storage
75type ConfigManager struct {
76 backend *db.Backend
77 KvStoreConfigPrefix string
78}
79
80// ComponentConfig represents a category of configuration for a specific VOLTHA component type
81// stored in a persistent storage pointed to by Config Manager
82// For example, one ComponentConfig instance will be created for loglevel config type for rw-core
83// component while another ComponentConfig instance will refer to connection config type for same
84// rw-core component. So, there can be multiple ComponentConfig instance created per component
85// pointing to different category of configuration.
86//
87// Configuration pointed to be by ComponentConfig is stored in kvstore as a list of key/value pairs
88// under the hierarchical tree with following base path
89// <Backend Prefix Path>/<Config Prefix>/<Component Name>/<Config Type>/
90//
91// For example, rw-core ComponentConfig for loglevel config entries will be stored under following path
92// /voltha/service/config/rw-core/loglevel/
93type ComponentConfig struct {
94 cManager *ConfigManager
95 componentLabel string
96 configType ConfigType
97 changeEventChan chan *ConfigChangeEvent
98 kvStoreEventChan chan *kvstore.Event
99}
100
101func NewConfigManager(kvClient kvstore.Client, kvStoreType, kvStoreHost string, kvStorePort, kvStoreTimeout int) *ConfigManager {
102
103 return &ConfigManager{
104 KvStoreConfigPrefix: defaultkvStoreConfigPath,
105 backend: &db.Backend{
106 Client: kvClient,
107 StoreType: kvStoreType,
108 Host: kvStoreHost,
109 Port: kvStorePort,
110 Timeout: kvStoreTimeout,
111 PathPrefix: kvStoreDataPathPrefix,
112 },
113 }
114}
115
116// RetrieveComponentList list the component Names for which loglevel is stored in kvstore
117func (c *ConfigManager) RetrieveComponentList(ctx context.Context, configType ConfigType) ([]string, error) {
118 data, err := c.backend.List(ctx, c.KvStoreConfigPrefix)
119 if err != nil {
120 return nil, err
121 }
122
123 // Looping through the data recieved from the backend for config
124 // Trimming and Splitting the required key and value from data and storing as componentName,PackageName and Level
125 // For Example, recieved key would be <Backend Prefix Path>/<Config Prefix>/<Component Name>/<Config Type>/default and value \"DEBUG\"
126 // Then in default will be stored as PackageName,componentName as <Component Name> and DEBUG will be stored as value in List struct
127 ccPathPrefix := kvStorePathSeparator + configType.String() + kvStorePathSeparator
128 pathPrefix := kvStoreDataPathPrefix + kvStorePathSeparator + c.KvStoreConfigPrefix + kvStorePathSeparator
129 var list []string
130 keys := make(map[string]interface{})
131 for attr := range data {
132 cname := strings.TrimPrefix(attr, pathPrefix)
133 cName := strings.SplitN(cname, ccPathPrefix, 2)
134 if len(cName) != 2 {
135 continue
136 }
137 if _, exist := keys[cName[0]]; !exist {
138 keys[cName[0]] = nil
139 list = append(list, cName[0])
140 }
141 }
142 return list, nil
143}
144
145// Initialize the component config
146func (cm *ConfigManager) InitComponentConfig(componentLabel string, configType ConfigType) *ComponentConfig {
147
148 return &ComponentConfig{
149 componentLabel: componentLabel,
150 configType: configType,
151 cManager: cm,
152 changeEventChan: nil,
153 kvStoreEventChan: nil,
154 }
155
156}
157
158func (c *ComponentConfig) makeConfigPath() string {
159
160 cType := c.configType.String()
161 return c.cManager.KvStoreConfigPrefix + kvStorePathSeparator +
162 c.componentLabel + kvStorePathSeparator + cType
163}
164
165// MonitorForConfigChange watch on the subkeys for the given key
166// Any changes to the subkeys for the given key will return an event channel
167// Then Event channel will be processed and new event channel with required values will be created and return
168// For example, rw-core will be watching on <Backend Prefix Path>/<Config Prefix>/<Component Name>/<Config Type>/
169// will return an event channel for PUT,DELETE eventType.
170// Then values from event channel will be processed and stored in kvStoreEventChan.
171func (c *ComponentConfig) MonitorForConfigChange(ctx context.Context) chan *ConfigChangeEvent {
172 key := c.makeConfigPath()
173
174 log.Debugw("monitoring-for-config-change", log.Fields{"key": key})
175
176 c.changeEventChan = make(chan *ConfigChangeEvent, 1)
177
178 c.kvStoreEventChan = c.cManager.backend.CreateWatch(ctx, key, true)
179
180 go c.processKVStoreWatchEvents()
181
182 return c.changeEventChan
183}
184
185// processKVStoreWatchEvents process event channel recieved from the backend for any ChangeType
186// It checks for the EventType is valid or not.For the valid EventTypes creates ConfigChangeEvent and send it on channel
187func (c *ComponentConfig) processKVStoreWatchEvents() {
188
189 ccKeyPrefix := c.makeConfigPath()
190
191 log.Debugw("processing-kvstore-event-change", log.Fields{"key-prefix": ccKeyPrefix})
192
193 ccPathPrefix := c.cManager.backend.PathPrefix + ccKeyPrefix + kvStorePathSeparator
194
195 for watchResp := range c.kvStoreEventChan {
196
197 if watchResp.EventType == kvstore.CONNECTIONDOWN || watchResp.EventType == kvstore.UNKNOWN {
198 log.Warnw("received-invalid-change-type-in-watch-channel-from-kvstore", log.Fields{"change-type": watchResp.EventType})
199 continue
200 }
201
202 // populating the configAttribute from the received Key
203 // For Example, Key received would be <Backend Prefix Path>/<Config Prefix>/<Component Name>/<Config Type>/default
204 // Storing default in configAttribute variable
205 ky := fmt.Sprintf("%s", watchResp.Key)
206
207 c.changeEventChan <- &ConfigChangeEvent{
208 ChangeType: ChangeEvent(watchResp.EventType),
209 ConfigAttribute: strings.TrimPrefix(ky, ccPathPrefix),
210 }
211 }
212}
213
divyadesai52dc0882020-03-19 06:38:11 +0000214// Retrieves value of a specific config key. Value of key is returned in String format
215func (c *ComponentConfig) Retrieve(ctx context.Context, configKey string) (string, error) {
216 key := c.makeConfigPath() + "/" + configKey
217
218 log.Debugw("retrieving-config", log.Fields{"key": key})
219
220 if kvpair, err := c.cManager.backend.Get(ctx, key); err != nil {
221 return "", err
222 } else {
223 if kvpair == nil {
224 return "", fmt.Errorf("config-key-does-not-exist : %s", key)
225 }
226
227 value := strings.Trim(fmt.Sprintf("%s", kvpair.Value), "\"")
228 log.Debugw("retrieved-config", log.Fields{"key": key, "value": value})
229 return value, nil
230 }
231}
232
divyadesai81bb7ba2020-03-11 11:45:23 +0000233func (c *ComponentConfig) RetrieveAll(ctx context.Context) (map[string]string, error) {
234 key := c.makeConfigPath()
235
236 log.Debugw("retreiving-list", log.Fields{"key": key})
237
238 data, err := c.cManager.backend.List(ctx, key)
239 if err != nil {
240 return nil, err
241 }
242
243 // Looping through the data recieved from the backend for the given key
244 // Trimming the required key and value from data and storing as key/value pair
245 // For Example, recieved key would be <Backend Prefix Path>/<Config Prefix>/<Component Name>/<Config Type>/default and value \"DEBUG\"
246 // Then in default will be stored as key and DEBUG will be stored as value in map[string]string
247 res := make(map[string]string)
248 ccPathPrefix := c.cManager.backend.PathPrefix + kvStorePathSeparator + key + kvStorePathSeparator
249 for attr, val := range data {
250 res[strings.TrimPrefix(attr, ccPathPrefix)] = strings.Trim(fmt.Sprintf("%s", val.Value), "\"")
251 }
252
253 return res, nil
254}
255
256func (c *ComponentConfig) Save(ctx context.Context, configKey string, configValue string) error {
257 key := c.makeConfigPath() + "/" + configKey
258
divyadesai52dc0882020-03-19 06:38:11 +0000259 log.Debugw("saving-config", log.Fields{"key": key, "value": configValue})
divyadesai81bb7ba2020-03-11 11:45:23 +0000260
261 //save the data for update config
262 if err := c.cManager.backend.Put(ctx, key, configValue); err != nil {
263 return err
264 }
265 return nil
266}
267
268func (c *ComponentConfig) Delete(ctx context.Context, configKey string) error {
269 //construct key using makeConfigPath
270 key := c.makeConfigPath() + "/" + configKey
271
divyadesai52dc0882020-03-19 06:38:11 +0000272 log.Debugw("deleting-config", log.Fields{"key": key})
divyadesai81bb7ba2020-03-11 11:45:23 +0000273 //delete the config
274 if err := c.cManager.backend.Delete(ctx, key); err != nil {
275 return err
276 }
277 return nil
278}