Statistics
| Branch: | Tag: | Revision:

root / lib / locking.py @ 7a0156dc

History | View | Annotate | Download (29.9 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 42a999d1 Guido Trotter
def ssynchronized(lock, shared=0):
33 42a999d1 Guido Trotter
  """Shared Synchronization decorator.
34 42a999d1 Guido Trotter

35 42a999d1 Guido Trotter
  Calls the function holding the given lock, either in exclusive or shared
36 42a999d1 Guido Trotter
  mode. It requires the passed lock to be a SharedLock (or support its
37 42a999d1 Guido Trotter
  semantics).
38 42a999d1 Guido Trotter

39 42a999d1 Guido Trotter
  """
40 42a999d1 Guido Trotter
  def wrap(fn):
41 42a999d1 Guido Trotter
    def sync_function(*args, **kwargs):
42 42a999d1 Guido Trotter
      lock.acquire(shared=shared)
43 42a999d1 Guido Trotter
      try:
44 42a999d1 Guido Trotter
        return fn(*args, **kwargs)
45 42a999d1 Guido Trotter
      finally:
46 42a999d1 Guido Trotter
        lock.release()
47 42a999d1 Guido Trotter
    return sync_function
48 42a999d1 Guido Trotter
  return wrap
49 42a999d1 Guido Trotter
50 42a999d1 Guido Trotter
51 162c1c1f Guido Trotter
class SharedLock:
52 162c1c1f Guido Trotter
  """Implements a shared lock.
53 162c1c1f Guido Trotter

54 162c1c1f Guido Trotter
  Multiple threads can acquire the lock in a shared way, calling
55 162c1c1f Guido Trotter
  acquire_shared().  In order to acquire the lock in an exclusive way threads
56 162c1c1f Guido Trotter
  can call acquire_exclusive().
57 162c1c1f Guido Trotter

58 162c1c1f Guido Trotter
  The lock prevents starvation but does not guarantee that threads will acquire
59 162c1c1f Guido Trotter
  the shared lock in the order they queued for it, just that they will
60 162c1c1f Guido Trotter
  eventually do so.
61 162c1c1f Guido Trotter

62 162c1c1f Guido Trotter
  """
63 162c1c1f Guido Trotter
  def __init__(self):
64 d6646186 Guido Trotter
    """Construct a new SharedLock"""
65 162c1c1f Guido Trotter
    # we have two conditions, c_shr and c_exc, sharing the same lock.
66 162c1c1f Guido Trotter
    self.__lock = threading.Lock()
67 162c1c1f Guido Trotter
    self.__turn_shr = threading.Condition(self.__lock)
68 162c1c1f Guido Trotter
    self.__turn_exc = threading.Condition(self.__lock)
69 162c1c1f Guido Trotter
70 162c1c1f Guido Trotter
    # current lock holders
71 162c1c1f Guido Trotter
    self.__shr = set()
72 162c1c1f Guido Trotter
    self.__exc = None
73 162c1c1f Guido Trotter
74 162c1c1f Guido Trotter
    # lock waiters
75 162c1c1f Guido Trotter
    self.__nwait_exc = 0
76 162c1c1f Guido Trotter
    self.__nwait_shr = 0
77 4d686df8 Guido Trotter
    self.__npass_shr = 0
78 162c1c1f Guido Trotter
79 a95fd5d7 Guido Trotter
    # is this lock in the deleted state?
80 a95fd5d7 Guido Trotter
    self.__deleted = False
81 a95fd5d7 Guido Trotter
82 162c1c1f Guido Trotter
  def __is_sharer(self):
83 162c1c1f Guido Trotter
    """Is the current thread sharing the lock at this time?"""
84 162c1c1f Guido Trotter
    return threading.currentThread() in self.__shr
85 162c1c1f Guido Trotter
86 162c1c1f Guido Trotter
  def __is_exclusive(self):
87 162c1c1f Guido Trotter
    """Is the current thread holding the lock exclusively at this time?"""
88 162c1c1f Guido Trotter
    return threading.currentThread() == self.__exc
89 162c1c1f Guido Trotter
90 162c1c1f Guido Trotter
  def __is_owned(self, shared=-1):
91 162c1c1f Guido Trotter
    """Is the current thread somehow owning the lock at this time?
92 162c1c1f Guido Trotter

93 162c1c1f Guido Trotter
    This is a private version of the function, which presumes you're holding
94 162c1c1f Guido Trotter
    the internal lock.
95 162c1c1f Guido Trotter

96 162c1c1f Guido Trotter
    """
97 162c1c1f Guido Trotter
    if shared < 0:
98 162c1c1f Guido Trotter
      return self.__is_sharer() or self.__is_exclusive()
99 162c1c1f Guido Trotter
    elif shared:
100 162c1c1f Guido Trotter
      return self.__is_sharer()
101 162c1c1f Guido Trotter
    else:
102 162c1c1f Guido Trotter
      return self.__is_exclusive()
103 162c1c1f Guido Trotter
104 162c1c1f Guido Trotter
  def _is_owned(self, shared=-1):
105 162c1c1f Guido Trotter
    """Is the current thread somehow owning the lock at this time?
106 162c1c1f Guido Trotter

107 c41eea6e Iustin Pop
    @param shared:
108 c41eea6e Iustin Pop
        - < 0: check for any type of ownership (default)
109 c41eea6e Iustin Pop
        - 0: check for exclusive ownership
110 c41eea6e Iustin Pop
        - > 0: check for shared ownership
111 162c1c1f Guido Trotter

112 162c1c1f Guido Trotter
    """
113 162c1c1f Guido Trotter
    self.__lock.acquire()
114 162c1c1f Guido Trotter
    try:
115 cdb08f44 Michael Hanselmann
      result = self.__is_owned(shared=shared)
116 162c1c1f Guido Trotter
    finally:
117 162c1c1f Guido Trotter
      self.__lock.release()
118 162c1c1f Guido Trotter
119 162c1c1f Guido Trotter
    return result
120 162c1c1f Guido Trotter
121 cdb08f44 Michael Hanselmann
  def __wait(self, c):
122 a95fd5d7 Guido Trotter
    """Wait on the given condition, and raise an exception if the current lock
123 a95fd5d7 Guido Trotter
    is declared deleted in the meantime.
124 a95fd5d7 Guido Trotter

125 c41eea6e Iustin Pop
    @param c: the condition to wait on
126 a95fd5d7 Guido Trotter

127 a95fd5d7 Guido Trotter
    """
128 a95fd5d7 Guido Trotter
    c.wait()
129 a95fd5d7 Guido Trotter
    if self.__deleted:
130 a95fd5d7 Guido Trotter
      raise errors.LockError('deleted lock')
131 a95fd5d7 Guido Trotter
132 a95fd5d7 Guido Trotter
  def __exclusive_acquire(self):
133 a95fd5d7 Guido Trotter
    """Acquire the lock exclusively.
134 a95fd5d7 Guido Trotter

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

139 a95fd5d7 Guido Trotter
    """
140 a95fd5d7 Guido Trotter
    self.__nwait_exc += 1
141 a95fd5d7 Guido Trotter
    try:
142 a95fd5d7 Guido Trotter
      # This is to save ourselves from a nasty race condition that could
143 a95fd5d7 Guido Trotter
      # theoretically make the sharers starve.
144 a95fd5d7 Guido Trotter
      if self.__nwait_shr > 0 or self.__nwait_exc > 1:
145 a95fd5d7 Guido Trotter
        self.__wait(self.__turn_exc)
146 a95fd5d7 Guido Trotter
147 a95fd5d7 Guido Trotter
      while len(self.__shr) > 0 or self.__exc is not None:
148 a95fd5d7 Guido Trotter
        self.__wait(self.__turn_exc)
149 a95fd5d7 Guido Trotter
150 a95fd5d7 Guido Trotter
      self.__exc = threading.currentThread()
151 a95fd5d7 Guido Trotter
    finally:
152 a95fd5d7 Guido Trotter
      self.__nwait_exc -= 1
153 a95fd5d7 Guido Trotter
154 4d686df8 Guido Trotter
    assert self.__npass_shr == 0, "SharedLock: internal fairness violation"
155 4d686df8 Guido Trotter
156 9216a9f7 Michael Hanselmann
  def __shared_acquire(self):
157 9216a9f7 Michael Hanselmann
    """Acquire the lock in shared mode
158 9216a9f7 Michael Hanselmann

159 9216a9f7 Michael Hanselmann
    This is a private function that presumes you are already holding the
160 9216a9f7 Michael Hanselmann
    internal lock.
161 9216a9f7 Michael Hanselmann

162 9216a9f7 Michael Hanselmann
    """
163 9216a9f7 Michael Hanselmann
    self.__nwait_shr += 1
164 9216a9f7 Michael Hanselmann
    try:
165 9216a9f7 Michael Hanselmann
      wait = False
166 9216a9f7 Michael Hanselmann
      # If there is an exclusive holder waiting we have to wait.
167 9216a9f7 Michael Hanselmann
      # We'll only do this once, though, when we start waiting for
168 9216a9f7 Michael Hanselmann
      # the lock. Then we'll just wait while there are no
169 9216a9f7 Michael Hanselmann
      # exclusive holders.
170 9216a9f7 Michael Hanselmann
      if self.__nwait_exc > 0:
171 9216a9f7 Michael Hanselmann
        # TODO: if !blocking...
172 9216a9f7 Michael Hanselmann
        wait = True
173 9216a9f7 Michael Hanselmann
        self.__wait(self.__turn_shr)
174 9216a9f7 Michael Hanselmann
175 9216a9f7 Michael Hanselmann
      while self.__exc is not None:
176 9216a9f7 Michael Hanselmann
        wait = True
177 9216a9f7 Michael Hanselmann
        # TODO: if !blocking...
178 9216a9f7 Michael Hanselmann
        self.__wait(self.__turn_shr)
179 9216a9f7 Michael Hanselmann
180 9216a9f7 Michael Hanselmann
      self.__shr.add(threading.currentThread())
181 9216a9f7 Michael Hanselmann
182 9216a9f7 Michael Hanselmann
      # If we were waiting note that we passed
183 9216a9f7 Michael Hanselmann
      if wait:
184 9216a9f7 Michael Hanselmann
        self.__npass_shr -= 1
185 9216a9f7 Michael Hanselmann
186 9216a9f7 Michael Hanselmann
    finally:
187 9216a9f7 Michael Hanselmann
      self.__nwait_shr -= 1
188 9216a9f7 Michael Hanselmann
189 9216a9f7 Michael Hanselmann
    assert self.__npass_shr >= 0, "Internal fairness condition weirdness"
190 9216a9f7 Michael Hanselmann
191 162c1c1f Guido Trotter
  def acquire(self, blocking=1, shared=0):
192 162c1c1f Guido Trotter
    """Acquire a shared lock.
193 162c1c1f Guido Trotter

194 c41eea6e Iustin Pop
    @param shared: whether to acquire in shared mode; by default an
195 c41eea6e Iustin Pop
        exclusive lock will be acquired
196 c41eea6e Iustin Pop
    @param blocking: whether to block while trying to acquire or to
197 c41eea6e Iustin Pop
        operate in try-lock mode (this locking mode is not supported yet)
198 162c1c1f Guido Trotter

199 162c1c1f Guido Trotter
    """
200 162c1c1f Guido Trotter
    if not blocking:
201 162c1c1f Guido Trotter
      # We don't have non-blocking mode for now
202 162c1c1f Guido Trotter
      raise NotImplementedError
203 162c1c1f Guido Trotter
204 162c1c1f Guido Trotter
    self.__lock.acquire()
205 162c1c1f Guido Trotter
    try:
206 a95fd5d7 Guido Trotter
      if self.__deleted:
207 a95fd5d7 Guido Trotter
        raise errors.LockError('deleted lock')
208 a95fd5d7 Guido Trotter
209 162c1c1f Guido Trotter
      # We cannot acquire the lock if we already have it
210 162c1c1f Guido Trotter
      assert not self.__is_owned(), "double acquire() on a non-recursive lock"
211 4d686df8 Guido Trotter
      assert self.__npass_shr >= 0, "Internal fairness condition weirdness"
212 162c1c1f Guido Trotter
213 162c1c1f Guido Trotter
      if shared:
214 9216a9f7 Michael Hanselmann
        self.__shared_acquire()
215 162c1c1f Guido Trotter
      else:
216 a95fd5d7 Guido Trotter
        # TODO: if !blocking...
217 a95fd5d7 Guido Trotter
        # (or modify __exclusive_acquire for non-blocking mode)
218 a95fd5d7 Guido Trotter
        self.__exclusive_acquire()
219 162c1c1f Guido Trotter
220 162c1c1f Guido Trotter
    finally:
221 162c1c1f Guido Trotter
      self.__lock.release()
222 162c1c1f Guido Trotter
223 162c1c1f Guido Trotter
    return True
224 162c1c1f Guido Trotter
225 162c1c1f Guido Trotter
  def release(self):
226 162c1c1f Guido Trotter
    """Release a Shared Lock.
227 162c1c1f Guido Trotter

228 162c1c1f Guido Trotter
    You must have acquired the lock, either in shared or in exclusive mode,
229 162c1c1f Guido Trotter
    before calling this function.
230 162c1c1f Guido Trotter

231 162c1c1f Guido Trotter
    """
232 162c1c1f Guido Trotter
    self.__lock.acquire()
233 162c1c1f Guido Trotter
    try:
234 4d686df8 Guido Trotter
      assert self.__npass_shr >= 0, "Internal fairness condition weirdness"
235 162c1c1f Guido Trotter
      # Autodetect release type
236 162c1c1f Guido Trotter
      if self.__is_exclusive():
237 162c1c1f Guido Trotter
        self.__exc = None
238 162c1c1f Guido Trotter
239 162c1c1f Guido Trotter
        # An exclusive holder has just had the lock, time to put it in shared
240 162c1c1f Guido Trotter
        # mode if there are shared holders waiting. Otherwise wake up the next
241 162c1c1f Guido Trotter
        # exclusive holder.
242 162c1c1f Guido Trotter
        if self.__nwait_shr > 0:
243 4d686df8 Guido Trotter
          # Make sure at least the ones which were blocked pass.
244 4d686df8 Guido Trotter
          self.__npass_shr = self.__nwait_shr
245 162c1c1f Guido Trotter
          self.__turn_shr.notifyAll()
246 162c1c1f Guido Trotter
        elif self.__nwait_exc > 0:
247 c5cc3403 Guido Trotter
          self.__turn_exc.notify()
248 162c1c1f Guido Trotter
249 162c1c1f Guido Trotter
      elif self.__is_sharer():
250 162c1c1f Guido Trotter
        self.__shr.remove(threading.currentThread())
251 162c1c1f Guido Trotter
252 4d686df8 Guido Trotter
        # If there are shared holders waiting (and not just scheduled to pass)
253 4d686df8 Guido Trotter
        # there *must* be an exclusive holder waiting as well; otherwise what
254 4d686df8 Guido Trotter
        # were they waiting for?
255 f12eadb3 Iustin Pop
        assert (self.__nwait_exc > 0 or
256 f12eadb3 Iustin Pop
                self.__npass_shr == self.__nwait_shr), \
257 f12eadb3 Iustin Pop
                "Lock sharers waiting while no exclusive is queueing"
258 4d686df8 Guido Trotter
259 4d686df8 Guido Trotter
        # If there are no more shared holders either in or scheduled to pass,
260 4d686df8 Guido Trotter
        # and some exclusive holders are waiting let's wake one up.
261 4d686df8 Guido Trotter
        if (len(self.__shr) == 0 and
262 4d686df8 Guido Trotter
            self.__nwait_exc > 0 and
263 4d686df8 Guido Trotter
            not self.__npass_shr > 0):
264 162c1c1f Guido Trotter
          self.__turn_exc.notify()
265 162c1c1f Guido Trotter
266 162c1c1f Guido Trotter
      else:
267 162c1c1f Guido Trotter
        assert False, "Cannot release non-owned lock"
268 162c1c1f Guido Trotter
269 162c1c1f Guido Trotter
    finally:
270 162c1c1f Guido Trotter
      self.__lock.release()
271 162c1c1f Guido Trotter
272 a95fd5d7 Guido Trotter
  def delete(self, blocking=1):
273 a95fd5d7 Guido Trotter
    """Delete a Shared Lock.
274 a95fd5d7 Guido Trotter

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

279 c41eea6e Iustin Pop
    @param blocking: whether to block while trying to acquire or to
280 c41eea6e Iustin Pop
        operate in try-lock mode.  this locking mode is not supported
281 c41eea6e Iustin Pop
        yet unless you are already holding exclusively the lock.
282 a95fd5d7 Guido Trotter

283 a95fd5d7 Guido Trotter
    """
284 a95fd5d7 Guido Trotter
    self.__lock.acquire()
285 a95fd5d7 Guido Trotter
    try:
286 a95fd5d7 Guido Trotter
      assert not self.__is_sharer(), "cannot delete() a lock while sharing it"
287 a95fd5d7 Guido Trotter
288 a95fd5d7 Guido Trotter
      if self.__deleted:
289 a95fd5d7 Guido Trotter
        raise errors.LockError('deleted lock')
290 a95fd5d7 Guido Trotter
291 a95fd5d7 Guido Trotter
      if not self.__is_exclusive():
292 a95fd5d7 Guido Trotter
        if not blocking:
293 a95fd5d7 Guido Trotter
          # We don't have non-blocking mode for now
294 a95fd5d7 Guido Trotter
          raise NotImplementedError
295 a95fd5d7 Guido Trotter
        self.__exclusive_acquire()
296 a95fd5d7 Guido Trotter
297 a95fd5d7 Guido Trotter
      self.__deleted = True
298 a95fd5d7 Guido Trotter
      self.__exc = None
299 a95fd5d7 Guido Trotter
      # Wake up everybody, they will fail acquiring the lock and
300 a95fd5d7 Guido Trotter
      # raise an exception instead.
301 a95fd5d7 Guido Trotter
      self.__turn_exc.notifyAll()
302 a95fd5d7 Guido Trotter
      self.__turn_shr.notifyAll()
303 a95fd5d7 Guido Trotter
304 a95fd5d7 Guido Trotter
    finally:
305 a95fd5d7 Guido Trotter
      self.__lock.release()
306 a95fd5d7 Guido Trotter
307 aaae9bc0 Guido Trotter
308 f12eadb3 Iustin Pop
# Whenever we want to acquire a full LockSet we pass None as the value
309 5bbd3f7f Michael Hanselmann
# to acquire.  Hide this behind this nicely named constant.
310 e310b019 Guido Trotter
ALL_SET = None
311 e310b019 Guido Trotter
312 e310b019 Guido Trotter
313 aaae9bc0 Guido Trotter
class LockSet:
314 aaae9bc0 Guido Trotter
  """Implements a set of locks.
315 aaae9bc0 Guido Trotter

316 aaae9bc0 Guido Trotter
  This abstraction implements a set of shared locks for the same resource type,
317 aaae9bc0 Guido Trotter
  distinguished by name. The user can lock a subset of the resources and the
318 aaae9bc0 Guido Trotter
  LockSet will take care of acquiring the locks always in the same order, thus
319 aaae9bc0 Guido Trotter
  preventing deadlock.
320 aaae9bc0 Guido Trotter

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

323 aaae9bc0 Guido Trotter
  """
324 aaae9bc0 Guido Trotter
  def __init__(self, members=None):
325 aaae9bc0 Guido Trotter
    """Constructs a new LockSet.
326 aaae9bc0 Guido Trotter

327 c41eea6e Iustin Pop
    @param members: initial members of the set
328 aaae9bc0 Guido Trotter

329 aaae9bc0 Guido Trotter
    """
330 aaae9bc0 Guido Trotter
    # Used internally to guarantee coherency.
331 aaae9bc0 Guido Trotter
    self.__lock = SharedLock()
332 aaae9bc0 Guido Trotter
333 aaae9bc0 Guido Trotter
    # The lockdict indexes the relationship name -> lock
334 aaae9bc0 Guido Trotter
    # The order-of-locking is implied by the alphabetical order of names
335 aaae9bc0 Guido Trotter
    self.__lockdict = {}
336 aaae9bc0 Guido Trotter
337 aaae9bc0 Guido Trotter
    if members is not None:
338 aaae9bc0 Guido Trotter
      for name in members:
339 aaae9bc0 Guido Trotter
        self.__lockdict[name] = SharedLock()
340 aaae9bc0 Guido Trotter
341 aaae9bc0 Guido Trotter
    # The owner dict contains the set of locks each thread owns. For
342 aaae9bc0 Guido Trotter
    # performance each thread can access its own key without a global lock on
343 aaae9bc0 Guido Trotter
    # this structure. It is paramount though that *no* other type of access is
344 aaae9bc0 Guido Trotter
    # done to this structure (eg. no looping over its keys). *_owner helper
345 aaae9bc0 Guido Trotter
    # function are defined to guarantee access is correct, but in general never
346 aaae9bc0 Guido Trotter
    # do anything different than __owners[threading.currentThread()], or there
347 aaae9bc0 Guido Trotter
    # will be trouble.
348 aaae9bc0 Guido Trotter
    self.__owners = {}
349 aaae9bc0 Guido Trotter
350 aaae9bc0 Guido Trotter
  def _is_owned(self):
351 aaae9bc0 Guido Trotter
    """Is the current thread a current level owner?"""
352 aaae9bc0 Guido Trotter
    return threading.currentThread() in self.__owners
353 aaae9bc0 Guido Trotter
354 b2dabfd6 Guido Trotter
  def _add_owned(self, name=None):
355 aaae9bc0 Guido Trotter
    """Note the current thread owns the given lock"""
356 b2dabfd6 Guido Trotter
    if name is None:
357 b2dabfd6 Guido Trotter
      if not self._is_owned():
358 b2dabfd6 Guido Trotter
        self.__owners[threading.currentThread()] = set()
359 aaae9bc0 Guido Trotter
    else:
360 b2dabfd6 Guido Trotter
      if self._is_owned():
361 b2dabfd6 Guido Trotter
        self.__owners[threading.currentThread()].add(name)
362 b2dabfd6 Guido Trotter
      else:
363 b2dabfd6 Guido Trotter
        self.__owners[threading.currentThread()] = set([name])
364 b2dabfd6 Guido Trotter
365 b2dabfd6 Guido Trotter
  def _del_owned(self, name=None):
366 aaae9bc0 Guido Trotter
    """Note the current thread owns the given lock"""
367 aaae9bc0 Guido Trotter
368 b2dabfd6 Guido Trotter
    if name is not None:
369 b2dabfd6 Guido Trotter
      self.__owners[threading.currentThread()].remove(name)
370 b2dabfd6 Guido Trotter
371 b2dabfd6 Guido Trotter
    # Only remove the key if we don't hold the set-lock as well
372 b2dabfd6 Guido Trotter
    if (not self.__lock._is_owned() and
373 b2dabfd6 Guido Trotter
        not self.__owners[threading.currentThread()]):
374 aaae9bc0 Guido Trotter
      del self.__owners[threading.currentThread()]
375 aaae9bc0 Guido Trotter
376 aaae9bc0 Guido Trotter
  def _list_owned(self):
377 aaae9bc0 Guido Trotter
    """Get the set of resource names owned by the current thread"""
378 aaae9bc0 Guido Trotter
    if self._is_owned():
379 aaae9bc0 Guido Trotter
      return self.__owners[threading.currentThread()].copy()
380 aaae9bc0 Guido Trotter
    else:
381 aaae9bc0 Guido Trotter
      return set()
382 aaae9bc0 Guido Trotter
383 aaae9bc0 Guido Trotter
  def __names(self):
384 aaae9bc0 Guido Trotter
    """Return the current set of names.
385 aaae9bc0 Guido Trotter

386 aaae9bc0 Guido Trotter
    Only call this function while holding __lock and don't iterate on the
387 aaae9bc0 Guido Trotter
    result after releasing the lock.
388 aaae9bc0 Guido Trotter

389 aaae9bc0 Guido Trotter
    """
390 0cf257c5 Guido Trotter
    return self.__lockdict.keys()
391 aaae9bc0 Guido Trotter
392 aaae9bc0 Guido Trotter
  def _names(self):
393 aaae9bc0 Guido Trotter
    """Return a copy of the current set of elements.
394 aaae9bc0 Guido Trotter

395 aaae9bc0 Guido Trotter
    Used only for debugging purposes.
396 cdb08f44 Michael Hanselmann

397 aaae9bc0 Guido Trotter
    """
398 d4803c24 Guido Trotter
    # If we don't already own the set-level lock acquired
399 d4803c24 Guido Trotter
    # we'll get it and note we need to release it later.
400 d4803c24 Guido Trotter
    release_lock = False
401 d4803c24 Guido Trotter
    if not self.__lock._is_owned():
402 d4803c24 Guido Trotter
      release_lock = True
403 d4803c24 Guido Trotter
      self.__lock.acquire(shared=1)
404 aaae9bc0 Guido Trotter
    try:
405 aaae9bc0 Guido Trotter
      result = self.__names()
406 aaae9bc0 Guido Trotter
    finally:
407 d4803c24 Guido Trotter
      if release_lock:
408 d4803c24 Guido Trotter
        self.__lock.release()
409 0cf257c5 Guido Trotter
    return set(result)
410 aaae9bc0 Guido Trotter
411 aaae9bc0 Guido Trotter
  def acquire(self, names, blocking=1, shared=0):
412 aaae9bc0 Guido Trotter
    """Acquire a set of resource locks.
413 aaae9bc0 Guido Trotter

414 c41eea6e Iustin Pop
    @param names: the names of the locks which shall be acquired
415 c41eea6e Iustin Pop
        (special lock names, or instance/node names)
416 c41eea6e Iustin Pop
    @param shared: whether to acquire in shared mode; by default an
417 c41eea6e Iustin Pop
        exclusive lock will be acquired
418 c41eea6e Iustin Pop
    @param blocking: whether to block while trying to acquire or to
419 c41eea6e Iustin Pop
        operate in try-lock mode (this locking mode is not supported yet)
420 aaae9bc0 Guido Trotter

421 c41eea6e Iustin Pop
    @return: True when all the locks are successfully acquired
422 aaae9bc0 Guido Trotter

423 c41eea6e Iustin Pop
    @raise errors.LockError: when any lock we try to acquire has
424 c41eea6e Iustin Pop
        been deleted before we succeed. In this case none of the
425 c41eea6e Iustin Pop
        locks requested will be acquired.
426 aaae9bc0 Guido Trotter

427 aaae9bc0 Guido Trotter
    """
428 aaae9bc0 Guido Trotter
    if not blocking:
429 aaae9bc0 Guido Trotter
      # We don't have non-blocking mode for now
430 aaae9bc0 Guido Trotter
      raise NotImplementedError
431 aaae9bc0 Guido Trotter
432 aaae9bc0 Guido Trotter
    # Check we don't already own locks at this level
433 aaae9bc0 Guido Trotter
    assert not self._is_owned(), "Cannot acquire locks in the same set twice"
434 aaae9bc0 Guido Trotter
435 3b7ed473 Guido Trotter
    if names is None:
436 3b7ed473 Guido Trotter
      # If no names are given acquire the whole set by not letting new names
437 3b7ed473 Guido Trotter
      # being added before we release, and getting the current list of names.
438 3b7ed473 Guido Trotter
      # Some of them may then be deleted later, but we'll cope with this.
439 3b7ed473 Guido Trotter
      #
440 3b7ed473 Guido Trotter
      # We'd like to acquire this lock in a shared way, as it's nice if
441 3b7ed473 Guido Trotter
      # everybody else can use the instances at the same time. If are acquiring
442 3b7ed473 Guido Trotter
      # them exclusively though they won't be able to do this anyway, though,
443 3b7ed473 Guido Trotter
      # so we'll get the list lock exclusively as well in order to be able to
444 3b7ed473 Guido Trotter
      # do add() on the set while owning it.
445 3b7ed473 Guido Trotter
      self.__lock.acquire(shared=shared)
446 b2dabfd6 Guido Trotter
      try:
447 b2dabfd6 Guido Trotter
        # note we own the set-lock
448 b2dabfd6 Guido Trotter
        self._add_owned()
449 b2dabfd6 Guido Trotter
        names = self.__names()
450 b2dabfd6 Guido Trotter
      except:
451 b2dabfd6 Guido Trotter
        # We shouldn't have problems adding the lock to the owners list, but
452 b2dabfd6 Guido Trotter
        # if we did we'll try to release this lock and re-raise exception.
453 b2dabfd6 Guido Trotter
        # Of course something is going to be really wrong, after this.
454 b2dabfd6 Guido Trotter
        self.__lock.release()
455 b2dabfd6 Guido Trotter
        raise
456 3b7ed473 Guido Trotter
457 806e20fd Guido Trotter
    try:
458 806e20fd Guido Trotter
      # Support passing in a single resource to acquire rather than many
459 806e20fd Guido Trotter
      if isinstance(names, basestring):
460 806e20fd Guido Trotter
        names = [names]
461 806e20fd Guido Trotter
      else:
462 2a21bc88 Iustin Pop
        names = sorted(names)
463 806e20fd Guido Trotter
464 806e20fd Guido Trotter
      acquire_list = []
465 806e20fd Guido Trotter
      # First we look the locks up on __lockdict. We have no way of being sure
466 806e20fd Guido Trotter
      # they will still be there after, but this makes it a lot faster should
467 806e20fd Guido Trotter
      # just one of them be the already wrong
468 34ca3914 Guido Trotter
      for lname in utils.UniqueSequence(names):
469 806e20fd Guido Trotter
        try:
470 4e07ec8c Guido Trotter
          lock = self.__lockdict[lname] # raises KeyError if lock is not there
471 806e20fd Guido Trotter
          acquire_list.append((lname, lock))
472 806e20fd Guido Trotter
        except (KeyError):
473 3b7ed473 Guido Trotter
          if self.__lock._is_owned():
474 f12eadb3 Iustin Pop
            # We are acquiring all the set, it doesn't matter if this
475 f12eadb3 Iustin Pop
            # particular element is not there anymore.
476 3b7ed473 Guido Trotter
            continue
477 3b7ed473 Guido Trotter
          else:
478 3b7ed473 Guido Trotter
            raise errors.LockError('non-existing lock in set (%s)' % lname)
479 806e20fd Guido Trotter
480 806e20fd Guido Trotter
      # This will hold the locknames we effectively acquired.
481 806e20fd Guido Trotter
      acquired = set()
482 806e20fd Guido Trotter
      # Now acquire_list contains a sorted list of resources and locks we want.
483 806e20fd Guido Trotter
      # In order to get them we loop on this (private) list and acquire() them.
484 806e20fd Guido Trotter
      # We gave no real guarantee they will still exist till this is done but
485 806e20fd Guido Trotter
      # .acquire() itself is safe and will alert us if the lock gets deleted.
486 806e20fd Guido Trotter
      for (lname, lock) in acquire_list:
487 aaae9bc0 Guido Trotter
        try:
488 806e20fd Guido Trotter
          lock.acquire(shared=shared) # raises LockError if the lock is deleted
489 ea3f80bf Guido Trotter
          # now the lock cannot be deleted, we have it!
490 b2dabfd6 Guido Trotter
          self._add_owned(name=lname)
491 ea3f80bf Guido Trotter
          acquired.add(lname)
492 806e20fd Guido Trotter
        except (errors.LockError):
493 3b7ed473 Guido Trotter
          if self.__lock._is_owned():
494 f12eadb3 Iustin Pop
            # We are acquiring all the set, it doesn't matter if this
495 f12eadb3 Iustin Pop
            # particular element is not there anymore.
496 3b7ed473 Guido Trotter
            continue
497 3b7ed473 Guido Trotter
          else:
498 3b7ed473 Guido Trotter
            name_fail = lname
499 3b7ed473 Guido Trotter
            for lname in self._list_owned():
500 3b7ed473 Guido Trotter
              self.__lockdict[lname].release()
501 b2dabfd6 Guido Trotter
              self._del_owned(name=lname)
502 3b7ed473 Guido Trotter
            raise errors.LockError('non-existing lock in set (%s)' % name_fail)
503 ea3f80bf Guido Trotter
        except:
504 ea3f80bf Guido Trotter
          # We shouldn't have problems adding the lock to the owners list, but
505 ea3f80bf Guido Trotter
          # if we did we'll try to release this lock and re-raise exception.
506 ea3f80bf Guido Trotter
          # Of course something is going to be really wrong, after this.
507 ea3f80bf Guido Trotter
          if lock._is_owned():
508 ea3f80bf Guido Trotter
            lock.release()
509 470ce2ee Michael Hanselmann
          raise
510 806e20fd Guido Trotter
511 806e20fd Guido Trotter
    except:
512 3b7ed473 Guido Trotter
      # If something went wrong and we had the set-lock let's release it...
513 3b7ed473 Guido Trotter
      if self.__lock._is_owned():
514 3b7ed473 Guido Trotter
        self.__lock.release()
515 806e20fd Guido Trotter
      raise
516 aaae9bc0 Guido Trotter
517 0cc00929 Guido Trotter
    return acquired
518 aaae9bc0 Guido Trotter
519 aaae9bc0 Guido Trotter
  def release(self, names=None):
520 aaae9bc0 Guido Trotter
    """Release a set of resource locks, at the same level.
521 aaae9bc0 Guido Trotter

522 aaae9bc0 Guido Trotter
    You must have acquired the locks, either in shared or in exclusive mode,
523 aaae9bc0 Guido Trotter
    before releasing them.
524 aaae9bc0 Guido Trotter

525 c41eea6e Iustin Pop
    @param names: the names of the locks which shall be released
526 c41eea6e Iustin Pop
        (defaults to all the locks acquired at that level).
527 aaae9bc0 Guido Trotter

528 aaae9bc0 Guido Trotter
    """
529 aaae9bc0 Guido Trotter
    assert self._is_owned(), "release() on lock set while not owner"
530 aaae9bc0 Guido Trotter
531 aaae9bc0 Guido Trotter
    # Support passing in a single resource to release rather than many
532 aaae9bc0 Guido Trotter
    if isinstance(names, basestring):
533 aaae9bc0 Guido Trotter
      names = [names]
534 aaae9bc0 Guido Trotter
535 aaae9bc0 Guido Trotter
    if names is None:
536 aaae9bc0 Guido Trotter
      names = self._list_owned()
537 aaae9bc0 Guido Trotter
    else:
538 aaae9bc0 Guido Trotter
      names = set(names)
539 aaae9bc0 Guido Trotter
      assert self._list_owned().issuperset(names), (
540 aaae9bc0 Guido Trotter
               "release() on unheld resources %s" %
541 aaae9bc0 Guido Trotter
               names.difference(self._list_owned()))
542 aaae9bc0 Guido Trotter
543 3b7ed473 Guido Trotter
    # First of all let's release the "all elements" lock, if set.
544 3b7ed473 Guido Trotter
    # After this 'add' can work again
545 3b7ed473 Guido Trotter
    if self.__lock._is_owned():
546 3b7ed473 Guido Trotter
      self.__lock.release()
547 b2dabfd6 Guido Trotter
      self._del_owned()
548 3b7ed473 Guido Trotter
549 aaae9bc0 Guido Trotter
    for lockname in names:
550 aaae9bc0 Guido Trotter
      # If we are sure the lock doesn't leave __lockdict without being
551 aaae9bc0 Guido Trotter
      # exclusively held we can do this...
552 aaae9bc0 Guido Trotter
      self.__lockdict[lockname].release()
553 b2dabfd6 Guido Trotter
      self._del_owned(name=lockname)
554 aaae9bc0 Guido Trotter
555 aaae9bc0 Guido Trotter
  def add(self, names, acquired=0, shared=0):
556 aaae9bc0 Guido Trotter
    """Add a new set of elements to the set
557 aaae9bc0 Guido Trotter

558 c41eea6e Iustin Pop
    @param names: names of the new elements to add
559 c41eea6e Iustin Pop
    @param acquired: pre-acquire the new resource?
560 c41eea6e Iustin Pop
    @param shared: is the pre-acquisition shared?
561 aaae9bc0 Guido Trotter

562 aaae9bc0 Guido Trotter
    """
563 d2aff862 Guido Trotter
    # Check we don't already own locks at this level
564 d2aff862 Guido Trotter
    assert not self._is_owned() or self.__lock._is_owned(shared=0), \
565 d2aff862 Guido Trotter
      "Cannot add locks if the set is only partially owned, or shared"
566 3b7ed473 Guido Trotter
567 aaae9bc0 Guido Trotter
    # Support passing in a single resource to add rather than many
568 aaae9bc0 Guido Trotter
    if isinstance(names, basestring):
569 aaae9bc0 Guido Trotter
      names = [names]
570 aaae9bc0 Guido Trotter
571 ab62526c Guido Trotter
    # If we don't already own the set-level lock acquired in an exclusive way
572 3b7ed473 Guido Trotter
    # we'll get it and note we need to release it later.
573 3b7ed473 Guido Trotter
    release_lock = False
574 3b7ed473 Guido Trotter
    if not self.__lock._is_owned():
575 3b7ed473 Guido Trotter
      release_lock = True
576 3b7ed473 Guido Trotter
      self.__lock.acquire()
577 3b7ed473 Guido Trotter
578 aaae9bc0 Guido Trotter
    try:
579 0cf257c5 Guido Trotter
      invalid_names = set(self.__names()).intersection(names)
580 aaae9bc0 Guido Trotter
      if invalid_names:
581 aaae9bc0 Guido Trotter
        # This must be an explicit raise, not an assert, because assert is
582 aaae9bc0 Guido Trotter
        # turned off when using optimization, and this can happen because of
583 aaae9bc0 Guido Trotter
        # concurrency even if the user doesn't want it.
584 aaae9bc0 Guido Trotter
        raise errors.LockError("duplicate add() (%s)" % invalid_names)
585 aaae9bc0 Guido Trotter
586 aaae9bc0 Guido Trotter
      for lockname in names:
587 aaae9bc0 Guido Trotter
        lock = SharedLock()
588 aaae9bc0 Guido Trotter
589 aaae9bc0 Guido Trotter
        if acquired:
590 aaae9bc0 Guido Trotter
          lock.acquire(shared=shared)
591 aaae9bc0 Guido Trotter
          # now the lock cannot be deleted, we have it!
592 aaae9bc0 Guido Trotter
          try:
593 b2dabfd6 Guido Trotter
            self._add_owned(name=lockname)
594 aaae9bc0 Guido Trotter
          except:
595 aaae9bc0 Guido Trotter
            # We shouldn't have problems adding the lock to the owners list,
596 aaae9bc0 Guido Trotter
            # but if we did we'll try to release this lock and re-raise
597 aaae9bc0 Guido Trotter
            # exception.  Of course something is going to be really wrong,
598 aaae9bc0 Guido Trotter
            # after this.  On the other hand the lock hasn't been added to the
599 aaae9bc0 Guido Trotter
            # __lockdict yet so no other threads should be pending on it. This
600 aaae9bc0 Guido Trotter
            # release is just a safety measure.
601 aaae9bc0 Guido Trotter
            lock.release()
602 aaae9bc0 Guido Trotter
            raise
603 aaae9bc0 Guido Trotter
604 aaae9bc0 Guido Trotter
        self.__lockdict[lockname] = lock
605 aaae9bc0 Guido Trotter
606 aaae9bc0 Guido Trotter
    finally:
607 3b7ed473 Guido Trotter
      # Only release __lock if we were not holding it previously.
608 3b7ed473 Guido Trotter
      if release_lock:
609 3b7ed473 Guido Trotter
        self.__lock.release()
610 aaae9bc0 Guido Trotter
611 aaae9bc0 Guido Trotter
    return True
612 aaae9bc0 Guido Trotter
613 aaae9bc0 Guido Trotter
  def remove(self, names, blocking=1):
614 aaae9bc0 Guido Trotter
    """Remove elements from the lock set.
615 aaae9bc0 Guido Trotter

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

619 c41eea6e Iustin Pop
    @param names: names of the resource to remove.
620 c41eea6e Iustin Pop
    @param blocking: whether to block while trying to acquire or to
621 c41eea6e Iustin Pop
        operate in try-lock mode (this locking mode is not supported
622 c41eea6e Iustin Pop
        yet unless you are already holding exclusively the locks)
623 aaae9bc0 Guido Trotter

624 c41eea6e Iustin Pop
    @return:: a list of locks which we removed; the list is always
625 c41eea6e Iustin Pop
        equal to the names list if we were holding all the locks
626 c41eea6e Iustin Pop
        exclusively
627 aaae9bc0 Guido Trotter

628 aaae9bc0 Guido Trotter
    """
629 aaae9bc0 Guido Trotter
    if not blocking and not self._is_owned():
630 aaae9bc0 Guido Trotter
      # We don't have non-blocking mode for now
631 aaae9bc0 Guido Trotter
      raise NotImplementedError
632 aaae9bc0 Guido Trotter
633 aaae9bc0 Guido Trotter
    # Support passing in a single resource to remove rather than many
634 aaae9bc0 Guido Trotter
    if isinstance(names, basestring):
635 aaae9bc0 Guido Trotter
      names = [names]
636 aaae9bc0 Guido Trotter
637 aaae9bc0 Guido Trotter
    # If we own any subset of this lock it must be a superset of what we want
638 aaae9bc0 Guido Trotter
    # to delete. The ownership must also be exclusive, but that will be checked
639 aaae9bc0 Guido Trotter
    # by the lock itself.
640 aaae9bc0 Guido Trotter
    assert not self._is_owned() or self._list_owned().issuperset(names), (
641 aaae9bc0 Guido Trotter
      "remove() on acquired lockset while not owning all elements")
642 aaae9bc0 Guido Trotter
643 3f404fc5 Guido Trotter
    removed = []
644 aaae9bc0 Guido Trotter
645 aaae9bc0 Guido Trotter
    for lname in names:
646 aaae9bc0 Guido Trotter
      # Calling delete() acquires the lock exclusively if we don't already own
647 aaae9bc0 Guido Trotter
      # it, and causes all pending and subsequent lock acquires to fail. It's
648 aaae9bc0 Guido Trotter
      # fine to call it out of order because delete() also implies release(),
649 aaae9bc0 Guido Trotter
      # and the assertion above guarantees that if we either already hold
650 aaae9bc0 Guido Trotter
      # everything we want to delete, or we hold none.
651 aaae9bc0 Guido Trotter
      try:
652 aaae9bc0 Guido Trotter
        self.__lockdict[lname].delete()
653 3f404fc5 Guido Trotter
        removed.append(lname)
654 aaae9bc0 Guido Trotter
      except (KeyError, errors.LockError):
655 aaae9bc0 Guido Trotter
        # This cannot happen if we were already holding it, verify:
656 aaae9bc0 Guido Trotter
        assert not self._is_owned(), "remove failed while holding lockset"
657 aaae9bc0 Guido Trotter
      else:
658 aaae9bc0 Guido Trotter
        # If no LockError was raised we are the ones who deleted the lock.
659 aaae9bc0 Guido Trotter
        # This means we can safely remove it from lockdict, as any further or
660 aaae9bc0 Guido Trotter
        # pending delete() or acquire() will fail (and nobody can have the lock
661 aaae9bc0 Guido Trotter
        # since before our call to delete()).
662 aaae9bc0 Guido Trotter
        #
663 aaae9bc0 Guido Trotter
        # This is done in an else clause because if the exception was thrown
664 aaae9bc0 Guido Trotter
        # it's the job of the one who actually deleted it.
665 aaae9bc0 Guido Trotter
        del self.__lockdict[lname]
666 aaae9bc0 Guido Trotter
        # And let's remove it from our private list if we owned it.
667 aaae9bc0 Guido Trotter
        if self._is_owned():
668 b2dabfd6 Guido Trotter
          self._del_owned(name=lname)
669 aaae9bc0 Guido Trotter
670 3f404fc5 Guido Trotter
    return removed
671 aaae9bc0 Guido Trotter
672 7ee7c0c7 Guido Trotter
673 7ee7c0c7 Guido Trotter
# Locking levels, must be acquired in increasing order.
674 7ee7c0c7 Guido Trotter
# Current rules are:
675 7ee7c0c7 Guido Trotter
#   - at level LEVEL_CLUSTER resides the Big Ganeti Lock (BGL) which must be
676 7ee7c0c7 Guido Trotter
#   acquired before performing any operation, either in shared or in exclusive
677 7ee7c0c7 Guido Trotter
#   mode. acquiring the BGL in exclusive mode is discouraged and should be
678 7ee7c0c7 Guido Trotter
#   avoided.
679 7ee7c0c7 Guido Trotter
#   - at levels LEVEL_NODE and LEVEL_INSTANCE reside node and instance locks.
680 7ee7c0c7 Guido Trotter
#   If you need more than one node, or more than one instance, acquire them at
681 7ee7c0c7 Guido Trotter
#   the same time.
682 7ee7c0c7 Guido Trotter
LEVEL_CLUSTER = 0
683 04e1bfaf Guido Trotter
LEVEL_INSTANCE = 1
684 04e1bfaf Guido Trotter
LEVEL_NODE = 2
685 7ee7c0c7 Guido Trotter
686 7ee7c0c7 Guido Trotter
LEVELS = [LEVEL_CLUSTER,
687 04e1bfaf Guido Trotter
          LEVEL_INSTANCE,
688 04e1bfaf Guido Trotter
          LEVEL_NODE]
689 7ee7c0c7 Guido Trotter
690 7ee7c0c7 Guido Trotter
# Lock levels which are modifiable
691 7ee7c0c7 Guido Trotter
LEVELS_MOD = [LEVEL_NODE, LEVEL_INSTANCE]
692 7ee7c0c7 Guido Trotter
693 ea205dbc Michael Hanselmann
LEVEL_NAMES = {
694 ea205dbc Michael Hanselmann
  LEVEL_CLUSTER: "cluster",
695 ea205dbc Michael Hanselmann
  LEVEL_INSTANCE: "instance",
696 ea205dbc Michael Hanselmann
  LEVEL_NODE: "node",
697 ea205dbc Michael Hanselmann
  }
698 ea205dbc Michael Hanselmann
699 08a6c581 Guido Trotter
# Constant for the big ganeti lock
700 7ee7c0c7 Guido Trotter
BGL = 'BGL'
701 7ee7c0c7 Guido Trotter
702 7ee7c0c7 Guido Trotter
703 7ee7c0c7 Guido Trotter
class GanetiLockManager:
704 7ee7c0c7 Guido Trotter
  """The Ganeti Locking Library
705 7ee7c0c7 Guido Trotter

706 5bbd3f7f Michael Hanselmann
  The purpose of this small library is to manage locking for ganeti clusters
707 7ee7c0c7 Guido Trotter
  in a central place, while at the same time doing dynamic checks against
708 7ee7c0c7 Guido Trotter
  possible deadlocks. It will also make it easier to transition to a different
709 7ee7c0c7 Guido Trotter
  lock type should we migrate away from python threads.
710 7ee7c0c7 Guido Trotter

711 7ee7c0c7 Guido Trotter
  """
712 7ee7c0c7 Guido Trotter
  _instance = None
713 7ee7c0c7 Guido Trotter
714 7ee7c0c7 Guido Trotter
  def __init__(self, nodes=None, instances=None):
715 7ee7c0c7 Guido Trotter
    """Constructs a new GanetiLockManager object.
716 7ee7c0c7 Guido Trotter

717 4e07ec8c Guido Trotter
    There should be only a GanetiLockManager object at any time, so this
718 4e07ec8c Guido Trotter
    function raises an error if this is not the case.
719 7ee7c0c7 Guido Trotter

720 c41eea6e Iustin Pop
    @param nodes: list of node names
721 c41eea6e Iustin Pop
    @param instances: list of instance names
722 7ee7c0c7 Guido Trotter

723 7ee7c0c7 Guido Trotter
    """
724 c41eea6e Iustin Pop
    assert self.__class__._instance is None, \
725 c41eea6e Iustin Pop
           "double GanetiLockManager instance"
726 c41eea6e Iustin Pop
727 7ee7c0c7 Guido Trotter
    self.__class__._instance = self
728 7ee7c0c7 Guido Trotter
729 7ee7c0c7 Guido Trotter
    # The keyring contains all the locks, at their level and in the correct
730 7ee7c0c7 Guido Trotter
    # locking order.
731 7ee7c0c7 Guido Trotter
    self.__keyring = {
732 7ee7c0c7 Guido Trotter
      LEVEL_CLUSTER: LockSet([BGL]),
733 7ee7c0c7 Guido Trotter
      LEVEL_NODE: LockSet(nodes),
734 7ee7c0c7 Guido Trotter
      LEVEL_INSTANCE: LockSet(instances),
735 7ee7c0c7 Guido Trotter
    }
736 7ee7c0c7 Guido Trotter
737 7ee7c0c7 Guido Trotter
  def _names(self, level):
738 7ee7c0c7 Guido Trotter
    """List the lock names at the given level.
739 7ee7c0c7 Guido Trotter

740 c41eea6e Iustin Pop
    This can be used for debugging/testing purposes.
741 c41eea6e Iustin Pop

742 c41eea6e Iustin Pop
    @param level: the level whose list of locks to get
743 7ee7c0c7 Guido Trotter

744 7ee7c0c7 Guido Trotter
    """
745 7ee7c0c7 Guido Trotter
    assert level in LEVELS, "Invalid locking level %s" % level
746 7ee7c0c7 Guido Trotter
    return self.__keyring[level]._names()
747 7ee7c0c7 Guido Trotter
748 7ee7c0c7 Guido Trotter
  def _is_owned(self, level):
749 7ee7c0c7 Guido Trotter
    """Check whether we are owning locks at the given level
750 7ee7c0c7 Guido Trotter

751 7ee7c0c7 Guido Trotter
    """
752 7ee7c0c7 Guido Trotter
    return self.__keyring[level]._is_owned()
753 7ee7c0c7 Guido Trotter
754 d4f4b3e7 Guido Trotter
  is_owned = _is_owned
755 d4f4b3e7 Guido Trotter
756 7ee7c0c7 Guido Trotter
  def _list_owned(self, level):
757 7ee7c0c7 Guido Trotter
    """Get the set of owned locks at the given level
758 7ee7c0c7 Guido Trotter

759 7ee7c0c7 Guido Trotter
    """
760 7ee7c0c7 Guido Trotter
    return self.__keyring[level]._list_owned()
761 7ee7c0c7 Guido Trotter
762 7ee7c0c7 Guido Trotter
  def _upper_owned(self, level):
763 7ee7c0c7 Guido Trotter
    """Check that we don't own any lock at a level greater than the given one.
764 7ee7c0c7 Guido Trotter

765 7ee7c0c7 Guido Trotter
    """
766 7ee7c0c7 Guido Trotter
    # This way of checking only works if LEVELS[i] = i, which we check for in
767 7ee7c0c7 Guido Trotter
    # the test cases.
768 7ee7c0c7 Guido Trotter
    return utils.any((self._is_owned(l) for l in LEVELS[level + 1:]))
769 7ee7c0c7 Guido Trotter
770 7ee7c0c7 Guido Trotter
  def _BGL_owned(self):
771 7ee7c0c7 Guido Trotter
    """Check if the current thread owns the BGL.
772 7ee7c0c7 Guido Trotter

773 7ee7c0c7 Guido Trotter
    Both an exclusive or a shared acquisition work.
774 7ee7c0c7 Guido Trotter

775 7ee7c0c7 Guido Trotter
    """
776 7ee7c0c7 Guido Trotter
    return BGL in self.__keyring[LEVEL_CLUSTER]._list_owned()
777 7ee7c0c7 Guido Trotter
778 7ee7c0c7 Guido Trotter
  def _contains_BGL(self, level, names):
779 c41eea6e Iustin Pop
    """Check if the level contains the BGL.
780 c41eea6e Iustin Pop

781 c41eea6e Iustin Pop
    Check if acting on the given level and set of names will change
782 c41eea6e Iustin Pop
    the status of the Big Ganeti Lock.
783 7ee7c0c7 Guido Trotter

784 7ee7c0c7 Guido Trotter
    """
785 7ee7c0c7 Guido Trotter
    return level == LEVEL_CLUSTER and (names is None or BGL in names)
786 7ee7c0c7 Guido Trotter
787 7ee7c0c7 Guido Trotter
  def acquire(self, level, names, blocking=1, shared=0):
788 7ee7c0c7 Guido Trotter
    """Acquire a set of resource locks, at the same level.
789 7ee7c0c7 Guido Trotter

790 c41eea6e Iustin Pop
    @param level: the level at which the locks shall be acquired;
791 5bbd3f7f Michael Hanselmann
        it must be a member of LEVELS.
792 c41eea6e Iustin Pop
    @param names: the names of the locks which shall be acquired
793 c41eea6e Iustin Pop
        (special lock names, or instance/node names)
794 c41eea6e Iustin Pop
    @param shared: whether to acquire in shared mode; by default
795 c41eea6e Iustin Pop
        an exclusive lock will be acquired
796 c41eea6e Iustin Pop
    @param blocking: whether to block while trying to acquire or to
797 c41eea6e Iustin Pop
        operate in try-lock mode (this locking mode is not supported yet)
798 7ee7c0c7 Guido Trotter

799 7ee7c0c7 Guido Trotter
    """
800 7ee7c0c7 Guido Trotter
    assert level in LEVELS, "Invalid locking level %s" % level
801 7ee7c0c7 Guido Trotter
802 7ee7c0c7 Guido Trotter
    # Check that we are either acquiring the Big Ganeti Lock or we already own
803 7ee7c0c7 Guido Trotter
    # it. Some "legacy" opcodes need to be sure they are run non-concurrently
804 7ee7c0c7 Guido Trotter
    # so even if we've migrated we need to at least share the BGL to be
805 7ee7c0c7 Guido Trotter
    # compatible with them. Of course if we own the BGL exclusively there's no
806 7ee7c0c7 Guido Trotter
    # point in acquiring any other lock, unless perhaps we are half way through
807 7ee7c0c7 Guido Trotter
    # the migration of the current opcode.
808 7ee7c0c7 Guido Trotter
    assert (self._contains_BGL(level, names) or self._BGL_owned()), (
809 7ee7c0c7 Guido Trotter
            "You must own the Big Ganeti Lock before acquiring any other")
810 7ee7c0c7 Guido Trotter
811 7ee7c0c7 Guido Trotter
    # Check we don't own locks at the same or upper levels.
812 21a6c826 Guido Trotter
    assert not self._upper_owned(level), ("Cannot acquire locks at a level"
813 7ee7c0c7 Guido Trotter
           " while owning some at a greater one")
814 7ee7c0c7 Guido Trotter
815 7ee7c0c7 Guido Trotter
    # Acquire the locks in the set.
816 7ee7c0c7 Guido Trotter
    return self.__keyring[level].acquire(names, shared=shared,
817 7ee7c0c7 Guido Trotter
                                         blocking=blocking)
818 7ee7c0c7 Guido Trotter
819 7ee7c0c7 Guido Trotter
  def release(self, level, names=None):
820 7ee7c0c7 Guido Trotter
    """Release a set of resource locks, at the same level.
821 7ee7c0c7 Guido Trotter

822 c41eea6e Iustin Pop
    You must have acquired the locks, either in shared or in exclusive
823 c41eea6e Iustin Pop
    mode, before releasing them.
824 7ee7c0c7 Guido Trotter

825 c41eea6e Iustin Pop
    @param level: the level at which the locks shall be released;
826 5bbd3f7f Michael Hanselmann
        it must be a member of LEVELS
827 c41eea6e Iustin Pop
    @param names: the names of the locks which shall be released
828 c41eea6e Iustin Pop
        (defaults to all the locks acquired at that level)
829 7ee7c0c7 Guido Trotter

830 7ee7c0c7 Guido Trotter
    """
831 7ee7c0c7 Guido Trotter
    assert level in LEVELS, "Invalid locking level %s" % level
832 7ee7c0c7 Guido Trotter
    assert (not self._contains_BGL(level, names) or
833 7ee7c0c7 Guido Trotter
            not self._upper_owned(LEVEL_CLUSTER)), (
834 7ee7c0c7 Guido Trotter
            "Cannot release the Big Ganeti Lock while holding something"
835 7ee7c0c7 Guido Trotter
            " at upper levels")
836 7ee7c0c7 Guido Trotter
837 7ee7c0c7 Guido Trotter
    # Release will complain if we don't own the locks already
838 7ee7c0c7 Guido Trotter
    return self.__keyring[level].release(names)
839 7ee7c0c7 Guido Trotter
840 7ee7c0c7 Guido Trotter
  def add(self, level, names, acquired=0, shared=0):
841 7ee7c0c7 Guido Trotter
    """Add locks at the specified level.
842 7ee7c0c7 Guido Trotter

843 c41eea6e Iustin Pop
    @param level: the level at which the locks shall be added;
844 5bbd3f7f Michael Hanselmann
        it must be a member of LEVELS_MOD.
845 c41eea6e Iustin Pop
    @param names: names of the locks to acquire
846 c41eea6e Iustin Pop
    @param acquired: whether to acquire the newly added locks
847 c41eea6e Iustin Pop
    @param shared: whether the acquisition will be shared
848 c41eea6e Iustin Pop

849 7ee7c0c7 Guido Trotter
    """
850 7ee7c0c7 Guido Trotter
    assert level in LEVELS_MOD, "Invalid or immutable level %s" % level
851 7ee7c0c7 Guido Trotter
    assert self._BGL_owned(), ("You must own the BGL before performing other"
852 7ee7c0c7 Guido Trotter
           " operations")
853 7ee7c0c7 Guido Trotter
    assert not self._upper_owned(level), ("Cannot add locks at a level"
854 7ee7c0c7 Guido Trotter
           " while owning some at a greater one")
855 7ee7c0c7 Guido Trotter
    return self.__keyring[level].add(names, acquired=acquired, shared=shared)
856 7ee7c0c7 Guido Trotter
857 7ee7c0c7 Guido Trotter
  def remove(self, level, names, blocking=1):
858 7ee7c0c7 Guido Trotter
    """Remove locks from the specified level.
859 7ee7c0c7 Guido Trotter

860 c41eea6e Iustin Pop
    You must either already own the locks you are trying to remove
861 c41eea6e Iustin Pop
    exclusively or not own any lock at an upper level.
862 7ee7c0c7 Guido Trotter

863 c41eea6e Iustin Pop
    @param level: the level at which the locks shall be removed;
864 c41eea6e Iustin Pop
        it must be a member of LEVELS_MOD
865 c41eea6e Iustin Pop
    @param names: the names of the locks which shall be removed
866 c41eea6e Iustin Pop
        (special lock names, or instance/node names)
867 c41eea6e Iustin Pop
    @param blocking: whether to block while trying to operate in
868 c41eea6e Iustin Pop
        try-lock mode (this locking mode is not supported yet)
869 7ee7c0c7 Guido Trotter

870 7ee7c0c7 Guido Trotter
    """
871 7ee7c0c7 Guido Trotter
    assert level in LEVELS_MOD, "Invalid or immutable level %s" % level
872 7ee7c0c7 Guido Trotter
    assert self._BGL_owned(), ("You must own the BGL before performing other"
873 7ee7c0c7 Guido Trotter
           " operations")
874 f12eadb3 Iustin Pop
    # Check we either own the level or don't own anything from here
875 f12eadb3 Iustin Pop
    # up. LockSet.remove() will check the case in which we don't own
876 f12eadb3 Iustin Pop
    # all the needed resources, or we have a shared ownership.
877 7ee7c0c7 Guido Trotter
    assert self._is_owned(level) or not self._upper_owned(level), (
878 7ee7c0c7 Guido Trotter
           "Cannot remove locks at a level while not owning it or"
879 7ee7c0c7 Guido Trotter
           " owning some at a greater one")
880 cdb08f44 Michael Hanselmann
    return self.__keyring[level].remove(names, blocking=blocking)