Statistics
| Branch: | Tag: | Revision:

root / lib / mcpu.py @ 2afd577f

History | View | Annotate | Download (18.2 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 rpc
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 7260cfbe Iustin Pop
class OpExecCbBase: # pylint: disable-msg=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 a8083063 Iustin Pop
class Processor(object):
176 a8083063 Iustin Pop
  """Object which runs OpCodes"""
177 a1a7bc78 Iustin Pop
  DISPATCH_TABLE = _ComputeDispatchTable()
178 a8083063 Iustin Pop
179 adfa97e3 Guido Trotter
  def __init__(self, context, ec_id):
180 a8083063 Iustin Pop
    """Constructor for Processor
181 a8083063 Iustin Pop

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

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

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

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

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

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

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

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

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

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

391 031a3e57 Michael Hanselmann
    """
392 031a3e57 Michael Hanselmann
    if self._cbs:
393 031a3e57 Michael Hanselmann
      self._cbs.Feedback(*args)
394 031a3e57 Michael Hanselmann
395 0fbbf897 Iustin Pop
  def LogStep(self, current, total, message):
396 0fbbf897 Iustin Pop
    """Log a change in LU execution progress.
397 0fbbf897 Iustin Pop

398 0fbbf897 Iustin Pop
    """
399 a5eb7789 Iustin Pop
    logging.debug("Step %d/%d %s", current, total, message)
400 7b4c1cb9 Michael Hanselmann
    self.Log("STEP %d/%d %s" % (current, total, message))
401 0fbbf897 Iustin Pop
402 c0088fb9 Iustin Pop
  def LogWarning(self, message, *args, **kwargs):
403 0fbbf897 Iustin Pop
    """Log a warning to the logs and the user.
404 0fbbf897 Iustin Pop

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

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

424 0fbbf897 Iustin Pop
    """
425 c0088fb9 Iustin Pop
    if args:
426 c0088fb9 Iustin Pop
      message = message % tuple(args)
427 a5eb7789 Iustin Pop
    logging.info(message)
428 7b4c1cb9 Michael Hanselmann
    self.Log(" - INFO: %s" % message)
429 0fbbf897 Iustin Pop
430 adfa97e3 Guido Trotter
  def GetECId(self):
431 3ae70d76 Michael Hanselmann
    """Returns the current execution context ID.
432 3ae70d76 Michael Hanselmann

433 3ae70d76 Michael Hanselmann
    """
434 adfa97e3 Guido Trotter
    if not self._ec_id:
435 3ae70d76 Michael Hanselmann
      raise errors.ProgrammerError("Tried to use execution context id when"
436 3ae70d76 Michael Hanselmann
                                   " not set")
437 adfa97e3 Guido Trotter
    return self._ec_id
438 adfa97e3 Guido Trotter
439 a8083063 Iustin Pop
440 a8083063 Iustin Pop
class HooksMaster(object):
441 a8083063 Iustin Pop
  """Hooks master.
442 a8083063 Iustin Pop

443 a8083063 Iustin Pop
  This class distributes the run commands to the nodes based on the
444 a8083063 Iustin Pop
  specific LU class.
445 a8083063 Iustin Pop

446 a8083063 Iustin Pop
  In order to remove the direct dependency on the rpc module, the
447 a8083063 Iustin Pop
  constructor needs a function which actually does the remote
448 a8083063 Iustin Pop
  call. This will usually be rpc.call_hooks_runner, but any function
449 a8083063 Iustin Pop
  which behaves the same works.
450 a8083063 Iustin Pop

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

468 a8083063 Iustin Pop
    Based on the opcode and the current node list, this builds the
469 a8083063 Iustin Pop
    environment for the hooks and the target node list for the run.
470 a8083063 Iustin Pop

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

509 4167825b Iustin Pop
    This method fixes the environment before doing the rpc call.
510 4167825b Iustin Pop

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

545 a8083063 Iustin Pop
    This is the main function of the HookMaster.
546 a8083063 Iustin Pop

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

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

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

614 6a4aa7c1 Iustin Pop
    """
615 6a4aa7c1 Iustin Pop
    phase = constants.HOOKS_PHASE_POST
616 6a4aa7c1 Iustin Pop
    hpath = constants.HOOKS_NAME_CFGUPDATE
617 437138c9 Michael Hanselmann
    nodes = [self.lu.cfg.GetMasterNode()]
618 dd7f6776 Michael Hanselmann
    self._RunWrapper(nodes, hpath, phase, self.pre_env)