Statistics
| Branch: | Tag: | Revision:

root / lib / mcpu.py @ 2cbe9af3

History | View | Annotate | Download (23.2 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 a5eb7789 Iustin Pop
import logging
32 407339d0 Michael Hanselmann
import random
33 407339d0 Michael Hanselmann
import time
34 07923a3c Michael Hanselmann
import itertools
35 a8083063 Iustin Pop
36 a8083063 Iustin Pop
from ganeti import opcodes
37 a8083063 Iustin Pop
from ganeti import constants
38 a8083063 Iustin Pop
from ganeti import errors
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 ebc75510 Michael Hanselmann
from ganeti import compat
43 b3a6f851 Michael Hanselmann
from ganeti import pathutils
44 a8083063 Iustin Pop
45 7c0d6283 Michael Hanselmann
46 a1a7bc78 Iustin Pop
_OP_PREFIX = "Op"
47 a1a7bc78 Iustin Pop
_LU_PREFIX = "LU"
48 a1a7bc78 Iustin Pop
49 a1a7bc78 Iustin Pop
50 831bbbc1 Michael Hanselmann
class LockAcquireTimeout(Exception):
51 831bbbc1 Michael Hanselmann
  """Exception to report timeouts on acquiring locks.
52 407339d0 Michael Hanselmann

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

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

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

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

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

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

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

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

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

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

146 ef2df7d3 Michael Hanselmann
    """
147 ef2df7d3 Michael Hanselmann
148 6a373640 Michael Hanselmann
  def SubmitManyJobs(self, jobs):
149 6a373640 Michael Hanselmann
    """Submits jobs for processing.
150 6a373640 Michael Hanselmann

151 6a373640 Michael Hanselmann
    See L{jqueue.JobQueue.SubmitManyJobs}.
152 6a373640 Michael Hanselmann

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

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

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

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

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

201 07923a3c Michael Hanselmann
  If necessary, additional processing on the result is done.
202 07923a3c Michael Hanselmann

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

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

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

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

253 adfa97e3 Guido Trotter
    @type context: GanetiContext
254 adfa97e3 Guido Trotter
    @param context: global Ganeti context
255 adfa97e3 Guido Trotter
    @type ec_id: string
256 adfa97e3 Guido Trotter
    @param ec_id: execution context identifier
257 adfa97e3 Guido Trotter

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

269 dc4bdf73 Michael Hanselmann
    @raise errors.ProgrammerError: In case locking is not enabled
270 dc4bdf73 Michael Hanselmann

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

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

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

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

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

350 68adfdb2 Guido Trotter
    """
351 ca2a79e1 Guido Trotter
    adding_locks = level in lu.add_locks
352 ca2a79e1 Guido Trotter
    acquiring_locks = level in lu.needed_locks
353 8a2941c4 Guido Trotter
    if level not in locking.LEVELS:
354 031a3e57 Michael Hanselmann
      if self._cbs:
355 031a3e57 Michael Hanselmann
        self._cbs.NotifyStart()
356 031a3e57 Michael Hanselmann
357 8a2941c4 Guido Trotter
      result = self._ExecLU(lu)
358 407339d0 Michael Hanselmann
359 ca2a79e1 Guido Trotter
    elif adding_locks and acquiring_locks:
360 ca2a79e1 Guido Trotter
      # We could both acquire and add locks at the same level, but for now we
361 ca2a79e1 Guido Trotter
      # don't need this, so we'll avoid the complicated code needed.
362 407339d0 Michael Hanselmann
      raise NotImplementedError("Can't declare locks to acquire when adding"
363 407339d0 Michael Hanselmann
                                " others")
364 407339d0 Michael Hanselmann
365 ca2a79e1 Guido Trotter
    elif adding_locks or acquiring_locks:
366 dc4bdf73 Michael Hanselmann
      self._CheckLocksEnabled()
367 dc4bdf73 Michael Hanselmann
368 fb8dcb62 Guido Trotter
      lu.DeclareLocks(level)
369 3977a4c1 Guido Trotter
      share = lu.share_locks[level]
370 407339d0 Michael Hanselmann
371 68adfdb2 Guido Trotter
      try:
372 407339d0 Michael Hanselmann
        assert adding_locks ^ acquiring_locks, \
373 407339d0 Michael Hanselmann
          "Locks must be either added or acquired"
374 407339d0 Michael Hanselmann
375 407339d0 Michael Hanselmann
        if acquiring_locks:
376 407339d0 Michael Hanselmann
          # Acquiring locks
377 407339d0 Michael Hanselmann
          needed_locks = lu.needed_locks[level]
378 407339d0 Michael Hanselmann
379 0d5a0b96 Michael Hanselmann
          self._AcquireLocks(level, needed_locks, share,
380 0d5a0b96 Michael Hanselmann
                             calc_timeout(), priority)
381 407339d0 Michael Hanselmann
        else:
382 407339d0 Michael Hanselmann
          # Adding locks
383 407339d0 Michael Hanselmann
          add_locks = lu.add_locks[level]
384 407339d0 Michael Hanselmann
          lu.remove_locks[level] = add_locks
385 407339d0 Michael Hanselmann
386 407339d0 Michael Hanselmann
          try:
387 407339d0 Michael Hanselmann
            self.context.glm.add(level, add_locks, acquired=1, shared=share)
388 407339d0 Michael Hanselmann
          except errors.LockError:
389 407339d0 Michael Hanselmann
            raise errors.OpPrereqError(
390 407339d0 Michael Hanselmann
              "Couldn't add locks (%s), probably because of a race condition"
391 debac808 Iustin Pop
              " with another job, who added them first" % add_locks,
392 debac808 Iustin Pop
              errors.ECODE_FAULT)
393 407339d0 Michael Hanselmann
394 ca2a79e1 Guido Trotter
        try:
395 f879a9c7 Michael Hanselmann
          result = self._LockAndExecLU(lu, level + 1, calc_timeout, priority)
396 ca2a79e1 Guido Trotter
        finally:
397 ca2a79e1 Guido Trotter
          if level in lu.remove_locks:
398 ca2a79e1 Guido Trotter
            self.context.glm.remove(level, lu.remove_locks[level])
399 68adfdb2 Guido Trotter
      finally:
400 80ee04a4 Guido Trotter
        if self.context.glm.is_owned(level):
401 68adfdb2 Guido Trotter
          self.context.glm.release(level)
402 407339d0 Michael Hanselmann
403 68adfdb2 Guido Trotter
    else:
404 f879a9c7 Michael Hanselmann
      result = self._LockAndExecLU(lu, level + 1, calc_timeout, priority)
405 68adfdb2 Guido Trotter
406 68adfdb2 Guido Trotter
    return result
407 68adfdb2 Guido Trotter
408 f879a9c7 Michael Hanselmann
  def ExecOpCode(self, op, cbs, timeout=None, priority=None):
409 a8083063 Iustin Pop
    """Execute an opcode.
410 a8083063 Iustin Pop

411 e92376d7 Iustin Pop
    @type op: an OpCode instance
412 e92376d7 Iustin Pop
    @param op: the opcode to be executed
413 031a3e57 Michael Hanselmann
    @type cbs: L{OpExecCbBase}
414 031a3e57 Michael Hanselmann
    @param cbs: Runtime callbacks
415 831bbbc1 Michael Hanselmann
    @type timeout: float or None
416 831bbbc1 Michael Hanselmann
    @param timeout: Maximum time to acquire all locks, None for no timeout
417 f879a9c7 Michael Hanselmann
    @type priority: number or None
418 f879a9c7 Michael Hanselmann
    @param priority: Priority for acquiring lock(s)
419 831bbbc1 Michael Hanselmann
    @raise LockAcquireTimeout: In case locks couldn't be acquired in specified
420 831bbbc1 Michael Hanselmann
        amount of time
421 a8083063 Iustin Pop

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

480 031a3e57 Michael Hanselmann
    """
481 031a3e57 Michael Hanselmann
    if self._cbs:
482 031a3e57 Michael Hanselmann
      self._cbs.Feedback(*args)
483 031a3e57 Michael Hanselmann
484 0fbbf897 Iustin Pop
  def LogStep(self, current, total, message):
485 0fbbf897 Iustin Pop
    """Log a change in LU execution progress.
486 0fbbf897 Iustin Pop

487 0fbbf897 Iustin Pop
    """
488 a5eb7789 Iustin Pop
    logging.debug("Step %d/%d %s", current, total, message)
489 7b4c1cb9 Michael Hanselmann
    self.Log("STEP %d/%d %s" % (current, total, message))
490 0fbbf897 Iustin Pop
491 c0088fb9 Iustin Pop
  def LogWarning(self, message, *args, **kwargs):
492 0fbbf897 Iustin Pop
    """Log a warning to the logs and the user.
493 0fbbf897 Iustin Pop

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

499 c0088fb9 Iustin Pop
    """
500 c0088fb9 Iustin Pop
    assert not kwargs or (len(kwargs) == 1 and "hint" in kwargs), \
501 c0088fb9 Iustin Pop
           "Invalid keyword arguments for LogWarning (%s)" % str(kwargs)
502 c0088fb9 Iustin Pop
    if args:
503 c0088fb9 Iustin Pop
      message = message % tuple(args)
504 c0088fb9 Iustin Pop
    if message:
505 c0088fb9 Iustin Pop
      logging.warning(message)
506 7b4c1cb9 Michael Hanselmann
      self.Log(" - WARNING: %s" % message)
507 c0088fb9 Iustin Pop
    if "hint" in kwargs:
508 7b4c1cb9 Michael Hanselmann
      self.Log("      Hint: %s" % kwargs["hint"])
509 c0088fb9 Iustin Pop
510 c0088fb9 Iustin Pop
  def LogInfo(self, message, *args):
511 0fbbf897 Iustin Pop
    """Log an informational message to the logs and the user.
512 0fbbf897 Iustin Pop

513 0fbbf897 Iustin Pop
    """
514 c0088fb9 Iustin Pop
    if args:
515 c0088fb9 Iustin Pop
      message = message % tuple(args)
516 a5eb7789 Iustin Pop
    logging.info(message)
517 7b4c1cb9 Michael Hanselmann
    self.Log(" - INFO: %s" % message)
518 0fbbf897 Iustin Pop
519 adfa97e3 Guido Trotter
  def GetECId(self):
520 3ae70d76 Michael Hanselmann
    """Returns the current execution context ID.
521 3ae70d76 Michael Hanselmann

522 3ae70d76 Michael Hanselmann
    """
523 adfa97e3 Guido Trotter
    if not self._ec_id:
524 3ae70d76 Michael Hanselmann
      raise errors.ProgrammerError("Tried to use execution context id when"
525 3ae70d76 Michael Hanselmann
                                   " not set")
526 adfa97e3 Guido Trotter
    return self._ec_id
527 adfa97e3 Guido Trotter
528 a8083063 Iustin Pop
529 a8083063 Iustin Pop
class HooksMaster(object):
530 949dcb1d Andrea Spadaccini
  def __init__(self, opcode, hooks_path, nodes, hooks_execution_fn,
531 5ae4945a Iustin Pop
               hooks_results_adapt_fn, build_env_fn, log_fn, htype=None,
532 5ae4945a Iustin Pop
               cluster_name=None, master_name=None):
533 949dcb1d Andrea Spadaccini
    """Base class for hooks masters.
534 949dcb1d Andrea Spadaccini

535 949dcb1d Andrea Spadaccini
    This class invokes the execution of hooks according to the behaviour
536 949dcb1d Andrea Spadaccini
    specified by its parameters.
537 949dcb1d Andrea Spadaccini

538 949dcb1d Andrea Spadaccini
    @type opcode: string
539 949dcb1d Andrea Spadaccini
    @param opcode: opcode of the operation to which the hooks are tied
540 949dcb1d Andrea Spadaccini
    @type hooks_path: string
541 949dcb1d Andrea Spadaccini
    @param hooks_path: prefix of the hooks directories
542 949dcb1d Andrea Spadaccini
    @type nodes: 2-tuple of lists
543 949dcb1d Andrea Spadaccini
    @param nodes: 2-tuple of lists containing nodes on which pre-hooks must be
544 949dcb1d Andrea Spadaccini
      run and nodes on which post-hooks must be run
545 949dcb1d Andrea Spadaccini
    @type hooks_execution_fn: function that accepts the following parameters:
546 949dcb1d Andrea Spadaccini
      (node_list, hooks_path, phase, environment)
547 949dcb1d Andrea Spadaccini
    @param hooks_execution_fn: function that will execute the hooks; can be
548 949dcb1d Andrea Spadaccini
      None, indicating that no conversion is necessary.
549 949dcb1d Andrea Spadaccini
    @type hooks_results_adapt_fn: function
550 949dcb1d Andrea Spadaccini
    @param hooks_results_adapt_fn: function that will adapt the return value of
551 949dcb1d Andrea Spadaccini
      hooks_execution_fn to the format expected by RunPhase
552 949dcb1d Andrea Spadaccini
    @type build_env_fn: function that returns a dictionary having strings as
553 949dcb1d Andrea Spadaccini
      keys
554 949dcb1d Andrea Spadaccini
    @param build_env_fn: function that builds the environment for the hooks
555 949dcb1d Andrea Spadaccini
    @type log_fn: function that accepts a string
556 949dcb1d Andrea Spadaccini
    @param log_fn: logging function
557 949dcb1d Andrea Spadaccini
    @type htype: string or None
558 949dcb1d Andrea Spadaccini
    @param htype: None or one of L{constants.HTYPE_CLUSTER},
559 949dcb1d Andrea Spadaccini
     L{constants.HTYPE_NODE}, L{constants.HTYPE_INSTANCE}
560 949dcb1d Andrea Spadaccini
    @type cluster_name: string
561 949dcb1d Andrea Spadaccini
    @param cluster_name: name of the cluster
562 949dcb1d Andrea Spadaccini
    @type master_name: string
563 949dcb1d Andrea Spadaccini
    @param master_name: name of the master
564 a8083063 Iustin Pop

565 949dcb1d Andrea Spadaccini
    """
566 949dcb1d Andrea Spadaccini
    self.opcode = opcode
567 949dcb1d Andrea Spadaccini
    self.hooks_path = hooks_path
568 949dcb1d Andrea Spadaccini
    self.hooks_execution_fn = hooks_execution_fn
569 949dcb1d Andrea Spadaccini
    self.hooks_results_adapt_fn = hooks_results_adapt_fn
570 949dcb1d Andrea Spadaccini
    self.build_env_fn = build_env_fn
571 949dcb1d Andrea Spadaccini
    self.log_fn = log_fn
572 949dcb1d Andrea Spadaccini
    self.htype = htype
573 949dcb1d Andrea Spadaccini
    self.cluster_name = cluster_name
574 949dcb1d Andrea Spadaccini
    self.master_name = master_name
575 a8083063 Iustin Pop
576 07e0896f Michael Hanselmann
    self.pre_env = self._BuildEnv(constants.HOOKS_PHASE_PRE)
577 07e0896f Michael Hanselmann
    (self.pre_nodes, self.post_nodes) = nodes
578 a8083063 Iustin Pop
579 dd7f6776 Michael Hanselmann
  def _BuildEnv(self, phase):
580 a8083063 Iustin Pop
    """Compute the environment and the target nodes.
581 a8083063 Iustin Pop

582 a8083063 Iustin Pop
    Based on the opcode and the current node list, this builds the
583 a8083063 Iustin Pop
    environment for the hooks and the target node list for the run.
584 a8083063 Iustin Pop

585 a8083063 Iustin Pop
    """
586 dd7f6776 Michael Hanselmann
    if phase == constants.HOOKS_PHASE_PRE:
587 dd7f6776 Michael Hanselmann
      prefix = "GANETI_"
588 dd7f6776 Michael Hanselmann
    elif phase == constants.HOOKS_PHASE_POST:
589 dd7f6776 Michael Hanselmann
      prefix = "GANETI_POST_"
590 dd7f6776 Michael Hanselmann
    else:
591 dd7f6776 Michael Hanselmann
      raise AssertionError("Unknown phase '%s'" % phase)
592 dd7f6776 Michael Hanselmann
593 dd7f6776 Michael Hanselmann
    env = {}
594 a8083063 Iustin Pop
595 949dcb1d Andrea Spadaccini
    if self.hooks_path is not None:
596 949dcb1d Andrea Spadaccini
      phase_env = self.build_env_fn()
597 949dcb1d Andrea Spadaccini
      if phase_env:
598 949dcb1d Andrea Spadaccini
        assert not compat.any(key.upper().startswith(prefix)
599 949dcb1d Andrea Spadaccini
                              for key in phase_env)
600 dd7f6776 Michael Hanselmann
        env.update(("%s%s" % (prefix, key), value)
601 949dcb1d Andrea Spadaccini
                   for (key, value) in phase_env.items())
602 a8083063 Iustin Pop
603 dd7f6776 Michael Hanselmann
    if phase == constants.HOOKS_PHASE_PRE:
604 dd7f6776 Michael Hanselmann
      assert compat.all((key.startswith("GANETI_") and
605 dd7f6776 Michael Hanselmann
                         not key.startswith("GANETI_POST_"))
606 dd7f6776 Michael Hanselmann
                        for key in env)
607 dd7f6776 Michael Hanselmann
608 dd7f6776 Michael Hanselmann
    elif phase == constants.HOOKS_PHASE_POST:
609 dd7f6776 Michael Hanselmann
      assert compat.all(key.startswith("GANETI_POST_") for key in env)
610 07e0896f Michael Hanselmann
      assert isinstance(self.pre_env, dict)
611 dd7f6776 Michael Hanselmann
612 07e0896f Michael Hanselmann
      # Merge with pre-phase environment
613 07e0896f Michael Hanselmann
      assert not compat.any(key.startswith("GANETI_POST_")
614 07e0896f Michael Hanselmann
                            for key in self.pre_env)
615 07e0896f Michael Hanselmann
      env.update(self.pre_env)
616 dd7f6776 Michael Hanselmann
    else:
617 dd7f6776 Michael Hanselmann
      raise AssertionError("Unknown phase '%s'" % phase)
618 dd7f6776 Michael Hanselmann
619 07e0896f Michael Hanselmann
    return env
620 4167825b Iustin Pop
621 dd7f6776 Michael Hanselmann
  def _RunWrapper(self, node_list, hpath, phase, phase_env):
622 4167825b Iustin Pop
    """Simple wrapper over self.callfn.
623 4167825b Iustin Pop

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

626 4167825b Iustin Pop
    """
627 dd7f6776 Michael Hanselmann
    env = {
628 fe5ca2bb Andrea Spadaccini
      "PATH": constants.HOOKS_PATH,
629 dd7f6776 Michael Hanselmann
      "GANETI_HOOKS_VERSION": constants.HOOKS_VERSION,
630 949dcb1d Andrea Spadaccini
      "GANETI_OP_CODE": self.opcode,
631 b3a6f851 Michael Hanselmann
      "GANETI_DATA_DIR": pathutils.DATA_DIR,
632 dd7f6776 Michael Hanselmann
      "GANETI_HOOKS_PHASE": phase,
633 dd7f6776 Michael Hanselmann
      "GANETI_HOOKS_PATH": hpath,
634 dd7f6776 Michael Hanselmann
      }
635 dd7f6776 Michael Hanselmann
636 949dcb1d Andrea Spadaccini
    if self.htype:
637 949dcb1d Andrea Spadaccini
      env["GANETI_OBJECT_TYPE"] = self.htype
638 949dcb1d Andrea Spadaccini
639 949dcb1d Andrea Spadaccini
    if self.cluster_name is not None:
640 949dcb1d Andrea Spadaccini
      env["GANETI_CLUSTER"] = self.cluster_name
641 07e0896f Michael Hanselmann
642 949dcb1d Andrea Spadaccini
    if self.master_name is not None:
643 949dcb1d Andrea Spadaccini
      env["GANETI_MASTER"] = self.master_name
644 dd7f6776 Michael Hanselmann
645 dd7f6776 Michael Hanselmann
    if phase_env:
646 af5af644 Andrea Spadaccini
      env = utils.algo.JoinDisjointDicts(env, phase_env)
647 a8083063 Iustin Pop
648 dd7f6776 Michael Hanselmann
    # Convert everything to strings
649 4167825b Iustin Pop
    env = dict([(str(key), str(val)) for key, val in env.iteritems()])
650 a8083063 Iustin Pop
651 dd7f6776 Michael Hanselmann
    assert compat.all(key == "PATH" or key.startswith("GANETI_")
652 ebc75510 Michael Hanselmann
                      for key in env)
653 ebc75510 Michael Hanselmann
654 949dcb1d Andrea Spadaccini
    return self.hooks_execution_fn(node_list, hpath, phase, env)
655 a8083063 Iustin Pop
656 17e82923 Luca Bigliardi
  def RunPhase(self, phase, nodes=None):
657 a8083063 Iustin Pop
    """Run all the scripts for a phase.
658 a8083063 Iustin Pop

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

664 8dca23a3 Iustin Pop
    @param phase: one of L{constants.HOOKS_PHASE_POST} or
665 8dca23a3 Iustin Pop
        L{constants.HOOKS_PHASE_PRE}; it denotes the hooks phase
666 17e82923 Luca Bigliardi
    @param nodes: overrides the predefined list of nodes for the given phase
667 8dca23a3 Iustin Pop
    @return: the processed results of the hooks multi-node rpc call
668 8dca23a3 Iustin Pop
    @raise errors.HooksFailure: on communication failure to the nodes
669 6ef2dc74 Luca Bigliardi
    @raise errors.HooksAbort: on failure of one of the hooks
670 b07a6922 Guido Trotter

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

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

732 6a4aa7c1 Iustin Pop
    """
733 6a4aa7c1 Iustin Pop
    phase = constants.HOOKS_PHASE_POST
734 6a4aa7c1 Iustin Pop
    hpath = constants.HOOKS_NAME_CFGUPDATE
735 949dcb1d Andrea Spadaccini
    nodes = [self.master_name]
736 dd7f6776 Michael Hanselmann
    self._RunWrapper(nodes, hpath, phase, self.pre_env)
737 949dcb1d Andrea Spadaccini
738 949dcb1d Andrea Spadaccini
  @staticmethod
739 949dcb1d Andrea Spadaccini
  def BuildFromLu(hooks_execution_fn, lu):
740 949dcb1d Andrea Spadaccini
    if lu.HPATH is None:
741 949dcb1d Andrea Spadaccini
      nodes = (None, None)
742 949dcb1d Andrea Spadaccini
    else:
743 949dcb1d Andrea Spadaccini
      nodes = map(frozenset, lu.BuildHooksNodes())
744 949dcb1d Andrea Spadaccini
745 949dcb1d Andrea Spadaccini
    master_name = cluster_name = None
746 949dcb1d Andrea Spadaccini
    if lu.cfg:
747 949dcb1d Andrea Spadaccini
      master_name = lu.cfg.GetMasterNode()
748 949dcb1d Andrea Spadaccini
      cluster_name = lu.cfg.GetClusterName()
749 949dcb1d Andrea Spadaccini
750 949dcb1d Andrea Spadaccini
    return HooksMaster(lu.op.OP_ID, lu.HPATH, nodes, hooks_execution_fn,
751 949dcb1d Andrea Spadaccini
                       _RpcResultsToHooksResults, lu.BuildHooksEnv,
752 949dcb1d Andrea Spadaccini
                       lu.LogWarning, lu.HTYPE, cluster_name, master_name)