Statistics
| Branch: | Tag: | Revision:

root / lib / mcpu.py @ 966e1580

History | View | Annotate | Download (26.1 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 e9a81214 Michael Hanselmann
#: LU classes which don't need to acquire the node allocation lock
52 e9a81214 Michael Hanselmann
#: (L{locking.NAL}) when they acquire all node or node resource locks
53 b8028dcf Michael Hanselmann
_NODE_ALLOC_WHITELIST = frozenset([])
54 e9a81214 Michael Hanselmann
55 e9a81214 Michael Hanselmann
#: LU classes which don't need to acquire the node allocation lock
56 e9a81214 Michael Hanselmann
#: (L{locking.NAL}) in the same mode (shared/exclusive) as the node
57 e9a81214 Michael Hanselmann
#: or node resource locks
58 b8028dcf Michael Hanselmann
_NODE_ALLOC_MODE_WHITELIST = compat.UniqueFrozenset([
59 e9a81214 Michael Hanselmann
  cmdlib.LUBackupExport,
60 e9a81214 Michael Hanselmann
  cmdlib.LUBackupRemove,
61 e9a81214 Michael Hanselmann
  cmdlib.LUOobCommand,
62 e9a81214 Michael Hanselmann
  ])
63 e9a81214 Michael Hanselmann
64 a1a7bc78 Iustin Pop
65 831bbbc1 Michael Hanselmann
class LockAcquireTimeout(Exception):
66 831bbbc1 Michael Hanselmann
  """Exception to report timeouts on acquiring locks.
67 407339d0 Michael Hanselmann

68 407339d0 Michael Hanselmann
  """
69 407339d0 Michael Hanselmann
70 407339d0 Michael Hanselmann
71 e3200b18 Michael Hanselmann
def _CalculateLockAttemptTimeouts():
72 e3200b18 Michael Hanselmann
  """Calculate timeouts for lock attempts.
73 e3200b18 Michael Hanselmann

74 e3200b18 Michael Hanselmann
  """
75 d385a174 Iustin Pop
  result = [constants.LOCK_ATTEMPTS_MINWAIT]
76 d385a174 Iustin Pop
  running_sum = result[0]
77 e3200b18 Michael Hanselmann
78 d385a174 Iustin Pop
  # Wait for a total of at least LOCK_ATTEMPTS_TIMEOUT before doing a
79 d385a174 Iustin Pop
  # blocking acquire
80 d385a174 Iustin Pop
  while running_sum < constants.LOCK_ATTEMPTS_TIMEOUT:
81 e3200b18 Michael Hanselmann
    timeout = (result[-1] * 1.05) ** 1.25
82 e3200b18 Michael Hanselmann
83 d385a174 Iustin Pop
    # Cap max timeout. This gives other jobs a chance to run even if
84 d385a174 Iustin Pop
    # we're still trying to get our locks, before finally moving to a
85 d385a174 Iustin Pop
    # blocking acquire.
86 d385a174 Iustin Pop
    timeout = min(timeout, constants.LOCK_ATTEMPTS_MAXWAIT)
87 d385a174 Iustin Pop
    # And also cap the lower boundary for safety
88 d385a174 Iustin Pop
    timeout = max(timeout, constants.LOCK_ATTEMPTS_MINWAIT)
89 e3200b18 Michael Hanselmann
90 e3200b18 Michael Hanselmann
    result.append(timeout)
91 d385a174 Iustin Pop
    running_sum += timeout
92 e3200b18 Michael Hanselmann
93 e3200b18 Michael Hanselmann
  return result
94 e3200b18 Michael Hanselmann
95 e3200b18 Michael Hanselmann
96 a7770f03 Michael Hanselmann
class LockAttemptTimeoutStrategy(object):
97 407339d0 Michael Hanselmann
  """Class with lock acquire timeout strategy.
98 407339d0 Michael Hanselmann

99 407339d0 Michael Hanselmann
  """
100 407339d0 Michael Hanselmann
  __slots__ = [
101 a7770f03 Michael Hanselmann
    "_timeouts",
102 407339d0 Michael Hanselmann
    "_random_fn",
103 e3200b18 Michael Hanselmann
    "_time_fn",
104 407339d0 Michael Hanselmann
    ]
105 407339d0 Michael Hanselmann
106 e3200b18 Michael Hanselmann
  _TIMEOUT_PER_ATTEMPT = _CalculateLockAttemptTimeouts()
107 407339d0 Michael Hanselmann
108 a7770f03 Michael Hanselmann
  def __init__(self, _time_fn=time.time, _random_fn=random.random):
109 407339d0 Michael Hanselmann
    """Initializes this class.
110 407339d0 Michael Hanselmann

111 e3200b18 Michael Hanselmann
    @param _time_fn: Time function for unittests
112 407339d0 Michael Hanselmann
    @param _random_fn: Random number generator for unittests
113 407339d0 Michael Hanselmann

114 407339d0 Michael Hanselmann
    """
115 407339d0 Michael Hanselmann
    object.__init__(self)
116 407339d0 Michael Hanselmann
117 a7770f03 Michael Hanselmann
    self._timeouts = iter(self._TIMEOUT_PER_ATTEMPT)
118 e3200b18 Michael Hanselmann
    self._time_fn = _time_fn
119 e3200b18 Michael Hanselmann
    self._random_fn = _random_fn
120 e3200b18 Michael Hanselmann
121 407339d0 Michael Hanselmann
  def NextAttempt(self):
122 a7770f03 Michael Hanselmann
    """Returns the timeout for the next attempt.
123 407339d0 Michael Hanselmann

124 407339d0 Michael Hanselmann
    """
125 a7770f03 Michael Hanselmann
    try:
126 a7770f03 Michael Hanselmann
      timeout = self._timeouts.next()
127 a7770f03 Michael Hanselmann
    except StopIteration:
128 a7770f03 Michael Hanselmann
      # No more timeouts, do blocking acquire
129 a7770f03 Michael Hanselmann
      timeout = None
130 407339d0 Michael Hanselmann
131 a6db1af2 Michael Hanselmann
    if timeout is not None:
132 a6db1af2 Michael Hanselmann
      # Add a small variation (-/+ 5%) to timeout. This helps in situations
133 a6db1af2 Michael Hanselmann
      # where two or more jobs are fighting for the same lock(s).
134 a6db1af2 Michael Hanselmann
      variation_range = timeout * 0.1
135 a6db1af2 Michael Hanselmann
      timeout += ((self._random_fn() * variation_range) -
136 a6db1af2 Michael Hanselmann
                  (variation_range * 0.5))
137 407339d0 Michael Hanselmann
138 a6db1af2 Michael Hanselmann
    return timeout
139 407339d0 Michael Hanselmann
140 407339d0 Michael Hanselmann
141 b459a848 Andrea Spadaccini
class OpExecCbBase: # pylint: disable=W0232
142 031a3e57 Michael Hanselmann
  """Base class for OpCode execution callbacks.
143 031a3e57 Michael Hanselmann

144 031a3e57 Michael Hanselmann
  """
145 031a3e57 Michael Hanselmann
  def NotifyStart(self):
146 031a3e57 Michael Hanselmann
    """Called when we are about to execute the LU.
147 031a3e57 Michael Hanselmann

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

151 031a3e57 Michael Hanselmann
    """
152 031a3e57 Michael Hanselmann
153 031a3e57 Michael Hanselmann
  def Feedback(self, *args):
154 031a3e57 Michael Hanselmann
    """Sends feedback from the LU code to the end-user.
155 031a3e57 Michael Hanselmann

156 031a3e57 Michael Hanselmann
    """
157 031a3e57 Michael Hanselmann
158 e4e59de8 Michael Hanselmann
  def CurrentPriority(self): # pylint: disable=R0201
159 e4e59de8 Michael Hanselmann
    """Returns current priority or C{None}.
160 ef2df7d3 Michael Hanselmann

161 ef2df7d3 Michael Hanselmann
    """
162 e4e59de8 Michael Hanselmann
    return None
163 ef2df7d3 Michael Hanselmann
164 6a373640 Michael Hanselmann
  def SubmitManyJobs(self, jobs):
165 6a373640 Michael Hanselmann
    """Submits jobs for processing.
166 6a373640 Michael Hanselmann

167 6a373640 Michael Hanselmann
    See L{jqueue.JobQueue.SubmitManyJobs}.
168 6a373640 Michael Hanselmann

169 6a373640 Michael Hanselmann
    """
170 6a373640 Michael Hanselmann
    raise NotImplementedError
171 6a373640 Michael Hanselmann
172 031a3e57 Michael Hanselmann
173 a1a7bc78 Iustin Pop
def _LUNameForOpName(opname):
174 a1a7bc78 Iustin Pop
  """Computes the LU name for a given OpCode name.
175 a1a7bc78 Iustin Pop

176 a1a7bc78 Iustin Pop
  """
177 a1a7bc78 Iustin Pop
  assert opname.startswith(_OP_PREFIX), \
178 a1a7bc78 Iustin Pop
      "Invalid OpCode name, doesn't start with %s: %s" % (_OP_PREFIX, opname)
179 a1a7bc78 Iustin Pop
180 a1a7bc78 Iustin Pop
  return _LU_PREFIX + opname[len(_OP_PREFIX):]
181 a1a7bc78 Iustin Pop
182 a1a7bc78 Iustin Pop
183 a1a7bc78 Iustin Pop
def _ComputeDispatchTable():
184 a1a7bc78 Iustin Pop
  """Computes the opcode-to-lu dispatch table.
185 a1a7bc78 Iustin Pop

186 a1a7bc78 Iustin Pop
  """
187 a1a7bc78 Iustin Pop
  return dict((op, getattr(cmdlib, _LUNameForOpName(op.__name__)))
188 a1a7bc78 Iustin Pop
              for op in opcodes.OP_MAPPING.values()
189 a1a7bc78 Iustin Pop
              if op.WITH_LU)
190 a1a7bc78 Iustin Pop
191 a1a7bc78 Iustin Pop
192 07923a3c Michael Hanselmann
def _SetBaseOpParams(src, defcomment, dst):
193 07923a3c Michael Hanselmann
  """Copies basic opcode parameters.
194 07923a3c Michael Hanselmann

195 07923a3c Michael Hanselmann
  @type src: L{opcodes.OpCode}
196 07923a3c Michael Hanselmann
  @param src: Source opcode
197 07923a3c Michael Hanselmann
  @type defcomment: string
198 07923a3c Michael Hanselmann
  @param defcomment: Comment to specify if not already given
199 07923a3c Michael Hanselmann
  @type dst: L{opcodes.OpCode}
200 07923a3c Michael Hanselmann
  @param dst: Destination opcode
201 07923a3c Michael Hanselmann

202 07923a3c Michael Hanselmann
  """
203 07923a3c Michael Hanselmann
  if hasattr(src, "debug_level"):
204 07923a3c Michael Hanselmann
    dst.debug_level = src.debug_level
205 07923a3c Michael Hanselmann
206 07923a3c Michael Hanselmann
  if (getattr(dst, "priority", None) is None and
207 07923a3c Michael Hanselmann
      hasattr(src, "priority")):
208 07923a3c Michael Hanselmann
    dst.priority = src.priority
209 07923a3c Michael Hanselmann
210 07923a3c Michael Hanselmann
  if not getattr(dst, opcodes.COMMENT_ATTR, None):
211 07923a3c Michael Hanselmann
    dst.comment = defcomment
212 07923a3c Michael Hanselmann
213 07923a3c Michael Hanselmann
214 07923a3c Michael Hanselmann
def _ProcessResult(submit_fn, op, result):
215 07923a3c Michael Hanselmann
  """Examines opcode result.
216 07923a3c Michael Hanselmann

217 07923a3c Michael Hanselmann
  If necessary, additional processing on the result is done.
218 07923a3c Michael Hanselmann

219 07923a3c Michael Hanselmann
  """
220 07923a3c Michael Hanselmann
  if isinstance(result, cmdlib.ResultWithJobs):
221 07923a3c Michael Hanselmann
    # Copy basic parameters (e.g. priority)
222 07923a3c Michael Hanselmann
    map(compat.partial(_SetBaseOpParams, op,
223 07923a3c Michael Hanselmann
                       "Submitted by %s" % op.OP_ID),
224 07923a3c Michael Hanselmann
        itertools.chain(*result.jobs))
225 07923a3c Michael Hanselmann
226 07923a3c Michael Hanselmann
    # Submit jobs
227 07923a3c Michael Hanselmann
    job_submission = submit_fn(result.jobs)
228 07923a3c Michael Hanselmann
229 07923a3c Michael Hanselmann
    # Build dictionary
230 07923a3c Michael Hanselmann
    result = result.other
231 07923a3c Michael Hanselmann
232 07923a3c Michael Hanselmann
    assert constants.JOB_IDS_KEY not in result, \
233 07923a3c Michael Hanselmann
      "Key '%s' found in additional return values" % constants.JOB_IDS_KEY
234 07923a3c Michael Hanselmann
235 07923a3c Michael Hanselmann
    result[constants.JOB_IDS_KEY] = job_submission
236 07923a3c Michael Hanselmann
237 07923a3c Michael Hanselmann
  return result
238 07923a3c Michael Hanselmann
239 07923a3c Michael Hanselmann
240 abe362d3 Michael Hanselmann
def _FailingSubmitManyJobs(_):
241 abe362d3 Michael Hanselmann
  """Implementation of L{OpExecCbBase.SubmitManyJobs} to raise an exception.
242 abe362d3 Michael Hanselmann

243 abe362d3 Michael Hanselmann
  """
244 abe362d3 Michael Hanselmann
  raise errors.ProgrammerError("Opcodes processed without callbacks (e.g."
245 abe362d3 Michael Hanselmann
                               " queries) can not submit jobs")
246 abe362d3 Michael Hanselmann
247 abe362d3 Michael Hanselmann
248 949dcb1d Andrea Spadaccini
def _RpcResultsToHooksResults(rpc_results):
249 949dcb1d Andrea Spadaccini
  """Function to convert RPC results to the format expected by HooksMaster.
250 949dcb1d Andrea Spadaccini

251 949dcb1d Andrea Spadaccini
  @type rpc_results: dict(node: L{rpc.RpcResult})
252 949dcb1d Andrea Spadaccini
  @param rpc_results: RPC results
253 949dcb1d Andrea Spadaccini
  @rtype: dict(node: (fail_msg, offline, hooks_results))
254 949dcb1d Andrea Spadaccini
  @return: RPC results unpacked according to the format expected by
255 949dcb1d Andrea Spadaccini
    L({mcpu.HooksMaster}
256 949dcb1d Andrea Spadaccini

257 949dcb1d Andrea Spadaccini
  """
258 949dcb1d Andrea Spadaccini
  return dict((node, (rpc_res.fail_msg, rpc_res.offline, rpc_res.payload))
259 949dcb1d Andrea Spadaccini
              for (node, rpc_res) in rpc_results.items())
260 949dcb1d Andrea Spadaccini
261 949dcb1d Andrea Spadaccini
262 e9a81214 Michael Hanselmann
def _VerifyLocks(lu, glm, _mode_whitelist=_NODE_ALLOC_MODE_WHITELIST,
263 e9a81214 Michael Hanselmann
                 _nal_whitelist=_NODE_ALLOC_WHITELIST):
264 e9a81214 Michael Hanselmann
  """Performs consistency checks on locks acquired by a logical unit.
265 e9a81214 Michael Hanselmann

266 e9a81214 Michael Hanselmann
  @type lu: L{cmdlib.LogicalUnit}
267 e9a81214 Michael Hanselmann
  @param lu: Logical unit instance
268 e9a81214 Michael Hanselmann
  @type glm: L{locking.GanetiLockManager}
269 e9a81214 Michael Hanselmann
  @param glm: Lock manager
270 e9a81214 Michael Hanselmann

271 e9a81214 Michael Hanselmann
  """
272 e9a81214 Michael Hanselmann
  if not __debug__:
273 e9a81214 Michael Hanselmann
    return
274 e9a81214 Michael Hanselmann
275 e9a81214 Michael Hanselmann
  have_nal = glm.check_owned(locking.LEVEL_NODE_ALLOC, locking.NAL)
276 e9a81214 Michael Hanselmann
277 e9a81214 Michael Hanselmann
  for level in [locking.LEVEL_NODE, locking.LEVEL_NODE_RES]:
278 e9a81214 Michael Hanselmann
    # TODO: Verify using actual lock mode, not using LU variables
279 e9a81214 Michael Hanselmann
    if level in lu.needed_locks:
280 e9a81214 Michael Hanselmann
      share_node_alloc = lu.share_locks[locking.LEVEL_NODE_ALLOC]
281 e9a81214 Michael Hanselmann
      share_level = lu.share_locks[level]
282 e9a81214 Michael Hanselmann
283 e9a81214 Michael Hanselmann
      if lu.__class__ in _mode_whitelist:
284 e9a81214 Michael Hanselmann
        assert share_node_alloc != share_level, \
285 e9a81214 Michael Hanselmann
          "LU is whitelisted to use different modes for node allocation lock"
286 e9a81214 Michael Hanselmann
      else:
287 e9a81214 Michael Hanselmann
        assert bool(share_node_alloc) == bool(share_level), \
288 e9a81214 Michael Hanselmann
          ("Node allocation lock must be acquired using the same mode as nodes"
289 e9a81214 Michael Hanselmann
           " and node resources")
290 e9a81214 Michael Hanselmann
291 e9a81214 Michael Hanselmann
      if lu.__class__ in _nal_whitelist:
292 e9a81214 Michael Hanselmann
        assert not have_nal, \
293 e9a81214 Michael Hanselmann
          "LU is whitelisted for not acquiring the node allocation lock"
294 e9a81214 Michael Hanselmann
      elif lu.needed_locks[level] == locking.ALL_SET or glm.owning_all(level):
295 e9a81214 Michael Hanselmann
        assert have_nal, \
296 e9a81214 Michael Hanselmann
          ("Node allocation lock must be used if an LU acquires all nodes"
297 e9a81214 Michael Hanselmann
           " or node resources")
298 e9a81214 Michael Hanselmann
299 e9a81214 Michael Hanselmann
300 a8083063 Iustin Pop
class Processor(object):
301 a8083063 Iustin Pop
  """Object which runs OpCodes"""
302 a1a7bc78 Iustin Pop
  DISPATCH_TABLE = _ComputeDispatchTable()
303 a8083063 Iustin Pop
304 dc4bdf73 Michael Hanselmann
  def __init__(self, context, ec_id, enable_locks=True):
305 a8083063 Iustin Pop
    """Constructor for Processor
306 a8083063 Iustin Pop

307 adfa97e3 Guido Trotter
    @type context: GanetiContext
308 adfa97e3 Guido Trotter
    @param context: global Ganeti context
309 adfa97e3 Guido Trotter
    @type ec_id: string
310 adfa97e3 Guido Trotter
    @param ec_id: execution context identifier
311 adfa97e3 Guido Trotter

312 a8083063 Iustin Pop
    """
313 1c901d13 Guido Trotter
    self.context = context
314 adfa97e3 Guido Trotter
    self._ec_id = ec_id
315 031a3e57 Michael Hanselmann
    self._cbs = None
316 87b3cb26 Michael Hanselmann
    self.rpc = context.rpc
317 cd46f3b4 Luca Bigliardi
    self.hmclass = HooksMaster
318 dc4bdf73 Michael Hanselmann
    self._enable_locks = enable_locks
319 dc4bdf73 Michael Hanselmann
320 dc4bdf73 Michael Hanselmann
  def _CheckLocksEnabled(self):
321 dc4bdf73 Michael Hanselmann
    """Checks if locking is enabled.
322 dc4bdf73 Michael Hanselmann

323 dc4bdf73 Michael Hanselmann
    @raise errors.ProgrammerError: In case locking is not enabled
324 dc4bdf73 Michael Hanselmann

325 dc4bdf73 Michael Hanselmann
    """
326 dc4bdf73 Michael Hanselmann
    if not self._enable_locks:
327 dc4bdf73 Michael Hanselmann
      raise errors.ProgrammerError("Attempted to use disabled locks")
328 a8083063 Iustin Pop
329 686d24f0 Michael Hanselmann
  def _AcquireLocks(self, level, names, shared, opportunistic, timeout):
330 211b6132 Michael Hanselmann
    """Acquires locks via the Ganeti lock manager.
331 211b6132 Michael Hanselmann

332 211b6132 Michael Hanselmann
    @type level: int
333 211b6132 Michael Hanselmann
    @param level: Lock level
334 211b6132 Michael Hanselmann
    @type names: list or string
335 211b6132 Michael Hanselmann
    @param names: Lock names
336 211b6132 Michael Hanselmann
    @type shared: bool
337 211b6132 Michael Hanselmann
    @param shared: Whether the locks should be acquired in shared mode
338 686d24f0 Michael Hanselmann
    @type opportunistic: bool
339 686d24f0 Michael Hanselmann
    @param opportunistic: Whether to acquire opportunistically
340 211b6132 Michael Hanselmann
    @type timeout: None or float
341 211b6132 Michael Hanselmann
    @param timeout: Timeout for acquiring the locks
342 900df6cd Michael Hanselmann
    @raise LockAcquireTimeout: In case locks couldn't be acquired in specified
343 900df6cd Michael Hanselmann
        amount of time
344 211b6132 Michael Hanselmann

345 211b6132 Michael Hanselmann
    """
346 dc4bdf73 Michael Hanselmann
    self._CheckLocksEnabled()
347 dc4bdf73 Michael Hanselmann
348 acf931b7 Michael Hanselmann
    if self._cbs:
349 e4e59de8 Michael Hanselmann
      priority = self._cbs.CurrentPriority()
350 e4e59de8 Michael Hanselmann
    else:
351 e4e59de8 Michael Hanselmann
      priority = None
352 211b6132 Michael Hanselmann
353 211b6132 Michael Hanselmann
    acquired = self.context.glm.acquire(level, names, shared=shared,
354 686d24f0 Michael Hanselmann
                                        timeout=timeout, priority=priority,
355 686d24f0 Michael Hanselmann
                                        opportunistic=opportunistic)
356 211b6132 Michael Hanselmann
357 900df6cd Michael Hanselmann
    if acquired is None:
358 900df6cd Michael Hanselmann
      raise LockAcquireTimeout()
359 900df6cd Michael Hanselmann
360 211b6132 Michael Hanselmann
    return acquired
361 211b6132 Michael Hanselmann
362 36c381d7 Guido Trotter
  def _ExecLU(self, lu):
363 36c381d7 Guido Trotter
    """Logical Unit execution sequence.
364 36c381d7 Guido Trotter

365 36c381d7 Guido Trotter
    """
366 36c381d7 Guido Trotter
    write_count = self.context.cfg.write_count
367 36c381d7 Guido Trotter
    lu.CheckPrereq()
368 949dcb1d Andrea Spadaccini
369 949dcb1d Andrea Spadaccini
    hm = self.BuildHooksManager(lu)
370 36c381d7 Guido Trotter
    h_results = hm.RunPhase(constants.HOOKS_PHASE_PRE)
371 36c381d7 Guido Trotter
    lu.HooksCallBack(constants.HOOKS_PHASE_PRE, h_results,
372 7b4c1cb9 Michael Hanselmann
                     self.Log, None)
373 20777413 Iustin Pop
374 20777413 Iustin Pop
    if getattr(lu.op, "dry_run", False):
375 20777413 Iustin Pop
      # in this mode, no post-hooks are run, and the config is not
376 20777413 Iustin Pop
      # written (as it might have been modified by another LU, and we
377 20777413 Iustin Pop
      # shouldn't do writeout on behalf of other threads
378 20777413 Iustin Pop
      self.LogInfo("dry-run mode requested, not actually executing"
379 20777413 Iustin Pop
                   " the operation")
380 20777413 Iustin Pop
      return lu.dry_run_result
381 20777413 Iustin Pop
382 abe362d3 Michael Hanselmann
    if self._cbs:
383 abe362d3 Michael Hanselmann
      submit_mj_fn = self._cbs.SubmitManyJobs
384 abe362d3 Michael Hanselmann
    else:
385 abe362d3 Michael Hanselmann
      submit_mj_fn = _FailingSubmitManyJobs
386 abe362d3 Michael Hanselmann
387 36c381d7 Guido Trotter
    try:
388 abe362d3 Michael Hanselmann
      result = _ProcessResult(submit_mj_fn, lu.op, lu.Exec(self.Log))
389 36c381d7 Guido Trotter
      h_results = hm.RunPhase(constants.HOOKS_PHASE_POST)
390 36c381d7 Guido Trotter
      result = lu.HooksCallBack(constants.HOOKS_PHASE_POST, h_results,
391 7b4c1cb9 Michael Hanselmann
                                self.Log, result)
392 36c381d7 Guido Trotter
    finally:
393 36c381d7 Guido Trotter
      # FIXME: This needs locks if not lu_class.REQ_BGL
394 36c381d7 Guido Trotter
      if write_count != self.context.cfg.write_count:
395 36c381d7 Guido Trotter
        hm.RunConfigUpdate()
396 36c381d7 Guido Trotter
397 36c381d7 Guido Trotter
    return result
398 36c381d7 Guido Trotter
399 949dcb1d Andrea Spadaccini
  def BuildHooksManager(self, lu):
400 949dcb1d Andrea Spadaccini
    return self.hmclass.BuildFromLu(lu.rpc.call_hooks_runner, lu)
401 949dcb1d Andrea Spadaccini
402 e4e59de8 Michael Hanselmann
  def _LockAndExecLU(self, lu, level, calc_timeout):
403 68adfdb2 Guido Trotter
    """Execute a Logical Unit, with the needed locks.
404 68adfdb2 Guido Trotter

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

409 68adfdb2 Guido Trotter
    """
410 e9a81214 Michael Hanselmann
    glm = self.context.glm
411 ca2a79e1 Guido Trotter
    adding_locks = level in lu.add_locks
412 ca2a79e1 Guido Trotter
    acquiring_locks = level in lu.needed_locks
413 e9a81214 Michael Hanselmann
414 8a2941c4 Guido Trotter
    if level not in locking.LEVELS:
415 e9a81214 Michael Hanselmann
      _VerifyLocks(lu, glm)
416 e9a81214 Michael Hanselmann
417 031a3e57 Michael Hanselmann
      if self._cbs:
418 031a3e57 Michael Hanselmann
        self._cbs.NotifyStart()
419 031a3e57 Michael Hanselmann
420 b183c4a8 Iustin Pop
      try:
421 b183c4a8 Iustin Pop
        result = self._ExecLU(lu)
422 b183c4a8 Iustin Pop
      except AssertionError, err:
423 b183c4a8 Iustin Pop
        # this is a bit ugly, as we don't know from which phase
424 b183c4a8 Iustin Pop
        # (prereq, exec) this comes; but it's better than an exception
425 b183c4a8 Iustin Pop
        # with no information
426 b183c4a8 Iustin Pop
        (_, _, tb) = sys.exc_info()
427 b183c4a8 Iustin Pop
        err_info = traceback.format_tb(tb)
428 b183c4a8 Iustin Pop
        del tb
429 b183c4a8 Iustin Pop
        logging.exception("Detected AssertionError")
430 b183c4a8 Iustin Pop
        raise errors.OpExecError("Internal assertion error: please report"
431 b183c4a8 Iustin Pop
                                 " this as a bug.\nError message: '%s';"
432 b183c4a8 Iustin Pop
                                 " location:\n%s" % (str(err), err_info[-1]))
433 407339d0 Michael Hanselmann
434 ca2a79e1 Guido Trotter
    elif adding_locks and acquiring_locks:
435 ca2a79e1 Guido Trotter
      # We could both acquire and add locks at the same level, but for now we
436 ca2a79e1 Guido Trotter
      # don't need this, so we'll avoid the complicated code needed.
437 407339d0 Michael Hanselmann
      raise NotImplementedError("Can't declare locks to acquire when adding"
438 407339d0 Michael Hanselmann
                                " others")
439 407339d0 Michael Hanselmann
440 ca2a79e1 Guido Trotter
    elif adding_locks or acquiring_locks:
441 dc4bdf73 Michael Hanselmann
      self._CheckLocksEnabled()
442 dc4bdf73 Michael Hanselmann
443 fb8dcb62 Guido Trotter
      lu.DeclareLocks(level)
444 3977a4c1 Guido Trotter
      share = lu.share_locks[level]
445 686d24f0 Michael Hanselmann
      opportunistic = lu.opportunistic_locks[level]
446 407339d0 Michael Hanselmann
447 68adfdb2 Guido Trotter
      try:
448 407339d0 Michael Hanselmann
        assert adding_locks ^ acquiring_locks, \
449 407339d0 Michael Hanselmann
          "Locks must be either added or acquired"
450 407339d0 Michael Hanselmann
451 407339d0 Michael Hanselmann
        if acquiring_locks:
452 407339d0 Michael Hanselmann
          # Acquiring locks
453 407339d0 Michael Hanselmann
          needed_locks = lu.needed_locks[level]
454 407339d0 Michael Hanselmann
455 686d24f0 Michael Hanselmann
          self._AcquireLocks(level, needed_locks, share, opportunistic,
456 e4e59de8 Michael Hanselmann
                             calc_timeout())
457 407339d0 Michael Hanselmann
        else:
458 407339d0 Michael Hanselmann
          # Adding locks
459 407339d0 Michael Hanselmann
          add_locks = lu.add_locks[level]
460 407339d0 Michael Hanselmann
          lu.remove_locks[level] = add_locks
461 407339d0 Michael Hanselmann
462 407339d0 Michael Hanselmann
          try:
463 e9a81214 Michael Hanselmann
            glm.add(level, add_locks, acquired=1, shared=share)
464 407339d0 Michael Hanselmann
          except errors.LockError:
465 a9d40c65 Iustin Pop
            logging.exception("Detected lock error in level %s for locks"
466 a9d40c65 Iustin Pop
                              " %s, shared=%s", level, add_locks, share)
467 407339d0 Michael Hanselmann
            raise errors.OpPrereqError(
468 a9d40c65 Iustin Pop
              "Couldn't add locks (%s), most likely because of another"
469 a9d40c65 Iustin Pop
              " job who added them first" % add_locks,
470 a9d40c65 Iustin Pop
              errors.ECODE_NOTUNIQUE)
471 407339d0 Michael Hanselmann
472 ca2a79e1 Guido Trotter
        try:
473 e4e59de8 Michael Hanselmann
          result = self._LockAndExecLU(lu, level + 1, calc_timeout)
474 ca2a79e1 Guido Trotter
        finally:
475 ca2a79e1 Guido Trotter
          if level in lu.remove_locks:
476 e9a81214 Michael Hanselmann
            glm.remove(level, lu.remove_locks[level])
477 68adfdb2 Guido Trotter
      finally:
478 e9a81214 Michael Hanselmann
        if glm.is_owned(level):
479 e9a81214 Michael Hanselmann
          glm.release(level)
480 407339d0 Michael Hanselmann
481 68adfdb2 Guido Trotter
    else:
482 e4e59de8 Michael Hanselmann
      result = self._LockAndExecLU(lu, level + 1, calc_timeout)
483 68adfdb2 Guido Trotter
484 68adfdb2 Guido Trotter
    return result
485 68adfdb2 Guido Trotter
486 e4e59de8 Michael Hanselmann
  def ExecOpCode(self, op, cbs, timeout=None):
487 a8083063 Iustin Pop
    """Execute an opcode.
488 a8083063 Iustin Pop

489 e92376d7 Iustin Pop
    @type op: an OpCode instance
490 e92376d7 Iustin Pop
    @param op: the opcode to be executed
491 031a3e57 Michael Hanselmann
    @type cbs: L{OpExecCbBase}
492 031a3e57 Michael Hanselmann
    @param cbs: Runtime callbacks
493 831bbbc1 Michael Hanselmann
    @type timeout: float or None
494 831bbbc1 Michael Hanselmann
    @param timeout: Maximum time to acquire all locks, None for no timeout
495 831bbbc1 Michael Hanselmann
    @raise LockAcquireTimeout: In case locks couldn't be acquired in specified
496 831bbbc1 Michael Hanselmann
        amount of time
497 a8083063 Iustin Pop

498 a8083063 Iustin Pop
    """
499 a8083063 Iustin Pop
    if not isinstance(op, opcodes.OpCode):
500 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Non-opcode instance passed"
501 c7bb3095 Michael Hanselmann
                                   " to ExecOpcode (%s)" % type(op))
502 a8083063 Iustin Pop
503 831bbbc1 Michael Hanselmann
    lu_class = self.DISPATCH_TABLE.get(op.__class__, None)
504 831bbbc1 Michael Hanselmann
    if lu_class is None:
505 831bbbc1 Michael Hanselmann
      raise errors.OpCodeUnknown("Unknown opcode")
506 831bbbc1 Michael Hanselmann
507 831bbbc1 Michael Hanselmann
    if timeout is None:
508 831bbbc1 Michael Hanselmann
      calc_timeout = lambda: None
509 831bbbc1 Michael Hanselmann
    else:
510 557838c1 René Nussbaumer
      calc_timeout = utils.RunningTimeout(timeout, False).Remaining
511 831bbbc1 Michael Hanselmann
512 031a3e57 Michael Hanselmann
    self._cbs = cbs
513 fe482621 Iustin Pop
    try:
514 dc4bdf73 Michael Hanselmann
      if self._enable_locks:
515 dc4bdf73 Michael Hanselmann
        # Acquire the Big Ganeti Lock exclusively if this LU requires it,
516 dc4bdf73 Michael Hanselmann
        # and in a shared fashion otherwise (to prevent concurrent run with
517 dc4bdf73 Michael Hanselmann
        # an exclusive LU.
518 dc4bdf73 Michael Hanselmann
        self._AcquireLocks(locking.LEVEL_CLUSTER, locking.BGL,
519 686d24f0 Michael Hanselmann
                            not lu_class.REQ_BGL, False, calc_timeout())
520 dc4bdf73 Michael Hanselmann
      elif lu_class.REQ_BGL:
521 dc4bdf73 Michael Hanselmann
        raise errors.ProgrammerError("Opcode '%s' requires BGL, but locks are"
522 dc4bdf73 Michael Hanselmann
                                     " disabled" % op.OP_ID)
523 dc4bdf73 Michael Hanselmann
524 831bbbc1 Michael Hanselmann
      try:
525 831bbbc1 Michael Hanselmann
        lu = lu_class(self, op, self.context, self.rpc)
526 831bbbc1 Michael Hanselmann
        lu.ExpandNames()
527 831bbbc1 Michael Hanselmann
        assert lu.needed_locks is not None, "needed_locks not set by LU"
528 407339d0 Michael Hanselmann
529 407339d0 Michael Hanselmann
        try:
530 46cde471 Michael Hanselmann
          result = self._LockAndExecLU(lu, locking.LEVEL_CLUSTER + 1,
531 46cde471 Michael Hanselmann
                                       calc_timeout)
532 831bbbc1 Michael Hanselmann
        finally:
533 831bbbc1 Michael Hanselmann
          if self._ec_id:
534 831bbbc1 Michael Hanselmann
            self.context.cfg.DropECReservations(self._ec_id)
535 831bbbc1 Michael Hanselmann
      finally:
536 dc4bdf73 Michael Hanselmann
        # Release BGL if owned
537 dc4bdf73 Michael Hanselmann
        if self.context.glm.is_owned(locking.LEVEL_CLUSTER):
538 dc4bdf73 Michael Hanselmann
          assert self._enable_locks
539 dc4bdf73 Michael Hanselmann
          self.context.glm.release(locking.LEVEL_CLUSTER)
540 04864530 Guido Trotter
    finally:
541 031a3e57 Michael Hanselmann
      self._cbs = None
542 6a4aa7c1 Iustin Pop
543 1ce03fb1 Michael Hanselmann
    resultcheck_fn = op.OP_RESULT
544 1ce03fb1 Michael Hanselmann
    if not (resultcheck_fn is None or resultcheck_fn(result)):
545 1ce03fb1 Michael Hanselmann
      logging.error("Expected opcode result matching %s, got %s",
546 1ce03fb1 Michael Hanselmann
                    resultcheck_fn, result)
547 80b207df Iustin Pop
      if not getattr(op, "dry_run", False):
548 80b207df Iustin Pop
        # FIXME: LUs should still behave in dry_run mode, or
549 80b207df Iustin Pop
        # alternately we should have OP_DRYRUN_RESULT; in the
550 80b207df Iustin Pop
        # meantime, we simply skip the OP_RESULT check in dry-run mode
551 80b207df Iustin Pop
        raise errors.OpResultError("Opcode result does not match %s: %s" %
552 80b207df Iustin Pop
                                   (resultcheck_fn, utils.Truncate(result, 80)))
553 1ce03fb1 Michael Hanselmann
554 1ce03fb1 Michael Hanselmann
    return result
555 1ce03fb1 Michael Hanselmann
556 7b4c1cb9 Michael Hanselmann
  def Log(self, *args):
557 031a3e57 Michael Hanselmann
    """Forward call to feedback callback function.
558 031a3e57 Michael Hanselmann

559 031a3e57 Michael Hanselmann
    """
560 031a3e57 Michael Hanselmann
    if self._cbs:
561 031a3e57 Michael Hanselmann
      self._cbs.Feedback(*args)
562 031a3e57 Michael Hanselmann
563 0fbbf897 Iustin Pop
  def LogStep(self, current, total, message):
564 0fbbf897 Iustin Pop
    """Log a change in LU execution progress.
565 0fbbf897 Iustin Pop

566 0fbbf897 Iustin Pop
    """
567 a5eb7789 Iustin Pop
    logging.debug("Step %d/%d %s", current, total, message)
568 7b4c1cb9 Michael Hanselmann
    self.Log("STEP %d/%d %s" % (current, total, message))
569 0fbbf897 Iustin Pop
570 c0088fb9 Iustin Pop
  def LogWarning(self, message, *args, **kwargs):
571 0fbbf897 Iustin Pop
    """Log a warning to the logs and the user.
572 0fbbf897 Iustin Pop

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

578 c0088fb9 Iustin Pop
    """
579 c0088fb9 Iustin Pop
    assert not kwargs or (len(kwargs) == 1 and "hint" in kwargs), \
580 c0088fb9 Iustin Pop
           "Invalid keyword arguments for LogWarning (%s)" % str(kwargs)
581 c0088fb9 Iustin Pop
    if args:
582 c0088fb9 Iustin Pop
      message = message % tuple(args)
583 c0088fb9 Iustin Pop
    if message:
584 c0088fb9 Iustin Pop
      logging.warning(message)
585 7b4c1cb9 Michael Hanselmann
      self.Log(" - WARNING: %s" % message)
586 c0088fb9 Iustin Pop
    if "hint" in kwargs:
587 7b4c1cb9 Michael Hanselmann
      self.Log("      Hint: %s" % kwargs["hint"])
588 c0088fb9 Iustin Pop
589 c0088fb9 Iustin Pop
  def LogInfo(self, message, *args):
590 0fbbf897 Iustin Pop
    """Log an informational message to the logs and the user.
591 0fbbf897 Iustin Pop

592 0fbbf897 Iustin Pop
    """
593 c0088fb9 Iustin Pop
    if args:
594 c0088fb9 Iustin Pop
      message = message % tuple(args)
595 a5eb7789 Iustin Pop
    logging.info(message)
596 7b4c1cb9 Michael Hanselmann
    self.Log(" - INFO: %s" % message)
597 0fbbf897 Iustin Pop
598 adfa97e3 Guido Trotter
  def GetECId(self):
599 3ae70d76 Michael Hanselmann
    """Returns the current execution context ID.
600 3ae70d76 Michael Hanselmann

601 3ae70d76 Michael Hanselmann
    """
602 adfa97e3 Guido Trotter
    if not self._ec_id:
603 3ae70d76 Michael Hanselmann
      raise errors.ProgrammerError("Tried to use execution context id when"
604 3ae70d76 Michael Hanselmann
                                   " not set")
605 adfa97e3 Guido Trotter
    return self._ec_id
606 adfa97e3 Guido Trotter
607 a8083063 Iustin Pop
608 a8083063 Iustin Pop
class HooksMaster(object):
609 949dcb1d Andrea Spadaccini
  def __init__(self, opcode, hooks_path, nodes, hooks_execution_fn,
610 5ae4945a Iustin Pop
               hooks_results_adapt_fn, build_env_fn, log_fn, htype=None,
611 5ae4945a Iustin Pop
               cluster_name=None, master_name=None):
612 949dcb1d Andrea Spadaccini
    """Base class for hooks masters.
613 949dcb1d Andrea Spadaccini

614 949dcb1d Andrea Spadaccini
    This class invokes the execution of hooks according to the behaviour
615 949dcb1d Andrea Spadaccini
    specified by its parameters.
616 949dcb1d Andrea Spadaccini

617 949dcb1d Andrea Spadaccini
    @type opcode: string
618 949dcb1d Andrea Spadaccini
    @param opcode: opcode of the operation to which the hooks are tied
619 949dcb1d Andrea Spadaccini
    @type hooks_path: string
620 949dcb1d Andrea Spadaccini
    @param hooks_path: prefix of the hooks directories
621 949dcb1d Andrea Spadaccini
    @type nodes: 2-tuple of lists
622 949dcb1d Andrea Spadaccini
    @param nodes: 2-tuple of lists containing nodes on which pre-hooks must be
623 949dcb1d Andrea Spadaccini
      run and nodes on which post-hooks must be run
624 949dcb1d Andrea Spadaccini
    @type hooks_execution_fn: function that accepts the following parameters:
625 949dcb1d Andrea Spadaccini
      (node_list, hooks_path, phase, environment)
626 949dcb1d Andrea Spadaccini
    @param hooks_execution_fn: function that will execute the hooks; can be
627 949dcb1d Andrea Spadaccini
      None, indicating that no conversion is necessary.
628 949dcb1d Andrea Spadaccini
    @type hooks_results_adapt_fn: function
629 949dcb1d Andrea Spadaccini
    @param hooks_results_adapt_fn: function that will adapt the return value of
630 949dcb1d Andrea Spadaccini
      hooks_execution_fn to the format expected by RunPhase
631 949dcb1d Andrea Spadaccini
    @type build_env_fn: function that returns a dictionary having strings as
632 949dcb1d Andrea Spadaccini
      keys
633 949dcb1d Andrea Spadaccini
    @param build_env_fn: function that builds the environment for the hooks
634 949dcb1d Andrea Spadaccini
    @type log_fn: function that accepts a string
635 949dcb1d Andrea Spadaccini
    @param log_fn: logging function
636 949dcb1d Andrea Spadaccini
    @type htype: string or None
637 949dcb1d Andrea Spadaccini
    @param htype: None or one of L{constants.HTYPE_CLUSTER},
638 949dcb1d Andrea Spadaccini
     L{constants.HTYPE_NODE}, L{constants.HTYPE_INSTANCE}
639 949dcb1d Andrea Spadaccini
    @type cluster_name: string
640 949dcb1d Andrea Spadaccini
    @param cluster_name: name of the cluster
641 949dcb1d Andrea Spadaccini
    @type master_name: string
642 949dcb1d Andrea Spadaccini
    @param master_name: name of the master
643 a8083063 Iustin Pop

644 949dcb1d Andrea Spadaccini
    """
645 949dcb1d Andrea Spadaccini
    self.opcode = opcode
646 949dcb1d Andrea Spadaccini
    self.hooks_path = hooks_path
647 949dcb1d Andrea Spadaccini
    self.hooks_execution_fn = hooks_execution_fn
648 949dcb1d Andrea Spadaccini
    self.hooks_results_adapt_fn = hooks_results_adapt_fn
649 949dcb1d Andrea Spadaccini
    self.build_env_fn = build_env_fn
650 949dcb1d Andrea Spadaccini
    self.log_fn = log_fn
651 949dcb1d Andrea Spadaccini
    self.htype = htype
652 949dcb1d Andrea Spadaccini
    self.cluster_name = cluster_name
653 949dcb1d Andrea Spadaccini
    self.master_name = master_name
654 a8083063 Iustin Pop
655 07e0896f Michael Hanselmann
    self.pre_env = self._BuildEnv(constants.HOOKS_PHASE_PRE)
656 07e0896f Michael Hanselmann
    (self.pre_nodes, self.post_nodes) = nodes
657 a8083063 Iustin Pop
658 dd7f6776 Michael Hanselmann
  def _BuildEnv(self, phase):
659 a8083063 Iustin Pop
    """Compute the environment and the target nodes.
660 a8083063 Iustin Pop

661 a8083063 Iustin Pop
    Based on the opcode and the current node list, this builds the
662 a8083063 Iustin Pop
    environment for the hooks and the target node list for the run.
663 a8083063 Iustin Pop

664 a8083063 Iustin Pop
    """
665 dd7f6776 Michael Hanselmann
    if phase == constants.HOOKS_PHASE_PRE:
666 dd7f6776 Michael Hanselmann
      prefix = "GANETI_"
667 dd7f6776 Michael Hanselmann
    elif phase == constants.HOOKS_PHASE_POST:
668 dd7f6776 Michael Hanselmann
      prefix = "GANETI_POST_"
669 dd7f6776 Michael Hanselmann
    else:
670 dd7f6776 Michael Hanselmann
      raise AssertionError("Unknown phase '%s'" % phase)
671 dd7f6776 Michael Hanselmann
672 dd7f6776 Michael Hanselmann
    env = {}
673 a8083063 Iustin Pop
674 949dcb1d Andrea Spadaccini
    if self.hooks_path is not None:
675 949dcb1d Andrea Spadaccini
      phase_env = self.build_env_fn()
676 949dcb1d Andrea Spadaccini
      if phase_env:
677 949dcb1d Andrea Spadaccini
        assert not compat.any(key.upper().startswith(prefix)
678 949dcb1d Andrea Spadaccini
                              for key in phase_env)
679 dd7f6776 Michael Hanselmann
        env.update(("%s%s" % (prefix, key), value)
680 949dcb1d Andrea Spadaccini
                   for (key, value) in phase_env.items())
681 a8083063 Iustin Pop
682 dd7f6776 Michael Hanselmann
    if phase == constants.HOOKS_PHASE_PRE:
683 dd7f6776 Michael Hanselmann
      assert compat.all((key.startswith("GANETI_") and
684 dd7f6776 Michael Hanselmann
                         not key.startswith("GANETI_POST_"))
685 dd7f6776 Michael Hanselmann
                        for key in env)
686 dd7f6776 Michael Hanselmann
687 dd7f6776 Michael Hanselmann
    elif phase == constants.HOOKS_PHASE_POST:
688 dd7f6776 Michael Hanselmann
      assert compat.all(key.startswith("GANETI_POST_") for key in env)
689 07e0896f Michael Hanselmann
      assert isinstance(self.pre_env, dict)
690 dd7f6776 Michael Hanselmann
691 07e0896f Michael Hanselmann
      # Merge with pre-phase environment
692 07e0896f Michael Hanselmann
      assert not compat.any(key.startswith("GANETI_POST_")
693 07e0896f Michael Hanselmann
                            for key in self.pre_env)
694 07e0896f Michael Hanselmann
      env.update(self.pre_env)
695 dd7f6776 Michael Hanselmann
    else:
696 dd7f6776 Michael Hanselmann
      raise AssertionError("Unknown phase '%s'" % phase)
697 dd7f6776 Michael Hanselmann
698 07e0896f Michael Hanselmann
    return env
699 4167825b Iustin Pop
700 dd7f6776 Michael Hanselmann
  def _RunWrapper(self, node_list, hpath, phase, phase_env):
701 4167825b Iustin Pop
    """Simple wrapper over self.callfn.
702 4167825b Iustin Pop

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

705 4167825b Iustin Pop
    """
706 dd7f6776 Michael Hanselmann
    env = {
707 fe5ca2bb Andrea Spadaccini
      "PATH": constants.HOOKS_PATH,
708 dd7f6776 Michael Hanselmann
      "GANETI_HOOKS_VERSION": constants.HOOKS_VERSION,
709 949dcb1d Andrea Spadaccini
      "GANETI_OP_CODE": self.opcode,
710 b3a6f851 Michael Hanselmann
      "GANETI_DATA_DIR": pathutils.DATA_DIR,
711 dd7f6776 Michael Hanselmann
      "GANETI_HOOKS_PHASE": phase,
712 dd7f6776 Michael Hanselmann
      "GANETI_HOOKS_PATH": hpath,
713 dd7f6776 Michael Hanselmann
      }
714 dd7f6776 Michael Hanselmann
715 949dcb1d Andrea Spadaccini
    if self.htype:
716 949dcb1d Andrea Spadaccini
      env["GANETI_OBJECT_TYPE"] = self.htype
717 949dcb1d Andrea Spadaccini
718 949dcb1d Andrea Spadaccini
    if self.cluster_name is not None:
719 949dcb1d Andrea Spadaccini
      env["GANETI_CLUSTER"] = self.cluster_name
720 07e0896f Michael Hanselmann
721 949dcb1d Andrea Spadaccini
    if self.master_name is not None:
722 949dcb1d Andrea Spadaccini
      env["GANETI_MASTER"] = self.master_name
723 dd7f6776 Michael Hanselmann
724 dd7f6776 Michael Hanselmann
    if phase_env:
725 af5af644 Andrea Spadaccini
      env = utils.algo.JoinDisjointDicts(env, phase_env)
726 a8083063 Iustin Pop
727 dd7f6776 Michael Hanselmann
    # Convert everything to strings
728 4167825b Iustin Pop
    env = dict([(str(key), str(val)) for key, val in env.iteritems()])
729 a8083063 Iustin Pop
730 dd7f6776 Michael Hanselmann
    assert compat.all(key == "PATH" or key.startswith("GANETI_")
731 ebc75510 Michael Hanselmann
                      for key in env)
732 ebc75510 Michael Hanselmann
733 949dcb1d Andrea Spadaccini
    return self.hooks_execution_fn(node_list, hpath, phase, env)
734 a8083063 Iustin Pop
735 17e82923 Luca Bigliardi
  def RunPhase(self, phase, nodes=None):
736 a8083063 Iustin Pop
    """Run all the scripts for a phase.
737 a8083063 Iustin Pop

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

743 8dca23a3 Iustin Pop
    @param phase: one of L{constants.HOOKS_PHASE_POST} or
744 8dca23a3 Iustin Pop
        L{constants.HOOKS_PHASE_PRE}; it denotes the hooks phase
745 17e82923 Luca Bigliardi
    @param nodes: overrides the predefined list of nodes for the given phase
746 8dca23a3 Iustin Pop
    @return: the processed results of the hooks multi-node rpc call
747 8dca23a3 Iustin Pop
    @raise errors.HooksFailure: on communication failure to the nodes
748 6ef2dc74 Luca Bigliardi
    @raise errors.HooksAbort: on failure of one of the hooks
749 b07a6922 Guido Trotter

750 a8083063 Iustin Pop
    """
751 07e0896f Michael Hanselmann
    if phase == constants.HOOKS_PHASE_PRE:
752 07e0896f Michael Hanselmann
      if nodes is None:
753 07e0896f Michael Hanselmann
        nodes = self.pre_nodes
754 07e0896f Michael Hanselmann
      env = self.pre_env
755 07e0896f Michael Hanselmann
    elif phase == constants.HOOKS_PHASE_POST:
756 07e0896f Michael Hanselmann
      if nodes is None:
757 b423c513 Michael Hanselmann
        nodes = self.post_nodes
758 07e0896f Michael Hanselmann
      env = self._BuildEnv(phase)
759 07e0896f Michael Hanselmann
    else:
760 07e0896f Michael Hanselmann
      raise AssertionError("Unknown phase '%s'" % phase)
761 0306ff62 Michael Hanselmann
762 0306ff62 Michael Hanselmann
    if not nodes:
763 9a395a76 Iustin Pop
      # empty node list, we should not attempt to run this as either
764 9a395a76 Iustin Pop
      # we're in the cluster init phase and the rpc client part can't
765 9a395a76 Iustin Pop
      # even attempt to run, or this LU doesn't do hooks at all
766 a8083063 Iustin Pop
      return
767 0306ff62 Michael Hanselmann
768 949dcb1d Andrea Spadaccini
    results = self._RunWrapper(nodes, self.hooks_path, phase, env)
769 8c4b9364 Luca Bigliardi
    if not results:
770 8c4b9364 Luca Bigliardi
      msg = "Communication Failure"
771 8c4b9364 Luca Bigliardi
      if phase == constants.HOOKS_PHASE_PRE:
772 8c4b9364 Luca Bigliardi
        raise errors.HooksFailure(msg)
773 8c4b9364 Luca Bigliardi
      else:
774 949dcb1d Andrea Spadaccini
        self.log_fn(msg)
775 640b961e Luca Bigliardi
        return results
776 0306ff62 Michael Hanselmann
777 949dcb1d Andrea Spadaccini
    converted_res = results
778 949dcb1d Andrea Spadaccini
    if self.hooks_results_adapt_fn:
779 949dcb1d Andrea Spadaccini
      converted_res = self.hooks_results_adapt_fn(results)
780 949dcb1d Andrea Spadaccini
781 0306ff62 Michael Hanselmann
    errs = []
782 949dcb1d Andrea Spadaccini
    for node_name, (fail_msg, offline, hooks_results) in converted_res.items():
783 949dcb1d Andrea Spadaccini
      if offline:
784 8c4b9364 Luca Bigliardi
        continue
785 0306ff62 Michael Hanselmann
786 949dcb1d Andrea Spadaccini
      if fail_msg:
787 949dcb1d Andrea Spadaccini
        self.log_fn("Communication failure to node %s: %s", node_name, fail_msg)
788 8c4b9364 Luca Bigliardi
        continue
789 0306ff62 Michael Hanselmann
790 949dcb1d Andrea Spadaccini
      for script, hkr, output in hooks_results:
791 8c4b9364 Luca Bigliardi
        if hkr == constants.HKR_FAIL:
792 8c4b9364 Luca Bigliardi
          if phase == constants.HOOKS_PHASE_PRE:
793 a8083063 Iustin Pop
            errs.append((node_name, script, output))
794 8c4b9364 Luca Bigliardi
          else:
795 8c4b9364 Luca Bigliardi
            if not output:
796 640b961e Luca Bigliardi
              output = "(no output)"
797 949dcb1d Andrea Spadaccini
            self.log_fn("On %s script %s failed, output: %s" %
798 949dcb1d Andrea Spadaccini
                        (node_name, script, output))
799 0306ff62 Michael Hanselmann
800 8c4b9364 Luca Bigliardi
    if errs and phase == constants.HOOKS_PHASE_PRE:
801 8c4b9364 Luca Bigliardi
      raise errors.HooksAbort(errs)
802 0306ff62 Michael Hanselmann
803 b07a6922 Guido Trotter
    return results
804 6a4aa7c1 Iustin Pop
805 6a4aa7c1 Iustin Pop
  def RunConfigUpdate(self):
806 6a4aa7c1 Iustin Pop
    """Run the special configuration update hook
807 6a4aa7c1 Iustin Pop

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

811 6a4aa7c1 Iustin Pop
    """
812 6a4aa7c1 Iustin Pop
    phase = constants.HOOKS_PHASE_POST
813 6a4aa7c1 Iustin Pop
    hpath = constants.HOOKS_NAME_CFGUPDATE
814 949dcb1d Andrea Spadaccini
    nodes = [self.master_name]
815 dd7f6776 Michael Hanselmann
    self._RunWrapper(nodes, hpath, phase, self.pre_env)
816 949dcb1d Andrea Spadaccini
817 949dcb1d Andrea Spadaccini
  @staticmethod
818 949dcb1d Andrea Spadaccini
  def BuildFromLu(hooks_execution_fn, lu):
819 949dcb1d Andrea Spadaccini
    if lu.HPATH is None:
820 949dcb1d Andrea Spadaccini
      nodes = (None, None)
821 949dcb1d Andrea Spadaccini
    else:
822 949dcb1d Andrea Spadaccini
      nodes = map(frozenset, lu.BuildHooksNodes())
823 949dcb1d Andrea Spadaccini
824 949dcb1d Andrea Spadaccini
    master_name = cluster_name = None
825 949dcb1d Andrea Spadaccini
    if lu.cfg:
826 949dcb1d Andrea Spadaccini
      master_name = lu.cfg.GetMasterNode()
827 949dcb1d Andrea Spadaccini
      cluster_name = lu.cfg.GetClusterName()
828 949dcb1d Andrea Spadaccini
829 949dcb1d Andrea Spadaccini
    return HooksMaster(lu.op.OP_ID, lu.HPATH, nodes, hooks_execution_fn,
830 949dcb1d Andrea Spadaccini
                       _RpcResultsToHooksResults, lu.BuildHooksEnv,
831 949dcb1d Andrea Spadaccini
                       lu.LogWarning, lu.HTYPE, cluster_name, master_name)