blob: a05fbdd4e609affa40d8caf39da77b3f4519dc92 [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 Barbarie4a2564d2018-07-26 11:02:58 -040022 "github.com/google/uuid"
khenaidoob9203542018-09-17 22:56:37 -040023 "github.com/opencord/voltha-go/common/log"
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040024 "reflect"
Stephane Barbariedc5022d2018-11-19 15:21:44 -050025 "sync"
Stephane Barbarieec0919b2018-09-05 14:14:29 -040026 "time"
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 Load(rootClass interface{}) *root
33
34 ExecuteCallbacks()
35 AddCallback(callback CallbackFunction, args ...interface{})
36 AddNotificationCallback(callback CallbackFunction, args ...interface{})
Stephane Barbarie06c4a742018-10-01 11:09:32 -040037}
38
Stephane Barbariedc5022d2018-11-19 15:21:44 -050039// root points to the top of the data model tree or sub-tree identified by a proxy
Stephane Barbarie06c4a742018-10-01 11:09:32 -040040type root struct {
Stephane Barbarie126101e2018-10-11 16:18:48 -040041 *node
42
43 Callbacks []CallbackTuple
44 NotificationCallbacks []CallbackTuple
Stephane Barbarie06c4a742018-10-01 11:09:32 -040045
Stephane Barbariedc5022d2018-11-19 15:21:44 -050046 DirtyNodes map[string][]*node
47 KvStore *Backend
48 Loading bool
49 RevisionClass interface{}
50
51 mutex sync.RWMutex
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040052}
53
Stephane Barbariedc5022d2018-11-19 15:21:44 -050054// NewRoot creates an new instance of a root object
Stephane Barbarie06c4a742018-10-01 11:09:32 -040055func NewRoot(initialData interface{}, kvStore *Backend) *root {
56 root := &root{}
Stephane Barbarie126101e2018-10-11 16:18:48 -040057
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040058 root.KvStore = kvStore
Stephane Barbarie06c4a742018-10-01 11:09:32 -040059 root.DirtyNodes = make(map[string][]*node)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040060 root.Loading = false
Stephane Barbarie06c4a742018-10-01 11:09:32 -040061
Stephane Barbariedc5022d2018-11-19 15:21:44 -050062 // If there is no storage in place just revert to
63 // a non persistent mechanism
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -040064 if kvStore != nil {
65 root.RevisionClass = reflect.TypeOf(PersistedRevision{})
66 } else {
67 root.RevisionClass = reflect.TypeOf(NonPersistedRevision{})
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040068 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -050069
Stephane Barbarie694e2b92018-09-07 12:17:36 -040070 root.Callbacks = []CallbackTuple{}
71 root.NotificationCallbacks = []CallbackTuple{}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040072
Stephane Barbariedc5022d2018-11-19 15:21:44 -050073 root.node = NewNode(root, initialData, false, "")
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040074
75 return root
76}
77
Stephane Barbariedc5022d2018-11-19 15:21:44 -050078// MakeTxBranch creates a new transaction branch
Stephane Barbarie06c4a742018-10-01 11:09:32 -040079func (r *root) MakeTxBranch() string {
Stephane Barbariedc5022d2018-11-19 15:21:44 -050080 txidBin, _ := uuid.New().MarshalBinary()
81 txid := hex.EncodeToString(txidBin)[:12]
82
Stephane Barbarie126101e2018-10-11 16:18:48 -040083 r.DirtyNodes[txid] = []*node{r.node}
Stephane Barbarie06c4a742018-10-01 11:09:32 -040084 r.node.MakeBranch(txid)
Stephane Barbariedc5022d2018-11-19 15:21:44 -050085
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040086 return txid
87}
88
Stephane Barbariedc5022d2018-11-19 15:21:44 -050089// DeleteTxBranch removes a transaction branch
Stephane Barbarie06c4a742018-10-01 11:09:32 -040090func (r *root) DeleteTxBranch(txid string) {
Stephane Barbariee16186c2018-09-11 10:46:34 -040091 for _, dirtyNode := range r.DirtyNodes[txid] {
Stephane Barbarie06c4a742018-10-01 11:09:32 -040092 dirtyNode.DeleteBranch(txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040093 }
94 delete(r.DirtyNodes, txid)
95}
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 Barbarieec0919b2018-09-05 14:14:29 -0400107 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400108}
109
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500110// ExecuteCallbacks will invoke all the callbacks linked to root object
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400111func (r *root) ExecuteCallbacks() {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500112 r.mutex.Lock()
113 log.Debugf("ExecuteCallbacks has the ROOT lock : %+v", r)
114 defer r.mutex.Unlock()
115 defer log.Debugf("ExecuteCallbacks released the ROOT lock : %+v", r)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400116 for len(r.Callbacks) > 0 {
117 callback := r.Callbacks[0]
118 r.Callbacks = r.Callbacks[1:]
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400119 callback.Execute(nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400120 }
121 for len(r.NotificationCallbacks) > 0 {
122 callback := r.NotificationCallbacks[0]
123 r.NotificationCallbacks = r.NotificationCallbacks[1:]
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400124 callback.Execute(nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400125 }
126}
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()
135 log.Debugf("getCallbacks has the ROOT lock : %+v", r)
136 defer r.mutex.Unlock()
137 defer log.Debugf("getCallbacks released the ROOT lock : %+v", r)
138 return r.Callbacks
139}
140
141// getCallbacks returns the available notification callbacks
142func (r *root) GetNotificationCallbacks() []CallbackTuple {
143 r.mutex.Lock()
144 log.Debugf("GetNotificationCallbacks has the ROOT lock : %+v", r)
145 defer r.mutex.Unlock()
146 defer log.Debugf("GetNotificationCallbacks released the ROOT lock : %+v", r)
147 return r.NotificationCallbacks
148}
149
150// AddCallback inserts a new callback with its arguments
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400151func (r *root) AddCallback(callback CallbackFunction, args ...interface{}) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500152 r.mutex.Lock()
153 log.Debugf("AddCallback has the ROOT lock : %+v", r)
154 defer r.mutex.Unlock()
155 defer log.Debugf("AddCallback released the ROOT lock : %+v", r)
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400156 r.Callbacks = append(r.Callbacks, CallbackTuple{callback, args})
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400157}
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500158
159// AddNotificationCallback inserts a new notification callback with its arguments
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400160func (r *root) AddNotificationCallback(callback CallbackFunction, args ...interface{}) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500161 r.mutex.Lock()
162 log.Debugf("AddNotificationCallback has the ROOT lock : %+v", r)
163 defer r.mutex.Unlock()
164 defer log.Debugf("AddNotificationCallback released the ROOT lock : %+v", r)
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400165 r.NotificationCallbacks = append(r.NotificationCallbacks, CallbackTuple{callback, args})
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400166}
167
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500168// Update modifies the content of an object at a given path with the provided data
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400169func (r *root) Update(path string, data interface{}, strict bool, txid string, makeBranch MakeBranchFunction) Revision {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400170 var result Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400171
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500172 if makeBranch != nil {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400173 // TODO: raise error
174 }
175
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500176 if r.hasCallbacks() {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400177 // TODO: raise error
178 }
179
180 if txid != "" {
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400181 trackDirty := func(node *node) *Branch {
Stephane Barbariee16186c2018-09-11 10:46:34 -0400182 r.DirtyNodes[txid] = append(r.DirtyNodes[txid], node)
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400183 return node.MakeBranch(txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400184 }
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400185 result = r.node.Update(path, data, strict, txid, trackDirty)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400186 } else {
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400187 result = r.node.Update(path, data, strict, "", nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400188 }
189
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500190 r.node.GetRoot().ExecuteCallbacks()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400191
192 return result
193}
194
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500195// Add creates a new object at the given path with the provided data
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400196func (r *root) Add(path string, data interface{}, txid string, makeBranch MakeBranchFunction) Revision {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400197 var result Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400198
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500199 if makeBranch != nil {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400200 // TODO: raise error
201 }
202
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500203 if r.hasCallbacks() {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400204 // TODO: raise error
205 }
206
207 if txid != "" {
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400208 trackDirty := func(node *node) *Branch {
Stephane Barbariee16186c2018-09-11 10:46:34 -0400209 r.DirtyNodes[txid] = append(r.DirtyNodes[txid], node)
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400210 return node.MakeBranch(txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400211 }
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400212 result = r.node.Add(path, data, txid, trackDirty)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400213 } else {
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400214 result = r.node.Add(path, data, "", nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400215 }
216
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500217 r.node.GetRoot().ExecuteCallbacks()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400218
219 return result
220}
221
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500222// Remove discards an object at a given path
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400223func (r *root) Remove(path string, txid string, makeBranch MakeBranchFunction) Revision {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400224 var result Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400225
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500226 if makeBranch != nil {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400227 // TODO: raise error
228 }
229
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500230 if r.hasCallbacks() {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400231 // TODO: raise error
232 }
233
234 if txid != "" {
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400235 trackDirty := func(node *node) *Branch {
Stephane Barbariee16186c2018-09-11 10:46:34 -0400236 r.DirtyNodes[txid] = append(r.DirtyNodes[txid], node)
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400237 return node.MakeBranch(txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400238 }
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400239 result = r.node.Remove(path, txid, trackDirty)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400240 } else {
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400241 result = r.node.Remove(path, "", nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400242 }
243
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500244 r.node.GetRoot().ExecuteCallbacks()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400245
246 return result
247}
248
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500249// Load retrieves data from a persistent storage
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400250func (r *root) Load(rootClass interface{}) *root {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400251 //fakeKvStore := &Backend{}
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400252 //root := NewRoot(rootClass, nil)
253 //root.KvStore = r.KvStore
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500254 r.loadFromPersistence(rootClass)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400255 return r
256}
257
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500258// MakeLatest updates a branch with the latest node revision
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400259func (r *root) MakeLatest(branch *Branch, revision Revision, changeAnnouncement []ChangeTuple) {
260 r.makeLatest(branch, revision, changeAnnouncement)
261}
262
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500263func (r *root) MakeRevision(branch *Branch, data interface{}, children map[string][]Revision) Revision {
264 if r.RevisionClass.(reflect.Type) == reflect.TypeOf(PersistedRevision{}) {
265 return NewPersistedRevision(branch, data, children)
266 }
267
268 return NewNonPersistedRevision(r, branch, data, children)
269}
270
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400271func (r *root) makeLatest(branch *Branch, revision Revision, changeAnnouncement []ChangeTuple) {
272 r.node.makeLatest(branch, revision, changeAnnouncement)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400273
274 if r.KvStore != nil && branch.Txid == "" {
275 tags := make(map[string]string)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500276 for k, v := range r.node.Tags {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400277 tags[k] = v.GetHash()
278 }
279 data := &rootData{
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500280 Latest: branch.GetLatest().GetHash(),
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400281 Tags: tags,
282 }
283 if blob, err := json.Marshal(data); err != nil {
284 // TODO report error
285 } else {
286 log.Debugf("Changing root to : %s", string(blob))
287 if err := r.KvStore.Put("root", blob); err != nil {
288 log.Errorf("failed to properly put value in kvstore - err: %s", err.Error())
289 }
290 }
291 }
292}
293
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400294type rootData struct {
khenaidoob9203542018-09-17 22:56:37 -0400295 Latest string `json:latest`
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400296 Tags map[string]string `json:tags`
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400297}
298
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500299func (r *root) loadFromPersistence(rootClass interface{}) {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400300 var data rootData
301
302 r.Loading = true
303 blob, _ := r.KvStore.Get("root")
304
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400305 start := time.Now()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400306 if err := json.Unmarshal(blob.Value.([]byte), &data); err != nil {
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -0400307 log.Errorf("problem to unmarshal blob - error:%s\n", err.Error())
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400308 }
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400309 stop := time.Now()
310 GetProfiling().AddToInMemoryModelTime(stop.Sub(start).Seconds())
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400311 for tag, hash := range data.Tags {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500312 r.node.LoadLatest(hash)
313 r.node.Tags[tag] = r.node.Latest()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400314 }
315
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500316 r.node.LoadLatest(data.Latest)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400317 r.Loading = false
318}