blob: 182dcdd8ff60a29e00b0c6dfa1b8e0751f29baaf [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 Barbarie694e2b92018-09-07 12:17:36 -040020 "crypto/md5"
Stephane Barbarie694e2b92018-09-07 12:17:36 -040021 "errors"
khenaidoob9203542018-09-17 22:56:37 -040022 "fmt"
23 "github.com/opencord/voltha-go/common/log"
24 "reflect"
Stephane Barbarie694e2b92018-09-07 12:17:36 -040025 "runtime"
khenaidoob9203542018-09-17 22:56:37 -040026 "strings"
Stephane Barbariea188d942018-10-16 16:43:04 -040027 "sync"
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040028)
29
Stephane Barbariedc5022d2018-11-19 15:21:44 -050030// OperationContext holds details on the information used during an operation
Stephane Barbarieec0919b2018-09-05 14:14:29 -040031type OperationContext struct {
32 Path string
33 Data interface{}
34 FieldName string
35 ChildKey string
36}
37
Stephane Barbariedc5022d2018-11-19 15:21:44 -050038// NewOperationContext instantiates a new OperationContext structure
Stephane Barbarieec0919b2018-09-05 14:14:29 -040039func NewOperationContext(path string, data interface{}, fieldName string, childKey string) *OperationContext {
40 oc := &OperationContext{
41 Path: path,
42 Data: data,
43 FieldName: fieldName,
44 ChildKey: childKey,
45 }
46 return oc
47}
48
Stephane Barbariedc5022d2018-11-19 15:21:44 -050049// Update applies new data to the context structure
Stephane Barbarieec0919b2018-09-05 14:14:29 -040050func (oc *OperationContext) Update(data interface{}) *OperationContext {
51 oc.Data = data
52 return oc
53}
54
Stephane Barbariedc5022d2018-11-19 15:21:44 -050055// Proxy holds the information for a specific location with the data model
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040056type Proxy struct {
Stephane Barbariedc5022d2018-11-19 15:21:44 -050057 sync.RWMutex
Stephane Barbariec53a2752019-03-08 17:50:10 -050058 Root *root
59 Node *node
60 ParentNode *node
61 Path string
62 FullPath string
63 Exclusive bool
64 Callbacks map[CallbackType]map[string]*CallbackTuple
65 Operation ProxyOperation
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040066}
67
Stephane Barbariedc5022d2018-11-19 15:21:44 -050068// NewProxy instantiates a new proxy to a specific location
Stephane Barbarie1ab43272018-12-08 21:42:13 -050069func NewProxy(root *root, node *node, parentNode *node, path string, fullPath string, exclusive bool) *Proxy {
Stephane Barbariedc5022d2018-11-19 15:21:44 -050070 callbacks := make(map[CallbackType]map[string]*CallbackTuple)
Stephane Barbariea188d942018-10-16 16:43:04 -040071 if fullPath == "/" {
72 fullPath = ""
73 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040074 p := &Proxy{
Stephane Barbariec53a2752019-03-08 17:50:10 -050075 Root: root,
76 Node: node,
77 ParentNode: parentNode,
78 Exclusive: exclusive,
79 Path: path,
80 FullPath: fullPath,
81 Callbacks: callbacks,
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040082 }
83 return p
84}
85
Stephane Barbariedc5022d2018-11-19 15:21:44 -050086// GetRoot returns the root attribute of the proxy
87func (p *Proxy) GetRoot() *root {
Stephane Barbariedc5022d2018-11-19 15:21:44 -050088 return p.Root
89}
90
91// getPath returns the path attribute of the proxy
92func (p *Proxy) getPath() string {
Stephane Barbariedc5022d2018-11-19 15:21:44 -050093 return p.Path
94}
95
96// getFullPath returns the full path attribute of the proxy
97func (p *Proxy) getFullPath() string {
Stephane Barbariedc5022d2018-11-19 15:21:44 -050098 return p.FullPath
99}
100
101// getCallbacks returns the full list of callbacks associated to the proxy
102func (p *Proxy) getCallbacks(callbackType CallbackType) map[string]*CallbackTuple {
Stephane Barbariec92d1072019-06-07 16:21:49 -0400103 if p != nil {
104 if cb, exists := p.Callbacks[callbackType]; exists {
105 return cb
106 }
107 } else {
108 log.Debugw("proxy-is-nil", log.Fields{"callback-type": callbackType.String()})
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500109 }
110 return nil
111}
112
113// getCallback returns a specific callback matching the type and function hash
114func (p *Proxy) getCallback(callbackType CallbackType, funcHash string) *CallbackTuple {
Stephane Barbarie260a5632019-02-26 16:12:49 -0500115 p.Lock()
116 defer p.Unlock()
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500117 if tuple, exists := p.Callbacks[callbackType][funcHash]; exists {
118 return tuple
119 }
120 return nil
121}
122
123// setCallbacks applies a callbacks list to a type
124func (p *Proxy) setCallbacks(callbackType CallbackType, callbacks map[string]*CallbackTuple) {
125 p.Lock()
126 defer p.Unlock()
127 p.Callbacks[callbackType] = callbacks
128}
129
130// setCallback applies a callback to a type and hash value
131func (p *Proxy) setCallback(callbackType CallbackType, funcHash string, tuple *CallbackTuple) {
132 p.Lock()
133 defer p.Unlock()
134 p.Callbacks[callbackType][funcHash] = tuple
135}
136
137// DeleteCallback removes a callback matching the type and hash
138func (p *Proxy) DeleteCallback(callbackType CallbackType, funcHash string) {
139 p.Lock()
140 defer p.Unlock()
141 delete(p.Callbacks[callbackType], funcHash)
142}
143
Stephane Barbariec53a2752019-03-08 17:50:10 -0500144// CallbackType is an enumerated value to express when a callback should be executed
145type ProxyOperation uint8
146
147// Enumerated list of callback types
148const (
149 PROXY_GET ProxyOperation = iota
150 PROXY_LIST
151 PROXY_ADD
152 PROXY_UPDATE
153 PROXY_REMOVE
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400154 PROXY_CREATE
Stephane Barbariec92d1072019-06-07 16:21:49 -0400155 PROXY_WATCH
Stephane Barbariec53a2752019-03-08 17:50:10 -0500156)
157
Stephane Barbarie802aca42019-05-21 12:19:28 -0400158var proxyOperationTypes = []string{
159 "PROXY_GET",
160 "PROXY_LIST",
161 "PROXY_ADD",
162 "PROXY_UPDATE",
163 "PROXY_REMOVE",
164 "PROXY_CREATE",
Stephane Barbariec92d1072019-06-07 16:21:49 -0400165 "PROXY_WATCH",
Stephane Barbarie802aca42019-05-21 12:19:28 -0400166}
167
168func (t ProxyOperation) String() string {
169 return proxyOperationTypes[t]
170}
171
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500172// parseForControlledPath verifies if a proxy path matches a pattern
173// for locations that need to be access controlled.
Stephane Barbariea188d942018-10-16 16:43:04 -0400174func (p *Proxy) parseForControlledPath(path string) (pathLock string, controlled bool) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500175 // TODO: Add other path prefixes that may need control
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500176 if strings.HasPrefix(path, "/devices") ||
177 strings.HasPrefix(path, "/logical_devices") ||
178 strings.HasPrefix(path, "/adapters") {
179
Stephane Barbariea188d942018-10-16 16:43:04 -0400180 split := strings.SplitN(path, "/", -1)
181 switch len(split) {
182 case 2:
183 controlled = false
184 pathLock = ""
185 break
186 case 3:
187 fallthrough
188 default:
189 pathLock = fmt.Sprintf("%s/%s", split[1], split[2])
190 controlled = true
191 }
192 }
193 return pathLock, controlled
194}
195
Stephane Barbarieaa467942019-02-06 14:09:44 -0500196// List will retrieve information from the data model at the specified path location
197// A list operation will force access to persistence storage
198func (p *Proxy) List(path string, depth int, deep bool, txid string) interface{} {
199 var effectivePath string
200 if path == "/" {
201 effectivePath = p.getFullPath()
202 } else {
203 effectivePath = p.getFullPath() + path
204 }
205
206 pathLock, controlled := p.parseForControlledPath(effectivePath)
207
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400208 log.Debugw("proxy-list", log.Fields{
209 "path": path,
210 "effective": effectivePath,
211 "pathLock": pathLock,
212 "controlled": controlled,
213 })
Stephane Barbarieaa467942019-02-06 14:09:44 -0500214
215 pac := PAC().ReservePath(effectivePath, p, pathLock)
216 defer PAC().ReleasePath(pathLock)
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400217 p.Operation = PROXY_LIST
Stephane Barbarieaa467942019-02-06 14:09:44 -0500218 pac.SetProxy(p)
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400219 defer func(op ProxyOperation) {
220 pac.getProxy().Operation = op
221 }(PROXY_GET)
Stephane Barbarieaa467942019-02-06 14:09:44 -0500222
223 rv := pac.List(path, depth, deep, txid, controlled)
224
225 return rv
226}
227
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500228// Get will retrieve information from the data model at the specified path location
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400229func (p *Proxy) Get(path string, depth int, deep bool, txid string) interface{} {
Stephane Barbariea188d942018-10-16 16:43:04 -0400230 var effectivePath string
231 if path == "/" {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500232 effectivePath = p.getFullPath()
Stephane Barbariea188d942018-10-16 16:43:04 -0400233 } else {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500234 effectivePath = p.getFullPath() + path
Stephane Barbariea188d942018-10-16 16:43:04 -0400235 }
236
237 pathLock, controlled := p.parseForControlledPath(effectivePath)
238
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400239 log.Debugw("proxy-get", log.Fields{
240 "path": path,
241 "effective": effectivePath,
242 "pathLock": pathLock,
243 "controlled": controlled,
244 })
Stephane Barbariea188d942018-10-16 16:43:04 -0400245
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500246 pac := PAC().ReservePath(effectivePath, p, pathLock)
247 defer PAC().ReleasePath(pathLock)
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400248 p.Operation = PROXY_GET
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500249 pac.SetProxy(p)
Stephane Barbariea188d942018-10-16 16:43:04 -0400250
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500251 rv := pac.Get(path, depth, deep, txid, controlled)
252
253 return rv
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400254}
255
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500256// Update will modify information in the data model at the specified location with the provided data
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400257func (p *Proxy) Update(path string, data interface{}, strict bool, txid string) interface{} {
258 if !strings.HasPrefix(path, "/") {
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -0400259 log.Errorf("invalid path: %s", path)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400260 return nil
261 }
262 var fullPath string
Stephane Barbariea188d942018-10-16 16:43:04 -0400263 var effectivePath string
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400264 if path == "/" {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500265 fullPath = p.getPath()
266 effectivePath = p.getFullPath()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400267 } else {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500268 fullPath = p.getPath() + path
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500269 effectivePath = p.getFullPath() + path
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400270 }
Stephane Barbarie126101e2018-10-11 16:18:48 -0400271
Stephane Barbariea188d942018-10-16 16:43:04 -0400272 pathLock, controlled := p.parseForControlledPath(effectivePath)
273
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400274 log.Debugw("proxy-update", log.Fields{
275 "path": path,
276 "effective": effectivePath,
277 "full": fullPath,
278 "pathLock": pathLock,
279 "controlled": controlled,
280 })
Stephane Barbariea188d942018-10-16 16:43:04 -0400281
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500282 pac := PAC().ReservePath(effectivePath, p, pathLock)
283 defer PAC().ReleasePath(pathLock)
Stephane Barbariec53a2752019-03-08 17:50:10 -0500284
285 p.Operation = PROXY_UPDATE
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500286 pac.SetProxy(p)
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400287 defer func(op ProxyOperation) {
288 pac.getProxy().Operation = op
289 }(PROXY_GET)
Kent Hagerman0ab4cb22019-04-24 13:13:35 -0400290 log.Debugw("proxy-operation--update", log.Fields{"operation": p.Operation})
Stephane Barbariec53a2752019-03-08 17:50:10 -0500291
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500292 return pac.Update(fullPath, data, strict, txid, controlled)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400293}
294
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500295// AddWithID will insert new data at specified location.
296// This method also allows the user to specify the ID of the data entry to ensure
297// that access control is active while inserting the information.
298func (p *Proxy) AddWithID(path string, id string, data interface{}, txid string) interface{} {
299 if !strings.HasPrefix(path, "/") {
300 log.Errorf("invalid path: %s", path)
301 return nil
302 }
303 var fullPath string
304 var effectivePath string
305 if path == "/" {
306 fullPath = p.getPath()
307 effectivePath = p.getFullPath()
308 } else {
309 fullPath = p.getPath() + path
310 effectivePath = p.getFullPath() + path + "/" + id
311 }
312
313 pathLock, controlled := p.parseForControlledPath(effectivePath)
314
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400315 log.Debugw("proxy-add-with-id", log.Fields{
316 "path": path,
317 "effective": effectivePath,
318 "full": fullPath,
319 "pathLock": pathLock,
320 "controlled": controlled,
321 })
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500322
323 pac := PAC().ReservePath(path, p, pathLock)
324 defer PAC().ReleasePath(pathLock)
Stephane Barbariec53a2752019-03-08 17:50:10 -0500325
326 p.Operation = PROXY_ADD
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400327 defer func(op ProxyOperation) {
328 pac.getProxy().Operation = op
329 }(PROXY_GET)
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400330
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500331 pac.SetProxy(p)
332
Kent Hagerman0ab4cb22019-04-24 13:13:35 -0400333 log.Debugw("proxy-operation--add", log.Fields{"operation": p.Operation})
Stephane Barbariec53a2752019-03-08 17:50:10 -0500334
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500335 return pac.Add(fullPath, data, txid, controlled)
336}
337
338// Add will insert new data at specified location.
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400339func (p *Proxy) Add(path string, data interface{}, txid string) interface{} {
340 if !strings.HasPrefix(path, "/") {
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -0400341 log.Errorf("invalid path: %s", path)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400342 return nil
343 }
344 var fullPath string
Stephane Barbariea188d942018-10-16 16:43:04 -0400345 var effectivePath string
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400346 if path == "/" {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500347 fullPath = p.getPath()
348 effectivePath = p.getFullPath()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400349 } else {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500350 fullPath = p.getPath() + path
351 effectivePath = p.getFullPath() + path
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400352 }
Stephane Barbariea188d942018-10-16 16:43:04 -0400353
354 pathLock, controlled := p.parseForControlledPath(effectivePath)
355
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400356 log.Debugw("proxy-add", log.Fields{
357 "path": path,
358 "effective": effectivePath,
359 "full": fullPath,
360 "pathLock": pathLock,
361 "controlled": controlled,
362 })
Stephane Barbariea188d942018-10-16 16:43:04 -0400363
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500364 pac := PAC().ReservePath(path, p, pathLock)
365 defer PAC().ReleasePath(pathLock)
Stephane Barbariec53a2752019-03-08 17:50:10 -0500366
367 p.Operation = PROXY_ADD
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500368 pac.SetProxy(p)
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400369 defer func(op ProxyOperation) {
370 pac.getProxy().Operation = op
371 }(PROXY_GET)
Stephane Barbariea188d942018-10-16 16:43:04 -0400372
Kent Hagerman0ab4cb22019-04-24 13:13:35 -0400373 log.Debugw("proxy-operation--add", log.Fields{"operation": p.Operation})
Stephane Barbariec53a2752019-03-08 17:50:10 -0500374
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500375 return pac.Add(fullPath, data, txid, controlled)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400376}
377
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500378// Remove will delete an entry at the specified location
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400379func (p *Proxy) Remove(path string, txid string) interface{} {
380 if !strings.HasPrefix(path, "/") {
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -0400381 log.Errorf("invalid path: %s", path)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400382 return nil
383 }
384 var fullPath string
Stephane Barbariea188d942018-10-16 16:43:04 -0400385 var effectivePath string
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400386 if path == "/" {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500387 fullPath = p.getPath()
388 effectivePath = p.getFullPath()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400389 } else {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500390 fullPath = p.getPath() + path
391 effectivePath = p.getFullPath() + path
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400392 }
Stephane Barbariea188d942018-10-16 16:43:04 -0400393
394 pathLock, controlled := p.parseForControlledPath(effectivePath)
395
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400396 log.Debugw("proxy-remove", log.Fields{
397 "path": path,
398 "effective": effectivePath,
399 "full": fullPath,
400 "pathLock": pathLock,
401 "controlled": controlled,
402 })
Stephane Barbariea188d942018-10-16 16:43:04 -0400403
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500404 pac := PAC().ReservePath(effectivePath, p, pathLock)
405 defer PAC().ReleasePath(pathLock)
Stephane Barbariec53a2752019-03-08 17:50:10 -0500406
407 p.Operation = PROXY_REMOVE
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500408 pac.SetProxy(p)
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400409 defer func(op ProxyOperation) {
410 pac.getProxy().Operation = op
411 }(PROXY_GET)
Stephane Barbariea188d942018-10-16 16:43:04 -0400412
Kent Hagerman0ab4cb22019-04-24 13:13:35 -0400413 log.Debugw("proxy-operation--remove", log.Fields{"operation": p.Operation})
Stephane Barbariec53a2752019-03-08 17:50:10 -0500414
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500415 return pac.Remove(fullPath, txid, controlled)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400416}
417
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400418// CreateProxy to interact with specific path directly
419func (p *Proxy) CreateProxy(path string, exclusive bool) *Proxy {
420 if !strings.HasPrefix(path, "/") {
421 log.Errorf("invalid path: %s", path)
422 return nil
423 }
424
425 var fullPath string
426 var effectivePath string
427 if path == "/" {
428 fullPath = p.getPath()
429 effectivePath = p.getFullPath()
430 } else {
431 fullPath = p.getPath() + path
432 effectivePath = p.getFullPath() + path
433 }
434
435 pathLock, controlled := p.parseForControlledPath(effectivePath)
436
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400437 log.Debugw("proxy-create", log.Fields{
438 "path": path,
439 "effective": effectivePath,
440 "full": fullPath,
441 "pathLock": pathLock,
442 "controlled": controlled,
443 })
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400444
445 pac := PAC().ReservePath(path, p, pathLock)
446 defer PAC().ReleasePath(pathLock)
447
448 p.Operation = PROXY_CREATE
449 pac.SetProxy(p)
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400450 defer func(op ProxyOperation) {
451 pac.getProxy().Operation = op
452 }(PROXY_GET)
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400453
Kent Hagerman0ab4cb22019-04-24 13:13:35 -0400454 log.Debugw("proxy-operation--create-proxy", log.Fields{"operation": p.Operation})
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400455
456 return pac.CreateProxy(fullPath, exclusive, controlled)
457}
458
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500459// OpenTransaction creates a new transaction branch to isolate operations made to the data model
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400460func (p *Proxy) OpenTransaction() *Transaction {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500461 txid := p.GetRoot().MakeTxBranch()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400462 return NewTransaction(p, txid)
463}
464
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500465// commitTransaction will apply and merge modifications made in the transaction branch to the data model
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400466func (p *Proxy) commitTransaction(txid string) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500467 p.GetRoot().FoldTxBranch(txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400468}
469
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500470// cancelTransaction will terminate a transaction branch along will all changes within it
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400471func (p *Proxy) cancelTransaction(txid string) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500472 p.GetRoot().DeleteTxBranch(txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400473}
474
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500475// CallbackFunction is a type used to define callback functions
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400476type CallbackFunction func(args ...interface{}) interface{}
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500477
478// CallbackTuple holds the function and arguments details of a callback
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400479type CallbackTuple struct {
480 callback CallbackFunction
481 args []interface{}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400482}
483
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500484// Execute will process the a callback with its provided arguments
485func (tuple *CallbackTuple) Execute(contextArgs []interface{}) interface{} {
Stephane Barbarie126101e2018-10-11 16:18:48 -0400486 args := []interface{}{}
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500487
488 for _, ta := range tuple.args {
489 args = append(args, ta)
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400490 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500491
492 if contextArgs != nil {
493 for _, ca := range contextArgs {
494 args = append(args, ca)
495 }
496 }
497
Stephane Barbarie126101e2018-10-11 16:18:48 -0400498 return tuple.callback(args...)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400499}
500
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500501// RegisterCallback associates a callback to the proxy
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400502func (p *Proxy) RegisterCallback(callbackType CallbackType, callback CallbackFunction, args ...interface{}) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500503 if p.getCallbacks(callbackType) == nil {
504 p.setCallbacks(callbackType, make(map[string]*CallbackTuple))
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400505 }
506 funcName := runtime.FuncForPC(reflect.ValueOf(callback).Pointer()).Name()
507 log.Debugf("value of function: %s", funcName)
508 funcHash := fmt.Sprintf("%x", md5.Sum([]byte(funcName)))[:12]
509
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500510 p.setCallback(callbackType, funcHash, &CallbackTuple{callback, args})
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400511}
512
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500513// UnregisterCallback removes references to a callback within a proxy
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400514func (p *Proxy) UnregisterCallback(callbackType CallbackType, callback CallbackFunction, args ...interface{}) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500515 if p.getCallbacks(callbackType) == nil {
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400516 log.Errorf("no such callback type - %s", callbackType.String())
517 return
518 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500519
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400520 funcName := runtime.FuncForPC(reflect.ValueOf(callback).Pointer()).Name()
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400521 funcHash := fmt.Sprintf("%x", md5.Sum([]byte(funcName)))[:12]
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500522
523 log.Debugf("value of function: %s", funcName)
524
525 if p.getCallback(callbackType, funcHash) == nil {
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400526 log.Errorf("function with hash value: '%s' not registered with callback type: '%s'", funcHash, callbackType)
527 return
528 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500529
530 p.DeleteCallback(callbackType, funcHash)
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400531}
532
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500533func (p *Proxy) invoke(callback *CallbackTuple, context []interface{}) (result interface{}, err error) {
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400534 defer func() {
535 if r := recover(); r != nil {
536 errStr := fmt.Sprintf("callback error occurred: %+v", r)
537 err = errors.New(errStr)
538 log.Error(errStr)
539 }
540 }()
541
542 result = callback.Execute(context)
543
544 return result, err
545}
546
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500547// InvokeCallbacks executes all callbacks associated to a specific type
Stephane Barbarie126101e2018-10-11 16:18:48 -0400548func (p *Proxy) InvokeCallbacks(args ...interface{}) (result interface{}) {
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400549 callbackType := args[0].(CallbackType)
Stephane Barbarie126101e2018-10-11 16:18:48 -0400550 proceedOnError := args[1].(bool)
551 context := args[2:]
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400552
553 var err error
554
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500555 if callbacks := p.getCallbacks(callbackType); callbacks != nil {
556 p.Lock()
557 for _, callback := range callbacks {
Stephane Barbarie126101e2018-10-11 16:18:48 -0400558 if result, err = p.invoke(callback, context); err != nil {
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400559 if !proceedOnError {
560 log.Info("An error occurred. Stopping callback invocation")
561 break
562 }
563 log.Info("An error occurred. Invoking next callback")
564 }
565 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500566 p.Unlock()
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400567 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500568
Stephane Barbariea188d942018-10-16 16:43:04 -0400569 return result
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400570}