blob: 947cfc793ebe883e947d5f339639d22c36107549 [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
Girish Kumarf56a4682020-03-20 20:07:46 +0000102 logger.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() != "" {
Girish Kumarf56a4682020-03-20 20:07:46 +0000131 logger.Debugw("saving-latest-data", log.Fields{"hash": revision.GetHash(), "data": revision.GetData()})
Stephane Barbarie802aca42019-05-21 12:19:28 -0400132 // 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 {
Girish Kumarf56a4682020-03-20 20:07:46 +0000147 logger.Debugw("adding-callback",
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400148 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() {
Girish Kumarf56a4682020-03-20 20:07:46 +0000200 // //logger.Errorf("duplicate key - %s", k)
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -0500201 // }
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 {
Girish Kumarf56a4682020-03-20 20:07:46 +0000220 logger.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
Girish Kumarf56a4682020-03-20 20:07:46 +0000258 logger.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 {
Girish Kumarf56a4682020-03-20 20:07:46 +0000282 logger.Errorf("failed-to-load-from-persistence")
Thomas Lee Se5a44012019-11-07 20:32:24 +0530283 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
Girish Kumarf56a4682020-03-20 20:07:46 +0000299 logger.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 {
Girish Kumarf56a4682020-03-20 20:07:46 +0000329 logger.Debugw("using-cache-entry", log.Fields{
Stephane Barbariec92d1072019-06-07 16:21:49 -0400330 "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 }
Girish Kumarf56a4682020-03-20 20:07:46 +0000336 logger.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() {
Girish Kumarf56a4682020-03-20 20:07:46 +0000338 logger.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 {
Girish Kumarf56a4682020-03-20 20:07:46 +0000341 logger.Debugw("not-using-cache-entry", log.Fields{
Stephane Barbarie802aca42019-05-21 12:19:28 -0400342 "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 {
Girish Kumarf56a4682020-03-20 20:07:46 +0000349 logger.Debugw("reconcile-requested", log.Fields{
Stephane Barbarie802aca42019-05-21 12:19:28 -0400350 "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 {
Girish Kumarf56a4682020-03-20 20:07:46 +0000360 logger.Errorf("failed-to-load-from-persistence")
Thomas Lee Se5a44012019-11-07 20:32:24 +0530361 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 {
Girish Kumarf56a4682020-03-20 20:07:46 +0000447 logger.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
Girish Kumarf56a4682020-03-20 20:07:46 +0000462 logger.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 {
Girish Kumarf56a4682020-03-20 20:07:46 +0000476 logger.Debugf("Branch data : %+v, Passed data: %+v", branch.GetLatest().GetData(), data)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500477 }
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 == "" {
Girish Kumarf56a4682020-03-20 20:07:46 +0000502 logger.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 {
Girish Kumarf56a4682020-03-20 20:07:46 +0000519 logger.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 {
Girish Kumarf56a4682020-03-20 20:07:46 +0000535 logger.Debug("clear-hash - %s %+v", newChildRev.GetHash(), newChildRev)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400536 newChildRev.ClearHash()
537 }
Girish Kumarf56a4682020-03-20 20:07:46 +0000538 logger.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 {
Girish Kumarf56a4682020-03-20 20:07:46 +0000548 logger.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 {
Girish Kumarf56a4682020-03-20 20:07:46 +0000571 logger.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 {
Girish Kumarf56a4682020-03-20 20:07:46 +0000593 logger.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
Girish Kumarf56a4682020-03-20 20:07:46 +0000597 logger.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
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500601 if n.GetProxy() != nil {
Girish Kumarf56a4682020-03-20 20:07:46 +0000602 logger.Debug("invoking proxy PreUpdate Callbacks")
npujar467fe752020-01-16 20:17:45 +0530603 n.GetProxy().InvokeCallbacks(ctx, PreUpdate, false, branch.GetLatest(), data)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400604 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500605
khenaidoo67b22152020-03-02 16:01:25 -0500606 if strict {
607 // TODO: checkAccessViolations(data, Branch.GetLatest.data)
Girish Kumarf56a4682020-03-20 20:07:46 +0000608 logger.Warn("access-violations-not-supported")
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400609 }
khenaidoo67b22152020-03-02 16:01:25 -0500610
611 // The way the model is used, this function is only invoked upon data change. Therefore, to also
612 // avoid a deep proto.message comparison (expensive), just create a new branch regardless
613 rev := branch.GetLatest().UpdateData(ctx, data, branch)
614 changes := []ChangeTuple{{PostUpdate, branch.GetLatest().GetData(), rev.GetData()}}
615 n.makeLatest(branch, rev, changes)
616
617 return rev
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400618}
619
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500620// Add inserts a new node at the specified path with the provided data
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400621func (n *node) Add(ctx context.Context, path string, data interface{}, txid string, makeBranch MakeBranchFunction) Revision {
Stephane Barbarie802aca42019-05-21 12:19:28 -0400622 n.mutex.Lock()
623 defer n.mutex.Unlock()
624
Girish Kumarf56a4682020-03-20 20:07:46 +0000625 logger.Debugw("node-add-request", log.Fields{"path": path, "txid": txid})
Stephane Barbarieaa467942019-02-06 14:09:44 -0500626
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400627 for strings.HasPrefix(path, "/") {
628 path = path[1:]
629 }
630 if path == "" {
631 // TODO raise error
Girish Kumarf56a4682020-03-20 20:07:46 +0000632 logger.Errorf("cannot add for non-container mode")
Stephane Barbarieaa467942019-02-06 14:09:44 -0500633 return nil
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400634 }
635
636 var branch *Branch
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400637 if txid == "" {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500638 branch = n.GetBranch(NONE)
639 } else if branch = n.GetBranch(txid); branch == nil {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400640 branch = makeBranch(n)
641 }
642
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500643 rev := branch.GetLatest()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400644
645 partition := strings.SplitN(path, "/", 2)
646 name := partition[0]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400647
648 if len(partition) < 2 {
649 path = ""
650 } else {
651 path = partition[1]
652 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400653
654 field := ChildrenFields(n.Type)[name]
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500655
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400656 var children []Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400657
658 if field.IsContainer {
659 if path == "" {
660 if field.Key != "" {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500661 if n.GetProxy() != nil {
Girish Kumarf56a4682020-03-20 20:07:46 +0000662 logger.Debug("invoking proxy PreAdd Callbacks")
npujar467fe752020-01-16 20:17:45 +0530663 n.GetProxy().InvokeCallbacks(ctx, PreAdd, false, data)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400664 }
665
Stephane Barbarie3cb01222019-01-16 17:15:56 -0500666 children = make([]Revision, len(rev.GetChildren(name)))
667 copy(children, rev.GetChildren(name))
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500668
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400669 _, key := GetAttributeValue(data, field.Key, 0)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500670
Stephane Barbarie126101e2018-10-11 16:18:48 -0400671 if _, exists := n.findRevByKey(children, field.Key, key.String()); exists != nil {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400672 // TODO raise error
Girish Kumarf56a4682020-03-20 20:07:46 +0000673 logger.Warnw("duplicate-key-found", log.Fields{"key": key.String()})
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500674 return exists
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400675 }
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500676 childRev := n.MakeNode(data, "").Latest()
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500677
678 // Prefix the hash with the data type (e.g. devices, logical_devices, adapters)
Stephane Barbarief7fc1782019-03-28 22:33:41 -0400679 childRev.SetName(name + "/" + key.String())
Stephane Barbariee0a4c792019-01-16 11:26:29 -0500680
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400681 branch.LatestLock.Lock()
682 defer branch.LatestLock.Unlock()
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500683
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500684 children = append(children, childRev)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400685
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400686 updatedRev := rev.UpdateChildren(ctx, name, children, branch)
npujar9a30c702019-11-14 17:06:39 +0530687 changes := []ChangeTuple{{PostAdd, nil, childRev.GetData()}}
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400688
689 n.makeLatest(branch, updatedRev, changes)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500690
691 return childRev
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400692 }
Girish Kumarf56a4682020-03-20 20:07:46 +0000693 logger.Errorf("cannot add to non-keyed container")
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500694
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400695 } else if field.Key != "" {
696 partition := strings.SplitN(path, "/", 2)
697 key := partition[0]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400698 if len(partition) < 2 {
699 path = ""
700 } else {
701 path = partition[1]
702 }
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400703 keyValue := field.KeyFromStr(key)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500704
Stephane Barbarie3cb01222019-01-16 17:15:56 -0500705 children = make([]Revision, len(rev.GetChildren(name)))
706 copy(children, rev.GetChildren(name))
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500707
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400708 idx, childRev := n.findRevByKey(children, field.Key, keyValue)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500709
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400710 if childRev == nil {
711 return branch.GetLatest()
712 }
713
npujar9a30c702019-11-14 17:06:39 +0530714 childNode := childRev.getNode()
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400715 newChildRev := childNode.Add(ctx, path, data, txid, makeBranch)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500716
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500717 // Prefix the hash with the data type (e.g. devices, logical_devices, adapters)
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400718 newChildRev.SetName(name + "/" + keyValue.(string))
719
720 branch.LatestLock.Lock()
721 defer branch.LatestLock.Unlock()
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500722
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400723 if idx >= 0 {
724 children[idx] = newChildRev
725 } else {
726 children = append(children, newChildRev)
727 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500728
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400729 updatedRev := rev.UpdateChildren(ctx, name, children, branch)
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400730 n.makeLatest(branch, updatedRev, nil)
731
732 updatedRev.ChildDrop(name, childRev.GetHash())
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500733
734 return newChildRev
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400735 } else {
Girish Kumarf56a4682020-03-20 20:07:46 +0000736 logger.Errorf("cannot add to non-keyed container")
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400737 }
738 } else {
Girish Kumarf56a4682020-03-20 20:07:46 +0000739 logger.Errorf("cannot add to non-container field")
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400740 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500741
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400742 return nil
743}
744
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500745// Remove eliminates a node at the specified path
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400746func (n *node) Remove(ctx context.Context, path string, txid string, makeBranch MakeBranchFunction) Revision {
Stephane Barbarie802aca42019-05-21 12:19:28 -0400747 n.mutex.Lock()
748 defer n.mutex.Unlock()
749
Girish Kumarf56a4682020-03-20 20:07:46 +0000750 logger.Debugw("node-remove-request", log.Fields{"path": path, "txid": txid, "makeBranch": makeBranch})
Stephane Barbarieaa467942019-02-06 14:09:44 -0500751
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400752 for strings.HasPrefix(path, "/") {
753 path = path[1:]
754 }
755 if path == "" {
756 // TODO raise error
Girish Kumarf56a4682020-03-20 20:07:46 +0000757 logger.Errorf("cannot remove for non-container mode")
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400758 }
759 var branch *Branch
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400760 if txid == "" {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500761 branch = n.GetBranch(NONE)
762 } else if branch = n.GetBranch(txid); branch == nil {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400763 branch = makeBranch(n)
764 }
765
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500766 rev := branch.GetLatest()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400767
768 partition := strings.SplitN(path, "/", 2)
769 name := partition[0]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400770 if len(partition) < 2 {
771 path = ""
772 } else {
773 path = partition[1]
774 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400775
776 field := ChildrenFields(n.Type)[name]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400777 var children []Revision
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400778 postAnnouncement := []ChangeTuple{}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400779
780 if field.IsContainer {
781 if path == "" {
Girish Kumarf56a4682020-03-20 20:07:46 +0000782 logger.Errorw("cannot-remove-without-key", log.Fields{"name": name, "key": path})
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400783 } else if field.Key != "" {
784 partition := strings.SplitN(path, "/", 2)
785 key := partition[0]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400786 if len(partition) < 2 {
787 path = ""
788 } else {
789 path = partition[1]
790 }
Stephane Barbarieb0c79892019-02-13 11:29:59 -0500791
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400792 keyValue := field.KeyFromStr(key)
Stephane Barbarie3cb01222019-01-16 17:15:56 -0500793 children = make([]Revision, len(rev.GetChildren(name)))
794 copy(children, rev.GetChildren(name))
Stephane Barbarieb0c79892019-02-13 11:29:59 -0500795
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400796 if path != "" {
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400797 if idx, childRev := n.findRevByKey(children, field.Key, keyValue); childRev != nil {
npujar9a30c702019-11-14 17:06:39 +0530798 childNode := childRev.getNode()
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400799 if childNode.Proxy == nil {
800 childNode.Proxy = n.Proxy
801 }
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400802 newChildRev := childNode.Remove(ctx, path, txid, makeBranch)
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400803
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400804 branch.LatestLock.Lock()
805 defer branch.LatestLock.Unlock()
806
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400807 if idx >= 0 {
808 children[idx] = newChildRev
809 } else {
810 children = append(children, newChildRev)
811 }
812
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400813 rev.SetChildren(name, children)
814 branch.GetLatest().Drop(txid, false)
815 n.makeLatest(branch, rev, nil)
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500816 }
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400817 return branch.GetLatest()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400818 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500819
npujar9a30c702019-11-14 17:06:39 +0530820 idx, childRev := n.findRevByKey(children, field.Key, keyValue)
821 if childRev != nil && idx >= 0 {
Stephane Barbarieb0c79892019-02-13 11:29:59 -0500822 if n.GetProxy() != nil {
823 data := childRev.GetData()
npujar467fe752020-01-16 20:17:45 +0530824 n.GetProxy().InvokeCallbacks(ctx, PreRemove, false, data)
npujar9a30c702019-11-14 17:06:39 +0530825 postAnnouncement = append(postAnnouncement, ChangeTuple{PostRemove, data, nil})
Stephane Barbarieb0c79892019-02-13 11:29:59 -0500826 } else {
npujar9a30c702019-11-14 17:06:39 +0530827 postAnnouncement = append(postAnnouncement, ChangeTuple{PostRemove, childRev.GetData(), nil})
Stephane Barbarieb0c79892019-02-13 11:29:59 -0500828 }
829
npujar467fe752020-01-16 20:17:45 +0530830 childRev.StorageDrop(ctx, txid, true)
npujar9a30c702019-11-14 17:06:39 +0530831 getRevCache().Delete(childRev.GetName())
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400832
833 branch.LatestLock.Lock()
834 defer branch.LatestLock.Unlock()
835
Stephane Barbarieb0c79892019-02-13 11:29:59 -0500836 children = append(children[:idx], children[idx+1:]...)
837 rev.SetChildren(name, children)
838
839 branch.GetLatest().Drop(txid, false)
840 n.makeLatest(branch, rev, postAnnouncement)
841
842 return rev
Stephane Barbarieb0c79892019-02-13 11:29:59 -0500843 }
Girish Kumarf56a4682020-03-20 20:07:46 +0000844 logger.Errorw("failed-to-find-revision", log.Fields{"name": name, "key": keyValue.(string)})
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400845 }
Girish Kumarf56a4682020-03-20 20:07:46 +0000846 logger.Errorw("cannot-add-to-non-keyed-container", log.Fields{"name": name, "path": path, "fieldKey": field.Key})
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500847
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400848 } else {
Girish Kumarf56a4682020-03-20 20:07:46 +0000849 logger.Errorw("cannot-add-to-non-container-field", log.Fields{"name": name, "path": path})
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400850 }
851
852 return nil
853}
854
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400855// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Branching ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
856
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500857// MakeBranchFunction is a type for function references intented to create a branch
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400858type MakeBranchFunction func(*node) *Branch
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400859
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500860// MakeBranch creates a new branch for the provided transaction id
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400861func (n *node) MakeBranch(txid string) *Branch {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500862 branchPoint := n.GetBranch(NONE).GetLatest()
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400863 branch := NewBranch(n, txid, branchPoint, true)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500864 n.SetBranch(txid, branch)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400865 return branch
866}
867
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500868// DeleteBranch removes a branch with the specified id
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400869func (n *node) DeleteBranch(txid string) {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400870 delete(n.Branches, txid)
871}
872
npujar467fe752020-01-16 20:17:45 +0530873func (n *node) mergeChild(ctx context.Context, txid string, dryRun bool) func(Revision) Revision {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400874 f := func(rev Revision) Revision {
875 childBranch := rev.GetBranch()
876
877 if childBranch.Txid == txid {
npujar467fe752020-01-16 20:17:45 +0530878 rev, _ = childBranch.Node.MergeBranch(ctx, txid, dryRun)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400879 }
880
881 return rev
882 }
883 return f
884}
885
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500886// MergeBranch will integrate the contents of a transaction branch within the latest branch of a given node
npujar467fe752020-01-16 20:17:45 +0530887func (n *node) MergeBranch(ctx context.Context, txid string, dryRun bool) (Revision, error) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500888 srcBranch := n.GetBranch(txid)
889 dstBranch := n.GetBranch(NONE)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400890
891 forkRev := srcBranch.Origin
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500892 srcRev := srcBranch.GetLatest()
893 dstRev := dstBranch.GetLatest()
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400894
npujar467fe752020-01-16 20:17:45 +0530895 rev, changes := Merge3Way(ctx, forkRev, srcRev, dstRev, n.mergeChild(ctx, txid, dryRun), dryRun)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400896
897 if !dryRun {
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500898 if rev != nil {
Stephane Barbarief7fc1782019-03-28 22:33:41 -0400899 rev.SetName(dstRev.GetName())
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500900 n.makeLatest(dstBranch, rev, changes)
901 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500902 n.DeleteBranch(txid)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400903 }
904
Stephane Barbariee16186c2018-09-11 10:46:34 -0400905 // TODO: return proper error when one occurs
906 return rev, nil
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400907}
908
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500909// CreateProxy returns a reference to a sub-tree of the data model
Thomas Lee Se5a44012019-11-07 20:32:24 +0530910func (n *node) CreateProxy(ctx context.Context, path string, exclusive bool) (*Proxy, error) {
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400911 return n.createProxy(ctx, path, path, n, exclusive)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400912}
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500913
Thomas Lee Se5a44012019-11-07 20:32:24 +0530914func (n *node) createProxy(ctx context.Context, path string, fullPath string, parentNode *node, exclusive bool) (*Proxy, error) {
Girish Kumarf56a4682020-03-20 20:07:46 +0000915 logger.Debugw("node-create-proxy", log.Fields{
Stephane Barbariec92d1072019-06-07 16:21:49 -0400916 "node-type": reflect.ValueOf(n.Type).Type(),
917 "parent-node-type": reflect.ValueOf(parentNode.Type).Type(),
918 "path": path,
919 "fullPath": fullPath,
920 })
921
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400922 for strings.HasPrefix(path, "/") {
923 path = path[1:]
924 }
925 if path == "" {
Thomas Lee Se5a44012019-11-07 20:32:24 +0530926 return n.makeProxy(path, fullPath, parentNode, exclusive), nil
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400927 }
928
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500929 rev := n.GetBranch(NONE).GetLatest()
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400930 partition := strings.SplitN(path, "/", 2)
931 name := partition[0]
Stephane Barbariec92d1072019-06-07 16:21:49 -0400932 var nodeType interface{}
Stephane Barbarie126101e2018-10-11 16:18:48 -0400933 if len(partition) < 2 {
934 path = ""
Stephane Barbariec92d1072019-06-07 16:21:49 -0400935 nodeType = n.Type
Stephane Barbarie126101e2018-10-11 16:18:48 -0400936 } else {
937 path = partition[1]
Stephane Barbariec92d1072019-06-07 16:21:49 -0400938 nodeType = parentNode.Type
Stephane Barbarie126101e2018-10-11 16:18:48 -0400939 }
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400940
Stephane Barbariec92d1072019-06-07 16:21:49 -0400941 field := ChildrenFields(nodeType)[name]
942
943 if field != nil {
944 if field.IsContainer {
Girish Kumarf56a4682020-03-20 20:07:46 +0000945 logger.Debugw("container-field", log.Fields{
Stephane Barbariec92d1072019-06-07 16:21:49 -0400946 "node-type": reflect.ValueOf(n.Type).Type(),
947 "parent-node-type": reflect.ValueOf(parentNode.Type).Type(),
948 "path": path,
949 "name": name,
950 })
951 if path == "" {
Girish Kumarf56a4682020-03-20 20:07:46 +0000952 logger.Debugw("folder-proxy", log.Fields{
Stephane Barbariec92d1072019-06-07 16:21:49 -0400953 "node-type": reflect.ValueOf(n.Type).Type(),
954 "parent-node-type": reflect.ValueOf(parentNode.Type).Type(),
955 "fullPath": fullPath,
956 "name": name,
957 })
958 newNode := n.MakeNode(reflect.New(field.ClassType.Elem()).Interface(), "")
Thomas Lee Se5a44012019-11-07 20:32:24 +0530959 return newNode.makeProxy(path, fullPath, parentNode, exclusive), nil
Stephane Barbariec92d1072019-06-07 16:21:49 -0400960 } else if field.Key != "" {
Girish Kumarf56a4682020-03-20 20:07:46 +0000961 logger.Debugw("key-proxy", log.Fields{
Stephane Barbariec92d1072019-06-07 16:21:49 -0400962 "node-type": reflect.ValueOf(n.Type).Type(),
963 "parent-node-type": reflect.ValueOf(parentNode.Type).Type(),
964 "fullPath": fullPath,
965 "name": name,
966 })
967 partition := strings.SplitN(path, "/", 2)
968 key := partition[0]
969 if len(partition) < 2 {
970 path = ""
971 } else {
972 path = partition[1]
973 }
974 keyValue := field.KeyFromStr(key)
npujar9a30c702019-11-14 17:06:39 +0530975 children := make([]Revision, len(rev.GetChildren(name)))
Stephane Barbariec92d1072019-06-07 16:21:49 -0400976 copy(children, rev.GetChildren(name))
977
Stephane Barbariec92d1072019-06-07 16:21:49 -0400978 var childRev Revision
979 if _, childRev = n.findRevByKey(children, field.Key, keyValue); childRev != nil {
Girish Kumarf56a4682020-03-20 20:07:46 +0000980 logger.Debugw("found-revision-matching-key-in-memory", log.Fields{
Stephane Barbariec92d1072019-06-07 16:21:49 -0400981 "node-type": reflect.ValueOf(n.Type).Type(),
982 "parent-node-type": reflect.ValueOf(parentNode.Type).Type(),
983 "fullPath": fullPath,
984 "name": name,
985 })
Thomas Lee Se5a44012019-11-07 20:32:24 +0530986 } else if revs, err := n.GetBranch(NONE).GetLatest().LoadFromPersistence(ctx, fullPath, "", nil); err != nil {
Girish Kumarf56a4682020-03-20 20:07:46 +0000987 logger.Errorf("failed-to-load-from-persistence")
Thomas Lee Se5a44012019-11-07 20:32:24 +0530988 return nil, err
npujar9a30c702019-11-14 17:06:39 +0530989 } else if len(revs) > 0 {
Girish Kumarf56a4682020-03-20 20:07:46 +0000990 logger.Debugw("found-revision-matching-key-in-db", log.Fields{
Stephane Barbariec92d1072019-06-07 16:21:49 -0400991 "node-type": reflect.ValueOf(n.Type).Type(),
992 "parent-node-type": reflect.ValueOf(parentNode.Type).Type(),
993 "fullPath": fullPath,
994 "name": name,
995 })
996 childRev = revs[0]
997 } else {
Girish Kumarf56a4682020-03-20 20:07:46 +0000998 logger.Debugw("no-revision-matching-key", log.Fields{
Stephane Barbariec92d1072019-06-07 16:21:49 -0400999 "node-type": reflect.ValueOf(n.Type).Type(),
1000 "parent-node-type": reflect.ValueOf(parentNode.Type).Type(),
1001 "fullPath": fullPath,
1002 "name": name,
1003 })
1004 }
1005 if childRev != nil {
npujar9a30c702019-11-14 17:06:39 +05301006 childNode := childRev.getNode()
Stephane Barbarieef6650d2019-07-18 12:15:09 -04001007 return childNode.createProxy(ctx, path, fullPath, n, exclusive)
Stephane Barbariec92d1072019-06-07 16:21:49 -04001008 }
Stephane Barbarie06c4a742018-10-01 11:09:32 -04001009 } else {
Girish Kumarf56a4682020-03-20 20:07:46 +00001010 logger.Errorw("cannot-access-index-of-empty-container", log.Fields{
Stephane Barbariec92d1072019-06-07 16:21:49 -04001011 "node-type": reflect.ValueOf(n.Type).Type(),
1012 "parent-node-type": reflect.ValueOf(parentNode.Type).Type(),
1013 "path": path,
1014 "name": name,
1015 })
Stephane Barbarie933b09b2019-01-09 11:12:09 -05001016 }
Stephane Barbarie126101e2018-10-11 16:18:48 -04001017 } else {
Girish Kumarf56a4682020-03-20 20:07:46 +00001018 logger.Debugw("non-container-field", log.Fields{
Stephane Barbariec92d1072019-06-07 16:21:49 -04001019 "node-type": reflect.ValueOf(n.Type).Type(),
1020 "parent-node-type": reflect.ValueOf(parentNode.Type).Type(),
1021 "path": path,
1022 "name": name,
1023 })
1024 childRev := rev.GetChildren(name)[0]
npujar9a30c702019-11-14 17:06:39 +05301025 childNode := childRev.getNode()
Stephane Barbarieef6650d2019-07-18 12:15:09 -04001026 return childNode.createProxy(ctx, path, fullPath, n, exclusive)
Stephane Barbarieec0919b2018-09-05 14:14:29 -04001027 }
Stephane Barbarieec0919b2018-09-05 14:14:29 -04001028 } else {
Girish Kumarf56a4682020-03-20 20:07:46 +00001029 logger.Debugw("field-object-is-nil", log.Fields{
Stephane Barbariec92d1072019-06-07 16:21:49 -04001030 "node-type": reflect.ValueOf(n.Type).Type(),
1031 "parent-node-type": reflect.ValueOf(parentNode.Type).Type(),
1032 "fullPath": fullPath,
1033 "name": name,
1034 })
Stephane Barbarieec0919b2018-09-05 14:14:29 -04001035 }
1036
Girish Kumarf56a4682020-03-20 20:07:46 +00001037 logger.Warnw("cannot-create-proxy", log.Fields{
Stephane Barbariec92d1072019-06-07 16:21:49 -04001038 "node-type": reflect.ValueOf(n.Type).Type(),
1039 "parent-node-type": reflect.ValueOf(parentNode.Type).Type(),
1040 "path": path,
1041 "fullPath": fullPath,
1042 "latest-rev": rev.GetHash(),
1043 })
Thomas Lee Se5a44012019-11-07 20:32:24 +05301044 return nil, nil
Stephane Barbarieec0919b2018-09-05 14:14:29 -04001045}
1046
Stephane Barbarie1ab43272018-12-08 21:42:13 -05001047func (n *node) makeProxy(path string, fullPath string, parentNode *node, exclusive bool) *Proxy {
Girish Kumarf56a4682020-03-20 20:07:46 +00001048 logger.Debugw("node-make-proxy", log.Fields{
Stephane Barbariec92d1072019-06-07 16:21:49 -04001049 "node-type": reflect.ValueOf(n.Type).Type(),
1050 "parent-node-type": reflect.ValueOf(parentNode.Type).Type(),
1051 "path": path,
1052 "fullPath": fullPath,
1053 })
1054
Stephane Barbarie126101e2018-10-11 16:18:48 -04001055 r := &root{
1056 node: n,
Stephane Barbariedc5022d2018-11-19 15:21:44 -05001057 Callbacks: n.Root.GetCallbacks(),
1058 NotificationCallbacks: n.Root.GetNotificationCallbacks(),
Stephane Barbarie126101e2018-10-11 16:18:48 -04001059 DirtyNodes: n.Root.DirtyNodes,
1060 KvStore: n.Root.KvStore,
1061 Loading: n.Root.Loading,
1062 RevisionClass: n.Root.RevisionClass,
1063 }
1064
Stephane Barbarieec0919b2018-09-05 14:14:29 -04001065 if n.Proxy == nil {
Girish Kumarf56a4682020-03-20 20:07:46 +00001066 logger.Debugw("constructing-new-proxy", log.Fields{
Stephane Barbariec92d1072019-06-07 16:21:49 -04001067 "node-type": reflect.ValueOf(n.Type).Type(),
1068 "parent-node-type": reflect.ValueOf(parentNode.Type).Type(),
1069 "path": path,
1070 "fullPath": fullPath,
1071 })
Stephane Barbarie1ab43272018-12-08 21:42:13 -05001072 n.Proxy = NewProxy(r, n, parentNode, path, fullPath, exclusive)
Stephane Barbarieec0919b2018-09-05 14:14:29 -04001073 } else {
Girish Kumarf56a4682020-03-20 20:07:46 +00001074 logger.Debugw("node-has-existing-proxy", log.Fields{
Stephane Barbarieef6650d2019-07-18 12:15:09 -04001075 "node-type": reflect.ValueOf(n.GetProxy().Node.Type).Type(),
1076 "parent-node-type": reflect.ValueOf(n.GetProxy().ParentNode.Type).Type(),
1077 "path": n.GetProxy().Path,
1078 "fullPath": n.GetProxy().FullPath,
Stephane Barbariec92d1072019-06-07 16:21:49 -04001079 })
Stephane Barbarieef6650d2019-07-18 12:15:09 -04001080 if n.GetProxy().Exclusive {
Girish Kumarf56a4682020-03-20 20:07:46 +00001081 logger.Error("node is already owned exclusively")
Stephane Barbarieec0919b2018-09-05 14:14:29 -04001082 }
1083 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -05001084
Stephane Barbarieec0919b2018-09-05 14:14:29 -04001085 return n.Proxy
1086}
1087
Stephane Barbariedc5022d2018-11-19 15:21:44 -05001088func (n *node) SetProxy(proxy *Proxy) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -05001089 n.Proxy = proxy
1090}
1091
1092func (n *node) GetProxy() *Proxy {
Stephane Barbariedc5022d2018-11-19 15:21:44 -05001093 return n.Proxy
1094}
1095
1096func (n *node) GetBranch(key string) *Branch {
Stephane Barbarie1ab43272018-12-08 21:42:13 -05001097 if n.Branches != nil {
1098 if branch, exists := n.Branches[key]; exists {
1099 return branch
1100 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -05001101 }
1102 return nil
1103}
1104
1105func (n *node) SetBranch(key string, branch *Branch) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -05001106 n.Branches[key] = branch
1107}
1108
1109func (n *node) GetRoot() *root {
Stephane Barbariedc5022d2018-11-19 15:21:44 -05001110 return n.Root
Stephane Barbarie06c4a742018-10-01 11:09:32 -04001111}
Stephane Barbarieef6650d2019-07-18 12:15:09 -04001112func (n *node) SetRoot(root *root) {
1113 n.Root = root
1114}