Statistics
| Branch: | Tag: | Revision:

root / lib / mcpu.py @ 386b57af

History | View | Annotate | Download (8.2 kB)

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)