Statistics
| Branch: | Tag: | Revision:

root / lib / mcpu.py @ 3ecf6786

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.OpQueryNodeData: cmdlib.LUQueryNodeData,
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.OpActivateInstanceDisks: cmdlib.LUActivateInstanceDisks,
63
    opcodes.OpShutdownInstance: cmdlib.LUShutdownInstance,
64
    opcodes.OpStartupInstance: cmdlib.LUStartupInstance,
65
    opcodes.OpDeactivateInstanceDisks: cmdlib.LUDeactivateInstanceDisks,
66
    opcodes.OpAddMDDRBDComponent: cmdlib.LUAddMDDRBDComponent,
67
    opcodes.OpRemoveMDDRBDComponent: cmdlib.LURemoveMDDRBDComponent,
68
    opcodes.OpReplaceDisks: cmdlib.LUReplaceDisks,
69
    opcodes.OpFailoverInstance: cmdlib.LUFailoverInstance,
70
    opcodes.OpConnectConsole: cmdlib.LUConnectConsole,
71
    opcodes.OpQueryInstances: cmdlib.LUQueryInstances,
72
    opcodes.OpQueryInstanceData: cmdlib.LUQueryInstanceData,
73
    opcodes.OpSetInstanceParms: cmdlib.LUSetInstanceParms,
74
    # os lu
75
    opcodes.OpDiagnoseOS: cmdlib.LUDiagnoseOS,
76
    # exports lu
77
    opcodes.OpQueryExports: cmdlib.LUQueryExports,
78
    opcodes.OpExportInstance: cmdlib.LUExportInstance,
79
    # tags lu
80
    opcodes.OpGetTags: cmdlib.LUGetTags,
81
    opcodes.OpSetTag: cmdlib.LUAddTag,
82
    opcodes.OpDelTag: cmdlib.LUDelTag,
83
    }
84

    
85

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

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

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

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

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

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

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

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

128
    This is used by LUs when they need to execute a child LU.
129

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

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

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

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

    
158

    
159
class HooksMaster(object):
160
  """Hooks master.
161

162
  This class distributes the run commands to the nodes based on the
163
  specific LU class.
164

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

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

    
180
    self.node_list = {constants.HOOKS_PHASE_PRE: node_list_pre,
181
                      constants.HOOKS_PHASE_POST: node_list_post}
182

    
183
  def _BuildEnv(self):
184
    """Compute the environment and the target nodes.
185

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

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

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

    
202
    if self.sstore is not None:
203
      env["GANETI_CLUSTER"] = self.sstore.GetClusterName()
204
      env["GANETI_MASTER"] = self.sstore.GetMasterNode()
205

    
206
    for key in env:
207
      if not isinstance(env[key], str):
208
        env[key] = str(env[key])
209

    
210
    return env, frozenset(lu_nodes_pre), frozenset(lu_nodes_post)
211

    
212
  def RunPhase(self, phase):
213
    """Run all the scripts for a phase.
214

215
    This is the main function of the HookMaster.
216

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