blob: decbf9b4e72dcb6295d676d43d7053abefd13bdb [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 {
Stephane Barbarie126101e2018-10-11 16:18:48 -040051 Root *root
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040052 Path string
53 Exclusive bool
Stephane Barbarie694e2b92018-09-07 12:17:36 -040054 Callbacks map[CallbackType]map[string]CallbackTuple
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040055}
56
Stephane Barbarie126101e2018-10-11 16:18:48 -040057func NewProxy(root *root, path string, exclusive bool) *Proxy {
Stephane Barbarie694e2b92018-09-07 12:17:36 -040058 callbacks := make(map[CallbackType]map[string]CallbackTuple)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040059 p := &Proxy{
60 Root: root,
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040061 Exclusive: exclusive,
62 Path: path,
Stephane Barbarie694e2b92018-09-07 12:17:36 -040063 Callbacks: callbacks,
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040064 }
65 return p
66}
67
68func (p *Proxy) Get(path string, depth int, deep bool, txid string) interface{} {
Stephane Barbarie126101e2018-10-11 16:18:48 -040069 return p.Root.Get(path, "", depth, deep, txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040070}
71
72func (p *Proxy) Update(path string, data interface{}, strict bool, txid string) interface{} {
73 if !strings.HasPrefix(path, "/") {
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -040074 log.Errorf("invalid path: %s", path)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040075 return nil
76 }
77 var fullPath string
78 if path == "/" {
79 fullPath = p.Path
80 } else {
81 fullPath = p.Path + path
82 }
Stephane Barbarie126101e2018-10-11 16:18:48 -040083
Stephane Barbariee16186c2018-09-11 10:46:34 -040084 return p.Root.Update(fullPath, data, strict, txid, nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040085}
86
87func (p *Proxy) Add(path string, data interface{}, txid string) interface{} {
88 if !strings.HasPrefix(path, "/") {
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -040089 log.Errorf("invalid path: %s", path)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040090 return nil
91 }
92 var fullPath string
93 if path == "/" {
94 fullPath = p.Path
95 } else {
96 fullPath = p.Path + path
97 }
Stephane Barbariee16186c2018-09-11 10:46:34 -040098 return p.Root.Add(fullPath, data, txid, nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040099}
100
101func (p *Proxy) Remove(path string, txid string) interface{} {
102 if !strings.HasPrefix(path, "/") {
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -0400103 log.Errorf("invalid path: %s", path)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400104 return nil
105 }
106 var fullPath string
107 if path == "/" {
108 fullPath = p.Path
109 } else {
110 fullPath = p.Path + path
111 }
Stephane Barbariee16186c2018-09-11 10:46:34 -0400112 return p.Root.Remove(fullPath, txid, nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400113}
114
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400115func (p *Proxy) OpenTransaction() *Transaction {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400116 txid := p.Root.MakeTxBranch()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400117 return NewTransaction(p, txid)
118}
119
120func (p *Proxy) commitTransaction(txid string) {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400121 p.Root.FoldTxBranch(txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400122}
123
124func (p *Proxy) cancelTransaction(txid string) {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400125 p.Root.DeleteTxBranch(txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400126}
127
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400128type CallbackFunction func(args ...interface{}) interface{}
129type CallbackTuple struct {
130 callback CallbackFunction
131 args []interface{}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400132}
133
Stephane Barbarie126101e2018-10-11 16:18:48 -0400134func (tuple *CallbackTuple) Execute(contextArgs interface{}) interface{} {
135 args := []interface{}{}
136 args = append(args, tuple.args...)
137 if contextArgs != nil {
138 args = append(args, contextArgs)
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400139 }
Stephane Barbarie126101e2018-10-11 16:18:48 -0400140 return tuple.callback(args...)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400141}
142
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400143func (p *Proxy) RegisterCallback(callbackType CallbackType, callback CallbackFunction, args ...interface{}) {
144 if _, exists := p.Callbacks[callbackType]; !exists {
145 p.Callbacks[callbackType] = make(map[string]CallbackTuple)
146 }
147 funcName := runtime.FuncForPC(reflect.ValueOf(callback).Pointer()).Name()
148 log.Debugf("value of function: %s", funcName)
149 funcHash := fmt.Sprintf("%x", md5.Sum([]byte(funcName)))[:12]
150
151 p.Callbacks[callbackType][funcHash] = CallbackTuple{callback, args}
152}
153
154func (p *Proxy) UnregisterCallback(callbackType CallbackType, callback CallbackFunction, args ...interface{}) {
155 if _, exists := p.Callbacks[callbackType]; !exists {
156 log.Errorf("no such callback type - %s", callbackType.String())
157 return
158 }
159 // TODO: Not sure if this is the best way to do it.
160 funcName := runtime.FuncForPC(reflect.ValueOf(callback).Pointer()).Name()
161 log.Debugf("value of function: %s", funcName)
162 funcHash := fmt.Sprintf("%x", md5.Sum([]byte(funcName)))[:12]
163 if _, exists := p.Callbacks[callbackType][funcHash]; !exists {
164 log.Errorf("function with hash value: '%s' not registered with callback type: '%s'", funcHash, callbackType)
165 return
166 }
167 delete(p.Callbacks[callbackType], funcHash)
168}
169
Stephane Barbarie126101e2018-10-11 16:18:48 -0400170func (p *Proxy) invoke(callback CallbackTuple, context ...interface{}) (result interface{}, err error) {
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400171 defer func() {
172 if r := recover(); r != nil {
173 errStr := fmt.Sprintf("callback error occurred: %+v", r)
174 err = errors.New(errStr)
175 log.Error(errStr)
176 }
177 }()
178
179 result = callback.Execute(context)
180
181 return result, err
182}
183
Stephane Barbarie126101e2018-10-11 16:18:48 -0400184func (p *Proxy) InvokeCallbacks(args ...interface{}) (result interface{}) {
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400185 callbackType := args[0].(CallbackType)
Stephane Barbarie126101e2018-10-11 16:18:48 -0400186 proceedOnError := args[1].(bool)
187 context := args[2:]
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400188
189 var err error
190
191 if _, exists := p.Callbacks[callbackType]; exists {
192 for _, callback := range p.Callbacks[callbackType] {
Stephane Barbarie126101e2018-10-11 16:18:48 -0400193 if result, err = p.invoke(callback, context); err != nil {
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400194 if !proceedOnError {
195 log.Info("An error occurred. Stopping callback invocation")
196 break
197 }
198 log.Info("An error occurred. Invoking next callback")
199 }
200 }
201 }
202 return context
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400203}