github.com/infobloxopen/infoblox-go-client@v1.1.1/lock.go (about)

     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  		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  
   226  func (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  }