Statistics
| Branch: | Tag: | Revision:

root / lib / mcpu.py @ c6868e1d

History | View | Annotate | Download (10.7 kB)

1
#
2
#
3

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

    
21

    
22
"""Module implementing the logic behind the cluster operations
23

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

29
"""
30

    
31

    
32
from ganeti import opcodes
33
from ganeti import constants
34
from ganeti import errors
35
from ganeti import rpc
36
from ganeti import cmdlib
37
from ganeti import config
38
from ganeti import ssconf
39
from ganeti import logger
40

    
41

    
42
class Processor(object):
43
  """Object which runs OpCodes"""
44
  DISPATCH_TABLE = {
45
    # Cluster
46
    opcodes.OpDestroyCluster: cmdlib.LUDestroyCluster,
47
    opcodes.OpQueryClusterInfo: cmdlib.LUQueryClusterInfo,
48
    opcodes.OpVerifyCluster: cmdlib.LUVerifyCluster,
49
    opcodes.OpMasterFailover: cmdlib.LUMasterFailover,
50
    opcodes.OpDumpClusterConfig: cmdlib.LUDumpClusterConfig,
51
    opcodes.OpRenameCluster: cmdlib.LURenameCluster,
52
    opcodes.OpVerifyDisks: cmdlib.LUVerifyDisks,
53
    opcodes.OpSetClusterParams: cmdlib.LUSetClusterParams,
54
    # node lu
55
    opcodes.OpAddNode: cmdlib.LUAddNode,
56
    opcodes.OpQueryNodes: cmdlib.LUQueryNodes,
57
    opcodes.OpQueryNodeVolumes: cmdlib.LUQueryNodeVolumes,
58
    opcodes.OpRemoveNode: cmdlib.LURemoveNode,
59
    # instance lu
60
    opcodes.OpCreateInstance: cmdlib.LUCreateInstance,
61
    opcodes.OpReinstallInstance: cmdlib.LUReinstallInstance,
62
    opcodes.OpRemoveInstance: cmdlib.LURemoveInstance,
63
    opcodes.OpRenameInstance: cmdlib.LURenameInstance,
64
    opcodes.OpActivateInstanceDisks: cmdlib.LUActivateInstanceDisks,
65
    opcodes.OpShutdownInstance: cmdlib.LUShutdownInstance,
66
    opcodes.OpStartupInstance: cmdlib.LUStartupInstance,
67
    opcodes.OpRebootInstance: cmdlib.LURebootInstance,
68
    opcodes.OpDeactivateInstanceDisks: cmdlib.LUDeactivateInstanceDisks,
69
    opcodes.OpReplaceDisks: cmdlib.LUReplaceDisks,
70
    opcodes.OpFailoverInstance: cmdlib.LUFailoverInstance,
71
    opcodes.OpConnectConsole: cmdlib.LUConnectConsole,
72
    opcodes.OpQueryInstances: cmdlib.LUQueryInstances,
73
    opcodes.OpQueryInstanceData: cmdlib.LUQueryInstanceData,
74
    opcodes.OpSetInstanceParams: cmdlib.LUSetInstanceParams,
75
    opcodes.OpGrowDisk: cmdlib.LUGrowDisk,
76
    # os lu
77
    opcodes.OpDiagnoseOS: cmdlib.LUDiagnoseOS,
78
    # exports lu
79
    opcodes.OpQueryExports: cmdlib.LUQueryExports,
80
    opcodes.OpExportInstance: cmdlib.LUExportInstance,
81
    opcodes.OpRemoveExport: cmdlib.LURemoveExport,
82
    # tags lu
83
    opcodes.OpGetTags: cmdlib.LUGetTags,
84
    opcodes.OpSearchTags: cmdlib.LUSearchTags,
85
    opcodes.OpAddTags: cmdlib.LUAddTags,
86
    opcodes.OpDelTags: cmdlib.LUDelTags,
87
    # test lu
88
    opcodes.OpTestDelay: cmdlib.LUTestDelay,
89
    opcodes.OpTestAllocator: cmdlib.LUTestAllocator,
90
    }
91

    
92
  def __init__(self, feedback=None):
93
    """Constructor for Processor
94

95
    Args:
96
     - feedback_fn: the feedback function (taking one string) to be run when
97
                    interesting events are happening
98
    """
99
    self.cfg = None
100
    self._feedback_fn = feedback
101

    
102
  def ExecOpCode(self, op):
103
    """Execute an opcode.
104

105
    Args:
106
      op: the opcode to be executed
107

108
    """
109
    if not isinstance(op, opcodes.OpCode):
110
      raise errors.ProgrammerError("Non-opcode instance passed"
111
                                   " to ExecOpcode")
112

    
113
    lu_class = self.DISPATCH_TABLE.get(op.__class__, None)
114
    if lu_class is None:
115
      raise errors.OpCodeUnknown("Unknown opcode")
116

    
117
    if lu_class.REQ_WSSTORE:
118
      sstore = ssconf.WritableSimpleStore()
119
    else:
120
      sstore = ssconf.SimpleStore()
121

    
122
    if self.cfg is None:
123
      self.cfg = config.ConfigWriter()
124
    if self.cfg is not None:
125
      write_count = self.cfg.write_count
126
    else:
127
      write_count = 0
128
    lu = lu_class(self, op, self.cfg, sstore)
129
    lu.CheckPrereq()
130
    hm = HooksMaster(rpc.call_hooks_runner, self, lu)
131
    h_results = hm.RunPhase(constants.HOOKS_PHASE_PRE)
132
    lu.HooksCallBack(constants.HOOKS_PHASE_PRE,
133
                     h_results, self._feedback_fn, None)
134
    try:
135
      result = lu.Exec(self._feedback_fn)
136
      h_results = hm.RunPhase(constants.HOOKS_PHASE_POST)
137
      result = lu.HooksCallBack(constants.HOOKS_PHASE_POST,
138
                       h_results, self._feedback_fn, result)
139
    finally:
140
      if lu.cfg is not None:
141
        # we use lu.cfg and not self.cfg as for init cluster, self.cfg
142
        # is None but lu.cfg has been recently initialized in the
143
        # lu.Exec method
144
        if write_count != lu.cfg.write_count:
145
          hm.RunConfigUpdate()
146

    
147
    return result
148

    
149
  def ChainOpCode(self, op):
150
    """Chain and execute an opcode.
151

152
    This is used by LUs when they need to execute a child LU.
153

154
    Args:
155
     - opcode: the opcode to be executed
156

157
    """
158
    if not isinstance(op, opcodes.OpCode):
159
      raise errors.ProgrammerError("Non-opcode instance passed"
160
                                   " to ExecOpcode")
161

    
162
    lu_class = self.DISPATCH_TABLE.get(op.__class__, None)
163
    if lu_class is None:
164
      raise errors.OpCodeUnknown("Unknown opcode")
165

    
166
    if lu_class.REQ_WSSTORE:
167
      sstore = ssconf.WritableSimpleStore()
168
    else:
169
      sstore = ssconf.SimpleStore()
170

    
171
    if self.cfg is None:
172
      self.cfg = config.ConfigWriter()
173
    #do_hooks = lu_class.HPATH is not None
174
    lu = lu_class(self, op, self.cfg, sstore)
175
    lu.CheckPrereq()
176
    #if do_hooks:
177
    #  hm = HooksMaster(rpc.call_hooks_runner, self, lu)
178
    #  h_results = hm.RunPhase(constants.HOOKS_PHASE_PRE)
179
    #  lu.HooksCallBack(constants.HOOKS_PHASE_PRE,
180
    #                   h_results, self._feedback_fn, None)
181
    result = lu.Exec(self._feedback_fn)
182
    #if do_hooks:
183
    #  h_results = hm.RunPhase(constants.HOOKS_PHASE_POST)
184
    #  result = lu.HooksCallBack(constants.HOOKS_PHASE_POST,
185
    #                   h_results, self._feedback_fn, result)
186
    return result
187

    
188
  def LogStep(self, current, total, message):
189
    """Log a change in LU execution progress.
190

191
    """
192
    logger.Debug("Step %d/%d %s" % (current, total, message))
193
    self._feedback_fn("STEP %d/%d %s" % (current, total, message))
194

    
195
  def LogWarning(self, message, hint=None):
196
    """Log a warning to the logs and the user.
197

198
    """
199
    logger.Error(message)
200
    self._feedback_fn(" - WARNING: %s" % message)
201
    if hint:
202
      self._feedback_fn("      Hint: %s" % hint)
203

    
204
  def LogInfo(self, message):
205
    """Log an informational message to the logs and the user.
206

207
    """
208
    logger.Info(message)
209
    self._feedback_fn(" - INFO: %s" % message)
210

    
211

    
212
class HooksMaster(object):
213
  """Hooks master.
214

215
  This class distributes the run commands to the nodes based on the
216
  specific LU class.
217

218
  In order to remove the direct dependency on the rpc module, the
219
  constructor needs a function which actually does the remote
220
  call. This will usually be rpc.call_hooks_runner, but any function
221
  which behaves the same works.
222

223
  """
224
  def __init__(self, callfn, proc, lu):
225
    self.callfn = callfn
226
    self.proc = proc
227
    self.lu = lu
228
    self.op = lu.op
229
    self.env, node_list_pre, node_list_post = self._BuildEnv()
230
    self.node_list = {constants.HOOKS_PHASE_PRE: node_list_pre,
231
                      constants.HOOKS_PHASE_POST: node_list_post}
232

    
233
  def _BuildEnv(self):
234
    """Compute the environment and the target nodes.
235

236
    Based on the opcode and the current node list, this builds the
237
    environment for the hooks and the target node list for the run.
238

239
    """
240
    env = {
241
      "PATH": "/sbin:/bin:/usr/sbin:/usr/bin",
242
      "GANETI_HOOKS_VERSION": constants.HOOKS_VERSION,
243
      "GANETI_OP_CODE": self.op.OP_ID,
244
      "GANETI_OBJECT_TYPE": self.lu.HTYPE,
245
      "GANETI_DATA_DIR": constants.DATA_DIR,
246
      }
247

    
248
    if self.lu.HPATH is not None:
249
      lu_env, lu_nodes_pre, lu_nodes_post = self.lu.BuildHooksEnv()
250
      if lu_env:
251
        for key in lu_env:
252
          env["GANETI_" + key] = lu_env[key]
253
    else:
254
      lu_nodes_pre = lu_nodes_post = []
255

    
256
    return env, frozenset(lu_nodes_pre), frozenset(lu_nodes_post)
257

    
258
  def _RunWrapper(self, node_list, hpath, phase):
259
    """Simple wrapper over self.callfn.
260

261
    This method fixes the environment before doing the rpc call.
262

263
    """
264
    env = self.env.copy()
265
    env["GANETI_HOOKS_PHASE"] = phase
266
    env["GANETI_HOOKS_PATH"] = hpath
267
    if self.lu.sstore is not None:
268
      env["GANETI_CLUSTER"] = self.lu.sstore.GetClusterName()
269
      env["GANETI_MASTER"] = self.lu.sstore.GetMasterNode()
270

    
271
    env = dict([(str(key), str(val)) for key, val in env.iteritems()])
272

    
273
    return self.callfn(node_list, hpath, phase, env)
274

    
275
  def RunPhase(self, phase):
276
    """Run all the scripts for a phase.
277

278
    This is the main function of the HookMaster.
279

280
    Args:
281
      phase: the hooks phase to run
282

283
    Returns:
284
      the result of the hooks multi-node rpc call
285

286
    """
287
    if not self.node_list[phase]:
288
      # empty node list, we should not attempt to run this as either
289
      # we're in the cluster init phase and the rpc client part can't
290
      # even attempt to run, or this LU doesn't do hooks at all
291
      return
292
    hpath = self.lu.HPATH
293
    results = self._RunWrapper(self.node_list[phase], hpath, phase)
294
    if phase == constants.HOOKS_PHASE_PRE:
295
      errs = []
296
      if not results:
297
        raise errors.HooksFailure("Communication failure")
298
      for node_name in results:
299
        res = results[node_name]
300
        if res is False or not isinstance(res, list):
301
          self.proc.LogWarning("Communication failure to node %s" % node_name)
302
          continue
303
        for script, hkr, output in res:
304
          if hkr == constants.HKR_FAIL:
305
            output = output.strip().encode("string_escape")
306
            errs.append((node_name, script, output))
307
      if errs:
308
        raise errors.HooksAbort(errs)
309
    return results
310

    
311
  def RunConfigUpdate(self):
312
    """Run the special configuration update hook
313

314
    This is a special hook that runs only on the master after each
315
    top-level LI if the configuration has been updated.
316

317
    """
318
    phase = constants.HOOKS_PHASE_POST
319
    hpath = constants.HOOKS_NAME_CFGUPDATE
320
    if self.lu.sstore is None:
321
      raise errors.ProgrammerError("Null sstore on config update hook")
322
    nodes = [self.lu.sstore.GetMasterNode()]
323
    results = self._RunWrapper(nodes, hpath, phase)