blob: 5036ce14ac46098086a4b3cff324be057b92ee72 [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
19import (
20 "encoding/hex"
21 "encoding/json"
Stephane Barbarie1ab43272018-12-08 21:42:13 -050022 "github.com/golang/protobuf/proto"
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040023 "github.com/google/uuid"
khenaidoob9203542018-09-17 22:56:37 -040024 "github.com/opencord/voltha-go/common/log"
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040025 "reflect"
Stephane Barbariedc5022d2018-11-19 15:21:44 -050026 "sync"
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040027)
28
Stephane Barbariedc5022d2018-11-19 15:21:44 -050029// Root is used to provide an abstraction to the base root structure
Stephane Barbarie06c4a742018-10-01 11:09:32 -040030type Root interface {
31 Node
Stephane Barbariedc5022d2018-11-19 15:21:44 -050032
33 ExecuteCallbacks()
34 AddCallback(callback CallbackFunction, args ...interface{})
35 AddNotificationCallback(callback CallbackFunction, args ...interface{})
Stephane Barbarie06c4a742018-10-01 11:09:32 -040036}
37
Stephane Barbariedc5022d2018-11-19 15:21:44 -050038// root points to the top of the data model tree or sub-tree identified by a proxy
Stephane Barbarie06c4a742018-10-01 11:09:32 -040039type root struct {
Stephane Barbarie126101e2018-10-11 16:18:48 -040040 *node
41
42 Callbacks []CallbackTuple
43 NotificationCallbacks []CallbackTuple
Stephane Barbarie06c4a742018-10-01 11:09:32 -040044
Stephane Barbariedc5022d2018-11-19 15:21:44 -050045 DirtyNodes map[string][]*node
46 KvStore *Backend
47 Loading bool
48 RevisionClass interface{}
49
khenaidoo43c82122018-11-22 18:38:28 -050050 mutex sync.RWMutex
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040051}
52
Stephane Barbariedc5022d2018-11-19 15:21:44 -050053// NewRoot creates an new instance of a root object
Stephane Barbarie06c4a742018-10-01 11:09:32 -040054func NewRoot(initialData interface{}, kvStore *Backend) *root {
55 root := &root{}
Stephane Barbarie126101e2018-10-11 16:18:48 -040056
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040057 root.KvStore = kvStore
Stephane Barbarie06c4a742018-10-01 11:09:32 -040058 root.DirtyNodes = make(map[string][]*node)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040059 root.Loading = false
Stephane Barbarie06c4a742018-10-01 11:09:32 -040060
Stephane Barbariedc5022d2018-11-19 15:21:44 -050061 // If there is no storage in place just revert to
62 // a non persistent mechanism
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -040063 if kvStore != nil {
64 root.RevisionClass = reflect.TypeOf(PersistedRevision{})
65 } else {
66 root.RevisionClass = reflect.TypeOf(NonPersistedRevision{})
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040067 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -050068
Stephane Barbarie694e2b92018-09-07 12:17:36 -040069 root.Callbacks = []CallbackTuple{}
70 root.NotificationCallbacks = []CallbackTuple{}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040071
Stephane Barbariedc5022d2018-11-19 15:21:44 -050072 root.node = NewNode(root, initialData, false, "")
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040073
74 return root
75}
76
Stephane Barbariedc5022d2018-11-19 15:21:44 -050077// MakeTxBranch creates a new transaction branch
Stephane Barbarie06c4a742018-10-01 11:09:32 -040078func (r *root) MakeTxBranch() string {
Stephane Barbariedc5022d2018-11-19 15:21:44 -050079 txidBin, _ := uuid.New().MarshalBinary()
80 txid := hex.EncodeToString(txidBin)[:12]
81
Stephane Barbarie126101e2018-10-11 16:18:48 -040082 r.DirtyNodes[txid] = []*node{r.node}
Stephane Barbarie06c4a742018-10-01 11:09:32 -040083 r.node.MakeBranch(txid)
Stephane Barbariedc5022d2018-11-19 15:21:44 -050084
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040085 return txid
86}
87
Stephane Barbariedc5022d2018-11-19 15:21:44 -050088// DeleteTxBranch removes a transaction branch
Stephane Barbarie06c4a742018-10-01 11:09:32 -040089func (r *root) DeleteTxBranch(txid string) {
Stephane Barbariee16186c2018-09-11 10:46:34 -040090 for _, dirtyNode := range r.DirtyNodes[txid] {
Stephane Barbarie06c4a742018-10-01 11:09:32 -040091 dirtyNode.DeleteBranch(txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040092 }
93 delete(r.DirtyNodes, txid)
Stephane Barbariec92d1072019-06-07 16:21:49 -040094 r.node.DeleteBranch(txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040095}
96
Stephane Barbariedc5022d2018-11-19 15:21:44 -050097// FoldTxBranch will merge the contents of a transaction branch with the root object
Stephane Barbarie06c4a742018-10-01 11:09:32 -040098func (r *root) FoldTxBranch(txid string) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -050099 // Start by doing a dry run of the merge
100 // If that fails, it bails out and the branch is deleted
101 if _, err := r.node.MergeBranch(txid, true); err != nil {
102 // Merge operation fails
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400103 r.DeleteTxBranch(txid)
104 } else {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500105 r.node.MergeBranch(txid, false)
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400106 r.ExecuteCallbacks()
Stephane Barbarie260a5632019-02-26 16:12:49 -0500107 r.DeleteTxBranch(txid)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400108 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400109}
110
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500111// ExecuteCallbacks will invoke all the callbacks linked to root object
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400112func (r *root) ExecuteCallbacks() {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500113 r.mutex.Lock()
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500114 defer r.mutex.Unlock()
Stephane Barbariec92d1072019-06-07 16:21:49 -0400115
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400116 for len(r.Callbacks) > 0 {
117 callback := r.Callbacks[0]
118 r.Callbacks = r.Callbacks[1:]
khenaidoo43c82122018-11-22 18:38:28 -0500119 go callback.Execute(nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400120 }
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400121 //for len(r.NotificationCallbacks) > 0 {
122 // callback := r.NotificationCallbacks[0]
123 // r.NotificationCallbacks = r.NotificationCallbacks[1:]
124 // go callback.Execute(nil)
125 //}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400126}
127
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500128func (r *root) hasCallbacks() bool {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400129 return len(r.Callbacks) == 0
130}
131
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500132// getCallbacks returns the available callbacks
133func (r *root) GetCallbacks() []CallbackTuple {
134 r.mutex.Lock()
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500135 defer r.mutex.Unlock()
Stephane Barbariec92d1072019-06-07 16:21:49 -0400136
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500137 return r.Callbacks
138}
139
140// getCallbacks returns the available notification callbacks
141func (r *root) GetNotificationCallbacks() []CallbackTuple {
142 r.mutex.Lock()
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500143 defer r.mutex.Unlock()
Stephane Barbariec92d1072019-06-07 16:21:49 -0400144
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500145 return r.NotificationCallbacks
146}
147
148// AddCallback inserts a new callback with its arguments
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400149func (r *root) AddCallback(callback CallbackFunction, args ...interface{}) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500150 r.mutex.Lock()
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500151 defer r.mutex.Unlock()
Stephane Barbariec92d1072019-06-07 16:21:49 -0400152
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400153 r.Callbacks = append(r.Callbacks, CallbackTuple{callback, args})
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400154}
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500155
156// AddNotificationCallback inserts a new notification callback with its arguments
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400157func (r *root) AddNotificationCallback(callback CallbackFunction, args ...interface{}) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500158 r.mutex.Lock()
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500159 defer r.mutex.Unlock()
Stephane Barbariec92d1072019-06-07 16:21:49 -0400160
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400161 r.NotificationCallbacks = append(r.NotificationCallbacks, CallbackTuple{callback, args})
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400162}
163
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500164func (r *root) syncParent(childRev Revision, txid string) {
165 data := proto.Clone(r.Proxy.ParentNode.Latest().GetData().(proto.Message))
166
167 for fieldName, _ := range ChildrenFields(data) {
168 childDataName, childDataHolder := GetAttributeValue(data, fieldName, 0)
169 if reflect.TypeOf(childRev.GetData()) == reflect.TypeOf(childDataHolder.Interface()) {
170 childDataHolder = reflect.ValueOf(childRev.GetData())
171 reflect.ValueOf(data).Elem().FieldByName(childDataName).Set(childDataHolder)
172 }
173 }
174
175 r.Proxy.ParentNode.Latest().SetConfig(NewDataRevision(r.Proxy.ParentNode.Root, data))
176 r.Proxy.ParentNode.Latest(txid).Finalize(false)
177}
178
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500179// Update modifies the content of an object at a given path with the provided data
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400180func (r *root) Update(path string, data interface{}, strict bool, txid string, makeBranch MakeBranchFunction) Revision {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400181 var result Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400182
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500183 if makeBranch != nil {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400184 // TODO: raise error
185 }
186
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500187 if r.hasCallbacks() {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400188 // TODO: raise error
189 }
190
191 if txid != "" {
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400192 trackDirty := func(node *node) *Branch {
Stephane Barbariee16186c2018-09-11 10:46:34 -0400193 r.DirtyNodes[txid] = append(r.DirtyNodes[txid], node)
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400194 return node.MakeBranch(txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400195 }
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400196 result = r.node.Update(path, data, strict, txid, trackDirty)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400197 } else {
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400198 result = r.node.Update(path, data, strict, "", nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400199 }
200
Stephane Barbariee0a4c792019-01-16 11:26:29 -0500201 if result != nil {
202 if r.Proxy.FullPath != r.Proxy.Path {
203 r.syncParent(result, txid)
204 } else {
205 result.Finalize(false)
206 }
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500207 }
208
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500209 r.node.GetRoot().ExecuteCallbacks()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400210
211 return result
212}
213
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500214// Add creates a new object at the given path with the provided data
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400215func (r *root) Add(path string, data interface{}, txid string, makeBranch MakeBranchFunction) Revision {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400216 var result Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400217
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500218 if makeBranch != nil {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400219 // TODO: raise error
220 }
221
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500222 if r.hasCallbacks() {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400223 // TODO: raise error
224 }
225
226 if txid != "" {
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400227 trackDirty := func(node *node) *Branch {
Stephane Barbariee16186c2018-09-11 10:46:34 -0400228 r.DirtyNodes[txid] = append(r.DirtyNodes[txid], node)
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400229 return node.MakeBranch(txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400230 }
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400231 result = r.node.Add(path, data, txid, trackDirty)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400232 } else {
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400233 result = r.node.Add(path, data, "", nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400234 }
235
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500236 if result != nil {
237 result.Finalize(true)
238 r.node.GetRoot().ExecuteCallbacks()
239 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400240 return result
241}
242
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500243// Remove discards an object at a given path
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400244func (r *root) Remove(path string, txid string, makeBranch MakeBranchFunction) Revision {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400245 var result Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400246
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500247 if makeBranch != nil {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400248 // TODO: raise error
249 }
250
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500251 if r.hasCallbacks() {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400252 // TODO: raise error
253 }
254
255 if txid != "" {
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400256 trackDirty := func(node *node) *Branch {
Stephane Barbariee16186c2018-09-11 10:46:34 -0400257 r.DirtyNodes[txid] = append(r.DirtyNodes[txid], node)
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400258 return node.MakeBranch(txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400259 }
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400260 result = r.node.Remove(path, txid, trackDirty)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400261 } else {
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400262 result = r.node.Remove(path, "", nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400263 }
264
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500265 r.node.GetRoot().ExecuteCallbacks()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400266
267 return result
268}
269
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500270// MakeLatest updates a branch with the latest node revision
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400271func (r *root) MakeLatest(branch *Branch, revision Revision, changeAnnouncement []ChangeTuple) {
272 r.makeLatest(branch, revision, changeAnnouncement)
273}
274
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500275func (r *root) MakeRevision(branch *Branch, data interface{}, children map[string][]Revision) Revision {
276 if r.RevisionClass.(reflect.Type) == reflect.TypeOf(PersistedRevision{}) {
277 return NewPersistedRevision(branch, data, children)
278 }
279
280 return NewNonPersistedRevision(r, branch, data, children)
281}
282
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400283func (r *root) makeLatest(branch *Branch, revision Revision, changeAnnouncement []ChangeTuple) {
284 r.node.makeLatest(branch, revision, changeAnnouncement)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400285
286 if r.KvStore != nil && branch.Txid == "" {
287 tags := make(map[string]string)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500288 for k, v := range r.node.Tags {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400289 tags[k] = v.GetHash()
290 }
291 data := &rootData{
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500292 Latest: branch.GetLatest().GetHash(),
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400293 Tags: tags,
294 }
295 if blob, err := json.Marshal(data); err != nil {
296 // TODO report error
297 } else {
298 log.Debugf("Changing root to : %s", string(blob))
299 if err := r.KvStore.Put("root", blob); err != nil {
300 log.Errorf("failed to properly put value in kvstore - err: %s", err.Error())
301 }
302 }
303 }
304}
305
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400306type rootData struct {
Kent Hagerman0ab4cb22019-04-24 13:13:35 -0400307 Latest string `json:"latest"`
308 Tags map[string]string `json:"tags"`
309}