Statistics
| Branch: | Tag: | Revision:

root / lib / mcpu.py @ c124045f

History | View | Annotate | Download (15.9 kB)

1 2f31098c Iustin Pop
#
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 a8083063 Iustin Pop
# Copyright (C) 2006, 2007 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 a8083063 Iustin Pop
33 a8083063 Iustin Pop
from ganeti import opcodes
34 a8083063 Iustin Pop
from ganeti import constants
35 a8083063 Iustin Pop
from ganeti import errors
36 a8083063 Iustin Pop
from ganeti import rpc
37 a8083063 Iustin Pop
from ganeti import cmdlib
38 04864530 Guido Trotter
from ganeti import locking
39 ef2df7d3 Michael Hanselmann
from ganeti import utils
40 a8083063 Iustin Pop
41 7c0d6283 Michael Hanselmann
42 031a3e57 Michael Hanselmann
class OpExecCbBase:
43 031a3e57 Michael Hanselmann
  """Base class for OpCode execution callbacks.
44 031a3e57 Michael Hanselmann

45 031a3e57 Michael Hanselmann
  """
46 031a3e57 Michael Hanselmann
  def NotifyStart(self):
47 031a3e57 Michael Hanselmann
    """Called when we are about to execute the LU.
48 031a3e57 Michael Hanselmann

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

52 031a3e57 Michael Hanselmann
    """
53 031a3e57 Michael Hanselmann
54 031a3e57 Michael Hanselmann
  def Feedback(self, *args):
55 031a3e57 Michael Hanselmann
    """Sends feedback from the LU code to the end-user.
56 031a3e57 Michael Hanselmann

57 031a3e57 Michael Hanselmann
    """
58 031a3e57 Michael Hanselmann
59 ef2df7d3 Michael Hanselmann
  def ReportLocks(self, msg):
60 ef2df7d3 Michael Hanselmann
    """Report lock operations.
61 ef2df7d3 Michael Hanselmann

62 ef2df7d3 Michael Hanselmann
    """
63 ef2df7d3 Michael Hanselmann
64 031a3e57 Michael Hanselmann
65 a8083063 Iustin Pop
class Processor(object):
66 a8083063 Iustin Pop
  """Object which runs OpCodes"""
67 a8083063 Iustin Pop
  DISPATCH_TABLE = {
68 a8083063 Iustin Pop
    # Cluster
69 b5f5fae9 Luca Bigliardi
    opcodes.OpPostInitCluster: cmdlib.LUPostInitCluster,
70 a8083063 Iustin Pop
    opcodes.OpDestroyCluster: cmdlib.LUDestroyCluster,
71 a8083063 Iustin Pop
    opcodes.OpQueryClusterInfo: cmdlib.LUQueryClusterInfo,
72 a8083063 Iustin Pop
    opcodes.OpVerifyCluster: cmdlib.LUVerifyCluster,
73 ae5849b5 Michael Hanselmann
    opcodes.OpQueryConfigValues: cmdlib.LUQueryConfigValues,
74 07bd8a51 Iustin Pop
    opcodes.OpRenameCluster: cmdlib.LURenameCluster,
75 f4d4e184 Iustin Pop
    opcodes.OpVerifyDisks: cmdlib.LUVerifyDisks,
76 0cc05d44 Manuel Franceschini
    opcodes.OpSetClusterParams: cmdlib.LUSetClusterParams,
77 afee0879 Iustin Pop
    opcodes.OpRedistributeConfig: cmdlib.LURedistributeConfig,
78 60975797 Iustin Pop
    opcodes.OpRepairDiskSizes: cmdlib.LURepairDiskSizes,
79 a8083063 Iustin Pop
    # node lu
80 a8083063 Iustin Pop
    opcodes.OpAddNode: cmdlib.LUAddNode,
81 a8083063 Iustin Pop
    opcodes.OpQueryNodes: cmdlib.LUQueryNodes,
82 dcb93971 Michael Hanselmann
    opcodes.OpQueryNodeVolumes: cmdlib.LUQueryNodeVolumes,
83 9e5442ce Michael Hanselmann
    opcodes.OpQueryNodeStorage: cmdlib.LUQueryNodeStorage,
84 efb8da02 Michael Hanselmann
    opcodes.OpModifyNodeStorage: cmdlib.LUModifyNodeStorage,
85 76aef8fc Michael Hanselmann
    opcodes.OpRepairNodeStorage: cmdlib.LURepairNodeStorage,
86 a8083063 Iustin Pop
    opcodes.OpRemoveNode: cmdlib.LURemoveNode,
87 b31c8676 Iustin Pop
    opcodes.OpSetNodeParams: cmdlib.LUSetNodeParams,
88 f5118ade Iustin Pop
    opcodes.OpPowercycleNode: cmdlib.LUPowercycleNode,
89 7ffc5a86 Michael Hanselmann
    opcodes.OpEvacuateNode: cmdlib.LUEvacuateNode,
90 80cb875c Michael Hanselmann
    opcodes.OpMigrateNode: cmdlib.LUMigrateNode,
91 a8083063 Iustin Pop
    # instance lu
92 a8083063 Iustin Pop
    opcodes.OpCreateInstance: cmdlib.LUCreateInstance,
93 fe7b0351 Michael Hanselmann
    opcodes.OpReinstallInstance: cmdlib.LUReinstallInstance,
94 a8083063 Iustin Pop
    opcodes.OpRemoveInstance: cmdlib.LURemoveInstance,
95 decd5f45 Iustin Pop
    opcodes.OpRenameInstance: cmdlib.LURenameInstance,
96 a8083063 Iustin Pop
    opcodes.OpActivateInstanceDisks: cmdlib.LUActivateInstanceDisks,
97 a8083063 Iustin Pop
    opcodes.OpShutdownInstance: cmdlib.LUShutdownInstance,
98 a8083063 Iustin Pop
    opcodes.OpStartupInstance: cmdlib.LUStartupInstance,
99 bf6929a2 Alexander Schreiber
    opcodes.OpRebootInstance: cmdlib.LURebootInstance,
100 a8083063 Iustin Pop
    opcodes.OpDeactivateInstanceDisks: cmdlib.LUDeactivateInstanceDisks,
101 a8083063 Iustin Pop
    opcodes.OpReplaceDisks: cmdlib.LUReplaceDisks,
102 bd315bfa Iustin Pop
    opcodes.OpRecreateInstanceDisks: cmdlib.LURecreateInstanceDisks,
103 a8083063 Iustin Pop
    opcodes.OpFailoverInstance: cmdlib.LUFailoverInstance,
104 53c776b5 Iustin Pop
    opcodes.OpMigrateInstance: cmdlib.LUMigrateInstance,
105 313bcead Iustin Pop
    opcodes.OpMoveInstance: cmdlib.LUMoveInstance,
106 a8083063 Iustin Pop
    opcodes.OpConnectConsole: cmdlib.LUConnectConsole,
107 a8083063 Iustin Pop
    opcodes.OpQueryInstances: cmdlib.LUQueryInstances,
108 a8083063 Iustin Pop
    opcodes.OpQueryInstanceData: cmdlib.LUQueryInstanceData,
109 7767bbf5 Manuel Franceschini
    opcodes.OpSetInstanceParams: cmdlib.LUSetInstanceParams,
110 8729e0d7 Iustin Pop
    opcodes.OpGrowDisk: cmdlib.LUGrowDisk,
111 a8083063 Iustin Pop
    # os lu
112 a8083063 Iustin Pop
    opcodes.OpDiagnoseOS: cmdlib.LUDiagnoseOS,
113 a8083063 Iustin Pop
    # exports lu
114 a8083063 Iustin Pop
    opcodes.OpQueryExports: cmdlib.LUQueryExports,
115 a8083063 Iustin Pop
    opcodes.OpExportInstance: cmdlib.LUExportInstance,
116 9ac99fda Guido Trotter
    opcodes.OpRemoveExport: cmdlib.LURemoveExport,
117 5c947f38 Iustin Pop
    # tags lu
118 5c947f38 Iustin Pop
    opcodes.OpGetTags: cmdlib.LUGetTags,
119 73415719 Iustin Pop
    opcodes.OpSearchTags: cmdlib.LUSearchTags,
120 f27302fa Iustin Pop
    opcodes.OpAddTags: cmdlib.LUAddTags,
121 f27302fa Iustin Pop
    opcodes.OpDelTags: cmdlib.LUDelTags,
122 06009e27 Iustin Pop
    # test lu
123 06009e27 Iustin Pop
    opcodes.OpTestDelay: cmdlib.LUTestDelay,
124 d61df03e Iustin Pop
    opcodes.OpTestAllocator: cmdlib.LUTestAllocator,
125 a8083063 Iustin Pop
    }
126 a8083063 Iustin Pop
127 f1048938 Iustin Pop
  def __init__(self, context):
128 a8083063 Iustin Pop
    """Constructor for Processor
129 a8083063 Iustin Pop

130 a8083063 Iustin Pop
    """
131 1c901d13 Guido Trotter
    self.context = context
132 031a3e57 Michael Hanselmann
    self._cbs = None
133 04864530 Guido Trotter
    self.exclusive_BGL = False
134 72737a7f Iustin Pop
    self.rpc = rpc.RpcRunner(context.cfg)
135 cd46f3b4 Luca Bigliardi
    self.hmclass = HooksMaster
136 a8083063 Iustin Pop
137 ef2df7d3 Michael Hanselmann
  def _ReportLocks(self, level, names, shared, acquired):
138 ef2df7d3 Michael Hanselmann
    """Reports lock operations.
139 ef2df7d3 Michael Hanselmann

140 ef2df7d3 Michael Hanselmann
    @type level: int
141 ef2df7d3 Michael Hanselmann
    @param level: Lock level
142 ef2df7d3 Michael Hanselmann
    @type names: list or string
143 ef2df7d3 Michael Hanselmann
    @param names: Lock names
144 ef2df7d3 Michael Hanselmann
    @type shared: bool
145 ef2df7d3 Michael Hanselmann
    @param shared: Whether the lock should be acquired in shared mode
146 ef2df7d3 Michael Hanselmann
    @type acquired: bool
147 ef2df7d3 Michael Hanselmann
    @param acquired: Whether the lock has already been acquired
148 ef2df7d3 Michael Hanselmann

149 ef2df7d3 Michael Hanselmann
    """
150 ef2df7d3 Michael Hanselmann
    parts = []
151 ef2df7d3 Michael Hanselmann
152 ef2df7d3 Michael Hanselmann
    # Build message
153 ef2df7d3 Michael Hanselmann
    if acquired:
154 ef2df7d3 Michael Hanselmann
      parts.append("acquired")
155 ef2df7d3 Michael Hanselmann
    else:
156 ef2df7d3 Michael Hanselmann
      parts.append("waiting")
157 ef2df7d3 Michael Hanselmann
158 ef2df7d3 Michael Hanselmann
    parts.append(locking.LEVEL_NAMES[level])
159 ef2df7d3 Michael Hanselmann
160 ef2df7d3 Michael Hanselmann
    if names == locking.ALL_SET:
161 ef2df7d3 Michael Hanselmann
      parts.append("ALL")
162 ef2df7d3 Michael Hanselmann
    elif isinstance(names, basestring):
163 ef2df7d3 Michael Hanselmann
      parts.append(names)
164 ef2df7d3 Michael Hanselmann
    else:
165 ef2df7d3 Michael Hanselmann
      parts.append(",".join(names))
166 ef2df7d3 Michael Hanselmann
167 ef2df7d3 Michael Hanselmann
    if shared:
168 ef2df7d3 Michael Hanselmann
      parts.append("shared")
169 ef2df7d3 Michael Hanselmann
    else:
170 ef2df7d3 Michael Hanselmann
      parts.append("exclusive")
171 ef2df7d3 Michael Hanselmann
172 ef2df7d3 Michael Hanselmann
    msg = "/".join(parts)
173 ef2df7d3 Michael Hanselmann
174 ef2df7d3 Michael Hanselmann
    logging.debug("LU locks %s", msg)
175 ef2df7d3 Michael Hanselmann
176 ef2df7d3 Michael Hanselmann
    if self._cbs:
177 ef2df7d3 Michael Hanselmann
      self._cbs.ReportLocks(msg)
178 ef2df7d3 Michael Hanselmann
179 36c381d7 Guido Trotter
  def _ExecLU(self, lu):
180 36c381d7 Guido Trotter
    """Logical Unit execution sequence.
181 36c381d7 Guido Trotter

182 36c381d7 Guido Trotter
    """
183 36c381d7 Guido Trotter
    write_count = self.context.cfg.write_count
184 36c381d7 Guido Trotter
    lu.CheckPrereq()
185 4b5e8271 Iustin Pop
    hm = HooksMaster(self.rpc.call_hooks_runner, lu)
186 36c381d7 Guido Trotter
    h_results = hm.RunPhase(constants.HOOKS_PHASE_PRE)
187 36c381d7 Guido Trotter
    lu.HooksCallBack(constants.HOOKS_PHASE_PRE, h_results,
188 031a3e57 Michael Hanselmann
                     self._Feedback, None)
189 20777413 Iustin Pop
190 20777413 Iustin Pop
    if getattr(lu.op, "dry_run", False):
191 20777413 Iustin Pop
      # in this mode, no post-hooks are run, and the config is not
192 20777413 Iustin Pop
      # written (as it might have been modified by another LU, and we
193 20777413 Iustin Pop
      # shouldn't do writeout on behalf of other threads
194 20777413 Iustin Pop
      self.LogInfo("dry-run mode requested, not actually executing"
195 20777413 Iustin Pop
                   " the operation")
196 20777413 Iustin Pop
      return lu.dry_run_result
197 20777413 Iustin Pop
198 36c381d7 Guido Trotter
    try:
199 031a3e57 Michael Hanselmann
      result = lu.Exec(self._Feedback)
200 36c381d7 Guido Trotter
      h_results = hm.RunPhase(constants.HOOKS_PHASE_POST)
201 36c381d7 Guido Trotter
      result = lu.HooksCallBack(constants.HOOKS_PHASE_POST, h_results,
202 031a3e57 Michael Hanselmann
                                self._Feedback, result)
203 36c381d7 Guido Trotter
    finally:
204 36c381d7 Guido Trotter
      # FIXME: This needs locks if not lu_class.REQ_BGL
205 36c381d7 Guido Trotter
      if write_count != self.context.cfg.write_count:
206 36c381d7 Guido Trotter
        hm.RunConfigUpdate()
207 36c381d7 Guido Trotter
208 36c381d7 Guido Trotter
    return result
209 36c381d7 Guido Trotter
210 68adfdb2 Guido Trotter
  def _LockAndExecLU(self, lu, level):
211 68adfdb2 Guido Trotter
    """Execute a Logical Unit, with the needed locks.
212 68adfdb2 Guido Trotter

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

217 68adfdb2 Guido Trotter
    """
218 ca2a79e1 Guido Trotter
    adding_locks = level in lu.add_locks
219 ca2a79e1 Guido Trotter
    acquiring_locks = level in lu.needed_locks
220 8a2941c4 Guido Trotter
    if level not in locking.LEVELS:
221 031a3e57 Michael Hanselmann
      if self._cbs:
222 031a3e57 Michael Hanselmann
        self._cbs.NotifyStart()
223 031a3e57 Michael Hanselmann
224 8a2941c4 Guido Trotter
      result = self._ExecLU(lu)
225 ca2a79e1 Guido Trotter
    elif adding_locks and acquiring_locks:
226 ca2a79e1 Guido Trotter
      # We could both acquire and add locks at the same level, but for now we
227 ca2a79e1 Guido Trotter
      # don't need this, so we'll avoid the complicated code needed.
228 ca2a79e1 Guido Trotter
      raise NotImplementedError(
229 ca2a79e1 Guido Trotter
        "Can't declare locks to acquire when adding others")
230 ca2a79e1 Guido Trotter
    elif adding_locks or acquiring_locks:
231 fb8dcb62 Guido Trotter
      lu.DeclareLocks(level)
232 3977a4c1 Guido Trotter
      share = lu.share_locks[level]
233 ca2a79e1 Guido Trotter
      if acquiring_locks:
234 ca2a79e1 Guido Trotter
        needed_locks = lu.needed_locks[level]
235 ef2df7d3 Michael Hanselmann
236 ef2df7d3 Michael Hanselmann
        self._ReportLocks(level, needed_locks, share, False)
237 ca2a79e1 Guido Trotter
        lu.acquired_locks[level] = self.context.glm.acquire(level,
238 ca2a79e1 Guido Trotter
                                                            needed_locks,
239 ca2a79e1 Guido Trotter
                                                            shared=share)
240 ef2df7d3 Michael Hanselmann
        self._ReportLocks(level, needed_locks, share, True)
241 ef2df7d3 Michael Hanselmann
242 ca2a79e1 Guido Trotter
      else: # adding_locks
243 ca2a79e1 Guido Trotter
        add_locks = lu.add_locks[level]
244 ca2a79e1 Guido Trotter
        lu.remove_locks[level] = add_locks
245 ca2a79e1 Guido Trotter
        try:
246 ca2a79e1 Guido Trotter
          self.context.glm.add(level, add_locks, acquired=1, shared=share)
247 ca2a79e1 Guido Trotter
        except errors.LockError:
248 ca2a79e1 Guido Trotter
          raise errors.OpPrereqError(
249 5bbd3f7f Michael Hanselmann
            "Couldn't add locks (%s), probably because of a race condition"
250 ca2a79e1 Guido Trotter
            " with another job, who added them first" % add_locks)
251 68adfdb2 Guido Trotter
      try:
252 ca2a79e1 Guido Trotter
        try:
253 ca2a79e1 Guido Trotter
          if adding_locks:
254 ca2a79e1 Guido Trotter
            lu.acquired_locks[level] = add_locks
255 ca2a79e1 Guido Trotter
          result = self._LockAndExecLU(lu, level + 1)
256 ca2a79e1 Guido Trotter
        finally:
257 ca2a79e1 Guido Trotter
          if level in lu.remove_locks:
258 ca2a79e1 Guido Trotter
            self.context.glm.remove(level, lu.remove_locks[level])
259 68adfdb2 Guido Trotter
      finally:
260 80ee04a4 Guido Trotter
        if self.context.glm.is_owned(level):
261 68adfdb2 Guido Trotter
          self.context.glm.release(level)
262 68adfdb2 Guido Trotter
    else:
263 8a2941c4 Guido Trotter
      result = self._LockAndExecLU(lu, level + 1)
264 68adfdb2 Guido Trotter
265 68adfdb2 Guido Trotter
    return result
266 68adfdb2 Guido Trotter
267 031a3e57 Michael Hanselmann
  def ExecOpCode(self, op, cbs):
268 a8083063 Iustin Pop
    """Execute an opcode.
269 a8083063 Iustin Pop

270 e92376d7 Iustin Pop
    @type op: an OpCode instance
271 e92376d7 Iustin Pop
    @param op: the opcode to be executed
272 031a3e57 Michael Hanselmann
    @type cbs: L{OpExecCbBase}
273 031a3e57 Michael Hanselmann
    @param cbs: Runtime callbacks
274 a8083063 Iustin Pop

275 a8083063 Iustin Pop
    """
276 a8083063 Iustin Pop
    if not isinstance(op, opcodes.OpCode):
277 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Non-opcode instance passed"
278 3ecf6786 Iustin Pop
                                   " to ExecOpcode")
279 a8083063 Iustin Pop
280 031a3e57 Michael Hanselmann
    self._cbs = cbs
281 fe482621 Iustin Pop
    try:
282 031a3e57 Michael Hanselmann
      lu_class = self.DISPATCH_TABLE.get(op.__class__, None)
283 031a3e57 Michael Hanselmann
      if lu_class is None:
284 031a3e57 Michael Hanselmann
        raise errors.OpCodeUnknown("Unknown opcode")
285 031a3e57 Michael Hanselmann
286 031a3e57 Michael Hanselmann
      # Acquire the Big Ganeti Lock exclusively if this LU requires it, and in a
287 031a3e57 Michael Hanselmann
      # shared fashion otherwise (to prevent concurrent run with an exclusive
288 031a3e57 Michael Hanselmann
      # LU.
289 ef2df7d3 Michael Hanselmann
      self._ReportLocks(locking.LEVEL_CLUSTER, [locking.BGL],
290 ef2df7d3 Michael Hanselmann
                        not lu_class.REQ_BGL, False)
291 ef2df7d3 Michael Hanselmann
      try:
292 ef2df7d3 Michael Hanselmann
        self.context.glm.acquire(locking.LEVEL_CLUSTER, [locking.BGL],
293 ef2df7d3 Michael Hanselmann
                                 shared=not lu_class.REQ_BGL)
294 ef2df7d3 Michael Hanselmann
      finally:
295 ef2df7d3 Michael Hanselmann
        self._ReportLocks(locking.LEVEL_CLUSTER, [locking.BGL],
296 ef2df7d3 Michael Hanselmann
                          not lu_class.REQ_BGL, True)
297 031a3e57 Michael Hanselmann
      try:
298 031a3e57 Michael Hanselmann
        self.exclusive_BGL = lu_class.REQ_BGL
299 031a3e57 Michael Hanselmann
        lu = lu_class(self, op, self.context, self.rpc)
300 031a3e57 Michael Hanselmann
        lu.ExpandNames()
301 031a3e57 Michael Hanselmann
        assert lu.needed_locks is not None, "needed_locks not set by LU"
302 031a3e57 Michael Hanselmann
        result = self._LockAndExecLU(lu, locking.LEVEL_INSTANCE)
303 031a3e57 Michael Hanselmann
      finally:
304 031a3e57 Michael Hanselmann
        self.context.glm.release(locking.LEVEL_CLUSTER)
305 031a3e57 Michael Hanselmann
        self.exclusive_BGL = False
306 04864530 Guido Trotter
    finally:
307 031a3e57 Michael Hanselmann
      self._cbs = None
308 6a4aa7c1 Iustin Pop
309 a8083063 Iustin Pop
    return result
310 a8083063 Iustin Pop
311 031a3e57 Michael Hanselmann
  def _Feedback(self, *args):
312 031a3e57 Michael Hanselmann
    """Forward call to feedback callback function.
313 031a3e57 Michael Hanselmann

314 031a3e57 Michael Hanselmann
    """
315 031a3e57 Michael Hanselmann
    if self._cbs:
316 031a3e57 Michael Hanselmann
      self._cbs.Feedback(*args)
317 031a3e57 Michael Hanselmann
318 0fbbf897 Iustin Pop
  def LogStep(self, current, total, message):
319 0fbbf897 Iustin Pop
    """Log a change in LU execution progress.
320 0fbbf897 Iustin Pop

321 0fbbf897 Iustin Pop
    """
322 a5eb7789 Iustin Pop
    logging.debug("Step %d/%d %s", current, total, message)
323 031a3e57 Michael Hanselmann
    self._Feedback("STEP %d/%d %s" % (current, total, message))
324 0fbbf897 Iustin Pop
325 c0088fb9 Iustin Pop
  def LogWarning(self, message, *args, **kwargs):
326 0fbbf897 Iustin Pop
    """Log a warning to the logs and the user.
327 0fbbf897 Iustin Pop

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

333 c0088fb9 Iustin Pop
    """
334 c0088fb9 Iustin Pop
    assert not kwargs or (len(kwargs) == 1 and "hint" in kwargs), \
335 c0088fb9 Iustin Pop
           "Invalid keyword arguments for LogWarning (%s)" % str(kwargs)
336 c0088fb9 Iustin Pop
    if args:
337 c0088fb9 Iustin Pop
      message = message % tuple(args)
338 c0088fb9 Iustin Pop
    if message:
339 c0088fb9 Iustin Pop
      logging.warning(message)
340 031a3e57 Michael Hanselmann
      self._Feedback(" - WARNING: %s" % message)
341 c0088fb9 Iustin Pop
    if "hint" in kwargs:
342 031a3e57 Michael Hanselmann
      self._Feedback("      Hint: %s" % kwargs["hint"])
343 c0088fb9 Iustin Pop
344 c0088fb9 Iustin Pop
  def LogInfo(self, message, *args):
345 0fbbf897 Iustin Pop
    """Log an informational message to the logs and the user.
346 0fbbf897 Iustin Pop

347 0fbbf897 Iustin Pop
    """
348 c0088fb9 Iustin Pop
    if args:
349 c0088fb9 Iustin Pop
      message = message % tuple(args)
350 a5eb7789 Iustin Pop
    logging.info(message)
351 031a3e57 Michael Hanselmann
    self._Feedback(" - INFO: %s" % message)
352 0fbbf897 Iustin Pop
353 a8083063 Iustin Pop
354 a8083063 Iustin Pop
class HooksMaster(object):
355 a8083063 Iustin Pop
  """Hooks master.
356 a8083063 Iustin Pop

357 a8083063 Iustin Pop
  This class distributes the run commands to the nodes based on the
358 a8083063 Iustin Pop
  specific LU class.
359 a8083063 Iustin Pop

360 a8083063 Iustin Pop
  In order to remove the direct dependency on the rpc module, the
361 a8083063 Iustin Pop
  constructor needs a function which actually does the remote
362 a8083063 Iustin Pop
  call. This will usually be rpc.call_hooks_runner, but any function
363 a8083063 Iustin Pop
  which behaves the same works.
364 a8083063 Iustin Pop

365 a8083063 Iustin Pop
  """
366 4b5e8271 Iustin Pop
  def __init__(self, callfn, lu):
367 a8083063 Iustin Pop
    self.callfn = callfn
368 a8083063 Iustin Pop
    self.lu = lu
369 a8083063 Iustin Pop
    self.op = lu.op
370 a8083063 Iustin Pop
    self.env, node_list_pre, node_list_post = self._BuildEnv()
371 a8083063 Iustin Pop
    self.node_list = {constants.HOOKS_PHASE_PRE: node_list_pre,
372 a8083063 Iustin Pop
                      constants.HOOKS_PHASE_POST: node_list_post}
373 a8083063 Iustin Pop
374 a8083063 Iustin Pop
  def _BuildEnv(self):
375 a8083063 Iustin Pop
    """Compute the environment and the target nodes.
376 a8083063 Iustin Pop

377 a8083063 Iustin Pop
    Based on the opcode and the current node list, this builds the
378 a8083063 Iustin Pop
    environment for the hooks and the target node list for the run.
379 a8083063 Iustin Pop

380 a8083063 Iustin Pop
    """
381 a8083063 Iustin Pop
    env = {
382 a8083063 Iustin Pop
      "PATH": "/sbin:/bin:/usr/sbin:/usr/bin",
383 a8083063 Iustin Pop
      "GANETI_HOOKS_VERSION": constants.HOOKS_VERSION,
384 a8083063 Iustin Pop
      "GANETI_OP_CODE": self.op.OP_ID,
385 a8083063 Iustin Pop
      "GANETI_OBJECT_TYPE": self.lu.HTYPE,
386 6a4aa7c1 Iustin Pop
      "GANETI_DATA_DIR": constants.DATA_DIR,
387 a8083063 Iustin Pop
      }
388 a8083063 Iustin Pop
389 9a395a76 Iustin Pop
    if self.lu.HPATH is not None:
390 9a395a76 Iustin Pop
      lu_env, lu_nodes_pre, lu_nodes_post = self.lu.BuildHooksEnv()
391 9a395a76 Iustin Pop
      if lu_env:
392 9a395a76 Iustin Pop
        for key in lu_env:
393 9a395a76 Iustin Pop
          env["GANETI_" + key] = lu_env[key]
394 9a395a76 Iustin Pop
    else:
395 9a395a76 Iustin Pop
      lu_nodes_pre = lu_nodes_post = []
396 a8083063 Iustin Pop
397 4167825b Iustin Pop
    return env, frozenset(lu_nodes_pre), frozenset(lu_nodes_post)
398 4167825b Iustin Pop
399 4167825b Iustin Pop
  def _RunWrapper(self, node_list, hpath, phase):
400 4167825b Iustin Pop
    """Simple wrapper over self.callfn.
401 4167825b Iustin Pop

402 4167825b Iustin Pop
    This method fixes the environment before doing the rpc call.
403 4167825b Iustin Pop

404 4167825b Iustin Pop
    """
405 4167825b Iustin Pop
    env = self.env.copy()
406 4167825b Iustin Pop
    env["GANETI_HOOKS_PHASE"] = phase
407 4167825b Iustin Pop
    env["GANETI_HOOKS_PATH"] = hpath
408 437138c9 Michael Hanselmann
    if self.lu.cfg is not None:
409 437138c9 Michael Hanselmann
      env["GANETI_CLUSTER"] = self.lu.cfg.GetClusterName()
410 437138c9 Michael Hanselmann
      env["GANETI_MASTER"] = self.lu.cfg.GetMasterNode()
411 a8083063 Iustin Pop
412 4167825b Iustin Pop
    env = dict([(str(key), str(val)) for key, val in env.iteritems()])
413 a8083063 Iustin Pop
414 4167825b Iustin Pop
    return self.callfn(node_list, hpath, phase, env)
415 a8083063 Iustin Pop
416 17e82923 Luca Bigliardi
  def RunPhase(self, phase, nodes=None):
417 a8083063 Iustin Pop
    """Run all the scripts for a phase.
418 a8083063 Iustin Pop

419 a8083063 Iustin Pop
    This is the main function of the HookMaster.
420 a8083063 Iustin Pop

421 8dca23a3 Iustin Pop
    @param phase: one of L{constants.HOOKS_PHASE_POST} or
422 8dca23a3 Iustin Pop
        L{constants.HOOKS_PHASE_PRE}; it denotes the hooks phase
423 17e82923 Luca Bigliardi
    @param nodes: overrides the predefined list of nodes for the given phase
424 8dca23a3 Iustin Pop
    @return: the processed results of the hooks multi-node rpc call
425 8dca23a3 Iustin Pop
    @raise errors.HooksFailure: on communication failure to the nodes
426 6ef2dc74 Luca Bigliardi
    @raise errors.HooksAbort: on failure of one of the hooks
427 b07a6922 Guido Trotter

428 a8083063 Iustin Pop
    """
429 17e82923 Luca Bigliardi
    if not self.node_list[phase] and not nodes:
430 9a395a76 Iustin Pop
      # empty node list, we should not attempt to run this as either
431 9a395a76 Iustin Pop
      # we're in the cluster init phase and the rpc client part can't
432 9a395a76 Iustin Pop
      # even attempt to run, or this LU doesn't do hooks at all
433 a8083063 Iustin Pop
      return
434 4167825b Iustin Pop
    hpath = self.lu.HPATH
435 17e82923 Luca Bigliardi
    if nodes is not None:
436 17e82923 Luca Bigliardi
      results = self._RunWrapper(nodes, hpath, phase)
437 17e82923 Luca Bigliardi
    else:
438 17e82923 Luca Bigliardi
      results = self._RunWrapper(self.node_list[phase], hpath, phase)
439 8c4b9364 Luca Bigliardi
    errs = []
440 8c4b9364 Luca Bigliardi
    if not results:
441 8c4b9364 Luca Bigliardi
      msg = "Communication Failure"
442 8c4b9364 Luca Bigliardi
      if phase == constants.HOOKS_PHASE_PRE:
443 8c4b9364 Luca Bigliardi
        raise errors.HooksFailure(msg)
444 8c4b9364 Luca Bigliardi
      else:
445 8c4b9364 Luca Bigliardi
        self.lu.LogWarning(msg)
446 640b961e Luca Bigliardi
        return results
447 8c4b9364 Luca Bigliardi
    for node_name in results:
448 8c4b9364 Luca Bigliardi
      res = results[node_name]
449 8c4b9364 Luca Bigliardi
      if res.offline:
450 8c4b9364 Luca Bigliardi
        continue
451 3cebe102 Michael Hanselmann
      msg = res.fail_msg
452 8c4b9364 Luca Bigliardi
      if msg:
453 8c4b9364 Luca Bigliardi
        self.lu.LogWarning("Communication failure to node %s: %s",
454 8c4b9364 Luca Bigliardi
                           node_name, msg)
455 8c4b9364 Luca Bigliardi
        continue
456 8c4b9364 Luca Bigliardi
      for script, hkr, output in res.payload:
457 8c4b9364 Luca Bigliardi
        if hkr == constants.HKR_FAIL:
458 8c4b9364 Luca Bigliardi
          if phase == constants.HOOKS_PHASE_PRE:
459 a8083063 Iustin Pop
            errs.append((node_name, script, output))
460 8c4b9364 Luca Bigliardi
          else:
461 8c4b9364 Luca Bigliardi
            if not output:
462 640b961e Luca Bigliardi
              output = "(no output)"
463 8c4b9364 Luca Bigliardi
            self.lu.LogWarning("On %s script %s failed, output: %s" %
464 8c4b9364 Luca Bigliardi
                               (node_name, script, output))
465 8c4b9364 Luca Bigliardi
    if errs and phase == constants.HOOKS_PHASE_PRE:
466 8c4b9364 Luca Bigliardi
      raise errors.HooksAbort(errs)
467 b07a6922 Guido Trotter
    return results
468 6a4aa7c1 Iustin Pop
469 6a4aa7c1 Iustin Pop
  def RunConfigUpdate(self):
470 6a4aa7c1 Iustin Pop
    """Run the special configuration update hook
471 6a4aa7c1 Iustin Pop

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

475 6a4aa7c1 Iustin Pop
    """
476 6a4aa7c1 Iustin Pop
    phase = constants.HOOKS_PHASE_POST
477 6a4aa7c1 Iustin Pop
    hpath = constants.HOOKS_NAME_CFGUPDATE
478 437138c9 Michael Hanselmann
    nodes = [self.lu.cfg.GetMasterNode()]
479 29921401 Iustin Pop
    self._RunWrapper(nodes, hpath, phase)