Statistics
| Branch: | Tag: | Revision:

root / lib / mcpu.py @ 3b7ed473

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
class Processor(object):
42
  """Object which runs OpCodes"""
43
  DISPATCH_TABLE = {
44
    # Cluster
45
    opcodes.OpInitCluster: cmdlib.LUInitCluster,
46
    opcodes.OpDestroyCluster: cmdlib.LUDestroyCluster,
47
    opcodes.OpQueryClusterInfo: cmdlib.LUQueryClusterInfo,
48
    opcodes.OpClusterCopyFile: cmdlib.LUClusterCopyFile,
49
    opcodes.OpRunClusterCommand: cmdlib.LURunClusterCommand,
50
    opcodes.OpVerifyCluster: cmdlib.LUVerifyCluster,
51
    opcodes.OpMasterFailover: cmdlib.LUMasterFailover,
52
    opcodes.OpDumpClusterConfig: cmdlib.LUDumpClusterConfig,
53
    opcodes.OpRenameCluster: cmdlib.LURenameCluster,
54
    opcodes.OpVerifyDisks: cmdlib.LUVerifyDisks,
55
    # node lu
56
    opcodes.OpAddNode: cmdlib.LUAddNode,
57
    opcodes.OpQueryNodes: cmdlib.LUQueryNodes,
58
    opcodes.OpQueryNodeVolumes: cmdlib.LUQueryNodeVolumes,
59
    opcodes.OpRemoveNode: cmdlib.LURemoveNode,
60
    # instance lu
61
    opcodes.OpCreateInstance: cmdlib.LUCreateInstance,
62
    opcodes.OpReinstallInstance: cmdlib.LUReinstallInstance,
63
    opcodes.OpRemoveInstance: cmdlib.LURemoveInstance,
64
    opcodes.OpRenameInstance: cmdlib.LURenameInstance,
65
    opcodes.OpActivateInstanceDisks: cmdlib.LUActivateInstanceDisks,
66
    opcodes.OpShutdownInstance: cmdlib.LUShutdownInstance,
67
    opcodes.OpStartupInstance: cmdlib.LUStartupInstance,
68
    opcodes.OpRebootInstance: cmdlib.LURebootInstance,
69
    opcodes.OpDeactivateInstanceDisks: cmdlib.LUDeactivateInstanceDisks,
70
    opcodes.OpAddMDDRBDComponent: cmdlib.LUAddMDDRBDComponent,
71
    opcodes.OpRemoveMDDRBDComponent: cmdlib.LURemoveMDDRBDComponent,
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.OpSetInstanceParms: cmdlib.LUSetInstanceParms,
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
    result = lu.Exec(self._feedback_fn)
130
    hm.RunPhase(constants.HOOKS_PHASE_POST)
131
    if lu.cfg is not None:
132
      # we use lu.cfg and not self.cfg as for init cluster, self.cfg
133
      # is None but lu.cfg has been recently initialized in the
134
      # lu.Exec method
135
      if write_count != lu.cfg.write_count:
136
        hm.RunConfigUpdate()
137

    
138
    return result
139

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

143
    This is used by LUs when they need to execute a child LU.
144

145
    Args:
146
     - opcode: the opcode to be executed
147

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

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

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

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

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

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

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

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

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

    
194

    
195
class HooksMaster(object):
196
  """Hooks master.
197

198
  This class distributes the run commands to the nodes based on the
199
  specific LU class.
200

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

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

    
216
  def _BuildEnv(self):
217
    """Compute the environment and the target nodes.
218

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

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

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

    
239
    return env, frozenset(lu_nodes_pre), frozenset(lu_nodes_post)
240

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

244
    This method fixes the environment before doing the rpc call.
245

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

    
254
    env = dict([(str(key), str(val)) for key, val in env.iteritems()])
255

    
256
    return self.callfn(node_list, hpath, phase, env)
257

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

261
    This is the main function of the HookMaster.
262

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

    
287
  def RunConfigUpdate(self):
288
    """Run the special configuration update hook
289

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

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