1package ibclient 2 3import ( 4 "fmt" 5 "math/rand" 6 "time" 7 8 "github.com/sirupsen/logrus" 9) 10 11const ( 12 timeout int32 = 60 // in seconds 13 freeLockVal string = "Available" 14) 15 16type Lock interface { 17 Lock() error 18 UnLock(force bool) error 19} 20 21type NetworkViewLock struct { 22 Name string 23 ObjMgr *ObjectManager 24 LockEA string 25 LockTimeoutEA string 26} 27 28func (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 84func (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 150func (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 186func (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 err = l.ObjMgr.UpdateNetworkViewEA(nw.Ref, EA{l.LockEA: freeLockVal}, nil) 198 if err != nil { 199 return fmt.Errorf("Failed to Update Network view with Lock EA") 200 } 201 } 202 203 retryCount := 0 204 for { 205 // Get lock on the network view 206 lock := l.getLock() 207 if lock == true { 208 // Got the lock. 209 logrus.Debugf("Got the lock on Network View %s\n", l.Name) 210 return nil 211 } 212 213 // Lock is held by some other agent. Wait for some time and retry it again 214 if retryCount >= 10 { 215 return fmt.Errorf("Failed to get Lock on Network View %s", l.Name) 216 } 217 218 retryCount++ 219 logrus.Debugf("Lock on Network View %s not free. Retrying again %d out of 10.\n", l.Name, retryCount) 220 // sleep for random time (between 1 - 10 seconds) to reduce collisions 221 time.Sleep(time.Duration(rand.Intn(9)+1) * time.Second) 222 continue 223 } 224} 225 226func (l *NetworkViewLock) UnLock(force bool) error { 227 // To unlock set the Docker-Plugin-Lock EA of network view to Available and 228 // remove the Docker-Plugin-Lock-Time EA 229 req := l.createUnlockRequest(force) 230 res, err := l.ObjMgr.CreateMultiObject(req) 231 232 if err != nil { 233 msg := fmt.Sprintf("Failed to release lock from Network View %s: %s\n", l.Name, err) 234 logrus.Errorf(msg) 235 return fmt.Errorf(msg) 236 } 237 238 dockerID := res[0]["DOCKER-ID"] 239 if dockerID == freeLockVal { 240 logrus.Debugln("Removed the lock!") 241 return nil 242 } 243 244 msg := fmt.Sprintf("Failed to release lock from Network View %s\n", l.Name) 245 logrus.Errorf(msg) 246 return fmt.Errorf(msg) 247} 248