Statistics
| Branch: | Tag: | Revision:

root / lib / mcpu.py @ 1a2eb2dc

History | View | Annotate | Download (23.7 kB)

1 2f31098c Iustin Pop
#
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 5ae4945a Iustin Pop
# Copyright (C) 2006, 2007, 2011, 2012 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 b183c4a8 Iustin Pop
import sys
32 a5eb7789 Iustin Pop
import logging
33 407339d0 Michael Hanselmann
import random
34 407339d0 Michael Hanselmann
import time
35 07923a3c Michael Hanselmann
import itertools
36 b183c4a8 Iustin Pop
import traceback
37 a8083063 Iustin Pop
38 a8083063 Iustin Pop
from ganeti import opcodes
39 a8083063 Iustin Pop
from ganeti import constants
40 a8083063 Iustin Pop
from ganeti import errors
41 a8083063 Iustin Pop
from ganeti import cmdlib
42 04864530 Guido Trotter
from ganeti import locking
43 557838c1 René Nussbaumer
from ganeti import utils
44 ebc75510 Michael Hanselmann
from ganeti import compat
45 b3a6f851 Michael Hanselmann
from ganeti import pathutils
46 a8083063 Iustin Pop
47 7c0d6283 Michael Hanselmann
48 a1a7bc78 Iustin Pop
_OP_PREFIX = "Op"
49 a1a7bc78 Iustin Pop
_LU_PREFIX = "LU"
50 a1a7bc78 Iustin Pop
51 a1a7bc78 Iustin Pop
52 831bbbc1 Michael Hanselmann
class LockAcquireTimeout(Exception):
53 831bbbc1 Michael Hanselmann
  """Exception to report timeouts on acquiring locks.
54 407339d0 Michael Hanselmann

55 407339d0 Michael Hanselmann
  """
56 407339d0 Michael Hanselmann
57 407339d0 Michael Hanselmann
58 e3200b18 Michael Hanselmann
def _CalculateLockAttemptTimeouts():
59 e3200b18 Michael Hanselmann
  """Calculate timeouts for lock attempts.
60 e3200b18 Michael Hanselmann

61 e3200b18 Michael Hanselmann
  """
62 d385a174 Iustin Pop
  result = [constants.LOCK_ATTEMPTS_MINWAIT]
63 d385a174 Iustin Pop
  running_sum = result[0]
64 e3200b18 Michael Hanselmann
65 d385a174 Iustin Pop
  # Wait for a total of at least LOCK_ATTEMPTS_TIMEOUT before doing a
66 d385a174 Iustin Pop
  # blocking acquire
67 d385a174 Iustin Pop
  while running_sum < constants.LOCK_ATTEMPTS_TIMEOUT:
68 e3200b18 Michael Hanselmann
    timeout = (result[-1] * 1.05) ** 1.25
69 e3200b18 Michael Hanselmann
70 d385a174 Iustin Pop
    # Cap max timeout. This gives other jobs a chance to run even if
71 d385a174 Iustin Pop
    # we're still trying to get our locks, before finally moving to a
72 d385a174 Iustin Pop
    # blocking acquire.
73 d385a174 Iustin Pop
    timeout = min(timeout, constants.LOCK_ATTEMPTS_MAXWAIT)
74 d385a174 Iustin Pop
    # And also cap the lower boundary for safety
75 d385a174 Iustin Pop
    timeout = max(timeout, constants.LOCK_ATTEMPTS_MINWAIT)
76 e3200b18 Michael Hanselmann
77 e3200b18 Michael Hanselmann
    result.append(timeout)
78 d385a174 Iustin Pop
    running_sum += timeout
79 e3200b18 Michael Hanselmann
80 e3200b18 Michael Hanselmann
  return result
81 e3200b18 Michael Hanselmann
82 e3200b18 Michael Hanselmann
83 a7770f03 Michael Hanselmann
class LockAttemptTimeoutStrategy(object):
84 407339d0 Michael Hanselmann
  """Class with lock acquire timeout strategy.
85 407339d0 Michael Hanselmann

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

98 e3200b18 Michael Hanselmann
    @param _time_fn: Time function for unittests
99 407339d0 Michael Hanselmann
    @param _random_fn: Random number generator for unittests
100 407339d0 Michael Hanselmann

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

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

131 031a3e57 Michael Hanselmann
  """
132 031a3e57 Michael Hanselmann
  def NotifyStart(self):
133 031a3e57 Michael Hanselmann
    """Called when we are about to execute the LU.
134 031a3e57 Michael Hanselmann

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

138 031a3e57 Michael Hanselmann
    """
139 031a3e57 Michael Hanselmann
140 031a3e57 Michael Hanselmann
  def Feedback(self, *args):
141 031a3e57 Michael Hanselmann
    """Sends feedback from the LU code to the end-user.
142 031a3e57 Michael Hanselmann

143 031a3e57 Michael Hanselmann
    """
144 031a3e57 Michael Hanselmann
145 e4e59de8 Michael Hanselmann
  def CurrentPriority(self): # pylint: disable=R0201
146 e4e59de8 Michael Hanselmann
    """Returns current priority or C{None}.
147 ef2df7d3 Michael Hanselmann

148 ef2df7d3 Michael Hanselmann
    """
149 e4e59de8 Michael Hanselmann
    return None
150 ef2df7d3 Michael Hanselmann
151 6a373640 Michael Hanselmann
  def SubmitManyJobs(self, jobs):
152 6a373640 Michael Hanselmann
    """Submits jobs for processing.
153 6a373640 Michael Hanselmann

154 6a373640 Michael Hanselmann
    See L{jqueue.JobQueue.SubmitManyJobs}.
155 6a373640 Michael Hanselmann

156 6a373640 Michael Hanselmann
    """
157 6a373640 Michael Hanselmann
    raise NotImplementedError
158 6a373640 Michael Hanselmann
159 031a3e57 Michael Hanselmann
160 a1a7bc78 Iustin Pop
def _LUNameForOpName(opname):
161 a1a7bc78 Iustin Pop
  """Computes the LU name for a given OpCode name.
162 a1a7bc78 Iustin Pop

163 a1a7bc78 Iustin Pop
  """
164 a1a7bc78 Iustin Pop
  assert opname.startswith(_OP_PREFIX), \
165 a1a7bc78 Iustin Pop
      "Invalid OpCode name, doesn't start with %s: %s" % (_OP_PREFIX, opname)
166 a1a7bc78 Iustin Pop
167 a1a7bc78 Iustin Pop
  return _LU_PREFIX + opname[len(_OP_PREFIX):]
168 a1a7bc78 Iustin Pop
169 a1a7bc78 Iustin Pop
170 a1a7bc78 Iustin Pop
def _ComputeDispatchTable():
171 a1a7bc78 Iustin Pop
  """Computes the opcode-to-lu dispatch table.
172 a1a7bc78 Iustin Pop

173 a1a7bc78 Iustin Pop
  """
174 a1a7bc78 Iustin Pop
  return dict((op, getattr(cmdlib, _LUNameForOpName(op.__name__)))
175 a1a7bc78 Iustin Pop
              for op in opcodes.OP_MAPPING.values()
176 a1a7bc78 Iustin Pop
              if op.WITH_LU)
177 a1a7bc78 Iustin Pop
178 a1a7bc78 Iustin Pop
179 07923a3c Michael Hanselmann
def _SetBaseOpParams(src, defcomment, dst):
180 07923a3c Michael Hanselmann
  """Copies basic opcode parameters.
181 07923a3c Michael Hanselmann

182 07923a3c Michael Hanselmann
  @type src: L{opcodes.OpCode}
183 07923a3c Michael Hanselmann
  @param src: Source opcode
184 07923a3c Michael Hanselmann
  @type defcomment: string
185 07923a3c Michael Hanselmann
  @param defcomment: Comment to specify if not already given
186 07923a3c Michael Hanselmann
  @type dst: L{opcodes.OpCode}
187 07923a3c Michael Hanselmann
  @param dst: Destination opcode
188 07923a3c Michael Hanselmann

189 07923a3c Michael Hanselmann
  """
190 07923a3c Michael Hanselmann
  if hasattr(src, "debug_level"):
191 07923a3c Michael Hanselmann
    dst.debug_level = src.debug_level
192 07923a3c Michael Hanselmann
193 07923a3c Michael Hanselmann
  if (getattr(dst, "priority", None) is None and
194 07923a3c Michael Hanselmann
      hasattr(src, "priority")):
195 07923a3c Michael Hanselmann
    dst.priority = src.priority
196 07923a3c Michael Hanselmann
197 07923a3c Michael Hanselmann
  if not getattr(dst, opcodes.COMMENT_ATTR, None):
198 07923a3c Michael Hanselmann
    dst.comment = defcomment
199 07923a3c Michael Hanselmann
200 07923a3c Michael Hanselmann
201 07923a3c Michael Hanselmann
def _ProcessResult(submit_fn, op, result):
202 07923a3c Michael Hanselmann
  """Examines opcode result.
203 07923a3c Michael Hanselmann

204 07923a3c Michael Hanselmann
  If necessary, additional processing on the result is done.
205 07923a3c Michael Hanselmann

206 07923a3c Michael Hanselmann
  """
207 07923a3c Michael Hanselmann
  if isinstance(result, cmdlib.ResultWithJobs):
208 07923a3c Michael Hanselmann
    # Copy basic parameters (e.g. priority)
209 07923a3c Michael Hanselmann
    map(compat.partial(_SetBaseOpParams, op,
210 07923a3c Michael Hanselmann
                       "Submitted by %s" % op.OP_ID),
211 07923a3c Michael Hanselmann
        itertools.chain(*result.jobs))
212 07923a3c Michael Hanselmann
213 07923a3c Michael Hanselmann
    # Submit jobs
214 07923a3c Michael Hanselmann
    job_submission = submit_fn(result.jobs)
215 07923a3c Michael Hanselmann
216 07923a3c Michael Hanselmann
    # Build dictionary
217 07923a3c Michael Hanselmann
    result = result.other
218 07923a3c Michael Hanselmann
219 07923a3c Michael Hanselmann
    assert constants.JOB_IDS_KEY not in result, \
220 07923a3c Michael Hanselmann
      "Key '%s' found in additional return values" % constants.JOB_IDS_KEY
221 07923a3c Michael Hanselmann
222 07923a3c Michael Hanselmann
    result[constants.JOB_IDS_KEY] = job_submission
223 07923a3c Michael Hanselmann
224 07923a3c Michael Hanselmann
  return result
225 07923a3c Michael Hanselmann
226 07923a3c Michael Hanselmann
227 abe362d3 Michael Hanselmann
def _FailingSubmitManyJobs(_):
228 abe362d3 Michael Hanselmann
  """Implementation of L{OpExecCbBase.SubmitManyJobs} to raise an exception.
229 abe362d3 Michael Hanselmann

230 abe362d3 Michael Hanselmann
  """
231 abe362d3 Michael Hanselmann
  raise errors.ProgrammerError("Opcodes processed without callbacks (e.g."
232 abe362d3 Michael Hanselmann
                               " queries) can not submit jobs")
233 abe362d3 Michael Hanselmann
234 abe362d3 Michael Hanselmann
235 949dcb1d Andrea Spadaccini
def _RpcResultsToHooksResults(rpc_results):
236 949dcb1d Andrea Spadaccini
  """Function to convert RPC results to the format expected by HooksMaster.
237 949dcb1d Andrea Spadaccini

238 949dcb1d Andrea Spadaccini
  @type rpc_results: dict(node: L{rpc.RpcResult})
239 949dcb1d Andrea Spadaccini
  @param rpc_results: RPC results
240 949dcb1d Andrea Spadaccini
  @rtype: dict(node: (fail_msg, offline, hooks_results))
241 949dcb1d Andrea Spadaccini
  @return: RPC results unpacked according to the format expected by
242 949dcb1d Andrea Spadaccini
    L({mcpu.HooksMaster}
243 949dcb1d Andrea Spadaccini

244 949dcb1d Andrea Spadaccini
  """
245 949dcb1d Andrea Spadaccini
  return dict((node, (rpc_res.fail_msg, rpc_res.offline, rpc_res.payload))
246 949dcb1d Andrea Spadaccini
              for (node, rpc_res) in rpc_results.items())
247 949dcb1d Andrea Spadaccini
248 949dcb1d Andrea Spadaccini
249 a8083063 Iustin Pop
class Processor(object):
250 a8083063 Iustin Pop
  """Object which runs OpCodes"""
251 a1a7bc78 Iustin Pop
  DISPATCH_TABLE = _ComputeDispatchTable()
252 a8083063 Iustin Pop
253 dc4bdf73 Michael Hanselmann
  def __init__(self, context, ec_id, enable_locks=True):
254 a8083063 Iustin Pop
    """Constructor for Processor
255 a8083063 Iustin Pop

256 adfa97e3 Guido Trotter
    @type context: GanetiContext
257 adfa97e3 Guido Trotter
    @param context: global Ganeti context
258 adfa97e3 Guido Trotter
    @type ec_id: string
259 adfa97e3 Guido Trotter
    @param ec_id: execution context identifier
260 adfa97e3 Guido Trotter

261 a8083063 Iustin Pop
    """
262 1c901d13 Guido Trotter
    self.context = context
263 adfa97e3 Guido Trotter
    self._ec_id = ec_id
264 031a3e57 Michael Hanselmann
    self._cbs = None
265 87b3cb26 Michael Hanselmann
    self.rpc = context.rpc
266 cd46f3b4 Luca Bigliardi
    self.hmclass = HooksMaster
267 dc4bdf73 Michael Hanselmann
    self._enable_locks = enable_locks
268 dc4bdf73 Michael Hanselmann
269 dc4bdf73 Michael Hanselmann
  def _CheckLocksEnabled(self):
270 dc4bdf73 Michael Hanselmann
    """Checks if locking is enabled.
271 dc4bdf73 Michael Hanselmann

272 dc4bdf73 Michael Hanselmann
    @raise errors.ProgrammerError: In case locking is not enabled
273 dc4bdf73 Michael Hanselmann

274 dc4bdf73 Michael Hanselmann
    """
275 dc4bdf73 Michael Hanselmann
    if not self._enable_locks:
276 dc4bdf73 Michael Hanselmann
      raise errors.ProgrammerError("Attempted to use disabled locks")
277 a8083063 Iustin Pop
278 e4e59de8 Michael Hanselmann
  def _AcquireLocks(self, level, names, shared, timeout):
279 211b6132 Michael Hanselmann
    """Acquires locks via the Ganeti lock manager.
280 211b6132 Michael Hanselmann

281 211b6132 Michael Hanselmann
    @type level: int
282 211b6132 Michael Hanselmann
    @param level: Lock level
283 211b6132 Michael Hanselmann
    @type names: list or string
284 211b6132 Michael Hanselmann
    @param names: Lock names
285 211b6132 Michael Hanselmann
    @type shared: bool
286 211b6132 Michael Hanselmann
    @param shared: Whether the locks should be acquired in shared mode
287 211b6132 Michael Hanselmann
    @type timeout: None or float
288 211b6132 Michael Hanselmann
    @param timeout: Timeout for acquiring the locks
289 900df6cd Michael Hanselmann
    @raise LockAcquireTimeout: In case locks couldn't be acquired in specified
290 900df6cd Michael Hanselmann
        amount of time
291 211b6132 Michael Hanselmann

292 211b6132 Michael Hanselmann
    """
293 dc4bdf73 Michael Hanselmann
    self._CheckLocksEnabled()
294 dc4bdf73 Michael Hanselmann
295 acf931b7 Michael Hanselmann
    if self._cbs:
296 e4e59de8 Michael Hanselmann
      priority = self._cbs.CurrentPriority()
297 e4e59de8 Michael Hanselmann
    else:
298 e4e59de8 Michael Hanselmann
      priority = None
299 211b6132 Michael Hanselmann
300 211b6132 Michael Hanselmann
    acquired = self.context.glm.acquire(level, names, shared=shared,
301 f879a9c7 Michael Hanselmann
                                        timeout=timeout, priority=priority)
302 211b6132 Michael Hanselmann
303 900df6cd Michael Hanselmann
    if acquired is None:
304 900df6cd Michael Hanselmann
      raise LockAcquireTimeout()
305 900df6cd Michael Hanselmann
306 211b6132 Michael Hanselmann
    return acquired
307 211b6132 Michael Hanselmann
308 36c381d7 Guido Trotter
  def _ExecLU(self, lu):
309 36c381d7 Guido Trotter
    """Logical Unit execution sequence.
310 36c381d7 Guido Trotter

311 36c381d7 Guido Trotter
    """
312 36c381d7 Guido Trotter
    write_count = self.context.cfg.write_count
313 36c381d7 Guido Trotter
    lu.CheckPrereq()
314 949dcb1d Andrea Spadaccini
315 949dcb1d Andrea Spadaccini
    hm = self.BuildHooksManager(lu)
316 36c381d7 Guido Trotter
    h_results = hm.RunPhase(constants.HOOKS_PHASE_PRE)
317 36c381d7 Guido Trotter
    lu.HooksCallBack(constants.HOOKS_PHASE_PRE, h_results,
318 7b4c1cb9 Michael Hanselmann
                     self.Log, None)
319 20777413 Iustin Pop
320 20777413 Iustin Pop
    if getattr(lu.op, "dry_run", False):
321 20777413 Iustin Pop
      # in this mode, no post-hooks are run, and the config is not
322 20777413 Iustin Pop
      # written (as it might have been modified by another LU, and we
323 20777413 Iustin Pop
      # shouldn't do writeout on behalf of other threads
324 20777413 Iustin Pop
      self.LogInfo("dry-run mode requested, not actually executing"
325 20777413 Iustin Pop
                   " the operation")
326 20777413 Iustin Pop
      return lu.dry_run_result
327 20777413 Iustin Pop
328 abe362d3 Michael Hanselmann
    if self._cbs:
329 abe362d3 Michael Hanselmann
      submit_mj_fn = self._cbs.SubmitManyJobs
330 abe362d3 Michael Hanselmann
    else:
331 abe362d3 Michael Hanselmann
      submit_mj_fn = _FailingSubmitManyJobs
332 abe362d3 Michael Hanselmann
333 36c381d7 Guido Trotter
    try:
334 abe362d3 Michael Hanselmann
      result = _ProcessResult(submit_mj_fn, lu.op, lu.Exec(self.Log))
335 36c381d7 Guido Trotter
      h_results = hm.RunPhase(constants.HOOKS_PHASE_POST)
336 36c381d7 Guido Trotter
      result = lu.HooksCallBack(constants.HOOKS_PHASE_POST, h_results,
337 7b4c1cb9 Michael Hanselmann
                                self.Log, result)
338 36c381d7 Guido Trotter
    finally:
339 36c381d7 Guido Trotter
      # FIXME: This needs locks if not lu_class.REQ_BGL
340 36c381d7 Guido Trotter
      if write_count != self.context.cfg.write_count:
341 36c381d7 Guido Trotter
        hm.RunConfigUpdate()
342 36c381d7 Guido Trotter
343 36c381d7 Guido Trotter
    return result
344 36c381d7 Guido Trotter
345 949dcb1d Andrea Spadaccini
  def BuildHooksManager(self, lu):
346 949dcb1d Andrea Spadaccini
    return self.hmclass.BuildFromLu(lu.rpc.call_hooks_runner, lu)
347 949dcb1d Andrea Spadaccini
348 e4e59de8 Michael Hanselmann
  def _LockAndExecLU(self, lu, level, calc_timeout):
349 68adfdb2 Guido Trotter
    """Execute a Logical Unit, with the needed locks.
350 68adfdb2 Guido Trotter

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

355 68adfdb2 Guido Trotter
    """
356 ca2a79e1 Guido Trotter
    adding_locks = level in lu.add_locks
357 ca2a79e1 Guido Trotter
    acquiring_locks = level in lu.needed_locks
358 8a2941c4 Guido Trotter
    if level not in locking.LEVELS:
359 031a3e57 Michael Hanselmann
      if self._cbs:
360 031a3e57 Michael Hanselmann
        self._cbs.NotifyStart()
361 031a3e57 Michael Hanselmann
362 b183c4a8 Iustin Pop
      try:
363 b183c4a8 Iustin Pop
        result = self._ExecLU(lu)
364 b183c4a8 Iustin Pop
      except AssertionError, err:
365 b183c4a8 Iustin Pop
        # this is a bit ugly, as we don't know from which phase
366 b183c4a8 Iustin Pop
        # (prereq, exec) this comes; but it's better than an exception
367 b183c4a8 Iustin Pop
        # with no information
368 b183c4a8 Iustin Pop
        (_, _, tb) = sys.exc_info()
369 b183c4a8 Iustin Pop
        err_info = traceback.format_tb(tb)
370 b183c4a8 Iustin Pop
        del tb
371 b183c4a8 Iustin Pop
        logging.exception("Detected AssertionError")
372 b183c4a8 Iustin Pop
        raise errors.OpExecError("Internal assertion error: please report"
373 b183c4a8 Iustin Pop
                                 " this as a bug.\nError message: '%s';"
374 b183c4a8 Iustin Pop
                                 " location:\n%s" % (str(err), err_info[-1]))
375 407339d0 Michael Hanselmann
376 ca2a79e1 Guido Trotter
    elif adding_locks and acquiring_locks:
377 ca2a79e1 Guido Trotter
      # We could both acquire and add locks at the same level, but for now we
378 ca2a79e1 Guido Trotter
      # don't need this, so we'll avoid the complicated code needed.
379 407339d0 Michael Hanselmann
      raise NotImplementedError("Can't declare locks to acquire when adding"
380 407339d0 Michael Hanselmann
                                " others")
381 407339d0 Michael Hanselmann
382 ca2a79e1 Guido Trotter
    elif adding_locks or acquiring_locks:
383 dc4bdf73 Michael Hanselmann
      self._CheckLocksEnabled()
384 dc4bdf73 Michael Hanselmann
385 fb8dcb62 Guido Trotter
      lu.DeclareLocks(level)
386 3977a4c1 Guido Trotter
      share = lu.share_locks[level]
387 407339d0 Michael Hanselmann
388 68adfdb2 Guido Trotter
      try:
389 407339d0 Michael Hanselmann
        assert adding_locks ^ acquiring_locks, \
390 407339d0 Michael Hanselmann
          "Locks must be either added or acquired"
391 407339d0 Michael Hanselmann
392 407339d0 Michael Hanselmann
        if acquiring_locks:
393 407339d0 Michael Hanselmann
          # Acquiring locks
394 407339d0 Michael Hanselmann
          needed_locks = lu.needed_locks[level]
395 407339d0 Michael Hanselmann
396 0d5a0b96 Michael Hanselmann
          self._AcquireLocks(level, needed_locks, share,
397 e4e59de8 Michael Hanselmann
                             calc_timeout())
398 407339d0 Michael Hanselmann
        else:
399 407339d0 Michael Hanselmann
          # Adding locks
400 407339d0 Michael Hanselmann
          add_locks = lu.add_locks[level]
401 407339d0 Michael Hanselmann
          lu.remove_locks[level] = add_locks
402 407339d0 Michael Hanselmann
403 407339d0 Michael Hanselmann
          try:
404 407339d0 Michael Hanselmann
            self.context.glm.add(level, add_locks, acquired=1, shared=share)
405 407339d0 Michael Hanselmann
          except errors.LockError:
406 a9d40c65 Iustin Pop
            logging.exception("Detected lock error in level %s for locks"
407 a9d40c65 Iustin Pop
                              " %s, shared=%s", level, add_locks, share)
408 407339d0 Michael Hanselmann
            raise errors.OpPrereqError(
409 a9d40c65 Iustin Pop
              "Couldn't add locks (%s), most likely because of another"
410 a9d40c65 Iustin Pop
              " job who added them first" % add_locks,
411 a9d40c65 Iustin Pop
              errors.ECODE_NOTUNIQUE)
412 407339d0 Michael Hanselmann
413 ca2a79e1 Guido Trotter
        try:
414 e4e59de8 Michael Hanselmann
          result = self._LockAndExecLU(lu, level + 1, calc_timeout)
415 ca2a79e1 Guido Trotter
        finally:
416 ca2a79e1 Guido Trotter
          if level in lu.remove_locks:
417 ca2a79e1 Guido Trotter
            self.context.glm.remove(level, lu.remove_locks[level])
418 68adfdb2 Guido Trotter
      finally:
419 80ee04a4 Guido Trotter
        if self.context.glm.is_owned(level):
420 68adfdb2 Guido Trotter
          self.context.glm.release(level)
421 407339d0 Michael Hanselmann
422 68adfdb2 Guido Trotter
    else:
423 e4e59de8 Michael Hanselmann
      result = self._LockAndExecLU(lu, level + 1, calc_timeout)
424 68adfdb2 Guido Trotter
425 68adfdb2 Guido Trotter
    return result
426 68adfdb2 Guido Trotter
427 e4e59de8 Michael Hanselmann
  def ExecOpCode(self, op, cbs, timeout=None):
428 a8083063 Iustin Pop
    """Execute an opcode.
429 a8083063 Iustin Pop

430 e92376d7 Iustin Pop
    @type op: an OpCode instance
431 e92376d7 Iustin Pop
    @param op: the opcode to be executed
432 031a3e57 Michael Hanselmann
    @type cbs: L{OpExecCbBase}
433 031a3e57 Michael Hanselmann
    @param cbs: Runtime callbacks
434 831bbbc1 Michael Hanselmann
    @type timeout: float or None
435 831bbbc1 Michael Hanselmann
    @param timeout: Maximum time to acquire all locks, None for no timeout
436 831bbbc1 Michael Hanselmann
    @raise LockAcquireTimeout: In case locks couldn't be acquired in specified
437 831bbbc1 Michael Hanselmann
        amount of time
438 a8083063 Iustin Pop

439 a8083063 Iustin Pop
    """
440 a8083063 Iustin Pop
    if not isinstance(op, opcodes.OpCode):
441 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Non-opcode instance passed"
442 c7bb3095 Michael Hanselmann
                                   " to ExecOpcode (%s)" % type(op))
443 a8083063 Iustin Pop
444 831bbbc1 Michael Hanselmann
    lu_class = self.DISPATCH_TABLE.get(op.__class__, None)
445 831bbbc1 Michael Hanselmann
    if lu_class is None:
446 831bbbc1 Michael Hanselmann
      raise errors.OpCodeUnknown("Unknown opcode")
447 831bbbc1 Michael Hanselmann
448 831bbbc1 Michael Hanselmann
    if timeout is None:
449 831bbbc1 Michael Hanselmann
      calc_timeout = lambda: None
450 831bbbc1 Michael Hanselmann
    else:
451 557838c1 René Nussbaumer
      calc_timeout = utils.RunningTimeout(timeout, False).Remaining
452 831bbbc1 Michael Hanselmann
453 031a3e57 Michael Hanselmann
    self._cbs = cbs
454 fe482621 Iustin Pop
    try:
455 dc4bdf73 Michael Hanselmann
      if self._enable_locks:
456 dc4bdf73 Michael Hanselmann
        # Acquire the Big Ganeti Lock exclusively if this LU requires it,
457 dc4bdf73 Michael Hanselmann
        # and in a shared fashion otherwise (to prevent concurrent run with
458 dc4bdf73 Michael Hanselmann
        # an exclusive LU.
459 dc4bdf73 Michael Hanselmann
        self._AcquireLocks(locking.LEVEL_CLUSTER, locking.BGL,
460 e4e59de8 Michael Hanselmann
                            not lu_class.REQ_BGL, calc_timeout())
461 dc4bdf73 Michael Hanselmann
      elif lu_class.REQ_BGL:
462 dc4bdf73 Michael Hanselmann
        raise errors.ProgrammerError("Opcode '%s' requires BGL, but locks are"
463 dc4bdf73 Michael Hanselmann
                                     " disabled" % op.OP_ID)
464 dc4bdf73 Michael Hanselmann
465 831bbbc1 Michael Hanselmann
      try:
466 831bbbc1 Michael Hanselmann
        lu = lu_class(self, op, self.context, self.rpc)
467 831bbbc1 Michael Hanselmann
        lu.ExpandNames()
468 831bbbc1 Michael Hanselmann
        assert lu.needed_locks is not None, "needed_locks not set by LU"
469 407339d0 Michael Hanselmann
470 407339d0 Michael Hanselmann
        try:
471 e4e59de8 Michael Hanselmann
          result = self._LockAndExecLU(lu, locking.LEVEL_INSTANCE, calc_timeout)
472 831bbbc1 Michael Hanselmann
        finally:
473 831bbbc1 Michael Hanselmann
          if self._ec_id:
474 831bbbc1 Michael Hanselmann
            self.context.cfg.DropECReservations(self._ec_id)
475 831bbbc1 Michael Hanselmann
      finally:
476 dc4bdf73 Michael Hanselmann
        # Release BGL if owned
477 dc4bdf73 Michael Hanselmann
        if self.context.glm.is_owned(locking.LEVEL_CLUSTER):
478 dc4bdf73 Michael Hanselmann
          assert self._enable_locks
479 dc4bdf73 Michael Hanselmann
          self.context.glm.release(locking.LEVEL_CLUSTER)
480 04864530 Guido Trotter
    finally:
481 031a3e57 Michael Hanselmann
      self._cbs = None
482 6a4aa7c1 Iustin Pop
483 1ce03fb1 Michael Hanselmann
    resultcheck_fn = op.OP_RESULT
484 1ce03fb1 Michael Hanselmann
    if not (resultcheck_fn is None or resultcheck_fn(result)):
485 1ce03fb1 Michael Hanselmann
      logging.error("Expected opcode result matching %s, got %s",
486 1ce03fb1 Michael Hanselmann
                    resultcheck_fn, result)
487 5401c39d Michael Hanselmann
      raise errors.OpResultError("Opcode result does not match %s: %s" %
488 5401c39d Michael Hanselmann
                                 (resultcheck_fn, utils.Truncate(result, 80)))
489 1ce03fb1 Michael Hanselmann
490 1ce03fb1 Michael Hanselmann
    return result
491 1ce03fb1 Michael Hanselmann
492 7b4c1cb9 Michael Hanselmann
  def Log(self, *args):
493 031a3e57 Michael Hanselmann
    """Forward call to feedback callback function.
494 031a3e57 Michael Hanselmann

495 031a3e57 Michael Hanselmann
    """
496 031a3e57 Michael Hanselmann
    if self._cbs:
497 031a3e57 Michael Hanselmann
      self._cbs.Feedback(*args)
498 031a3e57 Michael Hanselmann
499 0fbbf897 Iustin Pop
  def LogStep(self, current, total, message):
500 0fbbf897 Iustin Pop
    """Log a change in LU execution progress.
501 0fbbf897 Iustin Pop

502 0fbbf897 Iustin Pop
    """
503 a5eb7789 Iustin Pop
    logging.debug("Step %d/%d %s", current, total, message)
504 7b4c1cb9 Michael Hanselmann
    self.Log("STEP %d/%d %s" % (current, total, message))
505 0fbbf897 Iustin Pop
506 c0088fb9 Iustin Pop
  def LogWarning(self, message, *args, **kwargs):
507 0fbbf897 Iustin Pop
    """Log a warning to the logs and the user.
508 0fbbf897 Iustin Pop

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

514 c0088fb9 Iustin Pop
    """
515 c0088fb9 Iustin Pop
    assert not kwargs or (len(kwargs) == 1 and "hint" in kwargs), \
516 c0088fb9 Iustin Pop
           "Invalid keyword arguments for LogWarning (%s)" % str(kwargs)
517 c0088fb9 Iustin Pop
    if args:
518 c0088fb9 Iustin Pop
      message = message % tuple(args)
519 c0088fb9 Iustin Pop
    if message:
520 c0088fb9 Iustin Pop
      logging.warning(message)
521 7b4c1cb9 Michael Hanselmann
      self.Log(" - WARNING: %s" % message)
522 c0088fb9 Iustin Pop
    if "hint" in kwargs:
523 7b4c1cb9 Michael Hanselmann
      self.Log("      Hint: %s" % kwargs["hint"])
524 c0088fb9 Iustin Pop
525 c0088fb9 Iustin Pop
  def LogInfo(self, message, *args):
526 0fbbf897 Iustin Pop
    """Log an informational message to the logs and the user.
527 0fbbf897 Iustin Pop

528 0fbbf897 Iustin Pop
    """
529 c0088fb9 Iustin Pop
    if args:
530 c0088fb9 Iustin Pop
      message = message % tuple(args)
531 a5eb7789 Iustin Pop
    logging.info(message)
532 7b4c1cb9 Michael Hanselmann
    self.Log(" - INFO: %s" % message)
533 0fbbf897 Iustin Pop
534 adfa97e3 Guido Trotter
  def GetECId(self):
535 3ae70d76 Michael Hanselmann
    """Returns the current execution context ID.
536 3ae70d76 Michael Hanselmann

537 3ae70d76 Michael Hanselmann
    """
538 adfa97e3 Guido Trotter
    if not self._ec_id:
539 3ae70d76 Michael Hanselmann
      raise errors.ProgrammerError("Tried to use execution context id when"
540 3ae70d76 Michael Hanselmann
                                   " not set")
541 adfa97e3 Guido Trotter
    return self._ec_id
542 adfa97e3 Guido Trotter
543 a8083063 Iustin Pop
544 a8083063 Iustin Pop
class HooksMaster(object):
545 949dcb1d Andrea Spadaccini
  def __init__(self, opcode, hooks_path, nodes, hooks_execution_fn,
546 5ae4945a Iustin Pop
               hooks_results_adapt_fn, build_env_fn, log_fn, htype=None,
547 5ae4945a Iustin Pop
               cluster_name=None, master_name=None):
548 949dcb1d Andrea Spadaccini
    """Base class for hooks masters.
549 949dcb1d Andrea Spadaccini

550 949dcb1d Andrea Spadaccini
    This class invokes the execution of hooks according to the behaviour
551 949dcb1d Andrea Spadaccini
    specified by its parameters.
552 949dcb1d Andrea Spadaccini

553 949dcb1d Andrea Spadaccini
    @type opcode: string
554 949dcb1d Andrea Spadaccini
    @param opcode: opcode of the operation to which the hooks are tied
555 949dcb1d Andrea Spadaccini
    @type hooks_path: string
556 949dcb1d Andrea Spadaccini
    @param hooks_path: prefix of the hooks directories
557 949dcb1d Andrea Spadaccini
    @type nodes: 2-tuple of lists
558 949dcb1d Andrea Spadaccini
    @param nodes: 2-tuple of lists containing nodes on which pre-hooks must be
559 949dcb1d Andrea Spadaccini
      run and nodes on which post-hooks must be run
560 949dcb1d Andrea Spadaccini
    @type hooks_execution_fn: function that accepts the following parameters:
561 949dcb1d Andrea Spadaccini
      (node_list, hooks_path, phase, environment)
562 949dcb1d Andrea Spadaccini
    @param hooks_execution_fn: function that will execute the hooks; can be
563 949dcb1d Andrea Spadaccini
      None, indicating that no conversion is necessary.
564 949dcb1d Andrea Spadaccini
    @type hooks_results_adapt_fn: function
565 949dcb1d Andrea Spadaccini
    @param hooks_results_adapt_fn: function that will adapt the return value of
566 949dcb1d Andrea Spadaccini
      hooks_execution_fn to the format expected by RunPhase
567 949dcb1d Andrea Spadaccini
    @type build_env_fn: function that returns a dictionary having strings as
568 949dcb1d Andrea Spadaccini
      keys
569 949dcb1d Andrea Spadaccini
    @param build_env_fn: function that builds the environment for the hooks
570 949dcb1d Andrea Spadaccini
    @type log_fn: function that accepts a string
571 949dcb1d Andrea Spadaccini
    @param log_fn: logging function
572 949dcb1d Andrea Spadaccini
    @type htype: string or None
573 949dcb1d Andrea Spadaccini
    @param htype: None or one of L{constants.HTYPE_CLUSTER},
574 949dcb1d Andrea Spadaccini
     L{constants.HTYPE_NODE}, L{constants.HTYPE_INSTANCE}
575 949dcb1d Andrea Spadaccini
    @type cluster_name: string
576 949dcb1d Andrea Spadaccini
    @param cluster_name: name of the cluster
577 949dcb1d Andrea Spadaccini
    @type master_name: string
578 949dcb1d Andrea Spadaccini
    @param master_name: name of the master
579 a8083063 Iustin Pop

580 949dcb1d Andrea Spadaccini
    """
581 949dcb1d Andrea Spadaccini
    self.opcode = opcode
582 949dcb1d Andrea Spadaccini
    self.hooks_path = hooks_path
583 949dcb1d Andrea Spadaccini
    self.hooks_execution_fn = hooks_execution_fn
584 949dcb1d Andrea Spadaccini
    self.hooks_results_adapt_fn = hooks_results_adapt_fn
585 949dcb1d Andrea Spadaccini
    self.build_env_fn = build_env_fn
586 949dcb1d Andrea Spadaccini
    self.log_fn = log_fn
587 949dcb1d Andrea Spadaccini
    self.htype = htype
588 949dcb1d Andrea Spadaccini
    self.cluster_name = cluster_name
589 949dcb1d Andrea Spadaccini
    self.master_name = master_name
590 a8083063 Iustin Pop
591 07e0896f Michael Hanselmann
    self.pre_env = self._BuildEnv(constants.HOOKS_PHASE_PRE)
592 07e0896f Michael Hanselmann
    (self.pre_nodes, self.post_nodes) = nodes
593 a8083063 Iustin Pop
594 dd7f6776 Michael Hanselmann
  def _BuildEnv(self, phase):
595 a8083063 Iustin Pop
    """Compute the environment and the target nodes.
596 a8083063 Iustin Pop

597 a8083063 Iustin Pop
    Based on the opcode and the current node list, this builds the
598 a8083063 Iustin Pop
    environment for the hooks and the target node list for the run.
599 a8083063 Iustin Pop

600 a8083063 Iustin Pop
    """
601 dd7f6776 Michael Hanselmann
    if phase == constants.HOOKS_PHASE_PRE:
602 dd7f6776 Michael Hanselmann
      prefix = "GANETI_"
603 dd7f6776 Michael Hanselmann
    elif phase == constants.HOOKS_PHASE_POST:
604 dd7f6776 Michael Hanselmann
      prefix = "GANETI_POST_"
605 dd7f6776 Michael Hanselmann
    else:
606 dd7f6776 Michael Hanselmann
      raise AssertionError("Unknown phase '%s'" % phase)
607 dd7f6776 Michael Hanselmann
608 dd7f6776 Michael Hanselmann
    env = {}
609 a8083063 Iustin Pop
610 949dcb1d Andrea Spadaccini
    if self.hooks_path is not None:
611 949dcb1d Andrea Spadaccini
      phase_env = self.build_env_fn()
612 949dcb1d Andrea Spadaccini
      if phase_env:
613 949dcb1d Andrea Spadaccini
        assert not compat.any(key.upper().startswith(prefix)
614 949dcb1d Andrea Spadaccini
                              for key in phase_env)
615 dd7f6776 Michael Hanselmann
        env.update(("%s%s" % (prefix, key), value)
616 949dcb1d Andrea Spadaccini
                   for (key, value) in phase_env.items())
617 a8083063 Iustin Pop
618 dd7f6776 Michael Hanselmann
    if phase == constants.HOOKS_PHASE_PRE:
619 dd7f6776 Michael Hanselmann
      assert compat.all((key.startswith("GANETI_") and
620 dd7f6776 Michael Hanselmann
                         not key.startswith("GANETI_POST_"))
621 dd7f6776 Michael Hanselmann
                        for key in env)
622 dd7f6776 Michael Hanselmann
623 dd7f6776 Michael Hanselmann
    elif phase == constants.HOOKS_PHASE_POST:
624 dd7f6776 Michael Hanselmann
      assert compat.all(key.startswith("GANETI_POST_") for key in env)
625 07e0896f Michael Hanselmann
      assert isinstance(self.pre_env, dict)
626 dd7f6776 Michael Hanselmann
627 07e0896f Michael Hanselmann
      # Merge with pre-phase environment
628 07e0896f Michael Hanselmann
      assert not compat.any(key.startswith("GANETI_POST_")
629 07e0896f Michael Hanselmann
                            for key in self.pre_env)
630 07e0896f Michael Hanselmann
      env.update(self.pre_env)
631 dd7f6776 Michael Hanselmann
    else:
632 dd7f6776 Michael Hanselmann
      raise AssertionError("Unknown phase '%s'" % phase)
633 dd7f6776 Michael Hanselmann
634 07e0896f Michael Hanselmann
    return env
635 4167825b Iustin Pop
636 dd7f6776 Michael Hanselmann
  def _RunWrapper(self, node_list, hpath, phase, phase_env):
637 4167825b Iustin Pop
    """Simple wrapper over self.callfn.
638 4167825b Iustin Pop

639 949dcb1d Andrea Spadaccini
    This method fixes the environment before executing the hooks.
640 4167825b Iustin Pop

641 4167825b Iustin Pop
    """
642 dd7f6776 Michael Hanselmann
    env = {
643 fe5ca2bb Andrea Spadaccini
      "PATH": constants.HOOKS_PATH,
644 dd7f6776 Michael Hanselmann
      "GANETI_HOOKS_VERSION": constants.HOOKS_VERSION,
645 949dcb1d Andrea Spadaccini
      "GANETI_OP_CODE": self.opcode,
646 b3a6f851 Michael Hanselmann
      "GANETI_DATA_DIR": pathutils.DATA_DIR,
647 dd7f6776 Michael Hanselmann
      "GANETI_HOOKS_PHASE": phase,
648 dd7f6776 Michael Hanselmann
      "GANETI_HOOKS_PATH": hpath,
649 dd7f6776 Michael Hanselmann
      }
650 dd7f6776 Michael Hanselmann
651 949dcb1d Andrea Spadaccini
    if self.htype:
652 949dcb1d Andrea Spadaccini
      env["GANETI_OBJECT_TYPE"] = self.htype
653 949dcb1d Andrea Spadaccini
654 949dcb1d Andrea Spadaccini
    if self.cluster_name is not None:
655 949dcb1d Andrea Spadaccini
      env["GANETI_CLUSTER"] = self.cluster_name
656 07e0896f Michael Hanselmann
657 949dcb1d Andrea Spadaccini
    if self.master_name is not None:
658 949dcb1d Andrea Spadaccini
      env["GANETI_MASTER"] = self.master_name
659 dd7f6776 Michael Hanselmann
660 dd7f6776 Michael Hanselmann
    if phase_env:
661 af5af644 Andrea Spadaccini
      env = utils.algo.JoinDisjointDicts(env, phase_env)
662 a8083063 Iustin Pop
663 dd7f6776 Michael Hanselmann
    # Convert everything to strings
664 4167825b Iustin Pop
    env = dict([(str(key), str(val)) for key, val in env.iteritems()])
665 a8083063 Iustin Pop
666 dd7f6776 Michael Hanselmann
    assert compat.all(key == "PATH" or key.startswith("GANETI_")
667 ebc75510 Michael Hanselmann
                      for key in env)
668 ebc75510 Michael Hanselmann
669 949dcb1d Andrea Spadaccini
    return self.hooks_execution_fn(node_list, hpath, phase, env)
670 a8083063 Iustin Pop
671 17e82923 Luca Bigliardi
  def RunPhase(self, phase, nodes=None):
672 a8083063 Iustin Pop
    """Run all the scripts for a phase.
673 a8083063 Iustin Pop

674 a8083063 Iustin Pop
    This is the main function of the HookMaster.
675 949dcb1d Andrea Spadaccini
    It executes self.hooks_execution_fn, and after running
676 949dcb1d Andrea Spadaccini
    self.hooks_results_adapt_fn on its results it expects them to be in the form
677 949dcb1d Andrea Spadaccini
    {node_name: (fail_msg, [(script, result, output), ...]}).
678 a8083063 Iustin Pop

679 8dca23a3 Iustin Pop
    @param phase: one of L{constants.HOOKS_PHASE_POST} or
680 8dca23a3 Iustin Pop
        L{constants.HOOKS_PHASE_PRE}; it denotes the hooks phase
681 17e82923 Luca Bigliardi
    @param nodes: overrides the predefined list of nodes for the given phase
682 8dca23a3 Iustin Pop
    @return: the processed results of the hooks multi-node rpc call
683 8dca23a3 Iustin Pop
    @raise errors.HooksFailure: on communication failure to the nodes
684 6ef2dc74 Luca Bigliardi
    @raise errors.HooksAbort: on failure of one of the hooks
685 b07a6922 Guido Trotter

686 a8083063 Iustin Pop
    """
687 07e0896f Michael Hanselmann
    if phase == constants.HOOKS_PHASE_PRE:
688 07e0896f Michael Hanselmann
      if nodes is None:
689 07e0896f Michael Hanselmann
        nodes = self.pre_nodes
690 07e0896f Michael Hanselmann
      env = self.pre_env
691 07e0896f Michael Hanselmann
    elif phase == constants.HOOKS_PHASE_POST:
692 07e0896f Michael Hanselmann
      if nodes is None:
693 b423c513 Michael Hanselmann
        nodes = self.post_nodes
694 07e0896f Michael Hanselmann
      env = self._BuildEnv(phase)
695 07e0896f Michael Hanselmann
    else:
696 07e0896f Michael Hanselmann
      raise AssertionError("Unknown phase '%s'" % phase)
697 0306ff62 Michael Hanselmann
698 0306ff62 Michael Hanselmann
    if not nodes:
699 9a395a76 Iustin Pop
      # empty node list, we should not attempt to run this as either
700 9a395a76 Iustin Pop
      # we're in the cluster init phase and the rpc client part can't
701 9a395a76 Iustin Pop
      # even attempt to run, or this LU doesn't do hooks at all
702 a8083063 Iustin Pop
      return
703 0306ff62 Michael Hanselmann
704 949dcb1d Andrea Spadaccini
    results = self._RunWrapper(nodes, self.hooks_path, phase, env)
705 8c4b9364 Luca Bigliardi
    if not results:
706 8c4b9364 Luca Bigliardi
      msg = "Communication Failure"
707 8c4b9364 Luca Bigliardi
      if phase == constants.HOOKS_PHASE_PRE:
708 8c4b9364 Luca Bigliardi
        raise errors.HooksFailure(msg)
709 8c4b9364 Luca Bigliardi
      else:
710 949dcb1d Andrea Spadaccini
        self.log_fn(msg)
711 640b961e Luca Bigliardi
        return results
712 0306ff62 Michael Hanselmann
713 949dcb1d Andrea Spadaccini
    converted_res = results
714 949dcb1d Andrea Spadaccini
    if self.hooks_results_adapt_fn:
715 949dcb1d Andrea Spadaccini
      converted_res = self.hooks_results_adapt_fn(results)
716 949dcb1d Andrea Spadaccini
717 0306ff62 Michael Hanselmann
    errs = []
718 949dcb1d Andrea Spadaccini
    for node_name, (fail_msg, offline, hooks_results) in converted_res.items():
719 949dcb1d Andrea Spadaccini
      if offline:
720 8c4b9364 Luca Bigliardi
        continue
721 0306ff62 Michael Hanselmann
722 949dcb1d Andrea Spadaccini
      if fail_msg:
723 949dcb1d Andrea Spadaccini
        self.log_fn("Communication failure to node %s: %s", node_name, fail_msg)
724 8c4b9364 Luca Bigliardi
        continue
725 0306ff62 Michael Hanselmann
726 949dcb1d Andrea Spadaccini
      for script, hkr, output in hooks_results:
727 8c4b9364 Luca Bigliardi
        if hkr == constants.HKR_FAIL:
728 8c4b9364 Luca Bigliardi
          if phase == constants.HOOKS_PHASE_PRE:
729 a8083063 Iustin Pop
            errs.append((node_name, script, output))
730 8c4b9364 Luca Bigliardi
          else:
731 8c4b9364 Luca Bigliardi
            if not output:
732 640b961e Luca Bigliardi
              output = "(no output)"
733 949dcb1d Andrea Spadaccini
            self.log_fn("On %s script %s failed, output: %s" %
734 949dcb1d Andrea Spadaccini
                        (node_name, script, output))
735 0306ff62 Michael Hanselmann
736 8c4b9364 Luca Bigliardi
    if errs and phase == constants.HOOKS_PHASE_PRE:
737 8c4b9364 Luca Bigliardi
      raise errors.HooksAbort(errs)
738 0306ff62 Michael Hanselmann
739 b07a6922 Guido Trotter
    return results
740 6a4aa7c1 Iustin Pop
741 6a4aa7c1 Iustin Pop
  def RunConfigUpdate(self):
742 6a4aa7c1 Iustin Pop
    """Run the special configuration update hook
743 6a4aa7c1 Iustin Pop

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

747 6a4aa7c1 Iustin Pop
    """
748 6a4aa7c1 Iustin Pop
    phase = constants.HOOKS_PHASE_POST
749 6a4aa7c1 Iustin Pop
    hpath = constants.HOOKS_NAME_CFGUPDATE
750 949dcb1d Andrea Spadaccini
    nodes = [self.master_name]
751 dd7f6776 Michael Hanselmann
    self._RunWrapper(nodes, hpath, phase, self.pre_env)
752 949dcb1d Andrea Spadaccini
753 949dcb1d Andrea Spadaccini
  @staticmethod
754 949dcb1d Andrea Spadaccini
  def BuildFromLu(hooks_execution_fn, lu):
755 949dcb1d Andrea Spadaccini
    if lu.HPATH is None:
756 949dcb1d Andrea Spadaccini
      nodes = (None, None)
757 949dcb1d Andrea Spadaccini
    else:
758 949dcb1d Andrea Spadaccini
      nodes = map(frozenset, lu.BuildHooksNodes())
759 949dcb1d Andrea Spadaccini
760 949dcb1d Andrea Spadaccini
    master_name = cluster_name = None
761 949dcb1d Andrea Spadaccini
    if lu.cfg:
762 949dcb1d Andrea Spadaccini
      master_name = lu.cfg.GetMasterNode()
763 949dcb1d Andrea Spadaccini
      cluster_name = lu.cfg.GetClusterName()
764 949dcb1d Andrea Spadaccini
765 949dcb1d Andrea Spadaccini
    return HooksMaster(lu.op.OP_ID, lu.HPATH, nodes, hooks_execution_fn,
766 949dcb1d Andrea Spadaccini
                       _RpcResultsToHooksResults, lu.BuildHooksEnv,
767 949dcb1d Andrea Spadaccini
                       lu.LogWarning, lu.HTYPE, cluster_name, master_name)