Statistics
| Branch: | Tag: | Revision:

root / lib / mcpu.py @ 4167825b

History | View | Annotate | Download (8.4 kB)

1
#
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
    opcodes.OpRenameCluster: cmdlib.LURenameCluster,
53
    # node lu
54
    opcodes.OpAddNode: cmdlib.LUAddNode,
55
    opcodes.OpQueryNodes: cmdlib.LUQueryNodes,
56
    opcodes.OpQueryNodeVolumes: cmdlib.LUQueryNodeVolumes,
57
    opcodes.OpRemoveNode: cmdlib.LURemoveNode,
58
    # instance lu
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,
75
    # os lu
76
    opcodes.OpDiagnoseOS: cmdlib.LUDiagnoseOS,
77
    # exports lu
78
    opcodes.OpQueryExports: cmdlib.LUQueryExports,
79
    opcodes.OpExportInstance: cmdlib.LUExportInstance,
80
    # tags lu
81
    opcodes.OpGetTags: cmdlib.LUGetTags,
82
    opcodes.OpAddTags: cmdlib.LUAddTags,
83
    opcodes.OpDelTags: cmdlib.LUDelTags,
84
    }
85

    
86

    
87
  def __init__(self):
88
    """Constructor for Processor
89

90
    """
91
    self.cfg = None
92
    self.sstore = None
93

    
94
  def ExecOpCode(self, op, feedback_fn):
95
    """Execute an opcode.
96

97
    Args:
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
102

103
    """
104
    if not isinstance(op, opcodes.OpCode):
105
      raise errors.ProgrammerError("Non-opcode instance passed"
106
                                   " to ExecOpcode")
107

    
108
    lu_class = self.DISPATCH_TABLE.get(op.__class__, None)
109
    if lu_class is None:
110
      raise errors.OpCodeUnknown("Unknown opcode")
111

    
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)
116
    lu.CheckPrereq()
117
    hm = HooksMaster(rpc.call_hooks_runner, lu)
118
    hm.RunPhase(constants.HOOKS_PHASE_PRE)
119
    result = lu.Exec(feedback_fn)
120
    hm.RunPhase(constants.HOOKS_PHASE_POST)
121
    return result
122

    
123
  def ChainOpCode(self, op, feedback_fn):
124
    """Chain and execute an opcode.
125

126
    This is used by LUs when they need to execute a child LU.
127

128
    Args:
129
     - opcode: the opcode to be executed
130
     - feedback_fn: the feedback function (taking one string) to be run when
131
                    interesting events are happening
132

133
    """
134
    if not isinstance(op, opcodes.OpCode):
135
      raise errors.ProgrammerError("Non-opcode instance passed"
136
                                   " to ExecOpcode")
137

    
138
    lu_class = self.DISPATCH_TABLE.get(op.__class__, None)
139
    if lu_class is None:
140
      raise errors.OpCodeUnknown("Unknown opcode")
141

    
142
    if lu_class.REQ_CLUSTER and self.cfg is None:
143
      self.cfg = config.ConfigWriter()
144
      self.sstore = ssconf.SimpleStore()
145
    #do_hooks = lu_class.HPATH is not None
146
    lu = lu_class(self, op, self.cfg, self.sstore)
147
    lu.CheckPrereq()
148
    #if do_hooks:
149
    #  hm = HooksMaster(rpc.call_hooks_runner, lu)
150
    #  hm.RunPhase(constants.HOOKS_PHASE_PRE)
151
    result = lu.Exec(feedback_fn)
152
    #if do_hooks:
153
    #  hm.RunPhase(constants.HOOKS_PHASE_POST)
154
    return result
155

    
156

    
157
class HooksMaster(object):
158
  """Hooks master.
159

160
  This class distributes the run commands to the nodes based on the
161
  specific LU class.
162

163
  In order to remove the direct dependency on the rpc module, the
164
  constructor needs a function which actually does the remote
165
  call. This will usually be rpc.call_hooks_runner, but any function
166
  which behaves the same works.
167

168
  """
169
  def __init__(self, callfn, lu):
170
    self.callfn = callfn
171
    self.lu = lu
172
    self.op = lu.op
173
    self.env, node_list_pre, node_list_post = self._BuildEnv()
174
    self.node_list = {constants.HOOKS_PHASE_PRE: node_list_pre,
175
                      constants.HOOKS_PHASE_POST: node_list_post}
176

    
177
  def _BuildEnv(self):
178
    """Compute the environment and the target nodes.
179

180
    Based on the opcode and the current node list, this builds the
181
    environment for the hooks and the target node list for the run.
182

183
    """
184
    env = {
185
      "PATH": "/sbin:/bin:/usr/sbin:/usr/bin",
186
      "GANETI_HOOKS_VERSION": constants.HOOKS_VERSION,
187
      "GANETI_OP_CODE": self.op.OP_ID,
188
      "GANETI_OBJECT_TYPE": self.lu.HTYPE,
189
      }
190

    
191
    if self.lu.HPATH is not None:
192
      lu_env, lu_nodes_pre, lu_nodes_post = self.lu.BuildHooksEnv()
193
      if lu_env:
194
        for key in lu_env:
195
          env["GANETI_" + key] = lu_env[key]
196
    else:
197
      lu_nodes_pre = lu_nodes_post = []
198

    
199
    return env, frozenset(lu_nodes_pre), frozenset(lu_nodes_post)
200

    
201
  def _RunWrapper(self, node_list, hpath, phase):
202
    """Simple wrapper over self.callfn.
203

204
    This method fixes the environment before doing the rpc call.
205

206
    """
207
    env = self.env.copy()
208
    env["GANETI_HOOKS_PHASE"] = phase
209
    env["GANETI_HOOKS_PATH"] = hpath
210
    if self.lu.sstore is not None:
211
      env["GANETI_CLUSTER"] = self.lu.sstore.GetClusterName()
212
      env["GANETI_MASTER"] = self.lu.sstore.GetMasterNode()
213

    
214
    env = dict([(str(key), str(val)) for key, val in env.iteritems()])
215

    
216
    return self.callfn(node_list, hpath, phase, env)
217

    
218
  def RunPhase(self, phase):
219
    """Run all the scripts for a phase.
220

221
    This is the main function of the HookMaster.
222

223
    """
224
    if not self.node_list[phase]:
225
      # empty node list, we should not attempt to run this as either
226
      # we're in the cluster init phase and the rpc client part can't
227
      # even attempt to run, or this LU doesn't do hooks at all
228
      return
229
    hpath = self.lu.HPATH
230
    results = self._RunWrapper(self.node_list[phase], hpath, phase)
231
    if phase == constants.HOOKS_PHASE_PRE:
232
      errs = []
233
      if not results:
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" %
239
                                    node_name)
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))
244
      if errs:
245
        raise errors.HooksAbort(errs)