blob: 5adb99d729ed629cfbcb4f68683f9db5cee3543f [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"
23 "reflect"
Stephane Barbarieec0919b2018-09-05 14:14:29 -040024 "time"
25 "github.com/opencord/voltha-go/common/log"
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040026)
27
28type Root struct {
29 *Node
30 DirtyNodes map[string]*Node
31 KvStore *Backend
32 Loading bool
33 RevisionClass interface{}
Stephane Barbarieec0919b2018-09-05 14:14:29 -040034 Callbacks []func()
35 NotificationCallbacks []func()
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
41 root.DirtyNodes = make(map[string]*Node)
42 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 Barbarieec0919b2018-09-05 14:14:29 -040047 root.Callbacks = []func(){}
48 root.NotificationCallbacks = []func(){}
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]
66 r.DirtyNodes[txid] = r.Node
67 r.Node.makeTxBranch(txid)
68 return txid
69}
70
Stephane Barbarieec0919b2018-09-05 14:14:29 -040071func (r *Root) DeleteTxBranch(txid string) {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040072 for _, dirtyNode := range r.DirtyNodes {
73 dirtyNode.deleteTxBranch(txid)
74 }
75 delete(r.DirtyNodes, txid)
76}
77
Stephane Barbarieec0919b2018-09-05 14:14:29 -040078func (r *Root) FoldTxBranch(txid string) {
79 if err := r.Node.mergeTxBranch(txid, true); err != nil {
80 r.DeleteTxBranch(txid)
81 } else {
82 r.Node.mergeTxBranch(txid, false)
83 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:]
91 callback()
92 }
93 for len(r.NotificationCallbacks) > 0 {
94 callback := r.NotificationCallbacks[0]
95 r.NotificationCallbacks = r.NotificationCallbacks[1:]
96 callback()
97 }
98}
99
100func (r *Root) noCallbacks() bool {
101 return len(r.Callbacks) == 0
102}
103
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400104func (r *Root) addCallback(callback func()) {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400105 r.Callbacks = append(r.Callbacks, callback)
106}
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400107func (r *Root) addNotificationCallback(callback func()) {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400108 r.NotificationCallbacks = append(r.NotificationCallbacks, callback)
109}
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 != "" {
123 //dirtied := r.DirtyNodes[txid]
124
125 trackDirty := func(node *Node) *Branch {
126 //dirtied.Add(Node)
127 return node.makeTxBranch(txid)
128 }
129 result = r.Node.Update(path, data, strict, txid, trackDirty)
130 } else {
131 result = r.Node.Update(path, data, strict, "", nil)
132 }
133
134 r.executeCallbacks()
135
136 return result
137}
138
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400139func (r *Root) Add(path string, data interface{}, txid string, makeBranch t_makeBranch) Revision {
140 var result Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400141
142 if makeBranch == nil {
143 // TODO: raise error
144 }
145
146 if r.noCallbacks() {
147 // TODO: raise error
148 }
149
150 if txid != "" {
151 //dirtied := r.DirtyNodes[txid]
152
153 trackDirty := func(node *Node) *Branch {
154 //dirtied.Add(Node)
155 return node.makeTxBranch(txid)
156 }
157 result = r.Node.Add(path, data, txid, trackDirty)
158 } else {
159 result = r.Node.Add(path, data, "", nil)
160 }
161
162 r.executeCallbacks()
163
164 return result
165}
166
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400167func (r *Root) Remove(path string, txid string, makeBranch t_makeBranch) Revision {
168 var result Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400169
170 if makeBranch == nil {
171 // TODO: raise error
172 }
173
174 if r.noCallbacks() {
175 // TODO: raise error
176 }
177
178 if txid != "" {
179 //dirtied := r.DirtyNodes[txid]
180
181 trackDirty := func(node *Node) *Branch {
182 //dirtied.Add(Node)
183 return node.makeTxBranch(txid)
184 }
185 result = r.Node.Remove(path, txid, trackDirty)
186 } else {
187 result = r.Node.Remove(path, "", nil)
188 }
189
190 r.executeCallbacks()
191
192 return result
193}
194
195func (r *Root) Load(rootClass interface{}) *Root {
196 //fakeKvStore := &Backend{}
197 //root := NewRoot(rootClass, fakeKvStore, PersistedRevision{})
198 //r.KvStore = KvStore
199 r.loadFromPersistence(rootClass)
200 return r
201}
202
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400203func (r *Root) MakeLatest(branch *Branch, revision Revision, changeAnnouncement map[CallbackType][]interface{}) {
204 r.Node.MakeLatest(branch, revision, changeAnnouncement)
205
206 if r.KvStore != nil && branch.Txid == "" {
207 tags := make(map[string]string)
208 for k, v := range r.Tags {
209 tags[k] = v.GetHash()
210 }
211 data := &rootData{
212 Latest: branch.Latest.GetHash(),
213 Tags: tags,
214 }
215 if blob, err := json.Marshal(data); err != nil {
216 // TODO report error
217 } else {
218 log.Debugf("Changing root to : %s", string(blob))
219 if err := r.KvStore.Put("root", blob); err != nil {
220 log.Errorf("failed to properly put value in kvstore - err: %s", err.Error())
221 }
222 }
223 }
224}
225
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400226func (r *Root) LoadLatest(hash string) {
227 r.Node.LoadLatest(r.KvStore, hash)
228}
229
230type rootData struct {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400231 Latest string `json:latest`
232 Tags map[string]string `json:tags`
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400233}
234
235func (r *Root) loadFromPersistence(rootClass interface{}) {
236 var data rootData
237
238 r.Loading = true
239 blob, _ := r.KvStore.Get("root")
240
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400241 start := time.Now()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400242 if err := json.Unmarshal(blob.Value.([]byte), &data); err != nil {
243 fmt.Errorf("problem to unmarshal blob - error:%s\n", err.Error())
244 }
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400245 stop := time.Now()
246 GetProfiling().AddToInMemoryModelTime(stop.Sub(start).Seconds())
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400247 for tag, hash := range data.Tags {
248 r.Node.LoadLatest(r.KvStore, hash)
249 r.Node.Tags[tag] = r.Node.Latest()
250 }
251
252 r.Node.LoadLatest(r.KvStore, data.Latest)
253 r.Loading = false
254}