blob: 1c14f9a87210763b6b381d17332feefeb2660b7f [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
38func NewRoot(initialData interface{}, kvStore *Backend, revisionClass interface{}) *Root {
39 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 Barbarieec0919b2018-09-05 14:14:29 -040043 if kvStore != nil /*&& TODO: RevisionClass is not a subclass of PersistedRevision ??? */ {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040044 revisionClass = reflect.TypeOf(PersistedRevision{})
45 }
46 root.RevisionClass = revisionClass
Stephane Barbarie694e2b92018-09-07 12:17:36 -040047 root.Callbacks = []CallbackTuple{}
48 root.NotificationCallbacks = []CallbackTuple{}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040049
50 root.Node = NewNode(root, initialData, false, "")
51
52 return root
53}
54
Stephane Barbarieec0919b2018-09-05 14:14:29 -040055func (r *Root) MakeRevision(branch *Branch, data interface{}, children map[string][]Revision) Revision {
56 if r.RevisionClass.(reflect.Type) == reflect.TypeOf(PersistedRevision{}) {
57 return NewPersistedRevision(branch, data, children)
58 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040059
Stephane Barbarieec0919b2018-09-05 14:14:29 -040060 return NewNonPersistedRevision(branch, data, children)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040061}
62
Stephane Barbarieec0919b2018-09-05 14:14:29 -040063func (r *Root) MakeTxBranch() string {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040064 txid_bin, _ := uuid.New().MarshalBinary()
65 txid := hex.EncodeToString(txid_bin)[:12]
Stephane Barbariee16186c2018-09-11 10:46:34 -040066 r.DirtyNodes[txid] = []*Node{r.Node}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040067 r.Node.makeTxBranch(txid)
68 return txid
69}
70
Stephane Barbarieec0919b2018-09-05 14:14:29 -040071func (r *Root) DeleteTxBranch(txid string) {
Stephane Barbariee16186c2018-09-11 10:46:34 -040072 for _, dirtyNode := range r.DirtyNodes[txid] {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040073 dirtyNode.deleteTxBranch(txid)
74 }
75 delete(r.DirtyNodes, txid)
76}
77
Stephane Barbarieec0919b2018-09-05 14:14:29 -040078func (r *Root) FoldTxBranch(txid string) {
Stephane Barbariee16186c2018-09-11 10:46:34 -040079 if _, err := r.mergeTxBranch(txid, true); err != nil {
Stephane Barbarieec0919b2018-09-05 14:14:29 -040080 r.DeleteTxBranch(txid)
81 } else {
Stephane Barbariee16186c2018-09-11 10:46:34 -040082 r.mergeTxBranch(txid, false)
Stephane Barbarieec0919b2018-09-05 14:14:29 -040083 r.executeCallbacks()
84 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040085}
86
87func (r *Root) executeCallbacks() {
88 for len(r.Callbacks) > 0 {
89 callback := r.Callbacks[0]
90 r.Callbacks = r.Callbacks[1:]
Stephane Barbarie694e2b92018-09-07 12:17:36 -040091 callback.Execute(nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040092 }
93 for len(r.NotificationCallbacks) > 0 {
94 callback := r.NotificationCallbacks[0]
95 r.NotificationCallbacks = r.NotificationCallbacks[1:]
Stephane Barbarie694e2b92018-09-07 12:17:36 -040096 callback.Execute(nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040097 }
98}
99
100func (r *Root) noCallbacks() bool {
101 return len(r.Callbacks) == 0
102}
103
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400104func (r *Root) addCallback(callback CallbackFunction, args ...interface{}) {
105 r.Callbacks = append(r.Callbacks, CallbackTuple{callback, args})
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400106}
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400107func (r *Root) addNotificationCallback(callback CallbackFunction, args ...interface{}) {
108 r.NotificationCallbacks = append(r.NotificationCallbacks, CallbackTuple{callback, args})
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400109}
110
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400111func (r *Root) Update(path string, data interface{}, strict bool, txid string, makeBranch t_makeBranch) Revision {
112 var result Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400113
114 if makeBranch == nil {
115 // TODO: raise error
116 }
117
118 if r.noCallbacks() {
119 // TODO: raise error
120 }
121
122 if txid != "" {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400123 trackDirty := func(node *Node) *Branch {
Stephane Barbariee16186c2018-09-11 10:46:34 -0400124 r.DirtyNodes[txid] = append(r.DirtyNodes[txid], node)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400125 return node.makeTxBranch(txid)
126 }
127 result = r.Node.Update(path, data, strict, txid, trackDirty)
128 } else {
129 result = r.Node.Update(path, data, strict, "", nil)
130 }
131
132 r.executeCallbacks()
133
134 return result
135}
136
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400137func (r *Root) Add(path string, data interface{}, txid string, makeBranch t_makeBranch) Revision {
138 var result Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400139
140 if makeBranch == nil {
141 // TODO: raise error
142 }
143
144 if r.noCallbacks() {
145 // TODO: raise error
146 }
147
148 if txid != "" {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400149 trackDirty := func(node *Node) *Branch {
Stephane Barbariee16186c2018-09-11 10:46:34 -0400150 r.DirtyNodes[txid] = append(r.DirtyNodes[txid], node)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400151 return node.makeTxBranch(txid)
152 }
153 result = r.Node.Add(path, data, txid, trackDirty)
154 } else {
155 result = r.Node.Add(path, data, "", nil)
156 }
157
158 r.executeCallbacks()
159
160 return result
161}
162
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400163func (r *Root) Remove(path string, txid string, makeBranch t_makeBranch) Revision {
164 var result Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400165
166 if makeBranch == nil {
167 // TODO: raise error
168 }
169
170 if r.noCallbacks() {
171 // TODO: raise error
172 }
173
174 if txid != "" {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400175 trackDirty := func(node *Node) *Branch {
Stephane Barbariee16186c2018-09-11 10:46:34 -0400176 r.DirtyNodes[txid] = append(r.DirtyNodes[txid], node)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400177 return node.makeTxBranch(txid)
178 }
179 result = r.Node.Remove(path, txid, trackDirty)
180 } else {
181 result = r.Node.Remove(path, "", nil)
182 }
183
184 r.executeCallbacks()
185
186 return result
187}
188
189func (r *Root) Load(rootClass interface{}) *Root {
190 //fakeKvStore := &Backend{}
191 //root := NewRoot(rootClass, fakeKvStore, PersistedRevision{})
192 //r.KvStore = KvStore
193 r.loadFromPersistence(rootClass)
194 return r
195}
196
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400197func (r *Root) MakeLatest(branch *Branch, revision Revision, changeAnnouncement []ChangeTuple) {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400198 r.Node.MakeLatest(branch, revision, changeAnnouncement)
199
200 if r.KvStore != nil && branch.Txid == "" {
201 tags := make(map[string]string)
202 for k, v := range r.Tags {
203 tags[k] = v.GetHash()
204 }
205 data := &rootData{
206 Latest: branch.Latest.GetHash(),
207 Tags: tags,
208 }
209 if blob, err := json.Marshal(data); err != nil {
210 // TODO report error
211 } else {
212 log.Debugf("Changing root to : %s", string(blob))
213 if err := r.KvStore.Put("root", blob); err != nil {
214 log.Errorf("failed to properly put value in kvstore - err: %s", err.Error())
215 }
216 }
217 }
218}
219
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400220func (r *Root) LoadLatest(hash string) {
221 r.Node.LoadLatest(r.KvStore, hash)
222}
223
224type rootData struct {
khenaidoob9203542018-09-17 22:56:37 -0400225 Latest string `json:latest`
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400226 Tags map[string]string `json:tags`
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400227}
228
229func (r *Root) loadFromPersistence(rootClass interface{}) {
230 var data rootData
231
232 r.Loading = true
233 blob, _ := r.KvStore.Get("root")
234
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400235 start := time.Now()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400236 if err := json.Unmarshal(blob.Value.([]byte), &data); err != nil {
237 fmt.Errorf("problem to unmarshal blob - error:%s\n", err.Error())
238 }
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400239 stop := time.Now()
240 GetProfiling().AddToInMemoryModelTime(stop.Sub(start).Seconds())
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400241 for tag, hash := range data.Tags {
242 r.Node.LoadLatest(r.KvStore, hash)
243 r.Node.Tags[tag] = r.Node.Latest()
244 }
245
246 r.Node.LoadLatest(r.KvStore, data.Latest)
247 r.Loading = false
248}