Statistics
| Branch: | Tag: | Revision:

root / lib / mcpu.py @ 5c947f38

History | View | Annotate | Download (8.3 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
    # tags lu
86
    opcodes.OpGetTags: cmdlib.LUGetTags,
87
    opcodes.OpSetTag: cmdlib.LUAddTag,
88
    opcodes.OpDelTag: cmdlib.LUDelTag,
89
    }
90

    
91

    
92
  def __init__(self):
93
    """Constructor for Processor
94

95
    """
96
    self.cfg = None
97
    self.sstore = None
98

    
99
  def ExecOpCode(self, op, feedback_fn):
100
    """Execute an opcode.
101

102
    Args:
103
     - cfg: the configuration in which we execute this opcode
104
     - opcode: the opcode to be executed
105
     - feedback_fn: the feedback function (taking one string) to be run when
106
                    interesting events are happening
107

108
    """
109
    if not isinstance(op, opcodes.OpCode):
110
      raise errors.ProgrammerError, ("Non-opcode instance passed"
111
                                     " to ExecOpcode")
112

    
113
    lu_class = self.DISPATCH_TABLE.get(op.__class__, None)
114
    if lu_class is None:
115
      raise errors.OpCodeUnknown, "Unknown opcode"
116

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

    
131
  def ChainOpCode(self, op, feedback_fn):
132
    """Chain and execute an opcode.
133

134
    This is used by LUs when they need to execute a child LU.
135

136
    Args:
137
     - opcode: the opcode to be executed
138
     - feedback_fn: the feedback function (taking one string) to be run when
139
                    interesting events are happening
140

141
    """
142
    if not isinstance(op, opcodes.OpCode):
143
      raise errors.ProgrammerError, ("Non-opcode instance passed"
144
                                     " to ExecOpcode")
145

    
146
    lu_class = self.DISPATCH_TABLE.get(op.__class__, None)
147
    if lu_class is None:
148
      raise errors.OpCodeUnknown, "Unknown opcode"
149

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

    
164

    
165
class HooksMaster(object):
166
  """Hooks master.
167

168
  This class distributes the run commands to the nodes based on the
169
  specific LU class.
170

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

176
  """
177
  def __init__(self, callfn, cfg, sstore, lu):
178
    self.callfn = callfn
179
    self.cfg = cfg
180
    self.sstore = sstore
181
    self.lu = lu
182
    self.op = lu.op
183
    self.hpath = self.lu.HPATH
184
    self.env, node_list_pre, node_list_post = self._BuildEnv()
185

    
186
    self.node_list = {constants.HOOKS_PHASE_PRE: node_list_pre,
187
                      constants.HOOKS_PHASE_POST: node_list_post}
188

    
189
  def _BuildEnv(self):
190
    """Compute the environment and the target nodes.
191

192
    Based on the opcode and the current node list, this builds the
193
    environment for the hooks and the target node list for the run.
194

195
    """
196
    env = {
197
      "PATH": "/sbin:/bin:/usr/sbin:/usr/bin",
198
      "GANETI_HOOKS_VERSION": constants.HOOKS_VERSION,
199
      "GANETI_OP_CODE": self.op.OP_ID,
200
      "GANETI_OBJECT_TYPE": self.lu.HTYPE,
201
      }
202

    
203
    lu_env, lu_nodes_pre, lu_nodes_post = self.lu.BuildHooksEnv()
204
    if lu_env:
205
      for key in lu_env:
206
        env["GANETI_" + key] = lu_env[key]
207

    
208
    if self.sstore is not None:
209
      env["GANETI_CLUSTER"] = self.sstore.GetClusterName()
210
      env["GANETI_MASTER"] = self.sstore.GetMasterNode()
211

    
212
    for key in env:
213
      if not isinstance(env[key], str):
214
        env[key] = str(env[key])
215

    
216
    return env, frozenset(lu_nodes_pre), frozenset(lu_nodes_post)
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
226
      # as most probably we're in the cluster init phase and the rpc client
227
      # part can't even attempt to run
228
      return
229
    self.env["GANETI_HOOKS_PHASE"] = str(phase)
230
    results = self.callfn(self.node_list[phase], self.hpath, phase, self.env)
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