Revision 9d1b963f lib/utils/__init__.py

b/lib/utils/__init__.py
60 60
from ganeti.utils.log import * # pylint: disable-msg=W0401
61 61
from ganeti.utils.hash import * # pylint: disable-msg=W0401
62 62
from ganeti.utils.wrapper import * # pylint: disable-msg=W0401
63
from ganeti.utils.filelock import * # pylint: disable-msg=W0401
63 64

  
64 65

  
65 66
#: when set to True, L{RunCmd} is disabled
......
2549 2550
  return bool(exitcode)
2550 2551

  
2551 2552

  
2552
def LockFile(fd):
2553
  """Locks a file using POSIX locks.
2554

  
2555
  @type fd: int
2556
  @param fd: the file descriptor we need to lock
2557

  
2558
  """
2559
  try:
2560
    fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
2561
  except IOError, err:
2562
    if err.errno == errno.EAGAIN:
2563
      raise errors.LockError("File already locked")
2564
    raise
2565

  
2566

  
2567 2553
def ReadWatcherPauseFile(filename, now=None, remove_after=3600):
2568 2554
  """Reads the watcher pause file.
2569 2555

  
......
2658 2644
  WriteFile(filename, mode=0400, data=key_pem + cert_pem)
2659 2645

  
2660 2646

  
2661
class FileLock(object):
2662
  """Utility class for file locks.
2663

  
2664
  """
2665
  def __init__(self, fd, filename):
2666
    """Constructor for FileLock.
2667

  
2668
    @type fd: file
2669
    @param fd: File object
2670
    @type filename: str
2671
    @param filename: Path of the file opened at I{fd}
2672

  
2673
    """
2674
    self.fd = fd
2675
    self.filename = filename
2676

  
2677
  @classmethod
2678
  def Open(cls, filename):
2679
    """Creates and opens a file to be used as a file-based lock.
2680

  
2681
    @type filename: string
2682
    @param filename: path to the file to be locked
2683

  
2684
    """
2685
    # Using "os.open" is necessary to allow both opening existing file
2686
    # read/write and creating if not existing. Vanilla "open" will truncate an
2687
    # existing file -or- allow creating if not existing.
2688
    return cls(os.fdopen(os.open(filename, os.O_RDWR | os.O_CREAT), "w+"),
2689
               filename)
2690

  
2691
  def __del__(self):
2692
    self.Close()
2693

  
2694
  def Close(self):
2695
    """Close the file and release the lock.
2696

  
2697
    """
2698
    if hasattr(self, "fd") and self.fd:
2699
      self.fd.close()
2700
      self.fd = None
2701

  
2702
  def _flock(self, flag, blocking, timeout, errmsg):
2703
    """Wrapper for fcntl.flock.
2704

  
2705
    @type flag: int
2706
    @param flag: operation flag
2707
    @type blocking: bool
2708
    @param blocking: whether the operation should be done in blocking mode.
2709
    @type timeout: None or float
2710
    @param timeout: for how long the operation should be retried (implies
2711
                    non-blocking mode).
2712
    @type errmsg: string
2713
    @param errmsg: error message in case operation fails.
2714

  
2715
    """
2716
    assert self.fd, "Lock was closed"
2717
    assert timeout is None or timeout >= 0, \
2718
      "If specified, timeout must be positive"
2719
    assert not (flag & fcntl.LOCK_NB), "LOCK_NB must not be set"
2720

  
2721
    # When a timeout is used, LOCK_NB must always be set
2722
    if not (timeout is None and blocking):
2723
      flag |= fcntl.LOCK_NB
2724

  
2725
    if timeout is None:
2726
      self._Lock(self.fd, flag, timeout)
2727
    else:
2728
      try:
2729
        Retry(self._Lock, (0.1, 1.2, 1.0), timeout,
2730
              args=(self.fd, flag, timeout))
2731
      except RetryTimeout:
2732
        raise errors.LockError(errmsg)
2733

  
2734
  @staticmethod
2735
  def _Lock(fd, flag, timeout):
2736
    try:
2737
      fcntl.flock(fd, flag)
2738
    except IOError, err:
2739
      if timeout is not None and err.errno == errno.EAGAIN:
2740
        raise RetryAgain()
2741

  
2742
      logging.exception("fcntl.flock failed")
2743
      raise
2744

  
2745
  def Exclusive(self, blocking=False, timeout=None):
2746
    """Locks the file in exclusive mode.
2747

  
2748
    @type blocking: boolean
2749
    @param blocking: whether to block and wait until we
2750
        can lock the file or return immediately
2751
    @type timeout: int or None
2752
    @param timeout: if not None, the duration to wait for the lock
2753
        (in blocking mode)
2754

  
2755
    """
2756
    self._flock(fcntl.LOCK_EX, blocking, timeout,
2757
                "Failed to lock %s in exclusive mode" % self.filename)
2758

  
2759
  def Shared(self, blocking=False, timeout=None):
2760
    """Locks the file in shared mode.
2761

  
2762
    @type blocking: boolean
2763
    @param blocking: whether to block and wait until we
2764
        can lock the file or return immediately
2765
    @type timeout: int or None
2766
    @param timeout: if not None, the duration to wait for the lock
2767
        (in blocking mode)
2768

  
2769
    """
2770
    self._flock(fcntl.LOCK_SH, blocking, timeout,
2771
                "Failed to lock %s in shared mode" % self.filename)
2772

  
2773
  def Unlock(self, blocking=True, timeout=None):
2774
    """Unlocks the file.
2775

  
2776
    According to C{flock(2)}, unlocking can also be a nonblocking
2777
    operation::
2778

  
2779
      To make a non-blocking request, include LOCK_NB with any of the above
2780
      operations.
2781

  
2782
    @type blocking: boolean
2783
    @param blocking: whether to block and wait until we
2784
        can lock the file or return immediately
2785
    @type timeout: int or None
2786
    @param timeout: if not None, the duration to wait for the lock
2787
        (in blocking mode)
2788

  
2789
    """
2790
    self._flock(fcntl.LOCK_UN, blocking, timeout,
2791
                "Failed to unlock %s" % self.filename)
2792

  
2793

  
2794 2647
def SignalHandled(signums):
2795 2648
  """Signal Handled decoration.
2796 2649

  

Also available in: Unified diff