Statistics
| Branch: | Tag: | Revision:

root / lib / uidpool.py @ 14850c5e

History | View | Annotate | Download (11 kB)

1 6d127406 Balazs Lecz
#
2 6d127406 Balazs Lecz
#
3 6d127406 Balazs Lecz
4 6d127406 Balazs Lecz
# Copyright (C) 2010 Google Inc.
5 6d127406 Balazs Lecz
#
6 6d127406 Balazs Lecz
# This program is free software; you can redistribute it and/or modify
7 6d127406 Balazs Lecz
# it under the terms of the GNU General Public License as published by
8 6d127406 Balazs Lecz
# the Free Software Foundation; either version 2 of the License, or
9 6d127406 Balazs Lecz
# (at your option) any later version.
10 6d127406 Balazs Lecz
#
11 6d127406 Balazs Lecz
# This program is distributed in the hope that it will be useful, but
12 6d127406 Balazs Lecz
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 6d127406 Balazs Lecz
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 6d127406 Balazs Lecz
# General Public License for more details.
15 6d127406 Balazs Lecz
#
16 6d127406 Balazs Lecz
# You should have received a copy of the GNU General Public License
17 6d127406 Balazs Lecz
# along with this program; if not, write to the Free Software
18 6d127406 Balazs Lecz
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 6d127406 Balazs Lecz
# 02110-1301, USA.
20 6d127406 Balazs Lecz
21 6d127406 Balazs Lecz
22 6d127406 Balazs Lecz
"""User-id pool related functions.
23 6d127406 Balazs Lecz

24 6d127406 Balazs Lecz
The user-id pool is cluster-wide configuration option.
25 6d127406 Balazs Lecz
It is stored as a list of user-id ranges.
26 6d127406 Balazs Lecz
This module contains functions used for manipulating the
27 6d127406 Balazs Lecz
user-id pool parameter and for requesting/returning user-ids
28 6d127406 Balazs Lecz
from the pool.
29 6d127406 Balazs Lecz

30 6d127406 Balazs Lecz
"""
31 6d127406 Balazs Lecz
32 649bcdd8 Balazs Lecz
import errno
33 649bcdd8 Balazs Lecz
import logging
34 649bcdd8 Balazs Lecz
import os
35 649bcdd8 Balazs Lecz
import random
36 649bcdd8 Balazs Lecz
37 6d127406 Balazs Lecz
from ganeti import errors
38 6d127406 Balazs Lecz
from ganeti import constants
39 852bbc95 Balazs Lecz
from ganeti import utils
40 6d127406 Balazs Lecz
41 6d127406 Balazs Lecz
42 6d127406 Balazs Lecz
def ParseUidPool(value, separator=None):
43 6d127406 Balazs Lecz
  """Parse a user-id pool definition.
44 6d127406 Balazs Lecz

45 6d127406 Balazs Lecz
  @param value: string representation of the user-id pool.
46 6d127406 Balazs Lecz
                The accepted input format is a list of integer ranges.
47 6d127406 Balazs Lecz
                The boundaries are inclusive.
48 6d127406 Balazs Lecz
                Example: '1000-5000,8000,9000-9010'.
49 6d127406 Balazs Lecz
  @param separator: the separator character between the uids/uid-ranges.
50 6d127406 Balazs Lecz
                    Defaults to a comma.
51 6d127406 Balazs Lecz
  @return: a list of integer pairs (lower, higher range boundaries)
52 6d127406 Balazs Lecz

53 6d127406 Balazs Lecz
  """
54 6d127406 Balazs Lecz
  if separator is None:
55 6d127406 Balazs Lecz
    separator = ","
56 6d127406 Balazs Lecz
57 6d127406 Balazs Lecz
  ranges = []
58 6d127406 Balazs Lecz
  for range_def in value.split(separator):
59 6d127406 Balazs Lecz
    if not range_def:
60 6d127406 Balazs Lecz
      # Skip empty strings
61 6d127406 Balazs Lecz
      continue
62 6d127406 Balazs Lecz
    boundaries = range_def.split("-")
63 6d127406 Balazs Lecz
    n_elements = len(boundaries)
64 6d127406 Balazs Lecz
    if n_elements > 2:
65 6d127406 Balazs Lecz
      raise errors.OpPrereqError(
66 6d127406 Balazs Lecz
          "Invalid user-id range definition. Only one hyphen allowed: %s"
67 6d127406 Balazs Lecz
          % boundaries)
68 6d127406 Balazs Lecz
    try:
69 6d127406 Balazs Lecz
      lower = int(boundaries[0])
70 6d127406 Balazs Lecz
    except (ValueError, TypeError), err:
71 6d127406 Balazs Lecz
      raise errors.OpPrereqError("Invalid user-id value for lower boundary of"
72 6d127406 Balazs Lecz
                                 " user-id range: %s"
73 6d127406 Balazs Lecz
                                 % str(err), errors.ECODE_INVAL)
74 6d127406 Balazs Lecz
    try:
75 6d127406 Balazs Lecz
      higher = int(boundaries[n_elements - 1])
76 6d127406 Balazs Lecz
    except (ValueError, TypeError), err:
77 6d127406 Balazs Lecz
      raise errors.OpPrereqError("Invalid user-id value for higher boundary of"
78 6d127406 Balazs Lecz
                                 " user-id range: %s"
79 6d127406 Balazs Lecz
                                 % str(err), errors.ECODE_INVAL)
80 6d127406 Balazs Lecz
81 6d127406 Balazs Lecz
    ranges.append((lower, higher))
82 6d127406 Balazs Lecz
83 6d127406 Balazs Lecz
  ranges.sort()
84 6d127406 Balazs Lecz
  return ranges
85 6d127406 Balazs Lecz
86 6d127406 Balazs Lecz
87 fdad8c4d Balazs Lecz
def AddToUidPool(uid_pool, add_uids):
88 fdad8c4d Balazs Lecz
  """Add a list of user-ids/user-id ranges to a user-id pool.
89 fdad8c4d Balazs Lecz

90 fdad8c4d Balazs Lecz
  @param uid_pool: a user-id pool (list of integer tuples)
91 fdad8c4d Balazs Lecz
  @param add_uids: user-id ranges to be added to the pool
92 fdad8c4d Balazs Lecz
                   (list of integer tuples)
93 fdad8c4d Balazs Lecz

94 fdad8c4d Balazs Lecz
  """
95 fdad8c4d Balazs Lecz
  for uid_range in add_uids:
96 fdad8c4d Balazs Lecz
    if uid_range not in uid_pool:
97 fdad8c4d Balazs Lecz
      uid_pool.append(uid_range)
98 fdad8c4d Balazs Lecz
  uid_pool.sort()
99 fdad8c4d Balazs Lecz
100 fdad8c4d Balazs Lecz
101 fdad8c4d Balazs Lecz
def RemoveFromUidPool(uid_pool, remove_uids):
102 fdad8c4d Balazs Lecz
  """Remove a list of user-ids/user-id ranges from a user-id pool.
103 fdad8c4d Balazs Lecz

104 fdad8c4d Balazs Lecz
  @param uid_pool: a user-id pool (list of integer tuples)
105 fdad8c4d Balazs Lecz
  @param remove_uids: user-id ranges to be removed from the pool
106 fdad8c4d Balazs Lecz
                      (list of integer tuples)
107 fdad8c4d Balazs Lecz

108 fdad8c4d Balazs Lecz
  """
109 fdad8c4d Balazs Lecz
  for uid_range in remove_uids:
110 fdad8c4d Balazs Lecz
    if uid_range not in uid_pool:
111 fdad8c4d Balazs Lecz
      raise errors.OpPrereqError(
112 fdad8c4d Balazs Lecz
          "User-id range to be removed is not found in the current"
113 fdad8c4d Balazs Lecz
          " user-id pool: %s" % uid_range, errors.ECODE_INVAL)
114 fdad8c4d Balazs Lecz
    uid_pool.remove(uid_range)
115 fdad8c4d Balazs Lecz
116 fdad8c4d Balazs Lecz
117 852bbc95 Balazs Lecz
def _FormatUidRange(lower, higher):
118 852bbc95 Balazs Lecz
  """Convert a user-id range definition into a string.
119 852bbc95 Balazs Lecz

120 852bbc95 Balazs Lecz
  """
121 852bbc95 Balazs Lecz
  if lower == higher:
122 852bbc95 Balazs Lecz
    return str(lower)
123 852bbc95 Balazs Lecz
  return "%s-%s" % (lower, higher)
124 852bbc95 Balazs Lecz
125 852bbc95 Balazs Lecz
126 0fbae49a Balazs Lecz
def FormatUidPool(uid_pool, separator=None):
127 852bbc95 Balazs Lecz
  """Convert the internal representation of the user-id pool into a string.
128 852bbc95 Balazs Lecz

129 852bbc95 Balazs Lecz
  The output format is also accepted by ParseUidPool()
130 852bbc95 Balazs Lecz

131 852bbc95 Balazs Lecz
  @param uid_pool: a list of integer pairs representing UID ranges
132 0fbae49a Balazs Lecz
  @param separator: the separator character between the uids/uid-ranges.
133 0fbae49a Balazs Lecz
                    Defaults to ", ".
134 852bbc95 Balazs Lecz
  @return: a string with the formatted results
135 852bbc95 Balazs Lecz

136 852bbc95 Balazs Lecz
  """
137 0fbae49a Balazs Lecz
  if separator is None:
138 0fbae49a Balazs Lecz
    separator = ", "
139 0fbae49a Balazs Lecz
  return separator.join([_FormatUidRange(lower, higher)
140 0fbae49a Balazs Lecz
                         for lower, higher in uid_pool])
141 852bbc95 Balazs Lecz
142 852bbc95 Balazs Lecz
143 6d127406 Balazs Lecz
def CheckUidPool(uid_pool):
144 6d127406 Balazs Lecz
  """Sanity check user-id pool range definition values.
145 6d127406 Balazs Lecz

146 6d127406 Balazs Lecz
  @param uid_pool: a list of integer pairs (lower, higher range boundaries)
147 6d127406 Balazs Lecz

148 6d127406 Balazs Lecz
  """
149 6d127406 Balazs Lecz
  for lower, higher in uid_pool:
150 6d127406 Balazs Lecz
    if lower > higher:
151 6d127406 Balazs Lecz
      raise errors.OpPrereqError(
152 6d127406 Balazs Lecz
          "Lower user-id range boundary value (%s)"
153 6d127406 Balazs Lecz
          " is larger than higher boundary value (%s)" %
154 6d127406 Balazs Lecz
          (lower, higher), errors.ECODE_INVAL)
155 6d127406 Balazs Lecz
    if lower < constants.UIDPOOL_UID_MIN:
156 6d127406 Balazs Lecz
      raise errors.OpPrereqError(
157 6d127406 Balazs Lecz
          "Lower user-id range boundary value (%s)"
158 6d127406 Balazs Lecz
          " is smaller than UIDPOOL_UID_MIN (%s)." %
159 6d127406 Balazs Lecz
          (lower, constants.UIDPOOL_UID_MIN),
160 6d127406 Balazs Lecz
          errors.ECODE_INVAL)
161 6d127406 Balazs Lecz
    if higher > constants.UIDPOOL_UID_MAX:
162 6d127406 Balazs Lecz
      raise errors.OpPrereqError(
163 6d127406 Balazs Lecz
          "Higher user-id boundary value (%s)"
164 6d127406 Balazs Lecz
          " is larger than UIDPOOL_UID_MAX (%s)." %
165 6d127406 Balazs Lecz
          (higher, constants.UIDPOOL_UID_MAX),
166 6d127406 Balazs Lecz
          errors.ECODE_INVAL)
167 6d127406 Balazs Lecz
168 6d127406 Balazs Lecz
169 6d127406 Balazs Lecz
def ExpandUidPool(uid_pool):
170 6d127406 Balazs Lecz
  """Expands a uid-pool definition to a list of uids.
171 6d127406 Balazs Lecz

172 6d127406 Balazs Lecz
  @param uid_pool: a list of integer pairs (lower, higher range boundaries)
173 6d127406 Balazs Lecz
  @return: a list of integers
174 6d127406 Balazs Lecz

175 6d127406 Balazs Lecz
  """
176 6d127406 Balazs Lecz
  uids = set()
177 6d127406 Balazs Lecz
  for lower, higher in uid_pool:
178 6d127406 Balazs Lecz
    uids.update(range(lower, higher + 1))
179 6d127406 Balazs Lecz
  return list(uids)
180 649bcdd8 Balazs Lecz
181 649bcdd8 Balazs Lecz
182 649bcdd8 Balazs Lecz
def _IsUidUsed(uid):
183 649bcdd8 Balazs Lecz
  """Check if there is any process in the system running with the given user-id
184 649bcdd8 Balazs Lecz

185 649bcdd8 Balazs Lecz
  """
186 649bcdd8 Balazs Lecz
  pgrep_command = [constants.PGREP, "-u", uid]
187 649bcdd8 Balazs Lecz
  result = utils.RunCmd(pgrep_command)
188 649bcdd8 Balazs Lecz
189 649bcdd8 Balazs Lecz
  if result.exit_code == 0:
190 649bcdd8 Balazs Lecz
    return True
191 649bcdd8 Balazs Lecz
  elif result.exit_code == 1:
192 649bcdd8 Balazs Lecz
    return False
193 649bcdd8 Balazs Lecz
  else:
194 649bcdd8 Balazs Lecz
    raise errors.CommandError("Running pgrep failed. exit code: %s"
195 649bcdd8 Balazs Lecz
                              % result.exit_code)
196 649bcdd8 Balazs Lecz
197 649bcdd8 Balazs Lecz
198 649bcdd8 Balazs Lecz
class LockedUid(object):
199 649bcdd8 Balazs Lecz
  """Class representing a locked user-id in the uid-pool.
200 649bcdd8 Balazs Lecz

201 649bcdd8 Balazs Lecz
  This binds together a userid and a lock.
202 649bcdd8 Balazs Lecz

203 649bcdd8 Balazs Lecz
  """
204 649bcdd8 Balazs Lecz
  def __init__(self, uid, lock):
205 649bcdd8 Balazs Lecz
    """Constructor
206 649bcdd8 Balazs Lecz

207 649bcdd8 Balazs Lecz
    @param uid: a user-id
208 649bcdd8 Balazs Lecz
    @param lock: a utils.FileLock object
209 649bcdd8 Balazs Lecz

210 649bcdd8 Balazs Lecz
    """
211 649bcdd8 Balazs Lecz
    self._uid = uid
212 649bcdd8 Balazs Lecz
    self._lock = lock
213 649bcdd8 Balazs Lecz
214 649bcdd8 Balazs Lecz
  def Unlock(self):
215 649bcdd8 Balazs Lecz
    # Release the exclusive lock and close the filedescriptor
216 649bcdd8 Balazs Lecz
    self._lock.Close()
217 649bcdd8 Balazs Lecz
218 14850c5e Guido Trotter
  def GetUid(self):
219 14850c5e Guido Trotter
    return self._uid
220 14850c5e Guido Trotter
221 649bcdd8 Balazs Lecz
  def __str__(self):
222 649bcdd8 Balazs Lecz
    return "%s" % self._uid
223 649bcdd8 Balazs Lecz
224 649bcdd8 Balazs Lecz
225 649bcdd8 Balazs Lecz
def RequestUnusedUid(all_uids):
226 649bcdd8 Balazs Lecz
  """Tries to find an unused uid from the uid-pool, locks it and returns it.
227 649bcdd8 Balazs Lecz

228 d3b790bb Balazs Lecz
  Usage pattern
229 d3b790bb Balazs Lecz
  =============
230 649bcdd8 Balazs Lecz

231 d3b790bb Balazs Lecz
  1. When starting a process::
232 649bcdd8 Balazs Lecz

233 649bcdd8 Balazs Lecz
      from ganeti import ssconf
234 649bcdd8 Balazs Lecz
      from ganeti import uidpool
235 649bcdd8 Balazs Lecz

236 649bcdd8 Balazs Lecz
      # Get list of all user-ids in the uid-pool from ssconf
237 649bcdd8 Balazs Lecz
      ss = ssconf.SimpleStore()
238 d3b790bb Balazs Lecz
      uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\\n")
239 649bcdd8 Balazs Lecz
      all_uids = set(uidpool.ExpandUidPool(uid_pool))
240 649bcdd8 Balazs Lecz

241 649bcdd8 Balazs Lecz
      uid = uidpool.RequestUnusedUid(all_uids)
242 649bcdd8 Balazs Lecz
      try:
243 649bcdd8 Balazs Lecz
        <start a process with the UID>
244 649bcdd8 Balazs Lecz
        # Once the process is started, we can release the file lock
245 649bcdd8 Balazs Lecz
        uid.Unlock()
246 649bcdd8 Balazs Lecz
      except ..., err:
247 649bcdd8 Balazs Lecz
        # Return the UID to the pool
248 649bcdd8 Balazs Lecz
        uidpool.ReleaseUid(uid)
249 649bcdd8 Balazs Lecz

250 d3b790bb Balazs Lecz
  2. Stopping a process::
251 649bcdd8 Balazs Lecz

252 649bcdd8 Balazs Lecz
      from ganeti import uidpool
253 649bcdd8 Balazs Lecz

254 649bcdd8 Balazs Lecz
      uid = <get the UID the process is running under>
255 649bcdd8 Balazs Lecz
      <stop the process>
256 649bcdd8 Balazs Lecz
      uidpool.ReleaseUid(uid)
257 649bcdd8 Balazs Lecz

258 649bcdd8 Balazs Lecz
  @param all_uids: a set containing all the user-ids in the user-id pool
259 649bcdd8 Balazs Lecz
  @return: a LockedUid object representing the unused uid. It's the caller's
260 649bcdd8 Balazs Lecz
           responsibility to unlock the uid once an instance is started with
261 649bcdd8 Balazs Lecz
           this uid.
262 649bcdd8 Balazs Lecz

263 649bcdd8 Balazs Lecz
  """
264 649bcdd8 Balazs Lecz
  # Create the lock dir if it's not yet present
265 649bcdd8 Balazs Lecz
  try:
266 649bcdd8 Balazs Lecz
    utils.EnsureDirs([(constants.UIDPOOL_LOCKDIR, 0755)])
267 649bcdd8 Balazs Lecz
  except errors.GenericError, err:
268 649bcdd8 Balazs Lecz
    raise errors.LockError("Failed to create user-id pool lock dir: %s" % err)
269 649bcdd8 Balazs Lecz
270 649bcdd8 Balazs Lecz
  # Get list of currently used uids from the filesystem
271 649bcdd8 Balazs Lecz
  try:
272 649bcdd8 Balazs Lecz
    taken_uids = set(os.listdir(constants.UIDPOOL_LOCKDIR))
273 649bcdd8 Balazs Lecz
    # Filter out spurious entries from the directory listing
274 649bcdd8 Balazs Lecz
    taken_uids = all_uids.intersection(taken_uids)
275 649bcdd8 Balazs Lecz
  except OSError, err:
276 649bcdd8 Balazs Lecz
    raise errors.LockError("Failed to get list of used user-ids: %s" % err)
277 649bcdd8 Balazs Lecz
278 649bcdd8 Balazs Lecz
  # Remove the list of used uids from the list of all uids
279 649bcdd8 Balazs Lecz
  unused_uids = list(all_uids - taken_uids)
280 649bcdd8 Balazs Lecz
  if not unused_uids:
281 649bcdd8 Balazs Lecz
    logging.info("All user-ids in the uid-pool are marked 'taken'")
282 649bcdd8 Balazs Lecz
283 649bcdd8 Balazs Lecz
  # Randomize the order of the unused user-id list
284 649bcdd8 Balazs Lecz
  random.shuffle(unused_uids)
285 649bcdd8 Balazs Lecz
286 649bcdd8 Balazs Lecz
  # Randomize the order of the unused user-id list
287 649bcdd8 Balazs Lecz
  taken_uids = list(taken_uids)
288 649bcdd8 Balazs Lecz
  random.shuffle(taken_uids)
289 649bcdd8 Balazs Lecz
290 649bcdd8 Balazs Lecz
  for uid in (unused_uids + taken_uids):
291 649bcdd8 Balazs Lecz
    try:
292 649bcdd8 Balazs Lecz
      # Create the lock file
293 649bcdd8 Balazs Lecz
      # Note: we don't care if it exists. Only the fact that we can
294 649bcdd8 Balazs Lecz
      # (or can't) lock it later is what matters.
295 649bcdd8 Balazs Lecz
      uid_path = utils.PathJoin(constants.UIDPOOL_LOCKDIR, str(uid))
296 649bcdd8 Balazs Lecz
      lock = utils.FileLock.Open(uid_path)
297 649bcdd8 Balazs Lecz
    except OSError, err:
298 649bcdd8 Balazs Lecz
      raise errors.LockError("Failed to create lockfile for user-id %s: %s"
299 649bcdd8 Balazs Lecz
                             % (uid, err))
300 649bcdd8 Balazs Lecz
    try:
301 649bcdd8 Balazs Lecz
      # Try acquiring an exclusive lock on the lock file
302 649bcdd8 Balazs Lecz
      lock.Exclusive()
303 649bcdd8 Balazs Lecz
      # Check if there is any process running with this user-id
304 649bcdd8 Balazs Lecz
      if _IsUidUsed(uid):
305 649bcdd8 Balazs Lecz
        logging.debug("There is already a process running under"
306 649bcdd8 Balazs Lecz
                      " user-id %s", uid)
307 649bcdd8 Balazs Lecz
        lock.Unlock()
308 649bcdd8 Balazs Lecz
        continue
309 649bcdd8 Balazs Lecz
      return LockedUid(uid, lock)
310 649bcdd8 Balazs Lecz
    except IOError, err:
311 649bcdd8 Balazs Lecz
      if err.errno == errno.EAGAIN:
312 649bcdd8 Balazs Lecz
        # The file is already locked, let's skip it and try another unused uid
313 649bcdd8 Balazs Lecz
        logging.debug("Lockfile for user-id is already locked %s: %s", uid, err)
314 649bcdd8 Balazs Lecz
        continue
315 649bcdd8 Balazs Lecz
    except errors.LockError, err:
316 649bcdd8 Balazs Lecz
      # There was an unexpected error while trying to lock the file
317 649bcdd8 Balazs Lecz
      logging.error("Failed to lock the lockfile for user-id %s: %s", uid, err)
318 649bcdd8 Balazs Lecz
      raise
319 649bcdd8 Balazs Lecz
320 649bcdd8 Balazs Lecz
  raise errors.LockError("Failed to find an unused user-id")
321 649bcdd8 Balazs Lecz
322 649bcdd8 Balazs Lecz
323 649bcdd8 Balazs Lecz
def ReleaseUid(uid):
324 649bcdd8 Balazs Lecz
  """This should be called when the given user-id is no longer in use.
325 649bcdd8 Balazs Lecz

326 649bcdd8 Balazs Lecz
  """
327 649bcdd8 Balazs Lecz
  # Make sure we release the exclusive lock, if there is any
328 649bcdd8 Balazs Lecz
  uid.Unlock()
329 649bcdd8 Balazs Lecz
  try:
330 649bcdd8 Balazs Lecz
    uid_path = utils.PathJoin(constants.UIDPOOL_LOCKDIR, str(uid))
331 649bcdd8 Balazs Lecz
    os.remove(uid_path)
332 649bcdd8 Balazs Lecz
  except OSError, err:
333 649bcdd8 Balazs Lecz
    raise errors.LockError("Failed to remove user-id lockfile"
334 649bcdd8 Balazs Lecz
                           " for user-id %s: %s" % (uid, err))
335 5833b7e6 Balazs Lecz
336 5833b7e6 Balazs Lecz
337 5833b7e6 Balazs Lecz
def ExecWithUnusedUid(fn, all_uids, *args, **kwargs):
338 5833b7e6 Balazs Lecz
  """Execute a callable and provide an unused user-id in its kwargs.
339 5833b7e6 Balazs Lecz

340 5833b7e6 Balazs Lecz
  This wrapper function provides a simple way to handle the requesting,
341 5833b7e6 Balazs Lecz
  unlocking and releasing a user-id.
342 5833b7e6 Balazs Lecz
  "fn" is called by passing a "uid" keyword argument that
343 5833b7e6 Balazs Lecz
  contains an unused user-id (as a string) selected from the set of user-ids
344 5833b7e6 Balazs Lecz
  passed in all_uids.
345 5833b7e6 Balazs Lecz
  If there is an error while executing "fn", the user-id is returned
346 5833b7e6 Balazs Lecz
  to the pool.
347 5833b7e6 Balazs Lecz

348 5833b7e6 Balazs Lecz
  @param fn: a callable
349 5833b7e6 Balazs Lecz
  @param all_uids: a set containing all user-ids in the user-id pool
350 5833b7e6 Balazs Lecz

351 5833b7e6 Balazs Lecz
  """
352 5833b7e6 Balazs Lecz
  uid = RequestUnusedUid(all_uids)
353 5833b7e6 Balazs Lecz
  kwargs["uid"] = str(uid)
354 5833b7e6 Balazs Lecz
  try:
355 5833b7e6 Balazs Lecz
    return_value = fn(*args, **kwargs)
356 5833b7e6 Balazs Lecz
  except:
357 5833b7e6 Balazs Lecz
    # The failure of "callabe" means that starting a process with the uid
358 5833b7e6 Balazs Lecz
    # failed, so let's put the uid back into the pool.
359 5833b7e6 Balazs Lecz
    ReleaseUid(uid)
360 5833b7e6 Balazs Lecz
    raise
361 5833b7e6 Balazs Lecz
  uid.Unlock()
362 5833b7e6 Balazs Lecz
  return return_value