Statistics
| Branch: | Tag: | Revision:

root / lib / uidpool.py @ d3b790bb

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 649bcdd8 Balazs Lecz
  def __str__(self):
219 649bcdd8 Balazs Lecz
    return "%s" % self._uid
220 649bcdd8 Balazs Lecz
221 649bcdd8 Balazs Lecz
222 649bcdd8 Balazs Lecz
def RequestUnusedUid(all_uids):
223 649bcdd8 Balazs Lecz
  """Tries to find an unused uid from the uid-pool, locks it and returns it.
224 649bcdd8 Balazs Lecz

225 d3b790bb Balazs Lecz
  Usage pattern
226 d3b790bb Balazs Lecz
  =============
227 649bcdd8 Balazs Lecz

228 d3b790bb Balazs Lecz
  1. When starting a process::
229 649bcdd8 Balazs Lecz

230 649bcdd8 Balazs Lecz
      from ganeti import ssconf
231 649bcdd8 Balazs Lecz
      from ganeti import uidpool
232 649bcdd8 Balazs Lecz

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

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

247 d3b790bb Balazs Lecz
  2. Stopping a process::
248 649bcdd8 Balazs Lecz

249 649bcdd8 Balazs Lecz
      from ganeti import uidpool
250 649bcdd8 Balazs Lecz

251 649bcdd8 Balazs Lecz
      uid = <get the UID the process is running under>
252 649bcdd8 Balazs Lecz
      <stop the process>
253 649bcdd8 Balazs Lecz
      uidpool.ReleaseUid(uid)
254 649bcdd8 Balazs Lecz

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

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

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

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

345 5833b7e6 Balazs Lecz
  @param fn: a callable
346 5833b7e6 Balazs Lecz
  @param all_uids: a set containing all user-ids in the user-id pool
347 5833b7e6 Balazs Lecz

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