blob: 45eafb9dd6f12192a30d963061b9f580794321f8 [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 {
Stephane Barbarie126101e2018-10-11 16:18:48 -040032 *node
33
34 Callbacks []CallbackTuple
35 NotificationCallbacks []CallbackTuple
Stephane Barbarie06c4a742018-10-01 11:09:32 -040036
37 DirtyNodes map[string][]*node
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040038 KvStore *Backend
39 Loading bool
40 RevisionClass interface{}
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 Barbarie126101e2018-10-11 16:18:48 -040045
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040046 root.KvStore = kvStore
Stephane Barbarie06c4a742018-10-01 11:09:32 -040047 root.DirtyNodes = make(map[string][]*node)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040048 root.Loading = false
Stephane Barbarie06c4a742018-10-01 11:09:32 -040049
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -040050 if kvStore != nil {
51 root.RevisionClass = reflect.TypeOf(PersistedRevision{})
52 } else {
53 root.RevisionClass = reflect.TypeOf(NonPersistedRevision{})
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040054 }
Stephane Barbarie694e2b92018-09-07 12:17:36 -040055 root.Callbacks = []CallbackTuple{}
56 root.NotificationCallbacks = []CallbackTuple{}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040057
Stephane Barbarie126101e2018-10-11 16:18:48 -040058 root.node = NewNode(root, initialData,false, "")
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040059
60 return root
61}
62
Stephane Barbarie06c4a742018-10-01 11:09:32 -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 Barbarie126101e2018-10-11 16:18:48 -040066 r.DirtyNodes[txid] = []*node{r.node}
Stephane Barbarie06c4a742018-10-01 11:09:32 -040067 r.node.MakeBranch(txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040068 return txid
69}
70
Stephane Barbarie06c4a742018-10-01 11:09:32 -040071func (r *root) DeleteTxBranch(txid string) {
Stephane Barbariee16186c2018-09-11 10:46:34 -040072 for _, dirtyNode := range r.DirtyNodes[txid] {
Stephane Barbarie06c4a742018-10-01 11:09:32 -040073 dirtyNode.DeleteBranch(txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040074 }
75 delete(r.DirtyNodes, txid)
76}
77
Stephane Barbarie06c4a742018-10-01 11:09:32 -040078func (r *root) FoldTxBranch(txid string) {
79 if _, err := r.MergeBranch(txid, true); err != nil {
Stephane Barbarieec0919b2018-09-05 14:14:29 -040080 r.DeleteTxBranch(txid)
81 } else {
Stephane Barbarie06c4a742018-10-01 11:09:32 -040082 r.MergeBranch(txid, false)
83 r.ExecuteCallbacks()
Stephane Barbarieec0919b2018-09-05 14:14:29 -040084 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040085}
86
Stephane Barbarie06c4a742018-10-01 11:09:32 -040087func (r *root) ExecuteCallbacks() {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040088 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
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400100func (r *root) HasCallbacks() bool {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400101 return len(r.Callbacks) == 0
102}
103
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400104func (r *root) AddCallback(callback CallbackFunction, args ...interface{}) {
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400105 r.Callbacks = append(r.Callbacks, CallbackTuple{callback, args})
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400106}
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400107func (r *root) AddNotificationCallback(callback CallbackFunction, args ...interface{}) {
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400108 r.NotificationCallbacks = append(r.NotificationCallbacks, CallbackTuple{callback, args})
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400109}
110
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400111func (r *root) Update(path string, data interface{}, strict bool, txid string, makeBranch MakeBranchFunction) Revision {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400112 var result Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400113
114 if makeBranch == nil {
115 // TODO: raise error
116 }
117
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400118 if r.HasCallbacks() {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400119 // TODO: raise error
120 }
121
122 if txid != "" {
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400123 trackDirty := func(node *node) *Branch {
Stephane Barbariee16186c2018-09-11 10:46:34 -0400124 r.DirtyNodes[txid] = append(r.DirtyNodes[txid], node)
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400125 return node.MakeBranch(txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400126 }
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400127 result = r.node.Update(path, data, strict, txid, trackDirty)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400128 } else {
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400129 result = r.node.Update(path, data, strict, "", nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400130 }
131
Stephane Barbarie126101e2018-10-11 16:18:48 -0400132 r.node.ExecuteCallbacks()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400133
134 return result
135}
136
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400137func (r *root) Add(path string, data interface{}, txid string, makeBranch MakeBranchFunction) Revision {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400138 var result Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400139
140 if makeBranch == nil {
141 // TODO: raise error
142 }
143
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400144 if r.HasCallbacks() {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400145 // TODO: raise error
146 }
147
148 if txid != "" {
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400149 trackDirty := func(node *node) *Branch {
Stephane Barbariee16186c2018-09-11 10:46:34 -0400150 r.DirtyNodes[txid] = append(r.DirtyNodes[txid], node)
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400151 return node.MakeBranch(txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400152 }
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400153 result = r.node.Add(path, data, txid, trackDirty)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400154 } else {
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400155 result = r.node.Add(path, data, "", nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400156 }
157
Stephane Barbarie126101e2018-10-11 16:18:48 -0400158 r.node.ExecuteCallbacks()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400159
160 return result
161}
162
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400163func (r *root) Remove(path string, txid string, makeBranch MakeBranchFunction) Revision {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400164 var result Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400165
166 if makeBranch == nil {
167 // TODO: raise error
168 }
169
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400170 if r.HasCallbacks() {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400171 // TODO: raise error
172 }
173
174 if txid != "" {
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400175 trackDirty := func(node *node) *Branch {
Stephane Barbariee16186c2018-09-11 10:46:34 -0400176 r.DirtyNodes[txid] = append(r.DirtyNodes[txid], node)
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400177 return node.MakeBranch(txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400178 }
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400179 result = r.node.Remove(path, txid, trackDirty)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400180 } else {
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400181 result = r.node.Remove(path, "", nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400182 }
183
Stephane Barbarie126101e2018-10-11 16:18:48 -0400184 r.node.ExecuteCallbacks()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400185
186 return result
187}
188
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400189func (r *root) Load(rootClass interface{}) *root {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400190 //fakeKvStore := &Backend{}
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400191 //root := NewRoot(rootClass, nil)
192 //root.KvStore = r.KvStore
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400193 r.LoadFromPersistence(rootClass)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400194 return r
195}
196
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400197func (r *root) MakeLatest(branch *Branch, revision Revision, changeAnnouncement []ChangeTuple) {
198 r.makeLatest(branch, revision, changeAnnouncement)
199}
200
201func (r *root) makeLatest(branch *Branch, revision Revision, changeAnnouncement []ChangeTuple) {
202 r.node.makeLatest(branch, revision, changeAnnouncement)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400203
204 if r.KvStore != nil && branch.Txid == "" {
205 tags := make(map[string]string)
206 for k, v := range r.Tags {
207 tags[k] = v.GetHash()
208 }
209 data := &rootData{
210 Latest: branch.Latest.GetHash(),
211 Tags: tags,
212 }
213 if blob, err := json.Marshal(data); err != nil {
214 // TODO report error
215 } else {
216 log.Debugf("Changing root to : %s", string(blob))
217 if err := r.KvStore.Put("root", blob); err != nil {
218 log.Errorf("failed to properly put value in kvstore - err: %s", err.Error())
219 }
220 }
221 }
222}
223
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400224type 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
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400229func (r *root) LoadFromPersistence(rootClass interface{}) {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400230 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 {
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -0400237 log.Errorf("problem to unmarshal blob - error:%s\n", err.Error())
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400238 }
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 {
Stephane Barbarie126101e2018-10-11 16:18:48 -0400242 r.LoadLatest(hash)
243 r.Tags[tag] = r.Latest()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400244 }
245
Stephane Barbarie126101e2018-10-11 16:18:48 -0400246 r.LoadLatest(data.Latest)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400247 r.Loading = false
248}