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