blob: c3b932e9f9d7c38e3f57c64cbebfaae03f4b83f7 [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"
npujar9a30c702019-11-14 17:06:39 +053023 "reflect"
24 "sync"
25
Stephane Barbarie1ab43272018-12-08 21:42:13 -050026 "github.com/golang/protobuf/proto"
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040027 "github.com/google/uuid"
serkant.uluderya2ae470f2020-01-21 11:13:09 -080028 "github.com/opencord/voltha-lib-go/v3/pkg/db"
29 "github.com/opencord/voltha-lib-go/v3/pkg/log"
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040030)
31
Stephane Barbariedc5022d2018-11-19 15:21:44 -050032// Root is used to provide an abstraction to the base root structure
Stephane Barbarie06c4a742018-10-01 11:09:32 -040033type Root interface {
34 Node
Stephane Barbariedc5022d2018-11-19 15:21:44 -050035
npujar467fe752020-01-16 20:17:45 +053036 ExecuteCallbacks(ctx context.Context)
Stephane Barbariedc5022d2018-11-19 15:21:44 -050037 AddCallback(callback CallbackFunction, args ...interface{})
38 AddNotificationCallback(callback CallbackFunction, args ...interface{})
Stephane Barbarie06c4a742018-10-01 11:09:32 -040039}
40
Stephane Barbariedc5022d2018-11-19 15:21:44 -050041// root points to the top of the data model tree or sub-tree identified by a proxy
Stephane Barbarie06c4a742018-10-01 11:09:32 -040042type root struct {
Stephane Barbarie126101e2018-10-11 16:18:48 -040043 *node
44
45 Callbacks []CallbackTuple
46 NotificationCallbacks []CallbackTuple
Stephane Barbarie06c4a742018-10-01 11:09:32 -040047
Stephane Barbariedc5022d2018-11-19 15:21:44 -050048 DirtyNodes map[string][]*node
sbarbari17d7e222019-11-05 10:02:29 -050049 KvStore *db.Backend
Stephane Barbariedc5022d2018-11-19 15:21:44 -050050 Loading bool
51 RevisionClass interface{}
52
khenaidoo43c82122018-11-22 18:38:28 -050053 mutex sync.RWMutex
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040054}
55
Stephane Barbariedc5022d2018-11-19 15:21:44 -050056// NewRoot creates an new instance of a root object
npujar9a30c702019-11-14 17:06:39 +053057func NewRoot(initialData interface{}, kvStore *db.Backend) Root {
Stephane Barbarie06c4a742018-10-01 11:09:32 -040058 root := &root{}
Stephane Barbarie126101e2018-10-11 16:18:48 -040059
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040060 root.KvStore = kvStore
Stephane Barbarie06c4a742018-10-01 11:09:32 -040061 root.DirtyNodes = make(map[string][]*node)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040062 root.Loading = false
Stephane Barbarie06c4a742018-10-01 11:09:32 -040063
Stephane Barbariedc5022d2018-11-19 15:21:44 -050064 // If there is no storage in place just revert to
65 // a non persistent mechanism
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -040066 if kvStore != nil {
67 root.RevisionClass = reflect.TypeOf(PersistedRevision{})
68 } else {
69 root.RevisionClass = reflect.TypeOf(NonPersistedRevision{})
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040070 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -050071
Stephane Barbarie694e2b92018-09-07 12:17:36 -040072 root.Callbacks = []CallbackTuple{}
73 root.NotificationCallbacks = []CallbackTuple{}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040074
npujar9a30c702019-11-14 17:06:39 +053075 root.node = newNode(root, initialData, false, "")
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040076
77 return root
78}
79
Stephane Barbariedc5022d2018-11-19 15:21:44 -050080// MakeTxBranch creates a new transaction branch
Stephane Barbarie06c4a742018-10-01 11:09:32 -040081func (r *root) MakeTxBranch() string {
Stephane Barbariedc5022d2018-11-19 15:21:44 -050082 txidBin, _ := uuid.New().MarshalBinary()
83 txid := hex.EncodeToString(txidBin)[:12]
84
Stephane Barbarie126101e2018-10-11 16:18:48 -040085 r.DirtyNodes[txid] = []*node{r.node}
Stephane Barbarie06c4a742018-10-01 11:09:32 -040086 r.node.MakeBranch(txid)
Stephane Barbariedc5022d2018-11-19 15:21:44 -050087
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040088 return txid
89}
90
Stephane Barbariedc5022d2018-11-19 15:21:44 -050091// DeleteTxBranch removes a transaction branch
Stephane Barbarie06c4a742018-10-01 11:09:32 -040092func (r *root) DeleteTxBranch(txid string) {
Stephane Barbariee16186c2018-09-11 10:46:34 -040093 for _, dirtyNode := range r.DirtyNodes[txid] {
Stephane Barbarie06c4a742018-10-01 11:09:32 -040094 dirtyNode.DeleteBranch(txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040095 }
96 delete(r.DirtyNodes, txid)
Stephane Barbariec92d1072019-06-07 16:21:49 -040097 r.node.DeleteBranch(txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040098}
99
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500100// FoldTxBranch will merge the contents of a transaction branch with the root object
npujar467fe752020-01-16 20:17:45 +0530101func (r *root) FoldTxBranch(ctx context.Context, txid string) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500102 // Start by doing a dry run of the merge
103 // If that fails, it bails out and the branch is deleted
npujar467fe752020-01-16 20:17:45 +0530104 if _, err := r.node.MergeBranch(ctx, txid, true); err != nil {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500105 // Merge operation fails
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400106 r.DeleteTxBranch(txid)
107 } else {
npujar467fe752020-01-16 20:17:45 +0530108 if _, err = r.node.MergeBranch(ctx, txid, false); err != nil {
Girish Kumarf56a4682020-03-20 20:07:46 +0000109 logger.Errorw("Unable to integrate the contents of a transaction branch within the latest branch of a given node", log.Fields{"error": err})
npujar9a30c702019-11-14 17:06:39 +0530110 }
npujar467fe752020-01-16 20:17:45 +0530111 r.node.GetRoot().ExecuteCallbacks(ctx)
Stephane Barbarie260a5632019-02-26 16:12:49 -0500112 r.DeleteTxBranch(txid)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400113 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400114}
115
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500116// ExecuteCallbacks will invoke all the callbacks linked to root object
npujar467fe752020-01-16 20:17:45 +0530117func (r *root) ExecuteCallbacks(ctx context.Context) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500118 r.mutex.Lock()
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500119 defer r.mutex.Unlock()
Stephane Barbariec92d1072019-06-07 16:21:49 -0400120
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400121 for len(r.Callbacks) > 0 {
122 callback := r.Callbacks[0]
123 r.Callbacks = r.Callbacks[1:]
npujar467fe752020-01-16 20:17:45 +0530124 go callback.Execute(ctx, nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400125 }
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400126 //for len(r.NotificationCallbacks) > 0 {
127 // callback := r.NotificationCallbacks[0]
128 // r.NotificationCallbacks = r.NotificationCallbacks[1:]
129 // go callback.Execute(nil)
130 //}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400131}
132
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500133// getCallbacks returns the available callbacks
134func (r *root) GetCallbacks() []CallbackTuple {
135 r.mutex.Lock()
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500136 defer r.mutex.Unlock()
Stephane Barbariec92d1072019-06-07 16:21:49 -0400137
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500138 return r.Callbacks
139}
140
141// getCallbacks returns the available notification callbacks
142func (r *root) GetNotificationCallbacks() []CallbackTuple {
143 r.mutex.Lock()
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500144 defer r.mutex.Unlock()
Stephane Barbariec92d1072019-06-07 16:21:49 -0400145
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500146 return r.NotificationCallbacks
147}
148
149// AddCallback inserts a new callback with its arguments
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400150func (r *root) AddCallback(callback CallbackFunction, args ...interface{}) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500151 r.mutex.Lock()
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500152 defer r.mutex.Unlock()
Stephane Barbariec92d1072019-06-07 16:21:49 -0400153
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400154 r.Callbacks = append(r.Callbacks, CallbackTuple{callback, args})
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400155}
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500156
157// AddNotificationCallback inserts a new notification callback with its arguments
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400158func (r *root) AddNotificationCallback(callback CallbackFunction, args ...interface{}) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500159 r.mutex.Lock()
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500160 defer r.mutex.Unlock()
Stephane Barbariec92d1072019-06-07 16:21:49 -0400161
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400162 r.NotificationCallbacks = append(r.NotificationCallbacks, CallbackTuple{callback, args})
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400163}
164
npujar467fe752020-01-16 20:17:45 +0530165func (r *root) syncParent(ctx context.Context, childRev Revision, txid string) {
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400166 data := proto.Clone(r.GetProxy().ParentNode.Latest().GetData().(proto.Message))
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500167
npujar9a30c702019-11-14 17:06:39 +0530168 for fieldName := range ChildrenFields(data) {
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500169 childDataName, childDataHolder := GetAttributeValue(data, fieldName, 0)
170 if reflect.TypeOf(childRev.GetData()) == reflect.TypeOf(childDataHolder.Interface()) {
171 childDataHolder = reflect.ValueOf(childRev.GetData())
172 reflect.ValueOf(data).Elem().FieldByName(childDataName).Set(childDataHolder)
173 }
174 }
175
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400176 r.GetProxy().ParentNode.Latest().SetConfig(NewDataRevision(r.GetProxy().ParentNode.GetRoot(), data))
npujar467fe752020-01-16 20:17:45 +0530177 r.GetProxy().ParentNode.Latest(txid).Finalize(ctx, false)
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500178}
179
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500180// Update modifies the content of an object at a given path with the provided data
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400181func (r *root) Update(ctx context.Context, path string, data interface{}, strict bool, txid string, makeBranch MakeBranchFunction) Revision {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400182 var result Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400183
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400184 if txid != "" {
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400185 trackDirty := func(node *node) *Branch {
Stephane Barbariee16186c2018-09-11 10:46:34 -0400186 r.DirtyNodes[txid] = append(r.DirtyNodes[txid], node)
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400187 return node.MakeBranch(txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400188 }
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400189 result = r.node.Update(ctx, path, data, strict, txid, trackDirty)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400190 } else {
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400191 result = r.node.Update(ctx, path, data, strict, "", nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400192 }
193
Stephane Barbariee0a4c792019-01-16 11:26:29 -0500194 if result != nil {
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400195 if r.GetProxy().FullPath != r.GetProxy().Path {
npujar467fe752020-01-16 20:17:45 +0530196 r.syncParent(ctx, result, txid)
Stephane Barbariee0a4c792019-01-16 11:26:29 -0500197 } else {
npujar467fe752020-01-16 20:17:45 +0530198 result.Finalize(ctx, false)
Stephane Barbariee0a4c792019-01-16 11:26:29 -0500199 }
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500200 }
201
npujar467fe752020-01-16 20:17:45 +0530202 r.node.GetRoot().ExecuteCallbacks(ctx)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400203
204 return result
205}
206
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500207// Add creates a new object at the given path with the provided data
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400208func (r *root) Add(ctx context.Context, path string, data interface{}, txid string, makeBranch MakeBranchFunction) Revision {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400209 var result Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400210
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400211 if txid != "" {
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400212 trackDirty := func(node *node) *Branch {
Stephane Barbariee16186c2018-09-11 10:46:34 -0400213 r.DirtyNodes[txid] = append(r.DirtyNodes[txid], node)
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400214 return node.MakeBranch(txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400215 }
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400216 result = r.node.Add(ctx, path, data, txid, trackDirty)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400217 } else {
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400218 result = r.node.Add(ctx, path, data, "", nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400219 }
220
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500221 if result != nil {
npujar467fe752020-01-16 20:17:45 +0530222 result.Finalize(ctx, true)
223 r.node.GetRoot().ExecuteCallbacks(ctx)
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500224 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400225 return result
226}
227
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500228// Remove discards an object at a given path
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400229func (r *root) Remove(ctx context.Context, path string, txid string, makeBranch MakeBranchFunction) Revision {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400230 var result Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400231
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400232 if txid != "" {
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400233 trackDirty := func(node *node) *Branch {
Stephane Barbariee16186c2018-09-11 10:46:34 -0400234 r.DirtyNodes[txid] = append(r.DirtyNodes[txid], node)
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400235 return node.MakeBranch(txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400236 }
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400237 result = r.node.Remove(ctx, path, txid, trackDirty)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400238 } else {
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400239 result = r.node.Remove(ctx, path, "", nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400240 }
241
npujar467fe752020-01-16 20:17:45 +0530242 r.node.GetRoot().ExecuteCallbacks(ctx)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400243
244 return result
245}
246
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500247// MakeLatest updates a branch with the latest node revision
npujar467fe752020-01-16 20:17:45 +0530248func (r *root) MakeLatest(ctx context.Context, branch *Branch, revision Revision, changeAnnouncement []ChangeTuple) {
249 r.makeLatest(ctx, branch, revision, changeAnnouncement)
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400250}
251
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500252func (r *root) MakeRevision(branch *Branch, data interface{}, children map[string][]Revision) Revision {
253 if r.RevisionClass.(reflect.Type) == reflect.TypeOf(PersistedRevision{}) {
254 return NewPersistedRevision(branch, data, children)
255 }
256
257 return NewNonPersistedRevision(r, branch, data, children)
258}
259
npujar467fe752020-01-16 20:17:45 +0530260func (r *root) makeLatest(ctx context.Context, branch *Branch, revision Revision, changeAnnouncement []ChangeTuple) {
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400261 r.node.makeLatest(branch, revision, changeAnnouncement)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400262
263 if r.KvStore != nil && branch.Txid == "" {
264 tags := make(map[string]string)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500265 for k, v := range r.node.Tags {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400266 tags[k] = v.GetHash()
267 }
268 data := &rootData{
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500269 Latest: branch.GetLatest().GetHash(),
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400270 Tags: tags,
271 }
272 if blob, err := json.Marshal(data); err != nil {
273 // TODO report error
274 } else {
Girish Kumarf56a4682020-03-20 20:07:46 +0000275 logger.Debugf("Changing root to : %s", string(blob))
npujar467fe752020-01-16 20:17:45 +0530276 if err := r.KvStore.Put(ctx, "root", blob); err != nil {
Girish Kumarf56a4682020-03-20 20:07:46 +0000277 logger.Errorf("failed to properly put value in kvstore - err: %s", err.Error())
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400278 }
279 }
280 }
281}
282
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400283type rootData struct {
Kent Hagerman0ab4cb22019-04-24 13:13:35 -0400284 Latest string `json:"latest"`
285 Tags map[string]string `json:"tags"`
286}