Statistics
| Branch: | Tag: | Revision:

root / lib / mcpu.py @ 70817cee

History | View | Annotate | Download (23.1 kB)

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

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

29 a8083063 Iustin Pop
"""
30 a8083063 Iustin Pop
31 a5eb7789 Iustin Pop
import logging
32 407339d0 Michael Hanselmann
import random
33 407339d0 Michael Hanselmann
import time
34 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 a8083063 Iustin Pop
44 7c0d6283 Michael Hanselmann
45 a1a7bc78 Iustin Pop
_OP_PREFIX = "Op"
46 a1a7bc78 Iustin Pop
_LU_PREFIX = "LU"
47 a1a7bc78 Iustin Pop
48 a1a7bc78 Iustin Pop
49 831bbbc1 Michael Hanselmann
class LockAcquireTimeout(Exception):
50 831bbbc1 Michael Hanselmann
  """Exception to report timeouts on acquiring locks.
51 407339d0 Michael Hanselmann

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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