Statistics
| Branch: | Tag: | Revision:

root / lib / mcpu.py @ 73415719

History | View | Annotate | Download (9.3 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

    
40
class Processor(object):
41
  """Object which runs OpCodes"""
42
  DISPATCH_TABLE = {
43
    # Cluster
44
    opcodes.OpInitCluster: cmdlib.LUInitCluster,
45
    opcodes.OpDestroyCluster: cmdlib.LUDestroyCluster,
46
    opcodes.OpQueryClusterInfo: cmdlib.LUQueryClusterInfo,
47
    opcodes.OpClusterCopyFile: cmdlib.LUClusterCopyFile,
48
    opcodes.OpRunClusterCommand: cmdlib.LURunClusterCommand,
49
    opcodes.OpVerifyCluster: cmdlib.LUVerifyCluster,
50
    opcodes.OpMasterFailover: cmdlib.LUMasterFailover,
51
    opcodes.OpDumpClusterConfig: cmdlib.LUDumpClusterConfig,
52
    opcodes.OpRenameCluster: cmdlib.LURenameCluster,
53
    # node lu
54
    opcodes.OpAddNode: cmdlib.LUAddNode,
55
    opcodes.OpQueryNodes: cmdlib.LUQueryNodes,
56
    opcodes.OpQueryNodeVolumes: cmdlib.LUQueryNodeVolumes,
57
    opcodes.OpRemoveNode: cmdlib.LURemoveNode,
58
    # instance lu
59
    opcodes.OpCreateInstance: cmdlib.LUCreateInstance,
60
    opcodes.OpReinstallInstance: cmdlib.LUReinstallInstance,
61
    opcodes.OpRemoveInstance: cmdlib.LURemoveInstance,
62
    opcodes.OpRenameInstance: cmdlib.LURenameInstance,
63
    opcodes.OpActivateInstanceDisks: cmdlib.LUActivateInstanceDisks,
64
    opcodes.OpShutdownInstance: cmdlib.LUShutdownInstance,
65
    opcodes.OpStartupInstance: cmdlib.LUStartupInstance,
66
    opcodes.OpRebootInstance: cmdlib.LURebootInstance,
67
    opcodes.OpDeactivateInstanceDisks: cmdlib.LUDeactivateInstanceDisks,
68
    opcodes.OpAddMDDRBDComponent: cmdlib.LUAddMDDRBDComponent,
69
    opcodes.OpRemoveMDDRBDComponent: cmdlib.LURemoveMDDRBDComponent,
70
    opcodes.OpReplaceDisks: cmdlib.LUReplaceDisks,
71
    opcodes.OpFailoverInstance: cmdlib.LUFailoverInstance,
72
    opcodes.OpConnectConsole: cmdlib.LUConnectConsole,
73
    opcodes.OpQueryInstances: cmdlib.LUQueryInstances,
74
    opcodes.OpQueryInstanceData: cmdlib.LUQueryInstanceData,
75
    opcodes.OpSetInstanceParms: cmdlib.LUSetInstanceParms,
76
    # os lu
77
    opcodes.OpDiagnoseOS: cmdlib.LUDiagnoseOS,
78
    # exports lu
79
    opcodes.OpQueryExports: cmdlib.LUQueryExports,
80
    opcodes.OpExportInstance: cmdlib.LUExportInstance,
81
    # tags lu
82
    opcodes.OpGetTags: cmdlib.LUGetTags,
83
    opcodes.OpSearchTags: cmdlib.LUSearchTags,
84
    opcodes.OpAddTags: cmdlib.LUAddTags,
85
    opcodes.OpDelTags: cmdlib.LUDelTags,
86
    }
87

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

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

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

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

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

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

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

    
135
    return result
136

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

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

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

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

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

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

    
168

    
169
class HooksMaster(object):
170
  """Hooks master.
171

172
  This class distributes the run commands to the nodes based on the
173
  specific LU class.
174

175
  In order to remove the direct dependency on the rpc module, the
176
  constructor needs a function which actually does the remote
177
  call. This will usually be rpc.call_hooks_runner, but any function
178
  which behaves the same works.
179

180
  """
181
  def __init__(self, callfn, lu):
182
    self.callfn = callfn
183
    self.lu = lu
184
    self.op = lu.op
185
    self.env, node_list_pre, node_list_post = self._BuildEnv()
186
    self.node_list = {constants.HOOKS_PHASE_PRE: node_list_pre,
187
                      constants.HOOKS_PHASE_POST: node_list_post}
188

    
189
  def _BuildEnv(self):
190
    """Compute the environment and the target nodes.
191

192
    Based on the opcode and the current node list, this builds the
193
    environment for the hooks and the target node list for the run.
194

195
    """
196
    env = {
197
      "PATH": "/sbin:/bin:/usr/sbin:/usr/bin",
198
      "GANETI_HOOKS_VERSION": constants.HOOKS_VERSION,
199
      "GANETI_OP_CODE": self.op.OP_ID,
200
      "GANETI_OBJECT_TYPE": self.lu.HTYPE,
201
      "GANETI_DATA_DIR": constants.DATA_DIR,
202
      }
203

    
204
    if self.lu.HPATH is not None:
205
      lu_env, lu_nodes_pre, lu_nodes_post = self.lu.BuildHooksEnv()
206
      if lu_env:
207
        for key in lu_env:
208
          env["GANETI_" + key] = lu_env[key]
209
    else:
210
      lu_nodes_pre = lu_nodes_post = []
211

    
212
    return env, frozenset(lu_nodes_pre), frozenset(lu_nodes_post)
213

    
214
  def _RunWrapper(self, node_list, hpath, phase):
215
    """Simple wrapper over self.callfn.
216

217
    This method fixes the environment before doing the rpc call.
218

219
    """
220
    env = self.env.copy()
221
    env["GANETI_HOOKS_PHASE"] = phase
222
    env["GANETI_HOOKS_PATH"] = hpath
223
    if self.lu.sstore is not None:
224
      env["GANETI_CLUSTER"] = self.lu.sstore.GetClusterName()
225
      env["GANETI_MASTER"] = self.lu.sstore.GetMasterNode()
226

    
227
    env = dict([(str(key), str(val)) for key, val in env.iteritems()])
228

    
229
    return self.callfn(node_list, hpath, phase, env)
230

    
231
  def RunPhase(self, phase):
232
    """Run all the scripts for a phase.
233

234
    This is the main function of the HookMaster.
235

236
    """
237
    if not self.node_list[phase]:
238
      # empty node list, we should not attempt to run this as either
239
      # we're in the cluster init phase and the rpc client part can't
240
      # even attempt to run, or this LU doesn't do hooks at all
241
      return
242
    hpath = self.lu.HPATH
243
    results = self._RunWrapper(self.node_list[phase], hpath, phase)
244
    if phase == constants.HOOKS_PHASE_PRE:
245
      errs = []
246
      if not results:
247
        raise errors.HooksFailure("Communication failure")
248
      for node_name in results:
249
        res = results[node_name]
250
        if res is False or not isinstance(res, list):
251
          raise errors.HooksFailure("Communication failure to node %s" %
252
                                    node_name)
253
        for script, hkr, output in res:
254
          if hkr == constants.HKR_FAIL:
255
            output = output.strip().encode("string_escape")
256
            errs.append((node_name, script, output))
257
      if errs:
258
        raise errors.HooksAbort(errs)
259

    
260
  def RunConfigUpdate(self):
261
    """Run the special configuration update hook
262

263
    This is a special hook that runs only on the master after each
264
    top-level LI if the configuration has been updated.
265

266
    """
267
    phase = constants.HOOKS_PHASE_POST
268
    hpath = constants.HOOKS_NAME_CFGUPDATE
269
    if self.lu.sstore is None:
270
      raise errors.ProgrammerError("Null sstore on config update hook")
271
    nodes = [self.lu.sstore.GetMasterNode()]
272
    results = self._RunWrapper(nodes, hpath, phase)