blob: 27a67466f72f9d354780a540615094e4dd8e1367 [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"
24)
25
26type Root struct {
27 *Node
28 DirtyNodes map[string]*Node
29 KvStore *Backend
30 Loading bool
31 RevisionClass interface{}
32 Callbacks []func() interface{}
33 NotificationCallbacks []func() interface{}
34}
35
36func NewRoot(initialData interface{}, kvStore *Backend, revisionClass interface{}) *Root {
37 root := &Root{}
38 root.KvStore = kvStore
39 root.DirtyNodes = make(map[string]*Node)
40 root.Loading = false
41 if kvStore != nil /*&& FIXME: RevisionClass is a subclass of PersistedConfigRevision */ {
42 revisionClass = reflect.TypeOf(PersistedRevision{})
43 }
44 root.RevisionClass = revisionClass
45 root.Callbacks = []func() interface{}{}
46 root.NotificationCallbacks = []func() interface{}{}
47
48 root.Node = NewNode(root, initialData, false, "")
49
50 return root
51}
52
53func (r *Root) makeRevision(branch *Branch, data interface{}, children map[string][]*Revision) *Revision {
54
55 return &Revision{}
56}
57
58func (r *Root) makeTxBranch() string {
59 txid_bin, _ := uuid.New().MarshalBinary()
60 txid := hex.EncodeToString(txid_bin)[:12]
61 r.DirtyNodes[txid] = r.Node
62 r.Node.makeTxBranch(txid)
63 return txid
64}
65
66func (r *Root) deleteTxBranch(txid string) {
67 for _, dirtyNode := range r.DirtyNodes {
68 dirtyNode.deleteTxBranch(txid)
69 }
70 delete(r.DirtyNodes, txid)
71}
72
73func (r *Root) foldTxBranch(txid string) {
74 // TODO: implement foldTxBranch
75 // if err := r.Node.mergeTxBranch(txid, dryRun=true); err != nil {
76 // r.deleteTxBranch(txid)
77 // } else {
78 // r.Node.mergeTxBranch(txid)
79 // r.executeCallbacks()
80 // }
81}
82
83func (r *Root) executeCallbacks() {
84 for len(r.Callbacks) > 0 {
85 callback := r.Callbacks[0]
86 r.Callbacks = r.Callbacks[1:]
87 callback()
88 }
89 for len(r.NotificationCallbacks) > 0 {
90 callback := r.NotificationCallbacks[0]
91 r.NotificationCallbacks = r.NotificationCallbacks[1:]
92 callback()
93 }
94}
95
96func (r *Root) noCallbacks() bool {
97 return len(r.Callbacks) == 0
98}
99
100func (r *Root) addCallback(callback func() interface{}) {
101 r.Callbacks = append(r.Callbacks, callback)
102}
103func (r *Root) addNotificationCallback(callback func() interface{}) {
104 r.NotificationCallbacks = append(r.NotificationCallbacks, callback)
105}
106
107func (r *Root) Update(path string, data interface{}, strict bool, txid string, makeBranch t_makeBranch) *Revision {
108 var result *Revision
109 // FIXME: the more i look at this... i think i need to implement an interface for Node & root
110
111 if makeBranch == nil {
112 // TODO: raise error
113 }
114
115 if r.noCallbacks() {
116 // TODO: raise error
117 }
118
119 if txid != "" {
120 //dirtied := r.DirtyNodes[txid]
121
122 trackDirty := func(node *Node) *Branch {
123 //dirtied.Add(Node)
124 return node.makeTxBranch(txid)
125 }
126 result = r.Node.Update(path, data, strict, txid, trackDirty)
127 } else {
128 result = r.Node.Update(path, data, strict, "", nil)
129 }
130
131 r.executeCallbacks()
132
133 return result
134}
135
136func (r *Root) Add(path string, data interface{}, txid string, makeBranch t_makeBranch) *Revision {
137 var result *Revision
138 // FIXME: the more i look at this... i think i need to implement an interface for Node & root
139
140 if makeBranch == nil {
141 // TODO: raise error
142 }
143
144 if r.noCallbacks() {
145 // TODO: raise error
146 }
147
148 if txid != "" {
149 //dirtied := r.DirtyNodes[txid]
150
151 trackDirty := func(node *Node) *Branch {
152 //dirtied.Add(Node)
153 return node.makeTxBranch(txid)
154 }
155 result = r.Node.Add(path, data, txid, trackDirty)
156 } else {
157 result = r.Node.Add(path, data, "", nil)
158 }
159
160 r.executeCallbacks()
161
162 return result
163}
164
165func (r *Root) Remove(path string, txid string, makeBranch t_makeBranch) *Revision {
166 var result *Revision
167 // FIXME: the more i look at this... i think i need to implement an interface for Node & root
168
169 if makeBranch == nil {
170 // TODO: raise error
171 }
172
173 if r.noCallbacks() {
174 // TODO: raise error
175 }
176
177 if txid != "" {
178 //dirtied := r.DirtyNodes[txid]
179
180 trackDirty := func(node *Node) *Branch {
181 //dirtied.Add(Node)
182 return node.makeTxBranch(txid)
183 }
184 result = r.Node.Remove(path, txid, trackDirty)
185 } else {
186 result = r.Node.Remove(path, "", nil)
187 }
188
189 r.executeCallbacks()
190
191 return result
192}
193
194func (r *Root) Load(rootClass interface{}) *Root {
195 //fakeKvStore := &Backend{}
196 //root := NewRoot(rootClass, fakeKvStore, PersistedRevision{})
197 //r.KvStore = KvStore
198 r.loadFromPersistence(rootClass)
199 return r
200}
201
202func (r *Root) LoadLatest(hash string) {
203 r.Node.LoadLatest(r.KvStore, hash)
204}
205
206type rootData struct {
207 Latest string `json:GetLatest`
208 Tags map[string]string `json:Tags`
209}
210
211func (r *Root) loadFromPersistence(rootClass interface{}) {
212 var data rootData
213
214 r.Loading = true
215 blob, _ := r.KvStore.Get("root")
216
217 if err := json.Unmarshal(blob.Value.([]byte), &data); err != nil {
218 fmt.Errorf("problem to unmarshal blob - error:%s\n", err.Error())
219 }
220
221 for tag, hash := range data.Tags {
222 r.Node.LoadLatest(r.KvStore, hash)
223 r.Node.Tags[tag] = r.Node.Latest()
224 }
225
226 r.Node.LoadLatest(r.KvStore, data.Latest)
227 r.Loading = false
228}