from ganeti import errors
from ganeti import constants
+from ganeti import compat
from ganeti import utils
uid_pool.remove(uid_range)
-def _FormatUidRange(lower, higher):
+def _FormatUidRange(lower, higher, roman=False):
"""Convert a user-id range definition into a string.
"""
if lower == higher:
- return str(lower)
- return "%s-%s" % (lower, higher)
+ return str(compat.TryToRoman(lower, convert=roman))
+ return "%s-%s" % (compat.TryToRoman(lower, convert=roman),
+ compat.TryToRoman(higher, convert=roman))
-def FormatUidPool(uid_pool, separator=None):
+def FormatUidPool(uid_pool, separator=None, roman=False):
"""Convert the internal representation of the user-id pool into a string.
The output format is also accepted by ParseUidPool()
"""
if separator is None:
separator = ", "
- return separator.join([_FormatUidRange(lower, higher)
+ return separator.join([_FormatUidRange(lower, higher, roman=roman)
for lower, higher in uid_pool])
def _IsUidUsed(uid):
"""Check if there is any process in the system running with the given user-id
+ @type uid: integer
+ @param uid: the user-id to be checked.
+
"""
pgrep_command = [constants.PGREP, "-u", uid]
result = utils.RunCmd(pgrep_command)
# Release the exclusive lock and close the filedescriptor
self._lock.Close()
- def __str__(self):
+ def GetUid(self):
+ return self._uid
+
+ def AsStr(self):
return "%s" % self._uid
def RequestUnusedUid(all_uids):
"""Tries to find an unused uid from the uid-pool, locks it and returns it.
- Usage pattern:
+ Usage pattern
+ =============
- 1) When starting a process
+ 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")
+ uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\\n")
all_uids = set(uidpool.ExpandUidPool(uid_pool))
uid = uidpool.RequestUnusedUid(all_uids)
# Return the UID to the pool
uidpool.ReleaseUid(uid)
- 2) Stopping a process
+ 2. Stopping a process::
from ganeti import uidpool
<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
# Get list of currently used uids from the filesystem
try:
- taken_uids = set(os.listdir(constants.UIDPOOL_LOCKDIR))
- # Filter out spurious entries from the directory listing
- taken_uids = all_uids.intersection(taken_uids)
+ taken_uids = set()
+ for taken_uid in os.listdir(constants.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:
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
+
"""
- # Make sure we release the exclusive lock, if there is any
- uid.Unlock()
+ 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(constants.UIDPOOL_LOCKDIR, str(uid))
+ uid_path = utils.PathJoin(constants.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, err))
+ " for user-id %s: %s" % (uid_filename, err))
+
+
+def ExecWithUnusedUid(fn, all_uids, *args, **kwargs):
+ """Execute a callable and provide an unused user-id in its kwargs.
+
+ This wrapper function provides a simple way to handle the requesting,
+ unlocking and releasing a user-id.
+ "fn" is called by passing a "uid" keyword argument that
+ contains an unused user-id (as an integer) selected from the set of user-ids
+ passed in all_uids.
+ If there is an error while executing "fn", the user-id is returned
+ to the pool.
+
+ @param fn: a callable that accepts a keyword argument called "uid"
+ @type all_uids: a set of integers
+ @param all_uids: a set containing all user-ids in the user-id pool
+
+ """
+ uid = RequestUnusedUid(all_uids)
+ kwargs["uid"] = uid.GetUid()
+ try:
+ return_value = fn(*args, **kwargs)
+ except:
+ # The failure of "callabe" means that starting a process with the uid
+ # failed, so let's put the uid back into the pool.
+ ReleaseUid(uid)
+ raise
+ uid.Unlock()
+ return return_value