Exemple #1
0
    def _flock(self, flag, blocking, timeout, errmsg):
        """Wrapper for fcntl.flock.

    @type flag: int
    @param flag: operation flag
    @type blocking: bool
    @param blocking: whether the operation should be done in blocking mode.
    @type timeout: None or float
    @param timeout: for how long the operation should be retried (implies
                    non-blocking mode).
    @type errmsg: string
    @param errmsg: error message in case operation fails.

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

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

        if timeout is None:
            self._Lock(self.fd, flag, timeout)
        else:
            try:
                retry.Retry(self._Lock, (0.1, 1.2, 1.0),
                            timeout,
                            args=(self.fd, flag, timeout))
            except retry.RetryTimeout:
                raise errors.LockError(errmsg)
Exemple #2
0
def LockFile(fd):
    """Locks a file using POSIX locks.

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

  """
    try:
        fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
    except IOError as err:
        if err.errno == errno.EAGAIN:
            raise errors.LockError("File already locked")
        raise
Exemple #3
0
def RequestUnusedUid(all_uids):
    """Tries to find an unused uid from the uid-pool, locks it and returns it.

  Usage pattern
  =============

  1. When starting a process::

      from ganeti import ssconf
      from ganeti import uidpool

      # Get list of all user-ids in the uid-pool from ssconf
      ss = ssconf.SimpleStore()
      uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\\n")
      all_uids = set(uidpool.ExpandUidPool(uid_pool))

      uid = uidpool.RequestUnusedUid(all_uids)
      try:
        <start a process with the UID>
        # Once the process is started, we can release the file lock
        uid.Unlock()
      except ..., err:
        # Return the UID to the pool
        uidpool.ReleaseUid(uid)

  2. Stopping a process::

      from ganeti import uidpool

      uid = <get the UID the process is running under>
      <stop the process>
      uidpool.ReleaseUid(uid)

  @type all_uids: set of integers
  @param all_uids: a set containing all the user-ids in the user-id pool
  @return: a LockedUid object representing the unused uid. It's the caller's
           responsibility to unlock the uid once an instance is started with
           this uid.

  """
    # Create the lock dir if it's not yet present
    try:
        utils.EnsureDirs([(pathutils.UIDPOOL_LOCKDIR, 0755)])
    except errors.GenericError, err:
        raise errors.LockError("Failed to create user-id pool lock dir: %s" %
                               err)
Exemple #4
0
def ReleaseUid(uid):
    """This should be called when the given user-id is no longer in use.

  @type uid: LockedUid or integer
  @param uid: the uid to release back to the pool

  """
    if isinstance(uid, LockedUid):
        # Make sure we release the exclusive lock, if there is any
        uid.Unlock()
        uid_filename = uid.AsStr()
    else:
        uid_filename = str(uid)

    try:
        uid_path = utils.PathJoin(pathutils.UIDPOOL_LOCKDIR, uid_filename)
        os.remove(uid_path)
    except OSError, err:
        raise errors.LockError("Failed to remove user-id lockfile"
                               " for user-id %s: %s" % (uid_filename, err))
Exemple #5
0
def SafeWriteFile(file_name, file_id, **kwargs):
    """Wraper over L{WriteFile} that locks the target file.

  By keeping the target file locked during WriteFile, we ensure that
  cooperating writers will safely serialise access to the file.

  @type file_name: str
  @param file_name: the target filename
  @type file_id: tuple
  @param file_id: a result from L{GetFileID}

  """
    fd = os.open(file_name, os.O_RDONLY | os.O_CREAT)
    try:
        filelock.LockFile(fd)
        if file_id is not None:
            disk_id = GetFileID(fd=fd)
            if not VerifyFileID(disk_id, file_id):
                raise errors.LockError(
                    "Cannot overwrite file %s, it has been modified"
                    " since last written" % file_name)
        return WriteFile(file_name, **kwargs)
    finally:
        os.close(fd)
Exemple #6
0
    def __check_deleted(self):
        """Raises an exception if the lock has been deleted.

    """
        if self.__deleted:
            raise errors.LockError("Deleted lock %s" % self.name)
Exemple #7
0
    except errors.GenericError, err:
        raise errors.LockError("Failed to create user-id pool lock dir: %s" %
                               err)

    # Get list of currently used uids from the filesystem
    try:
        taken_uids = set()
        for taken_uid in os.listdir(pathutils.UIDPOOL_LOCKDIR):
            try:
                taken_uid = int(taken_uid)
            except ValueError, err:
                # Skip directory entries that can't be converted into an integer
                continue
            taken_uids.add(taken_uid)
    except OSError, err:
        raise errors.LockError("Failed to get list of used user-ids: %s" % err)

    # Filter out spurious entries from the directory listing
    taken_uids = all_uids.intersection(taken_uids)

    # Remove the list of used uids from the list of all uids
    unused_uids = list(all_uids - taken_uids)
    if not unused_uids:
        logging.info("All user-ids in the uid-pool are marked 'taken'")

    # Randomize the order of the unused user-id list
    random.shuffle(unused_uids)

    # Randomize the order of the unused user-id list
    taken_uids = list(taken_uids)
    random.shuffle(taken_uids)
Exemple #8
0
def RequestUnusedUid(all_uids):
    """Tries to find an unused uid from the uid-pool, locks it and returns it.

  Usage pattern
  =============

  1. When starting a process::

      from ganeti import ssconf
      from ganeti import uidpool

      # Get list of all user-ids in the uid-pool from ssconf
      ss = ssconf.SimpleStore()
      uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\\n")
      all_uids = set(uidpool.ExpandUidPool(uid_pool))

      uid = uidpool.RequestUnusedUid(all_uids)
      try:
        <start a process with the UID>
        # Once the process is started, we can release the file lock
        uid.Unlock()
      except ... as err:
        # Return the UID to the pool
        uidpool.ReleaseUid(uid)

  2. Stopping a process::

      from ganeti import uidpool

      uid = <get the UID the process is running under>
      <stop the process>
      uidpool.ReleaseUid(uid)

  @type all_uids: set of integers
  @param all_uids: a set containing all the user-ids in the user-id pool
  @return: a LockedUid object representing the unused uid. It's the caller's
           responsibility to unlock the uid once an instance is started with
           this uid.

  """
    # Create the lock dir if it's not yet present
    try:
        utils.EnsureDirs([(pathutils.UIDPOOL_LOCKDIR, 0o755)])
    except errors.GenericError as err:
        raise errors.LockError("Failed to create user-id pool lock dir: %s" %
                               err)

    # Get list of currently used uids from the filesystem
    try:
        taken_uids = set()
        for taken_uid in os.listdir(pathutils.UIDPOOL_LOCKDIR):
            try:
                taken_uid = int(taken_uid)
            except ValueError as err:
                # Skip directory entries that can't be converted into an integer
                continue
            taken_uids.add(taken_uid)
    except OSError as err:
        raise errors.LockError("Failed to get list of used user-ids: %s" % err)

    # Filter out spurious entries from the directory listing
    taken_uids = all_uids.intersection(taken_uids)

    # Remove the list of used uids from the list of all uids
    unused_uids = list(all_uids - taken_uids)
    if not unused_uids:
        logging.info("All user-ids in the uid-pool are marked 'taken'")

    # Randomize the order of the unused user-id list
    random.shuffle(unused_uids)

    # Randomize the order of the unused user-id list
    taken_uids = list(taken_uids)
    random.shuffle(taken_uids)

    for uid in unused_uids + taken_uids:
        try:
            # Create the lock file
            # Note: we don't care if it exists. Only the fact that we can
            # (or can't) lock it later is what matters.
            uid_path = utils.PathJoin(pathutils.UIDPOOL_LOCKDIR, str(uid))
            lock = utils.FileLock.Open(uid_path)
        except OSError as err:
            raise errors.LockError(
                "Failed to create lockfile for user-id %s: %s" % (uid, err))
        try:
            # Try acquiring an exclusive lock on the lock file
            lock.Exclusive()
            # Check if there is any process running with this user-id
            if _IsUidUsed(uid):
                logging.debug(
                    "There is already a process running under"
                    " user-id %s", uid)
                lock.Unlock()
                continue
            return LockedUid(uid, lock)
        except IOError as err:
            if err.errno == errno.EAGAIN:
                # The file is already locked, let's skip it and try another unused uid
                logging.debug("Lockfile for user-id is already locked %s: %s",
                              uid, err)
                continue
        except errors.LockError as err:
            # There was an unexpected error while trying to lock the file
            logging.error("Failed to lock the lockfile for user-id %s: %s",
                          uid, err)
            raise

    raise errors.LockError("Failed to find an unused user-id")