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.OpSetTag: cmdlib.LUAddTag,
83 opcodes.OpDelTag: cmdlib.LUDelTag,
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 lu = lu_class(self, op, self.cfg, self.sstore)
117 do_hooks = lu_class.HPATH is not None
119 hm = HooksMaster(rpc.call_hooks_runner, self.cfg, self.sstore, lu)
120 hm.RunPhase(constants.HOOKS_PHASE_PRE)
121 result = lu.Exec(feedback_fn)
123 hm.RunPhase(constants.HOOKS_PHASE_POST)
126 def ChainOpCode(self, op, feedback_fn):
127 """Chain and execute an opcode.
129 This is used by LUs when they need to execute a child LU.
132 - opcode: the opcode to be executed
133 - feedback_fn: the feedback function (taking one string) to be run when
134 interesting events are happening
137 if not isinstance(op, opcodes.OpCode):
138 raise errors.ProgrammerError("Non-opcode instance passed"
141 lu_class = self.DISPATCH_TABLE.get(op.__class__, None)
143 raise errors.OpCodeUnknown("Unknown opcode")
145 if lu_class.REQ_CLUSTER and self.cfg is None:
146 self.cfg = config.ConfigWriter()
147 self.sstore = ssconf.SimpleStore()
148 do_hooks = lu_class.HPATH is not None
149 lu = lu_class(self, op, self.cfg, self.sstore)
152 # hm = HooksMaster(rpc.call_hooks_runner, self.cfg, self.sstore, lu)
153 # hm.RunPhase(constants.HOOKS_PHASE_PRE)
154 result = lu.Exec(feedback_fn)
156 # hm.RunPhase(constants.HOOKS_PHASE_POST)
160 class HooksMaster(object):
163 This class distributes the run commands to the nodes based on the
166 In order to remove the direct dependency on the rpc module, the
167 constructor needs a function which actually does the remote
168 call. This will usually be rpc.call_hooks_runner, but any function
169 which behaves the same works.
172 def __init__(self, callfn, cfg, sstore, lu):
178 self.hpath = self.lu.HPATH
179 self.env, node_list_pre, node_list_post = self._BuildEnv()
181 self.node_list = {constants.HOOKS_PHASE_PRE: node_list_pre,
182 constants.HOOKS_PHASE_POST: node_list_post}
185 """Compute the environment and the target nodes.
187 Based on the opcode and the current node list, this builds the
188 environment for the hooks and the target node list for the run.
192 "PATH": "/sbin:/bin:/usr/sbin:/usr/bin",
193 "GANETI_HOOKS_VERSION": constants.HOOKS_VERSION,
194 "GANETI_OP_CODE": self.op.OP_ID,
195 "GANETI_OBJECT_TYPE": self.lu.HTYPE,
198 lu_env, lu_nodes_pre, lu_nodes_post = self.lu.BuildHooksEnv()
201 env["GANETI_" + key] = lu_env[key]
203 if self.sstore is not None:
204 env["GANETI_CLUSTER"] = self.sstore.GetClusterName()
205 env["GANETI_MASTER"] = self.sstore.GetMasterNode()
208 if not isinstance(env[key], str):
209 env[key] = str(env[key])
211 return env, frozenset(lu_nodes_pre), frozenset(lu_nodes_post)
213 def RunPhase(self, phase):
214 """Run all the scripts for a phase.
216 This is the main function of the HookMaster.
219 if not self.node_list[phase]:
220 # empty node list, we should not attempt to run this
221 # as most probably we're in the cluster init phase and the rpc client
222 # part can't even attempt to run
224 self.env["GANETI_HOOKS_PHASE"] = str(phase)
225 results = self.callfn(self.node_list[phase], self.hpath, phase, self.env)
226 if phase == constants.HOOKS_PHASE_PRE:
229 raise errors.HooksFailure("Communication failure")
230 for node_name in results:
231 res = results[node_name]
232 if res is False or not isinstance(res, list):
233 raise errors.HooksFailure("Communication failure to node %s" %
235 for script, hkr, output in res:
236 if hkr == constants.HKR_FAIL:
237 output = output.strip().encode("string_escape")
238 errs.append((node_name, script, output))
240 raise errors.HooksAbort(errs)