Statistics
| Branch: | Tag: | Revision:

root / lib / mcpu.py @ 6b9b18a2

History | View | Annotate | Download (16 kB)

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

24 a8083063 Iustin Pop
This module implements the logic for doing operations in the cluster. There
25 a8083063 Iustin Pop
are two kinds of classes defined:
26 a8083063 Iustin Pop
  - logical units, which know how to deal with their specific opcode only
27 a8083063 Iustin Pop
  - the processor, which dispatches the opcodes to their logical units
28 a8083063 Iustin Pop

29 a8083063 Iustin Pop
"""
30 a8083063 Iustin Pop
31 a5eb7789 Iustin Pop
import logging
32 407339d0 Michael Hanselmann
import random
33 407339d0 Michael Hanselmann
import time
34 a8083063 Iustin Pop
35 a8083063 Iustin Pop
from ganeti import opcodes
36 a8083063 Iustin Pop
from ganeti import constants
37 a8083063 Iustin Pop
from ganeti import errors
38 a8083063 Iustin Pop
from ganeti import rpc
39 a8083063 Iustin Pop
from ganeti import cmdlib
40 04864530 Guido Trotter
from ganeti import locking
41 557838c1 René Nussbaumer
from ganeti import utils
42 a8083063 Iustin Pop
43 7c0d6283 Michael Hanselmann
44 a1a7bc78 Iustin Pop
_OP_PREFIX = "Op"
45 a1a7bc78 Iustin Pop
_LU_PREFIX = "LU"
46 a1a7bc78 Iustin Pop
47 a1a7bc78 Iustin Pop
48 831bbbc1 Michael Hanselmann
class LockAcquireTimeout(Exception):
49 831bbbc1 Michael Hanselmann
  """Exception to report timeouts on acquiring locks.
50 407339d0 Michael Hanselmann

51 407339d0 Michael Hanselmann
  """
52 407339d0 Michael Hanselmann
53 407339d0 Michael Hanselmann
54 e3200b18 Michael Hanselmann
def _CalculateLockAttemptTimeouts():
55 e3200b18 Michael Hanselmann
  """Calculate timeouts for lock attempts.
56 e3200b18 Michael Hanselmann

57 e3200b18 Michael Hanselmann
  """
58 e3200b18 Michael Hanselmann
  result = [1.0]
59 e3200b18 Michael Hanselmann
60 e3200b18 Michael Hanselmann
  # Wait for a total of at least 150s before doing a blocking acquire
61 e3200b18 Michael Hanselmann
  while sum(result) < 150.0:
62 e3200b18 Michael Hanselmann
    timeout = (result[-1] * 1.05) ** 1.25
63 e3200b18 Michael Hanselmann
64 e3200b18 Michael Hanselmann
    # Cap timeout at 10 seconds. This gives other jobs a chance to run
65 e3200b18 Michael Hanselmann
    # even if we're still trying to get our locks, before finally moving
66 e3200b18 Michael Hanselmann
    # to a blocking acquire.
67 e3200b18 Michael Hanselmann
    if timeout > 10.0:
68 e3200b18 Michael Hanselmann
      timeout = 10.0
69 e3200b18 Michael Hanselmann
70 e3200b18 Michael Hanselmann
    elif timeout < 0.1:
71 e3200b18 Michael Hanselmann
      # Lower boundary for safety
72 e3200b18 Michael Hanselmann
      timeout = 0.1
73 e3200b18 Michael Hanselmann
74 e3200b18 Michael Hanselmann
    result.append(timeout)
75 e3200b18 Michael Hanselmann
76 e3200b18 Michael Hanselmann
  return result
77 e3200b18 Michael Hanselmann
78 e3200b18 Michael Hanselmann
79 a7770f03 Michael Hanselmann
class LockAttemptTimeoutStrategy(object):
80 407339d0 Michael Hanselmann
  """Class with lock acquire timeout strategy.
81 407339d0 Michael Hanselmann

82 407339d0 Michael Hanselmann
  """
83 407339d0 Michael Hanselmann
  __slots__ = [
84 a7770f03 Michael Hanselmann
    "_timeouts",
85 407339d0 Michael Hanselmann
    "_random_fn",
86 e3200b18 Michael Hanselmann
    "_time_fn",
87 407339d0 Michael Hanselmann
    ]
88 407339d0 Michael Hanselmann
89 e3200b18 Michael Hanselmann
  _TIMEOUT_PER_ATTEMPT = _CalculateLockAttemptTimeouts()
90 407339d0 Michael Hanselmann
91 a7770f03 Michael Hanselmann
  def __init__(self, _time_fn=time.time, _random_fn=random.random):
92 407339d0 Michael Hanselmann
    """Initializes this class.
93 407339d0 Michael Hanselmann

94 e3200b18 Michael Hanselmann
    @param _time_fn: Time function for unittests
95 407339d0 Michael Hanselmann
    @param _random_fn: Random number generator for unittests
96 407339d0 Michael Hanselmann

97 407339d0 Michael Hanselmann
    """
98 407339d0 Michael Hanselmann
    object.__init__(self)
99 407339d0 Michael Hanselmann
100 a7770f03 Michael Hanselmann
    self._timeouts = iter(self._TIMEOUT_PER_ATTEMPT)
101 e3200b18 Michael Hanselmann
    self._time_fn = _time_fn
102 e3200b18 Michael Hanselmann
    self._random_fn = _random_fn
103 e3200b18 Michael Hanselmann
104 407339d0 Michael Hanselmann
  def NextAttempt(self):
105 a7770f03 Michael Hanselmann
    """Returns the timeout for the next attempt.
106 407339d0 Michael Hanselmann

107 407339d0 Michael Hanselmann
    """
108 a7770f03 Michael Hanselmann
    try:
109 a7770f03 Michael Hanselmann
      timeout = self._timeouts.next()
110 a7770f03 Michael Hanselmann
    except StopIteration:
111 a7770f03 Michael Hanselmann
      # No more timeouts, do blocking acquire
112 a7770f03 Michael Hanselmann
      timeout = None
113 407339d0 Michael Hanselmann
114 a6db1af2 Michael Hanselmann
    if timeout is not None:
115 a6db1af2 Michael Hanselmann
      # Add a small variation (-/+ 5%) to timeout. This helps in situations
116 a6db1af2 Michael Hanselmann
      # where two or more jobs are fighting for the same lock(s).
117 a6db1af2 Michael Hanselmann
      variation_range = timeout * 0.1
118 a6db1af2 Michael Hanselmann
      timeout += ((self._random_fn() * variation_range) -
119 a6db1af2 Michael Hanselmann
                  (variation_range * 0.5))
120 407339d0 Michael Hanselmann
121 a6db1af2 Michael Hanselmann
    return timeout
122 407339d0 Michael Hanselmann
123 407339d0 Michael Hanselmann
124 7260cfbe Iustin Pop
class OpExecCbBase: # pylint: disable-msg=W0232
125 031a3e57 Michael Hanselmann
  """Base class for OpCode execution callbacks.
126 031a3e57 Michael Hanselmann

127 031a3e57 Michael Hanselmann
  """
128 031a3e57 Michael Hanselmann
  def NotifyStart(self):
129 031a3e57 Michael Hanselmann
    """Called when we are about to execute the LU.
130 031a3e57 Michael Hanselmann

131 031a3e57 Michael Hanselmann
    This function is called when we're about to start the lu's Exec() method,
132 031a3e57 Michael Hanselmann
    that is, after we have acquired all locks.
133 031a3e57 Michael Hanselmann

134 031a3e57 Michael Hanselmann
    """
135 031a3e57 Michael Hanselmann
136 031a3e57 Michael Hanselmann
  def Feedback(self, *args):
137 031a3e57 Michael Hanselmann
    """Sends feedback from the LU code to the end-user.
138 031a3e57 Michael Hanselmann

139 031a3e57 Michael Hanselmann
    """
140 031a3e57 Michael Hanselmann
141 acf931b7 Michael Hanselmann
  def CheckCancel(self):
142 acf931b7 Michael Hanselmann
    """Check whether job has been cancelled.
143 ef2df7d3 Michael Hanselmann

144 ef2df7d3 Michael Hanselmann
    """
145 ef2df7d3 Michael Hanselmann
146 031a3e57 Michael Hanselmann
147 a1a7bc78 Iustin Pop
def _LUNameForOpName(opname):
148 a1a7bc78 Iustin Pop
  """Computes the LU name for a given OpCode name.
149 a1a7bc78 Iustin Pop

150 a1a7bc78 Iustin Pop
  """
151 a1a7bc78 Iustin Pop
  assert opname.startswith(_OP_PREFIX), \
152 a1a7bc78 Iustin Pop
      "Invalid OpCode name, doesn't start with %s: %s" % (_OP_PREFIX, opname)
153 a1a7bc78 Iustin Pop
154 a1a7bc78 Iustin Pop
  return _LU_PREFIX + opname[len(_OP_PREFIX):]
155 a1a7bc78 Iustin Pop
156 a1a7bc78 Iustin Pop
157 a1a7bc78 Iustin Pop
def _ComputeDispatchTable():
158 a1a7bc78 Iustin Pop
  """Computes the opcode-to-lu dispatch table.
159 a1a7bc78 Iustin Pop

160 a1a7bc78 Iustin Pop
  """
161 a1a7bc78 Iustin Pop
  return dict((op, getattr(cmdlib, _LUNameForOpName(op.__name__)))
162 a1a7bc78 Iustin Pop
              for op in opcodes.OP_MAPPING.values()
163 a1a7bc78 Iustin Pop
              if op.WITH_LU)
164 a1a7bc78 Iustin Pop
165 a1a7bc78 Iustin Pop
166 a8083063 Iustin Pop
class Processor(object):
167 a8083063 Iustin Pop
  """Object which runs OpCodes"""
168 a1a7bc78 Iustin Pop
  DISPATCH_TABLE = _ComputeDispatchTable()
169 a8083063 Iustin Pop
170 adfa97e3 Guido Trotter
  def __init__(self, context, ec_id):
171 a8083063 Iustin Pop
    """Constructor for Processor
172 a8083063 Iustin Pop

173 adfa97e3 Guido Trotter
    @type context: GanetiContext
174 adfa97e3 Guido Trotter
    @param context: global Ganeti context
175 adfa97e3 Guido Trotter
    @type ec_id: string
176 adfa97e3 Guido Trotter
    @param ec_id: execution context identifier
177 adfa97e3 Guido Trotter

178 a8083063 Iustin Pop
    """
179 1c901d13 Guido Trotter
    self.context = context
180 adfa97e3 Guido Trotter
    self._ec_id = ec_id
181 031a3e57 Michael Hanselmann
    self._cbs = None
182 72737a7f Iustin Pop
    self.rpc = rpc.RpcRunner(context.cfg)
183 cd46f3b4 Luca Bigliardi
    self.hmclass = HooksMaster
184 a8083063 Iustin Pop
185 f879a9c7 Michael Hanselmann
  def _AcquireLocks(self, level, names, shared, timeout, priority):
186 211b6132 Michael Hanselmann
    """Acquires locks via the Ganeti lock manager.
187 211b6132 Michael Hanselmann

188 211b6132 Michael Hanselmann
    @type level: int
189 211b6132 Michael Hanselmann
    @param level: Lock level
190 211b6132 Michael Hanselmann
    @type names: list or string
191 211b6132 Michael Hanselmann
    @param names: Lock names
192 211b6132 Michael Hanselmann
    @type shared: bool
193 211b6132 Michael Hanselmann
    @param shared: Whether the locks should be acquired in shared mode
194 211b6132 Michael Hanselmann
    @type timeout: None or float
195 211b6132 Michael Hanselmann
    @param timeout: Timeout for acquiring the locks
196 900df6cd Michael Hanselmann
    @raise LockAcquireTimeout: In case locks couldn't be acquired in specified
197 900df6cd Michael Hanselmann
        amount of time
198 211b6132 Michael Hanselmann

199 211b6132 Michael Hanselmann
    """
200 acf931b7 Michael Hanselmann
    if self._cbs:
201 acf931b7 Michael Hanselmann
      self._cbs.CheckCancel()
202 211b6132 Michael Hanselmann
203 211b6132 Michael Hanselmann
    acquired = self.context.glm.acquire(level, names, shared=shared,
204 f879a9c7 Michael Hanselmann
                                        timeout=timeout, priority=priority)
205 211b6132 Michael Hanselmann
206 900df6cd Michael Hanselmann
    if acquired is None:
207 900df6cd Michael Hanselmann
      raise LockAcquireTimeout()
208 900df6cd Michael Hanselmann
209 211b6132 Michael Hanselmann
    return acquired
210 211b6132 Michael Hanselmann
211 36c381d7 Guido Trotter
  def _ExecLU(self, lu):
212 36c381d7 Guido Trotter
    """Logical Unit execution sequence.
213 36c381d7 Guido Trotter

214 36c381d7 Guido Trotter
    """
215 36c381d7 Guido Trotter
    write_count = self.context.cfg.write_count
216 36c381d7 Guido Trotter
    lu.CheckPrereq()
217 4b5e8271 Iustin Pop
    hm = HooksMaster(self.rpc.call_hooks_runner, lu)
218 36c381d7 Guido Trotter
    h_results = hm.RunPhase(constants.HOOKS_PHASE_PRE)
219 36c381d7 Guido Trotter
    lu.HooksCallBack(constants.HOOKS_PHASE_PRE, h_results,
220 7b4c1cb9 Michael Hanselmann
                     self.Log, None)
221 20777413 Iustin Pop
222 20777413 Iustin Pop
    if getattr(lu.op, "dry_run", False):
223 20777413 Iustin Pop
      # in this mode, no post-hooks are run, and the config is not
224 20777413 Iustin Pop
      # written (as it might have been modified by another LU, and we
225 20777413 Iustin Pop
      # shouldn't do writeout on behalf of other threads
226 20777413 Iustin Pop
      self.LogInfo("dry-run mode requested, not actually executing"
227 20777413 Iustin Pop
                   " the operation")
228 20777413 Iustin Pop
      return lu.dry_run_result
229 20777413 Iustin Pop
230 36c381d7 Guido Trotter
    try:
231 7b4c1cb9 Michael Hanselmann
      result = lu.Exec(self.Log)
232 36c381d7 Guido Trotter
      h_results = hm.RunPhase(constants.HOOKS_PHASE_POST)
233 36c381d7 Guido Trotter
      result = lu.HooksCallBack(constants.HOOKS_PHASE_POST, h_results,
234 7b4c1cb9 Michael Hanselmann
                                self.Log, result)
235 36c381d7 Guido Trotter
    finally:
236 36c381d7 Guido Trotter
      # FIXME: This needs locks if not lu_class.REQ_BGL
237 36c381d7 Guido Trotter
      if write_count != self.context.cfg.write_count:
238 36c381d7 Guido Trotter
        hm.RunConfigUpdate()
239 36c381d7 Guido Trotter
240 36c381d7 Guido Trotter
    return result
241 36c381d7 Guido Trotter
242 f879a9c7 Michael Hanselmann
  def _LockAndExecLU(self, lu, level, calc_timeout, priority):
243 68adfdb2 Guido Trotter
    """Execute a Logical Unit, with the needed locks.
244 68adfdb2 Guido Trotter

245 68adfdb2 Guido Trotter
    This is a recursive function that starts locking the given level, and
246 68adfdb2 Guido Trotter
    proceeds up, till there are no more locks to acquire. Then it executes the
247 68adfdb2 Guido Trotter
    given LU and its opcodes.
248 68adfdb2 Guido Trotter

249 68adfdb2 Guido Trotter
    """
250 ca2a79e1 Guido Trotter
    adding_locks = level in lu.add_locks
251 ca2a79e1 Guido Trotter
    acquiring_locks = level in lu.needed_locks
252 8a2941c4 Guido Trotter
    if level not in locking.LEVELS:
253 031a3e57 Michael Hanselmann
      if self._cbs:
254 031a3e57 Michael Hanselmann
        self._cbs.NotifyStart()
255 031a3e57 Michael Hanselmann
256 8a2941c4 Guido Trotter
      result = self._ExecLU(lu)
257 407339d0 Michael Hanselmann
258 ca2a79e1 Guido Trotter
    elif adding_locks and acquiring_locks:
259 ca2a79e1 Guido Trotter
      # We could both acquire and add locks at the same level, but for now we
260 ca2a79e1 Guido Trotter
      # don't need this, so we'll avoid the complicated code needed.
261 407339d0 Michael Hanselmann
      raise NotImplementedError("Can't declare locks to acquire when adding"
262 407339d0 Michael Hanselmann
                                " others")
263 407339d0 Michael Hanselmann
264 ca2a79e1 Guido Trotter
    elif adding_locks or acquiring_locks:
265 fb8dcb62 Guido Trotter
      lu.DeclareLocks(level)
266 3977a4c1 Guido Trotter
      share = lu.share_locks[level]
267 407339d0 Michael Hanselmann
268 68adfdb2 Guido Trotter
      try:
269 407339d0 Michael Hanselmann
        assert adding_locks ^ acquiring_locks, \
270 407339d0 Michael Hanselmann
          "Locks must be either added or acquired"
271 407339d0 Michael Hanselmann
272 407339d0 Michael Hanselmann
        if acquiring_locks:
273 407339d0 Michael Hanselmann
          # Acquiring locks
274 407339d0 Michael Hanselmann
          needed_locks = lu.needed_locks[level]
275 407339d0 Michael Hanselmann
276 211b6132 Michael Hanselmann
          acquired = self._AcquireLocks(level, needed_locks, share,
277 f879a9c7 Michael Hanselmann
                                        calc_timeout(), priority)
278 407339d0 Michael Hanselmann
        else:
279 407339d0 Michael Hanselmann
          # Adding locks
280 407339d0 Michael Hanselmann
          add_locks = lu.add_locks[level]
281 407339d0 Michael Hanselmann
          lu.remove_locks[level] = add_locks
282 407339d0 Michael Hanselmann
283 407339d0 Michael Hanselmann
          try:
284 407339d0 Michael Hanselmann
            self.context.glm.add(level, add_locks, acquired=1, shared=share)
285 407339d0 Michael Hanselmann
          except errors.LockError:
286 407339d0 Michael Hanselmann
            raise errors.OpPrereqError(
287 407339d0 Michael Hanselmann
              "Couldn't add locks (%s), probably because of a race condition"
288 debac808 Iustin Pop
              " with another job, who added them first" % add_locks,
289 debac808 Iustin Pop
              errors.ECODE_FAULT)
290 407339d0 Michael Hanselmann
291 6f14fc27 Michael Hanselmann
          acquired = add_locks
292 6f14fc27 Michael Hanselmann
293 ca2a79e1 Guido Trotter
        try:
294 6f14fc27 Michael Hanselmann
          lu.acquired_locks[level] = acquired
295 6f14fc27 Michael Hanselmann
296 f879a9c7 Michael Hanselmann
          result = self._LockAndExecLU(lu, level + 1, calc_timeout, priority)
297 ca2a79e1 Guido Trotter
        finally:
298 ca2a79e1 Guido Trotter
          if level in lu.remove_locks:
299 ca2a79e1 Guido Trotter
            self.context.glm.remove(level, lu.remove_locks[level])
300 68adfdb2 Guido Trotter
      finally:
301 80ee04a4 Guido Trotter
        if self.context.glm.is_owned(level):
302 68adfdb2 Guido Trotter
          self.context.glm.release(level)
303 407339d0 Michael Hanselmann
304 68adfdb2 Guido Trotter
    else:
305 f879a9c7 Michael Hanselmann
      result = self._LockAndExecLU(lu, level + 1, calc_timeout, priority)
306 68adfdb2 Guido Trotter
307 68adfdb2 Guido Trotter
    return result
308 68adfdb2 Guido Trotter
309 f879a9c7 Michael Hanselmann
  def ExecOpCode(self, op, cbs, timeout=None, priority=None):
310 a8083063 Iustin Pop
    """Execute an opcode.
311 a8083063 Iustin Pop

312 e92376d7 Iustin Pop
    @type op: an OpCode instance
313 e92376d7 Iustin Pop
    @param op: the opcode to be executed
314 031a3e57 Michael Hanselmann
    @type cbs: L{OpExecCbBase}
315 031a3e57 Michael Hanselmann
    @param cbs: Runtime callbacks
316 831bbbc1 Michael Hanselmann
    @type timeout: float or None
317 831bbbc1 Michael Hanselmann
    @param timeout: Maximum time to acquire all locks, None for no timeout
318 f879a9c7 Michael Hanselmann
    @type priority: number or None
319 f879a9c7 Michael Hanselmann
    @param priority: Priority for acquiring lock(s)
320 831bbbc1 Michael Hanselmann
    @raise LockAcquireTimeout: In case locks couldn't be acquired in specified
321 831bbbc1 Michael Hanselmann
        amount of time
322 a8083063 Iustin Pop

323 a8083063 Iustin Pop
    """
324 a8083063 Iustin Pop
    if not isinstance(op, opcodes.OpCode):
325 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Non-opcode instance passed"
326 3ecf6786 Iustin Pop
                                   " to ExecOpcode")
327 a8083063 Iustin Pop
328 831bbbc1 Michael Hanselmann
    lu_class = self.DISPATCH_TABLE.get(op.__class__, None)
329 831bbbc1 Michael Hanselmann
    if lu_class is None:
330 831bbbc1 Michael Hanselmann
      raise errors.OpCodeUnknown("Unknown opcode")
331 831bbbc1 Michael Hanselmann
332 831bbbc1 Michael Hanselmann
    if timeout is None:
333 831bbbc1 Michael Hanselmann
      calc_timeout = lambda: None
334 831bbbc1 Michael Hanselmann
    else:
335 557838c1 René Nussbaumer
      calc_timeout = utils.RunningTimeout(timeout, False).Remaining
336 831bbbc1 Michael Hanselmann
337 031a3e57 Michael Hanselmann
    self._cbs = cbs
338 fe482621 Iustin Pop
    try:
339 831bbbc1 Michael Hanselmann
      # Acquire the Big Ganeti Lock exclusively if this LU requires it,
340 831bbbc1 Michael Hanselmann
      # and in a shared fashion otherwise (to prevent concurrent run with
341 831bbbc1 Michael Hanselmann
      # an exclusive LU.
342 900df6cd Michael Hanselmann
      self._AcquireLocks(locking.LEVEL_CLUSTER, locking.BGL,
343 900df6cd Michael Hanselmann
                          not lu_class.REQ_BGL, calc_timeout(),
344 900df6cd Michael Hanselmann
                          priority)
345 831bbbc1 Michael Hanselmann
      try:
346 831bbbc1 Michael Hanselmann
        lu = lu_class(self, op, self.context, self.rpc)
347 831bbbc1 Michael Hanselmann
        lu.ExpandNames()
348 831bbbc1 Michael Hanselmann
        assert lu.needed_locks is not None, "needed_locks not set by LU"
349 407339d0 Michael Hanselmann
350 407339d0 Michael Hanselmann
        try:
351 f879a9c7 Michael Hanselmann
          return self._LockAndExecLU(lu, locking.LEVEL_INSTANCE, calc_timeout,
352 f879a9c7 Michael Hanselmann
                                     priority)
353 831bbbc1 Michael Hanselmann
        finally:
354 831bbbc1 Michael Hanselmann
          if self._ec_id:
355 831bbbc1 Michael Hanselmann
            self.context.cfg.DropECReservations(self._ec_id)
356 831bbbc1 Michael Hanselmann
      finally:
357 831bbbc1 Michael Hanselmann
        self.context.glm.release(locking.LEVEL_CLUSTER)
358 04864530 Guido Trotter
    finally:
359 031a3e57 Michael Hanselmann
      self._cbs = None
360 6a4aa7c1 Iustin Pop
361 7b4c1cb9 Michael Hanselmann
  def Log(self, *args):
362 031a3e57 Michael Hanselmann
    """Forward call to feedback callback function.
363 031a3e57 Michael Hanselmann

364 031a3e57 Michael Hanselmann
    """
365 031a3e57 Michael Hanselmann
    if self._cbs:
366 031a3e57 Michael Hanselmann
      self._cbs.Feedback(*args)
367 031a3e57 Michael Hanselmann
368 0fbbf897 Iustin Pop
  def LogStep(self, current, total, message):
369 0fbbf897 Iustin Pop
    """Log a change in LU execution progress.
370 0fbbf897 Iustin Pop

371 0fbbf897 Iustin Pop
    """
372 a5eb7789 Iustin Pop
    logging.debug("Step %d/%d %s", current, total, message)
373 7b4c1cb9 Michael Hanselmann
    self.Log("STEP %d/%d %s" % (current, total, message))
374 0fbbf897 Iustin Pop
375 c0088fb9 Iustin Pop
  def LogWarning(self, message, *args, **kwargs):
376 0fbbf897 Iustin Pop
    """Log a warning to the logs and the user.
377 0fbbf897 Iustin Pop

378 c0088fb9 Iustin Pop
    The optional keyword argument is 'hint' and can be used to show a
379 c0088fb9 Iustin Pop
    hint to the user (presumably related to the warning). If the
380 c0088fb9 Iustin Pop
    message is empty, it will not be printed at all, allowing one to
381 c0088fb9 Iustin Pop
    show only a hint.
382 0fbbf897 Iustin Pop

383 c0088fb9 Iustin Pop
    """
384 c0088fb9 Iustin Pop
    assert not kwargs or (len(kwargs) == 1 and "hint" in kwargs), \
385 c0088fb9 Iustin Pop
           "Invalid keyword arguments for LogWarning (%s)" % str(kwargs)
386 c0088fb9 Iustin Pop
    if args:
387 c0088fb9 Iustin Pop
      message = message % tuple(args)
388 c0088fb9 Iustin Pop
    if message:
389 c0088fb9 Iustin Pop
      logging.warning(message)
390 7b4c1cb9 Michael Hanselmann
      self.Log(" - WARNING: %s" % message)
391 c0088fb9 Iustin Pop
    if "hint" in kwargs:
392 7b4c1cb9 Michael Hanselmann
      self.Log("      Hint: %s" % kwargs["hint"])
393 c0088fb9 Iustin Pop
394 c0088fb9 Iustin Pop
  def LogInfo(self, message, *args):
395 0fbbf897 Iustin Pop
    """Log an informational message to the logs and the user.
396 0fbbf897 Iustin Pop

397 0fbbf897 Iustin Pop
    """
398 c0088fb9 Iustin Pop
    if args:
399 c0088fb9 Iustin Pop
      message = message % tuple(args)
400 a5eb7789 Iustin Pop
    logging.info(message)
401 7b4c1cb9 Michael Hanselmann
    self.Log(" - INFO: %s" % message)
402 0fbbf897 Iustin Pop
403 adfa97e3 Guido Trotter
  def GetECId(self):
404 adfa97e3 Guido Trotter
    if not self._ec_id:
405 adfa97e3 Guido Trotter
      errors.ProgrammerError("Tried to use execution context id when not set")
406 adfa97e3 Guido Trotter
    return self._ec_id
407 adfa97e3 Guido Trotter
408 a8083063 Iustin Pop
409 a8083063 Iustin Pop
class HooksMaster(object):
410 a8083063 Iustin Pop
  """Hooks master.
411 a8083063 Iustin Pop

412 a8083063 Iustin Pop
  This class distributes the run commands to the nodes based on the
413 a8083063 Iustin Pop
  specific LU class.
414 a8083063 Iustin Pop

415 a8083063 Iustin Pop
  In order to remove the direct dependency on the rpc module, the
416 a8083063 Iustin Pop
  constructor needs a function which actually does the remote
417 a8083063 Iustin Pop
  call. This will usually be rpc.call_hooks_runner, but any function
418 a8083063 Iustin Pop
  which behaves the same works.
419 a8083063 Iustin Pop

420 a8083063 Iustin Pop
  """
421 4b5e8271 Iustin Pop
  def __init__(self, callfn, lu):
422 a8083063 Iustin Pop
    self.callfn = callfn
423 a8083063 Iustin Pop
    self.lu = lu
424 a8083063 Iustin Pop
    self.op = lu.op
425 a8083063 Iustin Pop
    self.env, node_list_pre, node_list_post = self._BuildEnv()
426 a8083063 Iustin Pop
    self.node_list = {constants.HOOKS_PHASE_PRE: node_list_pre,
427 a8083063 Iustin Pop
                      constants.HOOKS_PHASE_POST: node_list_post}
428 a8083063 Iustin Pop
429 a8083063 Iustin Pop
  def _BuildEnv(self):
430 a8083063 Iustin Pop
    """Compute the environment and the target nodes.
431 a8083063 Iustin Pop

432 a8083063 Iustin Pop
    Based on the opcode and the current node list, this builds the
433 a8083063 Iustin Pop
    environment for the hooks and the target node list for the run.
434 a8083063 Iustin Pop

435 a8083063 Iustin Pop
    """
436 a8083063 Iustin Pop
    env = {
437 a8083063 Iustin Pop
      "PATH": "/sbin:/bin:/usr/sbin:/usr/bin",
438 a8083063 Iustin Pop
      "GANETI_HOOKS_VERSION": constants.HOOKS_VERSION,
439 a8083063 Iustin Pop
      "GANETI_OP_CODE": self.op.OP_ID,
440 a8083063 Iustin Pop
      "GANETI_OBJECT_TYPE": self.lu.HTYPE,
441 6a4aa7c1 Iustin Pop
      "GANETI_DATA_DIR": constants.DATA_DIR,
442 a8083063 Iustin Pop
      }
443 a8083063 Iustin Pop
444 9a395a76 Iustin Pop
    if self.lu.HPATH is not None:
445 9a395a76 Iustin Pop
      lu_env, lu_nodes_pre, lu_nodes_post = self.lu.BuildHooksEnv()
446 9a395a76 Iustin Pop
      if lu_env:
447 9a395a76 Iustin Pop
        for key in lu_env:
448 9a395a76 Iustin Pop
          env["GANETI_" + key] = lu_env[key]
449 9a395a76 Iustin Pop
    else:
450 9a395a76 Iustin Pop
      lu_nodes_pre = lu_nodes_post = []
451 a8083063 Iustin Pop
452 4167825b Iustin Pop
    return env, frozenset(lu_nodes_pre), frozenset(lu_nodes_post)
453 4167825b Iustin Pop
454 4167825b Iustin Pop
  def _RunWrapper(self, node_list, hpath, phase):
455 4167825b Iustin Pop
    """Simple wrapper over self.callfn.
456 4167825b Iustin Pop

457 4167825b Iustin Pop
    This method fixes the environment before doing the rpc call.
458 4167825b Iustin Pop

459 4167825b Iustin Pop
    """
460 4167825b Iustin Pop
    env = self.env.copy()
461 4167825b Iustin Pop
    env["GANETI_HOOKS_PHASE"] = phase
462 4167825b Iustin Pop
    env["GANETI_HOOKS_PATH"] = hpath
463 437138c9 Michael Hanselmann
    if self.lu.cfg is not None:
464 437138c9 Michael Hanselmann
      env["GANETI_CLUSTER"] = self.lu.cfg.GetClusterName()
465 437138c9 Michael Hanselmann
      env["GANETI_MASTER"] = self.lu.cfg.GetMasterNode()
466 a8083063 Iustin Pop
467 4167825b Iustin Pop
    env = dict([(str(key), str(val)) for key, val in env.iteritems()])
468 a8083063 Iustin Pop
469 4167825b Iustin Pop
    return self.callfn(node_list, hpath, phase, env)
470 a8083063 Iustin Pop
471 17e82923 Luca Bigliardi
  def RunPhase(self, phase, nodes=None):
472 a8083063 Iustin Pop
    """Run all the scripts for a phase.
473 a8083063 Iustin Pop

474 a8083063 Iustin Pop
    This is the main function of the HookMaster.
475 a8083063 Iustin Pop

476 8dca23a3 Iustin Pop
    @param phase: one of L{constants.HOOKS_PHASE_POST} or
477 8dca23a3 Iustin Pop
        L{constants.HOOKS_PHASE_PRE}; it denotes the hooks phase
478 17e82923 Luca Bigliardi
    @param nodes: overrides the predefined list of nodes for the given phase
479 8dca23a3 Iustin Pop
    @return: the processed results of the hooks multi-node rpc call
480 8dca23a3 Iustin Pop
    @raise errors.HooksFailure: on communication failure to the nodes
481 6ef2dc74 Luca Bigliardi
    @raise errors.HooksAbort: on failure of one of the hooks
482 b07a6922 Guido Trotter

483 a8083063 Iustin Pop
    """
484 17e82923 Luca Bigliardi
    if not self.node_list[phase] and not nodes:
485 9a395a76 Iustin Pop
      # empty node list, we should not attempt to run this as either
486 9a395a76 Iustin Pop
      # we're in the cluster init phase and the rpc client part can't
487 9a395a76 Iustin Pop
      # even attempt to run, or this LU doesn't do hooks at all
488 a8083063 Iustin Pop
      return
489 4167825b Iustin Pop
    hpath = self.lu.HPATH
490 17e82923 Luca Bigliardi
    if nodes is not None:
491 17e82923 Luca Bigliardi
      results = self._RunWrapper(nodes, hpath, phase)
492 17e82923 Luca Bigliardi
    else:
493 17e82923 Luca Bigliardi
      results = self._RunWrapper(self.node_list[phase], hpath, phase)
494 8c4b9364 Luca Bigliardi
    errs = []
495 8c4b9364 Luca Bigliardi
    if not results:
496 8c4b9364 Luca Bigliardi
      msg = "Communication Failure"
497 8c4b9364 Luca Bigliardi
      if phase == constants.HOOKS_PHASE_PRE:
498 8c4b9364 Luca Bigliardi
        raise errors.HooksFailure(msg)
499 8c4b9364 Luca Bigliardi
      else:
500 8c4b9364 Luca Bigliardi
        self.lu.LogWarning(msg)
501 640b961e Luca Bigliardi
        return results
502 8c4b9364 Luca Bigliardi
    for node_name in results:
503 8c4b9364 Luca Bigliardi
      res = results[node_name]
504 8c4b9364 Luca Bigliardi
      if res.offline:
505 8c4b9364 Luca Bigliardi
        continue
506 3cebe102 Michael Hanselmann
      msg = res.fail_msg
507 8c4b9364 Luca Bigliardi
      if msg:
508 8c4b9364 Luca Bigliardi
        self.lu.LogWarning("Communication failure to node %s: %s",
509 8c4b9364 Luca Bigliardi
                           node_name, msg)
510 8c4b9364 Luca Bigliardi
        continue
511 8c4b9364 Luca Bigliardi
      for script, hkr, output in res.payload:
512 8c4b9364 Luca Bigliardi
        if hkr == constants.HKR_FAIL:
513 8c4b9364 Luca Bigliardi
          if phase == constants.HOOKS_PHASE_PRE:
514 a8083063 Iustin Pop
            errs.append((node_name, script, output))
515 8c4b9364 Luca Bigliardi
          else:
516 8c4b9364 Luca Bigliardi
            if not output:
517 640b961e Luca Bigliardi
              output = "(no output)"
518 8c4b9364 Luca Bigliardi
            self.lu.LogWarning("On %s script %s failed, output: %s" %
519 8c4b9364 Luca Bigliardi
                               (node_name, script, output))
520 8c4b9364 Luca Bigliardi
    if errs and phase == constants.HOOKS_PHASE_PRE:
521 8c4b9364 Luca Bigliardi
      raise errors.HooksAbort(errs)
522 b07a6922 Guido Trotter
    return results
523 6a4aa7c1 Iustin Pop
524 6a4aa7c1 Iustin Pop
  def RunConfigUpdate(self):
525 6a4aa7c1 Iustin Pop
    """Run the special configuration update hook
526 6a4aa7c1 Iustin Pop

527 6a4aa7c1 Iustin Pop
    This is a special hook that runs only on the master after each
528 6a4aa7c1 Iustin Pop
    top-level LI if the configuration has been updated.
529 6a4aa7c1 Iustin Pop

530 6a4aa7c1 Iustin Pop
    """
531 6a4aa7c1 Iustin Pop
    phase = constants.HOOKS_PHASE_POST
532 6a4aa7c1 Iustin Pop
    hpath = constants.HOOKS_NAME_CFGUPDATE
533 437138c9 Michael Hanselmann
    nodes = [self.lu.cfg.GetMasterNode()]
534 29921401 Iustin Pop
    self._RunWrapper(nodes, hpath, phase)