Statistics
| Branch: | Tag: | Revision:

root / lib / mcpu.py @ 6a4aa7c1

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

    
86

    
87
  def __init__(self):
88
    """Constructor for Processor
89

90
    """
91
    self.cfg = None
92
    self.sstore = None
93

    
94
  def ExecOpCode(self, op, feedback_fn):
95
    """Execute an opcode.
96

97
    Args:
98
     - cfg: the configuration in which we execute this opcode
99
     - opcode: the opcode to be executed
100
     - feedback_fn: the feedback function (taking one string) to be run when
101
                    interesting events are happening
102

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

    
108
    lu_class = self.DISPATCH_TABLE.get(op.__class__, None)
109
    if lu_class is None:
110
      raise errors.OpCodeUnknown("Unknown opcode")
111

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

    
132
    return result
133

    
134
  def ChainOpCode(self, op, feedback_fn):
135
    """Chain and execute an opcode.
136

137
    This is used by LUs when they need to execute a child LU.
138

139
    Args:
140
     - opcode: the opcode to be executed
141
     - feedback_fn: the feedback function (taking one string) to be run when
142
                    interesting events are happening
143

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

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

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

    
167

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

233
    This is the main function of the HookMaster.
234

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

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

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

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