Statistics
| Branch: | Tag: | Revision:

root / lib / mcpu.py @ 7767bbf5

History | View | Annotate | Download (10 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.OpInitCluster: cmdlib.LUInitCluster,
47
    opcodes.OpDestroyCluster: cmdlib.LUDestroyCluster,
48
    opcodes.OpQueryClusterInfo: cmdlib.LUQueryClusterInfo,
49
    opcodes.OpClusterCopyFile: cmdlib.LUClusterCopyFile,
50
    opcodes.OpRunClusterCommand: cmdlib.LURunClusterCommand,
51
    opcodes.OpVerifyCluster: cmdlib.LUVerifyCluster,
52
    opcodes.OpMasterFailover: cmdlib.LUMasterFailover,
53
    opcodes.OpDumpClusterConfig: cmdlib.LUDumpClusterConfig,
54
    opcodes.OpRenameCluster: cmdlib.LURenameCluster,
55
    opcodes.OpVerifyDisks: cmdlib.LUVerifyDisks,
56
    opcodes.OpSetClusterParams: cmdlib.LUSetClusterParams,
57
    # node lu
58
    opcodes.OpAddNode: cmdlib.LUAddNode,
59
    opcodes.OpQueryNodes: cmdlib.LUQueryNodes,
60
    opcodes.OpQueryNodeVolumes: cmdlib.LUQueryNodeVolumes,
61
    opcodes.OpRemoveNode: cmdlib.LURemoveNode,
62
    # instance lu
63
    opcodes.OpCreateInstance: cmdlib.LUCreateInstance,
64
    opcodes.OpReinstallInstance: cmdlib.LUReinstallInstance,
65
    opcodes.OpRemoveInstance: cmdlib.LURemoveInstance,
66
    opcodes.OpRenameInstance: cmdlib.LURenameInstance,
67
    opcodes.OpActivateInstanceDisks: cmdlib.LUActivateInstanceDisks,
68
    opcodes.OpShutdownInstance: cmdlib.LUShutdownInstance,
69
    opcodes.OpStartupInstance: cmdlib.LUStartupInstance,
70
    opcodes.OpRebootInstance: cmdlib.LURebootInstance,
71
    opcodes.OpDeactivateInstanceDisks: cmdlib.LUDeactivateInstanceDisks,
72
    opcodes.OpReplaceDisks: cmdlib.LUReplaceDisks,
73
    opcodes.OpFailoverInstance: cmdlib.LUFailoverInstance,
74
    opcodes.OpConnectConsole: cmdlib.LUConnectConsole,
75
    opcodes.OpQueryInstances: cmdlib.LUQueryInstances,
76
    opcodes.OpQueryInstanceData: cmdlib.LUQueryInstanceData,
77
    opcodes.OpSetInstanceParams: cmdlib.LUSetInstanceParams,
78
    # os lu
79
    opcodes.OpDiagnoseOS: cmdlib.LUDiagnoseOS,
80
    # exports lu
81
    opcodes.OpQueryExports: cmdlib.LUQueryExports,
82
    opcodes.OpExportInstance: cmdlib.LUExportInstance,
83
    # tags lu
84
    opcodes.OpGetTags: cmdlib.LUGetTags,
85
    opcodes.OpSearchTags: cmdlib.LUSearchTags,
86
    opcodes.OpAddTags: cmdlib.LUAddTags,
87
    opcodes.OpDelTags: cmdlib.LUDelTags,
88
    # test lu
89
    opcodes.OpTestDelay: cmdlib.LUTestDelay,
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 lu_class.REQ_CLUSTER and self.cfg is None:
119
      self.cfg = config.ConfigWriter()
120
      self.sstore = ssconf.SimpleStore()
121
    if self.cfg is not None:
122
      write_count = self.cfg.write_count
123
    else:
124
      write_count = 0
125
    lu = lu_class(self, op, self.cfg, self.sstore)
126
    lu.CheckPrereq()
127
    hm = HooksMaster(rpc.call_hooks_runner, self, lu)
128
    hm.RunPhase(constants.HOOKS_PHASE_PRE)
129
    try:
130
      result = lu.Exec(self._feedback_fn)
131
      hm.RunPhase(constants.HOOKS_PHASE_POST)
132
    finally:
133
      if lu.cfg is not None:
134
        # we use lu.cfg and not self.cfg as for init cluster, self.cfg
135
        # is None but lu.cfg has been recently initialized in the
136
        # lu.Exec method
137
        if write_count != lu.cfg.write_count:
138
          hm.RunConfigUpdate()
139

    
140
    return result
141

    
142
  def ChainOpCode(self, op):
143
    """Chain and execute an opcode.
144

145
    This is used by LUs when they need to execute a child LU.
146

147
    Args:
148
     - opcode: the opcode to be executed
149

150
    """
151
    if not isinstance(op, opcodes.OpCode):
152
      raise errors.ProgrammerError("Non-opcode instance passed"
153
                                   " to ExecOpcode")
154

    
155
    lu_class = self.DISPATCH_TABLE.get(op.__class__, None)
156
    if lu_class is None:
157
      raise errors.OpCodeUnknown("Unknown opcode")
158

    
159
    if lu_class.REQ_CLUSTER and self.cfg is None:
160
      self.cfg = config.ConfigWriter()
161
      self.sstore = ssconf.SimpleStore()
162
    #do_hooks = lu_class.HPATH is not None
163
    lu = lu_class(self, op, self.cfg, self.sstore)
164
    lu.CheckPrereq()
165
    #if do_hooks:
166
    #  hm = HooksMaster(rpc.call_hooks_runner, self, lu)
167
    #  hm.RunPhase(constants.HOOKS_PHASE_PRE)
168
    result = lu.Exec(self._feedback_fn)
169
    #if do_hooks:
170
    #  hm.RunPhase(constants.HOOKS_PHASE_POST)
171
    return result
172

    
173
  def LogStep(self, current, total, message):
174
    """Log a change in LU execution progress.
175

176
    """
177
    logger.Debug("Step %d/%d %s" % (current, total, message))
178
    self._feedback_fn("STEP %d/%d %s" % (current, total, message))
179

    
180
  def LogWarning(self, message, hint=None):
181
    """Log a warning to the logs and the user.
182

183
    """
184
    logger.Error(message)
185
    self._feedback_fn(" - WARNING: %s" % message)
186
    if hint:
187
      self._feedback_fn("      Hint: %s" % hint)
188

    
189
  def LogInfo(self, message):
190
    """Log an informational message to the logs and the user.
191

192
    """
193
    logger.Info(message)
194
    self._feedback_fn(" - INFO: %s" % message)
195

    
196

    
197
class HooksMaster(object):
198
  """Hooks master.
199

200
  This class distributes the run commands to the nodes based on the
201
  specific LU class.
202

203
  In order to remove the direct dependency on the rpc module, the
204
  constructor needs a function which actually does the remote
205
  call. This will usually be rpc.call_hooks_runner, but any function
206
  which behaves the same works.
207

208
  """
209
  def __init__(self, callfn, proc, lu):
210
    self.callfn = callfn
211
    self.proc = proc
212
    self.lu = lu
213
    self.op = lu.op
214
    self.env, node_list_pre, node_list_post = self._BuildEnv()
215
    self.node_list = {constants.HOOKS_PHASE_PRE: node_list_pre,
216
                      constants.HOOKS_PHASE_POST: node_list_post}
217

    
218
  def _BuildEnv(self):
219
    """Compute the environment and the target nodes.
220

221
    Based on the opcode and the current node list, this builds the
222
    environment for the hooks and the target node list for the run.
223

224
    """
225
    env = {
226
      "PATH": "/sbin:/bin:/usr/sbin:/usr/bin",
227
      "GANETI_HOOKS_VERSION": constants.HOOKS_VERSION,
228
      "GANETI_OP_CODE": self.op.OP_ID,
229
      "GANETI_OBJECT_TYPE": self.lu.HTYPE,
230
      "GANETI_DATA_DIR": constants.DATA_DIR,
231
      }
232

    
233
    if self.lu.HPATH is not None:
234
      lu_env, lu_nodes_pre, lu_nodes_post = self.lu.BuildHooksEnv()
235
      if lu_env:
236
        for key in lu_env:
237
          env["GANETI_" + key] = lu_env[key]
238
    else:
239
      lu_nodes_pre = lu_nodes_post = []
240

    
241
    return env, frozenset(lu_nodes_pre), frozenset(lu_nodes_post)
242

    
243
  def _RunWrapper(self, node_list, hpath, phase):
244
    """Simple wrapper over self.callfn.
245

246
    This method fixes the environment before doing the rpc call.
247

248
    """
249
    env = self.env.copy()
250
    env["GANETI_HOOKS_PHASE"] = phase
251
    env["GANETI_HOOKS_PATH"] = hpath
252
    if self.lu.sstore is not None:
253
      env["GANETI_CLUSTER"] = self.lu.sstore.GetClusterName()
254
      env["GANETI_MASTER"] = self.lu.sstore.GetMasterNode()
255

    
256
    env = dict([(str(key), str(val)) for key, val in env.iteritems()])
257

    
258
    return self.callfn(node_list, hpath, phase, env)
259

    
260
  def RunPhase(self, phase):
261
    """Run all the scripts for a phase.
262

263
    This is the main function of the HookMaster.
264

265
    """
266
    if not self.node_list[phase]:
267
      # empty node list, we should not attempt to run this as either
268
      # we're in the cluster init phase and the rpc client part can't
269
      # even attempt to run, or this LU doesn't do hooks at all
270
      return
271
    hpath = self.lu.HPATH
272
    results = self._RunWrapper(self.node_list[phase], hpath, phase)
273
    if phase == constants.HOOKS_PHASE_PRE:
274
      errs = []
275
      if not results:
276
        raise errors.HooksFailure("Communication failure")
277
      for node_name in results:
278
        res = results[node_name]
279
        if res is False or not isinstance(res, list):
280
          self.proc.LogWarning("Communication failure to node %s" % node_name)
281
          continue
282
        for script, hkr, output in res:
283
          if hkr == constants.HKR_FAIL:
284
            output = output.strip().encode("string_escape")
285
            errs.append((node_name, script, output))
286
      if errs:
287
        raise errors.HooksAbort(errs)
288

    
289
  def RunConfigUpdate(self):
290
    """Run the special configuration update hook
291

292
    This is a special hook that runs only on the master after each
293
    top-level LI if the configuration has been updated.
294

295
    """
296
    phase = constants.HOOKS_PHASE_POST
297
    hpath = constants.HOOKS_NAME_CFGUPDATE
298
    if self.lu.sstore is None:
299
      raise errors.ProgrammerError("Null sstore on config update hook")
300
    nodes = [self.lu.sstore.GetMasterNode()]
301
    results = self._RunWrapper(nodes, hpath, phase)