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,
86 opcodes.OpGetTags: cmdlib.LUGetTags,
87 opcodes.OpSetTag: cmdlib.LUAddTag,
88 opcodes.OpDelTag: cmdlib.LUDelTag,
93 """Constructor for Processor
99 def ExecOpCode(self, op, feedback_fn):
100 """Execute an opcode.
103 - cfg: the configuration in which we execute this opcode
104 - opcode: the opcode to be executed
105 - feedback_fn: the feedback function (taking one string) to be run when
106 interesting events are happening
109 if not isinstance(op, opcodes.OpCode):
110 raise errors.ProgrammerError, ("Non-opcode instance passed"
113 lu_class = self.DISPATCH_TABLE.get(op.__class__, None)
115 raise errors.OpCodeUnknown, "Unknown opcode"
117 if lu_class.REQ_CLUSTER and self.cfg is None:
118 self.cfg = config.ConfigWriter()
119 self.sstore = ssconf.SimpleStore()
120 lu = lu_class(self, op, self.cfg, self.sstore)
122 do_hooks = lu_class.HPATH is not None
124 hm = HooksMaster(rpc.call_hooks_runner, self.cfg, self.sstore, lu)
125 hm.RunPhase(constants.HOOKS_PHASE_PRE)
126 result = lu.Exec(feedback_fn)
128 hm.RunPhase(constants.HOOKS_PHASE_POST)
131 def ChainOpCode(self, op, feedback_fn):
132 """Chain and execute an opcode.
134 This is used by LUs when they need to execute a child LU.
137 - opcode: the opcode to be executed
138 - feedback_fn: the feedback function (taking one string) to be run when
139 interesting events are happening
142 if not isinstance(op, opcodes.OpCode):
143 raise errors.ProgrammerError, ("Non-opcode instance passed"
146 lu_class = self.DISPATCH_TABLE.get(op.__class__, None)
148 raise errors.OpCodeUnknown, "Unknown opcode"
150 if lu_class.REQ_CLUSTER and self.cfg is None:
151 self.cfg = config.ConfigWriter()
152 self.sstore = ssconf.SimpleStore()
153 do_hooks = lu_class.HPATH is not None
154 lu = lu_class(self, op, self.cfg, self.sstore)
157 # hm = HooksMaster(rpc.call_hooks_runner, self.cfg, self.sstore, lu)
158 # hm.RunPhase(constants.HOOKS_PHASE_PRE)
159 result = lu.Exec(feedback_fn)
161 # hm.RunPhase(constants.HOOKS_PHASE_POST)
165 class HooksMaster(object):
168 This class distributes the run commands to the nodes based on the
171 In order to remove the direct dependency on the rpc module, the
172 constructor needs a function which actually does the remote
173 call. This will usually be rpc.call_hooks_runner, but any function
174 which behaves the same works.
177 def __init__(self, callfn, cfg, sstore, lu):
183 self.hpath = self.lu.HPATH
184 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,
203 lu_env, lu_nodes_pre, lu_nodes_post = self.lu.BuildHooksEnv()
206 env["GANETI_" + key] = lu_env[key]
208 if self.sstore is not None:
209 env["GANETI_CLUSTER"] = self.sstore.GetClusterName()
210 env["GANETI_MASTER"] = self.sstore.GetMasterNode()
213 if not isinstance(env[key], str):
214 env[key] = str(env[key])
216 return env, frozenset(lu_nodes_pre), frozenset(lu_nodes_post)
218 def RunPhase(self, phase):
219 """Run all the scripts for a phase.
221 This is the main function of the HookMaster.
224 if not self.node_list[phase]:
225 # empty node list, we should not attempt to run this
226 # as most probably we're in the cluster init phase and the rpc client
227 # part can't even attempt to run
229 self.env["GANETI_HOOKS_PHASE"] = str(phase)
230 results = self.callfn(self.node_list[phase], self.hpath, phase, self.env)
231 if phase == constants.HOOKS_PHASE_PRE:
234 raise errors.HooksFailure, "Communication failure"
235 for node_name in results:
236 res = results[node_name]
237 if res is False or not isinstance(res, list):
238 raise errors.HooksFailure, ("Communication failure to node %s" %
240 for script, hkr, output in res:
241 if hkr == constants.HKR_FAIL:
242 output = output.strip().encode("string_escape")
243 errs.append((node_name, script, output))
245 raise errors.HooksAbort, errs