lock.go raw
1 package ibclient
2
3 import (
4 "fmt"
5 "math/rand"
6 "time"
7
8 "github.com/sirupsen/logrus"
9 )
10
11 const (
12 timeout int32 = 60 // in seconds
13 freeLockVal string = "Available"
14 )
15
16 type Lock interface {
17 Lock() error
18 UnLock(force bool) error
19 }
20
21 type NetworkViewLock struct {
22 Name string
23 ObjMgr *ObjectManager
24 LockEA string
25 LockTimeoutEA string
26 }
27
28 func (l *NetworkViewLock) createLockRequest() *MultiRequest {
29
30 req := NewMultiRequest(
31 []*RequestBody{
32 &RequestBody{
33 Method: "GET",
34 Object: "networkview",
35 Data: map[string]interface{}{
36 "name": l.Name,
37 "*" + l.LockEA: freeLockVal,
38 },
39 Args: map[string]string{
40 "_return_fields": "extattrs",
41 },
42 AssignState: map[string]string{
43 "NET_VIEW_REF": "_ref",
44 },
45 Discard: true,
46 },
47 &RequestBody{
48 Method: "PUT",
49 Object: "##STATE:NET_VIEW_REF:##",
50 Data: map[string]interface{}{
51 "extattrs+": map[string]interface{}{
52 l.LockEA: map[string]string{
53 "value": l.ObjMgr.tenantID,
54 },
55 l.LockTimeoutEA: map[string]int32{
56 "value": int32(time.Now().Unix()),
57 },
58 },
59 },
60 EnableSubstitution: true,
61 Discard: true,
62 },
63 &RequestBody{
64 Method: "GET",
65 Object: "##STATE:NET_VIEW_REF:##",
66 Args: map[string]string{
67 "_return_fields": "extattrs",
68 },
69 AssignState: map[string]string{
70 "DOCKER-ID": "*" + l.LockEA,
71 },
72 EnableSubstitution: true,
73 Discard: true,
74 },
75 &RequestBody{
76 Method: "STATE:DISPLAY",
77 },
78 },
79 )
80
81 return req
82 }
83
84 func (l *NetworkViewLock) createUnlockRequest(force bool) *MultiRequest {
85
86 getData := map[string]interface{}{"name": l.Name}
87 if !force {
88 getData["*"+l.LockEA] = l.ObjMgr.tenantID
89 }
90
91 req := NewMultiRequest(
92 []*RequestBody{
93 &RequestBody{
94 Method: "GET",
95 Object: "networkview",
96 Data: getData,
97 Args: map[string]string{
98 "_return_fields": "extattrs",
99 },
100 AssignState: map[string]string{
101 "NET_VIEW_REF": "_ref",
102 },
103 Discard: true,
104 },
105 &RequestBody{
106 Method: "PUT",
107 Object: "##STATE:NET_VIEW_REF:##",
108 Data: map[string]interface{}{
109 "extattrs+": map[string]interface{}{
110 l.LockEA: map[string]string{
111 "value": freeLockVal,
112 },
113 },
114 },
115 EnableSubstitution: true,
116 Discard: true,
117 },
118 &RequestBody{
119 Method: "PUT",
120 Object: "##STATE:NET_VIEW_REF:##",
121 Data: map[string]interface{}{
122 "extattrs-": map[string]interface{}{
123 l.LockTimeoutEA: map[string]interface{}{},
124 },
125 },
126 EnableSubstitution: true,
127 Discard: true,
128 },
129 &RequestBody{
130 Method: "GET",
131 Object: "##STATE:NET_VIEW_REF:##",
132 Args: map[string]string{
133 "_return_fields": "extattrs",
134 },
135 AssignState: map[string]string{
136 "DOCKER-ID": "*" + l.LockEA,
137 },
138 EnableSubstitution: true,
139 Discard: true,
140 },
141 &RequestBody{
142 Method: "STATE:DISPLAY",
143 },
144 },
145 )
146
147 return req
148 }
149
150 func (l *NetworkViewLock) getLock() bool {
151 logrus.Debugf("Creating lock on network niew %s\n", l.Name)
152 req := l.createLockRequest()
153 res, err := l.ObjMgr.CreateMultiObject(req)
154
155 if err != nil {
156 logrus.Debugf("Failed to create lock on network view %s: %s\n", l.Name, err)
157
158 //Check for Lock Timeout
159 nw, err := l.ObjMgr.GetNetworkView(l.Name)
160 if err != nil {
161 logrus.Debugf("Failed to get the network view object for %s : %s\n", l.Name, err)
162 return false
163 }
164
165 if t, ok := nw.Ea[l.LockTimeoutEA]; ok {
166 if int32(time.Now().Unix())-int32(t.(int)) > timeout {
167 logrus.Debugln("Lock is timed out. Forcefully acquiring it.")
168 //remove the lock forcefully and acquire it
169 l.UnLock(true)
170 // try to get lock again
171 return l.getLock()
172 }
173 }
174 return false
175 }
176
177 dockerID := res[0]["DOCKER-ID"]
178 if dockerID == l.ObjMgr.tenantID {
179 logrus.Debugln("Got the lock !!!")
180 return true
181 }
182
183 return false
184 }
185
186 func (l *NetworkViewLock) Lock() error {
187
188 // verify if network view exists and has EA for the lock
189 nw, err := l.ObjMgr.GetNetworkView(l.Name)
190 if err != nil {
191 msg := fmt.Sprintf("Failed to get the network view object for %s : %s\n", l.Name, err)
192 logrus.Debugf(msg)
193 return fmt.Errorf(msg)
194 }
195
196 if _, ok := nw.Ea[l.LockEA]; !ok {
197 nw.Ea[l.LockEA] = freeLockVal
198 _, err = l.ObjMgr.UpdateNetworkView(nw.Ref, "", "", nw.Ea)
199 if err != nil {
200 return fmt.Errorf("Failed to Update Network view with Lock EA")
201 }
202 }
203
204 retryCount := 0
205 for {
206 // Get lock on the network view
207 lock := l.getLock()
208 if lock == true {
209 // Got the lock.
210 logrus.Debugf("Got the lock on Network View %s\n", l.Name)
211 return nil
212 }
213
214 // Lock is held by some other agent. Wait for some time and retry it again
215 if retryCount >= 10 {
216 return fmt.Errorf("Failed to get Lock on Network View %s", l.Name)
217 }
218
219 retryCount++
220 logrus.Debugf("Lock on Network View %s not free. Retrying again %d out of 10.\n", l.Name, retryCount)
221 // sleep for random time (between 1 - 10 seconds) to reduce collisions
222 time.Sleep(time.Duration(rand.Intn(9)+1) * time.Second)
223 continue
224 }
225 }
226
227 func (l *NetworkViewLock) UnLock(force bool) error {
228 // To unlock set the Docker-Plugin-Lock EA of network view to Available and
229 // remove the Docker-Plugin-Lock-Time EA
230 req := l.createUnlockRequest(force)
231 res, err := l.ObjMgr.CreateMultiObject(req)
232
233 if err != nil {
234 msg := fmt.Sprintf("Failed to release lock from Network View %s: %s\n", l.Name, err)
235 logrus.Errorf(msg)
236 return fmt.Errorf(msg)
237 }
238
239 dockerID := res[0]["DOCKER-ID"]
240 if dockerID == freeLockVal {
241 logrus.Debugln("Removed the lock!")
242 return nil
243 }
244
245 msg := fmt.Sprintf("Failed to release lock from Network View %s\n", l.Name)
246 logrus.Errorf(msg)
247 return fmt.Errorf(msg)
248 }
249