Statistics
| Branch: | Tag: | Revision:

root / lib / mcpu.py @ 6915fe26

History | View | Annotate | Download (18.5 kB)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

168 a1a7bc78 Iustin Pop
  """
169 a1a7bc78 Iustin Pop
  return dict((op, getattr(cmdlib, _LUNameForOpName(op.__name__)))
170 a1a7bc78 Iustin Pop
              for op in opcodes.OP_MAPPING.values()
171 a1a7bc78 Iustin Pop
              if op.WITH_LU)
172 a1a7bc78 Iustin Pop
173 a1a7bc78 Iustin Pop
174 a8083063 Iustin Pop
class Processor(object):
175 a8083063 Iustin Pop
  """Object which runs OpCodes"""
176 a1a7bc78 Iustin Pop
  DISPATCH_TABLE = _ComputeDispatchTable()
177 a8083063 Iustin Pop
178 adfa97e3 Guido Trotter
  def __init__(self, context, ec_id):
179 a8083063 Iustin Pop
    """Constructor for Processor
180 a8083063 Iustin Pop

181 adfa97e3 Guido Trotter
    @type context: GanetiContext
182 adfa97e3 Guido Trotter
    @param context: global Ganeti context
183 adfa97e3 Guido Trotter
    @type ec_id: string
184 adfa97e3 Guido Trotter
    @param ec_id: execution context identifier
185 adfa97e3 Guido Trotter

186 a8083063 Iustin Pop
    """
187 1c901d13 Guido Trotter
    self.context = context
188 adfa97e3 Guido Trotter
    self._ec_id = ec_id
189 031a3e57 Michael Hanselmann
    self._cbs = None
190 87b3cb26 Michael Hanselmann
    self.rpc = context.rpc
191 cd46f3b4 Luca Bigliardi
    self.hmclass = HooksMaster
192 a8083063 Iustin Pop
193 f879a9c7 Michael Hanselmann
  def _AcquireLocks(self, level, names, shared, timeout, priority):
194 211b6132 Michael Hanselmann
    """Acquires locks via the Ganeti lock manager.
195 211b6132 Michael Hanselmann

196 211b6132 Michael Hanselmann
    @type level: int
197 211b6132 Michael Hanselmann
    @param level: Lock level
198 211b6132 Michael Hanselmann
    @type names: list or string
199 211b6132 Michael Hanselmann
    @param names: Lock names
200 211b6132 Michael Hanselmann
    @type shared: bool
201 211b6132 Michael Hanselmann
    @param shared: Whether the locks should be acquired in shared mode
202 211b6132 Michael Hanselmann
    @type timeout: None or float
203 211b6132 Michael Hanselmann
    @param timeout: Timeout for acquiring the locks
204 900df6cd Michael Hanselmann
    @raise LockAcquireTimeout: In case locks couldn't be acquired in specified
205 900df6cd Michael Hanselmann
        amount of time
206 211b6132 Michael Hanselmann

207 211b6132 Michael Hanselmann
    """
208 acf931b7 Michael Hanselmann
    if self._cbs:
209 acf931b7 Michael Hanselmann
      self._cbs.CheckCancel()
210 211b6132 Michael Hanselmann
211 211b6132 Michael Hanselmann
    acquired = self.context.glm.acquire(level, names, shared=shared,
212 f879a9c7 Michael Hanselmann
                                        timeout=timeout, priority=priority)
213 211b6132 Michael Hanselmann
214 900df6cd Michael Hanselmann
    if acquired is None:
215 900df6cd Michael Hanselmann
      raise LockAcquireTimeout()
216 900df6cd Michael Hanselmann
217 211b6132 Michael Hanselmann
    return acquired
218 211b6132 Michael Hanselmann
219 6a373640 Michael Hanselmann
  def _ProcessResult(self, result):
220 eb279644 Michael Hanselmann
    """Examines opcode result.
221 eb279644 Michael Hanselmann

222 eb279644 Michael Hanselmann
    If necessary, additional processing on the result is done.
223 6a373640 Michael Hanselmann

224 6a373640 Michael Hanselmann
    """
225 6a373640 Michael Hanselmann
    if isinstance(result, cmdlib.ResultWithJobs):
226 6a373640 Michael Hanselmann
      # Submit jobs
227 6a373640 Michael Hanselmann
      job_submission = self._cbs.SubmitManyJobs(result.jobs)
228 6a373640 Michael Hanselmann
229 6a373640 Michael Hanselmann
      # Build dictionary
230 6a373640 Michael Hanselmann
      result = result.other
231 6a373640 Michael Hanselmann
232 6a373640 Michael Hanselmann
      assert constants.JOB_IDS_KEY not in result, \
233 6a373640 Michael Hanselmann
        "Key '%s' found in additional return values" % constants.JOB_IDS_KEY
234 6a373640 Michael Hanselmann
235 6a373640 Michael Hanselmann
      result[constants.JOB_IDS_KEY] = job_submission
236 6a373640 Michael Hanselmann
237 6a373640 Michael Hanselmann
    return result
238 6a373640 Michael Hanselmann
239 36c381d7 Guido Trotter
  def _ExecLU(self, lu):
240 36c381d7 Guido Trotter
    """Logical Unit execution sequence.
241 36c381d7 Guido Trotter

242 36c381d7 Guido Trotter
    """
243 36c381d7 Guido Trotter
    write_count = self.context.cfg.write_count
244 36c381d7 Guido Trotter
    lu.CheckPrereq()
245 4b5e8271 Iustin Pop
    hm = HooksMaster(self.rpc.call_hooks_runner, lu)
246 36c381d7 Guido Trotter
    h_results = hm.RunPhase(constants.HOOKS_PHASE_PRE)
247 36c381d7 Guido Trotter
    lu.HooksCallBack(constants.HOOKS_PHASE_PRE, h_results,
248 7b4c1cb9 Michael Hanselmann
                     self.Log, None)
249 20777413 Iustin Pop
250 20777413 Iustin Pop
    if getattr(lu.op, "dry_run", False):
251 20777413 Iustin Pop
      # in this mode, no post-hooks are run, and the config is not
252 20777413 Iustin Pop
      # written (as it might have been modified by another LU, and we
253 20777413 Iustin Pop
      # shouldn't do writeout on behalf of other threads
254 20777413 Iustin Pop
      self.LogInfo("dry-run mode requested, not actually executing"
255 20777413 Iustin Pop
                   " the operation")
256 20777413 Iustin Pop
      return lu.dry_run_result
257 20777413 Iustin Pop
258 36c381d7 Guido Trotter
    try:
259 6a373640 Michael Hanselmann
      result = self._ProcessResult(lu.Exec(self.Log))
260 36c381d7 Guido Trotter
      h_results = hm.RunPhase(constants.HOOKS_PHASE_POST)
261 36c381d7 Guido Trotter
      result = lu.HooksCallBack(constants.HOOKS_PHASE_POST, h_results,
262 7b4c1cb9 Michael Hanselmann
                                self.Log, result)
263 36c381d7 Guido Trotter
    finally:
264 36c381d7 Guido Trotter
      # FIXME: This needs locks if not lu_class.REQ_BGL
265 36c381d7 Guido Trotter
      if write_count != self.context.cfg.write_count:
266 36c381d7 Guido Trotter
        hm.RunConfigUpdate()
267 36c381d7 Guido Trotter
268 36c381d7 Guido Trotter
    return result
269 36c381d7 Guido Trotter
270 f879a9c7 Michael Hanselmann
  def _LockAndExecLU(self, lu, level, calc_timeout, priority):
271 68adfdb2 Guido Trotter
    """Execute a Logical Unit, with the needed locks.
272 68adfdb2 Guido Trotter

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

277 68adfdb2 Guido Trotter
    """
278 ca2a79e1 Guido Trotter
    adding_locks = level in lu.add_locks
279 ca2a79e1 Guido Trotter
    acquiring_locks = level in lu.needed_locks
280 8a2941c4 Guido Trotter
    if level not in locking.LEVELS:
281 031a3e57 Michael Hanselmann
      if self._cbs:
282 031a3e57 Michael Hanselmann
        self._cbs.NotifyStart()
283 031a3e57 Michael Hanselmann
284 8a2941c4 Guido Trotter
      result = self._ExecLU(lu)
285 407339d0 Michael Hanselmann
286 ca2a79e1 Guido Trotter
    elif adding_locks and acquiring_locks:
287 ca2a79e1 Guido Trotter
      # We could both acquire and add locks at the same level, but for now we
288 ca2a79e1 Guido Trotter
      # don't need this, so we'll avoid the complicated code needed.
289 407339d0 Michael Hanselmann
      raise NotImplementedError("Can't declare locks to acquire when adding"
290 407339d0 Michael Hanselmann
                                " others")
291 407339d0 Michael Hanselmann
292 ca2a79e1 Guido Trotter
    elif adding_locks or acquiring_locks:
293 fb8dcb62 Guido Trotter
      lu.DeclareLocks(level)
294 3977a4c1 Guido Trotter
      share = lu.share_locks[level]
295 407339d0 Michael Hanselmann
296 68adfdb2 Guido Trotter
      try:
297 407339d0 Michael Hanselmann
        assert adding_locks ^ acquiring_locks, \
298 407339d0 Michael Hanselmann
          "Locks must be either added or acquired"
299 407339d0 Michael Hanselmann
300 407339d0 Michael Hanselmann
        if acquiring_locks:
301 407339d0 Michael Hanselmann
          # Acquiring locks
302 407339d0 Michael Hanselmann
          needed_locks = lu.needed_locks[level]
303 407339d0 Michael Hanselmann
304 0d5a0b96 Michael Hanselmann
          self._AcquireLocks(level, needed_locks, share,
305 0d5a0b96 Michael Hanselmann
                             calc_timeout(), priority)
306 407339d0 Michael Hanselmann
        else:
307 407339d0 Michael Hanselmann
          # Adding locks
308 407339d0 Michael Hanselmann
          add_locks = lu.add_locks[level]
309 407339d0 Michael Hanselmann
          lu.remove_locks[level] = add_locks
310 407339d0 Michael Hanselmann
311 407339d0 Michael Hanselmann
          try:
312 407339d0 Michael Hanselmann
            self.context.glm.add(level, add_locks, acquired=1, shared=share)
313 407339d0 Michael Hanselmann
          except errors.LockError:
314 407339d0 Michael Hanselmann
            raise errors.OpPrereqError(
315 407339d0 Michael Hanselmann
              "Couldn't add locks (%s), probably because of a race condition"
316 debac808 Iustin Pop
              " with another job, who added them first" % add_locks,
317 debac808 Iustin Pop
              errors.ECODE_FAULT)
318 407339d0 Michael Hanselmann
319 ca2a79e1 Guido Trotter
        try:
320 f879a9c7 Michael Hanselmann
          result = self._LockAndExecLU(lu, level + 1, calc_timeout, priority)
321 ca2a79e1 Guido Trotter
        finally:
322 ca2a79e1 Guido Trotter
          if level in lu.remove_locks:
323 ca2a79e1 Guido Trotter
            self.context.glm.remove(level, lu.remove_locks[level])
324 68adfdb2 Guido Trotter
      finally:
325 80ee04a4 Guido Trotter
        if self.context.glm.is_owned(level):
326 68adfdb2 Guido Trotter
          self.context.glm.release(level)
327 407339d0 Michael Hanselmann
328 68adfdb2 Guido Trotter
    else:
329 f879a9c7 Michael Hanselmann
      result = self._LockAndExecLU(lu, level + 1, calc_timeout, priority)
330 68adfdb2 Guido Trotter
331 68adfdb2 Guido Trotter
    return result
332 68adfdb2 Guido Trotter
333 f879a9c7 Michael Hanselmann
  def ExecOpCode(self, op, cbs, timeout=None, priority=None):
334 a8083063 Iustin Pop
    """Execute an opcode.
335 a8083063 Iustin Pop

336 e92376d7 Iustin Pop
    @type op: an OpCode instance
337 e92376d7 Iustin Pop
    @param op: the opcode to be executed
338 031a3e57 Michael Hanselmann
    @type cbs: L{OpExecCbBase}
339 031a3e57 Michael Hanselmann
    @param cbs: Runtime callbacks
340 831bbbc1 Michael Hanselmann
    @type timeout: float or None
341 831bbbc1 Michael Hanselmann
    @param timeout: Maximum time to acquire all locks, None for no timeout
342 f879a9c7 Michael Hanselmann
    @type priority: number or None
343 f879a9c7 Michael Hanselmann
    @param priority: Priority for acquiring lock(s)
344 831bbbc1 Michael Hanselmann
    @raise LockAcquireTimeout: In case locks couldn't be acquired in specified
345 831bbbc1 Michael Hanselmann
        amount of time
346 a8083063 Iustin Pop

347 a8083063 Iustin Pop
    """
348 a8083063 Iustin Pop
    if not isinstance(op, opcodes.OpCode):
349 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Non-opcode instance passed"
350 c7bb3095 Michael Hanselmann
                                   " to ExecOpcode (%s)" % type(op))
351 a8083063 Iustin Pop
352 831bbbc1 Michael Hanselmann
    lu_class = self.DISPATCH_TABLE.get(op.__class__, None)
353 831bbbc1 Michael Hanselmann
    if lu_class is None:
354 831bbbc1 Michael Hanselmann
      raise errors.OpCodeUnknown("Unknown opcode")
355 831bbbc1 Michael Hanselmann
356 831bbbc1 Michael Hanselmann
    if timeout is None:
357 831bbbc1 Michael Hanselmann
      calc_timeout = lambda: None
358 831bbbc1 Michael Hanselmann
    else:
359 557838c1 René Nussbaumer
      calc_timeout = utils.RunningTimeout(timeout, False).Remaining
360 831bbbc1 Michael Hanselmann
361 031a3e57 Michael Hanselmann
    self._cbs = cbs
362 fe482621 Iustin Pop
    try:
363 831bbbc1 Michael Hanselmann
      # Acquire the Big Ganeti Lock exclusively if this LU requires it,
364 831bbbc1 Michael Hanselmann
      # and in a shared fashion otherwise (to prevent concurrent run with
365 831bbbc1 Michael Hanselmann
      # an exclusive LU.
366 900df6cd Michael Hanselmann
      self._AcquireLocks(locking.LEVEL_CLUSTER, locking.BGL,
367 900df6cd Michael Hanselmann
                          not lu_class.REQ_BGL, calc_timeout(),
368 900df6cd Michael Hanselmann
                          priority)
369 831bbbc1 Michael Hanselmann
      try:
370 831bbbc1 Michael Hanselmann
        lu = lu_class(self, op, self.context, self.rpc)
371 831bbbc1 Michael Hanselmann
        lu.ExpandNames()
372 831bbbc1 Michael Hanselmann
        assert lu.needed_locks is not None, "needed_locks not set by LU"
373 407339d0 Michael Hanselmann
374 407339d0 Michael Hanselmann
        try:
375 1ce03fb1 Michael Hanselmann
          result = self._LockAndExecLU(lu, locking.LEVEL_INSTANCE, calc_timeout,
376 1ce03fb1 Michael Hanselmann
                                       priority)
377 831bbbc1 Michael Hanselmann
        finally:
378 831bbbc1 Michael Hanselmann
          if self._ec_id:
379 831bbbc1 Michael Hanselmann
            self.context.cfg.DropECReservations(self._ec_id)
380 831bbbc1 Michael Hanselmann
      finally:
381 831bbbc1 Michael Hanselmann
        self.context.glm.release(locking.LEVEL_CLUSTER)
382 04864530 Guido Trotter
    finally:
383 031a3e57 Michael Hanselmann
      self._cbs = None
384 6a4aa7c1 Iustin Pop
385 1ce03fb1 Michael Hanselmann
    resultcheck_fn = op.OP_RESULT
386 1ce03fb1 Michael Hanselmann
    if not (resultcheck_fn is None or resultcheck_fn(result)):
387 1ce03fb1 Michael Hanselmann
      logging.error("Expected opcode result matching %s, got %s",
388 1ce03fb1 Michael Hanselmann
                    resultcheck_fn, result)
389 1ce03fb1 Michael Hanselmann
      raise errors.OpResultError("Opcode result does not match %s" %
390 1ce03fb1 Michael Hanselmann
                                 resultcheck_fn)
391 1ce03fb1 Michael Hanselmann
392 1ce03fb1 Michael Hanselmann
    return result
393 1ce03fb1 Michael Hanselmann
394 7b4c1cb9 Michael Hanselmann
  def Log(self, *args):
395 031a3e57 Michael Hanselmann
    """Forward call to feedback callback function.
396 031a3e57 Michael Hanselmann

397 031a3e57 Michael Hanselmann
    """
398 031a3e57 Michael Hanselmann
    if self._cbs:
399 031a3e57 Michael Hanselmann
      self._cbs.Feedback(*args)
400 031a3e57 Michael Hanselmann
401 0fbbf897 Iustin Pop
  def LogStep(self, current, total, message):
402 0fbbf897 Iustin Pop
    """Log a change in LU execution progress.
403 0fbbf897 Iustin Pop

404 0fbbf897 Iustin Pop
    """
405 a5eb7789 Iustin Pop
    logging.debug("Step %d/%d %s", current, total, message)
406 7b4c1cb9 Michael Hanselmann
    self.Log("STEP %d/%d %s" % (current, total, message))
407 0fbbf897 Iustin Pop
408 c0088fb9 Iustin Pop
  def LogWarning(self, message, *args, **kwargs):
409 0fbbf897 Iustin Pop
    """Log a warning to the logs and the user.
410 0fbbf897 Iustin Pop

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

416 c0088fb9 Iustin Pop
    """
417 c0088fb9 Iustin Pop
    assert not kwargs or (len(kwargs) == 1 and "hint" in kwargs), \
418 c0088fb9 Iustin Pop
           "Invalid keyword arguments for LogWarning (%s)" % str(kwargs)
419 c0088fb9 Iustin Pop
    if args:
420 c0088fb9 Iustin Pop
      message = message % tuple(args)
421 c0088fb9 Iustin Pop
    if message:
422 c0088fb9 Iustin Pop
      logging.warning(message)
423 7b4c1cb9 Michael Hanselmann
      self.Log(" - WARNING: %s" % message)
424 c0088fb9 Iustin Pop
    if "hint" in kwargs:
425 7b4c1cb9 Michael Hanselmann
      self.Log("      Hint: %s" % kwargs["hint"])
426 c0088fb9 Iustin Pop
427 c0088fb9 Iustin Pop
  def LogInfo(self, message, *args):
428 0fbbf897 Iustin Pop
    """Log an informational message to the logs and the user.
429 0fbbf897 Iustin Pop

430 0fbbf897 Iustin Pop
    """
431 c0088fb9 Iustin Pop
    if args:
432 c0088fb9 Iustin Pop
      message = message % tuple(args)
433 a5eb7789 Iustin Pop
    logging.info(message)
434 7b4c1cb9 Michael Hanselmann
    self.Log(" - INFO: %s" % message)
435 0fbbf897 Iustin Pop
436 adfa97e3 Guido Trotter
  def GetECId(self):
437 3ae70d76 Michael Hanselmann
    """Returns the current execution context ID.
438 3ae70d76 Michael Hanselmann

439 3ae70d76 Michael Hanselmann
    """
440 adfa97e3 Guido Trotter
    if not self._ec_id:
441 3ae70d76 Michael Hanselmann
      raise errors.ProgrammerError("Tried to use execution context id when"
442 3ae70d76 Michael Hanselmann
                                   " not set")
443 adfa97e3 Guido Trotter
    return self._ec_id
444 adfa97e3 Guido Trotter
445 a8083063 Iustin Pop
446 a8083063 Iustin Pop
class HooksMaster(object):
447 a8083063 Iustin Pop
  """Hooks master.
448 a8083063 Iustin Pop

449 a8083063 Iustin Pop
  This class distributes the run commands to the nodes based on the
450 a8083063 Iustin Pop
  specific LU class.
451 a8083063 Iustin Pop

452 a8083063 Iustin Pop
  In order to remove the direct dependency on the rpc module, the
453 a8083063 Iustin Pop
  constructor needs a function which actually does the remote
454 a8083063 Iustin Pop
  call. This will usually be rpc.call_hooks_runner, but any function
455 a8083063 Iustin Pop
  which behaves the same works.
456 a8083063 Iustin Pop

457 a8083063 Iustin Pop
  """
458 4b5e8271 Iustin Pop
  def __init__(self, callfn, lu):
459 a8083063 Iustin Pop
    self.callfn = callfn
460 a8083063 Iustin Pop
    self.lu = lu
461 a8083063 Iustin Pop
    self.op = lu.op
462 07e0896f Michael Hanselmann
    self.pre_env = self._BuildEnv(constants.HOOKS_PHASE_PRE)
463 07e0896f Michael Hanselmann
464 07e0896f Michael Hanselmann
    if self.lu.HPATH is None:
465 07e0896f Michael Hanselmann
      nodes = (None, None)
466 07e0896f Michael Hanselmann
    else:
467 07e0896f Michael Hanselmann
      nodes = map(frozenset, self.lu.BuildHooksNodes())
468 07e0896f Michael Hanselmann
469 07e0896f Michael Hanselmann
    (self.pre_nodes, self.post_nodes) = nodes
470 a8083063 Iustin Pop
471 dd7f6776 Michael Hanselmann
  def _BuildEnv(self, phase):
472 a8083063 Iustin Pop
    """Compute the environment and the target nodes.
473 a8083063 Iustin Pop

474 a8083063 Iustin Pop
    Based on the opcode and the current node list, this builds the
475 a8083063 Iustin Pop
    environment for the hooks and the target node list for the run.
476 a8083063 Iustin Pop

477 a8083063 Iustin Pop
    """
478 dd7f6776 Michael Hanselmann
    if phase == constants.HOOKS_PHASE_PRE:
479 dd7f6776 Michael Hanselmann
      prefix = "GANETI_"
480 dd7f6776 Michael Hanselmann
    elif phase == constants.HOOKS_PHASE_POST:
481 dd7f6776 Michael Hanselmann
      prefix = "GANETI_POST_"
482 dd7f6776 Michael Hanselmann
    else:
483 dd7f6776 Michael Hanselmann
      raise AssertionError("Unknown phase '%s'" % phase)
484 dd7f6776 Michael Hanselmann
485 dd7f6776 Michael Hanselmann
    env = {}
486 a8083063 Iustin Pop
487 9a395a76 Iustin Pop
    if self.lu.HPATH is not None:
488 07e0896f Michael Hanselmann
      lu_env = self.lu.BuildHooksEnv()
489 9a395a76 Iustin Pop
      if lu_env:
490 07e0896f Michael Hanselmann
        assert not compat.any(key.upper().startswith(prefix) for key in lu_env)
491 dd7f6776 Michael Hanselmann
        env.update(("%s%s" % (prefix, key), value)
492 dd7f6776 Michael Hanselmann
                   for (key, value) in lu_env.items())
493 a8083063 Iustin Pop
494 dd7f6776 Michael Hanselmann
    if phase == constants.HOOKS_PHASE_PRE:
495 dd7f6776 Michael Hanselmann
      assert compat.all((key.startswith("GANETI_") and
496 dd7f6776 Michael Hanselmann
                         not key.startswith("GANETI_POST_"))
497 dd7f6776 Michael Hanselmann
                        for key in env)
498 dd7f6776 Michael Hanselmann
499 dd7f6776 Michael Hanselmann
    elif phase == constants.HOOKS_PHASE_POST:
500 dd7f6776 Michael Hanselmann
      assert compat.all(key.startswith("GANETI_POST_") for key in env)
501 07e0896f Michael Hanselmann
      assert isinstance(self.pre_env, dict)
502 dd7f6776 Michael Hanselmann
503 07e0896f Michael Hanselmann
      # Merge with pre-phase environment
504 07e0896f Michael Hanselmann
      assert not compat.any(key.startswith("GANETI_POST_")
505 07e0896f Michael Hanselmann
                            for key in self.pre_env)
506 07e0896f Michael Hanselmann
      env.update(self.pre_env)
507 dd7f6776 Michael Hanselmann
    else:
508 dd7f6776 Michael Hanselmann
      raise AssertionError("Unknown phase '%s'" % phase)
509 dd7f6776 Michael Hanselmann
510 07e0896f Michael Hanselmann
    return env
511 4167825b Iustin Pop
512 dd7f6776 Michael Hanselmann
  def _RunWrapper(self, node_list, hpath, phase, phase_env):
513 4167825b Iustin Pop
    """Simple wrapper over self.callfn.
514 4167825b Iustin Pop

515 4167825b Iustin Pop
    This method fixes the environment before doing the rpc call.
516 4167825b Iustin Pop

517 4167825b Iustin Pop
    """
518 dd7f6776 Michael Hanselmann
    cfg = self.lu.cfg
519 dd7f6776 Michael Hanselmann
520 dd7f6776 Michael Hanselmann
    env = {
521 dd7f6776 Michael Hanselmann
      "PATH": "/sbin:/bin:/usr/sbin:/usr/bin",
522 dd7f6776 Michael Hanselmann
      "GANETI_HOOKS_VERSION": constants.HOOKS_VERSION,
523 dd7f6776 Michael Hanselmann
      "GANETI_OP_CODE": self.op.OP_ID,
524 dd7f6776 Michael Hanselmann
      "GANETI_DATA_DIR": constants.DATA_DIR,
525 dd7f6776 Michael Hanselmann
      "GANETI_HOOKS_PHASE": phase,
526 dd7f6776 Michael Hanselmann
      "GANETI_HOOKS_PATH": hpath,
527 dd7f6776 Michael Hanselmann
      }
528 dd7f6776 Michael Hanselmann
529 07e0896f Michael Hanselmann
    if self.lu.HTYPE:
530 07e0896f Michael Hanselmann
      env["GANETI_OBJECT_TYPE"] = self.lu.HTYPE
531 07e0896f Michael Hanselmann
532 dd7f6776 Michael Hanselmann
    if cfg is not None:
533 dd7f6776 Michael Hanselmann
      env["GANETI_CLUSTER"] = cfg.GetClusterName()
534 dd7f6776 Michael Hanselmann
      env["GANETI_MASTER"] = cfg.GetMasterNode()
535 dd7f6776 Michael Hanselmann
536 dd7f6776 Michael Hanselmann
    if phase_env:
537 dd7f6776 Michael Hanselmann
      assert not (set(env) & set(phase_env)), "Environment variables conflict"
538 dd7f6776 Michael Hanselmann
      env.update(phase_env)
539 a8083063 Iustin Pop
540 dd7f6776 Michael Hanselmann
    # Convert everything to strings
541 4167825b Iustin Pop
    env = dict([(str(key), str(val)) for key, val in env.iteritems()])
542 a8083063 Iustin Pop
543 dd7f6776 Michael Hanselmann
    assert compat.all(key == "PATH" or key.startswith("GANETI_")
544 ebc75510 Michael Hanselmann
                      for key in env)
545 ebc75510 Michael Hanselmann
546 4167825b Iustin Pop
    return self.callfn(node_list, hpath, phase, env)
547 a8083063 Iustin Pop
548 17e82923 Luca Bigliardi
  def RunPhase(self, phase, nodes=None):
549 a8083063 Iustin Pop
    """Run all the scripts for a phase.
550 a8083063 Iustin Pop

551 a8083063 Iustin Pop
    This is the main function of the HookMaster.
552 a8083063 Iustin Pop

553 8dca23a3 Iustin Pop
    @param phase: one of L{constants.HOOKS_PHASE_POST} or
554 8dca23a3 Iustin Pop
        L{constants.HOOKS_PHASE_PRE}; it denotes the hooks phase
555 17e82923 Luca Bigliardi
    @param nodes: overrides the predefined list of nodes for the given phase
556 8dca23a3 Iustin Pop
    @return: the processed results of the hooks multi-node rpc call
557 8dca23a3 Iustin Pop
    @raise errors.HooksFailure: on communication failure to the nodes
558 6ef2dc74 Luca Bigliardi
    @raise errors.HooksAbort: on failure of one of the hooks
559 b07a6922 Guido Trotter

560 a8083063 Iustin Pop
    """
561 07e0896f Michael Hanselmann
    if phase == constants.HOOKS_PHASE_PRE:
562 07e0896f Michael Hanselmann
      if nodes is None:
563 07e0896f Michael Hanselmann
        nodes = self.pre_nodes
564 07e0896f Michael Hanselmann
      env = self.pre_env
565 07e0896f Michael Hanselmann
    elif phase == constants.HOOKS_PHASE_POST:
566 07e0896f Michael Hanselmann
      if nodes is None:
567 b423c513 Michael Hanselmann
        nodes = self.post_nodes
568 07e0896f Michael Hanselmann
      env = self._BuildEnv(phase)
569 07e0896f Michael Hanselmann
    else:
570 07e0896f Michael Hanselmann
      raise AssertionError("Unknown phase '%s'" % phase)
571 0306ff62 Michael Hanselmann
572 0306ff62 Michael Hanselmann
    if not nodes:
573 9a395a76 Iustin Pop
      # empty node list, we should not attempt to run this as either
574 9a395a76 Iustin Pop
      # we're in the cluster init phase and the rpc client part can't
575 9a395a76 Iustin Pop
      # even attempt to run, or this LU doesn't do hooks at all
576 a8083063 Iustin Pop
      return
577 0306ff62 Michael Hanselmann
578 dd7f6776 Michael Hanselmann
    results = self._RunWrapper(nodes, self.lu.HPATH, phase, env)
579 8c4b9364 Luca Bigliardi
    if not results:
580 8c4b9364 Luca Bigliardi
      msg = "Communication Failure"
581 8c4b9364 Luca Bigliardi
      if phase == constants.HOOKS_PHASE_PRE:
582 8c4b9364 Luca Bigliardi
        raise errors.HooksFailure(msg)
583 8c4b9364 Luca Bigliardi
      else:
584 8c4b9364 Luca Bigliardi
        self.lu.LogWarning(msg)
585 640b961e Luca Bigliardi
        return results
586 0306ff62 Michael Hanselmann
587 0306ff62 Michael Hanselmann
    errs = []
588 8c4b9364 Luca Bigliardi
    for node_name in results:
589 8c4b9364 Luca Bigliardi
      res = results[node_name]
590 8c4b9364 Luca Bigliardi
      if res.offline:
591 8c4b9364 Luca Bigliardi
        continue
592 0306ff62 Michael Hanselmann
593 3cebe102 Michael Hanselmann
      msg = res.fail_msg
594 8c4b9364 Luca Bigliardi
      if msg:
595 8c4b9364 Luca Bigliardi
        self.lu.LogWarning("Communication failure to node %s: %s",
596 8c4b9364 Luca Bigliardi
                           node_name, msg)
597 8c4b9364 Luca Bigliardi
        continue
598 0306ff62 Michael Hanselmann
599 8c4b9364 Luca Bigliardi
      for script, hkr, output in res.payload:
600 8c4b9364 Luca Bigliardi
        if hkr == constants.HKR_FAIL:
601 8c4b9364 Luca Bigliardi
          if phase == constants.HOOKS_PHASE_PRE:
602 a8083063 Iustin Pop
            errs.append((node_name, script, output))
603 8c4b9364 Luca Bigliardi
          else:
604 8c4b9364 Luca Bigliardi
            if not output:
605 640b961e Luca Bigliardi
              output = "(no output)"
606 8c4b9364 Luca Bigliardi
            self.lu.LogWarning("On %s script %s failed, output: %s" %
607 8c4b9364 Luca Bigliardi
                               (node_name, script, output))
608 0306ff62 Michael Hanselmann
609 8c4b9364 Luca Bigliardi
    if errs and phase == constants.HOOKS_PHASE_PRE:
610 8c4b9364 Luca Bigliardi
      raise errors.HooksAbort(errs)
611 0306ff62 Michael Hanselmann
612 b07a6922 Guido Trotter
    return results
613 6a4aa7c1 Iustin Pop
614 6a4aa7c1 Iustin Pop
  def RunConfigUpdate(self):
615 6a4aa7c1 Iustin Pop
    """Run the special configuration update hook
616 6a4aa7c1 Iustin Pop

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

620 6a4aa7c1 Iustin Pop
    """
621 6a4aa7c1 Iustin Pop
    phase = constants.HOOKS_PHASE_POST
622 6a4aa7c1 Iustin Pop
    hpath = constants.HOOKS_NAME_CFGUPDATE
623 437138c9 Michael Hanselmann
    nodes = [self.lu.cfg.GetMasterNode()]
624 dd7f6776 Michael Hanselmann
    self._RunWrapper(nodes, hpath, phase, self.pre_env)