blob: 8f96b22dc14bc90240ebe3070c698de931e20331 [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"
29 kvStoreDataPathPrefix = "/service/voltha"
30 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
55// ConfigChangeEvent represents config for the events recieved from watch
56// For example,ChangeType is Put ,ConfigAttribute default
57type ConfigChangeEvent struct {
58 ChangeType ChangeEvent
59 ConfigAttribute string
60}
61
62// ConfigManager is a wrapper over backend to maintain Configuration of voltha components
63// in kvstore based persistent storage
64type ConfigManager struct {
65 backend *db.Backend
66 KvStoreConfigPrefix string
67}
68
69// ComponentConfig represents a category of configuration for a specific VOLTHA component type
70// stored in a persistent storage pointed to by Config Manager
71// For example, one ComponentConfig instance will be created for loglevel config type for rw-core
72// component while another ComponentConfig instance will refer to connection config type for same
73// rw-core component. So, there can be multiple ComponentConfig instance created per component
74// pointing to different category of configuration.
75//
76// Configuration pointed to be by ComponentConfig is stored in kvstore as a list of key/value pairs
77// under the hierarchical tree with following base path
78// <Backend Prefix Path>/<Config Prefix>/<Component Name>/<Config Type>/
79//
80// For example, rw-core ComponentConfig for loglevel config entries will be stored under following path
81// /voltha/service/config/rw-core/loglevel/
82type ComponentConfig struct {
83 cManager *ConfigManager
84 componentLabel string
85 configType ConfigType
86 changeEventChan chan *ConfigChangeEvent
87 kvStoreEventChan chan *kvstore.Event
88}
89
90func NewConfigManager(kvClient kvstore.Client, kvStoreType, kvStoreHost string, kvStorePort, kvStoreTimeout int) *ConfigManager {
91
92 return &ConfigManager{
93 KvStoreConfigPrefix: defaultkvStoreConfigPath,
94 backend: &db.Backend{
95 Client: kvClient,
96 StoreType: kvStoreType,
97 Host: kvStoreHost,
98 Port: kvStorePort,
99 Timeout: kvStoreTimeout,
100 PathPrefix: kvStoreDataPathPrefix,
101 },
102 }
103}
104
105// Initialize the component config
106func (cm *ConfigManager) InitComponentConfig(componentLabel string, configType ConfigType) *ComponentConfig {
107
108 return &ComponentConfig{
109 componentLabel: componentLabel,
110 configType: configType,
111 cManager: cm,
112 changeEventChan: nil,
113 kvStoreEventChan: nil,
114 }
115
116}
117
118func (c *ComponentConfig) makeConfigPath() string {
119
120 cType := c.configType.String()
121 return c.cManager.KvStoreConfigPrefix + kvStorePathSeparator +
122 c.componentLabel + kvStorePathSeparator + cType
123}
124
125// MonitorForConfigChange watch on the subkeys for the given key
126// Any changes to the subkeys for the given key will return an event channel
127// Then Event channel will be processed and new event channel with required values will be created and return
128// For example, rw-core will be watching on <Backend Prefix Path>/<Config Prefix>/<Component Name>/<Config Type>/
129// will return an event channel for PUT,DELETE eventType.
130// Then values from event channel will be processed and stored in kvStoreEventChan.
131func (c *ComponentConfig) MonitorForConfigChange(ctx context.Context) chan *ConfigChangeEvent {
132 key := c.makeConfigPath()
133
134 log.Debugw("monitoring-for-config-change", log.Fields{"key": key})
135
136 c.changeEventChan = make(chan *ConfigChangeEvent, 1)
137
138 c.kvStoreEventChan = c.cManager.backend.CreateWatch(ctx, key, true)
139
140 go c.processKVStoreWatchEvents()
141
142 return c.changeEventChan
143}
144
145// processKVStoreWatchEvents process event channel recieved from the backend for any ChangeType
146// It checks for the EventType is valid or not.For the valid EventTypes creates ConfigChangeEvent and send it on channel
147func (c *ComponentConfig) processKVStoreWatchEvents() {
148
149 ccKeyPrefix := c.makeConfigPath()
150 log.Debugw("processing-kvstore-event-change", log.Fields{"key-prefix": ccKeyPrefix})
151 ccPathPrefix := c.cManager.backend.PathPrefix + ccKeyPrefix + kvStorePathSeparator
152 for watchResp := range c.kvStoreEventChan {
153
154 if watchResp.EventType == kvstore.CONNECTIONDOWN || watchResp.EventType == kvstore.UNKNOWN {
155 log.Warnw("received-invalid-change-type-in-watch-channel-from-kvstore", log.Fields{"change-type": watchResp.EventType})
156 continue
157 }
158
159 // populating the configAttribute from the received Key
160 // For Example, Key received would be <Backend Prefix Path>/<Config Prefix>/<Component Name>/<Config Type>/default
161 // Storing default in configAttribute variable
162 ky := fmt.Sprintf("%s", watchResp.Key)
163
164 c.changeEventChan <- &ConfigChangeEvent{
165 ChangeType: ChangeEvent(watchResp.EventType),
166 ConfigAttribute: strings.TrimPrefix(ky, ccPathPrefix),
167 }
168 }
169}
170
171func (c *ComponentConfig) RetrieveAll(ctx context.Context) (map[string]string, error) {
172 key := c.makeConfigPath()
173
174 log.Debugw("retreiving-list", log.Fields{"key": key})
175 data, err := c.cManager.backend.List(ctx, key)
176 if err != nil {
177 return nil, err
178 }
179
180 // Looping through the data recieved from the backend for the given key
181 // Trimming the required key and value from data and storing as key/value pair
182 // For Example, recieved key would be <Backend Prefix Path>/<Config Prefix>/<Component Name>/<Config Type>/default and value \"DEBUG\"
183 // Then in default will be stored as key and DEBUG will be stored as value in map[string]string
184 res := make(map[string]string)
185 ccPathPrefix := c.cManager.backend.PathPrefix + kvStorePathSeparator + key + kvStorePathSeparator
186 for attr, val := range data {
187 res[strings.TrimPrefix(attr, ccPathPrefix)] = strings.Trim(fmt.Sprintf("%s", val.Value), "\"")
188 }
189
190 return res, nil
191}
192
193func (c *ComponentConfig) Save(configKey string, configValue string, ctx context.Context) error {
194 key := c.makeConfigPath() + "/" + configKey
195
196 log.Debugw("saving-key", log.Fields{"key": key, "value": configValue})
197
198 //save the data for update config
199 if err := c.cManager.backend.Put(ctx, key, configValue); err != nil {
200 return err
201 }
202 return nil
203}
204
205func (c *ComponentConfig) Delete(configKey string, ctx context.Context) error {
206 //construct key using makeConfigPath
207 key := c.makeConfigPath() + "/" + configKey
208
209 log.Debugw("deleting-key", log.Fields{"key": key})
210 //delete the config
211 if err := c.cManager.backend.Delete(ctx, key); err != nil {
212 return err
213 }
214 return nil
215}