blob: 4aae7f4864b09b94ea1aa6af3e3ec5282c3613ca [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 (
Stephane Barbarie694e2b92018-09-07 12:17:36 -040019 "crypto/md5"
Stephane Barbarie694e2b92018-09-07 12:17:36 -040020 "errors"
khenaidoob9203542018-09-17 22:56:37 -040021 "fmt"
22 "github.com/opencord/voltha-go/common/log"
23 "reflect"
Stephane Barbarie694e2b92018-09-07 12:17:36 -040024 "runtime"
khenaidoob9203542018-09-17 22:56:37 -040025 "strings"
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040026)
27
Stephane Barbarieec0919b2018-09-05 14:14:29 -040028type OperationContext struct {
29 Path string
30 Data interface{}
31 FieldName string
32 ChildKey string
33}
34
35func NewOperationContext(path string, data interface{}, fieldName string, childKey string) *OperationContext {
36 oc := &OperationContext{
37 Path: path,
38 Data: data,
39 FieldName: fieldName,
40 ChildKey: childKey,
41 }
42 return oc
43}
44
45func (oc *OperationContext) Update(data interface{}) *OperationContext {
46 oc.Data = data
47 return oc
48}
49
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040050type Proxy struct {
51 Root *Root
52 Node *Node
53 Path string
54 Exclusive bool
Stephane Barbarie694e2b92018-09-07 12:17:36 -040055 Callbacks map[CallbackType]map[string]CallbackTuple
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040056}
57
58func NewProxy(root *Root, node *Node, path string, exclusive bool) *Proxy {
Stephane Barbarie694e2b92018-09-07 12:17:36 -040059 callbacks := make(map[CallbackType]map[string]CallbackTuple)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040060 p := &Proxy{
61 Root: root,
62 Node: node,
63 Exclusive: exclusive,
64 Path: path,
Stephane Barbarie694e2b92018-09-07 12:17:36 -040065 Callbacks: callbacks,
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040066 }
67 return p
68}
69
70func (p *Proxy) Get(path string, depth int, deep bool, txid string) interface{} {
71 return p.Node.Get(path, "", depth, deep, txid)
72}
73
74func (p *Proxy) Update(path string, data interface{}, strict bool, txid string) interface{} {
75 if !strings.HasPrefix(path, "/") {
76 fmt.Errorf("invalid path: %s", path)
77 return nil
78 }
79 var fullPath string
80 if path == "/" {
81 fullPath = p.Path
82 } else {
83 fullPath = p.Path + path
84 }
Stephane Barbariee16186c2018-09-11 10:46:34 -040085 return p.Root.Update(fullPath, data, strict, txid, nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040086}
87
88func (p *Proxy) Add(path string, data interface{}, txid string) interface{} {
89 if !strings.HasPrefix(path, "/") {
90 fmt.Errorf("invalid path: %s", path)
91 return nil
92 }
93 var fullPath string
94 if path == "/" {
95 fullPath = p.Path
96 } else {
97 fullPath = p.Path + path
98 }
Stephane Barbariee16186c2018-09-11 10:46:34 -040099 return p.Root.Add(fullPath, data, txid, nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400100}
101
102func (p *Proxy) Remove(path string, txid string) interface{} {
103 if !strings.HasPrefix(path, "/") {
104 fmt.Errorf("invalid path: %s", path)
105 return nil
106 }
107 var fullPath string
108 if path == "/" {
109 fullPath = p.Path
110 } else {
111 fullPath = p.Path + path
112 }
Stephane Barbariee16186c2018-09-11 10:46:34 -0400113 return p.Root.Remove(fullPath, txid, nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400114}
115
116func (p *Proxy) openTransaction() *Transaction {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400117 txid := p.Root.MakeTxBranch()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400118 return NewTransaction(p, txid)
119}
120
121func (p *Proxy) commitTransaction(txid string) {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400122 p.Root.FoldTxBranch(txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400123}
124
125func (p *Proxy) cancelTransaction(txid string) {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400126 p.Root.DeleteTxBranch(txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400127}
128
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400129//type CallbackFunction func(context context.Context, args ...interface{})
130type CallbackFunction func(args ...interface{}) interface{}
131type CallbackTuple struct {
132 callback CallbackFunction
133 args []interface{}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400134}
135
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400136func (tuple *CallbackTuple) Execute(context interface{}) interface{} {
137 newArgs := []interface{}{}
138 if context != nil {
139 newArgs = append(newArgs, context)
140 }
141 newArgs = append(newArgs, tuple.args...)
142 return tuple.callback(newArgs...)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400143}
144
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400145func (p *Proxy) RegisterCallback(callbackType CallbackType, callback CallbackFunction, args ...interface{}) {
146 if _, exists := p.Callbacks[callbackType]; !exists {
147 p.Callbacks[callbackType] = make(map[string]CallbackTuple)
148 }
149 funcName := runtime.FuncForPC(reflect.ValueOf(callback).Pointer()).Name()
150 log.Debugf("value of function: %s", funcName)
151 funcHash := fmt.Sprintf("%x", md5.Sum([]byte(funcName)))[:12]
152
153 p.Callbacks[callbackType][funcHash] = CallbackTuple{callback, args}
154}
155
156func (p *Proxy) UnregisterCallback(callbackType CallbackType, callback CallbackFunction, args ...interface{}) {
157 if _, exists := p.Callbacks[callbackType]; !exists {
158 log.Errorf("no such callback type - %s", callbackType.String())
159 return
160 }
161 // TODO: Not sure if this is the best way to do it.
162 funcName := runtime.FuncForPC(reflect.ValueOf(callback).Pointer()).Name()
163 log.Debugf("value of function: %s", funcName)
164 funcHash := fmt.Sprintf("%x", md5.Sum([]byte(funcName)))[:12]
165 if _, exists := p.Callbacks[callbackType][funcHash]; !exists {
166 log.Errorf("function with hash value: '%s' not registered with callback type: '%s'", funcHash, callbackType)
167 return
168 }
169 delete(p.Callbacks[callbackType], funcHash)
170}
171
172func (p *Proxy) invoke(callback CallbackTuple, context interface{}) (result interface{}, err error) {
173 defer func() {
174 if r := recover(); r != nil {
175 errStr := fmt.Sprintf("callback error occurred: %+v", r)
176 err = errors.New(errStr)
177 log.Error(errStr)
178 }
179 }()
180
181 result = callback.Execute(context)
182
183 return result, err
184}
185
186//func (p *Proxy) InvokeCallbacks(callbackType CallbackType, context context.Context, proceedOnError bool) {
187func (p *Proxy) InvokeCallbacks(args ...interface{}) interface{} {
188 callbackType := args[0].(CallbackType)
189 context := args[1]
190 proceedOnError := args[2].(bool)
191
192 var err error
193
194 if _, exists := p.Callbacks[callbackType]; exists {
195 for _, callback := range p.Callbacks[callbackType] {
196 if context, err = p.invoke(callback, context); err != nil {
197 if !proceedOnError {
198 log.Info("An error occurred. Stopping callback invocation")
199 break
200 }
201 log.Info("An error occurred. Invoking next callback")
202 }
203 }
204 }
205 return context
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400206}