blob: 27e4ec4217a0ad1a8263791e2b41c426b3cc3383 [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"
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040021 "github.com/google/uuid"
khenaidoob9203542018-09-17 22:56:37 -040022 "github.com/opencord/voltha-go/common/log"
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040023 "reflect"
Stephane Barbarieec0919b2018-09-05 14:14:29 -040024 "time"
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040025)
26
Stephane Barbarie06c4a742018-10-01 11:09:32 -040027type Root interface {
28 Node
29}
30
31type root struct {
32 node
33
34 DirtyNodes map[string][]*node
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040035 KvStore *Backend
36 Loading bool
37 RevisionClass interface{}
Stephane Barbarie694e2b92018-09-07 12:17:36 -040038 Callbacks []CallbackTuple
39 NotificationCallbacks []CallbackTuple
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040040}
41
Stephane Barbarie06c4a742018-10-01 11:09:32 -040042func NewRoot(initialData interface{}, kvStore *Backend) *root {
43 root := &root{}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040044 root.KvStore = kvStore
Stephane Barbarie06c4a742018-10-01 11:09:32 -040045 root.DirtyNodes = make(map[string][]*node)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040046 root.Loading = false
Stephane Barbarie06c4a742018-10-01 11:09:32 -040047
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -040048 if kvStore != nil {
49 root.RevisionClass = reflect.TypeOf(PersistedRevision{})
50 } else {
51 root.RevisionClass = reflect.TypeOf(NonPersistedRevision{})
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040052 }
Stephane Barbarie694e2b92018-09-07 12:17:36 -040053 root.Callbacks = []CallbackTuple{}
54 root.NotificationCallbacks = []CallbackTuple{}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040055
Stephane Barbarie06c4a742018-10-01 11:09:32 -040056 root.node = *NewNode(root, initialData, false, "")
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040057
58 return root
59}
60
Stephane Barbarie06c4a742018-10-01 11:09:32 -040061func (r *root) MakeRevision(branch *Branch, data interface{}, children map[string][]Revision) Revision {
Stephane Barbarieec0919b2018-09-05 14:14:29 -040062 if r.RevisionClass.(reflect.Type) == reflect.TypeOf(PersistedRevision{}) {
63 return NewPersistedRevision(branch, data, children)
64 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040065
Stephane Barbarieec0919b2018-09-05 14:14:29 -040066 return NewNonPersistedRevision(branch, data, children)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040067}
68
Stephane Barbarie06c4a742018-10-01 11:09:32 -040069func (r *root) MakeTxBranch() string {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040070 txid_bin, _ := uuid.New().MarshalBinary()
71 txid := hex.EncodeToString(txid_bin)[:12]
Stephane Barbarie06c4a742018-10-01 11:09:32 -040072 r.DirtyNodes[txid] = []*node{&r.node}
73 r.node.MakeBranch(txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040074 return txid
75}
76
Stephane Barbarie06c4a742018-10-01 11:09:32 -040077func (r *root) DeleteTxBranch(txid string) {
Stephane Barbariee16186c2018-09-11 10:46:34 -040078 for _, dirtyNode := range r.DirtyNodes[txid] {
Stephane Barbarie06c4a742018-10-01 11:09:32 -040079 dirtyNode.DeleteBranch(txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040080 }
81 delete(r.DirtyNodes, txid)
82}
83
Stephane Barbarie06c4a742018-10-01 11:09:32 -040084func (r *root) FoldTxBranch(txid string) {
85 if _, err := r.MergeBranch(txid, true); err != nil {
Stephane Barbarieec0919b2018-09-05 14:14:29 -040086 r.DeleteTxBranch(txid)
87 } else {
Stephane Barbarie06c4a742018-10-01 11:09:32 -040088 r.MergeBranch(txid, false)
89 r.ExecuteCallbacks()
Stephane Barbarieec0919b2018-09-05 14:14:29 -040090 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040091}
92
Stephane Barbarie06c4a742018-10-01 11:09:32 -040093func (r *root) ExecuteCallbacks() {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040094 for len(r.Callbacks) > 0 {
95 callback := r.Callbacks[0]
96 r.Callbacks = r.Callbacks[1:]
Stephane Barbarie694e2b92018-09-07 12:17:36 -040097 callback.Execute(nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040098 }
99 for len(r.NotificationCallbacks) > 0 {
100 callback := r.NotificationCallbacks[0]
101 r.NotificationCallbacks = r.NotificationCallbacks[1:]
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400102 callback.Execute(nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400103 }
104}
105
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400106func (r *root) HasCallbacks() bool {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400107 return len(r.Callbacks) == 0
108}
109
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400110func (r *root) AddCallback(callback CallbackFunction, args ...interface{}) {
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400111 r.Callbacks = append(r.Callbacks, CallbackTuple{callback, args})
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400112}
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400113func (r *root) AddNotificationCallback(callback CallbackFunction, args ...interface{}) {
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400114 r.NotificationCallbacks = append(r.NotificationCallbacks, CallbackTuple{callback, args})
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400115}
116
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400117func (r *root) Update(path string, data interface{}, strict bool, txid string, makeBranch MakeBranchFunction) Revision {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400118 var result Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400119
120 if makeBranch == nil {
121 // TODO: raise error
122 }
123
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400124 if r.HasCallbacks() {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400125 // TODO: raise error
126 }
127
128 if txid != "" {
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400129 trackDirty := func(node *node) *Branch {
Stephane Barbariee16186c2018-09-11 10:46:34 -0400130 r.DirtyNodes[txid] = append(r.DirtyNodes[txid], node)
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400131 return node.MakeBranch(txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400132 }
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400133 result = r.node.Update(path, data, strict, txid, trackDirty)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400134 } else {
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400135 result = r.node.Update(path, data, strict, "", nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400136 }
137
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400138 r.ExecuteCallbacks()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400139
140 return result
141}
142
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400143func (r *root) Add(path string, data interface{}, txid string, makeBranch MakeBranchFunction) Revision {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400144 var result Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400145
146 if makeBranch == nil {
147 // TODO: raise error
148 }
149
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400150 if r.HasCallbacks() {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400151 // TODO: raise error
152 }
153
154 if txid != "" {
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400155 trackDirty := func(node *node) *Branch {
Stephane Barbariee16186c2018-09-11 10:46:34 -0400156 r.DirtyNodes[txid] = append(r.DirtyNodes[txid], node)
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400157 return node.MakeBranch(txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400158 }
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400159 result = r.node.Add(path, data, txid, trackDirty)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400160 } else {
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400161 result = r.node.Add(path, data, "", nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400162 }
163
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400164 r.ExecuteCallbacks()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400165
166 return result
167}
168
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400169func (r *root) Remove(path string, txid string, makeBranch MakeBranchFunction) Revision {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400170 var result Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400171
172 if makeBranch == nil {
173 // TODO: raise error
174 }
175
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400176 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.Remove(path, txid, trackDirty)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400186 } else {
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400187 result = r.node.Remove(path, "", nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400188 }
189
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400190 r.ExecuteCallbacks()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400191
192 return result
193}
194
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400195func (r *root) Load(rootClass interface{}) *root {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400196 //fakeKvStore := &Backend{}
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400197 //root := NewRoot(rootClass, nil)
198 //root.KvStore = r.KvStore
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400199 r.LoadFromPersistence(rootClass)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400200 return r
201}
202
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400203func (r *root) MakeLatest(branch *Branch, revision Revision, changeAnnouncement []ChangeTuple) {
204 r.makeLatest(branch, revision, changeAnnouncement)
205}
206
207func (r *root) makeLatest(branch *Branch, revision Revision, changeAnnouncement []ChangeTuple) {
208 r.node.makeLatest(branch, revision, changeAnnouncement)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400209
210 if r.KvStore != nil && branch.Txid == "" {
211 tags := make(map[string]string)
212 for k, v := range r.Tags {
213 tags[k] = v.GetHash()
214 }
215 data := &rootData{
216 Latest: branch.Latest.GetHash(),
217 Tags: tags,
218 }
219 if blob, err := json.Marshal(data); err != nil {
220 // TODO report error
221 } else {
222 log.Debugf("Changing root to : %s", string(blob))
223 if err := r.KvStore.Put("root", blob); err != nil {
224 log.Errorf("failed to properly put value in kvstore - err: %s", err.Error())
225 }
226 }
227 }
228}
229
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400230func (r *root) LoadLatest(hash string) {
231 r.node.LoadLatest(r.KvStore, hash)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400232}
233
234type rootData struct {
khenaidoob9203542018-09-17 22:56:37 -0400235 Latest string `json:latest`
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400236 Tags map[string]string `json:tags`
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400237}
238
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400239func (r *root) LoadFromPersistence(rootClass interface{}) {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400240 var data rootData
241
242 r.Loading = true
243 blob, _ := r.KvStore.Get("root")
244
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400245 start := time.Now()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400246 if err := json.Unmarshal(blob.Value.([]byte), &data); err != nil {
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -0400247 log.Errorf("problem to unmarshal blob - error:%s\n", err.Error())
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400248 }
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400249 stop := time.Now()
250 GetProfiling().AddToInMemoryModelTime(stop.Sub(start).Seconds())
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400251 for tag, hash := range data.Tags {
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400252 r.node.LoadLatest(r.KvStore, hash)
253 r.node.Tags[tag] = r.node.Latest()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400254 }
255
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400256 r.node.LoadLatest(r.KvStore, data.Latest)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400257 r.Loading = false
258}