Statistics
| Branch: | Tag: | Revision:

root / lib / mcpu.py @ 150e978f

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
    # 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.OpAddMDDRBDComponent: cmdlib.LUAddMDDRBDComponent,
70
    opcodes.OpRemoveMDDRBDComponent: cmdlib.LURemoveMDDRBDComponent,
71
    opcodes.OpReplaceDisks: cmdlib.LUReplaceDisks,
72
    opcodes.OpFailoverInstance: cmdlib.LUFailoverInstance,
73
    opcodes.OpConnectConsole: cmdlib.LUConnectConsole,
74
    opcodes.OpQueryInstances: cmdlib.LUQueryInstances,
75
    opcodes.OpQueryInstanceData: cmdlib.LUQueryInstanceData,
76
    opcodes.OpSetInstanceParms: cmdlib.LUSetInstanceParms,
77
    # os lu
78
    opcodes.OpDiagnoseOS: cmdlib.LUDiagnoseOS,
79
    # exports lu
80
    opcodes.OpQueryExports: cmdlib.LUQueryExports,
81
    opcodes.OpExportInstance: cmdlib.LUExportInstance,
82
    # tags lu
83
    opcodes.OpGetTags: cmdlib.LUGetTags,
84
    opcodes.OpSearchTags: cmdlib.LUSearchTags,
85
    opcodes.OpAddTags: cmdlib.LUAddTags,
86
    opcodes.OpDelTags: cmdlib.LUDelTags,
87
    }
88

    
89
  def __init__(self, feedback=None):
90
    """Constructor for Processor
91

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

    
100
  def ExecOpCode(self, op):
101
    """Execute an opcode.
102

103
    Args:
104
     - cfg: the configuration in which we execute this opcode
105
     - opcode: the opcode to be executed
106

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

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

    
116
    if lu_class.REQ_CLUSTER and self.cfg is None:
117
      self.cfg = config.ConfigWriter()
118
      self.sstore = ssconf.SimpleStore()
119
    if self.cfg is not None:
120
      write_count = self.cfg.write_count
121
    else:
122
      write_count = 0
123
    lu = lu_class(self, op, self.cfg, self.sstore)
124
    lu.CheckPrereq()
125
    hm = HooksMaster(rpc.call_hooks_runner, self, lu)
126
    hm.RunPhase(constants.HOOKS_PHASE_PRE)
127
    result = lu.Exec(self._feedback_fn)
128
    hm.RunPhase(constants.HOOKS_PHASE_POST)
129
    if lu.cfg is not None:
130
      # we use lu.cfg and not self.cfg as for init cluster, self.cfg
131
      # is None but lu.cfg has been recently initialized in the
132
      # lu.Exec method
133
      if write_count != lu.cfg.write_count:
134
        hm.RunConfigUpdate()
135

    
136
    return result
137

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

141
    This is used by LUs when they need to execute a child LU.
142

143
    Args:
144
     - opcode: the opcode to be executed
145

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

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

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

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

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

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

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

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

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

    
192

    
193
class HooksMaster(object):
194
  """Hooks master.
195

196
  This class distributes the run commands to the nodes based on the
197
  specific LU class.
198

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

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

    
214
  def _BuildEnv(self):
215
    """Compute the environment and the target nodes.
216

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

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

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

    
237
    return env, frozenset(lu_nodes_pre), frozenset(lu_nodes_post)
238

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

242
    This method fixes the environment before doing the rpc call.
243

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

    
252
    env = dict([(str(key), str(val)) for key, val in env.iteritems()])
253

    
254
    return self.callfn(node_list, hpath, phase, env)
255

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

259
    This is the main function of the HookMaster.
260

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

    
285
  def RunConfigUpdate(self):
286
    """Run the special configuration update hook
287

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

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