blob: 3e3cc432e76f835ac15186725f7b6068dce8d9d5 [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
28type Root struct {
29 *Node
Stephane Barbariee16186c2018-09-11 10:46:34 -040030 DirtyNodes map[string][]*Node
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040031 KvStore *Backend
32 Loading bool
33 RevisionClass interface{}
Stephane Barbarie694e2b92018-09-07 12:17:36 -040034 Callbacks []CallbackTuple
35 NotificationCallbacks []CallbackTuple
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040036}
37
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -040038func NewRoot(initialData interface{}, kvStore *Backend) *Root {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040039 root := &Root{}
40 root.KvStore = kvStore
Stephane Barbariee16186c2018-09-11 10:46:34 -040041 root.DirtyNodes = make(map[string][]*Node)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040042 root.Loading = false
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -040043 if kvStore != nil {
44 root.RevisionClass = reflect.TypeOf(PersistedRevision{})
45 } else {
46 root.RevisionClass = reflect.TypeOf(NonPersistedRevision{})
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040047 }
Stephane Barbarie694e2b92018-09-07 12:17:36 -040048 root.Callbacks = []CallbackTuple{}
49 root.NotificationCallbacks = []CallbackTuple{}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040050
51 root.Node = NewNode(root, initialData, false, "")
52
53 return root
54}
55
Stephane Barbarieec0919b2018-09-05 14:14:29 -040056func (r *Root) MakeRevision(branch *Branch, data interface{}, children map[string][]Revision) Revision {
57 if r.RevisionClass.(reflect.Type) == reflect.TypeOf(PersistedRevision{}) {
58 return NewPersistedRevision(branch, data, children)
59 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040060
Stephane Barbarieec0919b2018-09-05 14:14:29 -040061 return NewNonPersistedRevision(branch, data, children)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040062}
63
Stephane Barbarieec0919b2018-09-05 14:14:29 -040064func (r *Root) MakeTxBranch() string {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040065 txid_bin, _ := uuid.New().MarshalBinary()
66 txid := hex.EncodeToString(txid_bin)[:12]
Stephane Barbariee16186c2018-09-11 10:46:34 -040067 r.DirtyNodes[txid] = []*Node{r.Node}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040068 r.Node.makeTxBranch(txid)
69 return txid
70}
71
Stephane Barbarieec0919b2018-09-05 14:14:29 -040072func (r *Root) DeleteTxBranch(txid string) {
Stephane Barbariee16186c2018-09-11 10:46:34 -040073 for _, dirtyNode := range r.DirtyNodes[txid] {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040074 dirtyNode.deleteTxBranch(txid)
75 }
76 delete(r.DirtyNodes, txid)
77}
78
Stephane Barbarieec0919b2018-09-05 14:14:29 -040079func (r *Root) FoldTxBranch(txid string) {
Stephane Barbariee16186c2018-09-11 10:46:34 -040080 if _, err := r.mergeTxBranch(txid, true); err != nil {
Stephane Barbarieec0919b2018-09-05 14:14:29 -040081 r.DeleteTxBranch(txid)
82 } else {
Stephane Barbariee16186c2018-09-11 10:46:34 -040083 r.mergeTxBranch(txid, false)
Stephane Barbarieec0919b2018-09-05 14:14:29 -040084 r.executeCallbacks()
85 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040086}
87
88func (r *Root) executeCallbacks() {
89 for len(r.Callbacks) > 0 {
90 callback := r.Callbacks[0]
91 r.Callbacks = r.Callbacks[1:]
Stephane Barbarie694e2b92018-09-07 12:17:36 -040092 callback.Execute(nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040093 }
94 for len(r.NotificationCallbacks) > 0 {
95 callback := r.NotificationCallbacks[0]
96 r.NotificationCallbacks = r.NotificationCallbacks[1:]
Stephane Barbarie694e2b92018-09-07 12:17:36 -040097 callback.Execute(nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040098 }
99}
100
101func (r *Root) noCallbacks() bool {
102 return len(r.Callbacks) == 0
103}
104
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400105func (r *Root) addCallback(callback CallbackFunction, args ...interface{}) {
106 r.Callbacks = append(r.Callbacks, CallbackTuple{callback, args})
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400107}
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400108func (r *Root) addNotificationCallback(callback CallbackFunction, args ...interface{}) {
109 r.NotificationCallbacks = append(r.NotificationCallbacks, CallbackTuple{callback, args})
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400110}
111
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400112func (r *Root) Update(path string, data interface{}, strict bool, txid string, makeBranch t_makeBranch) Revision {
113 var result Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400114
115 if makeBranch == nil {
116 // TODO: raise error
117 }
118
119 if r.noCallbacks() {
120 // TODO: raise error
121 }
122
123 if txid != "" {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400124 trackDirty := func(node *Node) *Branch {
Stephane Barbariee16186c2018-09-11 10:46:34 -0400125 r.DirtyNodes[txid] = append(r.DirtyNodes[txid], node)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400126 return node.makeTxBranch(txid)
127 }
128 result = r.Node.Update(path, data, strict, txid, trackDirty)
129 } else {
130 result = r.Node.Update(path, data, strict, "", nil)
131 }
132
133 r.executeCallbacks()
134
135 return result
136}
137
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400138func (r *Root) Add(path string, data interface{}, txid string, makeBranch t_makeBranch) Revision {
139 var result Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400140
141 if makeBranch == nil {
142 // TODO: raise error
143 }
144
145 if r.noCallbacks() {
146 // TODO: raise error
147 }
148
149 if txid != "" {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400150 trackDirty := func(node *Node) *Branch {
Stephane Barbariee16186c2018-09-11 10:46:34 -0400151 r.DirtyNodes[txid] = append(r.DirtyNodes[txid], node)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400152 return node.makeTxBranch(txid)
153 }
154 result = r.Node.Add(path, data, txid, trackDirty)
155 } else {
156 result = r.Node.Add(path, data, "", nil)
157 }
158
159 r.executeCallbacks()
160
161 return result
162}
163
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400164func (r *Root) Remove(path string, txid string, makeBranch t_makeBranch) Revision {
165 var result Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400166
167 if makeBranch == nil {
168 // TODO: raise error
169 }
170
171 if r.noCallbacks() {
172 // TODO: raise error
173 }
174
175 if txid != "" {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400176 trackDirty := func(node *Node) *Branch {
Stephane Barbariee16186c2018-09-11 10:46:34 -0400177 r.DirtyNodes[txid] = append(r.DirtyNodes[txid], node)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400178 return node.makeTxBranch(txid)
179 }
180 result = r.Node.Remove(path, txid, trackDirty)
181 } else {
182 result = r.Node.Remove(path, "", nil)
183 }
184
185 r.executeCallbacks()
186
187 return result
188}
189
190func (r *Root) Load(rootClass interface{}) *Root {
191 //fakeKvStore := &Backend{}
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400192 //root := NewRoot(rootClass, nil)
193 //root.KvStore = r.KvStore
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400194 r.loadFromPersistence(rootClass)
195 return r
196}
197
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400198func (r *Root) MakeLatest(branch *Branch, revision Revision, changeAnnouncement []ChangeTuple) {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400199 r.Node.MakeLatest(branch, revision, changeAnnouncement)
200
201 if r.KvStore != nil && branch.Txid == "" {
202 tags := make(map[string]string)
203 for k, v := range r.Tags {
204 tags[k] = v.GetHash()
205 }
206 data := &rootData{
207 Latest: branch.Latest.GetHash(),
208 Tags: tags,
209 }
210 if blob, err := json.Marshal(data); err != nil {
211 // TODO report error
212 } else {
213 log.Debugf("Changing root to : %s", string(blob))
214 if err := r.KvStore.Put("root", blob); err != nil {
215 log.Errorf("failed to properly put value in kvstore - err: %s", err.Error())
216 }
217 }
218 }
219}
220
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400221func (r *Root) LoadLatest(hash string) {
222 r.Node.LoadLatest(r.KvStore, hash)
223}
224
225type rootData struct {
khenaidoob9203542018-09-17 22:56:37 -0400226 Latest string `json:latest`
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400227 Tags map[string]string `json:tags`
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400228}
229
230func (r *Root) loadFromPersistence(rootClass interface{}) {
231 var data rootData
232
233 r.Loading = true
234 blob, _ := r.KvStore.Get("root")
235
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400236 start := time.Now()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400237 if err := json.Unmarshal(blob.Value.([]byte), &data); err != nil {
238 fmt.Errorf("problem to unmarshal blob - error:%s\n", err.Error())
239 }
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400240 stop := time.Now()
241 GetProfiling().AddToInMemoryModelTime(stop.Sub(start).Seconds())
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400242 for tag, hash := range data.Tags {
243 r.Node.LoadLatest(r.KvStore, hash)
244 r.Node.Tags[tag] = r.Node.Latest()
245 }
246
247 r.Node.LoadLatest(r.KvStore, data.Latest)
248 r.Loading = false
249}