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,
53 opcodes.OpAddNode: cmdlib.LUAddNode,
54 opcodes.OpQueryNodes: cmdlib.LUQueryNodes,
55 opcodes.OpQueryNodeVolumes: cmdlib.LUQueryNodeVolumes,
56 opcodes.OpRemoveNode: cmdlib.LURemoveNode,
58 opcodes.OpCreateInstance: cmdlib.LUCreateInstance,
59 opcodes.OpReinstallInstance: cmdlib.LUReinstallInstance,
60 opcodes.OpRemoveInstance: cmdlib.LURemoveInstance,
61 opcodes.OpActivateInstanceDisks: cmdlib.LUActivateInstanceDisks,
62 opcodes.OpShutdownInstance: cmdlib.LUShutdownInstance,
63 opcodes.OpStartupInstance: cmdlib.LUStartupInstance,
64 opcodes.OpDeactivateInstanceDisks: cmdlib.LUDeactivateInstanceDisks,
65 opcodes.OpAddMDDRBDComponent: cmdlib.LUAddMDDRBDComponent,
66 opcodes.OpRemoveMDDRBDComponent: cmdlib.LURemoveMDDRBDComponent,
67 opcodes.OpReplaceDisks: cmdlib.LUReplaceDisks,
68 opcodes.OpFailoverInstance: cmdlib.LUFailoverInstance,
69 opcodes.OpConnectConsole: cmdlib.LUConnectConsole,
70 opcodes.OpQueryInstances: cmdlib.LUQueryInstances,
71 opcodes.OpQueryInstanceData: cmdlib.LUQueryInstanceData,
72 opcodes.OpSetInstanceParms: cmdlib.LUSetInstanceParms,
74 opcodes.OpDiagnoseOS: cmdlib.LUDiagnoseOS,
76 opcodes.OpQueryExports: cmdlib.LUQueryExports,
77 opcodes.OpExportInstance: cmdlib.LUExportInstance,
79 opcodes.OpGetTags: cmdlib.LUGetTags,
80 opcodes.OpSetTag: cmdlib.LUAddTag,
81 opcodes.OpDelTag: cmdlib.LUDelTag,
86 """Constructor for Processor
92 def ExecOpCode(self, op, feedback_fn):
96 - cfg: the configuration in which we execute this opcode
97 - opcode: the opcode to be executed
98 - feedback_fn: the feedback function (taking one string) to be run when
99 interesting events are happening
102 if not isinstance(op, opcodes.OpCode):
103 raise errors.ProgrammerError("Non-opcode instance passed"
106 lu_class = self.DISPATCH_TABLE.get(op.__class__, None)
108 raise errors.OpCodeUnknown("Unknown opcode")
110 if lu_class.REQ_CLUSTER and self.cfg is None:
111 self.cfg = config.ConfigWriter()
112 self.sstore = ssconf.SimpleStore()
113 lu = lu_class(self, op, self.cfg, self.sstore)
115 do_hooks = lu_class.HPATH is not None
117 hm = HooksMaster(rpc.call_hooks_runner, self.cfg, self.sstore, lu)
118 hm.RunPhase(constants.HOOKS_PHASE_PRE)
119 result = lu.Exec(feedback_fn)
121 hm.RunPhase(constants.HOOKS_PHASE_POST)
124 def ChainOpCode(self, op, feedback_fn):
125 """Chain and execute an opcode.
127 This is used by LUs when they need to execute a child LU.
130 - opcode: the opcode to be executed
131 - feedback_fn: the feedback function (taking one string) to be run when
132 interesting events are happening
135 if not isinstance(op, opcodes.OpCode):
136 raise errors.ProgrammerError("Non-opcode instance passed"
139 lu_class = self.DISPATCH_TABLE.get(op.__class__, None)
141 raise errors.OpCodeUnknown("Unknown opcode")
143 if lu_class.REQ_CLUSTER and self.cfg is None:
144 self.cfg = config.ConfigWriter()
145 self.sstore = ssconf.SimpleStore()
146 do_hooks = lu_class.HPATH is not None
147 lu = lu_class(self, op, self.cfg, self.sstore)
150 # hm = HooksMaster(rpc.call_hooks_runner, self.cfg, self.sstore, lu)
151 # hm.RunPhase(constants.HOOKS_PHASE_PRE)
152 result = lu.Exec(feedback_fn)
154 # hm.RunPhase(constants.HOOKS_PHASE_POST)
158 class HooksMaster(object):
161 This class distributes the run commands to the nodes based on the
164 In order to remove the direct dependency on the rpc module, the
165 constructor needs a function which actually does the remote
166 call. This will usually be rpc.call_hooks_runner, but any function
167 which behaves the same works.
170 def __init__(self, callfn, cfg, sstore, lu):
176 self.hpath = self.lu.HPATH
177 self.env, node_list_pre, node_list_post = self._BuildEnv()
179 self.node_list = {constants.HOOKS_PHASE_PRE: node_list_pre,
180 constants.HOOKS_PHASE_POST: node_list_post}
183 """Compute the environment and the target nodes.
185 Based on the opcode and the current node list, this builds the
186 environment for the hooks and the target node list for the run.
190 "PATH": "/sbin:/bin:/usr/sbin:/usr/bin",
191 "GANETI_HOOKS_VERSION": constants.HOOKS_VERSION,
192 "GANETI_OP_CODE": self.op.OP_ID,
193 "GANETI_OBJECT_TYPE": self.lu.HTYPE,
196 lu_env, lu_nodes_pre, lu_nodes_post = self.lu.BuildHooksEnv()
199 env["GANETI_" + key] = lu_env[key]
201 if self.sstore is not None:
202 env["GANETI_CLUSTER"] = self.sstore.GetClusterName()
203 env["GANETI_MASTER"] = self.sstore.GetMasterNode()
206 if not isinstance(env[key], str):
207 env[key] = str(env[key])
209 return env, frozenset(lu_nodes_pre), frozenset(lu_nodes_post)
211 def RunPhase(self, phase):
212 """Run all the scripts for a phase.
214 This is the main function of the HookMaster.
217 if not self.node_list[phase]:
218 # empty node list, we should not attempt to run this
219 # as most probably we're in the cluster init phase and the rpc client
220 # part can't even attempt to run
222 self.env["GANETI_HOOKS_PHASE"] = str(phase)
223 results = self.callfn(self.node_list[phase], self.hpath, phase, self.env)
224 if phase == constants.HOOKS_PHASE_PRE:
227 raise errors.HooksFailure("Communication failure")
228 for node_name in results:
229 res = results[node_name]
230 if res is False or not isinstance(res, list):
231 raise errors.HooksFailure("Communication failure to node %s" %
233 for script, hkr, output in res:
234 if hkr == constants.HKR_FAIL:
235 output = output.strip().encode("string_escape")
236 errs.append((node_name, script, output))
238 raise errors.HooksAbort(errs)