4 # Copyright (C) 2006, 2007 Google Inc.
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.
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.
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
22 """Module implementing the logic behind the cluster operations
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
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
40 class Processor(object):
41 """Object which runs OpCodes"""
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,
54 opcodes.OpAddNode: cmdlib.LUAddNode,
55 opcodes.OpQueryNodes: cmdlib.LUQueryNodes,
56 opcodes.OpQueryNodeVolumes: cmdlib.LUQueryNodeVolumes,
57 opcodes.OpRemoveNode: cmdlib.LURemoveNode,
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,
76 opcodes.OpDiagnoseOS: cmdlib.LUDiagnoseOS,
78 opcodes.OpQueryExports: cmdlib.LUQueryExports,
79 opcodes.OpExportInstance: cmdlib.LUExportInstance,
81 opcodes.OpGetTags: cmdlib.LUGetTags,
82 opcodes.OpAddTags: cmdlib.LUAddTags,
83 opcodes.OpDelTags: cmdlib.LUDelTags,
88 """Constructor for Processor
94 def ExecOpCode(self, op, feedback_fn):
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
104 if not isinstance(op, opcodes.OpCode):
105 raise errors.ProgrammerError("Non-opcode instance passed"
108 lu_class = self.DISPATCH_TABLE.get(op.__class__, None)
110 raise errors.OpCodeUnknown("Unknown opcode")
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
119 lu = lu_class(self, op, self.cfg, self.sstore)
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
129 if write_count != lu.cfg.write_count:
134 def ChainOpCode(self, op, feedback_fn):
135 """Chain and execute an opcode.
137 This is used by LUs when they need to execute a child LU.
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
145 if not isinstance(op, opcodes.OpCode):
146 raise errors.ProgrammerError("Non-opcode instance passed"
149 lu_class = self.DISPATCH_TABLE.get(op.__class__, None)
151 raise errors.OpCodeUnknown("Unknown opcode")
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)
160 # hm = HooksMaster(rpc.call_hooks_runner, lu)
161 # hm.RunPhase(constants.HOOKS_PHASE_PRE)
162 result = lu.Exec(feedback_fn)
164 # hm.RunPhase(constants.HOOKS_PHASE_POST)
168 class HooksMaster(object):
171 This class distributes the run commands to the nodes based on the
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.
180 def __init__(self, callfn, lu):
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}
189 """Compute the environment and the target nodes.
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.
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,
203 if self.lu.HPATH is not None:
204 lu_env, lu_nodes_pre, lu_nodes_post = self.lu.BuildHooksEnv()
207 env["GANETI_" + key] = lu_env[key]
209 lu_nodes_pre = lu_nodes_post = []
211 return env, frozenset(lu_nodes_pre), frozenset(lu_nodes_post)
213 def _RunWrapper(self, node_list, hpath, phase):
214 """Simple wrapper over self.callfn.
216 This method fixes the environment before doing the rpc call.
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()
226 env = dict([(str(key), str(val)) for key, val in env.iteritems()])
228 return self.callfn(node_list, hpath, phase, env)
230 def RunPhase(self, phase):
231 """Run all the scripts for a phase.
233 This is the main function of the HookMaster.
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
241 hpath = self.lu.HPATH
242 results = self._RunWrapper(self.node_list[phase], hpath, phase)
243 if phase == constants.HOOKS_PHASE_PRE:
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" %
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))
257 raise errors.HooksAbort(errs)
259 def RunConfigUpdate(self):
260 """Run the special configuration update hook
262 This is a special hook that runs only on the master after each
263 top-level LI if the configuration has been updated.
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)