Statistics
| Branch: | Tag: | Revision:

root / lib / uidpool.py @ c7e4b037

History | View | Annotate | Download (11.6 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 c7e4b037 Balazs Lecz
  @type uid: integer
186 c7e4b037 Balazs Lecz
  @param uid: the user-id to be checked.
187 c7e4b037 Balazs Lecz

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

204 649bcdd8 Balazs Lecz
  This binds together a userid and a lock.
205 649bcdd8 Balazs Lecz

206 649bcdd8 Balazs Lecz
  """
207 649bcdd8 Balazs Lecz
  def __init__(self, uid, lock):
208 649bcdd8 Balazs Lecz
    """Constructor
209 649bcdd8 Balazs Lecz

210 649bcdd8 Balazs Lecz
    @param uid: a user-id
211 649bcdd8 Balazs Lecz
    @param lock: a utils.FileLock object
212 649bcdd8 Balazs Lecz

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

231 d3b790bb Balazs Lecz
  Usage pattern
232 d3b790bb Balazs Lecz
  =============
233 649bcdd8 Balazs Lecz

234 d3b790bb Balazs Lecz
  1. When starting a process::
235 649bcdd8 Balazs Lecz

236 649bcdd8 Balazs Lecz
      from ganeti import ssconf
237 649bcdd8 Balazs Lecz
      from ganeti import uidpool
238 649bcdd8 Balazs Lecz

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

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

253 d3b790bb Balazs Lecz
  2. Stopping a process::
254 649bcdd8 Balazs Lecz

255 649bcdd8 Balazs Lecz
      from ganeti import uidpool
256 649bcdd8 Balazs Lecz

257 649bcdd8 Balazs Lecz
      uid = <get the UID the process is running under>
258 649bcdd8 Balazs Lecz
      <stop the process>
259 649bcdd8 Balazs Lecz
      uidpool.ReleaseUid(uid)
260 649bcdd8 Balazs Lecz

261 c7e4b037 Balazs Lecz
  @type all_uids: set of integers
262 649bcdd8 Balazs Lecz
  @param all_uids: a set containing all the user-ids in the user-id pool
263 649bcdd8 Balazs Lecz
  @return: a LockedUid object representing the unused uid. It's the caller's
264 649bcdd8 Balazs Lecz
           responsibility to unlock the uid once an instance is started with
265 649bcdd8 Balazs Lecz
           this uid.
266 649bcdd8 Balazs Lecz

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

338 ff20190d Guido Trotter
  @type uid: LockedUid or integer
339 ff20190d Guido Trotter
  @param uid: the uid to release back to the pool
340 ff20190d Guido Trotter

341 649bcdd8 Balazs Lecz
  """
342 ff20190d Guido Trotter
  if isinstance(uid, LockedUid):
343 ff20190d Guido Trotter
    # Make sure we release the exclusive lock, if there is any
344 ff20190d Guido Trotter
    uid.Unlock()
345 c7e4b037 Balazs Lecz
    uid_filename = uid.AsStr()
346 c7e4b037 Balazs Lecz
  else:
347 c7e4b037 Balazs Lecz
    uid_filename = str(uid)
348 c7e4b037 Balazs Lecz
349 649bcdd8 Balazs Lecz
  try:
350 c7e4b037 Balazs Lecz
    uid_path = utils.PathJoin(constants.UIDPOOL_LOCKDIR, uid_filename)
351 649bcdd8 Balazs Lecz
    os.remove(uid_path)
352 649bcdd8 Balazs Lecz
  except OSError, err:
353 649bcdd8 Balazs Lecz
    raise errors.LockError("Failed to remove user-id lockfile"
354 c7e4b037 Balazs Lecz
                           " for user-id %s: %s" % (uid_filename, err))
355 5833b7e6 Balazs Lecz
356 5833b7e6 Balazs Lecz
357 5833b7e6 Balazs Lecz
def ExecWithUnusedUid(fn, all_uids, *args, **kwargs):
358 5833b7e6 Balazs Lecz
  """Execute a callable and provide an unused user-id in its kwargs.
359 5833b7e6 Balazs Lecz

360 5833b7e6 Balazs Lecz
  This wrapper function provides a simple way to handle the requesting,
361 5833b7e6 Balazs Lecz
  unlocking and releasing a user-id.
362 5833b7e6 Balazs Lecz
  "fn" is called by passing a "uid" keyword argument that
363 c7e4b037 Balazs Lecz
  contains an unused user-id (as an integer) selected from the set of user-ids
364 5833b7e6 Balazs Lecz
  passed in all_uids.
365 5833b7e6 Balazs Lecz
  If there is an error while executing "fn", the user-id is returned
366 5833b7e6 Balazs Lecz
  to the pool.
367 5833b7e6 Balazs Lecz

368 c7e4b037 Balazs Lecz
  @param fn: a callable that accepts a keyword argument called "uid"
369 c7e4b037 Balazs Lecz
  @type all_uids: a set of integers
370 5833b7e6 Balazs Lecz
  @param all_uids: a set containing all user-ids in the user-id pool
371 5833b7e6 Balazs Lecz

372 5833b7e6 Balazs Lecz
  """
373 5833b7e6 Balazs Lecz
  uid = RequestUnusedUid(all_uids)
374 c7e4b037 Balazs Lecz
  kwargs["uid"] = uid.GetUid()
375 5833b7e6 Balazs Lecz
  try:
376 5833b7e6 Balazs Lecz
    return_value = fn(*args, **kwargs)
377 5833b7e6 Balazs Lecz
  except:
378 5833b7e6 Balazs Lecz
    # The failure of "callabe" means that starting a process with the uid
379 5833b7e6 Balazs Lecz
    # failed, so let's put the uid back into the pool.
380 5833b7e6 Balazs Lecz
    ReleaseUid(uid)
381 5833b7e6 Balazs Lecz
    raise
382 5833b7e6 Balazs Lecz
  uid.Unlock()
383 5833b7e6 Balazs Lecz
  return return_value