Statistics
| Branch: | Tag: | Revision:

root / lib / locking.py @ 17dfc522

History | View | Annotate | Download (29 kB)

1 162c1c1f Guido Trotter
#
2 162c1c1f Guido Trotter
#
3 162c1c1f Guido Trotter
4 162c1c1f Guido Trotter
# Copyright (C) 2006, 2007 Google Inc.
5 162c1c1f Guido Trotter
#
6 162c1c1f Guido Trotter
# This program is free software; you can redistribute it and/or modify
7 162c1c1f Guido Trotter
# it under the terms of the GNU General Public License as published by
8 162c1c1f Guido Trotter
# the Free Software Foundation; either version 2 of the License, or
9 162c1c1f Guido Trotter
# (at your option) any later version.
10 162c1c1f Guido Trotter
#
11 162c1c1f Guido Trotter
# This program is distributed in the hope that it will be useful, but
12 162c1c1f Guido Trotter
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 162c1c1f Guido Trotter
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 162c1c1f Guido Trotter
# General Public License for more details.
15 162c1c1f Guido Trotter
#
16 162c1c1f Guido Trotter
# You should have received a copy of the GNU General Public License
17 162c1c1f Guido Trotter
# along with this program; if not, write to the Free Software
18 162c1c1f Guido Trotter
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 162c1c1f Guido Trotter
# 02110-1301, USA.
20 162c1c1f Guido Trotter
21 162c1c1f Guido Trotter
"""Module implementing the Ganeti locking code."""
22 162c1c1f Guido Trotter
23 162c1c1f Guido Trotter
# pylint: disable-msg=W0613,W0201
24 162c1c1f Guido Trotter
25 162c1c1f Guido Trotter
import threading
26 a95fd5d7 Guido Trotter
# Wouldn't it be better to define LockingError in the locking module?
27 a95fd5d7 Guido Trotter
# Well, for now that's how the rest of the code does it...
28 a95fd5d7 Guido Trotter
from ganeti import errors
29 7ee7c0c7 Guido Trotter
from ganeti import utils
30 162c1c1f Guido Trotter
31 162c1c1f Guido Trotter
32 162c1c1f Guido Trotter
class SharedLock:
33 162c1c1f Guido Trotter
  """Implements a shared lock.
34 162c1c1f Guido Trotter

35 162c1c1f Guido Trotter
  Multiple threads can acquire the lock in a shared way, calling
36 162c1c1f Guido Trotter
  acquire_shared().  In order to acquire the lock in an exclusive way threads
37 162c1c1f Guido Trotter
  can call acquire_exclusive().
38 162c1c1f Guido Trotter

39 162c1c1f Guido Trotter
  The lock prevents starvation but does not guarantee that threads will acquire
40 162c1c1f Guido Trotter
  the shared lock in the order they queued for it, just that they will
41 162c1c1f Guido Trotter
  eventually do so.
42 162c1c1f Guido Trotter

43 162c1c1f Guido Trotter
  """
44 162c1c1f Guido Trotter
  def __init__(self):
45 d6646186 Guido Trotter
    """Construct a new SharedLock"""
46 162c1c1f Guido Trotter
    # we have two conditions, c_shr and c_exc, sharing the same lock.
47 162c1c1f Guido Trotter
    self.__lock = threading.Lock()
48 162c1c1f Guido Trotter
    self.__turn_shr = threading.Condition(self.__lock)
49 162c1c1f Guido Trotter
    self.__turn_exc = threading.Condition(self.__lock)
50 162c1c1f Guido Trotter
51 162c1c1f Guido Trotter
    # current lock holders
52 162c1c1f Guido Trotter
    self.__shr = set()
53 162c1c1f Guido Trotter
    self.__exc = None
54 162c1c1f Guido Trotter
55 162c1c1f Guido Trotter
    # lock waiters
56 162c1c1f Guido Trotter
    self.__nwait_exc = 0
57 162c1c1f Guido Trotter
    self.__nwait_shr = 0
58 4d686df8 Guido Trotter
    self.__npass_shr = 0
59 162c1c1f Guido Trotter
60 a95fd5d7 Guido Trotter
    # is this lock in the deleted state?
61 a95fd5d7 Guido Trotter
    self.__deleted = False
62 a95fd5d7 Guido Trotter
63 162c1c1f Guido Trotter
  def __is_sharer(self):
64 162c1c1f Guido Trotter
    """Is the current thread sharing the lock at this time?"""
65 162c1c1f Guido Trotter
    return threading.currentThread() in self.__shr
66 162c1c1f Guido Trotter
67 162c1c1f Guido Trotter
  def __is_exclusive(self):
68 162c1c1f Guido Trotter
    """Is the current thread holding the lock exclusively at this time?"""
69 162c1c1f Guido Trotter
    return threading.currentThread() == self.__exc
70 162c1c1f Guido Trotter
71 162c1c1f Guido Trotter
  def __is_owned(self, shared=-1):
72 162c1c1f Guido Trotter
    """Is the current thread somehow owning the lock at this time?
73 162c1c1f Guido Trotter

74 162c1c1f Guido Trotter
    This is a private version of the function, which presumes you're holding
75 162c1c1f Guido Trotter
    the internal lock.
76 162c1c1f Guido Trotter

77 162c1c1f Guido Trotter
    """
78 162c1c1f Guido Trotter
    if shared < 0:
79 162c1c1f Guido Trotter
      return self.__is_sharer() or self.__is_exclusive()
80 162c1c1f Guido Trotter
    elif shared:
81 162c1c1f Guido Trotter
      return self.__is_sharer()
82 162c1c1f Guido Trotter
    else:
83 162c1c1f Guido Trotter
      return self.__is_exclusive()
84 162c1c1f Guido Trotter
85 162c1c1f Guido Trotter
  def _is_owned(self, shared=-1):
86 162c1c1f Guido Trotter
    """Is the current thread somehow owning the lock at this time?
87 162c1c1f Guido Trotter

88 162c1c1f Guido Trotter
    Args:
89 162c1c1f Guido Trotter
      shared:
90 162c1c1f Guido Trotter
        < 0: check for any type of ownership (default)
91 162c1c1f Guido Trotter
        0: check for exclusive ownership
92 162c1c1f Guido Trotter
        > 0: check for shared ownership
93 162c1c1f Guido Trotter

94 162c1c1f Guido Trotter
    """
95 162c1c1f Guido Trotter
    self.__lock.acquire()
96 162c1c1f Guido Trotter
    try:
97 cdb08f44 Michael Hanselmann
      result = self.__is_owned(shared=shared)
98 162c1c1f Guido Trotter
    finally:
99 162c1c1f Guido Trotter
      self.__lock.release()
100 162c1c1f Guido Trotter
101 162c1c1f Guido Trotter
    return result
102 162c1c1f Guido Trotter
103 cdb08f44 Michael Hanselmann
  def __wait(self, c):
104 a95fd5d7 Guido Trotter
    """Wait on the given condition, and raise an exception if the current lock
105 a95fd5d7 Guido Trotter
    is declared deleted in the meantime.
106 a95fd5d7 Guido Trotter

107 a95fd5d7 Guido Trotter
    Args:
108 a95fd5d7 Guido Trotter
      c: condition to wait on
109 a95fd5d7 Guido Trotter

110 a95fd5d7 Guido Trotter
    """
111 a95fd5d7 Guido Trotter
    c.wait()
112 a95fd5d7 Guido Trotter
    if self.__deleted:
113 a95fd5d7 Guido Trotter
      raise errors.LockError('deleted lock')
114 a95fd5d7 Guido Trotter
115 a95fd5d7 Guido Trotter
  def __exclusive_acquire(self):
116 a95fd5d7 Guido Trotter
    """Acquire the lock exclusively.
117 a95fd5d7 Guido Trotter

118 a95fd5d7 Guido Trotter
    This is a private function that presumes you are already holding the
119 a95fd5d7 Guido Trotter
    internal lock. It's defined separately to avoid code duplication between
120 a95fd5d7 Guido Trotter
    acquire() and delete()
121 a95fd5d7 Guido Trotter

122 a95fd5d7 Guido Trotter
    """
123 a95fd5d7 Guido Trotter
    self.__nwait_exc += 1
124 a95fd5d7 Guido Trotter
    try:
125 a95fd5d7 Guido Trotter
      # This is to save ourselves from a nasty race condition that could
126 a95fd5d7 Guido Trotter
      # theoretically make the sharers starve.
127 a95fd5d7 Guido Trotter
      if self.__nwait_shr > 0 or self.__nwait_exc > 1:
128 a95fd5d7 Guido Trotter
        self.__wait(self.__turn_exc)
129 a95fd5d7 Guido Trotter
130 a95fd5d7 Guido Trotter
      while len(self.__shr) > 0 or self.__exc is not None:
131 a95fd5d7 Guido Trotter
        self.__wait(self.__turn_exc)
132 a95fd5d7 Guido Trotter
133 a95fd5d7 Guido Trotter
      self.__exc = threading.currentThread()
134 a95fd5d7 Guido Trotter
    finally:
135 a95fd5d7 Guido Trotter
      self.__nwait_exc -= 1
136 a95fd5d7 Guido Trotter
137 4d686df8 Guido Trotter
    assert self.__npass_shr == 0, "SharedLock: internal fairness violation"
138 4d686df8 Guido Trotter
139 162c1c1f Guido Trotter
  def acquire(self, blocking=1, shared=0):
140 162c1c1f Guido Trotter
    """Acquire a shared lock.
141 162c1c1f Guido Trotter

142 162c1c1f Guido Trotter
    Args:
143 162c1c1f Guido Trotter
      shared: whether to acquire in shared mode. By default an exclusive lock
144 162c1c1f Guido Trotter
              will be acquired.
145 162c1c1f Guido Trotter
      blocking: whether to block while trying to acquire or to operate in try-lock mode.
146 162c1c1f Guido Trotter
                this locking mode is not supported yet.
147 162c1c1f Guido Trotter

148 162c1c1f Guido Trotter
    """
149 162c1c1f Guido Trotter
    if not blocking:
150 162c1c1f Guido Trotter
      # We don't have non-blocking mode for now
151 162c1c1f Guido Trotter
      raise NotImplementedError
152 162c1c1f Guido Trotter
153 162c1c1f Guido Trotter
    self.__lock.acquire()
154 162c1c1f Guido Trotter
    try:
155 a95fd5d7 Guido Trotter
      if self.__deleted:
156 a95fd5d7 Guido Trotter
        raise errors.LockError('deleted lock')
157 a95fd5d7 Guido Trotter
158 162c1c1f Guido Trotter
      # We cannot acquire the lock if we already have it
159 162c1c1f Guido Trotter
      assert not self.__is_owned(), "double acquire() on a non-recursive lock"
160 4d686df8 Guido Trotter
      assert self.__npass_shr >= 0, "Internal fairness condition weirdness"
161 162c1c1f Guido Trotter
162 162c1c1f Guido Trotter
      if shared:
163 162c1c1f Guido Trotter
        self.__nwait_shr += 1
164 162c1c1f Guido Trotter
        try:
165 4d686df8 Guido Trotter
          wait = False
166 162c1c1f Guido Trotter
          # If there is an exclusive holder waiting we have to wait.  We'll
167 162c1c1f Guido Trotter
          # only do this once, though, when we start waiting for the lock. Then
168 162c1c1f Guido Trotter
          # we'll just wait while there are no exclusive holders.
169 162c1c1f Guido Trotter
          if self.__nwait_exc > 0:
170 162c1c1f Guido Trotter
            # TODO: if !blocking...
171 4d686df8 Guido Trotter
            wait = True
172 a95fd5d7 Guido Trotter
            self.__wait(self.__turn_shr)
173 162c1c1f Guido Trotter
174 162c1c1f Guido Trotter
          while self.__exc is not None:
175 4d686df8 Guido Trotter
            wait = True
176 162c1c1f Guido Trotter
            # TODO: if !blocking...
177 a95fd5d7 Guido Trotter
            self.__wait(self.__turn_shr)
178 162c1c1f Guido Trotter
179 162c1c1f Guido Trotter
          self.__shr.add(threading.currentThread())
180 4d686df8 Guido Trotter
181 4d686df8 Guido Trotter
          # If we were waiting note that we passed
182 4d686df8 Guido Trotter
          if wait:
183 4d686df8 Guido Trotter
            self.__npass_shr -= 1
184 4d686df8 Guido Trotter
185 162c1c1f Guido Trotter
        finally:
186 162c1c1f Guido Trotter
          self.__nwait_shr -= 1
187 162c1c1f Guido Trotter
188 4d686df8 Guido Trotter
        assert self.__npass_shr >= 0, "Internal fairness condition weirdness"
189 162c1c1f Guido Trotter
      else:
190 a95fd5d7 Guido Trotter
        # TODO: if !blocking...
191 a95fd5d7 Guido Trotter
        # (or modify __exclusive_acquire for non-blocking mode)
192 a95fd5d7 Guido Trotter
        self.__exclusive_acquire()
193 162c1c1f Guido Trotter
194 162c1c1f Guido Trotter
    finally:
195 162c1c1f Guido Trotter
      self.__lock.release()
196 162c1c1f Guido Trotter
197 162c1c1f Guido Trotter
    return True
198 162c1c1f Guido Trotter
199 162c1c1f Guido Trotter
  def release(self):
200 162c1c1f Guido Trotter
    """Release a Shared Lock.
201 162c1c1f Guido Trotter

202 162c1c1f Guido Trotter
    You must have acquired the lock, either in shared or in exclusive mode,
203 162c1c1f Guido Trotter
    before calling this function.
204 162c1c1f Guido Trotter

205 162c1c1f Guido Trotter
    """
206 162c1c1f Guido Trotter
    self.__lock.acquire()
207 162c1c1f Guido Trotter
    try:
208 4d686df8 Guido Trotter
      assert self.__npass_shr >= 0, "Internal fairness condition weirdness"
209 162c1c1f Guido Trotter
      # Autodetect release type
210 162c1c1f Guido Trotter
      if self.__is_exclusive():
211 162c1c1f Guido Trotter
        self.__exc = None
212 162c1c1f Guido Trotter
213 162c1c1f Guido Trotter
        # An exclusive holder has just had the lock, time to put it in shared
214 162c1c1f Guido Trotter
        # mode if there are shared holders waiting. Otherwise wake up the next
215 162c1c1f Guido Trotter
        # exclusive holder.
216 162c1c1f Guido Trotter
        if self.__nwait_shr > 0:
217 4d686df8 Guido Trotter
          # Make sure at least the ones which were blocked pass.
218 4d686df8 Guido Trotter
          self.__npass_shr = self.__nwait_shr
219 162c1c1f Guido Trotter
          self.__turn_shr.notifyAll()
220 162c1c1f Guido Trotter
        elif self.__nwait_exc > 0:
221 162c1c1f Guido Trotter
         self.__turn_exc.notify()
222 162c1c1f Guido Trotter
223 162c1c1f Guido Trotter
      elif self.__is_sharer():
224 162c1c1f Guido Trotter
        self.__shr.remove(threading.currentThread())
225 162c1c1f Guido Trotter
226 4d686df8 Guido Trotter
        # If there are shared holders waiting (and not just scheduled to pass)
227 4d686df8 Guido Trotter
        # there *must* be an exclusive holder waiting as well; otherwise what
228 4d686df8 Guido Trotter
        # were they waiting for?
229 4d686df8 Guido Trotter
        assert (self.__nwait_exc > 0 or self.__npass_shr > 0 or
230 4d686df8 Guido Trotter
                self.__nwait_shr == 0), \
231 4d686df8 Guido Trotter
               "Lock sharers waiting while no exclusive is queueing"
232 4d686df8 Guido Trotter
233 4d686df8 Guido Trotter
        # If there are no more shared holders either in or scheduled to pass,
234 4d686df8 Guido Trotter
        # and some exclusive holders are waiting let's wake one up.
235 4d686df8 Guido Trotter
        if (len(self.__shr) == 0 and
236 4d686df8 Guido Trotter
            self.__nwait_exc > 0 and
237 4d686df8 Guido Trotter
            not self.__npass_shr > 0):
238 162c1c1f Guido Trotter
          self.__turn_exc.notify()
239 162c1c1f Guido Trotter
240 162c1c1f Guido Trotter
      else:
241 162c1c1f Guido Trotter
        assert False, "Cannot release non-owned lock"
242 162c1c1f Guido Trotter
243 162c1c1f Guido Trotter
    finally:
244 162c1c1f Guido Trotter
      self.__lock.release()
245 162c1c1f Guido Trotter
246 a95fd5d7 Guido Trotter
  def delete(self, blocking=1):
247 a95fd5d7 Guido Trotter
    """Delete a Shared Lock.
248 a95fd5d7 Guido Trotter

249 a95fd5d7 Guido Trotter
    This operation will declare the lock for removal. First the lock will be
250 a95fd5d7 Guido Trotter
    acquired in exclusive mode if you don't already own it, then the lock
251 a95fd5d7 Guido Trotter
    will be put in a state where any future and pending acquire() fail.
252 a95fd5d7 Guido Trotter

253 a95fd5d7 Guido Trotter
    Args:
254 a95fd5d7 Guido Trotter
      blocking: whether to block while trying to acquire or to operate in
255 a95fd5d7 Guido Trotter
                try-lock mode.  this locking mode is not supported yet unless
256 a95fd5d7 Guido Trotter
                you are already holding exclusively the lock.
257 a95fd5d7 Guido Trotter

258 a95fd5d7 Guido Trotter
    """
259 a95fd5d7 Guido Trotter
    self.__lock.acquire()
260 a95fd5d7 Guido Trotter
    try:
261 a95fd5d7 Guido Trotter
      assert not self.__is_sharer(), "cannot delete() a lock while sharing it"
262 a95fd5d7 Guido Trotter
263 a95fd5d7 Guido Trotter
      if self.__deleted:
264 a95fd5d7 Guido Trotter
        raise errors.LockError('deleted lock')
265 a95fd5d7 Guido Trotter
266 a95fd5d7 Guido Trotter
      if not self.__is_exclusive():
267 a95fd5d7 Guido Trotter
        if not blocking:
268 a95fd5d7 Guido Trotter
          # We don't have non-blocking mode for now
269 a95fd5d7 Guido Trotter
          raise NotImplementedError
270 a95fd5d7 Guido Trotter
        self.__exclusive_acquire()
271 a95fd5d7 Guido Trotter
272 a95fd5d7 Guido Trotter
      self.__deleted = True
273 a95fd5d7 Guido Trotter
      self.__exc = None
274 a95fd5d7 Guido Trotter
      # Wake up everybody, they will fail acquiring the lock and
275 a95fd5d7 Guido Trotter
      # raise an exception instead.
276 a95fd5d7 Guido Trotter
      self.__turn_exc.notifyAll()
277 a95fd5d7 Guido Trotter
      self.__turn_shr.notifyAll()
278 a95fd5d7 Guido Trotter
279 a95fd5d7 Guido Trotter
    finally:
280 a95fd5d7 Guido Trotter
      self.__lock.release()
281 a95fd5d7 Guido Trotter
282 aaae9bc0 Guido Trotter
283 aaae9bc0 Guido Trotter
class LockSet:
284 aaae9bc0 Guido Trotter
  """Implements a set of locks.
285 aaae9bc0 Guido Trotter

286 aaae9bc0 Guido Trotter
  This abstraction implements a set of shared locks for the same resource type,
287 aaae9bc0 Guido Trotter
  distinguished by name. The user can lock a subset of the resources and the
288 aaae9bc0 Guido Trotter
  LockSet will take care of acquiring the locks always in the same order, thus
289 aaae9bc0 Guido Trotter
  preventing deadlock.
290 aaae9bc0 Guido Trotter

291 aaae9bc0 Guido Trotter
  All the locks needed in the same set must be acquired together, though.
292 aaae9bc0 Guido Trotter

293 aaae9bc0 Guido Trotter
  """
294 aaae9bc0 Guido Trotter
  def __init__(self, members=None):
295 aaae9bc0 Guido Trotter
    """Constructs a new LockSet.
296 aaae9bc0 Guido Trotter

297 aaae9bc0 Guido Trotter
    Args:
298 aaae9bc0 Guido Trotter
      members: initial members of the set
299 aaae9bc0 Guido Trotter

300 aaae9bc0 Guido Trotter
    """
301 aaae9bc0 Guido Trotter
    # Used internally to guarantee coherency.
302 aaae9bc0 Guido Trotter
    self.__lock = SharedLock()
303 aaae9bc0 Guido Trotter
304 aaae9bc0 Guido Trotter
    # The lockdict indexes the relationship name -> lock
305 aaae9bc0 Guido Trotter
    # The order-of-locking is implied by the alphabetical order of names
306 aaae9bc0 Guido Trotter
    self.__lockdict = {}
307 aaae9bc0 Guido Trotter
308 aaae9bc0 Guido Trotter
    if members is not None:
309 aaae9bc0 Guido Trotter
      for name in members:
310 aaae9bc0 Guido Trotter
        self.__lockdict[name] = SharedLock()
311 aaae9bc0 Guido Trotter
312 aaae9bc0 Guido Trotter
    # The owner dict contains the set of locks each thread owns. For
313 aaae9bc0 Guido Trotter
    # performance each thread can access its own key without a global lock on
314 aaae9bc0 Guido Trotter
    # this structure. It is paramount though that *no* other type of access is
315 aaae9bc0 Guido Trotter
    # done to this structure (eg. no looping over its keys). *_owner helper
316 aaae9bc0 Guido Trotter
    # function are defined to guarantee access is correct, but in general never
317 aaae9bc0 Guido Trotter
    # do anything different than __owners[threading.currentThread()], or there
318 aaae9bc0 Guido Trotter
    # will be trouble.
319 aaae9bc0 Guido Trotter
    self.__owners = {}
320 aaae9bc0 Guido Trotter
321 aaae9bc0 Guido Trotter
  def _is_owned(self):
322 aaae9bc0 Guido Trotter
    """Is the current thread a current level owner?"""
323 aaae9bc0 Guido Trotter
    return threading.currentThread() in self.__owners
324 aaae9bc0 Guido Trotter
325 b2dabfd6 Guido Trotter
  def _add_owned(self, name=None):
326 aaae9bc0 Guido Trotter
    """Note the current thread owns the given lock"""
327 b2dabfd6 Guido Trotter
    if name is None:
328 b2dabfd6 Guido Trotter
      if not self._is_owned():
329 b2dabfd6 Guido Trotter
        self.__owners[threading.currentThread()] = set()
330 aaae9bc0 Guido Trotter
    else:
331 b2dabfd6 Guido Trotter
      if self._is_owned():
332 b2dabfd6 Guido Trotter
        self.__owners[threading.currentThread()].add(name)
333 b2dabfd6 Guido Trotter
      else:
334 b2dabfd6 Guido Trotter
        self.__owners[threading.currentThread()] = set([name])
335 b2dabfd6 Guido Trotter
336 aaae9bc0 Guido Trotter
337 b2dabfd6 Guido Trotter
  def _del_owned(self, name=None):
338 aaae9bc0 Guido Trotter
    """Note the current thread owns the given lock"""
339 aaae9bc0 Guido Trotter
340 b2dabfd6 Guido Trotter
    if name is not None:
341 b2dabfd6 Guido Trotter
      self.__owners[threading.currentThread()].remove(name)
342 b2dabfd6 Guido Trotter
343 b2dabfd6 Guido Trotter
    # Only remove the key if we don't hold the set-lock as well
344 b2dabfd6 Guido Trotter
    if (not self.__lock._is_owned() and
345 b2dabfd6 Guido Trotter
        not self.__owners[threading.currentThread()]):
346 aaae9bc0 Guido Trotter
      del self.__owners[threading.currentThread()]
347 aaae9bc0 Guido Trotter
348 aaae9bc0 Guido Trotter
  def _list_owned(self):
349 aaae9bc0 Guido Trotter
    """Get the set of resource names owned by the current thread"""
350 aaae9bc0 Guido Trotter
    if self._is_owned():
351 aaae9bc0 Guido Trotter
      return self.__owners[threading.currentThread()].copy()
352 aaae9bc0 Guido Trotter
    else:
353 aaae9bc0 Guido Trotter
      return set()
354 aaae9bc0 Guido Trotter
355 aaae9bc0 Guido Trotter
  def __names(self):
356 aaae9bc0 Guido Trotter
    """Return the current set of names.
357 aaae9bc0 Guido Trotter

358 aaae9bc0 Guido Trotter
    Only call this function while holding __lock and don't iterate on the
359 aaae9bc0 Guido Trotter
    result after releasing the lock.
360 aaae9bc0 Guido Trotter

361 aaae9bc0 Guido Trotter
    """
362 0cf257c5 Guido Trotter
    return self.__lockdict.keys()
363 aaae9bc0 Guido Trotter
364 aaae9bc0 Guido Trotter
  def _names(self):
365 aaae9bc0 Guido Trotter
    """Return a copy of the current set of elements.
366 aaae9bc0 Guido Trotter

367 aaae9bc0 Guido Trotter
    Used only for debugging purposes.
368 cdb08f44 Michael Hanselmann

369 aaae9bc0 Guido Trotter
    """
370 aaae9bc0 Guido Trotter
    self.__lock.acquire(shared=1)
371 aaae9bc0 Guido Trotter
    try:
372 aaae9bc0 Guido Trotter
      result = self.__names()
373 aaae9bc0 Guido Trotter
    finally:
374 aaae9bc0 Guido Trotter
      self.__lock.release()
375 0cf257c5 Guido Trotter
    return set(result)
376 aaae9bc0 Guido Trotter
377 aaae9bc0 Guido Trotter
  def acquire(self, names, blocking=1, shared=0):
378 aaae9bc0 Guido Trotter
    """Acquire a set of resource locks.
379 aaae9bc0 Guido Trotter

380 aaae9bc0 Guido Trotter
    Args:
381 aaae9bc0 Guido Trotter
      names: the names of the locks which shall be acquired.
382 aaae9bc0 Guido Trotter
             (special lock names, or instance/node names)
383 aaae9bc0 Guido Trotter
      shared: whether to acquire in shared mode. By default an exclusive lock
384 aaae9bc0 Guido Trotter
              will be acquired.
385 aaae9bc0 Guido Trotter
      blocking: whether to block while trying to acquire or to operate in try-lock mode.
386 aaae9bc0 Guido Trotter
                this locking mode is not supported yet.
387 aaae9bc0 Guido Trotter

388 aaae9bc0 Guido Trotter
    Returns:
389 aaae9bc0 Guido Trotter
      True: when all the locks are successfully acquired
390 aaae9bc0 Guido Trotter

391 aaae9bc0 Guido Trotter
    Raises:
392 aaae9bc0 Guido Trotter
      errors.LockError: when any lock we try to acquire has been deleted
393 aaae9bc0 Guido Trotter
      before we succeed. In this case none of the locks requested will be
394 aaae9bc0 Guido Trotter
      acquired.
395 aaae9bc0 Guido Trotter

396 aaae9bc0 Guido Trotter
    """
397 aaae9bc0 Guido Trotter
    if not blocking:
398 aaae9bc0 Guido Trotter
      # We don't have non-blocking mode for now
399 aaae9bc0 Guido Trotter
      raise NotImplementedError
400 aaae9bc0 Guido Trotter
401 aaae9bc0 Guido Trotter
    # Check we don't already own locks at this level
402 aaae9bc0 Guido Trotter
    assert not self._is_owned(), "Cannot acquire locks in the same set twice"
403 aaae9bc0 Guido Trotter
404 3b7ed473 Guido Trotter
    if names is None:
405 3b7ed473 Guido Trotter
      # If no names are given acquire the whole set by not letting new names
406 3b7ed473 Guido Trotter
      # being added before we release, and getting the current list of names.
407 3b7ed473 Guido Trotter
      # Some of them may then be deleted later, but we'll cope with this.
408 3b7ed473 Guido Trotter
      #
409 3b7ed473 Guido Trotter
      # We'd like to acquire this lock in a shared way, as it's nice if
410 3b7ed473 Guido Trotter
      # everybody else can use the instances at the same time. If are acquiring
411 3b7ed473 Guido Trotter
      # them exclusively though they won't be able to do this anyway, though,
412 3b7ed473 Guido Trotter
      # so we'll get the list lock exclusively as well in order to be able to
413 3b7ed473 Guido Trotter
      # do add() on the set while owning it.
414 3b7ed473 Guido Trotter
      self.__lock.acquire(shared=shared)
415 b2dabfd6 Guido Trotter
      try:
416 b2dabfd6 Guido Trotter
        # note we own the set-lock
417 b2dabfd6 Guido Trotter
        self._add_owned()
418 b2dabfd6 Guido Trotter
        names = self.__names()
419 b2dabfd6 Guido Trotter
      except:
420 b2dabfd6 Guido Trotter
        # We shouldn't have problems adding the lock to the owners list, but
421 b2dabfd6 Guido Trotter
        # if we did we'll try to release this lock and re-raise exception.
422 b2dabfd6 Guido Trotter
        # Of course something is going to be really wrong, after this.
423 b2dabfd6 Guido Trotter
        self.__lock.release()
424 b2dabfd6 Guido Trotter
        raise
425 3b7ed473 Guido Trotter
426 806e20fd Guido Trotter
    try:
427 806e20fd Guido Trotter
      # Support passing in a single resource to acquire rather than many
428 806e20fd Guido Trotter
      if isinstance(names, basestring):
429 806e20fd Guido Trotter
        names = [names]
430 806e20fd Guido Trotter
      else:
431 806e20fd Guido Trotter
        names.sort()
432 806e20fd Guido Trotter
433 806e20fd Guido Trotter
      acquire_list = []
434 806e20fd Guido Trotter
      # First we look the locks up on __lockdict. We have no way of being sure
435 806e20fd Guido Trotter
      # they will still be there after, but this makes it a lot faster should
436 806e20fd Guido Trotter
      # just one of them be the already wrong
437 806e20fd Guido Trotter
      for lname in names:
438 806e20fd Guido Trotter
        try:
439 806e20fd Guido Trotter
          lock = self.__lockdict[lname] # raises KeyError if the lock is not there
440 806e20fd Guido Trotter
          acquire_list.append((lname, lock))
441 806e20fd Guido Trotter
        except (KeyError):
442 3b7ed473 Guido Trotter
          if self.__lock._is_owned():
443 3b7ed473 Guido Trotter
            # We are acquiring all the set, it doesn't matter if this particular
444 3b7ed473 Guido Trotter
            # element is not there anymore.
445 3b7ed473 Guido Trotter
            continue
446 3b7ed473 Guido Trotter
          else:
447 3b7ed473 Guido Trotter
            raise errors.LockError('non-existing lock in set (%s)' % lname)
448 806e20fd Guido Trotter
449 806e20fd Guido Trotter
      # This will hold the locknames we effectively acquired.
450 806e20fd Guido Trotter
      acquired = set()
451 806e20fd Guido Trotter
      # Now acquire_list contains a sorted list of resources and locks we want.
452 806e20fd Guido Trotter
      # In order to get them we loop on this (private) list and acquire() them.
453 806e20fd Guido Trotter
      # We gave no real guarantee they will still exist till this is done but
454 806e20fd Guido Trotter
      # .acquire() itself is safe and will alert us if the lock gets deleted.
455 806e20fd Guido Trotter
      for (lname, lock) in acquire_list:
456 aaae9bc0 Guido Trotter
        try:
457 806e20fd Guido Trotter
          lock.acquire(shared=shared) # raises LockError if the lock is deleted
458 ea3f80bf Guido Trotter
          # now the lock cannot be deleted, we have it!
459 b2dabfd6 Guido Trotter
          self._add_owned(name=lname)
460 ea3f80bf Guido Trotter
          acquired.add(lname)
461 806e20fd Guido Trotter
        except (errors.LockError):
462 3b7ed473 Guido Trotter
          if self.__lock._is_owned():
463 3b7ed473 Guido Trotter
            # We are acquiring all the set, it doesn't matter if this particular
464 3b7ed473 Guido Trotter
            # element is not there anymore.
465 3b7ed473 Guido Trotter
            continue
466 3b7ed473 Guido Trotter
          else:
467 3b7ed473 Guido Trotter
            name_fail = lname
468 3b7ed473 Guido Trotter
            for lname in self._list_owned():
469 3b7ed473 Guido Trotter
              self.__lockdict[lname].release()
470 b2dabfd6 Guido Trotter
              self._del_owned(name=lname)
471 3b7ed473 Guido Trotter
            raise errors.LockError('non-existing lock in set (%s)' % name_fail)
472 ea3f80bf Guido Trotter
        except:
473 ea3f80bf Guido Trotter
          # We shouldn't have problems adding the lock to the owners list, but
474 ea3f80bf Guido Trotter
          # if we did we'll try to release this lock and re-raise exception.
475 ea3f80bf Guido Trotter
          # Of course something is going to be really wrong, after this.
476 ea3f80bf Guido Trotter
          if lock._is_owned():
477 ea3f80bf Guido Trotter
            lock.release()
478 ea3f80bf Guido Trotter
            raise
479 806e20fd Guido Trotter
480 806e20fd Guido Trotter
    except:
481 3b7ed473 Guido Trotter
      # If something went wrong and we had the set-lock let's release it...
482 3b7ed473 Guido Trotter
      if self.__lock._is_owned():
483 3b7ed473 Guido Trotter
        self.__lock.release()
484 806e20fd Guido Trotter
      raise
485 aaae9bc0 Guido Trotter
486 0cc00929 Guido Trotter
    return acquired
487 aaae9bc0 Guido Trotter
488 aaae9bc0 Guido Trotter
  def release(self, names=None):
489 aaae9bc0 Guido Trotter
    """Release a set of resource locks, at the same level.
490 aaae9bc0 Guido Trotter

491 aaae9bc0 Guido Trotter
    You must have acquired the locks, either in shared or in exclusive mode,
492 aaae9bc0 Guido Trotter
    before releasing them.
493 aaae9bc0 Guido Trotter

494 aaae9bc0 Guido Trotter
    Args:
495 aaae9bc0 Guido Trotter
      names: the names of the locks which shall be released.
496 aaae9bc0 Guido Trotter
             (defaults to all the locks acquired at that level).
497 aaae9bc0 Guido Trotter

498 aaae9bc0 Guido Trotter
    """
499 aaae9bc0 Guido Trotter
    assert self._is_owned(), "release() on lock set while not owner"
500 aaae9bc0 Guido Trotter
501 aaae9bc0 Guido Trotter
    # Support passing in a single resource to release rather than many
502 aaae9bc0 Guido Trotter
    if isinstance(names, basestring):
503 aaae9bc0 Guido Trotter
      names = [names]
504 aaae9bc0 Guido Trotter
505 aaae9bc0 Guido Trotter
    if names is None:
506 aaae9bc0 Guido Trotter
      names = self._list_owned()
507 aaae9bc0 Guido Trotter
    else:
508 aaae9bc0 Guido Trotter
      names = set(names)
509 aaae9bc0 Guido Trotter
      assert self._list_owned().issuperset(names), (
510 aaae9bc0 Guido Trotter
               "release() on unheld resources %s" %
511 aaae9bc0 Guido Trotter
               names.difference(self._list_owned()))
512 aaae9bc0 Guido Trotter
513 3b7ed473 Guido Trotter
    # First of all let's release the "all elements" lock, if set.
514 3b7ed473 Guido Trotter
    # After this 'add' can work again
515 3b7ed473 Guido Trotter
    if self.__lock._is_owned():
516 3b7ed473 Guido Trotter
      self.__lock.release()
517 b2dabfd6 Guido Trotter
      self._del_owned()
518 3b7ed473 Guido Trotter
519 aaae9bc0 Guido Trotter
    for lockname in names:
520 aaae9bc0 Guido Trotter
      # If we are sure the lock doesn't leave __lockdict without being
521 aaae9bc0 Guido Trotter
      # exclusively held we can do this...
522 aaae9bc0 Guido Trotter
      self.__lockdict[lockname].release()
523 b2dabfd6 Guido Trotter
      self._del_owned(name=lockname)
524 aaae9bc0 Guido Trotter
525 aaae9bc0 Guido Trotter
  def add(self, names, acquired=0, shared=0):
526 aaae9bc0 Guido Trotter
    """Add a new set of elements to the set
527 aaae9bc0 Guido Trotter

528 aaae9bc0 Guido Trotter
    Args:
529 aaae9bc0 Guido Trotter
      names: names of the new elements to add
530 aaae9bc0 Guido Trotter
      acquired: pre-acquire the new resource?
531 aaae9bc0 Guido Trotter
      shared: is the pre-acquisition shared?
532 aaae9bc0 Guido Trotter

533 aaae9bc0 Guido Trotter
    """
534 3b7ed473 Guido Trotter
535 3b7ed473 Guido Trotter
    assert not self.__lock._is_owned(shared=1), (
536 3b7ed473 Guido Trotter
           "Cannot add new elements while sharing the set-lock")
537 3b7ed473 Guido Trotter
538 aaae9bc0 Guido Trotter
    # Support passing in a single resource to add rather than many
539 aaae9bc0 Guido Trotter
    if isinstance(names, basestring):
540 aaae9bc0 Guido Trotter
      names = [names]
541 aaae9bc0 Guido Trotter
542 3b7ed473 Guido Trotter
    # If we don't already own the set-level lock acquire it in an exclusive way
543 3b7ed473 Guido Trotter
    # we'll get it and note we need to release it later.
544 3b7ed473 Guido Trotter
    release_lock = False
545 3b7ed473 Guido Trotter
    if not self.__lock._is_owned():
546 3b7ed473 Guido Trotter
      release_lock = True
547 3b7ed473 Guido Trotter
      self.__lock.acquire()
548 3b7ed473 Guido Trotter
549 aaae9bc0 Guido Trotter
    try:
550 0cf257c5 Guido Trotter
      invalid_names = set(self.__names()).intersection(names)
551 aaae9bc0 Guido Trotter
      if invalid_names:
552 aaae9bc0 Guido Trotter
        # This must be an explicit raise, not an assert, because assert is
553 aaae9bc0 Guido Trotter
        # turned off when using optimization, and this can happen because of
554 aaae9bc0 Guido Trotter
        # concurrency even if the user doesn't want it.
555 aaae9bc0 Guido Trotter
        raise errors.LockError("duplicate add() (%s)" % invalid_names)
556 aaae9bc0 Guido Trotter
557 aaae9bc0 Guido Trotter
      for lockname in names:
558 aaae9bc0 Guido Trotter
        lock = SharedLock()
559 aaae9bc0 Guido Trotter
560 aaae9bc0 Guido Trotter
        if acquired:
561 aaae9bc0 Guido Trotter
          lock.acquire(shared=shared)
562 aaae9bc0 Guido Trotter
          # now the lock cannot be deleted, we have it!
563 aaae9bc0 Guido Trotter
          try:
564 b2dabfd6 Guido Trotter
            self._add_owned(name=lockname)
565 aaae9bc0 Guido Trotter
          except:
566 aaae9bc0 Guido Trotter
            # We shouldn't have problems adding the lock to the owners list,
567 aaae9bc0 Guido Trotter
            # but if we did we'll try to release this lock and re-raise
568 aaae9bc0 Guido Trotter
            # exception.  Of course something is going to be really wrong,
569 aaae9bc0 Guido Trotter
            # after this.  On the other hand the lock hasn't been added to the
570 aaae9bc0 Guido Trotter
            # __lockdict yet so no other threads should be pending on it. This
571 aaae9bc0 Guido Trotter
            # release is just a safety measure.
572 aaae9bc0 Guido Trotter
            lock.release()
573 aaae9bc0 Guido Trotter
            raise
574 aaae9bc0 Guido Trotter
575 aaae9bc0 Guido Trotter
        self.__lockdict[lockname] = lock
576 aaae9bc0 Guido Trotter
577 aaae9bc0 Guido Trotter
    finally:
578 3b7ed473 Guido Trotter
      # Only release __lock if we were not holding it previously.
579 3b7ed473 Guido Trotter
      if release_lock:
580 3b7ed473 Guido Trotter
        self.__lock.release()
581 aaae9bc0 Guido Trotter
582 aaae9bc0 Guido Trotter
    return True
583 aaae9bc0 Guido Trotter
584 aaae9bc0 Guido Trotter
  def remove(self, names, blocking=1):
585 aaae9bc0 Guido Trotter
    """Remove elements from the lock set.
586 aaae9bc0 Guido Trotter

587 aaae9bc0 Guido Trotter
    You can either not hold anything in the lockset or already hold a superset
588 aaae9bc0 Guido Trotter
    of the elements you want to delete, exclusively.
589 aaae9bc0 Guido Trotter

590 aaae9bc0 Guido Trotter
    Args:
591 aaae9bc0 Guido Trotter
      names: names of the resource to remove.
592 aaae9bc0 Guido Trotter
      blocking: whether to block while trying to acquire or to operate in
593 aaae9bc0 Guido Trotter
                try-lock mode.  this locking mode is not supported yet unless
594 aaae9bc0 Guido Trotter
                you are already holding exclusively the locks.
595 aaae9bc0 Guido Trotter

596 aaae9bc0 Guido Trotter
    Returns:
597 3f404fc5 Guido Trotter
      A list of lock which we removed. The list is always equal to the names
598 3f404fc5 Guido Trotter
      list if we were holding all the locks exclusively.
599 aaae9bc0 Guido Trotter

600 aaae9bc0 Guido Trotter
    """
601 aaae9bc0 Guido Trotter
    if not blocking and not self._is_owned():
602 aaae9bc0 Guido Trotter
      # We don't have non-blocking mode for now
603 aaae9bc0 Guido Trotter
      raise NotImplementedError
604 aaae9bc0 Guido Trotter
605 aaae9bc0 Guido Trotter
    # Support passing in a single resource to remove rather than many
606 aaae9bc0 Guido Trotter
    if isinstance(names, basestring):
607 aaae9bc0 Guido Trotter
      names = [names]
608 aaae9bc0 Guido Trotter
609 aaae9bc0 Guido Trotter
    # If we own any subset of this lock it must be a superset of what we want
610 aaae9bc0 Guido Trotter
    # to delete. The ownership must also be exclusive, but that will be checked
611 aaae9bc0 Guido Trotter
    # by the lock itself.
612 aaae9bc0 Guido Trotter
    assert not self._is_owned() or self._list_owned().issuperset(names), (
613 aaae9bc0 Guido Trotter
      "remove() on acquired lockset while not owning all elements")
614 aaae9bc0 Guido Trotter
615 3f404fc5 Guido Trotter
    removed = []
616 aaae9bc0 Guido Trotter
617 aaae9bc0 Guido Trotter
    for lname in names:
618 aaae9bc0 Guido Trotter
      # Calling delete() acquires the lock exclusively if we don't already own
619 aaae9bc0 Guido Trotter
      # it, and causes all pending and subsequent lock acquires to fail. It's
620 aaae9bc0 Guido Trotter
      # fine to call it out of order because delete() also implies release(),
621 aaae9bc0 Guido Trotter
      # and the assertion above guarantees that if we either already hold
622 aaae9bc0 Guido Trotter
      # everything we want to delete, or we hold none.
623 aaae9bc0 Guido Trotter
      try:
624 aaae9bc0 Guido Trotter
        self.__lockdict[lname].delete()
625 3f404fc5 Guido Trotter
        removed.append(lname)
626 aaae9bc0 Guido Trotter
      except (KeyError, errors.LockError):
627 aaae9bc0 Guido Trotter
        # This cannot happen if we were already holding it, verify:
628 aaae9bc0 Guido Trotter
        assert not self._is_owned(), "remove failed while holding lockset"
629 aaae9bc0 Guido Trotter
      else:
630 aaae9bc0 Guido Trotter
        # If no LockError was raised we are the ones who deleted the lock.
631 aaae9bc0 Guido Trotter
        # This means we can safely remove it from lockdict, as any further or
632 aaae9bc0 Guido Trotter
        # pending delete() or acquire() will fail (and nobody can have the lock
633 aaae9bc0 Guido Trotter
        # since before our call to delete()).
634 aaae9bc0 Guido Trotter
        #
635 aaae9bc0 Guido Trotter
        # This is done in an else clause because if the exception was thrown
636 aaae9bc0 Guido Trotter
        # it's the job of the one who actually deleted it.
637 aaae9bc0 Guido Trotter
        del self.__lockdict[lname]
638 aaae9bc0 Guido Trotter
        # And let's remove it from our private list if we owned it.
639 aaae9bc0 Guido Trotter
        if self._is_owned():
640 b2dabfd6 Guido Trotter
          self._del_owned(name=lname)
641 aaae9bc0 Guido Trotter
642 3f404fc5 Guido Trotter
    return removed
643 aaae9bc0 Guido Trotter
644 7ee7c0c7 Guido Trotter
645 7ee7c0c7 Guido Trotter
# Locking levels, must be acquired in increasing order.
646 7ee7c0c7 Guido Trotter
# Current rules are:
647 7ee7c0c7 Guido Trotter
#   - at level LEVEL_CLUSTER resides the Big Ganeti Lock (BGL) which must be
648 7ee7c0c7 Guido Trotter
#   acquired before performing any operation, either in shared or in exclusive
649 7ee7c0c7 Guido Trotter
#   mode. acquiring the BGL in exclusive mode is discouraged and should be
650 7ee7c0c7 Guido Trotter
#   avoided.
651 7ee7c0c7 Guido Trotter
#   - at levels LEVEL_NODE and LEVEL_INSTANCE reside node and instance locks.
652 7ee7c0c7 Guido Trotter
#   If you need more than one node, or more than one instance, acquire them at
653 7ee7c0c7 Guido Trotter
#   the same time.
654 7ee7c0c7 Guido Trotter
#  - level LEVEL_CONFIG contains the configuration lock, which you must acquire
655 7ee7c0c7 Guido Trotter
#  before reading or changing the config file.
656 7ee7c0c7 Guido Trotter
LEVEL_CLUSTER = 0
657 7ee7c0c7 Guido Trotter
LEVEL_NODE = 1
658 7ee7c0c7 Guido Trotter
LEVEL_INSTANCE = 2
659 7ee7c0c7 Guido Trotter
LEVEL_CONFIG = 3
660 7ee7c0c7 Guido Trotter
661 7ee7c0c7 Guido Trotter
LEVELS = [LEVEL_CLUSTER,
662 7ee7c0c7 Guido Trotter
          LEVEL_NODE,
663 7ee7c0c7 Guido Trotter
          LEVEL_INSTANCE,
664 7ee7c0c7 Guido Trotter
          LEVEL_CONFIG]
665 7ee7c0c7 Guido Trotter
666 7ee7c0c7 Guido Trotter
# Lock levels which are modifiable
667 7ee7c0c7 Guido Trotter
LEVELS_MOD = [LEVEL_NODE, LEVEL_INSTANCE]
668 7ee7c0c7 Guido Trotter
669 7ee7c0c7 Guido Trotter
# Constant for the big ganeti lock and config lock
670 7ee7c0c7 Guido Trotter
BGL = 'BGL'
671 7ee7c0c7 Guido Trotter
CONFIG = 'config'
672 7ee7c0c7 Guido Trotter
673 7ee7c0c7 Guido Trotter
674 7ee7c0c7 Guido Trotter
class GanetiLockManager:
675 7ee7c0c7 Guido Trotter
  """The Ganeti Locking Library
676 7ee7c0c7 Guido Trotter

677 7ee7c0c7 Guido Trotter
  The purpouse of this small library is to manage locking for ganeti clusters
678 7ee7c0c7 Guido Trotter
  in a central place, while at the same time doing dynamic checks against
679 7ee7c0c7 Guido Trotter
  possible deadlocks. It will also make it easier to transition to a different
680 7ee7c0c7 Guido Trotter
  lock type should we migrate away from python threads.
681 7ee7c0c7 Guido Trotter

682 7ee7c0c7 Guido Trotter
  """
683 7ee7c0c7 Guido Trotter
  _instance = None
684 7ee7c0c7 Guido Trotter
685 7ee7c0c7 Guido Trotter
  def __init__(self, nodes=None, instances=None):
686 7ee7c0c7 Guido Trotter
    """Constructs a new GanetiLockManager object.
687 7ee7c0c7 Guido Trotter

688 7ee7c0c7 Guido Trotter
    There should be only a
689 7ee7c0c7 Guido Trotter
    GanetiLockManager object at any time, so this function raises an error if this
690 7ee7c0c7 Guido Trotter
    is not the case.
691 7ee7c0c7 Guido Trotter

692 7ee7c0c7 Guido Trotter
    Args:
693 7ee7c0c7 Guido Trotter
      nodes: list of node names
694 7ee7c0c7 Guido Trotter
      instances: list of instance names
695 7ee7c0c7 Guido Trotter

696 7ee7c0c7 Guido Trotter
    """
697 7ee7c0c7 Guido Trotter
    assert self.__class__._instance is None, "double GanetiLockManager instance"
698 7ee7c0c7 Guido Trotter
    self.__class__._instance = self
699 7ee7c0c7 Guido Trotter
700 7ee7c0c7 Guido Trotter
    # The keyring contains all the locks, at their level and in the correct
701 7ee7c0c7 Guido Trotter
    # locking order.
702 7ee7c0c7 Guido Trotter
    self.__keyring = {
703 7ee7c0c7 Guido Trotter
      LEVEL_CLUSTER: LockSet([BGL]),
704 7ee7c0c7 Guido Trotter
      LEVEL_NODE: LockSet(nodes),
705 7ee7c0c7 Guido Trotter
      LEVEL_INSTANCE: LockSet(instances),
706 7ee7c0c7 Guido Trotter
      LEVEL_CONFIG: LockSet([CONFIG]),
707 7ee7c0c7 Guido Trotter
    }
708 7ee7c0c7 Guido Trotter
709 7ee7c0c7 Guido Trotter
  def _names(self, level):
710 7ee7c0c7 Guido Trotter
    """List the lock names at the given level.
711 7ee7c0c7 Guido Trotter
    Used for debugging/testing purposes.
712 7ee7c0c7 Guido Trotter

713 7ee7c0c7 Guido Trotter
    Args:
714 7ee7c0c7 Guido Trotter
      level: the level whose list of locks to get
715 7ee7c0c7 Guido Trotter

716 7ee7c0c7 Guido Trotter
    """
717 7ee7c0c7 Guido Trotter
    assert level in LEVELS, "Invalid locking level %s" % level
718 7ee7c0c7 Guido Trotter
    return self.__keyring[level]._names()
719 7ee7c0c7 Guido Trotter
720 7ee7c0c7 Guido Trotter
  def _is_owned(self, level):
721 7ee7c0c7 Guido Trotter
    """Check whether we are owning locks at the given level
722 7ee7c0c7 Guido Trotter

723 7ee7c0c7 Guido Trotter
    """
724 7ee7c0c7 Guido Trotter
    return self.__keyring[level]._is_owned()
725 7ee7c0c7 Guido Trotter
726 7ee7c0c7 Guido Trotter
  def _list_owned(self, level):
727 7ee7c0c7 Guido Trotter
    """Get the set of owned locks at the given level
728 7ee7c0c7 Guido Trotter

729 7ee7c0c7 Guido Trotter
    """
730 7ee7c0c7 Guido Trotter
    return self.__keyring[level]._list_owned()
731 7ee7c0c7 Guido Trotter
732 7ee7c0c7 Guido Trotter
  def _upper_owned(self, level):
733 7ee7c0c7 Guido Trotter
    """Check that we don't own any lock at a level greater than the given one.
734 7ee7c0c7 Guido Trotter

735 7ee7c0c7 Guido Trotter
    """
736 7ee7c0c7 Guido Trotter
    # This way of checking only works if LEVELS[i] = i, which we check for in
737 7ee7c0c7 Guido Trotter
    # the test cases.
738 7ee7c0c7 Guido Trotter
    return utils.any((self._is_owned(l) for l in LEVELS[level + 1:]))
739 7ee7c0c7 Guido Trotter
740 7ee7c0c7 Guido Trotter
  def _BGL_owned(self):
741 7ee7c0c7 Guido Trotter
    """Check if the current thread owns the BGL.
742 7ee7c0c7 Guido Trotter

743 7ee7c0c7 Guido Trotter
    Both an exclusive or a shared acquisition work.
744 7ee7c0c7 Guido Trotter

745 7ee7c0c7 Guido Trotter
    """
746 7ee7c0c7 Guido Trotter
    return BGL in self.__keyring[LEVEL_CLUSTER]._list_owned()
747 7ee7c0c7 Guido Trotter
748 7ee7c0c7 Guido Trotter
  def _contains_BGL(self, level, names):
749 7ee7c0c7 Guido Trotter
    """Check if acting on the given level and set of names will change the
750 7ee7c0c7 Guido Trotter
    status of the Big Ganeti Lock.
751 7ee7c0c7 Guido Trotter

752 7ee7c0c7 Guido Trotter
    """
753 7ee7c0c7 Guido Trotter
    return level == LEVEL_CLUSTER and (names is None or BGL in names)
754 7ee7c0c7 Guido Trotter
755 7ee7c0c7 Guido Trotter
  def acquire(self, level, names, blocking=1, shared=0):
756 7ee7c0c7 Guido Trotter
    """Acquire a set of resource locks, at the same level.
757 7ee7c0c7 Guido Trotter

758 7ee7c0c7 Guido Trotter
    Args:
759 7ee7c0c7 Guido Trotter
      level: the level at which the locks shall be acquired.
760 7ee7c0c7 Guido Trotter
             It must be a memmber of LEVELS.
761 7ee7c0c7 Guido Trotter
      names: the names of the locks which shall be acquired.
762 7ee7c0c7 Guido Trotter
             (special lock names, or instance/node names)
763 7ee7c0c7 Guido Trotter
      shared: whether to acquire in shared mode. By default an exclusive lock
764 7ee7c0c7 Guido Trotter
              will be acquired.
765 7ee7c0c7 Guido Trotter
      blocking: whether to block while trying to acquire or to operate in try-lock mode.
766 7ee7c0c7 Guido Trotter
                this locking mode is not supported yet.
767 7ee7c0c7 Guido Trotter

768 7ee7c0c7 Guido Trotter
    """
769 7ee7c0c7 Guido Trotter
    assert level in LEVELS, "Invalid locking level %s" % level
770 7ee7c0c7 Guido Trotter
771 7ee7c0c7 Guido Trotter
    # Check that we are either acquiring the Big Ganeti Lock or we already own
772 7ee7c0c7 Guido Trotter
    # it. Some "legacy" opcodes need to be sure they are run non-concurrently
773 7ee7c0c7 Guido Trotter
    # so even if we've migrated we need to at least share the BGL to be
774 7ee7c0c7 Guido Trotter
    # compatible with them. Of course if we own the BGL exclusively there's no
775 7ee7c0c7 Guido Trotter
    # point in acquiring any other lock, unless perhaps we are half way through
776 7ee7c0c7 Guido Trotter
    # the migration of the current opcode.
777 7ee7c0c7 Guido Trotter
    assert (self._contains_BGL(level, names) or self._BGL_owned()), (
778 7ee7c0c7 Guido Trotter
            "You must own the Big Ganeti Lock before acquiring any other")
779 7ee7c0c7 Guido Trotter
780 7ee7c0c7 Guido Trotter
    # Check we don't own locks at the same or upper levels.
781 21a6c826 Guido Trotter
    assert not self._upper_owned(level), ("Cannot acquire locks at a level"
782 7ee7c0c7 Guido Trotter
           " while owning some at a greater one")
783 7ee7c0c7 Guido Trotter
784 7ee7c0c7 Guido Trotter
    # Acquire the locks in the set.
785 7ee7c0c7 Guido Trotter
    return self.__keyring[level].acquire(names, shared=shared,
786 7ee7c0c7 Guido Trotter
                                         blocking=blocking)
787 7ee7c0c7 Guido Trotter
788 7ee7c0c7 Guido Trotter
  def release(self, level, names=None):
789 7ee7c0c7 Guido Trotter
    """Release a set of resource locks, at the same level.
790 7ee7c0c7 Guido Trotter

791 7ee7c0c7 Guido Trotter
    You must have acquired the locks, either in shared or in exclusive mode,
792 7ee7c0c7 Guido Trotter
    before releasing them.
793 7ee7c0c7 Guido Trotter

794 7ee7c0c7 Guido Trotter
    Args:
795 7ee7c0c7 Guido Trotter
      level: the level at which the locks shall be released.
796 7ee7c0c7 Guido Trotter
             It must be a memmber of LEVELS.
797 7ee7c0c7 Guido Trotter
      names: the names of the locks which shall be released.
798 7ee7c0c7 Guido Trotter
             (defaults to all the locks acquired at that level).
799 7ee7c0c7 Guido Trotter

800 7ee7c0c7 Guido Trotter
    """
801 7ee7c0c7 Guido Trotter
    assert level in LEVELS, "Invalid locking level %s" % level
802 7ee7c0c7 Guido Trotter
    assert (not self._contains_BGL(level, names) or
803 7ee7c0c7 Guido Trotter
            not self._upper_owned(LEVEL_CLUSTER)), (
804 7ee7c0c7 Guido Trotter
            "Cannot release the Big Ganeti Lock while holding something"
805 7ee7c0c7 Guido Trotter
            " at upper levels")
806 7ee7c0c7 Guido Trotter
807 7ee7c0c7 Guido Trotter
    # Release will complain if we don't own the locks already
808 7ee7c0c7 Guido Trotter
    return self.__keyring[level].release(names)
809 7ee7c0c7 Guido Trotter
810 7ee7c0c7 Guido Trotter
  def add(self, level, names, acquired=0, shared=0):
811 7ee7c0c7 Guido Trotter
    """Add locks at the specified level.
812 7ee7c0c7 Guido Trotter

813 7ee7c0c7 Guido Trotter
    Args:
814 7ee7c0c7 Guido Trotter
      level: the level at which the locks shall be added.
815 7ee7c0c7 Guido Trotter
             It must be a memmber of LEVELS_MOD.
816 7ee7c0c7 Guido Trotter
      names: names of the locks to acquire
817 7ee7c0c7 Guido Trotter
      acquired: whether to acquire the newly added locks
818 7ee7c0c7 Guido Trotter
      shared: whether the acquisition will be shared
819 7ee7c0c7 Guido Trotter
    """
820 7ee7c0c7 Guido Trotter
    assert level in LEVELS_MOD, "Invalid or immutable level %s" % level
821 7ee7c0c7 Guido Trotter
    assert self._BGL_owned(), ("You must own the BGL before performing other"
822 7ee7c0c7 Guido Trotter
           " operations")
823 7ee7c0c7 Guido Trotter
    assert not self._upper_owned(level), ("Cannot add locks at a level"
824 7ee7c0c7 Guido Trotter
           " while owning some at a greater one")
825 7ee7c0c7 Guido Trotter
    return self.__keyring[level].add(names, acquired=acquired, shared=shared)
826 7ee7c0c7 Guido Trotter
827 7ee7c0c7 Guido Trotter
  def remove(self, level, names, blocking=1):
828 7ee7c0c7 Guido Trotter
    """Remove locks from the specified level.
829 7ee7c0c7 Guido Trotter

830 7ee7c0c7 Guido Trotter
    You must either already own the locks you are trying to remove exclusively
831 7ee7c0c7 Guido Trotter
    or not own any lock at an upper level.
832 7ee7c0c7 Guido Trotter

833 7ee7c0c7 Guido Trotter
    Args:
834 7ee7c0c7 Guido Trotter
      level: the level at which the locks shall be removed.
835 7ee7c0c7 Guido Trotter
             It must be a memmber of LEVELS_MOD.
836 7ee7c0c7 Guido Trotter
      names: the names of the locks which shall be removed.
837 7ee7c0c7 Guido Trotter
             (special lock names, or instance/node names)
838 7ee7c0c7 Guido Trotter
      blocking: whether to block while trying to operate in try-lock mode.
839 7ee7c0c7 Guido Trotter
                this locking mode is not supported yet.
840 7ee7c0c7 Guido Trotter

841 7ee7c0c7 Guido Trotter
    """
842 7ee7c0c7 Guido Trotter
    assert level in LEVELS_MOD, "Invalid or immutable level %s" % level
843 7ee7c0c7 Guido Trotter
    assert self._BGL_owned(), ("You must own the BGL before performing other"
844 7ee7c0c7 Guido Trotter
           " operations")
845 7ee7c0c7 Guido Trotter
    # Check we either own the level or don't own anything from here up.
846 7ee7c0c7 Guido Trotter
    # LockSet.remove() will check the case in which we don't own all the needed
847 7ee7c0c7 Guido Trotter
    # resources, or we have a shared ownership.
848 7ee7c0c7 Guido Trotter
    assert self._is_owned(level) or not self._upper_owned(level), (
849 7ee7c0c7 Guido Trotter
           "Cannot remove locks at a level while not owning it or"
850 7ee7c0c7 Guido Trotter
           " owning some at a greater one")
851 cdb08f44 Michael Hanselmann
    return self.__keyring[level].remove(names, blocking=blocking)