blob: 3c39e01ffbaa3446274d562f08741ded09e1bd3d [file] [log] [blame]
Matt Jeanneretcab955f2019-04-10 15:45:57 -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"
22 "github.com/golang/protobuf/proto"
23 "github.com/opencord/voltha-go/common/log"
24 "reflect"
25 "sort"
26 "sync"
27)
28
29type 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}
43
44type NonPersistedRevision struct {
45 mutex sync.RWMutex
46 Root *root
47 Config *DataRevision
48 Children map[string][]Revision
49 Hash string
50 Branch *Branch
51 WeakRef string
52 Name string
53}
54
55func NewNonPersistedRevision(root *root, branch *Branch, data interface{}, children map[string][]Revision) Revision {
56 r := &NonPersistedRevision{}
57 r.Root = root
58 r.Branch = branch
59 r.Config = NewDataRevision(root, data)
60 r.Children = children
61 r.Hash = r.hashContent()
62 return r
63}
64
65func (npr *NonPersistedRevision) SetConfig(config *DataRevision) {
66 npr.mutex.Lock()
67 defer npr.mutex.Unlock()
68 npr.Config = config
69}
70
71func (npr *NonPersistedRevision) GetConfig() *DataRevision {
72 npr.mutex.Lock()
73 defer npr.mutex.Unlock()
74 return npr.Config
75}
76
77func (npr *NonPersistedRevision) SetAllChildren(children map[string][]Revision) {
78 npr.mutex.Lock()
79 defer npr.mutex.Unlock()
80 npr.Children = children
81}
82
83func (npr *NonPersistedRevision) SetChildren(name string, children []Revision) {
84 npr.mutex.Lock()
85 defer npr.mutex.Unlock()
86 if _, exists := npr.Children[name]; exists {
87 npr.Children[name] = children
88 }
89}
90
91func (npr *NonPersistedRevision) GetAllChildren() map[string][]Revision {
92 npr.mutex.Lock()
93 defer npr.mutex.Unlock()
94 return npr.Children
95}
96
97func (npr *NonPersistedRevision) GetChildren(name string) []Revision {
98 npr.mutex.Lock()
99 defer npr.mutex.Unlock()
100 if _, exists := npr.Children[name]; exists {
101 return npr.Children[name]
102 }
103 return nil
104}
105
106func (npr *NonPersistedRevision) SetHash(hash string) {
107 npr.mutex.Lock()
108 defer npr.mutex.Unlock()
109 npr.Hash = hash
110}
111
112func (npr *NonPersistedRevision) GetHash() string {
113 //npr.mutex.Lock()
114 //defer npr.mutex.Unlock()
115 return npr.Hash
116}
117
118func (npr *NonPersistedRevision) ClearHash() {
119 npr.mutex.Lock()
120 defer npr.mutex.Unlock()
121 npr.Hash = ""
122}
123
124func (npr *NonPersistedRevision) GetName() string {
125 //npr.mutex.Lock()
126 //defer npr.mutex.Unlock()
127 return npr.Name
128}
129
130func (npr *NonPersistedRevision) SetName(name string) {
131 //npr.mutex.Lock()
132 //defer npr.mutex.Unlock()
133 npr.Name = name
134}
135func (npr *NonPersistedRevision) SetBranch(branch *Branch) {
136 npr.mutex.Lock()
137 defer npr.mutex.Unlock()
138 npr.Branch = branch
139}
140
141func (npr *NonPersistedRevision) GetBranch() *Branch {
142 npr.mutex.Lock()
143 defer npr.mutex.Unlock()
144 return npr.Branch
145}
146
147func (npr *NonPersistedRevision) GetData() interface{} {
148 npr.mutex.Lock()
149 defer npr.mutex.Unlock()
150 if npr.Config == nil {
151 return nil
152 }
153 return npr.Config.Data
154}
155
156func (npr *NonPersistedRevision) GetNode() *node {
157 npr.mutex.Lock()
158 defer npr.mutex.Unlock()
159 return npr.Branch.Node
160}
161
162func (npr *NonPersistedRevision) Finalize(skipOnExist bool) {
163 GetRevCache().Lock()
164 defer GetRevCache().Unlock()
165
166 if !skipOnExist {
167 npr.Hash = npr.hashContent()
168 }
169 if _, exists := GetRevCache().Cache[npr.Hash]; !exists {
170 GetRevCache().Cache[npr.Hash] = npr
171 }
172 if _, exists := GetRevCache().Cache[npr.Config.Hash]; !exists {
173 GetRevCache().Cache[npr.Config.Hash] = npr.Config
174 } else {
175 npr.Config = GetRevCache().Cache[npr.Config.Hash].(*DataRevision)
176 }
177}
178
179func (npr *NonPersistedRevision) hashContent() string {
180 var buffer bytes.Buffer
181 var childrenKeys []string
182
183 if npr.Config != nil {
184 buffer.WriteString(npr.Config.Hash)
185 }
186
187 for key := range npr.Children {
188 childrenKeys = append(childrenKeys, key)
189 }
190
191 sort.Strings(childrenKeys)
192
193 if len(npr.Children) > 0 {
194 // Loop through sorted Children keys
195 for _, key := range childrenKeys {
196 for _, child := range npr.Children[key] {
197 if child != nil && child.GetHash() != "" {
198 buffer.WriteString(child.GetHash())
199 }
200 }
201 }
202 }
203
204 return fmt.Sprintf("%x", md5.Sum(buffer.Bytes()))[:12]
205}
206
207func (npr *NonPersistedRevision) Get(depth int) interface{} {
208 // 1. Clone the data to avoid any concurrent access issues
209 // 2. The current rev might still be pointing to an old config
210 // thus, force the revision to get its latest value
211 latestRev := npr.GetBranch().GetLatest()
212 originalData := proto.Clone(latestRev.GetData().(proto.Message))
213
214 data := originalData
215 // Get back to the interface type
216 //data := reflect.ValueOf(originalData).Interface()
217
218 if depth != 0 {
219 for fieldName, field := range ChildrenFields(latestRev.GetData()) {
220 childDataName, childDataHolder := GetAttributeValue(data, fieldName, 0)
221 if field.IsContainer {
222 for _, rev := range latestRev.GetChildren(fieldName) {
223 childData := rev.Get(depth - 1)
224 foundEntry := false
225 for i := 0; i < childDataHolder.Len(); i++ {
226 cdh_if := childDataHolder.Index(i).Interface()
227 if cdh_if.(proto.Message).String() == childData.(proto.Message).String() {
228 foundEntry = true
229 break
230 }
231 }
232 if !foundEntry {
233 // avoid duplicates by adding it only if the child was not found in the holder
234 childDataHolder = reflect.Append(childDataHolder, reflect.ValueOf(childData))
235 }
236 }
237 } else {
238 if revs := latestRev.GetChildren(fieldName); revs != nil && len(revs) > 0 {
239 rev := latestRev.GetChildren(fieldName)[0]
240 if rev != nil {
241 childData := rev.Get(depth - 1)
242 if reflect.TypeOf(childData) == reflect.TypeOf(childDataHolder.Interface()) {
243 childDataHolder = reflect.ValueOf(childData)
244 }
245 }
246 }
247 }
248 // Merge child data with cloned object
249 reflect.ValueOf(data).Elem().FieldByName(childDataName).Set(childDataHolder)
250 }
251 }
252
253 result := data
254
255 if result != nil {
256 // We need to send back a copy of the retrieved object
257 result = proto.Clone(data.(proto.Message))
258 }
259
260 return result
261}
262
263func (npr *NonPersistedRevision) UpdateData(data interface{}, branch *Branch) Revision {
264 npr.mutex.Lock()
265 defer npr.mutex.Unlock()
266
267 if npr.Config.Data != nil && npr.Config.hashData(npr.Root, data) == npr.Config.Hash {
268 log.Debugw("stored-data-matches-latest", log.Fields{"stored": npr.Config.Data, "provided": data})
269 return npr
270 }
271
272 newRev := NonPersistedRevision{}
273 newRev.Config = NewDataRevision(npr.Root, data)
274 newRev.Hash = npr.Hash
275 newRev.Branch = branch
276
277 newRev.Children = make(map[string][]Revision)
278 for entryName, childrenEntry := range npr.Children {
279 newRev.Children[entryName] = append(newRev.Children[entryName], childrenEntry...)
280 }
281
282 newRev.Finalize(false)
283
284 return &newRev
285}
286
287func (npr *NonPersistedRevision) UpdateChildren(name string, children []Revision, branch *Branch) Revision {
288 npr.mutex.Lock()
289 defer npr.mutex.Unlock()
290
291 updatedRev := npr
292
293 // Verify if the map contains already contains an entry matching the name value
294 // If so, we need to retain the contents of that entry and merge them with the provided children revision list
295 if _, exists := updatedRev.Children[name]; exists {
296 // Go through all child hashes and save their index within the map
297 existChildMap := make(map[string]int)
298 for i, child := range updatedRev.Children[name] {
299 existChildMap[child.GetHash()] = i
300 }
301
302 for _, newChild := range children {
303 if _, childExists := existChildMap[newChild.GetHash()]; !childExists {
304 // revision is not present in the existing list... add it
305 updatedRev.Children[name] = append(updatedRev.Children[name], newChild)
306 } else {
307 // replace
308 updatedRev.Children[name][existChildMap[newChild.GetHash()]] = newChild
309 }
310 }
311 } else {
312 // Map entry does not exist, thus just create a new entry and assign the provided revisions
313 updatedRev.Children[name] = make([]Revision, len(children))
314 copy(updatedRev.Children[name], children)
315 }
316
317 updatedRev.Config = NewDataRevision(npr.Root, npr.Config.Data)
318 updatedRev.Hash = npr.Hash
319 updatedRev.Branch = branch
320 updatedRev.Finalize(false)
321
322 return updatedRev
323}
324
325func (npr *NonPersistedRevision) UpdateAllChildren(children map[string][]Revision, branch *Branch) Revision {
326 npr.mutex.Lock()
327 defer npr.mutex.Unlock()
328
329 newRev := npr
330 newRev.Config = npr.Config
331 newRev.Hash = npr.Hash
332 newRev.Branch = branch
333 newRev.Children = make(map[string][]Revision)
334 for entryName, childrenEntry := range children {
335 newRev.Children[entryName] = append(newRev.Children[entryName], childrenEntry...)
336 }
337 newRev.Finalize(false)
338
339 return newRev
340}
341
342func (npr *NonPersistedRevision) Drop(txid string, includeConfig bool) {
343 GetRevCache().Lock()
344 defer GetRevCache().Unlock()
345
346 if includeConfig {
347 delete(GetRevCache().Cache, npr.Config.Hash)
348 }
349 delete(GetRevCache().Cache, npr.Hash)
350}
351
352func (npr *NonPersistedRevision) LoadFromPersistence(path string, txid string) []Revision {
353 // stub... required by interface
354 return nil
355}
356
357func (npr *NonPersistedRevision) SetupWatch(key string) {
358 // stub ... required by interface
359}
360
361func (pr *NonPersistedRevision) StorageDrop(txid string, includeConfig bool) {
362 // stub ... required by interface
363}