Statistics
| Branch: | Tag: | Revision:

root / lib / mcpu.py @ b3989551

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.sstore = None
101
    self._feedback_fn = feedback
102

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

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

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

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

    
118
    if self.cfg is None:
119
      self.cfg = config.ConfigWriter()
120
      if lu_class.REQ_WSSTORE:
121
        self.sstore = ssconf.WritableSimpleStore()
122
      else:
123
        self.sstore = ssconf.SimpleStore()
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, self.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 self.cfg is None:
167
      self.cfg = config.ConfigWriter()
168
      self.sstore = ssconf.SimpleStore()
169
    #do_hooks = lu_class.HPATH is not None
170
    lu = lu_class(self, op, self.cfg, self.sstore)
171
    lu.CheckPrereq()
172
    #if do_hooks:
173
    #  hm = HooksMaster(rpc.call_hooks_runner, self, lu)
174
    #  h_results = hm.RunPhase(constants.HOOKS_PHASE_PRE)
175
    #  lu.HooksCallBack(constants.HOOKS_PHASE_PRE,
176
    #                   h_results, self._feedback_fn, None)
177
    result = lu.Exec(self._feedback_fn)
178
    #if do_hooks:
179
    #  h_results = hm.RunPhase(constants.HOOKS_PHASE_POST)
180
    #  result = lu.HooksCallBack(constants.HOOKS_PHASE_POST,
181
    #                   h_results, self._feedback_fn, result)
182
    return result
183

    
184
  def LogStep(self, current, total, message):
185
    """Log a change in LU execution progress.
186

187
    """
188
    logger.Debug("Step %d/%d %s" % (current, total, message))
189
    self._feedback_fn("STEP %d/%d %s" % (current, total, message))
190

    
191
  def LogWarning(self, message, hint=None):
192
    """Log a warning to the logs and the user.
193

194
    """
195
    logger.Error(message)
196
    self._feedback_fn(" - WARNING: %s" % message)
197
    if hint:
198
      self._feedback_fn("      Hint: %s" % hint)
199

    
200
  def LogInfo(self, message):
201
    """Log an informational message to the logs and the user.
202

203
    """
204
    logger.Info(message)
205
    self._feedback_fn(" - INFO: %s" % message)
206

    
207

    
208
class HooksMaster(object):
209
  """Hooks master.
210

211
  This class distributes the run commands to the nodes based on the
212
  specific LU class.
213

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

219
  """
220
  def __init__(self, callfn, proc, lu):
221
    self.callfn = callfn
222
    self.proc = proc
223
    self.lu = lu
224
    self.op = lu.op
225
    self.env, node_list_pre, node_list_post = self._BuildEnv()
226
    self.node_list = {constants.HOOKS_PHASE_PRE: node_list_pre,
227
                      constants.HOOKS_PHASE_POST: node_list_post}
228

    
229
  def _BuildEnv(self):
230
    """Compute the environment and the target nodes.
231

232
    Based on the opcode and the current node list, this builds the
233
    environment for the hooks and the target node list for the run.
234

235
    """
236
    env = {
237
      "PATH": "/sbin:/bin:/usr/sbin:/usr/bin",
238
      "GANETI_HOOKS_VERSION": constants.HOOKS_VERSION,
239
      "GANETI_OP_CODE": self.op.OP_ID,
240
      "GANETI_OBJECT_TYPE": self.lu.HTYPE,
241
      "GANETI_DATA_DIR": constants.DATA_DIR,
242
      }
243

    
244
    if self.lu.HPATH is not None:
245
      lu_env, lu_nodes_pre, lu_nodes_post = self.lu.BuildHooksEnv()
246
      if lu_env:
247
        for key in lu_env:
248
          env["GANETI_" + key] = lu_env[key]
249
    else:
250
      lu_nodes_pre = lu_nodes_post = []
251

    
252
    return env, frozenset(lu_nodes_pre), frozenset(lu_nodes_post)
253

    
254
  def _RunWrapper(self, node_list, hpath, phase):
255
    """Simple wrapper over self.callfn.
256

257
    This method fixes the environment before doing the rpc call.
258

259
    """
260
    env = self.env.copy()
261
    env["GANETI_HOOKS_PHASE"] = phase
262
    env["GANETI_HOOKS_PATH"] = hpath
263
    if self.lu.sstore is not None:
264
      env["GANETI_CLUSTER"] = self.lu.sstore.GetClusterName()
265
      env["GANETI_MASTER"] = self.lu.sstore.GetMasterNode()
266

    
267
    env = dict([(str(key), str(val)) for key, val in env.iteritems()])
268

    
269
    return self.callfn(node_list, hpath, phase, env)
270

    
271
  def RunPhase(self, phase):
272
    """Run all the scripts for a phase.
273

274
    This is the main function of the HookMaster.
275

276
    Args:
277
      phase: the hooks phase to run
278

279
    Returns:
280
      the result of the hooks multi-node rpc call
281

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

    
307
  def RunConfigUpdate(self):
308
    """Run the special configuration update hook
309

310
    This is a special hook that runs only on the master after each
311
    top-level LI if the configuration has been updated.
312

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