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
36 from ganeti import opcodes
37 from ganeti import logger
38 from ganeti import constants
39 from ganeti import utils
40 from ganeti import errors
41 from ganeti import rpc
42 from ganeti import cmdlib
43 from ganeti import config
44 from ganeti import ssconf
46 class Processor(object):
47 """Object which runs OpCodes"""
50 opcodes.OpInitCluster: cmdlib.LUInitCluster,
51 opcodes.OpDestroyCluster: cmdlib.LUDestroyCluster,
52 opcodes.OpQueryClusterInfo: cmdlib.LUQueryClusterInfo,
53 opcodes.OpClusterCopyFile: cmdlib.LUClusterCopyFile,
54 opcodes.OpRunClusterCommand: cmdlib.LURunClusterCommand,
55 opcodes.OpVerifyCluster: cmdlib.LUVerifyCluster,
56 opcodes.OpMasterFailover: cmdlib.LUMasterFailover,
57 opcodes.OpDumpClusterConfig: cmdlib.LUDumpClusterConfig,
59 opcodes.OpAddNode: cmdlib.LUAddNode,
60 opcodes.OpQueryNodes: cmdlib.LUQueryNodes,
61 opcodes.OpQueryNodeData: cmdlib.LUQueryNodeData,
62 opcodes.OpQueryNodeVolumes: cmdlib.LUQueryNodeVolumes,
63 opcodes.OpRemoveNode: cmdlib.LURemoveNode,
65 opcodes.OpCreateInstance: cmdlib.LUCreateInstance,
66 opcodes.OpReinstallInstance: cmdlib.LUReinstallInstance,
67 opcodes.OpRemoveInstance: cmdlib.LURemoveInstance,
68 opcodes.OpActivateInstanceDisks: cmdlib.LUActivateInstanceDisks,
69 opcodes.OpShutdownInstance: cmdlib.LUShutdownInstance,
70 opcodes.OpStartupInstance: cmdlib.LUStartupInstance,
71 opcodes.OpDeactivateInstanceDisks: cmdlib.LUDeactivateInstanceDisks,
72 opcodes.OpAddMDDRBDComponent: cmdlib.LUAddMDDRBDComponent,
73 opcodes.OpRemoveMDDRBDComponent: cmdlib.LURemoveMDDRBDComponent,
74 opcodes.OpReplaceDisks: cmdlib.LUReplaceDisks,
75 opcodes.OpFailoverInstance: cmdlib.LUFailoverInstance,
76 opcodes.OpConnectConsole: cmdlib.LUConnectConsole,
77 opcodes.OpQueryInstances: cmdlib.LUQueryInstances,
78 opcodes.OpQueryInstanceData: cmdlib.LUQueryInstanceData,
79 opcodes.OpSetInstanceParms: cmdlib.LUSetInstanceParms,
81 opcodes.OpDiagnoseOS: cmdlib.LUDiagnoseOS,
83 opcodes.OpQueryExports: cmdlib.LUQueryExports,
84 opcodes.OpExportInstance: cmdlib.LUExportInstance,
89 """Constructor for Processor
95 def ExecOpCode(self, op, feedback_fn):
99 - cfg: the configuration in which we execute this opcode
100 - opcode: the opcode to be executed
101 - feedback_fn: the feedback function (taking one string) to be run when
102 interesting events are happening
105 if not isinstance(op, opcodes.OpCode):
106 raise errors.ProgrammerError, ("Non-opcode instance passed"
109 lu_class = self.DISPATCH_TABLE.get(op.__class__, None)
111 raise errors.OpCodeUnknown, "Unknown opcode"
113 if lu_class.REQ_CLUSTER and self.cfg is None:
114 self.cfg = config.ConfigWriter()
115 self.sstore = ssconf.SimpleStore()
116 lu = lu_class(self, op, self.cfg, self.sstore)
118 do_hooks = lu_class.HPATH is not None
120 hm = HooksMaster(rpc.call_hooks_runner, self.cfg, self.sstore, lu)
121 hm.RunPhase(constants.HOOKS_PHASE_PRE)
122 result = lu.Exec(feedback_fn)
124 hm.RunPhase(constants.HOOKS_PHASE_POST)
127 def ChainOpCode(self, op, feedback_fn):
128 """Chain and execute an opcode.
130 This is used by LUs when they need to execute a child LU.
133 - opcode: the opcode to be executed
134 - feedback_fn: the feedback function (taking one string) to be run when
135 interesting events are happening
138 if not isinstance(op, opcodes.OpCode):
139 raise errors.ProgrammerError, ("Non-opcode instance passed"
142 lu_class = self.DISPATCH_TABLE.get(op.__class__, None)
144 raise errors.OpCodeUnknown, "Unknown opcode"
146 if lu_class.REQ_CLUSTER and self.cfg is None:
147 self.cfg = config.ConfigWriter()
148 self.sstore = ssconf.SimpleStore()
149 do_hooks = lu_class.HPATH is not None
150 lu = lu_class(self, op, self.cfg, self.sstore)
153 # hm = HooksMaster(rpc.call_hooks_runner, self.cfg, self.sstore, lu)
154 # hm.RunPhase(constants.HOOKS_PHASE_PRE)
155 result = lu.Exec(feedback_fn)
157 # hm.RunPhase(constants.HOOKS_PHASE_POST)
161 class HooksMaster(object):
164 This class distributes the run commands to the nodes based on the
167 In order to remove the direct dependency on the rpc module, the
168 constructor needs a function which actually does the remote
169 call. This will usually be rpc.call_hooks_runner, but any function
170 which behaves the same works.
173 def __init__(self, callfn, cfg, sstore, lu):
179 self.hpath = self.lu.HPATH
180 self.env, node_list_pre, node_list_post = self._BuildEnv()
182 self.node_list = {constants.HOOKS_PHASE_PRE: node_list_pre,
183 constants.HOOKS_PHASE_POST: node_list_post}
186 """Compute the environment and the target nodes.
188 Based on the opcode and the current node list, this builds the
189 environment for the hooks and the target node list for the run.
193 "PATH": "/sbin:/bin:/usr/sbin:/usr/bin",
194 "GANETI_HOOKS_VERSION": constants.HOOKS_VERSION,
195 "GANETI_OP_CODE": self.op.OP_ID,
196 "GANETI_OBJECT_TYPE": self.lu.HTYPE,
199 lu_env, lu_nodes_pre, lu_nodes_post = self.lu.BuildHooksEnv()
202 env["GANETI_" + key] = lu_env[key]
204 if self.sstore is not None:
205 env["GANETI_CLUSTER"] = self.sstore.GetClusterName()
206 env["GANETI_MASTER"] = self.sstore.GetMasterNode()
209 if not isinstance(env[key], str):
210 env[key] = str(env[key])
212 return env, frozenset(lu_nodes_pre), frozenset(lu_nodes_post)
214 def RunPhase(self, phase):
215 """Run all the scripts for a phase.
217 This is the main function of the HookMaster.
220 if not self.node_list[phase]:
221 # empty node list, we should not attempt to run this
222 # as most probably we're in the cluster init phase and the rpc client
223 # part can't even attempt to run
225 self.env["GANETI_HOOKS_PHASE"] = str(phase)
226 results = self.callfn(self.node_list[phase], self.hpath, phase, self.env)
227 if phase == constants.HOOKS_PHASE_PRE:
230 raise errors.HooksFailure, "Communication failure"
231 for node_name in results:
232 res = results[node_name]
233 if res is False or not isinstance(res, list):
234 raise errors.HooksFailure, ("Communication failure to node %s" %
236 for script, hkr, output in res:
237 if hkr == constants.HKR_FAIL:
238 output = output.strip().encode("string_escape")
239 errs.append((node_name, script, output))
241 raise errors.HooksAbort, errs