blob: 811e35dde7547f94f5f75a3788fac3091edd2a0f [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
Stephane Barbariedc5022d2018-11-19 15:21:44 -050018import "C"
Stephane Barbarieec0919b2018-09-05 14:14:29 -040019import (
20 "bytes"
21 "crypto/md5"
22 "fmt"
Stephane Barbarie126101e2018-10-11 16:18:48 -040023 "github.com/golang/protobuf/proto"
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
60 r.Finalize()
61 return r
Stephane Barbarieec0919b2018-09-05 14:14:29 -040062}
63
64func (npr *NonPersistedRevision) SetConfig(config *DataRevision) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -050065 npr.mutex.Lock()
66 defer npr.mutex.Unlock()
Stephane Barbarieec0919b2018-09-05 14:14:29 -040067 npr.Config = config
68}
69
70func (npr *NonPersistedRevision) GetConfig() *DataRevision {
Stephane Barbariedc5022d2018-11-19 15:21:44 -050071 npr.mutex.Lock()
72 defer npr.mutex.Unlock()
Stephane Barbarieec0919b2018-09-05 14:14:29 -040073 return npr.Config
74}
75
76func (npr *NonPersistedRevision) SetChildren(children map[string][]Revision) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -050077 npr.mutex.Lock()
78 defer npr.mutex.Unlock()
Stephane Barbarieec0919b2018-09-05 14:14:29 -040079 npr.Children = children
80}
81
82func (npr *NonPersistedRevision) GetChildren() map[string][]Revision {
Stephane Barbariedc5022d2018-11-19 15:21:44 -050083 npr.mutex.Lock()
84 defer npr.mutex.Unlock()
Stephane Barbarieec0919b2018-09-05 14:14:29 -040085 return npr.Children
86}
87
88func (npr *NonPersistedRevision) SetHash(hash string) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -050089 npr.mutex.Lock()
90 defer npr.mutex.Unlock()
Stephane Barbarieec0919b2018-09-05 14:14:29 -040091 npr.Hash = hash
92}
93
94func (npr *NonPersistedRevision) GetHash() string {
Stephane Barbariedc5022d2018-11-19 15:21:44 -050095 //npr.mutex.Lock()
96 //defer npr.mutex.Unlock()
Stephane Barbarieec0919b2018-09-05 14:14:29 -040097 return npr.Hash
98}
99
100func (npr *NonPersistedRevision) ClearHash() {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500101 npr.mutex.Lock()
102 defer npr.mutex.Unlock()
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400103 npr.Hash = ""
104}
105
106func (npr *NonPersistedRevision) SetBranch(branch *Branch) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500107 npr.mutex.Lock()
108 defer npr.mutex.Unlock()
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400109 npr.Branch = branch
110}
111
112func (npr *NonPersistedRevision) GetBranch() *Branch {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500113 npr.mutex.Lock()
114 defer npr.mutex.Unlock()
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400115 return npr.Branch
116}
117
118func (npr *NonPersistedRevision) GetData() interface{} {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500119 npr.mutex.Lock()
120 defer npr.mutex.Unlock()
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400121 if npr.Config == nil {
122 return nil
123 }
124 return npr.Config.Data
125}
126
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400127func (npr *NonPersistedRevision) GetNode() *node {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500128 npr.mutex.Lock()
129 defer npr.mutex.Unlock()
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400130 return npr.Branch.Node
131}
132
133func (npr *NonPersistedRevision) Finalize() {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500134 GetRevCache().Lock()
135 defer GetRevCache().Unlock()
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400136
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500137 npr.Hash = npr.hashContent()
138
139 if _, exists := GetRevCache().Cache[npr.Hash]; !exists {
140 GetRevCache().Cache[npr.Hash] = npr
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400141 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500142 if _, exists := GetRevCache().Cache[npr.Config.Hash]; !exists {
143 GetRevCache().Cache[npr.Config.Hash] = npr.Config
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400144 } else {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500145 npr.Config = GetRevCache().Cache[npr.Config.Hash].(*DataRevision)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400146 }
147}
148
149func (npr *NonPersistedRevision) hashContent() string {
150 var buffer bytes.Buffer
151 var childrenKeys []string
152
153 if npr.Config != nil {
154 buffer.WriteString(npr.Config.Hash)
155 }
156
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500157 for key := range npr.Children {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400158 childrenKeys = append(childrenKeys, key)
159 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500160
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400161 sort.Strings(childrenKeys)
162
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500163 if len(npr.Children) > 0 {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400164 // Loop through sorted Children keys
165 for _, key := range childrenKeys {
166 for _, child := range npr.Children[key] {
167 if child != nil && child.GetHash() != "" {
168 buffer.WriteString(child.GetHash())
169 }
170 }
171 }
172 }
173
174 return fmt.Sprintf("%x", md5.Sum(buffer.Bytes()))[:12]
175}
176
177func (npr *NonPersistedRevision) Get(depth int) interface{} {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500178 // 1. Clone the data to avoid any concurrent access issues
179 // 2. The current rev might still be pointing to an old config
180 // thus, force the revision to get its latest value
181 latestRev := npr.GetBranch().GetLatest()
182 originalData := proto.Clone(latestRev.GetData().(proto.Message))
183
184 data := originalData
185 // Get back to the interface type
186 //data := reflect.ValueOf(originalData).Interface()
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400187
188 if depth != 0 {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500189 for fieldName, field := range ChildrenFields(latestRev.GetData()) {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400190 childDataName, childDataHolder := GetAttributeValue(data, fieldName, 0)
191 if field.IsContainer {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500192 for _, rev := range latestRev.GetChildren()[fieldName] {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400193 childData := rev.Get(depth - 1)
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400194 foundEntry := false
195 for i := 0; i < childDataHolder.Len(); i++ {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500196 cdh_if := childDataHolder.Index(i).Interface()
197 if cdh_if.(proto.Message).String() == childData.(proto.Message).String() {
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400198 foundEntry = true
199 break
200 }
201 }
202 if !foundEntry {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500203 // avoid duplicates by adding it only if the child was not found in the holder
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400204 childDataHolder = reflect.Append(childDataHolder, reflect.ValueOf(childData))
205 }
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400206 }
207 } else {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500208 if revs := latestRev.GetChildren()[fieldName]; revs != nil && len(revs) > 0 {
209 rev := latestRev.GetChildren()[fieldName][0]
Stephane Barbarie126101e2018-10-11 16:18:48 -0400210 if rev != nil {
211 childData := rev.Get(depth - 1)
212 if reflect.TypeOf(childData) == reflect.TypeOf(childDataHolder.Interface()) {
213 childDataHolder = reflect.ValueOf(childData)
214 }
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400215 }
216 }
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400217 }
218 // Merge child data with cloned object
219 reflect.ValueOf(data).Elem().FieldByName(childDataName).Set(childDataHolder)
220 }
221 }
Stephane Barbarie126101e2018-10-11 16:18:48 -0400222
223 result := data
224
225 if result != nil {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500226 // We need to send back a copy of the retrieved object
227 result = proto.Clone(data.(proto.Message))
Stephane Barbarie126101e2018-10-11 16:18:48 -0400228 }
229
230 return result
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400231}
232
233func (npr *NonPersistedRevision) UpdateData(data interface{}, branch *Branch) Revision {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500234 npr.mutex.Lock()
235 defer npr.mutex.Unlock()
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400236
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500237 newRev := NonPersistedRevision{}
238 newRev.Config = NewDataRevision(npr.Root, data)
239 newRev.Hash = npr.Hash
240 newRev.Branch = branch
241
242 newRev.Children = make(map[string][]Revision)
243 for entryName, childrenEntry := range npr.Children {
244 newRev.Children[entryName] = append(newRev.Children[entryName], childrenEntry...)
245 }
246
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400247 newRev.Finalize()
248
249 return &newRev
250}
251
252func (npr *NonPersistedRevision) UpdateChildren(name string, children []Revision, branch *Branch) Revision {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500253 npr.mutex.Lock()
254 defer npr.mutex.Unlock()
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400255
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500256 newRev := NonPersistedRevision{}
257 newRev.Children = make(map[string][]Revision)
258 for entryName, childrenEntry := range npr.Children {
259 newRev.Children[entryName] = make([]Revision, len(childrenEntry))
260 copy(newRev.Children[entryName], childrenEntry)
261 }
262
263 newRev.Children[name] = make([]Revision, len(children))
264 copy(newRev.Children[name], children)
265
266 newRev.Config = NewDataRevision(npr.Root, npr.Config.Data)
267 newRev.Hash = npr.Hash
268 newRev.Branch = branch
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400269 newRev.Finalize()
270
271 return &newRev
272}
273
274func (npr *NonPersistedRevision) UpdateAllChildren(children map[string][]Revision, branch *Branch) Revision {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500275 npr.mutex.Lock()
276 defer npr.mutex.Unlock()
277
278 newRev := &NonPersistedRevision{}
279 newRev.Config = npr.Config
280 newRev.Hash = npr.Hash
281 newRev.Branch = branch
282 newRev.Children = make(map[string][]Revision)
283 for entryName, childrenEntry := range npr.Children {
284 newRev.Children[entryName] = make([]Revision, len(childrenEntry))
285 copy(newRev.Children[entryName], childrenEntry)
286 }
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400287 newRev.Finalize()
288
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500289 return newRev
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400290}
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400291
292func (npr *NonPersistedRevision) Drop(txid string, includeConfig bool) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500293 GetRevCache().Lock()
294 defer GetRevCache().Unlock()
295
296 npr.mutex.Lock()
297 defer npr.mutex.Unlock()
298
299 if includeConfig {
300 delete(GetRevCache().Cache, npr.Config.Hash)
301 }
302 delete(GetRevCache().Cache, npr.Hash)
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400303}