blob: 3827ff3e07eaeb644f502fc7c1082dd87c4175f2 [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 Barbariea188d942018-10-16 16:43:04 -040026 "sync"
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040027)
28
Stephane Barbarieec0919b2018-09-05 14:14:29 -040029type OperationContext struct {
30 Path string
31 Data interface{}
32 FieldName string
33 ChildKey string
34}
35
36func NewOperationContext(path string, data interface{}, fieldName string, childKey string) *OperationContext {
37 oc := &OperationContext{
38 Path: path,
39 Data: data,
40 FieldName: fieldName,
41 ChildKey: childKey,
42 }
43 return oc
44}
45
46func (oc *OperationContext) Update(data interface{}) *OperationContext {
47 oc.Data = data
48 return oc
49}
50
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040051type Proxy struct {
Stephane Barbariea188d942018-10-16 16:43:04 -040052 sync.Mutex
Stephane Barbarie126101e2018-10-11 16:18:48 -040053 Root *root
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040054 Path string
Stephane Barbariea188d942018-10-16 16:43:04 -040055 FullPath string
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040056 Exclusive bool
Stephane Barbarie694e2b92018-09-07 12:17:36 -040057 Callbacks map[CallbackType]map[string]CallbackTuple
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040058}
59
Stephane Barbariea188d942018-10-16 16:43:04 -040060func NewProxy(root *root, path string, fullPath string, exclusive bool) *Proxy {
Stephane Barbarie694e2b92018-09-07 12:17:36 -040061 callbacks := make(map[CallbackType]map[string]CallbackTuple)
Stephane Barbariea188d942018-10-16 16:43:04 -040062 if fullPath == "/" {
63 fullPath = ""
64 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040065 p := &Proxy{
66 Root: root,
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040067 Exclusive: exclusive,
68 Path: path,
Stephane Barbariea188d942018-10-16 16:43:04 -040069 FullPath: fullPath,
Stephane Barbarie694e2b92018-09-07 12:17:36 -040070 Callbacks: callbacks,
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040071 }
72 return p
73}
74
Stephane Barbariea188d942018-10-16 16:43:04 -040075func (p *Proxy) parseForControlledPath(path string) (pathLock string, controlled bool) {
76 // TODO: Add other path prefixes they may need control
77 if strings.HasPrefix(path, "/devices") {
78 split := strings.SplitN(path, "/", -1)
79 switch len(split) {
80 case 2:
81 controlled = false
82 pathLock = ""
83 break
84 case 3:
85 fallthrough
86 default:
87 pathLock = fmt.Sprintf("%s/%s", split[1], split[2])
88 controlled = true
89 }
90 }
91 return pathLock, controlled
92}
93
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040094func (p *Proxy) Get(path string, depth int, deep bool, txid string) interface{} {
Stephane Barbariea188d942018-10-16 16:43:04 -040095 var effectivePath string
96 if path == "/" {
97 effectivePath = p.FullPath
98 } else {
99 effectivePath = p.FullPath + path
100 }
101
102 pathLock, controlled := p.parseForControlledPath(effectivePath)
103
104 var pac interface{}
105 var exists bool
106
107 p.Lock()
108 if pac, exists = GetProxyAccessControl().Cache.LoadOrStore(path, NewProxyAccessControl(p, pathLock)); !exists {
109 defer GetProxyAccessControl().Cache.Delete(pathLock)
110 }
111 p.Unlock()
112
113 return pac.(ProxyAccessControl).Get(path, depth, deep, txid, controlled)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400114}
115
116func (p *Proxy) Update(path string, data interface{}, strict bool, txid string) interface{} {
117 if !strings.HasPrefix(path, "/") {
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -0400118 log.Errorf("invalid path: %s", path)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400119 return nil
120 }
121 var fullPath string
Stephane Barbariea188d942018-10-16 16:43:04 -0400122 var effectivePath string
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400123 if path == "/" {
124 fullPath = p.Path
Stephane Barbariea188d942018-10-16 16:43:04 -0400125 effectivePath = p.FullPath
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400126 } else {
127 fullPath = p.Path + path
Stephane Barbariea188d942018-10-16 16:43:04 -0400128 effectivePath = p.FullPath + path
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400129 }
Stephane Barbarie126101e2018-10-11 16:18:48 -0400130
Stephane Barbariea188d942018-10-16 16:43:04 -0400131 pathLock, controlled := p.parseForControlledPath(effectivePath)
132
133 var pac interface{}
134 var exists bool
135
136 p.Lock()
137 if pac, exists = GetProxyAccessControl().Cache.LoadOrStore(path, NewProxyAccessControl(p, pathLock)); !exists {
138 defer GetProxyAccessControl().Cache.Delete(pathLock)
139 }
140 p.Unlock()
141
142 return pac.(ProxyAccessControl).Update(fullPath, data, strict, txid, controlled)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400143}
144
145func (p *Proxy) Add(path string, data interface{}, txid string) interface{} {
146 if !strings.HasPrefix(path, "/") {
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -0400147 log.Errorf("invalid path: %s", path)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400148 return nil
149 }
150 var fullPath string
Stephane Barbariea188d942018-10-16 16:43:04 -0400151 var effectivePath string
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400152 if path == "/" {
153 fullPath = p.Path
Stephane Barbariea188d942018-10-16 16:43:04 -0400154 effectivePath = p.FullPath
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400155 } else {
156 fullPath = p.Path + path
Stephane Barbariea188d942018-10-16 16:43:04 -0400157 effectivePath = p.FullPath + path
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400158 }
Stephane Barbariea188d942018-10-16 16:43:04 -0400159
160 pathLock, controlled := p.parseForControlledPath(effectivePath)
161
162 var pac interface{}
163 var exists bool
164
165 p.Lock()
166 if pac, exists = GetProxyAccessControl().Cache.LoadOrStore(path, NewProxyAccessControl(p, pathLock)); !exists {
167 defer GetProxyAccessControl().Cache.Delete(pathLock)
168 }
169 p.Unlock()
170
171 return pac.(ProxyAccessControl).Add(fullPath, data, txid, controlled)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400172}
173
174func (p *Proxy) Remove(path string, txid string) interface{} {
175 if !strings.HasPrefix(path, "/") {
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -0400176 log.Errorf("invalid path: %s", path)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400177 return nil
178 }
179 var fullPath string
Stephane Barbariea188d942018-10-16 16:43:04 -0400180 var effectivePath string
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400181 if path == "/" {
182 fullPath = p.Path
Stephane Barbariea188d942018-10-16 16:43:04 -0400183 effectivePath = p.FullPath
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400184 } else {
185 fullPath = p.Path + path
Stephane Barbariea188d942018-10-16 16:43:04 -0400186 effectivePath = p.FullPath + path
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400187 }
Stephane Barbariea188d942018-10-16 16:43:04 -0400188
189 pathLock, controlled := p.parseForControlledPath(effectivePath)
190
191 var pac interface{}
192 var exists bool
193
194 p.Lock()
195 if pac, exists = GetProxyAccessControl().Cache.LoadOrStore(path, NewProxyAccessControl(p, pathLock)); !exists {
196 defer GetProxyAccessControl().Cache.Delete(pathLock)
197 }
198 p.Unlock()
199
200 return pac.(ProxyAccessControl).Remove(fullPath, txid, controlled)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400201}
202
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400203func (p *Proxy) OpenTransaction() *Transaction {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400204 txid := p.Root.MakeTxBranch()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400205 return NewTransaction(p, txid)
206}
207
208func (p *Proxy) commitTransaction(txid string) {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400209 p.Root.FoldTxBranch(txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400210}
211
212func (p *Proxy) cancelTransaction(txid string) {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400213 p.Root.DeleteTxBranch(txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400214}
215
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400216type CallbackFunction func(args ...interface{}) interface{}
217type CallbackTuple struct {
218 callback CallbackFunction
219 args []interface{}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400220}
221
Stephane Barbarie126101e2018-10-11 16:18:48 -0400222func (tuple *CallbackTuple) Execute(contextArgs interface{}) interface{} {
223 args := []interface{}{}
224 args = append(args, tuple.args...)
225 if contextArgs != nil {
226 args = append(args, contextArgs)
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400227 }
Stephane Barbarie126101e2018-10-11 16:18:48 -0400228 return tuple.callback(args...)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400229}
230
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400231func (p *Proxy) RegisterCallback(callbackType CallbackType, callback CallbackFunction, args ...interface{}) {
232 if _, exists := p.Callbacks[callbackType]; !exists {
233 p.Callbacks[callbackType] = make(map[string]CallbackTuple)
234 }
235 funcName := runtime.FuncForPC(reflect.ValueOf(callback).Pointer()).Name()
236 log.Debugf("value of function: %s", funcName)
237 funcHash := fmt.Sprintf("%x", md5.Sum([]byte(funcName)))[:12]
238
239 p.Callbacks[callbackType][funcHash] = CallbackTuple{callback, args}
240}
241
242func (p *Proxy) UnregisterCallback(callbackType CallbackType, callback CallbackFunction, args ...interface{}) {
243 if _, exists := p.Callbacks[callbackType]; !exists {
244 log.Errorf("no such callback type - %s", callbackType.String())
245 return
246 }
247 // TODO: Not sure if this is the best way to do it.
248 funcName := runtime.FuncForPC(reflect.ValueOf(callback).Pointer()).Name()
249 log.Debugf("value of function: %s", funcName)
250 funcHash := fmt.Sprintf("%x", md5.Sum([]byte(funcName)))[:12]
251 if _, exists := p.Callbacks[callbackType][funcHash]; !exists {
252 log.Errorf("function with hash value: '%s' not registered with callback type: '%s'", funcHash, callbackType)
253 return
254 }
255 delete(p.Callbacks[callbackType], funcHash)
256}
257
Stephane Barbarie126101e2018-10-11 16:18:48 -0400258func (p *Proxy) invoke(callback CallbackTuple, context ...interface{}) (result interface{}, err error) {
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400259 defer func() {
260 if r := recover(); r != nil {
261 errStr := fmt.Sprintf("callback error occurred: %+v", r)
262 err = errors.New(errStr)
263 log.Error(errStr)
264 }
265 }()
266
267 result = callback.Execute(context)
268
269 return result, err
270}
271
Stephane Barbarie126101e2018-10-11 16:18:48 -0400272func (p *Proxy) InvokeCallbacks(args ...interface{}) (result interface{}) {
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400273 callbackType := args[0].(CallbackType)
Stephane Barbarie126101e2018-10-11 16:18:48 -0400274 proceedOnError := args[1].(bool)
275 context := args[2:]
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400276
277 var err error
278
279 if _, exists := p.Callbacks[callbackType]; exists {
280 for _, callback := range p.Callbacks[callbackType] {
Stephane Barbarie126101e2018-10-11 16:18:48 -0400281 if result, err = p.invoke(callback, context); err != nil {
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400282 if !proceedOnError {
283 log.Info("An error occurred. Stopping callback invocation")
284 break
285 }
286 log.Info("An error occurred. Invoking next callback")
287 }
288 }
289 }
Stephane Barbariea188d942018-10-16 16:43:04 -0400290 return result
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400291}