Remove OpQueryNodeData and LUQueryNodeData
[ganeti-local] / lib / mcpu.py
1 #!/usr/bin/python
2 #
3
4 # Copyright (C) 2006, 2007 Google Inc.
5 #
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.
10 #
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.
15 #
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
19 # 02110-1301, USA.
20
21
22 """Module implementing the logic behind the cluster operations
23
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
28
29 """
30
31
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
39
40 class Processor(object):
41   """Object which runs OpCodes"""
42   DISPATCH_TABLE = {
43     # Cluster
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     # node lu
53     opcodes.OpAddNode: cmdlib.LUAddNode,
54     opcodes.OpQueryNodes: cmdlib.LUQueryNodes,
55     opcodes.OpQueryNodeVolumes: cmdlib.LUQueryNodeVolumes,
56     opcodes.OpRemoveNode: cmdlib.LURemoveNode,
57     # instance lu
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,
73     # os lu
74     opcodes.OpDiagnoseOS: cmdlib.LUDiagnoseOS,
75     # exports lu
76     opcodes.OpQueryExports: cmdlib.LUQueryExports,
77     opcodes.OpExportInstance: cmdlib.LUExportInstance,
78     # tags lu
79     opcodes.OpGetTags: cmdlib.LUGetTags,
80     opcodes.OpSetTag: cmdlib.LUAddTag,
81     opcodes.OpDelTag: cmdlib.LUDelTag,
82     }
83
84
85   def __init__(self):
86     """Constructor for Processor
87
88     """
89     self.cfg = None
90     self.sstore = None
91
92   def ExecOpCode(self, op, feedback_fn):
93     """Execute an opcode.
94
95     Args:
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
100
101     """
102     if not isinstance(op, opcodes.OpCode):
103       raise errors.ProgrammerError("Non-opcode instance passed"
104                                    " to ExecOpcode")
105
106     lu_class = self.DISPATCH_TABLE.get(op.__class__, None)
107     if lu_class is None:
108       raise errors.OpCodeUnknown("Unknown opcode")
109
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)
114     lu.CheckPrereq()
115     do_hooks = lu_class.HPATH is not None
116     if do_hooks:
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)
120     if do_hooks:
121       hm.RunPhase(constants.HOOKS_PHASE_POST)
122     return result
123
124   def ChainOpCode(self, op, feedback_fn):
125     """Chain and execute an opcode.
126
127     This is used by LUs when they need to execute a child LU.
128
129     Args:
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
133
134     """
135     if not isinstance(op, opcodes.OpCode):
136       raise errors.ProgrammerError("Non-opcode instance passed"
137                                    " to ExecOpcode")
138
139     lu_class = self.DISPATCH_TABLE.get(op.__class__, None)
140     if lu_class is None:
141       raise errors.OpCodeUnknown("Unknown opcode")
142
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)
148     lu.CheckPrereq()
149     #if do_hooks:
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)
153     #if do_hooks:
154     #  hm.RunPhase(constants.HOOKS_PHASE_POST)
155     return result
156
157
158 class HooksMaster(object):
159   """Hooks master.
160
161   This class distributes the run commands to the nodes based on the
162   specific LU class.
163
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.
168
169   """
170   def __init__(self, callfn, cfg, sstore, lu):
171     self.callfn = callfn
172     self.cfg = cfg
173     self.sstore = sstore
174     self.lu = lu
175     self.op = lu.op
176     self.hpath = self.lu.HPATH
177     self.env, node_list_pre, node_list_post = self._BuildEnv()
178
179     self.node_list = {constants.HOOKS_PHASE_PRE: node_list_pre,
180                       constants.HOOKS_PHASE_POST: node_list_post}
181
182   def _BuildEnv(self):
183     """Compute the environment and the target nodes.
184
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.
187
188     """
189     env = {
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,
194       }
195
196     lu_env, lu_nodes_pre, lu_nodes_post = self.lu.BuildHooksEnv()
197     if lu_env:
198       for key in lu_env:
199         env["GANETI_" + key] = lu_env[key]
200
201     if self.sstore is not None:
202       env["GANETI_CLUSTER"] = self.sstore.GetClusterName()
203       env["GANETI_MASTER"] = self.sstore.GetMasterNode()
204
205     for key in env:
206       if not isinstance(env[key], str):
207         env[key] = str(env[key])
208
209     return env, frozenset(lu_nodes_pre), frozenset(lu_nodes_post)
210
211   def RunPhase(self, phase):
212     """Run all the scripts for a phase.
213
214     This is the main function of the HookMaster.
215
216     """
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
221       return
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:
225       errs = []
226       if not results:
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" %
232                                     node_name)
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))
237       if errs:
238         raise errors.HooksAbort(errs)