blob: 8ac1311804e508e3cb3dba7391a134050409ca14 [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 Barbariedc5022d2018-11-19 15:21:44 -050016
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040017package model
18
19import (
Stephane Barbarieef6650d2019-07-18 12:15:09 -040020 "context"
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040021 "encoding/hex"
22 "encoding/json"
Stephane Barbarie1ab43272018-12-08 21:42:13 -050023 "github.com/golang/protobuf/proto"
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040024 "github.com/google/uuid"
sbarbari17d7e222019-11-05 10:02:29 -050025 "github.com/opencord/voltha-lib-go/v2/pkg/db"
Scott Baker807addd2019-10-24 15:16:21 -070026 "github.com/opencord/voltha-lib-go/v2/pkg/log"
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040027 "reflect"
Stephane Barbariedc5022d2018-11-19 15:21:44 -050028 "sync"
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040029)
30
Stephane Barbariedc5022d2018-11-19 15:21:44 -050031// Root is used to provide an abstraction to the base root structure
Stephane Barbarie06c4a742018-10-01 11:09:32 -040032type Root interface {
33 Node
Stephane Barbariedc5022d2018-11-19 15:21:44 -050034
35 ExecuteCallbacks()
36 AddCallback(callback CallbackFunction, args ...interface{})
37 AddNotificationCallback(callback CallbackFunction, args ...interface{})
Stephane Barbarie06c4a742018-10-01 11:09:32 -040038}
39
Stephane Barbariedc5022d2018-11-19 15:21:44 -050040// root points to the top of the data model tree or sub-tree identified by a proxy
Stephane Barbarie06c4a742018-10-01 11:09:32 -040041type root struct {
Stephane Barbarie126101e2018-10-11 16:18:48 -040042 *node
43
44 Callbacks []CallbackTuple
45 NotificationCallbacks []CallbackTuple
Stephane Barbarie06c4a742018-10-01 11:09:32 -040046
Stephane Barbariedc5022d2018-11-19 15:21:44 -050047 DirtyNodes map[string][]*node
sbarbari17d7e222019-11-05 10:02:29 -050048 KvStore *db.Backend
Stephane Barbariedc5022d2018-11-19 15:21:44 -050049 Loading bool
50 RevisionClass interface{}
51
khenaidoo43c82122018-11-22 18:38:28 -050052 mutex sync.RWMutex
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040053}
54
Stephane Barbariedc5022d2018-11-19 15:21:44 -050055// NewRoot creates an new instance of a root object
sbarbari17d7e222019-11-05 10:02:29 -050056func NewRoot(initialData interface{}, kvStore *db.Backend) *root {
Stephane Barbarie06c4a742018-10-01 11:09:32 -040057 root := &root{}
Stephane Barbarie126101e2018-10-11 16:18:48 -040058
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040059 root.KvStore = kvStore
Stephane Barbarie06c4a742018-10-01 11:09:32 -040060 root.DirtyNodes = make(map[string][]*node)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040061 root.Loading = false
Stephane Barbarie06c4a742018-10-01 11:09:32 -040062
Stephane Barbariedc5022d2018-11-19 15:21:44 -050063 // If there is no storage in place just revert to
64 // a non persistent mechanism
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -040065 if kvStore != nil {
66 root.RevisionClass = reflect.TypeOf(PersistedRevision{})
67 } else {
68 root.RevisionClass = reflect.TypeOf(NonPersistedRevision{})
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040069 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -050070
Stephane Barbarie694e2b92018-09-07 12:17:36 -040071 root.Callbacks = []CallbackTuple{}
72 root.NotificationCallbacks = []CallbackTuple{}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040073
Stephane Barbariedc5022d2018-11-19 15:21:44 -050074 root.node = NewNode(root, initialData, false, "")
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040075
76 return root
77}
78
Stephane Barbariedc5022d2018-11-19 15:21:44 -050079// MakeTxBranch creates a new transaction branch
Stephane Barbarie06c4a742018-10-01 11:09:32 -040080func (r *root) MakeTxBranch() string {
Stephane Barbariedc5022d2018-11-19 15:21:44 -050081 txidBin, _ := uuid.New().MarshalBinary()
82 txid := hex.EncodeToString(txidBin)[:12]
83
Stephane Barbarie126101e2018-10-11 16:18:48 -040084 r.DirtyNodes[txid] = []*node{r.node}
Stephane Barbarie06c4a742018-10-01 11:09:32 -040085 r.node.MakeBranch(txid)
Stephane Barbariedc5022d2018-11-19 15:21:44 -050086
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040087 return txid
88}
89
Stephane Barbariedc5022d2018-11-19 15:21:44 -050090// DeleteTxBranch removes a transaction branch
Stephane Barbarie06c4a742018-10-01 11:09:32 -040091func (r *root) DeleteTxBranch(txid string) {
Stephane Barbariee16186c2018-09-11 10:46:34 -040092 for _, dirtyNode := range r.DirtyNodes[txid] {
Stephane Barbarie06c4a742018-10-01 11:09:32 -040093 dirtyNode.DeleteBranch(txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040094 }
95 delete(r.DirtyNodes, txid)
Stephane Barbariec92d1072019-06-07 16:21:49 -040096 r.node.DeleteBranch(txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040097}
98
Stephane Barbariedc5022d2018-11-19 15:21:44 -050099// FoldTxBranch will merge the contents of a transaction branch with the root object
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400100func (r *root) FoldTxBranch(txid string) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500101 // Start by doing a dry run of the merge
102 // If that fails, it bails out and the branch is deleted
103 if _, err := r.node.MergeBranch(txid, true); err != nil {
104 // Merge operation fails
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400105 r.DeleteTxBranch(txid)
106 } else {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500107 r.node.MergeBranch(txid, false)
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400108 r.node.GetRoot().ExecuteCallbacks()
Stephane Barbarie260a5632019-02-26 16:12:49 -0500109 r.DeleteTxBranch(txid)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400110 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400111}
112
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500113// ExecuteCallbacks will invoke all the callbacks linked to root object
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400114func (r *root) ExecuteCallbacks() {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500115 r.mutex.Lock()
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500116 defer r.mutex.Unlock()
Stephane Barbariec92d1072019-06-07 16:21:49 -0400117
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400118 for len(r.Callbacks) > 0 {
119 callback := r.Callbacks[0]
120 r.Callbacks = r.Callbacks[1:]
khenaidoo43c82122018-11-22 18:38:28 -0500121 go callback.Execute(nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400122 }
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400123 //for len(r.NotificationCallbacks) > 0 {
124 // callback := r.NotificationCallbacks[0]
125 // r.NotificationCallbacks = r.NotificationCallbacks[1:]
126 // go callback.Execute(nil)
127 //}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400128}
129
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500130func (r *root) hasCallbacks() bool {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400131 return len(r.Callbacks) == 0
132}
133
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500134// getCallbacks returns the available callbacks
135func (r *root) GetCallbacks() []CallbackTuple {
136 r.mutex.Lock()
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500137 defer r.mutex.Unlock()
Stephane Barbariec92d1072019-06-07 16:21:49 -0400138
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500139 return r.Callbacks
140}
141
142// getCallbacks returns the available notification callbacks
143func (r *root) GetNotificationCallbacks() []CallbackTuple {
144 r.mutex.Lock()
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500145 defer r.mutex.Unlock()
Stephane Barbariec92d1072019-06-07 16:21:49 -0400146
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500147 return r.NotificationCallbacks
148}
149
150// AddCallback inserts a new callback with its arguments
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400151func (r *root) AddCallback(callback CallbackFunction, args ...interface{}) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500152 r.mutex.Lock()
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500153 defer r.mutex.Unlock()
Stephane Barbariec92d1072019-06-07 16:21:49 -0400154
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400155 r.Callbacks = append(r.Callbacks, CallbackTuple{callback, args})
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400156}
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500157
158// AddNotificationCallback inserts a new notification callback with its arguments
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400159func (r *root) AddNotificationCallback(callback CallbackFunction, args ...interface{}) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500160 r.mutex.Lock()
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500161 defer r.mutex.Unlock()
Stephane Barbariec92d1072019-06-07 16:21:49 -0400162
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400163 r.NotificationCallbacks = append(r.NotificationCallbacks, CallbackTuple{callback, args})
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400164}
165
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500166func (r *root) syncParent(childRev Revision, txid string) {
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400167 data := proto.Clone(r.GetProxy().ParentNode.Latest().GetData().(proto.Message))
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500168
169 for fieldName, _ := range ChildrenFields(data) {
170 childDataName, childDataHolder := GetAttributeValue(data, fieldName, 0)
171 if reflect.TypeOf(childRev.GetData()) == reflect.TypeOf(childDataHolder.Interface()) {
172 childDataHolder = reflect.ValueOf(childRev.GetData())
173 reflect.ValueOf(data).Elem().FieldByName(childDataName).Set(childDataHolder)
174 }
175 }
176
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400177 r.GetProxy().ParentNode.Latest().SetConfig(NewDataRevision(r.GetProxy().ParentNode.GetRoot(), data))
178 r.GetProxy().ParentNode.Latest(txid).Finalize(false)
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500179}
180
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500181// Update modifies the content of an object at a given path with the provided data
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400182func (r *root) Update(ctx context.Context, path string, data interface{}, strict bool, txid string, makeBranch MakeBranchFunction) Revision {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400183 var result Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400184
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500185 if makeBranch != nil {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400186 // TODO: raise error
187 }
188
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500189 if r.hasCallbacks() {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400190 // TODO: raise error
191 }
192
193 if txid != "" {
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400194 trackDirty := func(node *node) *Branch {
Stephane Barbariee16186c2018-09-11 10:46:34 -0400195 r.DirtyNodes[txid] = append(r.DirtyNodes[txid], node)
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400196 return node.MakeBranch(txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400197 }
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400198 result = r.node.Update(ctx, path, data, strict, txid, trackDirty)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400199 } else {
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400200 result = r.node.Update(ctx, path, data, strict, "", nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400201 }
202
Stephane Barbariee0a4c792019-01-16 11:26:29 -0500203 if result != nil {
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400204 if r.GetProxy().FullPath != r.GetProxy().Path {
Stephane Barbariee0a4c792019-01-16 11:26:29 -0500205 r.syncParent(result, txid)
206 } else {
207 result.Finalize(false)
208 }
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500209 }
210
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500211 r.node.GetRoot().ExecuteCallbacks()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400212
213 return result
214}
215
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500216// Add creates a new object at the given path with the provided data
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400217func (r *root) Add(ctx context.Context, path string, data interface{}, txid string, makeBranch MakeBranchFunction) Revision {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400218 var result Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400219
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500220 if makeBranch != nil {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400221 // TODO: raise error
222 }
223
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500224 if r.hasCallbacks() {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400225 // TODO: raise error
226 }
227
228 if txid != "" {
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400229 trackDirty := func(node *node) *Branch {
Stephane Barbariee16186c2018-09-11 10:46:34 -0400230 r.DirtyNodes[txid] = append(r.DirtyNodes[txid], node)
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400231 return node.MakeBranch(txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400232 }
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400233 result = r.node.Add(ctx, path, data, txid, trackDirty)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400234 } else {
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400235 result = r.node.Add(ctx, path, data, "", nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400236 }
237
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500238 if result != nil {
239 result.Finalize(true)
240 r.node.GetRoot().ExecuteCallbacks()
241 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400242 return result
243}
244
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500245// Remove discards an object at a given path
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400246func (r *root) Remove(ctx context.Context, path string, txid string, makeBranch MakeBranchFunction) Revision {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400247 var result Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400248
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500249 if makeBranch != nil {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400250 // TODO: raise error
251 }
252
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500253 if r.hasCallbacks() {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400254 // TODO: raise error
255 }
256
257 if txid != "" {
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400258 trackDirty := func(node *node) *Branch {
Stephane Barbariee16186c2018-09-11 10:46:34 -0400259 r.DirtyNodes[txid] = append(r.DirtyNodes[txid], node)
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400260 return node.MakeBranch(txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400261 }
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400262 result = r.node.Remove(ctx, path, txid, trackDirty)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400263 } else {
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400264 result = r.node.Remove(ctx, path, "", nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400265 }
266
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500267 r.node.GetRoot().ExecuteCallbacks()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400268
269 return result
270}
271
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500272// MakeLatest updates a branch with the latest node revision
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400273func (r *root) MakeLatest(branch *Branch, revision Revision, changeAnnouncement []ChangeTuple) {
274 r.makeLatest(branch, revision, changeAnnouncement)
275}
276
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500277func (r *root) MakeRevision(branch *Branch, data interface{}, children map[string][]Revision) Revision {
278 if r.RevisionClass.(reflect.Type) == reflect.TypeOf(PersistedRevision{}) {
279 return NewPersistedRevision(branch, data, children)
280 }
281
282 return NewNonPersistedRevision(r, branch, data, children)
283}
284
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400285func (r *root) makeLatest(branch *Branch, revision Revision, changeAnnouncement []ChangeTuple) {
286 r.node.makeLatest(branch, revision, changeAnnouncement)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400287
288 if r.KvStore != nil && branch.Txid == "" {
289 tags := make(map[string]string)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500290 for k, v := range r.node.Tags {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400291 tags[k] = v.GetHash()
292 }
293 data := &rootData{
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500294 Latest: branch.GetLatest().GetHash(),
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400295 Tags: tags,
296 }
297 if blob, err := json.Marshal(data); err != nil {
298 // TODO report error
299 } else {
300 log.Debugf("Changing root to : %s", string(blob))
301 if err := r.KvStore.Put("root", blob); err != nil {
302 log.Errorf("failed to properly put value in kvstore - err: %s", err.Error())
303 }
304 }
305 }
306}
307
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400308type rootData struct {
Kent Hagerman0ab4cb22019-04-24 13:13:35 -0400309 Latest string `json:"latest"`
310 Tags map[string]string `json:"tags"`
311}