Statistics
| Branch: | Tag: | Revision:

root / lib / mcpu.py @ fe7b0351

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
import os
33
import os.path
34
import time
35

    
36
from ganeti import opcodes
37
from ganeti import logger
38
from ganeti import constants
39
from ganeti import utils
40
from ganeti import errors
41
from ganeti import rpc
42
from ganeti import cmdlib
43
from ganeti import config
44
from ganeti import ssconf
45

    
46
class Processor(object):
47
  """Object which runs OpCodes"""
48
  DISPATCH_TABLE = {
49
    # Cluster
50
    opcodes.OpInitCluster: cmdlib.LUInitCluster,
51
    opcodes.OpDestroyCluster: cmdlib.LUDestroyCluster,
52
    opcodes.OpQueryClusterInfo: cmdlib.LUQueryClusterInfo,
53
    opcodes.OpClusterCopyFile: cmdlib.LUClusterCopyFile,
54
    opcodes.OpRunClusterCommand: cmdlib.LURunClusterCommand,
55
    opcodes.OpVerifyCluster: cmdlib.LUVerifyCluster,
56
    opcodes.OpMasterFailover: cmdlib.LUMasterFailover,
57
    opcodes.OpDumpClusterConfig: cmdlib.LUDumpClusterConfig,
58
    # node lu
59
    opcodes.OpAddNode: cmdlib.LUAddNode,
60
    opcodes.OpQueryNodes: cmdlib.LUQueryNodes,
61
    opcodes.OpQueryNodeData: cmdlib.LUQueryNodeData,
62
    opcodes.OpQueryNodeVolumes: cmdlib.LUQueryNodeVolumes,
63
    opcodes.OpRemoveNode: cmdlib.LURemoveNode,
64
    # instance lu
65
    opcodes.OpCreateInstance: cmdlib.LUCreateInstance,
66
    opcodes.OpReinstallInstance: cmdlib.LUReinstallInstance,
67
    opcodes.OpRemoveInstance: cmdlib.LURemoveInstance,
68
    opcodes.OpActivateInstanceDisks: cmdlib.LUActivateInstanceDisks,
69
    opcodes.OpShutdownInstance: cmdlib.LUShutdownInstance,
70
    opcodes.OpStartupInstance: cmdlib.LUStartupInstance,
71
    opcodes.OpDeactivateInstanceDisks: cmdlib.LUDeactivateInstanceDisks,
72
    opcodes.OpAddMDDRBDComponent: cmdlib.LUAddMDDRBDComponent,
73
    opcodes.OpRemoveMDDRBDComponent: cmdlib.LURemoveMDDRBDComponent,
74
    opcodes.OpReplaceDisks: cmdlib.LUReplaceDisks,
75
    opcodes.OpFailoverInstance: cmdlib.LUFailoverInstance,
76
    opcodes.OpConnectConsole: cmdlib.LUConnectConsole,
77
    opcodes.OpQueryInstances: cmdlib.LUQueryInstances,
78
    opcodes.OpQueryInstanceData: cmdlib.LUQueryInstanceData,
79
    opcodes.OpSetInstanceParms: cmdlib.LUSetInstanceParms,
80
    # os lu
81
    opcodes.OpDiagnoseOS: cmdlib.LUDiagnoseOS,
82
    # exports lu
83
    opcodes.OpQueryExports: cmdlib.LUQueryExports,
84
    opcodes.OpExportInstance: cmdlib.LUExportInstance,
85
    }
86

    
87

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

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

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

98
    Args:
99
     - cfg: the configuration in which we execute this opcode
100
     - opcode: the opcode to be executed
101
     - feedback_fn: the feedback function (taking one string) to be run when
102
                    interesting events are happening
103

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

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

    
113
    if lu_class.REQ_CLUSTER and self.cfg is None:
114
      self.cfg = config.ConfigWriter()
115
      self.sstore = ssconf.SimpleStore()
116
    lu = lu_class(self, op, self.cfg, self.sstore)
117
    lu.CheckPrereq()
118
    do_hooks = lu_class.HPATH is not None
119
    if do_hooks:
120
      hm = HooksMaster(rpc.call_hooks_runner, self.cfg, self.sstore, lu)
121
      hm.RunPhase(constants.HOOKS_PHASE_PRE)
122
    result = lu.Exec(feedback_fn)
123
    if do_hooks:
124
      hm.RunPhase(constants.HOOKS_PHASE_POST)
125
    return result
126

    
127
  def ChainOpCode(self, op, feedback_fn):
128
    """Chain and execute an opcode.
129

130
    This is used by LUs when they need to execute a child LU.
131

132
    Args:
133
     - opcode: the opcode to be executed
134
     - feedback_fn: the feedback function (taking one string) to be run when
135
                    interesting events are happening
136

137
    """
138
    if not isinstance(op, opcodes.OpCode):
139
      raise errors.ProgrammerError, ("Non-opcode instance passed"
140
                                     " to ExecOpcode")
141

    
142
    lu_class = self.DISPATCH_TABLE.get(op.__class__, None)
143
    if lu_class is None:
144
      raise errors.OpCodeUnknown, "Unknown opcode"
145

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

    
160

    
161
class HooksMaster(object):
162
  """Hooks master.
163

164
  This class distributes the run commands to the nodes based on the
165
  specific LU class.
166

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

172
  """
173
  def __init__(self, callfn, cfg, sstore, lu):
174
    self.callfn = callfn
175
    self.cfg = cfg
176
    self.sstore = sstore
177
    self.lu = lu
178
    self.op = lu.op
179
    self.hpath = self.lu.HPATH
180
    self.env, node_list_pre, node_list_post = self._BuildEnv()
181

    
182
    self.node_list = {constants.HOOKS_PHASE_PRE: node_list_pre,
183
                      constants.HOOKS_PHASE_POST: node_list_post}
184

    
185
  def _BuildEnv(self):
186
    """Compute the environment and the target nodes.
187

188
    Based on the opcode and the current node list, this builds the
189
    environment for the hooks and the target node list for the run.
190

191
    """
192
    env = {
193
      "PATH": "/sbin:/bin:/usr/sbin:/usr/bin",
194
      "GANETI_HOOKS_VERSION": constants.HOOKS_VERSION,
195
      "GANETI_OP_CODE": self.op.OP_ID,
196
      "GANETI_OBJECT_TYPE": self.lu.HTYPE,
197
      }
198

    
199
    lu_env, lu_nodes_pre, lu_nodes_post = self.lu.BuildHooksEnv()
200
    if lu_env:
201
      for key in lu_env:
202
        env["GANETI_" + key] = lu_env[key]
203

    
204
    if self.cfg is not None:
205
      env["GANETI_CLUSTER"] = self.cfg.GetClusterName()
206
    if self.sstore is not None:
207
      env["GANETI_MASTER"] = self.sstore.GetMasterNode()
208

    
209
    for key in env:
210
      if not isinstance(env[key], str):
211
        env[key] = str(env[key])
212

    
213
    return env, frozenset(lu_nodes_pre), frozenset(lu_nodes_post)
214

    
215
  def RunPhase(self, phase):
216
    """Run all the scripts for a phase.
217

218
    This is the main function of the HookMaster.
219

220
    """
221
    if not self.node_list[phase]:
222
      # empty node list, we should not attempt to run this
223
      # as most probably we're in the cluster init phase and the rpc client
224
      # part can't even attempt to run
225
      return
226
    self.env["GANETI_HOOKS_PHASE"] = str(phase)
227
    results = self.callfn(self.node_list[phase], self.hpath, phase, self.env)
228
    if phase == constants.HOOKS_PHASE_PRE:
229
      errs = []
230
      if not results:
231
        raise errors.HooksFailure, "Communication failure"
232
      for node_name in results:
233
        res = results[node_name]
234
        if res is False or not isinstance(res, list):
235
          raise errors.HooksFailure, ("Communication failure to node %s" %
236
                                      node_name)
237
        for script, hkr, output in res:
238
          if hkr == constants.HKR_FAIL:
239
            output = output.strip().encode("string_escape")
240
            errs.append((node_name, script, output))
241
      if errs:
242
        raise errors.HooksAbort, errs