Statistics
| Branch: | Tag: | Revision:

root / lib / mcpu.py @ f27302fa

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
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
    do_hooks = lu_class.HPATH is not None
118
    if do_hooks:
119
      hm = HooksMaster(rpc.call_hooks_runner, self.cfg, self.sstore, lu)
120
      hm.RunPhase(constants.HOOKS_PHASE_PRE)
121
    result = lu.Exec(feedback_fn)
122
    if do_hooks:
123
      hm.RunPhase(constants.HOOKS_PHASE_POST)
124
    return result
125

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

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

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

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

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

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

    
159

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

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

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

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

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

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

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

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

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

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

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

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

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

216
    This is the main function of the HookMaster.
217

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