blob: bec07a563fe3a5b3fcac5ee8b099975af1a0635d [file] [log] [blame]
khenaidoobf6e7bb2018-08-14 22:27: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 */
Stephane Barbariedc5022d2018-11-19 15:21:44 -050016
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040017package model
18
Stephane Barbariee16186c2018-09-11 10:46:34 -040019// TODO: proper error handling
20// TODO: proper logging
21
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040022import (
Stephane Barbarieef6650d2019-07-18 12:15:09 -040023 "context"
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040024 "fmt"
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040025 "reflect"
26 "strings"
Stephane Barbariedc5022d2018-11-19 15:21:44 -050027 "sync"
Stephane Barbarie802aca42019-05-21 12:19:28 -040028 "time"
npujar9a30c702019-11-14 17:06:39 +053029
30 "github.com/golang/protobuf/proto"
serkant.uluderya2ae470f2020-01-21 11:13:09 -080031 "github.com/opencord/voltha-lib-go/v3/pkg/log"
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040032)
33
Stephane Barbariedc5022d2018-11-19 15:21:44 -050034// When a branch has no transaction id, everything gets stored in NONE
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040035const (
36 NONE string = "none"
37)
38
Stephane Barbariedc5022d2018-11-19 15:21:44 -050039// Node interface is an abstraction of the node data structure
Stephane Barbarie06c4a742018-10-01 11:09:32 -040040type Node interface {
npujar467fe752020-01-16 20:17:45 +053041 MakeLatest(ctx context.Context, branch *Branch, revision Revision, changeAnnouncement []ChangeTuple)
Stephane Barbarie06c4a742018-10-01 11:09:32 -040042
43 // CRUD functions
Stephane Barbarieef6650d2019-07-18 12:15:09 -040044 Add(ctx context.Context, path string, data interface{}, txid string, makeBranch MakeBranchFunction) Revision
Thomas Lee Se5a44012019-11-07 20:32:24 +053045 Get(ctx context.Context, path string, hash string, depth int, deep bool, txid string) (interface{}, error)
46 List(ctx context.Context, path string, hash string, depth int, deep bool, txid string) (interface{}, error)
Stephane Barbarieef6650d2019-07-18 12:15:09 -040047 Update(ctx context.Context, path string, data interface{}, strict bool, txid string, makeBranch MakeBranchFunction) Revision
48 Remove(ctx context.Context, path string, txid string, makeBranch MakeBranchFunction) Revision
Thomas Lee Se5a44012019-11-07 20:32:24 +053049 CreateProxy(ctx context.Context, path string, exclusive bool) (*Proxy, error)
Stephane Barbarieef6650d2019-07-18 12:15:09 -040050
51 GetProxy() *Proxy
Stephane Barbarie06c4a742018-10-01 11:09:32 -040052
53 MakeBranch(txid string) *Branch
54 DeleteBranch(txid string)
npujar467fe752020-01-16 20:17:45 +053055 MergeBranch(ctx context.Context, txid string, dryRun bool) (Revision, error)
Stephane Barbarie06c4a742018-10-01 11:09:32 -040056
57 MakeTxBranch() string
58 DeleteTxBranch(txid string)
npujar467fe752020-01-16 20:17:45 +053059 FoldTxBranch(ctx context.Context, txid string)
Stephane Barbarie06c4a742018-10-01 11:09:32 -040060}
61
62type node struct {
Stephane Barbarieef6650d2019-07-18 12:15:09 -040063 mutex sync.RWMutex
64 Root *root
65 Type interface{}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040066 Branches map[string]*Branch
Stephane Barbarieec0919b2018-09-05 14:14:29 -040067 Tags map[string]Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040068 Proxy *Proxy
69 EventBus *EventBus
70 AutoPrune bool
71}
72
Stephane Barbariedc5022d2018-11-19 15:21:44 -050073// ChangeTuple holds details of modifications made to a revision
Stephane Barbarie694e2b92018-09-07 12:17:36 -040074type ChangeTuple struct {
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -040075 Type CallbackType
76 PreviousData interface{}
77 LatestData interface{}
Stephane Barbarie694e2b92018-09-07 12:17:36 -040078}
79
npujar9a30c702019-11-14 17:06:39 +053080// newNode creates a new instance of the node data structure
81func newNode(root *root, initialData interface{}, autoPrune bool, txid string) *node {
Stephane Barbarie06c4a742018-10-01 11:09:32 -040082 n := &node{}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040083
Stephane Barbarie126101e2018-10-11 16:18:48 -040084 n.Root = root
Stephane Barbarieec0919b2018-09-05 14:14:29 -040085 n.Branches = make(map[string]*Branch)
86 n.Tags = make(map[string]Revision)
87 n.Proxy = nil
88 n.EventBus = nil
89 n.AutoPrune = autoPrune
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040090
91 if IsProtoMessage(initialData) {
Stephane Barbarieec0919b2018-09-05 14:14:29 -040092 n.Type = reflect.ValueOf(initialData).Interface()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040093 dataCopy := proto.Clone(initialData.(proto.Message))
Stephane Barbarieec0919b2018-09-05 14:14:29 -040094 n.initialize(dataCopy, txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040095 } else if reflect.ValueOf(initialData).IsValid() {
Stephane Barbariedc5022d2018-11-19 15:21:44 -050096 // FIXME: this block does not reflect the original implementation
97 // it should be checking if the provided initial_data is already a type!??!
98 // it should be checked before IsProtoMessage
Stephane Barbarieec0919b2018-09-05 14:14:29 -040099 n.Type = reflect.ValueOf(initialData).Interface()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400100 } else {
101 // not implemented error
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -0400102 log.Errorf("cannot process initial data - %+v", initialData)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400103 }
104
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400105 return n
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400106}
107
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500108// MakeNode creates a new node in the tree
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400109func (n *node) MakeNode(data interface{}, txid string) *node {
npujar9a30c702019-11-14 17:06:39 +0530110 return newNode(n.Root, data, true, txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400111}
112
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500113// MakeRevision create a new revision of the node in the tree
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400114func (n *node) MakeRevision(branch *Branch, data interface{}, children map[string][]Revision) Revision {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500115 return n.GetRoot().MakeRevision(branch, data, children)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400116}
117
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500118// makeLatest will mark the revision of a node as being the latest
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400119func (n *node) makeLatest(branch *Branch, revision Revision, changeAnnouncement []ChangeTuple) {
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400120 // Keep a reference to the current revision
121 var previous string
122 if branch.GetLatest() != nil {
123 previous = branch.GetLatest().GetHash()
124 }
125
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500126 branch.AddRevision(revision)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400127
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400128 // If anything is new, then set the revision as the latest
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500129 if branch.GetLatest() == nil || revision.GetHash() != branch.GetLatest().GetHash() {
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400130 if revision.GetName() != "" {
Stephane Barbarie802aca42019-05-21 12:19:28 -0400131 log.Debugw("saving-latest-data", log.Fields{"hash": revision.GetHash(), "data": revision.GetData()})
132 // Tag a timestamp to that revision
133 revision.SetLastUpdate()
npujar9a30c702019-11-14 17:06:39 +0530134 getRevCache().Set(revision.GetName(), revision)
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400135 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500136 branch.SetLatest(revision)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400137 }
138
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400139 // Delete the previous revision if anything has changed
140 if previous != "" && previous != branch.GetLatest().GetHash() {
141 branch.DeleteRevision(previous)
142 }
143
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400144 if changeAnnouncement != nil && branch.Txid == "" {
Stephane Barbarie260a5632019-02-26 16:12:49 -0500145 if n.Proxy != nil {
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400146 for _, change := range changeAnnouncement {
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400147 log.Debugw("adding-callback",
148 log.Fields{
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400149 "callbacks": n.GetProxy().getCallbacks(change.Type),
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400150 "type": change.Type,
151 "previousData": change.PreviousData,
152 "latestData": change.LatestData,
153 })
Stephane Barbarie260a5632019-02-26 16:12:49 -0500154 n.Root.AddCallback(
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400155 n.GetProxy().InvokeCallbacks,
Stephane Barbarie126101e2018-10-11 16:18:48 -0400156 change.Type,
157 true,
158 change.PreviousData,
159 change.LatestData)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400160 }
161 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400162 }
163}
164
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500165// Latest returns the latest revision of node with or without the transaction id
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400166func (n *node) Latest(txid ...string) Revision {
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400167 var branch *Branch
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400168
169 if len(txid) > 0 && txid[0] != "" {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500170 if branch = n.GetBranch(txid[0]); branch != nil {
171 return branch.GetLatest()
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400172 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500173 } else if branch = n.GetBranch(NONE); branch != nil {
174 return branch.GetLatest()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400175 }
176 return nil
177}
178
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500179// initialize prepares the content of a node along with its possible ramifications
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400180func (n *node) initialize(data interface{}, txid string) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500181 children := make(map[string][]Revision)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400182 for fieldName, field := range ChildrenFields(n.Type) {
183 _, fieldValue := GetAttributeValue(data, fieldName, 0)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400184
185 if fieldValue.IsValid() {
186 if field.IsContainer {
187 if field.Key != "" {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400188 for i := 0; i < fieldValue.Len(); i++ {
189 v := fieldValue.Index(i)
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400190
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500191 if rev := n.MakeNode(v.Interface(), txid).Latest(txid); rev != nil {
192 children[fieldName] = append(children[fieldName], rev)
193 }
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400194
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -0500195 // TODO: The following logic was ported from v1.0. Need to verify if it is required
196 //var keysSeen []string
197 //_, key := GetAttributeValue(v.Interface(), field.Key, 0)
198 //for _, k := range keysSeen {
199 // if k == key.String() {
200 // //log.Errorf("duplicate key - %s", k)
201 // }
202 //}
203 //keysSeen = append(keysSeen, key.String())
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400204 }
205
206 } else {
207 for i := 0; i < fieldValue.Len(); i++ {
208 v := fieldValue.Index(i)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500209 if newNodeRev := n.MakeNode(v.Interface(), txid).Latest(); newNodeRev != nil {
210 children[fieldName] = append(children[fieldName], newNodeRev)
211 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400212 }
213 }
214 } else {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500215 if newNodeRev := n.MakeNode(fieldValue.Interface(), txid).Latest(); newNodeRev != nil {
216 children[fieldName] = append(children[fieldName], newNodeRev)
217 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400218 }
219 } else {
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -0400220 log.Errorf("field is invalid - %+v", fieldValue)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400221 }
222 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500223
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400224 branch := NewBranch(n, "", nil, n.AutoPrune)
225 rev := n.MakeRevision(branch, data, children)
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400226 n.makeLatest(branch, rev, nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400227
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400228 if txid == "" {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500229 n.SetBranch(NONE, branch)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400230 } else {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500231 n.SetBranch(txid, branch)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400232 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400233}
234
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500235// findRevByKey retrieves a specific revision from a node tree
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400236func (n *node) findRevByKey(revs []Revision, keyName string, value interface{}) (int, Revision) {
237 for i, rev := range revs {
238 dataValue := reflect.ValueOf(rev.GetData())
239 dataStruct := GetAttributeStructure(rev.GetData(), keyName, 0)
240
241 fieldValue := dataValue.Elem().FieldByName(dataStruct.Name)
242
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400243 a := fmt.Sprintf("%s", fieldValue.Interface())
244 b := fmt.Sprintf("%s", value)
245 if a == b {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500246 return i, revs[i]
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400247 }
248 }
249
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400250 return -1, nil
251}
252
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500253// Get retrieves the data from a node tree that resides at the specified path
Thomas Lee Se5a44012019-11-07 20:32:24 +0530254func (n *node) List(ctx context.Context, path string, hash string, depth int, deep bool, txid string) (interface{}, error) {
Stephane Barbarie802aca42019-05-21 12:19:28 -0400255 n.mutex.Lock()
256 defer n.mutex.Unlock()
257
Stephane Barbarie11b88e72019-02-07 12:28:29 -0500258 log.Debugw("node-list-request", log.Fields{"path": path, "hash": hash, "depth": depth, "deep": deep, "txid": txid})
Stephane Barbarieaa467942019-02-06 14:09:44 -0500259
260 for strings.HasPrefix(path, "/") {
261 path = path[1:]
262 }
263
264 var branch *Branch
265 var rev Revision
266
267 if branch = n.GetBranch(txid); txid == "" || branch == nil {
268 branch = n.GetBranch(NONE)
269 }
270
271 if hash != "" {
272 rev = branch.GetRevision(hash)
273 } else {
274 rev = branch.GetLatest()
275 }
276
277 var result interface{}
278 var prList []interface{}
Thomas Lee Se5a44012019-11-07 20:32:24 +0530279
280 pr, err := rev.LoadFromPersistence(ctx, path, txid, nil)
281 if err != nil {
282 log.Errorf("failed-to-load-from-persistence")
283 return nil, err
284 }
285 if pr != nil {
Stephane Barbarieaa467942019-02-06 14:09:44 -0500286 for _, revEntry := range pr {
287 prList = append(prList, revEntry.GetData())
288 }
289 result = prList
290 }
Thomas Lee Se5a44012019-11-07 20:32:24 +0530291 return result, nil
Stephane Barbarieaa467942019-02-06 14:09:44 -0500292}
293
294// Get retrieves the data from a node tree that resides at the specified path
Thomas Lee Se5a44012019-11-07 20:32:24 +0530295func (n *node) Get(ctx context.Context, path string, hash string, depth int, reconcile bool, txid string) (interface{}, error) {
Stephane Barbarie802aca42019-05-21 12:19:28 -0400296 n.mutex.Lock()
297 defer n.mutex.Unlock()
298
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400299 log.Debugw("node-get-request", log.Fields{"path": path, "hash": hash, "depth": depth, "reconcile": reconcile, "txid": txid})
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400300
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400301 for strings.HasPrefix(path, "/") {
302 path = path[1:]
303 }
304
305 var branch *Branch
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400306 var rev Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400307
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500308 if branch = n.GetBranch(txid); txid == "" || branch == nil {
309 branch = n.GetBranch(NONE)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400310 }
311
312 if hash != "" {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500313 rev = branch.GetRevision(hash)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400314 } else {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500315 rev = branch.GetLatest()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400316 }
317
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500318 var result interface{}
Stephane Barbarie260a5632019-02-26 16:12:49 -0500319
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400320 // If there is no request to reconcile, try to get it from memory
Stephane Barbarie260a5632019-02-26 16:12:49 -0500321 if !reconcile {
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400322 // Try to find an entry matching the path value from one of these sources
323 // 1. Start with the cache which stores revisions by watch names
324 // 2. Then look in the revision tree, especially if it's a sub-path such as /devices/1234/flows
Stephane Barbarie802aca42019-05-21 12:19:28 -0400325 // 3. Move on to the KV store if that path cannot be found or if the entry has expired
npujar9a30c702019-11-14 17:06:39 +0530326 if entry, exists := getRevCache().Get(path); exists && entry.(Revision) != nil {
327 entryAge := time.Since(entry.(Revision).GetLastUpdate()).Nanoseconds() / int64(time.Millisecond)
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400328 if entryAge < DataRefreshPeriod {
Stephane Barbariec92d1072019-06-07 16:21:49 -0400329 log.Debugw("using-cache-entry", log.Fields{
330 "path": path,
331 "hash": hash,
332 "age": entryAge,
333 })
Thomas Lee Se5a44012019-11-07 20:32:24 +0530334 return proto.Clone(entry.(Revision).GetData().(proto.Message)), nil
Stephane Barbarie802aca42019-05-21 12:19:28 -0400335 }
npujar9a30c702019-11-14 17:06:39 +0530336 log.Debugw("cache-entry-expired", log.Fields{"path": path, "hash": hash, "age": entryAge})
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400337 } else if result = n.getPath(ctx, rev.GetBranch().GetLatest(), path, depth); result != nil && reflect.ValueOf(result).IsValid() && !reflect.ValueOf(result).IsNil() {
Stephane Barbarie802aca42019-05-21 12:19:28 -0400338 log.Debugw("using-rev-tree-entry", log.Fields{"path": path, "hash": hash, "depth": depth, "reconcile": reconcile, "txid": txid})
Thomas Lee Se5a44012019-11-07 20:32:24 +0530339 return result, nil
Stephane Barbarie802aca42019-05-21 12:19:28 -0400340 } else {
341 log.Debugw("not-using-cache-entry", log.Fields{
342 "path": path,
343 "hash": hash, "depth": depth,
344 "reconcile": reconcile,
345 "txid": txid,
346 })
Stephane Barbarie260a5632019-02-26 16:12:49 -0500347 }
Stephane Barbarie802aca42019-05-21 12:19:28 -0400348 } else {
349 log.Debugw("reconcile-requested", log.Fields{
350 "path": path,
351 "hash": hash,
352 "reconcile": reconcile,
353 })
Stephane Barbarie260a5632019-02-26 16:12:49 -0500354 }
355
Stephane Barbarie802aca42019-05-21 12:19:28 -0400356 // If we got to this point, we are either trying to reconcile with the db
Stephane Barbarie260a5632019-02-26 16:12:49 -0500357 // or we simply failed at getting information from memory
358 if n.Root.KvStore != nil {
Thomas Lee Se5a44012019-11-07 20:32:24 +0530359 if pr, err := rev.LoadFromPersistence(ctx, path, txid, nil); err != nil {
360 log.Errorf("failed-to-load-from-persistence")
361 return nil, err
362 } else if len(pr) > 0 {
Stephane Barbarie11b88e72019-02-07 12:28:29 -0500363 // Did we receive a single or multiple revisions?
364 if len(pr) > 1 {
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400365 var revs []interface{}
Stephane Barbarie11b88e72019-02-07 12:28:29 -0500366 for _, revEntry := range pr {
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400367 revs = append(revs, revEntry.GetData())
Stephane Barbarie11b88e72019-02-07 12:28:29 -0500368 }
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400369 result = revs
Stephane Barbarie11b88e72019-02-07 12:28:29 -0500370 } else {
371 result = pr[0].GetData()
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500372 }
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500373 }
374 }
Thomas Lee Se5a44012019-11-07 20:32:24 +0530375 return result, nil
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400376}
377
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400378//getPath traverses the specified path and retrieves the data associated to it
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400379func (n *node) getPath(ctx context.Context, rev Revision, path string, depth int) interface{} {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400380 if path == "" {
npujar467fe752020-01-16 20:17:45 +0530381 return n.getData(ctx, rev, depth)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400382 }
383
384 partition := strings.SplitN(path, "/", 2)
385 name := partition[0]
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400386
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400387 if len(partition) < 2 {
388 path = ""
389 } else {
390 path = partition[1]
391 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400392
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400393 names := ChildrenFields(n.Type)
394 field := names[name]
395
Stephane Barbariee0a4c792019-01-16 11:26:29 -0500396 if field != nil && field.IsContainer {
Stephane Barbarie3cb01222019-01-16 17:15:56 -0500397 children := make([]Revision, len(rev.GetChildren(name)))
398 copy(children, rev.GetChildren(name))
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500399
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400400 if field.Key != "" {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400401 if path != "" {
402 partition = strings.SplitN(path, "/", 2)
403 key := partition[0]
404 path = ""
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400405 keyValue := field.KeyFromStr(key)
npujar9a30c702019-11-14 17:06:39 +0530406 _, childRev := n.findRevByKey(children, field.Key, keyValue)
407 if childRev == nil {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400408 return nil
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400409 }
npujar9a30c702019-11-14 17:06:39 +0530410 childNode := childRev.getNode()
411 return childNode.getPath(ctx, childRev, path, depth)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400412 }
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400413 var response []interface{}
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500414 for _, childRev := range children {
npujar9a30c702019-11-14 17:06:39 +0530415 childNode := childRev.getNode()
npujar467fe752020-01-16 20:17:45 +0530416 value := childNode.getData(ctx, childRev, depth)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400417 response = append(response, value)
418 }
419 return response
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400420 }
npujar9a30c702019-11-14 17:06:39 +0530421 var response []interface{}
422 if path != "" {
423 // TODO: raise error
424 return response
425 }
426 for _, childRev := range children {
427 childNode := childRev.getNode()
npujar467fe752020-01-16 20:17:45 +0530428 value := childNode.getData(ctx, childRev, depth)
npujar9a30c702019-11-14 17:06:39 +0530429 response = append(response, value)
430 }
431 return response
432 } else if children := rev.GetChildren(name); children != nil {
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400433 childRev := children[0]
npujar9a30c702019-11-14 17:06:39 +0530434 childNode := childRev.getNode()
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400435 return childNode.getPath(ctx, childRev, path, depth)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400436 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500437
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400438 return nil
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400439}
440
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500441// getData retrieves the data from a node revision
npujar467fe752020-01-16 20:17:45 +0530442func (n *node) getData(ctx context.Context, rev Revision, depth int) interface{} {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500443 msg := rev.GetBranch().GetLatest().Get(depth)
Stephane Barbariea188d942018-10-16 16:43:04 -0400444 var modifiedMsg interface{}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400445
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500446 if n.GetProxy() != nil {
Stephane Barbarieaa467942019-02-06 14:09:44 -0500447 log.Debugw("invoking-get-callbacks", log.Fields{"data": msg})
npujar467fe752020-01-16 20:17:45 +0530448 if modifiedMsg = n.GetProxy().InvokeCallbacks(ctx, Get, false, msg); modifiedMsg != nil {
Stephane Barbariea188d942018-10-16 16:43:04 -0400449 msg = modifiedMsg
450 }
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400451
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400452 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500453
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400454 return msg
455}
456
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500457// Update changes the content of a node at the specified path with the provided data
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400458func (n *node) Update(ctx context.Context, path string, data interface{}, strict bool, txid string, makeBranch MakeBranchFunction) Revision {
Stephane Barbarie802aca42019-05-21 12:19:28 -0400459 n.mutex.Lock()
460 defer n.mutex.Unlock()
461
khenaidoob3244212019-08-27 14:32:27 -0400462 log.Debugw("node-update-request", log.Fields{"path": path, "strict": strict, "txid": txid})
Stephane Barbarieaa467942019-02-06 14:09:44 -0500463
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400464 for strings.HasPrefix(path, "/") {
465 path = path[1:]
466 }
467
468 var branch *Branch
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400469 if txid == "" {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500470 branch = n.GetBranch(NONE)
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500471 } else if branch = n.GetBranch(txid); branch == nil {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400472 branch = makeBranch(n)
473 }
474
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500475 if branch.GetLatest() != nil {
476 log.Debugf("Branch data : %+v, Passed data: %+v", branch.GetLatest().GetData(), data)
477 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400478 if path == "" {
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400479 return n.doUpdate(ctx, branch, data, strict)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400480 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400481
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500482 rev := branch.GetLatest()
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400483
484 partition := strings.SplitN(path, "/", 2)
485 name := partition[0]
486
487 if len(partition) < 2 {
488 path = ""
489 } else {
490 path = partition[1]
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400491 }
492
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400493 field := ChildrenFields(n.Type)[name]
494 var children []Revision
495
Stephane Barbarieaa467942019-02-06 14:09:44 -0500496 if field == nil {
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400497 return n.doUpdate(ctx, branch, data, strict)
Stephane Barbarieaa467942019-02-06 14:09:44 -0500498 }
499
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400500 if field.IsContainer {
501 if path == "" {
Stephane Barbarie126101e2018-10-11 16:18:48 -0400502 log.Errorf("cannot update a list")
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400503 } else if field.Key != "" {
504 partition := strings.SplitN(path, "/", 2)
505 key := partition[0]
506 if len(partition) < 2 {
507 path = ""
508 } else {
509 path = partition[1]
510 }
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400511 keyValue := field.KeyFromStr(key)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500512
Stephane Barbarie3cb01222019-01-16 17:15:56 -0500513 children = make([]Revision, len(rev.GetChildren(name)))
514 copy(children, rev.GetChildren(name))
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500515
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400516 idx, childRev := n.findRevByKey(children, field.Key, keyValue)
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400517
518 if childRev == nil {
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400519 log.Debugw("child-revision-is-nil", log.Fields{"key": keyValue})
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400520 return branch.GetLatest()
521 }
522
npujar9a30c702019-11-14 17:06:39 +0530523 childNode := childRev.getNode()
Stephane Barbariea188d942018-10-16 16:43:04 -0400524
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500525 // Save proxy in child node to ensure callbacks are called later on
Stephane Barbaried62ac4e2019-02-05 14:08:38 -0500526 // only assign in cases of non sub-folder proxies, i.e. "/"
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400527 if childNode.Proxy == nil && n.Proxy != nil && n.GetProxy().getFullPath() == "" {
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500528 childNode.Proxy = n.Proxy
529 }
530
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400531 newChildRev := childNode.Update(ctx, path, data, strict, txid, makeBranch)
Stephane Barbarie126101e2018-10-11 16:18:48 -0400532
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400533 if newChildRev.GetHash() == childRev.GetHash() {
534 if newChildRev != childRev {
535 log.Debug("clear-hash - %s %+v", newChildRev.GetHash(), newChildRev)
536 newChildRev.ClearHash()
537 }
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400538 log.Debugw("child-revisions-have-matching-hash", log.Fields{"hash": childRev.GetHash(), "key": keyValue})
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500539 return branch.GetLatest()
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400540 }
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400541
542 _, newKey := GetAttributeValue(newChildRev.GetData(), field.Key, 0)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500543
npujar9a30c702019-11-14 17:06:39 +0530544 _newKeyType := newKey.String()
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400545 _keyValueType := fmt.Sprintf("%s", keyValue)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500546
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400547 if _newKeyType != _keyValueType {
Stephane Barbarie126101e2018-10-11 16:18:48 -0400548 log.Errorf("cannot change key field")
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400549 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500550
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500551 // Prefix the hash value with the data type (e.g. devices, logical_devices, adapters)
Stephane Barbarief7fc1782019-03-28 22:33:41 -0400552 newChildRev.SetName(name + "/" + _keyValueType)
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400553
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400554 branch.LatestLock.Lock()
555 defer branch.LatestLock.Unlock()
556
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400557 if idx >= 0 {
558 children[idx] = newChildRev
559 } else {
560 children = append(children, newChildRev)
561 }
562
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400563 updatedRev := rev.UpdateChildren(ctx, name, children, branch)
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500564
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500565 n.makeLatest(branch, updatedRev, nil)
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400566 updatedRev.ChildDrop(name, childRev.GetHash())
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500567
Stephane Barbariea188d942018-10-16 16:43:04 -0400568 return newChildRev
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500569
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400570 } else {
Stephane Barbarie126101e2018-10-11 16:18:48 -0400571 log.Errorf("cannot index into container with no keys")
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400572 }
573 } else {
Stephane Barbarie3cb01222019-01-16 17:15:56 -0500574 childRev := rev.GetChildren(name)[0]
npujar9a30c702019-11-14 17:06:39 +0530575 childNode := childRev.getNode()
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400576 newChildRev := childNode.Update(ctx, path, data, strict, txid, makeBranch)
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400577
578 branch.LatestLock.Lock()
579 defer branch.LatestLock.Unlock()
580
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400581 updatedRev := rev.UpdateChildren(ctx, name, []Revision{newChildRev}, branch)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500582 n.makeLatest(branch, updatedRev, nil)
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500583
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400584 updatedRev.ChildDrop(name, childRev.GetHash())
585
Stephane Barbariea188d942018-10-16 16:43:04 -0400586 return newChildRev
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400587 }
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500588
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400589 return nil
590}
591
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400592func (n *node) doUpdate(ctx context.Context, branch *Branch, data interface{}, strict bool) Revision {
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400593 log.Debugw("comparing-types", log.Fields{"expected": reflect.ValueOf(n.Type).Type(), "actual": reflect.TypeOf(data)})
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400594
595 if reflect.TypeOf(data) != reflect.ValueOf(n.Type).Type() {
596 // TODO raise error
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400597 log.Errorw("types-do-not-match: %+v", log.Fields{"actual": reflect.TypeOf(data), "expected": n.Type})
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400598 return nil
599 }
600
601 // TODO: validate that this actually works
602 //if n.hasChildren(data) {
603 // return nil
604 //}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400605
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500606 if n.GetProxy() != nil {
npujar9a30c702019-11-14 17:06:39 +0530607 log.Debug("invoking proxy PreUpdate Callbacks")
npujar467fe752020-01-16 20:17:45 +0530608 n.GetProxy().InvokeCallbacks(ctx, PreUpdate, false, branch.GetLatest(), data)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400609 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500610
611 if branch.GetLatest().GetData().(proto.Message).String() != data.(proto.Message).String() {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400612 if strict {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400613 // TODO: checkAccessViolations(data, Branch.GetLatest.data)
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -0400614 log.Debugf("checking access violations")
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400615 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500616
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400617 rev := branch.GetLatest().UpdateData(ctx, data, branch)
npujar9a30c702019-11-14 17:06:39 +0530618 changes := []ChangeTuple{{PostUpdate, branch.GetLatest().GetData(), rev.GetData()}}
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500619 n.makeLatest(branch, rev, changes)
620
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400621 return rev
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400622 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500623 return branch.GetLatest()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400624}
625
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500626// Add inserts a new node at the specified path with the provided data
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400627func (n *node) Add(ctx context.Context, path string, data interface{}, txid string, makeBranch MakeBranchFunction) Revision {
Stephane Barbarie802aca42019-05-21 12:19:28 -0400628 n.mutex.Lock()
629 defer n.mutex.Unlock()
630
khenaidoob3244212019-08-27 14:32:27 -0400631 log.Debugw("node-add-request", log.Fields{"path": path, "txid": txid})
Stephane Barbarieaa467942019-02-06 14:09:44 -0500632
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400633 for strings.HasPrefix(path, "/") {
634 path = path[1:]
635 }
636 if path == "" {
637 // TODO raise error
Stephane Barbarie126101e2018-10-11 16:18:48 -0400638 log.Errorf("cannot add for non-container mode")
Stephane Barbarieaa467942019-02-06 14:09:44 -0500639 return nil
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400640 }
641
642 var branch *Branch
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400643 if txid == "" {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500644 branch = n.GetBranch(NONE)
645 } else if branch = n.GetBranch(txid); branch == nil {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400646 branch = makeBranch(n)
647 }
648
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500649 rev := branch.GetLatest()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400650
651 partition := strings.SplitN(path, "/", 2)
652 name := partition[0]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400653
654 if len(partition) < 2 {
655 path = ""
656 } else {
657 path = partition[1]
658 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400659
660 field := ChildrenFields(n.Type)[name]
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500661
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400662 var children []Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400663
664 if field.IsContainer {
665 if path == "" {
666 if field.Key != "" {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500667 if n.GetProxy() != nil {
npujar9a30c702019-11-14 17:06:39 +0530668 log.Debug("invoking proxy PreAdd Callbacks")
npujar467fe752020-01-16 20:17:45 +0530669 n.GetProxy().InvokeCallbacks(ctx, PreAdd, false, data)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400670 }
671
Stephane Barbarie3cb01222019-01-16 17:15:56 -0500672 children = make([]Revision, len(rev.GetChildren(name)))
673 copy(children, rev.GetChildren(name))
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500674
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400675 _, key := GetAttributeValue(data, field.Key, 0)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500676
Stephane Barbarie126101e2018-10-11 16:18:48 -0400677 if _, exists := n.findRevByKey(children, field.Key, key.String()); exists != nil {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400678 // TODO raise error
Stephane Barbarie260a5632019-02-26 16:12:49 -0500679 log.Warnw("duplicate-key-found", log.Fields{"key": key.String()})
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500680 return exists
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400681 }
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500682 childRev := n.MakeNode(data, "").Latest()
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500683
684 // Prefix the hash with the data type (e.g. devices, logical_devices, adapters)
Stephane Barbarief7fc1782019-03-28 22:33:41 -0400685 childRev.SetName(name + "/" + key.String())
Stephane Barbariee0a4c792019-01-16 11:26:29 -0500686
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400687 branch.LatestLock.Lock()
688 defer branch.LatestLock.Unlock()
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500689
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500690 children = append(children, childRev)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400691
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400692 updatedRev := rev.UpdateChildren(ctx, name, children, branch)
npujar9a30c702019-11-14 17:06:39 +0530693 changes := []ChangeTuple{{PostAdd, nil, childRev.GetData()}}
npujar467fe752020-01-16 20:17:45 +0530694 childRev.SetupWatch(ctx, childRev.GetName())
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400695
696 n.makeLatest(branch, updatedRev, changes)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500697
698 return childRev
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400699 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500700 log.Errorf("cannot add to non-keyed container")
701
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400702 } else if field.Key != "" {
703 partition := strings.SplitN(path, "/", 2)
704 key := partition[0]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400705 if len(partition) < 2 {
706 path = ""
707 } else {
708 path = partition[1]
709 }
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400710 keyValue := field.KeyFromStr(key)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500711
Stephane Barbarie3cb01222019-01-16 17:15:56 -0500712 children = make([]Revision, len(rev.GetChildren(name)))
713 copy(children, rev.GetChildren(name))
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500714
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400715 idx, childRev := n.findRevByKey(children, field.Key, keyValue)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500716
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400717 if childRev == nil {
718 return branch.GetLatest()
719 }
720
npujar9a30c702019-11-14 17:06:39 +0530721 childNode := childRev.getNode()
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400722 newChildRev := childNode.Add(ctx, path, data, txid, makeBranch)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500723
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500724 // Prefix the hash with the data type (e.g. devices, logical_devices, adapters)
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400725 newChildRev.SetName(name + "/" + keyValue.(string))
726
727 branch.LatestLock.Lock()
728 defer branch.LatestLock.Unlock()
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500729
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400730 if idx >= 0 {
731 children[idx] = newChildRev
732 } else {
733 children = append(children, newChildRev)
734 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500735
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400736 updatedRev := rev.UpdateChildren(ctx, name, children, branch)
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400737 n.makeLatest(branch, updatedRev, nil)
738
739 updatedRev.ChildDrop(name, childRev.GetHash())
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500740
741 return newChildRev
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400742 } else {
Stephane Barbarie126101e2018-10-11 16:18:48 -0400743 log.Errorf("cannot add to non-keyed container")
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400744 }
745 } else {
Stephane Barbarie126101e2018-10-11 16:18:48 -0400746 log.Errorf("cannot add to non-container field")
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400747 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500748
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400749 return nil
750}
751
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500752// Remove eliminates a node at the specified path
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400753func (n *node) Remove(ctx context.Context, path string, txid string, makeBranch MakeBranchFunction) Revision {
Stephane Barbarie802aca42019-05-21 12:19:28 -0400754 n.mutex.Lock()
755 defer n.mutex.Unlock()
756
Stephane Barbarie11b88e72019-02-07 12:28:29 -0500757 log.Debugw("node-remove-request", log.Fields{"path": path, "txid": txid, "makeBranch": makeBranch})
Stephane Barbarieaa467942019-02-06 14:09:44 -0500758
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400759 for strings.HasPrefix(path, "/") {
760 path = path[1:]
761 }
762 if path == "" {
763 // TODO raise error
Stephane Barbarie126101e2018-10-11 16:18:48 -0400764 log.Errorf("cannot remove for non-container mode")
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400765 }
766 var branch *Branch
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400767 if txid == "" {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500768 branch = n.GetBranch(NONE)
769 } else if branch = n.GetBranch(txid); branch == nil {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400770 branch = makeBranch(n)
771 }
772
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500773 rev := branch.GetLatest()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400774
775 partition := strings.SplitN(path, "/", 2)
776 name := partition[0]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400777 if len(partition) < 2 {
778 path = ""
779 } else {
780 path = partition[1]
781 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400782
783 field := ChildrenFields(n.Type)[name]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400784 var children []Revision
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400785 postAnnouncement := []ChangeTuple{}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400786
787 if field.IsContainer {
788 if path == "" {
Stephane Barbarieb0c79892019-02-13 11:29:59 -0500789 log.Errorw("cannot-remove-without-key", log.Fields{"name": name, "key": path})
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400790 } else if field.Key != "" {
791 partition := strings.SplitN(path, "/", 2)
792 key := partition[0]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400793 if len(partition) < 2 {
794 path = ""
795 } else {
796 path = partition[1]
797 }
Stephane Barbarieb0c79892019-02-13 11:29:59 -0500798
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400799 keyValue := field.KeyFromStr(key)
Stephane Barbarie3cb01222019-01-16 17:15:56 -0500800 children = make([]Revision, len(rev.GetChildren(name)))
801 copy(children, rev.GetChildren(name))
Stephane Barbarieb0c79892019-02-13 11:29:59 -0500802
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400803 if path != "" {
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400804 if idx, childRev := n.findRevByKey(children, field.Key, keyValue); childRev != nil {
npujar9a30c702019-11-14 17:06:39 +0530805 childNode := childRev.getNode()
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400806 if childNode.Proxy == nil {
807 childNode.Proxy = n.Proxy
808 }
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400809 newChildRev := childNode.Remove(ctx, path, txid, makeBranch)
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400810
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400811 branch.LatestLock.Lock()
812 defer branch.LatestLock.Unlock()
813
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400814 if idx >= 0 {
815 children[idx] = newChildRev
816 } else {
817 children = append(children, newChildRev)
818 }
819
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400820 rev.SetChildren(name, children)
821 branch.GetLatest().Drop(txid, false)
822 n.makeLatest(branch, rev, nil)
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500823 }
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400824 return branch.GetLatest()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400825 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500826
npujar9a30c702019-11-14 17:06:39 +0530827 idx, childRev := n.findRevByKey(children, field.Key, keyValue)
828 if childRev != nil && idx >= 0 {
Stephane Barbarieb0c79892019-02-13 11:29:59 -0500829 if n.GetProxy() != nil {
830 data := childRev.GetData()
npujar467fe752020-01-16 20:17:45 +0530831 n.GetProxy().InvokeCallbacks(ctx, PreRemove, false, data)
npujar9a30c702019-11-14 17:06:39 +0530832 postAnnouncement = append(postAnnouncement, ChangeTuple{PostRemove, data, nil})
Stephane Barbarieb0c79892019-02-13 11:29:59 -0500833 } else {
npujar9a30c702019-11-14 17:06:39 +0530834 postAnnouncement = append(postAnnouncement, ChangeTuple{PostRemove, childRev.GetData(), nil})
Stephane Barbarieb0c79892019-02-13 11:29:59 -0500835 }
836
npujar467fe752020-01-16 20:17:45 +0530837 childRev.StorageDrop(ctx, txid, true)
npujar9a30c702019-11-14 17:06:39 +0530838 getRevCache().Delete(childRev.GetName())
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400839
840 branch.LatestLock.Lock()
841 defer branch.LatestLock.Unlock()
842
Stephane Barbarieb0c79892019-02-13 11:29:59 -0500843 children = append(children[:idx], children[idx+1:]...)
844 rev.SetChildren(name, children)
845
846 branch.GetLatest().Drop(txid, false)
847 n.makeLatest(branch, rev, postAnnouncement)
848
849 return rev
Stephane Barbarieb0c79892019-02-13 11:29:59 -0500850 }
npujar9a30c702019-11-14 17:06:39 +0530851 log.Errorw("failed-to-find-revision", log.Fields{"name": name, "key": keyValue.(string)})
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400852 }
Stephane Barbarieb0c79892019-02-13 11:29:59 -0500853 log.Errorw("cannot-add-to-non-keyed-container", log.Fields{"name": name, "path": path, "fieldKey": field.Key})
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500854
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400855 } else {
Stephane Barbarieb0c79892019-02-13 11:29:59 -0500856 log.Errorw("cannot-add-to-non-container-field", log.Fields{"name": name, "path": path})
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400857 }
858
859 return nil
860}
861
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400862// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Branching ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
863
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500864// MakeBranchFunction is a type for function references intented to create a branch
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400865type MakeBranchFunction func(*node) *Branch
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400866
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500867// MakeBranch creates a new branch for the provided transaction id
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400868func (n *node) MakeBranch(txid string) *Branch {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500869 branchPoint := n.GetBranch(NONE).GetLatest()
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400870 branch := NewBranch(n, txid, branchPoint, true)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500871 n.SetBranch(txid, branch)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400872 return branch
873}
874
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500875// DeleteBranch removes a branch with the specified id
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400876func (n *node) DeleteBranch(txid string) {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400877 delete(n.Branches, txid)
878}
879
npujar467fe752020-01-16 20:17:45 +0530880func (n *node) mergeChild(ctx context.Context, txid string, dryRun bool) func(Revision) Revision {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400881 f := func(rev Revision) Revision {
882 childBranch := rev.GetBranch()
883
884 if childBranch.Txid == txid {
npujar467fe752020-01-16 20:17:45 +0530885 rev, _ = childBranch.Node.MergeBranch(ctx, txid, dryRun)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400886 }
887
888 return rev
889 }
890 return f
891}
892
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500893// MergeBranch will integrate the contents of a transaction branch within the latest branch of a given node
npujar467fe752020-01-16 20:17:45 +0530894func (n *node) MergeBranch(ctx context.Context, txid string, dryRun bool) (Revision, error) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500895 srcBranch := n.GetBranch(txid)
896 dstBranch := n.GetBranch(NONE)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400897
898 forkRev := srcBranch.Origin
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500899 srcRev := srcBranch.GetLatest()
900 dstRev := dstBranch.GetLatest()
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400901
npujar467fe752020-01-16 20:17:45 +0530902 rev, changes := Merge3Way(ctx, forkRev, srcRev, dstRev, n.mergeChild(ctx, txid, dryRun), dryRun)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400903
904 if !dryRun {
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500905 if rev != nil {
Stephane Barbarief7fc1782019-03-28 22:33:41 -0400906 rev.SetName(dstRev.GetName())
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500907 n.makeLatest(dstBranch, rev, changes)
908 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500909 n.DeleteBranch(txid)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400910 }
911
Stephane Barbariee16186c2018-09-11 10:46:34 -0400912 // TODO: return proper error when one occurs
913 return rev, nil
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400914}
915
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500916// CreateProxy returns a reference to a sub-tree of the data model
Thomas Lee Se5a44012019-11-07 20:32:24 +0530917func (n *node) CreateProxy(ctx context.Context, path string, exclusive bool) (*Proxy, error) {
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400918 return n.createProxy(ctx, path, path, n, exclusive)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400919}
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500920
Thomas Lee Se5a44012019-11-07 20:32:24 +0530921func (n *node) createProxy(ctx context.Context, path string, fullPath string, parentNode *node, exclusive bool) (*Proxy, error) {
Stephane Barbariec92d1072019-06-07 16:21:49 -0400922 log.Debugw("node-create-proxy", log.Fields{
923 "node-type": reflect.ValueOf(n.Type).Type(),
924 "parent-node-type": reflect.ValueOf(parentNode.Type).Type(),
925 "path": path,
926 "fullPath": fullPath,
927 })
928
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400929 for strings.HasPrefix(path, "/") {
930 path = path[1:]
931 }
932 if path == "" {
Thomas Lee Se5a44012019-11-07 20:32:24 +0530933 return n.makeProxy(path, fullPath, parentNode, exclusive), nil
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400934 }
935
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500936 rev := n.GetBranch(NONE).GetLatest()
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400937 partition := strings.SplitN(path, "/", 2)
938 name := partition[0]
Stephane Barbariec92d1072019-06-07 16:21:49 -0400939 var nodeType interface{}
Stephane Barbarie126101e2018-10-11 16:18:48 -0400940 if len(partition) < 2 {
941 path = ""
Stephane Barbariec92d1072019-06-07 16:21:49 -0400942 nodeType = n.Type
Stephane Barbarie126101e2018-10-11 16:18:48 -0400943 } else {
944 path = partition[1]
Stephane Barbariec92d1072019-06-07 16:21:49 -0400945 nodeType = parentNode.Type
Stephane Barbarie126101e2018-10-11 16:18:48 -0400946 }
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400947
Stephane Barbariec92d1072019-06-07 16:21:49 -0400948 field := ChildrenFields(nodeType)[name]
949
950 if field != nil {
951 if field.IsContainer {
952 log.Debugw("container-field", log.Fields{
953 "node-type": reflect.ValueOf(n.Type).Type(),
954 "parent-node-type": reflect.ValueOf(parentNode.Type).Type(),
955 "path": path,
956 "name": name,
957 })
958 if path == "" {
959 log.Debugw("folder-proxy", log.Fields{
960 "node-type": reflect.ValueOf(n.Type).Type(),
961 "parent-node-type": reflect.ValueOf(parentNode.Type).Type(),
962 "fullPath": fullPath,
963 "name": name,
964 })
965 newNode := n.MakeNode(reflect.New(field.ClassType.Elem()).Interface(), "")
Thomas Lee Se5a44012019-11-07 20:32:24 +0530966 return newNode.makeProxy(path, fullPath, parentNode, exclusive), nil
Stephane Barbariec92d1072019-06-07 16:21:49 -0400967 } else if field.Key != "" {
968 log.Debugw("key-proxy", log.Fields{
969 "node-type": reflect.ValueOf(n.Type).Type(),
970 "parent-node-type": reflect.ValueOf(parentNode.Type).Type(),
971 "fullPath": fullPath,
972 "name": name,
973 })
974 partition := strings.SplitN(path, "/", 2)
975 key := partition[0]
976 if len(partition) < 2 {
977 path = ""
978 } else {
979 path = partition[1]
980 }
981 keyValue := field.KeyFromStr(key)
npujar9a30c702019-11-14 17:06:39 +0530982 children := make([]Revision, len(rev.GetChildren(name)))
Stephane Barbariec92d1072019-06-07 16:21:49 -0400983 copy(children, rev.GetChildren(name))
984
Stephane Barbariec92d1072019-06-07 16:21:49 -0400985 var childRev Revision
986 if _, childRev = n.findRevByKey(children, field.Key, keyValue); childRev != nil {
987 log.Debugw("found-revision-matching-key-in-memory", log.Fields{
988 "node-type": reflect.ValueOf(n.Type).Type(),
989 "parent-node-type": reflect.ValueOf(parentNode.Type).Type(),
990 "fullPath": fullPath,
991 "name": name,
992 })
Thomas Lee Se5a44012019-11-07 20:32:24 +0530993 } else if revs, err := n.GetBranch(NONE).GetLatest().LoadFromPersistence(ctx, fullPath, "", nil); err != nil {
994 log.Errorf("failed-to-load-from-persistence")
995 return nil, err
npujar9a30c702019-11-14 17:06:39 +0530996 } else if len(revs) > 0 {
Stephane Barbariec92d1072019-06-07 16:21:49 -0400997 log.Debugw("found-revision-matching-key-in-db", log.Fields{
998 "node-type": reflect.ValueOf(n.Type).Type(),
999 "parent-node-type": reflect.ValueOf(parentNode.Type).Type(),
1000 "fullPath": fullPath,
1001 "name": name,
1002 })
1003 childRev = revs[0]
1004 } else {
1005 log.Debugw("no-revision-matching-key", log.Fields{
1006 "node-type": reflect.ValueOf(n.Type).Type(),
1007 "parent-node-type": reflect.ValueOf(parentNode.Type).Type(),
1008 "fullPath": fullPath,
1009 "name": name,
1010 })
1011 }
1012 if childRev != nil {
npujar9a30c702019-11-14 17:06:39 +05301013 childNode := childRev.getNode()
Stephane Barbarieef6650d2019-07-18 12:15:09 -04001014 return childNode.createProxy(ctx, path, fullPath, n, exclusive)
Stephane Barbariec92d1072019-06-07 16:21:49 -04001015 }
Stephane Barbarie06c4a742018-10-01 11:09:32 -04001016 } else {
Stephane Barbariec92d1072019-06-07 16:21:49 -04001017 log.Errorw("cannot-access-index-of-empty-container", log.Fields{
1018 "node-type": reflect.ValueOf(n.Type).Type(),
1019 "parent-node-type": reflect.ValueOf(parentNode.Type).Type(),
1020 "path": path,
1021 "name": name,
1022 })
Stephane Barbarie933b09b2019-01-09 11:12:09 -05001023 }
Stephane Barbarie126101e2018-10-11 16:18:48 -04001024 } else {
Stephane Barbariec92d1072019-06-07 16:21:49 -04001025 log.Debugw("non-container-field", log.Fields{
1026 "node-type": reflect.ValueOf(n.Type).Type(),
1027 "parent-node-type": reflect.ValueOf(parentNode.Type).Type(),
1028 "path": path,
1029 "name": name,
1030 })
1031 childRev := rev.GetChildren(name)[0]
npujar9a30c702019-11-14 17:06:39 +05301032 childNode := childRev.getNode()
Stephane Barbarieef6650d2019-07-18 12:15:09 -04001033 return childNode.createProxy(ctx, path, fullPath, n, exclusive)
Stephane Barbarieec0919b2018-09-05 14:14:29 -04001034 }
Stephane Barbarieec0919b2018-09-05 14:14:29 -04001035 } else {
Stephane Barbariec92d1072019-06-07 16:21:49 -04001036 log.Debugw("field-object-is-nil", log.Fields{
1037 "node-type": reflect.ValueOf(n.Type).Type(),
1038 "parent-node-type": reflect.ValueOf(parentNode.Type).Type(),
1039 "fullPath": fullPath,
1040 "name": name,
1041 })
Stephane Barbarieec0919b2018-09-05 14:14:29 -04001042 }
1043
Stephane Barbariec92d1072019-06-07 16:21:49 -04001044 log.Warnw("cannot-create-proxy", log.Fields{
1045 "node-type": reflect.ValueOf(n.Type).Type(),
1046 "parent-node-type": reflect.ValueOf(parentNode.Type).Type(),
1047 "path": path,
1048 "fullPath": fullPath,
1049 "latest-rev": rev.GetHash(),
1050 })
Thomas Lee Se5a44012019-11-07 20:32:24 +05301051 return nil, nil
Stephane Barbarieec0919b2018-09-05 14:14:29 -04001052}
1053
Stephane Barbarie1ab43272018-12-08 21:42:13 -05001054func (n *node) makeProxy(path string, fullPath string, parentNode *node, exclusive bool) *Proxy {
Stephane Barbariec92d1072019-06-07 16:21:49 -04001055 log.Debugw("node-make-proxy", log.Fields{
1056 "node-type": reflect.ValueOf(n.Type).Type(),
1057 "parent-node-type": reflect.ValueOf(parentNode.Type).Type(),
1058 "path": path,
1059 "fullPath": fullPath,
1060 })
1061
Stephane Barbarie126101e2018-10-11 16:18:48 -04001062 r := &root{
1063 node: n,
Stephane Barbariedc5022d2018-11-19 15:21:44 -05001064 Callbacks: n.Root.GetCallbacks(),
1065 NotificationCallbacks: n.Root.GetNotificationCallbacks(),
Stephane Barbarie126101e2018-10-11 16:18:48 -04001066 DirtyNodes: n.Root.DirtyNodes,
1067 KvStore: n.Root.KvStore,
1068 Loading: n.Root.Loading,
1069 RevisionClass: n.Root.RevisionClass,
1070 }
1071
Stephane Barbarieec0919b2018-09-05 14:14:29 -04001072 if n.Proxy == nil {
Stephane Barbariec92d1072019-06-07 16:21:49 -04001073 log.Debugw("constructing-new-proxy", log.Fields{
1074 "node-type": reflect.ValueOf(n.Type).Type(),
1075 "parent-node-type": reflect.ValueOf(parentNode.Type).Type(),
1076 "path": path,
1077 "fullPath": fullPath,
1078 })
Stephane Barbarie1ab43272018-12-08 21:42:13 -05001079 n.Proxy = NewProxy(r, n, parentNode, path, fullPath, exclusive)
Stephane Barbarieec0919b2018-09-05 14:14:29 -04001080 } else {
Stephane Barbariec92d1072019-06-07 16:21:49 -04001081 log.Debugw("node-has-existing-proxy", log.Fields{
Stephane Barbarieef6650d2019-07-18 12:15:09 -04001082 "node-type": reflect.ValueOf(n.GetProxy().Node.Type).Type(),
1083 "parent-node-type": reflect.ValueOf(n.GetProxy().ParentNode.Type).Type(),
1084 "path": n.GetProxy().Path,
1085 "fullPath": n.GetProxy().FullPath,
Stephane Barbariec92d1072019-06-07 16:21:49 -04001086 })
Stephane Barbarieef6650d2019-07-18 12:15:09 -04001087 if n.GetProxy().Exclusive {
Stephane Barbarieec0919b2018-09-05 14:14:29 -04001088 log.Error("node is already owned exclusively")
1089 }
1090 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -05001091
Stephane Barbarieec0919b2018-09-05 14:14:29 -04001092 return n.Proxy
1093}
1094
Stephane Barbariedc5022d2018-11-19 15:21:44 -05001095func (n *node) SetProxy(proxy *Proxy) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -05001096 n.Proxy = proxy
1097}
1098
1099func (n *node) GetProxy() *Proxy {
Stephane Barbariedc5022d2018-11-19 15:21:44 -05001100 return n.Proxy
1101}
1102
1103func (n *node) GetBranch(key string) *Branch {
Stephane Barbarie1ab43272018-12-08 21:42:13 -05001104 if n.Branches != nil {
1105 if branch, exists := n.Branches[key]; exists {
1106 return branch
1107 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -05001108 }
1109 return nil
1110}
1111
1112func (n *node) SetBranch(key string, branch *Branch) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -05001113 n.Branches[key] = branch
1114}
1115
1116func (n *node) GetRoot() *root {
Stephane Barbariedc5022d2018-11-19 15:21:44 -05001117 return n.Root
Stephane Barbarie06c4a742018-10-01 11:09:32 -04001118}
Stephane Barbarieef6650d2019-07-18 12:15:09 -04001119func (n *node) SetRoot(root *root) {
1120 n.Root = root
1121}