blob: 66e6f747a2116988bc36843115c6e3858640950c [file] [log] [blame]
Stephane Barbarieec0919b2018-09-05 14:14:29 -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 model
17
18import (
19 "bytes"
20 "crypto/md5"
21 "fmt"
Stephane Barbarie126101e2018-10-11 16:18:48 -040022 "github.com/golang/protobuf/proto"
Stephane Barbarie933b09b2019-01-09 11:12:09 -050023 "github.com/opencord/voltha-go/common/log"
Stephane Barbarieec0919b2018-09-05 14:14:29 -040024 "reflect"
25 "sort"
Stephane Barbariedc5022d2018-11-19 15:21:44 -050026 "sync"
Stephane Barbarieec0919b2018-09-05 14:14:29 -040027)
28
Stephane Barbariedc5022d2018-11-19 15:21:44 -050029type revCacheSingleton struct {
30 sync.RWMutex
31 Cache map[string]interface{}
32}
33
34var revCacheInstance *revCacheSingleton
35var revCacheOnce sync.Once
36
37func GetRevCache() *revCacheSingleton {
38 revCacheOnce.Do(func() {
39 revCacheInstance = &revCacheSingleton{Cache: make(map[string]interface{})}
40 })
41 return revCacheInstance
42}
Stephane Barbarieec0919b2018-09-05 14:14:29 -040043
44type NonPersistedRevision struct {
Stephane Barbariedc5022d2018-11-19 15:21:44 -050045 mutex sync.RWMutex
46 Root *root
Stephane Barbarieec0919b2018-09-05 14:14:29 -040047 Config *DataRevision
48 Children map[string][]Revision
49 Hash string
50 Branch *Branch
51 WeakRef string
52}
53
Stephane Barbariedc5022d2018-11-19 15:21:44 -050054func NewNonPersistedRevision(root *root, branch *Branch, data interface{}, children map[string][]Revision) Revision {
55 r := &NonPersistedRevision{}
56 r.Root = root
57 r.Branch = branch
58 r.Config = NewDataRevision(root, data)
59 r.Children = children
Stephane Barbariedc5022d2018-11-19 15:21:44 -050060 return r
Stephane Barbarieec0919b2018-09-05 14:14:29 -040061}
62
63func (npr *NonPersistedRevision) SetConfig(config *DataRevision) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -050064 npr.mutex.Lock()
65 defer npr.mutex.Unlock()
Stephane Barbarieec0919b2018-09-05 14:14:29 -040066 npr.Config = config
67}
68
69func (npr *NonPersistedRevision) GetConfig() *DataRevision {
Stephane Barbariedc5022d2018-11-19 15:21:44 -050070 npr.mutex.Lock()
71 defer npr.mutex.Unlock()
Stephane Barbarieec0919b2018-09-05 14:14:29 -040072 return npr.Config
73}
74
75func (npr *NonPersistedRevision) SetChildren(children map[string][]Revision) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -050076 npr.mutex.Lock()
77 defer npr.mutex.Unlock()
Stephane Barbarieec0919b2018-09-05 14:14:29 -040078 npr.Children = children
79}
80
81func (npr *NonPersistedRevision) GetChildren() map[string][]Revision {
Stephane Barbariedc5022d2018-11-19 15:21:44 -050082 npr.mutex.Lock()
83 defer npr.mutex.Unlock()
Stephane Barbarieec0919b2018-09-05 14:14:29 -040084 return npr.Children
85}
86
87func (npr *NonPersistedRevision) SetHash(hash string) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -050088 npr.mutex.Lock()
89 defer npr.mutex.Unlock()
Stephane Barbarieec0919b2018-09-05 14:14:29 -040090 npr.Hash = hash
91}
92
93func (npr *NonPersistedRevision) GetHash() string {
Stephane Barbariedc5022d2018-11-19 15:21:44 -050094 //npr.mutex.Lock()
95 //defer npr.mutex.Unlock()
Stephane Barbarieec0919b2018-09-05 14:14:29 -040096 return npr.Hash
97}
98
99func (npr *NonPersistedRevision) ClearHash() {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500100 npr.mutex.Lock()
101 defer npr.mutex.Unlock()
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400102 npr.Hash = ""
103}
104
105func (npr *NonPersistedRevision) SetBranch(branch *Branch) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500106 npr.mutex.Lock()
107 defer npr.mutex.Unlock()
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400108 npr.Branch = branch
109}
110
111func (npr *NonPersistedRevision) GetBranch() *Branch {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500112 npr.mutex.Lock()
113 defer npr.mutex.Unlock()
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400114 return npr.Branch
115}
116
117func (npr *NonPersistedRevision) GetData() interface{} {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500118 npr.mutex.Lock()
119 defer npr.mutex.Unlock()
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400120 if npr.Config == nil {
121 return nil
122 }
123 return npr.Config.Data
124}
125
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400126func (npr *NonPersistedRevision) GetNode() *node {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500127 npr.mutex.Lock()
128 defer npr.mutex.Unlock()
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400129 return npr.Branch.Node
130}
131
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500132func (npr *NonPersistedRevision) Finalize(skipOnExist bool) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500133 GetRevCache().Lock()
134 defer GetRevCache().Unlock()
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400135
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500136 npr.Hash = npr.hashContent()
137
138 if _, exists := GetRevCache().Cache[npr.Hash]; !exists {
139 GetRevCache().Cache[npr.Hash] = npr
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400140 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500141 if _, exists := GetRevCache().Cache[npr.Config.Hash]; !exists {
142 GetRevCache().Cache[npr.Config.Hash] = npr.Config
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400143 } else {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500144 npr.Config = GetRevCache().Cache[npr.Config.Hash].(*DataRevision)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400145 }
146}
147
148func (npr *NonPersistedRevision) hashContent() string {
149 var buffer bytes.Buffer
150 var childrenKeys []string
151
152 if npr.Config != nil {
153 buffer.WriteString(npr.Config.Hash)
154 }
155
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500156 for key := range npr.Children {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400157 childrenKeys = append(childrenKeys, key)
158 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500159
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400160 sort.Strings(childrenKeys)
161
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500162 if len(npr.Children) > 0 {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400163 // Loop through sorted Children keys
164 for _, key := range childrenKeys {
165 for _, child := range npr.Children[key] {
166 if child != nil && child.GetHash() != "" {
167 buffer.WriteString(child.GetHash())
168 }
169 }
170 }
171 }
172
173 return fmt.Sprintf("%x", md5.Sum(buffer.Bytes()))[:12]
174}
175
176func (npr *NonPersistedRevision) Get(depth int) interface{} {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500177 // 1. Clone the data to avoid any concurrent access issues
178 // 2. The current rev might still be pointing to an old config
179 // thus, force the revision to get its latest value
180 latestRev := npr.GetBranch().GetLatest()
181 originalData := proto.Clone(latestRev.GetData().(proto.Message))
182
183 data := originalData
184 // Get back to the interface type
185 //data := reflect.ValueOf(originalData).Interface()
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400186
187 if depth != 0 {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500188 for fieldName, field := range ChildrenFields(latestRev.GetData()) {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400189 childDataName, childDataHolder := GetAttributeValue(data, fieldName, 0)
190 if field.IsContainer {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500191 for _, rev := range latestRev.GetChildren()[fieldName] {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400192 childData := rev.Get(depth - 1)
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400193 foundEntry := false
194 for i := 0; i < childDataHolder.Len(); i++ {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500195 cdh_if := childDataHolder.Index(i).Interface()
196 if cdh_if.(proto.Message).String() == childData.(proto.Message).String() {
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400197 foundEntry = true
198 break
199 }
200 }
201 if !foundEntry {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500202 // avoid duplicates by adding it only if the child was not found in the holder
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400203 childDataHolder = reflect.Append(childDataHolder, reflect.ValueOf(childData))
204 }
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400205 }
206 } else {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500207 if revs := latestRev.GetChildren()[fieldName]; revs != nil && len(revs) > 0 {
208 rev := latestRev.GetChildren()[fieldName][0]
Stephane Barbarie126101e2018-10-11 16:18:48 -0400209 if rev != nil {
210 childData := rev.Get(depth - 1)
211 if reflect.TypeOf(childData) == reflect.TypeOf(childDataHolder.Interface()) {
212 childDataHolder = reflect.ValueOf(childData)
213 }
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400214 }
215 }
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400216 }
217 // Merge child data with cloned object
218 reflect.ValueOf(data).Elem().FieldByName(childDataName).Set(childDataHolder)
219 }
220 }
Stephane Barbarie126101e2018-10-11 16:18:48 -0400221
222 result := data
223
224 if result != nil {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500225 // We need to send back a copy of the retrieved object
226 result = proto.Clone(data.(proto.Message))
Stephane Barbarie126101e2018-10-11 16:18:48 -0400227 }
228
229 return result
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400230}
231
232func (npr *NonPersistedRevision) UpdateData(data interface{}, branch *Branch) Revision {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500233 npr.mutex.Lock()
234 defer npr.mutex.Unlock()
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400235
Stephane Barbariee0a4c792019-01-16 11:26:29 -0500236 if npr.Config.Data != nil && npr.Config.Data == data {
237 log.Debugw("stored-data-matches-latest", log.Fields{"stored": npr.Config.Data, "provided": data})
238 return nil
239 }
240
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500241 newRev := NonPersistedRevision{}
242 newRev.Config = NewDataRevision(npr.Root, data)
243 newRev.Hash = npr.Hash
244 newRev.Branch = branch
245
246 newRev.Children = make(map[string][]Revision)
247 for entryName, childrenEntry := range npr.Children {
248 newRev.Children[entryName] = append(newRev.Children[entryName], childrenEntry...)
249 }
250
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500251 newRev.Finalize(false)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400252
253 return &newRev
254}
255
256func (npr *NonPersistedRevision) UpdateChildren(name string, children []Revision, branch *Branch) Revision {
Stephane Barbariee0a4c792019-01-16 11:26:29 -0500257 npr.mutex.Lock()
258 defer npr.mutex.Unlock()
259
260 updatedRev := npr
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400261
Stephane Barbarie933b09b2019-01-09 11:12:09 -0500262 // Verify if the map contains already contains an entry matching the name value
263 // If so, we need to retain the contents of that entry and merge them with the provided children revision list
264 if _, exists := updatedRev.Children[name]; exists {
265 // Go through all child hashes and save their index within the map
266 existChildMap := make(map[string]int)
267 for i, child := range updatedRev.Children[name] {
268 existChildMap[child.GetHash()] = i
269 }
270
271 // Identify the revisions that are not present in the existing list and add them
272 for _, newChild := range children {
273 if _, childExists := existChildMap[newChild.GetHash()]; !childExists {
274 updatedRev.Children[name] = append(updatedRev.Children[name], newChild)
275 }
276 }
277 } else {
278 // Map entry does not exist, thus just create a new entry and assign the provided revisions
279 updatedRev.Children[name] = make([]Revision, len(children))
280 copy(updatedRev.Children[name], children)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500281 }
282
Stephane Barbarie933b09b2019-01-09 11:12:09 -0500283 updatedRev.Config = NewDataRevision(npr.Root, npr.Config.Data)
284 updatedRev.Hash = npr.Hash
285 updatedRev.Branch = branch
286 updatedRev.Finalize(false)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400287
Stephane Barbariee0a4c792019-01-16 11:26:29 -0500288 return updatedRev
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400289}
290
291func (npr *NonPersistedRevision) UpdateAllChildren(children map[string][]Revision, branch *Branch) Revision {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500292 npr.mutex.Lock()
293 defer npr.mutex.Unlock()
294
295 newRev := &NonPersistedRevision{}
296 newRev.Config = npr.Config
297 newRev.Hash = npr.Hash
298 newRev.Branch = branch
299 newRev.Children = make(map[string][]Revision)
300 for entryName, childrenEntry := range npr.Children {
301 newRev.Children[entryName] = make([]Revision, len(childrenEntry))
302 copy(newRev.Children[entryName], childrenEntry)
303 }
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500304 newRev.Finalize(false)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400305
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500306 return newRev
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400307}
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400308
309func (npr *NonPersistedRevision) Drop(txid string, includeConfig bool) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500310 GetRevCache().Lock()
311 defer GetRevCache().Unlock()
312
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500313 if includeConfig {
314 delete(GetRevCache().Cache, npr.Config.Hash)
315 }
316 delete(GetRevCache().Cache, npr.Hash)
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400317}
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500318
319func (npr *NonPersistedRevision) LoadFromPersistence(path string, txid string) []Revision {
Stephane Barbariee0a4c792019-01-16 11:26:29 -0500320 // stub... required by interface
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500321 return nil
322}
Stephane Barbariee0a4c792019-01-16 11:26:29 -0500323
324func (npr *NonPersistedRevision) SetupWatch(key string) {
325 // stub ... required by interface
326}