blob: 1621b6f34c616be1db817223cfdb564f2845cc50 [file] [log] [blame]
Matt Jeanneretcab955f2019-04-10 15:45:57 -04001/*
2 * Copyright 2018-present Open Networking Foundation
3
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7
8 * http://www.apache.org/licenses/LICENSE-2.0
9
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package model
18
19// TODO: proper error handling
20// TODO: proper logging
21
22import (
23 "fmt"
24 "github.com/golang/protobuf/proto"
25 "github.com/opencord/voltha-go/common/log"
26 "reflect"
Matt Jeanneretcab955f2019-04-10 15:45:57 -040027 "strings"
28 "sync"
29)
30
31// When a branch has no transaction id, everything gets stored in NONE
32const (
33 NONE string = "none"
34)
35
36// Node interface is an abstraction of the node data structure
37type Node interface {
38 MakeLatest(branch *Branch, revision Revision, changeAnnouncement []ChangeTuple)
39
40 // CRUD functions
41 Add(path string, data interface{}, txid string, makeBranch MakeBranchFunction) Revision
42 Get(path string, hash string, depth int, deep bool, txid string) interface{}
43 Update(path string, data interface{}, strict bool, txid string, makeBranch MakeBranchFunction) Revision
44 Remove(path string, txid string, makeBranch MakeBranchFunction) Revision
45
46 MakeBranch(txid string) *Branch
47 DeleteBranch(txid string)
48 MergeBranch(txid string, dryRun bool) (Revision, error)
49
50 MakeTxBranch() string
51 DeleteTxBranch(txid string)
52 FoldTxBranch(txid string)
53
54 CreateProxy(path string, exclusive bool) *Proxy
55 GetProxy() *Proxy
56}
57
58type node struct {
59 sync.RWMutex
60 Root *root
61 Type interface{}
62 Branches map[string]*Branch
63 Tags map[string]Revision
64 Proxy *Proxy
65 EventBus *EventBus
66 AutoPrune bool
67}
68
69// ChangeTuple holds details of modifications made to a revision
70type ChangeTuple struct {
71 Type CallbackType
72 PreviousData interface{}
73 LatestData interface{}
74}
75
76// NewNode creates a new instance of the node data structure
77func NewNode(root *root, initialData interface{}, autoPrune bool, txid string) *node {
78 n := &node{}
79
80 n.Root = root
81 n.Branches = make(map[string]*Branch)
82 n.Tags = make(map[string]Revision)
83 n.Proxy = nil
84 n.EventBus = nil
85 n.AutoPrune = autoPrune
86
87 if IsProtoMessage(initialData) {
88 n.Type = reflect.ValueOf(initialData).Interface()
89 dataCopy := proto.Clone(initialData.(proto.Message))
90 n.initialize(dataCopy, txid)
91 } else if reflect.ValueOf(initialData).IsValid() {
92 // FIXME: this block does not reflect the original implementation
93 // it should be checking if the provided initial_data is already a type!??!
94 // it should be checked before IsProtoMessage
95 n.Type = reflect.ValueOf(initialData).Interface()
96 } else {
97 // not implemented error
98 log.Errorf("cannot process initial data - %+v", initialData)
99 }
100
101 return n
102}
103
104// MakeNode creates a new node in the tree
105func (n *node) MakeNode(data interface{}, txid string) *node {
106 return NewNode(n.Root, data, true, txid)
107}
108
109// MakeRevision create a new revision of the node in the tree
110func (n *node) MakeRevision(branch *Branch, data interface{}, children map[string][]Revision) Revision {
111 return n.GetRoot().MakeRevision(branch, data, children)
112}
113
114// makeLatest will mark the revision of a node as being the latest
115func (n *node) makeLatest(branch *Branch, revision Revision, changeAnnouncement []ChangeTuple) {
116 n.Lock()
117 defer n.Unlock()
118
Matt Jeanneret384d8c92019-05-06 14:27:31 -0400119 // Keep a reference to the current revision
120 var previous string
121 if branch.GetLatest() != nil {
122 previous = branch.GetLatest().GetHash()
123 }
124
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400125 branch.AddRevision(revision)
126
Matt Jeanneret384d8c92019-05-06 14:27:31 -0400127 // If anything is new, then set the revision as the latest
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400128 if branch.GetLatest() == nil || revision.GetHash() != branch.GetLatest().GetHash() {
manikkaraj k9eb6cac2019-05-09 12:32:03 -0400129 if revision.GetName() != "" {
130 GetRevCache().Cache.Store(revision.GetName(), revision)
131 }
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400132 branch.SetLatest(revision)
133 }
134
Matt Jeanneret384d8c92019-05-06 14:27:31 -0400135 // Delete the previous revision if anything has changed
136 if previous != "" && previous != branch.GetLatest().GetHash() {
137 branch.DeleteRevision(previous)
138 }
139
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400140 if changeAnnouncement != nil && branch.Txid == "" {
141 if n.Proxy != nil {
142 for _, change := range changeAnnouncement {
Matt Jeanneret384d8c92019-05-06 14:27:31 -0400143 log.Debugw("adding-callback",
144 log.Fields{
145 "callbacks": n.Proxy.getCallbacks(change.Type),
146 "type": change.Type,
147 "previousData": change.PreviousData,
148 "latestData": change.LatestData,
149 })
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400150 n.Root.AddCallback(
151 n.Proxy.InvokeCallbacks,
152 change.Type,
153 true,
154 change.PreviousData,
155 change.LatestData)
156 }
157 }
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400158 }
159}
160
161// Latest returns the latest revision of node with or without the transaction id
162func (n *node) Latest(txid ...string) Revision {
163 var branch *Branch
164
165 if len(txid) > 0 && txid[0] != "" {
166 if branch = n.GetBranch(txid[0]); branch != nil {
167 return branch.GetLatest()
168 }
169 } else if branch = n.GetBranch(NONE); branch != nil {
170 return branch.GetLatest()
171 }
172 return nil
173}
174
175// initialize prepares the content of a node along with its possible ramifications
176func (n *node) initialize(data interface{}, txid string) {
177 n.Lock()
178 children := make(map[string][]Revision)
179 for fieldName, field := range ChildrenFields(n.Type) {
180 _, fieldValue := GetAttributeValue(data, fieldName, 0)
181
182 if fieldValue.IsValid() {
183 if field.IsContainer {
184 if field.Key != "" {
185 for i := 0; i < fieldValue.Len(); i++ {
186 v := fieldValue.Index(i)
187
188 if rev := n.MakeNode(v.Interface(), txid).Latest(txid); rev != nil {
189 children[fieldName] = append(children[fieldName], rev)
190 }
191
192 // TODO: The following logic was ported from v1.0. Need to verify if it is required
193 //var keysSeen []string
194 //_, key := GetAttributeValue(v.Interface(), field.Key, 0)
195 //for _, k := range keysSeen {
196 // if k == key.String() {
197 // //log.Errorf("duplicate key - %s", k)
198 // }
199 //}
200 //keysSeen = append(keysSeen, key.String())
201 }
202
203 } else {
204 for i := 0; i < fieldValue.Len(); i++ {
205 v := fieldValue.Index(i)
206 if newNodeRev := n.MakeNode(v.Interface(), txid).Latest(); newNodeRev != nil {
207 children[fieldName] = append(children[fieldName], newNodeRev)
208 }
209 }
210 }
211 } else {
212 if newNodeRev := n.MakeNode(fieldValue.Interface(), txid).Latest(); newNodeRev != nil {
213 children[fieldName] = append(children[fieldName], newNodeRev)
214 }
215 }
216 } else {
217 log.Errorf("field is invalid - %+v", fieldValue)
218 }
219 }
220 n.Unlock()
221
222 branch := NewBranch(n, "", nil, n.AutoPrune)
223 rev := n.MakeRevision(branch, data, children)
224 n.makeLatest(branch, rev, nil)
225
226 if txid == "" {
227 n.SetBranch(NONE, branch)
228 } else {
229 n.SetBranch(txid, branch)
230 }
231}
232
233// findRevByKey retrieves a specific revision from a node tree
234func (n *node) findRevByKey(revs []Revision, keyName string, value interface{}) (int, Revision) {
235 n.Lock()
236 defer n.Unlock()
237
238 for i, rev := range revs {
239 dataValue := reflect.ValueOf(rev.GetData())
240 dataStruct := GetAttributeStructure(rev.GetData(), keyName, 0)
241
242 fieldValue := dataValue.Elem().FieldByName(dataStruct.Name)
243
244 a := fmt.Sprintf("%s", fieldValue.Interface())
245 b := fmt.Sprintf("%s", value)
246 if a == b {
247 return i, revs[i]
248 }
249 }
250
251 return -1, nil
252}
253
254// Get retrieves the data from a node tree that resides at the specified path
255func (n *node) List(path string, hash string, depth int, deep bool, txid string) interface{} {
256 log.Debugw("node-list-request", log.Fields{"path": path, "hash": hash, "depth": depth, "deep": deep, "txid": txid})
257 if deep {
258 depth = -1
259 }
260
261 for strings.HasPrefix(path, "/") {
262 path = path[1:]
263 }
264
265 var branch *Branch
266 var rev Revision
267
268 if branch = n.GetBranch(txid); txid == "" || branch == nil {
269 branch = n.GetBranch(NONE)
270 }
271
272 if hash != "" {
273 rev = branch.GetRevision(hash)
274 } else {
275 rev = branch.GetLatest()
276 }
277
278 var result interface{}
279 var prList []interface{}
manikkaraj k9eb6cac2019-05-09 12:32:03 -0400280 if pr := rev.LoadFromPersistence(path, txid, nil); pr != nil {
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400281 for _, revEntry := range pr {
282 prList = append(prList, revEntry.GetData())
283 }
284 result = prList
285 }
286
287 return result
288}
289
290// Get retrieves the data from a node tree that resides at the specified path
291func (n *node) Get(path string, hash string, depth int, reconcile bool, txid string) interface{} {
Matt Jeanneret384d8c92019-05-06 14:27:31 -0400292 log.Debugw("node-get-request", log.Fields{"path": path, "hash": hash, "depth": depth, "reconcile": reconcile, "txid": txid})
manikkaraj k9eb6cac2019-05-09 12:32:03 -0400293
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400294 for strings.HasPrefix(path, "/") {
295 path = path[1:]
296 }
297
298 var branch *Branch
299 var rev Revision
300
301 if branch = n.GetBranch(txid); txid == "" || branch == nil {
302 branch = n.GetBranch(NONE)
303 }
304
305 if hash != "" {
306 rev = branch.GetRevision(hash)
307 } else {
308 rev = branch.GetLatest()
309 }
310
311 var result interface{}
312
manikkaraj k9eb6cac2019-05-09 12:32:03 -0400313 // If there is no request to reconcile, try to get it from memory
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400314 if !reconcile {
manikkaraj k9eb6cac2019-05-09 12:32:03 -0400315 // Try to find an entry matching the path value from one of these sources
316 // 1. Start with the cache which stores revisions by watch names
317 // 2. Then look in the revision tree, especially if it's a sub-path such as /devices/1234/flows
318 // 3. As a last effort, move on to the KV store
319 if entry, exists := GetRevCache().Cache.Load(path); exists && entry.(Revision) != nil {
320 return proto.Clone(entry.(Revision).GetData().(proto.Message))
321 } else if result = n.getPath(rev.GetBranch().GetLatest(), path, depth); result != nil && reflect.ValueOf(result).IsValid() && !reflect.ValueOf(result).IsNil() {
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400322 return result
323 }
324 }
325
326 // If we got to this point, we are either trying to reconcile with the db or
327 // or we simply failed at getting information from memory
328 if n.Root.KvStore != nil {
manikkaraj k9eb6cac2019-05-09 12:32:03 -0400329 if pr := rev.LoadFromPersistence(path, txid, nil); pr != nil && len(pr) > 0 {
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400330 // Did we receive a single or multiple revisions?
331 if len(pr) > 1 {
manikkaraj k9eb6cac2019-05-09 12:32:03 -0400332 var revs []interface{}
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400333 for _, revEntry := range pr {
manikkaraj k9eb6cac2019-05-09 12:32:03 -0400334 revs = append(revs, revEntry.GetData())
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400335 }
manikkaraj k9eb6cac2019-05-09 12:32:03 -0400336 result = revs
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400337 } else {
338 result = pr[0].GetData()
339 }
340 }
341 }
342
343 return result
344}
345
manikkaraj k9eb6cac2019-05-09 12:32:03 -0400346//getPath traverses the specified path and retrieves the data associated to it
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400347func (n *node) getPath(rev Revision, path string, depth int) interface{} {
348 if path == "" {
349 return n.getData(rev, depth)
350 }
351
352 partition := strings.SplitN(path, "/", 2)
353 name := partition[0]
354
355 if len(partition) < 2 {
356 path = ""
357 } else {
358 path = partition[1]
359 }
360
361 names := ChildrenFields(n.Type)
362 field := names[name]
363
364 if field != nil && field.IsContainer {
365 children := make([]Revision, len(rev.GetChildren(name)))
366 copy(children, rev.GetChildren(name))
367
368 if field.Key != "" {
369 if path != "" {
370 partition = strings.SplitN(path, "/", 2)
371 key := partition[0]
372 path = ""
373 keyValue := field.KeyFromStr(key)
374 if _, childRev := n.findRevByKey(children, field.Key, keyValue); childRev == nil {
375 return nil
376 } else {
377 childNode := childRev.GetNode()
378 return childNode.getPath(childRev, path, depth)
379 }
380 } else {
381 var response []interface{}
382 for _, childRev := range children {
383 childNode := childRev.GetNode()
384 value := childNode.getData(childRev, depth)
385 response = append(response, value)
386 }
387 return response
388 }
389 } else {
390 var response []interface{}
391 if path != "" {
392 // TODO: raise error
393 return response
394 }
395 for _, childRev := range children {
396 childNode := childRev.GetNode()
397 value := childNode.getData(childRev, depth)
398 response = append(response, value)
399 }
400 return response
401 }
402 }
403
404 childRev := rev.GetChildren(name)[0]
405 childNode := childRev.GetNode()
406 return childNode.getPath(childRev, path, depth)
407}
408
409// getData retrieves the data from a node revision
410func (n *node) getData(rev Revision, depth int) interface{} {
411 msg := rev.GetBranch().GetLatest().Get(depth)
412 var modifiedMsg interface{}
413
414 if n.GetProxy() != nil {
415 log.Debugw("invoking-get-callbacks", log.Fields{"data": msg})
416 if modifiedMsg = n.GetProxy().InvokeCallbacks(GET, false, msg); modifiedMsg != nil {
417 msg = modifiedMsg
418 }
419
420 }
421
422 return msg
423}
424
425// Update changes the content of a node at the specified path with the provided data
426func (n *node) Update(path string, data interface{}, strict bool, txid string, makeBranch MakeBranchFunction) Revision {
427 log.Debugw("node-update-request", log.Fields{"path": path, "strict": strict, "txid": txid, "makeBranch": makeBranch})
428
429 for strings.HasPrefix(path, "/") {
430 path = path[1:]
431 }
432
433 var branch *Branch
434 if txid == "" {
435 branch = n.GetBranch(NONE)
436 } else if branch = n.GetBranch(txid); branch == nil {
437 branch = makeBranch(n)
438 }
439
440 if branch.GetLatest() != nil {
441 log.Debugf("Branch data : %+v, Passed data: %+v", branch.GetLatest().GetData(), data)
442 }
443 if path == "" {
444 return n.doUpdate(branch, data, strict)
445 }
446
447 rev := branch.GetLatest()
448
449 partition := strings.SplitN(path, "/", 2)
450 name := partition[0]
451
452 if len(partition) < 2 {
453 path = ""
454 } else {
455 path = partition[1]
456 }
457
458 field := ChildrenFields(n.Type)[name]
459 var children []Revision
460
461 if field == nil {
462 return n.doUpdate(branch, data, strict)
463 }
464
465 if field.IsContainer {
466 if path == "" {
467 log.Errorf("cannot update a list")
468 } else if field.Key != "" {
469 partition := strings.SplitN(path, "/", 2)
470 key := partition[0]
471 if len(partition) < 2 {
472 path = ""
473 } else {
474 path = partition[1]
475 }
476 keyValue := field.KeyFromStr(key)
477
478 children = make([]Revision, len(rev.GetChildren(name)))
479 copy(children, rev.GetChildren(name))
480
481 idx, childRev := n.findRevByKey(children, field.Key, keyValue)
Matt Jeanneret384d8c92019-05-06 14:27:31 -0400482
483 if childRev == nil {
manikkaraj k9eb6cac2019-05-09 12:32:03 -0400484 log.Debugw("child-revision-is-nil", log.Fields{"key": keyValue})
Matt Jeanneret384d8c92019-05-06 14:27:31 -0400485 return branch.GetLatest()
486 }
487
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400488 childNode := childRev.GetNode()
489
490 // Save proxy in child node to ensure callbacks are called later on
491 // only assign in cases of non sub-folder proxies, i.e. "/"
492 if childNode.Proxy == nil && n.Proxy != nil && n.Proxy.getFullPath() == "" {
493 childNode.Proxy = n.Proxy
494 }
495
496 newChildRev := childNode.Update(path, data, strict, txid, makeBranch)
497
498 if newChildRev.GetHash() == childRev.GetHash() {
499 if newChildRev != childRev {
500 log.Debug("clear-hash - %s %+v", newChildRev.GetHash(), newChildRev)
501 newChildRev.ClearHash()
502 }
manikkaraj k9eb6cac2019-05-09 12:32:03 -0400503 log.Debugw("child-revisions-have-matching-hash", log.Fields{"hash": childRev.GetHash(), "key": keyValue})
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400504 return branch.GetLatest()
505 }
506
507 _, newKey := GetAttributeValue(newChildRev.GetData(), field.Key, 0)
508
509 _newKeyType := fmt.Sprintf("%s", newKey)
510 _keyValueType := fmt.Sprintf("%s", keyValue)
511
512 if _newKeyType != _keyValueType {
513 log.Errorf("cannot change key field")
514 }
515
516 // Prefix the hash value with the data type (e.g. devices, logical_devices, adapters)
517 newChildRev.SetName(name + "/" + _keyValueType)
Matt Jeanneret384d8c92019-05-06 14:27:31 -0400518
manikkaraj k9eb6cac2019-05-09 12:32:03 -0400519 branch.LatestLock.Lock()
520 defer branch.LatestLock.Unlock()
521
Matt Jeanneret384d8c92019-05-06 14:27:31 -0400522 if idx >= 0 {
523 children[idx] = newChildRev
524 } else {
525 children = append(children, newChildRev)
526 }
527
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400528 updatedRev := rev.UpdateChildren(name, children, branch)
529
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400530 n.makeLatest(branch, updatedRev, nil)
Matt Jeanneret384d8c92019-05-06 14:27:31 -0400531 updatedRev.ChildDrop(name, childRev.GetHash())
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400532
533 return newChildRev
534
535 } else {
536 log.Errorf("cannot index into container with no keys")
537 }
538 } else {
539 childRev := rev.GetChildren(name)[0]
540 childNode := childRev.GetNode()
541 newChildRev := childNode.Update(path, data, strict, txid, makeBranch)
Matt Jeanneret384d8c92019-05-06 14:27:31 -0400542
543 branch.LatestLock.Lock()
544 defer branch.LatestLock.Unlock()
545
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400546 updatedRev := rev.UpdateChildren(name, []Revision{newChildRev}, branch)
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400547 n.makeLatest(branch, updatedRev, nil)
548
Matt Jeanneret384d8c92019-05-06 14:27:31 -0400549 updatedRev.ChildDrop(name, childRev.GetHash())
550
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400551 return newChildRev
552 }
553
554 return nil
555}
556
557func (n *node) doUpdate(branch *Branch, data interface{}, strict bool) Revision {
manikkaraj k9eb6cac2019-05-09 12:32:03 -0400558 log.Debugw("comparing-types", log.Fields{"expected": reflect.ValueOf(n.Type).Type(), "actual": reflect.TypeOf(data)})
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400559
560 if reflect.TypeOf(data) != reflect.ValueOf(n.Type).Type() {
561 // TODO raise error
manikkaraj k9eb6cac2019-05-09 12:32:03 -0400562 log.Errorw("types-do-not-match: %+v", log.Fields{"actual": reflect.TypeOf(data), "expected": n.Type})
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400563 return nil
564 }
565
566 // TODO: validate that this actually works
567 //if n.hasChildren(data) {
568 // return nil
569 //}
570
571 if n.GetProxy() != nil {
572 log.Debug("invoking proxy PRE_UPDATE Callbacks")
573 n.GetProxy().InvokeCallbacks(PRE_UPDATE, false, branch.GetLatest(), data)
574 }
575
576 if branch.GetLatest().GetData().(proto.Message).String() != data.(proto.Message).String() {
577 if strict {
578 // TODO: checkAccessViolations(data, Branch.GetLatest.data)
579 log.Debugf("checking access violations")
580 }
581
582 rev := branch.GetLatest().UpdateData(data, branch)
583 changes := []ChangeTuple{{POST_UPDATE, branch.GetLatest().GetData(), rev.GetData()}}
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400584 n.makeLatest(branch, rev, changes)
585
586 return rev
587 }
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400588 return branch.GetLatest()
589}
590
591// Add inserts a new node at the specified path with the provided data
592func (n *node) Add(path string, data interface{}, txid string, makeBranch MakeBranchFunction) Revision {
593 log.Debugw("node-add-request", log.Fields{"path": path, "txid": txid, "makeBranch": makeBranch})
594
595 for strings.HasPrefix(path, "/") {
596 path = path[1:]
597 }
598 if path == "" {
599 // TODO raise error
600 log.Errorf("cannot add for non-container mode")
601 return nil
602 }
603
604 var branch *Branch
605 if txid == "" {
606 branch = n.GetBranch(NONE)
607 } else if branch = n.GetBranch(txid); branch == nil {
608 branch = makeBranch(n)
609 }
610
611 rev := branch.GetLatest()
612
613 partition := strings.SplitN(path, "/", 2)
614 name := partition[0]
615
616 if len(partition) < 2 {
617 path = ""
618 } else {
619 path = partition[1]
620 }
621
622 field := ChildrenFields(n.Type)[name]
623
624 var children []Revision
625
626 if field.IsContainer {
627 if path == "" {
628 if field.Key != "" {
629 if n.GetProxy() != nil {
630 log.Debug("invoking proxy PRE_ADD Callbacks")
631 n.GetProxy().InvokeCallbacks(PRE_ADD, false, data)
632 }
633
634 children = make([]Revision, len(rev.GetChildren(name)))
635 copy(children, rev.GetChildren(name))
636
637 _, key := GetAttributeValue(data, field.Key, 0)
638
639 if _, exists := n.findRevByKey(children, field.Key, key.String()); exists != nil {
640 // TODO raise error
641 log.Warnw("duplicate-key-found", log.Fields{"key": key.String()})
642 return exists
643 }
644 childRev := n.MakeNode(data, "").Latest()
645
646 // Prefix the hash with the data type (e.g. devices, logical_devices, adapters)
647 childRev.SetName(name + "/" + key.String())
648
Matt Jeanneret384d8c92019-05-06 14:27:31 -0400649 branch.LatestLock.Lock()
650 defer branch.LatestLock.Unlock()
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400651
652 children = append(children, childRev)
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400653
Matt Jeanneret384d8c92019-05-06 14:27:31 -0400654 updatedRev := rev.UpdateChildren(name, children, branch)
655 changes := []ChangeTuple{{POST_ADD, nil, childRev.GetData()}}
656 childRev.SetupWatch(childRev.GetName())
657
658 n.makeLatest(branch, updatedRev, changes)
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400659
660 return childRev
661 }
662 log.Errorf("cannot add to non-keyed container")
663
664 } else if field.Key != "" {
665 partition := strings.SplitN(path, "/", 2)
666 key := partition[0]
667 if len(partition) < 2 {
668 path = ""
669 } else {
670 path = partition[1]
671 }
672 keyValue := field.KeyFromStr(key)
673
674 children = make([]Revision, len(rev.GetChildren(name)))
675 copy(children, rev.GetChildren(name))
676
677 idx, childRev := n.findRevByKey(children, field.Key, keyValue)
678
Matt Jeanneret384d8c92019-05-06 14:27:31 -0400679 if childRev == nil {
680 return branch.GetLatest()
681 }
682
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400683 childNode := childRev.GetNode()
684 newChildRev := childNode.Add(path, data, txid, makeBranch)
685
686 // Prefix the hash with the data type (e.g. devices, logical_devices, adapters)
manikkaraj k9eb6cac2019-05-09 12:32:03 -0400687 newChildRev.SetName(name + "/" + keyValue.(string))
688
689 branch.LatestLock.Lock()
690 defer branch.LatestLock.Unlock()
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400691
Matt Jeanneret384d8c92019-05-06 14:27:31 -0400692 if idx >= 0 {
693 children[idx] = newChildRev
694 } else {
695 children = append(children, newChildRev)
696 }
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400697
Matt Jeanneret384d8c92019-05-06 14:27:31 -0400698 updatedRev := rev.UpdateChildren(name, children, branch)
699 n.makeLatest(branch, updatedRev, nil)
700
701 updatedRev.ChildDrop(name, childRev.GetHash())
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400702
703 return newChildRev
704 } else {
705 log.Errorf("cannot add to non-keyed container")
706 }
707 } else {
708 log.Errorf("cannot add to non-container field")
709 }
710
711 return nil
712}
713
714// Remove eliminates a node at the specified path
715func (n *node) Remove(path string, txid string, makeBranch MakeBranchFunction) Revision {
716 log.Debugw("node-remove-request", log.Fields{"path": path, "txid": txid, "makeBranch": makeBranch})
717
718 for strings.HasPrefix(path, "/") {
719 path = path[1:]
720 }
721 if path == "" {
722 // TODO raise error
723 log.Errorf("cannot remove for non-container mode")
724 }
725 var branch *Branch
726 if txid == "" {
727 branch = n.GetBranch(NONE)
728 } else if branch = n.GetBranch(txid); branch == nil {
729 branch = makeBranch(n)
730 }
731
732 rev := branch.GetLatest()
733
734 partition := strings.SplitN(path, "/", 2)
735 name := partition[0]
736 if len(partition) < 2 {
737 path = ""
738 } else {
739 path = partition[1]
740 }
741
742 field := ChildrenFields(n.Type)[name]
743 var children []Revision
744 postAnnouncement := []ChangeTuple{}
745
746 if field.IsContainer {
747 if path == "" {
748 log.Errorw("cannot-remove-without-key", log.Fields{"name": name, "key": path})
749 } else if field.Key != "" {
750 partition := strings.SplitN(path, "/", 2)
751 key := partition[0]
752 if len(partition) < 2 {
753 path = ""
754 } else {
755 path = partition[1]
756 }
757
758 keyValue := field.KeyFromStr(key)
759 children = make([]Revision, len(rev.GetChildren(name)))
760 copy(children, rev.GetChildren(name))
761
762 if path != "" {
Matt Jeanneret384d8c92019-05-06 14:27:31 -0400763 if idx, childRev := n.findRevByKey(children, field.Key, keyValue); childRev != nil {
764 childNode := childRev.GetNode()
765 if childNode.Proxy == nil {
766 childNode.Proxy = n.Proxy
767 }
768 newChildRev := childNode.Remove(path, txid, makeBranch)
769
manikkaraj k9eb6cac2019-05-09 12:32:03 -0400770 branch.LatestLock.Lock()
771 defer branch.LatestLock.Unlock()
772
Matt Jeanneret384d8c92019-05-06 14:27:31 -0400773 if idx >= 0 {
774 children[idx] = newChildRev
775 } else {
776 children = append(children, newChildRev)
777 }
778
Matt Jeanneret384d8c92019-05-06 14:27:31 -0400779 rev.SetChildren(name, children)
780 branch.GetLatest().Drop(txid, false)
781 n.makeLatest(branch, rev, nil)
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400782 }
Matt Jeanneret384d8c92019-05-06 14:27:31 -0400783 return branch.GetLatest()
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400784 }
785
Matt Jeanneret384d8c92019-05-06 14:27:31 -0400786 if idx, childRev := n.findRevByKey(children, field.Key, keyValue); childRev != nil && idx >= 0 {
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400787 if n.GetProxy() != nil {
788 data := childRev.GetData()
789 n.GetProxy().InvokeCallbacks(PRE_REMOVE, false, data)
790 postAnnouncement = append(postAnnouncement, ChangeTuple{POST_REMOVE, data, nil})
791 } else {
792 postAnnouncement = append(postAnnouncement, ChangeTuple{POST_REMOVE, childRev.GetData(), nil})
793 }
794
795 childRev.StorageDrop(txid, true)
manikkaraj k9eb6cac2019-05-09 12:32:03 -0400796 GetRevCache().Cache.Delete(childRev.GetName())
Matt Jeanneret384d8c92019-05-06 14:27:31 -0400797
798 branch.LatestLock.Lock()
799 defer branch.LatestLock.Unlock()
800
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400801 children = append(children[:idx], children[idx+1:]...)
802 rev.SetChildren(name, children)
803
804 branch.GetLatest().Drop(txid, false)
805 n.makeLatest(branch, rev, postAnnouncement)
806
807 return rev
808 } else {
809 log.Errorw("failed-to-find-revision", log.Fields{"name": name, "key": keyValue.(string)})
810 }
811 }
812 log.Errorw("cannot-add-to-non-keyed-container", log.Fields{"name": name, "path": path, "fieldKey": field.Key})
813
814 } else {
815 log.Errorw("cannot-add-to-non-container-field", log.Fields{"name": name, "path": path})
816 }
817
818 return nil
819}
820
821// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Branching ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
822
823// MakeBranchFunction is a type for function references intented to create a branch
824type MakeBranchFunction func(*node) *Branch
825
826// MakeBranch creates a new branch for the provided transaction id
827func (n *node) MakeBranch(txid string) *Branch {
828 branchPoint := n.GetBranch(NONE).GetLatest()
829 branch := NewBranch(n, txid, branchPoint, true)
830 n.SetBranch(txid, branch)
831 return branch
832}
833
834// DeleteBranch removes a branch with the specified id
835func (n *node) DeleteBranch(txid string) {
836 n.Lock()
837 defer n.Unlock()
838 delete(n.Branches, txid)
839}
840
841func (n *node) mergeChild(txid string, dryRun bool) func(Revision) Revision {
842 f := func(rev Revision) Revision {
843 childBranch := rev.GetBranch()
844
845 if childBranch.Txid == txid {
846 rev, _ = childBranch.Node.MergeBranch(txid, dryRun)
847 }
848
849 return rev
850 }
851 return f
852}
853
854// MergeBranch will integrate the contents of a transaction branch within the latest branch of a given node
855func (n *node) MergeBranch(txid string, dryRun bool) (Revision, error) {
856 srcBranch := n.GetBranch(txid)
857 dstBranch := n.GetBranch(NONE)
858
859 forkRev := srcBranch.Origin
860 srcRev := srcBranch.GetLatest()
861 dstRev := dstBranch.GetLatest()
862
863 rev, changes := Merge3Way(forkRev, srcRev, dstRev, n.mergeChild(txid, dryRun), dryRun)
864
865 if !dryRun {
866 if rev != nil {
867 rev.SetName(dstRev.GetName())
868 n.makeLatest(dstBranch, rev, changes)
869 }
870 n.DeleteBranch(txid)
871 }
872
873 // TODO: return proper error when one occurs
874 return rev, nil
875}
876
877// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Diff utility ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
878
879//func (n *node) diff(hash1, hash2, txid string) {
880// branch := n.Branches[txid]
881// rev1 := branch.GetHash(hash1)
882// rev2 := branch.GetHash(hash2)
883//
884// if rev1.GetHash() == rev2.GetHash() {
885// // empty patch
886// } else {
887// // translate data to json and generate patch
888// patch, err := jsonpatch.MakePatch(rev1.GetData(), rev2.GetData())
889// patch.
890// }
891//}
892
893// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Tag utility ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
894
895// TODO: is tag mgmt used in the python implementation? Need to validate
896
897// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Internals ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
898
899func (n *node) hasChildren(data interface{}) bool {
900 for fieldName, field := range ChildrenFields(n.Type) {
901 _, fieldValue := GetAttributeValue(data, fieldName, 0)
902
903 if (field.IsContainer && fieldValue.Len() > 0) || !fieldValue.IsNil() {
904 log.Error("cannot update external children")
905 return true
906 }
907 }
908
909 return false
910}
911
912// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ node Proxy ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
913
914// CreateProxy returns a reference to a sub-tree of the data model
915func (n *node) CreateProxy(path string, exclusive bool) *Proxy {
916 return n.createProxy(path, path, n, exclusive)
917}
918
919func (n *node) createProxy(path string, fullPath string, parentNode *node, exclusive bool) *Proxy {
920 for strings.HasPrefix(path, "/") {
921 path = path[1:]
922 }
923 if path == "" {
924 return n.makeProxy(path, fullPath, parentNode, exclusive)
925 }
926
927 rev := n.GetBranch(NONE).GetLatest()
928 partition := strings.SplitN(path, "/", 2)
929 name := partition[0]
930 if len(partition) < 2 {
931 path = ""
932 } else {
933 path = partition[1]
934 }
935
936 field := ChildrenFields(n.Type)[name]
937 if field.IsContainer {
938 if path == "" {
939 //log.Error("cannot proxy a container field")
940 newNode := n.MakeNode(reflect.New(field.ClassType.Elem()).Interface(), "")
941 return newNode.makeProxy(path, fullPath, parentNode, exclusive)
942 } else if field.Key != "" {
943 partition := strings.SplitN(path, "/", 2)
944 key := partition[0]
945 if len(partition) < 2 {
946 path = ""
947 } else {
948 path = partition[1]
949 }
950 keyValue := field.KeyFromStr(key)
951 var children []Revision
952 children = make([]Revision, len(rev.GetChildren(name)))
953 copy(children, rev.GetChildren(name))
954 if _, childRev := n.findRevByKey(children, field.Key, keyValue); childRev != nil {
955 childNode := childRev.GetNode()
956 return childNode.createProxy(path, fullPath, n, exclusive)
957 }
958 } else {
959 log.Error("cannot index into container with no keys")
960 }
961 } else {
962 childRev := rev.GetChildren(name)[0]
963 childNode := childRev.GetNode()
964 return childNode.createProxy(path, fullPath, n, exclusive)
965 }
966
967 log.Warnf("Cannot create proxy - latest rev:%s, all revs:%+v", rev.GetHash(), n.GetBranch(NONE).Revisions)
968 return nil
969}
970
971func (n *node) makeProxy(path string, fullPath string, parentNode *node, exclusive bool) *Proxy {
972 n.Lock()
973 defer n.Unlock()
974 r := &root{
975 node: n,
976 Callbacks: n.Root.GetCallbacks(),
977 NotificationCallbacks: n.Root.GetNotificationCallbacks(),
978 DirtyNodes: n.Root.DirtyNodes,
979 KvStore: n.Root.KvStore,
980 Loading: n.Root.Loading,
981 RevisionClass: n.Root.RevisionClass,
982 }
983
984 if n.Proxy == nil {
985 n.Proxy = NewProxy(r, n, parentNode, path, fullPath, exclusive)
986 } else {
987 if n.Proxy.Exclusive {
988 log.Error("node is already owned exclusively")
989 }
990 }
991
992 return n.Proxy
993}
994
995func (n *node) makeEventBus() *EventBus {
996 n.Lock()
997 defer n.Unlock()
998 if n.EventBus == nil {
999 n.EventBus = NewEventBus()
1000 }
1001 return n.EventBus
1002}
1003
1004func (n *node) SetProxy(proxy *Proxy) {
1005 n.Lock()
1006 defer n.Unlock()
1007 n.Proxy = proxy
1008}
1009
1010func (n *node) GetProxy() *Proxy {
1011 n.Lock()
1012 defer n.Unlock()
1013 return n.Proxy
1014}
1015
1016func (n *node) GetBranch(key string) *Branch {
1017 n.Lock()
1018 defer n.Unlock()
1019
1020 if n.Branches != nil {
1021 if branch, exists := n.Branches[key]; exists {
1022 return branch
1023 }
1024 }
1025 return nil
1026}
1027
1028func (n *node) SetBranch(key string, branch *Branch) {
1029 n.Lock()
1030 defer n.Unlock()
1031 n.Branches[key] = branch
1032}
1033
1034func (n *node) GetRoot() *root {
1035 n.Lock()
1036 defer n.Unlock()
1037 return n.Root
1038}