blob: 2a5d034a9306e2f857084e50f7511011921b6c38 [file] [log] [blame]
Stephane Barbariea188d942018-10-16 16:43:04 -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 Barbariea188d942018-10-16 16:43:04 -040017package model
18
19import (
20 "github.com/opencord/voltha-go/common/log"
Stephane Barbariea188d942018-10-16 16:43:04 -040021 "sync"
22 "time"
23)
24
Stephane Barbariedc5022d2018-11-19 15:21:44 -050025type singletonProxyAccessControl struct {
26 sync.RWMutex
Stephane Barbariec53a2752019-03-08 17:50:10 -050027 cache sync.Map
Stephane Barbarie933b09b2019-01-09 11:12:09 -050028 reservedCount int
Stephane Barbariea188d942018-10-16 16:43:04 -040029}
30
Stephane Barbariedc5022d2018-11-19 15:21:44 -050031var instanceProxyAccessControl *singletonProxyAccessControl
32var onceProxyAccessControl sync.Once
Stephane Barbariea188d942018-10-16 16:43:04 -040033
Stephane Barbariedc5022d2018-11-19 15:21:44 -050034// PAC provides access to the proxy access control singleton instance
35func PAC() *singletonProxyAccessControl {
36 onceProxyAccessControl.Do(func() {
Stephane Barbarie933b09b2019-01-09 11:12:09 -050037 instanceProxyAccessControl = &singletonProxyAccessControl{}
Stephane Barbariea188d942018-10-16 16:43:04 -040038 })
Stephane Barbariedc5022d2018-11-19 15:21:44 -050039 return instanceProxyAccessControl
Stephane Barbariea188d942018-10-16 16:43:04 -040040}
41
Stephane Barbariec53a2752019-03-08 17:50:10 -050042// IsReserved will verify if access control is active for a specific path within the model
43func (singleton *singletonProxyAccessControl) IsReserved(pathLock string) bool {
44 singleton.Lock()
45 defer singleton.Unlock()
46
47 _, exists := singleton.cache.Load(pathLock)
48 log.Debugw("is-reserved", log.Fields{"pathLock": pathLock, "exists": exists})
49
50 return exists
51}
52
Stephane Barbariedc5022d2018-11-19 15:21:44 -050053// ReservePath will apply access control for a specific path within the model
Stephane Barbarie933b09b2019-01-09 11:12:09 -050054func (singleton *singletonProxyAccessControl) ReservePath(path string, proxy *Proxy, pathLock string) *proxyAccessControl {
Stephane Barbariedc5022d2018-11-19 15:21:44 -050055 singleton.Lock()
56 defer singleton.Unlock()
Stephane Barbarie933b09b2019-01-09 11:12:09 -050057 singleton.reservedCount++
58 if pac, exists := singleton.cache.Load(pathLock); !exists {
59 log.Debugf("Creating new PAC entry for path:%s pathLock:%s", path, pathLock)
60 newPac := NewProxyAccessControl(proxy, pathLock)
Stephane Barbariec53a2752019-03-08 17:50:10 -050061 singleton.cache.Store(pathLock, newPac)
Stephane Barbarie933b09b2019-01-09 11:12:09 -050062 return newPac
Stephane Barbariedc5022d2018-11-19 15:21:44 -050063 } else {
Stephane Barbarie933b09b2019-01-09 11:12:09 -050064 log.Debugf("Re-using existing PAC entry for path:%s pathLock:%s", path, pathLock)
65 return pac.(*proxyAccessControl)
Stephane Barbariedc5022d2018-11-19 15:21:44 -050066 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -050067}
68
69// ReleasePath will remove access control for a specific path within the model
70func (singleton *singletonProxyAccessControl) ReleasePath(pathLock string) {
71 singleton.Lock()
72 defer singleton.Unlock()
Stephane Barbarie933b09b2019-01-09 11:12:09 -050073
74 singleton.reservedCount--
75
76 if singleton.reservedCount == 0 {
77 singleton.cache.Delete(pathLock)
78 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -050079}
80
81// ProxyAccessControl is the abstraction interface to the base proxyAccessControl structure
Stephane Barbariea188d942018-10-16 16:43:04 -040082type ProxyAccessControl interface {
83 Get(path string, depth int, deep bool, txid string, control bool) interface{}
84 Update(path string, data interface{}, strict bool, txid string, control bool) interface{}
85 Add(path string, data interface{}, txid string, control bool) interface{}
86 Remove(path string, txid string, control bool) interface{}
Stephane Barbariedc5022d2018-11-19 15:21:44 -050087 SetProxy(proxy *Proxy)
Stephane Barbariea188d942018-10-16 16:43:04 -040088}
89
Stephane Barbariedc5022d2018-11-19 15:21:44 -050090// proxyAccessControl holds details of the path and proxy that requires access control
Stephane Barbariea188d942018-10-16 16:43:04 -040091type proxyAccessControl struct {
Stephane Barbariedc5022d2018-11-19 15:21:44 -050092 sync.RWMutex
Stephane Barbariea188d942018-10-16 16:43:04 -040093 Proxy *Proxy
94 PathLock chan struct{}
95 Path string
96
97 start time.Time
98 stop time.Time
99}
100
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500101// NewProxyAccessControl creates a new instance of an access control structure
Stephane Barbarie933b09b2019-01-09 11:12:09 -0500102func NewProxyAccessControl(proxy *Proxy, path string) *proxyAccessControl {
Stephane Barbariea188d942018-10-16 16:43:04 -0400103 return &proxyAccessControl{
104 Proxy: proxy,
105 Path: path,
106 PathLock: make(chan struct{}, 1),
107 }
108}
109
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500110// lock will prevent access to a model path
Stephane Barbariea188d942018-10-16 16:43:04 -0400111func (pac *proxyAccessControl) lock() {
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400112 log.Debugw("locking", log.Fields{"path": pac.Path})
Stephane Barbariea188d942018-10-16 16:43:04 -0400113 pac.PathLock <- struct{}{}
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500114 pac.setStart(time.Now())
Stephane Barbariea188d942018-10-16 16:43:04 -0400115}
116
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500117// unlock will release control of a model path
118func (pac *proxyAccessControl) unlock() {
119 <-pac.PathLock
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400120 log.Debugw("unlocking", log.Fields{"path": pac.Path})
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500121 pac.setStop(time.Now())
122 GetProfiling().AddToInMemoryLockTime(pac.getStop().Sub(pac.getStart()).Seconds())
123}
124
125// getStart is used for profiling purposes and returns the time at which access control was applied
126func (pac *proxyAccessControl) getStart() time.Time {
127 pac.Lock()
128 defer pac.Unlock()
129 return pac.start
130}
131
132// getStart is used for profiling purposes and returns the time at which access control was removed
133func (pac *proxyAccessControl) getStop() time.Time {
134 pac.Lock()
135 defer pac.Unlock()
136 return pac.stop
137}
138
139// getPath returns the access controlled path
140func (pac *proxyAccessControl) getPath() string {
141 pac.Lock()
142 defer pac.Unlock()
143 return pac.Path
144}
145
146// getProxy returns the proxy used to reach a specific location in the data model
147func (pac *proxyAccessControl) getProxy() *Proxy {
148 pac.Lock()
149 defer pac.Unlock()
150 return pac.Proxy
151}
152
153// setStart is for profiling purposes and applies a start time value at which access control was started
154func (pac *proxyAccessControl) setStart(time time.Time) {
155 pac.Lock()
156 defer pac.Unlock()
157 pac.start = time
158}
159
160// setStop is for profiling purposes and applies a stop time value at which access control was stopped
161func (pac *proxyAccessControl) setStop(time time.Time) {
162 pac.Lock()
163 defer pac.Unlock()
164 pac.stop = time
165}
166
167// SetProxy is used to changed the proxy object of an access controlled path
168func (pac *proxyAccessControl) SetProxy(proxy *Proxy) {
169 pac.Lock()
170 defer pac.Unlock()
171 pac.Proxy = proxy
172}
173
Stephane Barbarieaa467942019-02-06 14:09:44 -0500174// List retrieves data linked to a data model path
175func (pac *proxyAccessControl) List(path string, depth int, deep bool, txid string, control bool) interface{} {
176 if control {
177 pac.lock()
Stephane Barbariec53a2752019-03-08 17:50:10 -0500178 log.Debugw("locked-access--list", log.Fields{"path": path, "fullPath": pac.Proxy.getFullPath()})
Stephane Barbarieaa467942019-02-06 14:09:44 -0500179 defer pac.unlock()
Stephane Barbariec53a2752019-03-08 17:50:10 -0500180 defer log.Debugw("unlocked-access--list", log.Fields{"path": path, "fullPath": pac.Proxy.getFullPath()})
Stephane Barbarieaa467942019-02-06 14:09:44 -0500181 }
182
183 // FIXME: Forcing depth to 0 for now due to problems deep copying the data structure
184 // The data traversal through reflection currently corrupts the content
185
186 return pac.getProxy().GetRoot().List(path, "", depth, deep, txid)
187}
188
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500189// Get retrieves data linked to a data model path
Stephane Barbariea188d942018-10-16 16:43:04 -0400190func (pac *proxyAccessControl) Get(path string, depth int, deep bool, txid string, control bool) interface{} {
191 if control {
192 pac.lock()
Stephane Barbariec53a2752019-03-08 17:50:10 -0500193 log.Debugw("locked-access--get", log.Fields{"path": path, "fullPath": pac.Proxy.getFullPath()})
Stephane Barbariea188d942018-10-16 16:43:04 -0400194 defer pac.unlock()
Stephane Barbariec53a2752019-03-08 17:50:10 -0500195 defer log.Debugw("unlocked-access--get", log.Fields{"path": path, "fullPath": pac.Proxy.getFullPath()})
Stephane Barbariea188d942018-10-16 16:43:04 -0400196 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500197
198 // FIXME: Forcing depth to 0 for now due to problems deep copying the data structure
199 // The data traversal through reflection currently corrupts the content
Stephane Barbarie11b88e72019-02-07 12:28:29 -0500200 return pac.getProxy().GetRoot().Get(path, "", 0, deep, txid)
Stephane Barbariea188d942018-10-16 16:43:04 -0400201}
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500202
203// Update changes the content of the data model at the specified location with the provided data
Stephane Barbariea188d942018-10-16 16:43:04 -0400204func (pac *proxyAccessControl) Update(path string, data interface{}, strict bool, txid string, control bool) interface{} {
205 if control {
206 pac.lock()
Stephane Barbariec53a2752019-03-08 17:50:10 -0500207 log.Debugw("locked-access--update", log.Fields{"path": path, "fullPath": pac.Proxy.getFullPath()})
Stephane Barbariea188d942018-10-16 16:43:04 -0400208 defer pac.unlock()
Stephane Barbariec53a2752019-03-08 17:50:10 -0500209 defer log.Debugw("unlocked-access--update", log.Fields{"path": path, "fullPath": pac.Proxy.getFullPath()})
Stephane Barbariea188d942018-10-16 16:43:04 -0400210 }
Stephane Barbariec53a2752019-03-08 17:50:10 -0500211
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500212 result := pac.getProxy().GetRoot().Update(path, data, strict, txid, nil)
213
214 if result != nil {
215 return result.GetData()
216 }
217 return nil
Stephane Barbariea188d942018-10-16 16:43:04 -0400218}
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500219
220// Add creates a new data model entry at the specified location with the provided data
Stephane Barbariea188d942018-10-16 16:43:04 -0400221func (pac *proxyAccessControl) Add(path string, data interface{}, txid string, control bool) interface{} {
222 if control {
223 pac.lock()
Stephane Barbariec53a2752019-03-08 17:50:10 -0500224 log.Debugw("locked-access--add", log.Fields{"path": path, "fullPath": pac.Path})
Stephane Barbariea188d942018-10-16 16:43:04 -0400225 defer pac.unlock()
Stephane Barbariec53a2752019-03-08 17:50:10 -0500226 defer log.Debugw("unlocked-access--add", log.Fields{"path": path, "fullPath": pac.Proxy.getFullPath()})
Stephane Barbariea188d942018-10-16 16:43:04 -0400227 }
Stephane Barbariec53a2752019-03-08 17:50:10 -0500228
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500229 result := pac.getProxy().GetRoot().Add(path, data, txid, nil)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500230
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500231 if result != nil {
232 return result.GetData()
233 }
234 return nil
Stephane Barbariea188d942018-10-16 16:43:04 -0400235}
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500236
237// Remove discards information linked to the data model path
Stephane Barbariea188d942018-10-16 16:43:04 -0400238func (pac *proxyAccessControl) Remove(path string, txid string, control bool) interface{} {
239 if control {
240 pac.lock()
Stephane Barbariec53a2752019-03-08 17:50:10 -0500241 log.Debugw("locked-access--remove", log.Fields{"path": path, "fullPath": pac.Proxy.getFullPath()})
Stephane Barbariea188d942018-10-16 16:43:04 -0400242 defer pac.unlock()
Stephane Barbariec53a2752019-03-08 17:50:10 -0500243 defer log.Debugw("unlocked-access--remove", log.Fields{"path": path, "fullPath": pac.Proxy.getFullPath()})
Stephane Barbariea188d942018-10-16 16:43:04 -0400244 }
Stephane Barbariec53a2752019-03-08 17:50:10 -0500245
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500246 return pac.getProxy().GetRoot().Remove(path, txid, nil)
Stephane Barbariea188d942018-10-16 16:43:04 -0400247}
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400248
249// CreateProxy allows interaction for a specific path
250func (pac *proxyAccessControl) CreateProxy(path string, exclusive bool, control bool) *Proxy {
251 if control {
252 pac.lock()
253 log.Debugw("locked-access--create-proxy", log.Fields{"path": path, "fullPath": pac.Path})
254 defer pac.unlock()
255 defer log.Debugw("unlocked-access--create-proxy", log.Fields{"path": path, "fullPath": pac.Proxy.getFullPath()})
256 }
257
258 result := pac.getProxy().GetRoot().CreateProxy(path, exclusive)
259
260 if result != nil {
261 return result
262 }
263 return nil
264}