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.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,
77 opcodes.OpDiagnoseOS: cmdlib.LUDiagnoseOS,
79 opcodes.OpQueryExports: cmdlib.LUQueryExports,
80 opcodes.OpExportInstance: cmdlib.LUExportInstance,
82 opcodes.OpGetTags: cmdlib.LUGetTags,
83 opcodes.OpSearchTags: cmdlib.LUSearchTags,
84 opcodes.OpAddTags: cmdlib.LUAddTags,
85 opcodes.OpDelTags: cmdlib.LUDelTags,
88 def __init__(self, feedback=None):
89 """Constructor for Processor
92 - feedback_fn: the feedback function (taking one string) to be run when
93 interesting events are happening
97 self._feedback_fn = feedback
99 def ExecOpCode(self, op):
100 """Execute an opcode.
103 - cfg: the configuration in which we execute this opcode
104 - opcode: the opcode to be executed
107 if not isinstance(op, opcodes.OpCode):
108 raise errors.ProgrammerError("Non-opcode instance passed"
111 lu_class = self.DISPATCH_TABLE.get(op.__class__, None)
113 raise errors.OpCodeUnknown("Unknown opcode")
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
122 lu = lu_class(self, op, self.cfg, self.sstore)
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
132 if write_count != lu.cfg.write_count:
137 def ChainOpCode(self, op):
138 """Chain and execute an opcode.
140 This is used by LUs when they need to execute a child LU.
143 - opcode: the opcode to be executed
146 if not isinstance(op, opcodes.OpCode):
147 raise errors.ProgrammerError("Non-opcode instance passed"
150 lu_class = self.DISPATCH_TABLE.get(op.__class__, None)
152 raise errors.OpCodeUnknown("Unknown opcode")
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)
161 # hm = HooksMaster(rpc.call_hooks_runner, lu)
162 # hm.RunPhase(constants.HOOKS_PHASE_PRE)
163 result = lu.Exec(self._feedback_fn)
165 # hm.RunPhase(constants.HOOKS_PHASE_POST)
169 class HooksMaster(object):
172 This class distributes the run commands to the nodes based on the
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.
181 def __init__(self, callfn, lu):
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}
190 """Compute the environment and the target nodes.
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.
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,
204 if self.lu.HPATH is not None:
205 lu_env, lu_nodes_pre, lu_nodes_post = self.lu.BuildHooksEnv()
208 env["GANETI_" + key] = lu_env[key]
210 lu_nodes_pre = lu_nodes_post = []
212 return env, frozenset(lu_nodes_pre), frozenset(lu_nodes_post)
214 def _RunWrapper(self, node_list, hpath, phase):
215 """Simple wrapper over self.callfn.
217 This method fixes the environment before doing the rpc call.
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()
227 env = dict([(str(key), str(val)) for key, val in env.iteritems()])
229 return self.callfn(node_list, hpath, phase, env)
231 def RunPhase(self, phase):
232 """Run all the scripts for a phase.
234 This is the main function of the HookMaster.
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
242 hpath = self.lu.HPATH
243 results = self._RunWrapper(self.node_list[phase], hpath, phase)
244 if phase == constants.HOOKS_PHASE_PRE:
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" %
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))
258 raise errors.HooksAbort(errs)
260 def RunConfigUpdate(self):
261 """Run the special configuration update hook
263 This is a special hook that runs only on the master after each
264 top-level LI if the configuration has been updated.
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)