blob: 66d3222e1e568eb6ee0e843524d5d6a19891c125 [file] [log] [blame]
Matt Jeanneretcab955f2019-04-10 15:45:57 -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 */
16
17package model
18
19import (
20 "github.com/opencord/voltha-go/common/log"
21 "sync"
22 "time"
23)
24
25type singletonProxyAccessControl struct {
26 sync.RWMutex
27 cache sync.Map
28 reservedCount int
29}
30
31var instanceProxyAccessControl *singletonProxyAccessControl
32var onceProxyAccessControl sync.Once
33
34// PAC provides access to the proxy access control singleton instance
35func PAC() *singletonProxyAccessControl {
36 onceProxyAccessControl.Do(func() {
37 instanceProxyAccessControl = &singletonProxyAccessControl{}
38 })
39 return instanceProxyAccessControl
40}
41
42// 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
53// ReservePath will apply access control for a specific path within the model
54func (singleton *singletonProxyAccessControl) ReservePath(path string, proxy *Proxy, pathLock string) *proxyAccessControl {
55 singleton.Lock()
56 defer singleton.Unlock()
57 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)
61 singleton.cache.Store(pathLock, newPac)
62 return newPac
63 } else {
64 log.Debugf("Re-using existing PAC entry for path:%s pathLock:%s", path, pathLock)
65 return pac.(*proxyAccessControl)
66 }
67}
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()
73
74 singleton.reservedCount--
75
76 if singleton.reservedCount == 0 {
77 singleton.cache.Delete(pathLock)
78 }
79}
80
81// ProxyAccessControl is the abstraction interface to the base proxyAccessControl structure
82type 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{}
87 SetProxy(proxy *Proxy)
88}
89
90// proxyAccessControl holds details of the path and proxy that requires access control
91type proxyAccessControl struct {
92 sync.RWMutex
93 Proxy *Proxy
94 PathLock chan struct{}
95 Path string
96
97 start time.Time
98 stop time.Time
99}
100
101// NewProxyAccessControl creates a new instance of an access control structure
102func NewProxyAccessControl(proxy *Proxy, path string) *proxyAccessControl {
103 return &proxyAccessControl{
104 Proxy: proxy,
105 Path: path,
106 PathLock: make(chan struct{}, 1),
107 }
108}
109
110// lock will prevent access to a model path
111func (pac *proxyAccessControl) lock() {
112 pac.PathLock <- struct{}{}
113 pac.setStart(time.Now())
114}
115
116// unlock will release control of a model path
117func (pac *proxyAccessControl) unlock() {
118 <-pac.PathLock
119 pac.setStop(time.Now())
120 GetProfiling().AddToInMemoryLockTime(pac.getStop().Sub(pac.getStart()).Seconds())
121}
122
123// getStart is used for profiling purposes and returns the time at which access control was applied
124func (pac *proxyAccessControl) getStart() time.Time {
125 pac.Lock()
126 defer pac.Unlock()
127 return pac.start
128}
129
130// getStart is used for profiling purposes and returns the time at which access control was removed
131func (pac *proxyAccessControl) getStop() time.Time {
132 pac.Lock()
133 defer pac.Unlock()
134 return pac.stop
135}
136
137// getPath returns the access controlled path
138func (pac *proxyAccessControl) getPath() string {
139 pac.Lock()
140 defer pac.Unlock()
141 return pac.Path
142}
143
144// getProxy returns the proxy used to reach a specific location in the data model
145func (pac *proxyAccessControl) getProxy() *Proxy {
146 pac.Lock()
147 defer pac.Unlock()
148 return pac.Proxy
149}
150
151// setStart is for profiling purposes and applies a start time value at which access control was started
152func (pac *proxyAccessControl) setStart(time time.Time) {
153 pac.Lock()
154 defer pac.Unlock()
155 pac.start = time
156}
157
158// setStop is for profiling purposes and applies a stop time value at which access control was stopped
159func (pac *proxyAccessControl) setStop(time time.Time) {
160 pac.Lock()
161 defer pac.Unlock()
162 pac.stop = time
163}
164
165// SetProxy is used to changed the proxy object of an access controlled path
166func (pac *proxyAccessControl) SetProxy(proxy *Proxy) {
167 pac.Lock()
168 defer pac.Unlock()
169 pac.Proxy = proxy
170}
171
172// List retrieves data linked to a data model path
173func (pac *proxyAccessControl) List(path string, depth int, deep bool, txid string, control bool) interface{} {
174 if control {
175 pac.lock()
176 log.Debugw("locked-access--list", log.Fields{"path": path, "fullPath": pac.Proxy.getFullPath()})
177 defer pac.unlock()
178 defer log.Debugw("unlocked-access--list", log.Fields{"path": path, "fullPath": pac.Proxy.getFullPath()})
179 }
180
181 // FIXME: Forcing depth to 0 for now due to problems deep copying the data structure
182 // The data traversal through reflection currently corrupts the content
183
184 return pac.getProxy().GetRoot().List(path, "", depth, deep, txid)
185}
186
187// Get retrieves data linked to a data model path
188func (pac *proxyAccessControl) Get(path string, depth int, deep bool, txid string, control bool) interface{} {
189 if control {
190 pac.lock()
191 log.Debugw("locked-access--get", log.Fields{"path": path, "fullPath": pac.Proxy.getFullPath()})
192 defer pac.unlock()
193 defer log.Debugw("unlocked-access--get", log.Fields{"path": path, "fullPath": pac.Proxy.getFullPath()})
194 }
195
196 // FIXME: Forcing depth to 0 for now due to problems deep copying the data structure
197 // The data traversal through reflection currently corrupts the content
198 return pac.getProxy().GetRoot().Get(path, "", 0, deep, txid)
199}
200
201// Update changes the content of the data model at the specified location with the provided data
202func (pac *proxyAccessControl) Update(path string, data interface{}, strict bool, txid string, control bool) interface{} {
203 if control {
204 pac.lock()
205 log.Debugw("locked-access--update", log.Fields{"path": path, "fullPath": pac.Proxy.getFullPath()})
206 defer pac.unlock()
207 defer log.Debugw("unlocked-access--update", log.Fields{"path": path, "fullPath": pac.Proxy.getFullPath()})
208 }
209
210 result := pac.getProxy().GetRoot().Update(path, data, strict, txid, nil)
211
212 if result != nil {
213 return result.GetData()
214 }
215 return nil
216}
217
218// Add creates a new data model entry at the specified location with the provided data
219func (pac *proxyAccessControl) Add(path string, data interface{}, txid string, control bool) interface{} {
220 if control {
221 pac.lock()
222 log.Debugw("locked-access--add", log.Fields{"path": path, "fullPath": pac.Path})
223 defer pac.unlock()
224 defer log.Debugw("unlocked-access--add", log.Fields{"path": path, "fullPath": pac.Proxy.getFullPath()})
225 }
226
227 result := pac.getProxy().GetRoot().Add(path, data, txid, nil)
228
229 if result != nil {
230 return result.GetData()
231 }
232 return nil
233}
234
235// Remove discards information linked to the data model path
236func (pac *proxyAccessControl) Remove(path string, txid string, control bool) interface{} {
237 if control {
238 pac.lock()
239 log.Debugw("locked-access--remove", log.Fields{"path": path, "fullPath": pac.Proxy.getFullPath()})
240 defer pac.unlock()
241 defer log.Debugw("unlocked-access--remove", log.Fields{"path": path, "fullPath": pac.Proxy.getFullPath()})
242 }
243
244 return pac.getProxy().GetRoot().Remove(path, txid, nil)
245}