Statistics
| Branch: | Tag: | Revision:

root / lib / mcpu.py @ b74159ee

History | View | Annotate | Download (10.1 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
    opcodes.OpRemoveExport: cmdlib.LURemoveExport,
84
    # tags lu
85
    opcodes.OpGetTags: cmdlib.LUGetTags,
86
    opcodes.OpSearchTags: cmdlib.LUSearchTags,
87
    opcodes.OpAddTags: cmdlib.LUAddTags,
88
    opcodes.OpDelTags: cmdlib.LUDelTags,
89
    # test lu
90
    opcodes.OpTestDelay: cmdlib.LUTestDelay,
91
    opcodes.OpTestAllocator: cmdlib.LUTestAllocator,
92
    }
93

    
94
  def __init__(self, feedback=None):
95
    """Constructor for Processor
96

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

    
105
  def ExecOpCode(self, op):
106
    """Execute an opcode.
107

108
    Args:
109
      op: the opcode to be executed
110

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

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

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

    
142
    return result
143

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

147
    This is used by LUs when they need to execute a child LU.
148

149
    Args:
150
     - opcode: the opcode to be executed
151

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

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

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

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

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

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

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

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

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

    
198

    
199
class HooksMaster(object):
200
  """Hooks master.
201

202
  This class distributes the run commands to the nodes based on the
203
  specific LU class.
204

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

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

    
220
  def _BuildEnv(self):
221
    """Compute the environment and the target nodes.
222

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

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

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

    
243
    return env, frozenset(lu_nodes_pre), frozenset(lu_nodes_post)
244

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

248
    This method fixes the environment before doing the rpc call.
249

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

    
258
    env = dict([(str(key), str(val)) for key, val in env.iteritems()])
259

    
260
    return self.callfn(node_list, hpath, phase, env)
261

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

265
    This is the main function of the HookMaster.
266

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

    
291
  def RunConfigUpdate(self):
292
    """Run the special configuration update hook
293

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

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