blob: a6c62c63996c10e8e6ab7ed7cf959a9121ef45c1 [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 Barbarie4a2564d2018-07-26 11:02:58 -040016package model
17
18import (
19 "encoding/hex"
20 "encoding/json"
21 "fmt"
22 "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 Barbarieec0919b2018-09-05 14:14:29 -040025 "time"
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040026)
27
Stephane Barbarie06c4a742018-10-01 11:09:32 -040028type Root interface {
29 Node
30}
31
32type root struct {
33 node
34
35 DirtyNodes map[string][]*node
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040036 KvStore *Backend
37 Loading bool
38 RevisionClass interface{}
Stephane Barbarie694e2b92018-09-07 12:17:36 -040039 Callbacks []CallbackTuple
40 NotificationCallbacks []CallbackTuple
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040041}
42
Stephane Barbarie06c4a742018-10-01 11:09:32 -040043func NewRoot(initialData interface{}, kvStore *Backend) *root {
44 root := &root{}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040045 root.KvStore = kvStore
Stephane Barbarie06c4a742018-10-01 11:09:32 -040046 root.DirtyNodes = make(map[string][]*node)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040047 root.Loading = false
Stephane Barbarie06c4a742018-10-01 11:09:32 -040048
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -040049 if kvStore != nil {
50 root.RevisionClass = reflect.TypeOf(PersistedRevision{})
51 } else {
52 root.RevisionClass = reflect.TypeOf(NonPersistedRevision{})
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040053 }
Stephane Barbarie694e2b92018-09-07 12:17:36 -040054 root.Callbacks = []CallbackTuple{}
55 root.NotificationCallbacks = []CallbackTuple{}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040056
Stephane Barbarie06c4a742018-10-01 11:09:32 -040057 root.node = *NewNode(root, initialData, false, "")
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040058
59 return root
60}
61
Stephane Barbarie06c4a742018-10-01 11:09:32 -040062func (r *root) MakeRevision(branch *Branch, data interface{}, children map[string][]Revision) Revision {
Stephane Barbarieec0919b2018-09-05 14:14:29 -040063 if r.RevisionClass.(reflect.Type) == reflect.TypeOf(PersistedRevision{}) {
64 return NewPersistedRevision(branch, data, children)
65 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040066
Stephane Barbarieec0919b2018-09-05 14:14:29 -040067 return NewNonPersistedRevision(branch, data, children)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040068}
69
Stephane Barbarie06c4a742018-10-01 11:09:32 -040070func (r *root) MakeTxBranch() string {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040071 txid_bin, _ := uuid.New().MarshalBinary()
72 txid := hex.EncodeToString(txid_bin)[:12]
Stephane Barbarie06c4a742018-10-01 11:09:32 -040073 r.DirtyNodes[txid] = []*node{&r.node}
74 r.node.MakeBranch(txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040075 return txid
76}
77
Stephane Barbarie06c4a742018-10-01 11:09:32 -040078func (r *root) DeleteTxBranch(txid string) {
Stephane Barbariee16186c2018-09-11 10:46:34 -040079 for _, dirtyNode := range r.DirtyNodes[txid] {
Stephane Barbarie06c4a742018-10-01 11:09:32 -040080 dirtyNode.DeleteBranch(txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040081 }
82 delete(r.DirtyNodes, txid)
83}
84
Stephane Barbarie06c4a742018-10-01 11:09:32 -040085func (r *root) FoldTxBranch(txid string) {
86 if _, err := r.MergeBranch(txid, true); err != nil {
Stephane Barbarieec0919b2018-09-05 14:14:29 -040087 r.DeleteTxBranch(txid)
88 } else {
Stephane Barbarie06c4a742018-10-01 11:09:32 -040089 r.MergeBranch(txid, false)
90 r.ExecuteCallbacks()
Stephane Barbarieec0919b2018-09-05 14:14:29 -040091 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040092}
93
Stephane Barbarie06c4a742018-10-01 11:09:32 -040094func (r *root) ExecuteCallbacks() {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040095 for len(r.Callbacks) > 0 {
96 callback := r.Callbacks[0]
97 r.Callbacks = r.Callbacks[1:]
Stephane Barbarie694e2b92018-09-07 12:17:36 -040098 callback.Execute(nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040099 }
100 for len(r.NotificationCallbacks) > 0 {
101 callback := r.NotificationCallbacks[0]
102 r.NotificationCallbacks = r.NotificationCallbacks[1:]
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400103 callback.Execute(nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400104 }
105}
106
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400107func (r *root) HasCallbacks() bool {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400108 return len(r.Callbacks) == 0
109}
110
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400111func (r *root) AddCallback(callback CallbackFunction, args ...interface{}) {
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400112 r.Callbacks = append(r.Callbacks, CallbackTuple{callback, args})
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400113}
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400114func (r *root) AddNotificationCallback(callback CallbackFunction, args ...interface{}) {
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400115 r.NotificationCallbacks = append(r.NotificationCallbacks, CallbackTuple{callback, args})
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400116}
117
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400118
119func (r *root) Update(path string, data interface{}, strict bool, txid string, makeBranch MakeBranchFunction) Revision {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400120 var result Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400121
122 if makeBranch == nil {
123 // TODO: raise error
124 }
125
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400126 if r.HasCallbacks() {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400127 // TODO: raise error
128 }
129
130 if txid != "" {
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400131 trackDirty := func(node *node) *Branch {
Stephane Barbariee16186c2018-09-11 10:46:34 -0400132 r.DirtyNodes[txid] = append(r.DirtyNodes[txid], node)
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400133 return node.MakeBranch(txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400134 }
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400135 result = r.node.Update(path, data, strict, txid, trackDirty)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400136 } else {
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400137 result = r.node.Update(path, data, strict, "", nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400138 }
139
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400140 r.ExecuteCallbacks()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400141
142 return result
143}
144
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400145func (r *root) Add(path string, data interface{}, txid string, makeBranch MakeBranchFunction) Revision {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400146 var result Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400147
148 if makeBranch == nil {
149 // TODO: raise error
150 }
151
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400152 if r.HasCallbacks() {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400153 // TODO: raise error
154 }
155
156 if txid != "" {
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400157 trackDirty := func(node *node) *Branch {
Stephane Barbariee16186c2018-09-11 10:46:34 -0400158 r.DirtyNodes[txid] = append(r.DirtyNodes[txid], node)
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400159 return node.MakeBranch(txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400160 }
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400161 result = r.node.Add(path, data, txid, trackDirty)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400162 } else {
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400163 result = r.node.Add(path, data, "", nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400164 }
165
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400166 r.ExecuteCallbacks()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400167
168 return result
169}
170
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400171func (r *root) Remove(path string, txid string, makeBranch MakeBranchFunction) Revision {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400172 var result Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400173
174 if makeBranch == nil {
175 // TODO: raise error
176 }
177
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400178 if r.HasCallbacks() {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400179 // TODO: raise error
180 }
181
182 if txid != "" {
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400183 trackDirty := func(node *node) *Branch {
Stephane Barbariee16186c2018-09-11 10:46:34 -0400184 r.DirtyNodes[txid] = append(r.DirtyNodes[txid], node)
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400185 return node.MakeBranch(txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400186 }
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400187 result = r.node.Remove(path, txid, trackDirty)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400188 } else {
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400189 result = r.node.Remove(path, "", nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400190 }
191
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400192 r.ExecuteCallbacks()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400193
194 return result
195}
196
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400197func (r *root) Load(rootClass interface{}) *root {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400198 //fakeKvStore := &Backend{}
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400199 //root := NewRoot(rootClass, nil)
200 //root.KvStore = r.KvStore
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400201 r.LoadFromPersistence(rootClass)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400202 return r
203}
204
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400205func (r *root) MakeLatest(branch *Branch, revision Revision, changeAnnouncement []ChangeTuple) {
206 r.makeLatest(branch, revision, changeAnnouncement)
207}
208
209func (r *root) makeLatest(branch *Branch, revision Revision, changeAnnouncement []ChangeTuple) {
210 r.node.makeLatest(branch, revision, changeAnnouncement)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400211
212 if r.KvStore != nil && branch.Txid == "" {
213 tags := make(map[string]string)
214 for k, v := range r.Tags {
215 tags[k] = v.GetHash()
216 }
217 data := &rootData{
218 Latest: branch.Latest.GetHash(),
219 Tags: tags,
220 }
221 if blob, err := json.Marshal(data); err != nil {
222 // TODO report error
223 } else {
224 log.Debugf("Changing root to : %s", string(blob))
225 if err := r.KvStore.Put("root", blob); err != nil {
226 log.Errorf("failed to properly put value in kvstore - err: %s", err.Error())
227 }
228 }
229 }
230}
231
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400232func (r *root) LoadLatest(hash string) {
233 r.node.LoadLatest(r.KvStore, hash)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400234}
235
236type rootData struct {
khenaidoob9203542018-09-17 22:56:37 -0400237 Latest string `json:latest`
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400238 Tags map[string]string `json:tags`
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400239}
240
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400241func (r *root) LoadFromPersistence(rootClass interface{}) {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400242 var data rootData
243
244 r.Loading = true
245 blob, _ := r.KvStore.Get("root")
246
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400247 start := time.Now()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400248 if err := json.Unmarshal(blob.Value.([]byte), &data); err != nil {
249 fmt.Errorf("problem to unmarshal blob - error:%s\n", err.Error())
250 }
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400251 stop := time.Now()
252 GetProfiling().AddToInMemoryModelTime(stop.Sub(start).Seconds())
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400253 for tag, hash := range data.Tags {
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400254 r.node.LoadLatest(r.KvStore, hash)
255 r.node.Tags[tag] = r.node.Latest()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400256 }
257
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400258 r.node.LoadLatest(r.KvStore, data.Latest)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400259 r.Loading = false
260}