Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / test.py @ f380d53c

History | View | Annotate | Download (11.9 kB)

1
#
2
#
3

    
4
# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 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
"""Test logical units."""
23

    
24
import logging
25
import shutil
26
import socket
27
import tempfile
28

    
29
from ganeti import compat
30
from ganeti import constants
31
from ganeti import errors
32
from ganeti import locking
33
from ganeti import utils
34
from ganeti.masterd import iallocator
35
from ganeti.cmdlib.base import NoHooksLU
36
from ganeti.cmdlib.common import _ExpandInstanceName, _GetWantedNodes, \
37
  _GetWantedInstances
38

    
39

    
40
class LUTestDelay(NoHooksLU):
41
  """Sleep for a specified amount of time.
42

43
  This LU sleeps on the master and/or nodes for a specified amount of
44
  time.
45

46
  """
47
  REQ_BGL = False
48

    
49
  def ExpandNames(self):
50
    """Expand names and set required locks.
51

52
    This expands the node list, if any.
53

54
    """
55
    self.needed_locks = {}
56
    if self.op.on_nodes:
57
      # _GetWantedNodes can be used here, but is not always appropriate to use
58
      # this way in ExpandNames. Check LogicalUnit.ExpandNames docstring for
59
      # more information.
60
      self.op.on_nodes = _GetWantedNodes(self, self.op.on_nodes)
61
      self.needed_locks[locking.LEVEL_NODE] = self.op.on_nodes
62

    
63
  def _TestDelay(self):
64
    """Do the actual sleep.
65

66
    """
67
    if self.op.on_master:
68
      if not utils.TestDelay(self.op.duration):
69
        raise errors.OpExecError("Error during master delay test")
70
    if self.op.on_nodes:
71
      result = self.rpc.call_test_delay(self.op.on_nodes, self.op.duration)
72
      for node, node_result in result.items():
73
        node_result.Raise("Failure during rpc call to node %s" % node)
74

    
75
  def Exec(self, feedback_fn):
76
    """Execute the test delay opcode, with the wanted repetitions.
77

78
    """
79
    if self.op.repeat == 0:
80
      self._TestDelay()
81
    else:
82
      top_value = self.op.repeat - 1
83
      for i in range(self.op.repeat):
84
        self.LogInfo("Test delay iteration %d/%d", i, top_value)
85
        self._TestDelay()
86

    
87

    
88
class LUTestJqueue(NoHooksLU):
89
  """Utility LU to test some aspects of the job queue.
90

91
  """
92
  REQ_BGL = False
93

    
94
  # Must be lower than default timeout for WaitForJobChange to see whether it
95
  # notices changed jobs
96
  _CLIENT_CONNECT_TIMEOUT = 20.0
97
  _CLIENT_CONFIRM_TIMEOUT = 60.0
98

    
99
  @classmethod
100
  def _NotifyUsingSocket(cls, cb, errcls):
101
    """Opens a Unix socket and waits for another program to connect.
102

103
    @type cb: callable
104
    @param cb: Callback to send socket name to client
105
    @type errcls: class
106
    @param errcls: Exception class to use for errors
107

108
    """
109
    # Using a temporary directory as there's no easy way to create temporary
110
    # sockets without writing a custom loop around tempfile.mktemp and
111
    # socket.bind
112
    tmpdir = tempfile.mkdtemp()
113
    try:
114
      tmpsock = utils.PathJoin(tmpdir, "sock")
115

    
116
      logging.debug("Creating temporary socket at %s", tmpsock)
117
      sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
118
      try:
119
        sock.bind(tmpsock)
120
        sock.listen(1)
121

    
122
        # Send details to client
123
        cb(tmpsock)
124

    
125
        # Wait for client to connect before continuing
126
        sock.settimeout(cls._CLIENT_CONNECT_TIMEOUT)
127
        try:
128
          (conn, _) = sock.accept()
129
        except socket.error, err:
130
          raise errcls("Client didn't connect in time (%s)" % err)
131
      finally:
132
        sock.close()
133
    finally:
134
      # Remove as soon as client is connected
135
      shutil.rmtree(tmpdir)
136

    
137
    # Wait for client to close
138
    try:
139
      try:
140
        # pylint: disable=E1101
141
        # Instance of '_socketobject' has no ... member
142
        conn.settimeout(cls._CLIENT_CONFIRM_TIMEOUT)
143
        conn.recv(1)
144
      except socket.error, err:
145
        raise errcls("Client failed to confirm notification (%s)" % err)
146
    finally:
147
      conn.close()
148

    
149
  def _SendNotification(self, test, arg, sockname):
150
    """Sends a notification to the client.
151

152
    @type test: string
153
    @param test: Test name
154
    @param arg: Test argument (depends on test)
155
    @type sockname: string
156
    @param sockname: Socket path
157

158
    """
159
    self.Log(constants.ELOG_JQUEUE_TEST, (sockname, test, arg))
160

    
161
  def _Notify(self, prereq, test, arg):
162
    """Notifies the client of a test.
163

164
    @type prereq: bool
165
    @param prereq: Whether this is a prereq-phase test
166
    @type test: string
167
    @param test: Test name
168
    @param arg: Test argument (depends on test)
169

170
    """
171
    if prereq:
172
      errcls = errors.OpPrereqError
173
    else:
174
      errcls = errors.OpExecError
175

    
176
    return self._NotifyUsingSocket(compat.partial(self._SendNotification,
177
                                                  test, arg),
178
                                   errcls)
179

    
180
  def CheckArguments(self):
181
    self.checkargs_calls = getattr(self, "checkargs_calls", 0) + 1
182
    self.expandnames_calls = 0
183

    
184
  def ExpandNames(self):
185
    checkargs_calls = getattr(self, "checkargs_calls", 0)
186
    if checkargs_calls < 1:
187
      raise errors.ProgrammerError("CheckArguments was not called")
188

    
189
    self.expandnames_calls += 1
190

    
191
    if self.op.notify_waitlock:
192
      self._Notify(True, constants.JQT_EXPANDNAMES, None)
193

    
194
    self.LogInfo("Expanding names")
195

    
196
    # Get lock on master node (just to get a lock, not for a particular reason)
197
    self.needed_locks = {
198
      locking.LEVEL_NODE: self.cfg.GetMasterNode(),
199
      }
200

    
201
  def Exec(self, feedback_fn):
202
    if self.expandnames_calls < 1:
203
      raise errors.ProgrammerError("ExpandNames was not called")
204

    
205
    if self.op.notify_exec:
206
      self._Notify(False, constants.JQT_EXEC, None)
207

    
208
    self.LogInfo("Executing")
209

    
210
    if self.op.log_messages:
211
      self._Notify(False, constants.JQT_STARTMSG, len(self.op.log_messages))
212
      for idx, msg in enumerate(self.op.log_messages):
213
        self.LogInfo("Sending log message %s", idx + 1)
214
        feedback_fn(constants.JQT_MSGPREFIX + msg)
215
        # Report how many test messages have been sent
216
        self._Notify(False, constants.JQT_LOGMSG, idx + 1)
217

    
218
    if self.op.fail:
219
      raise errors.OpExecError("Opcode failure was requested")
220

    
221
    return True
222

    
223

    
224
class LUTestAllocator(NoHooksLU):
225
  """Run allocator tests.
226

227
  This LU runs the allocator tests
228

229
  """
230
  def CheckPrereq(self):
231
    """Check prerequisites.
232

233
    This checks the opcode parameters depending on the director and mode test.
234

235
    """
236
    if self.op.mode in (constants.IALLOCATOR_MODE_ALLOC,
237
                        constants.IALLOCATOR_MODE_MULTI_ALLOC):
238
      for attr in ["memory", "disks", "disk_template",
239
                   "os", "tags", "nics", "vcpus"]:
240
        if not hasattr(self.op, attr):
241
          raise errors.OpPrereqError("Missing attribute '%s' on opcode input" %
242
                                     attr, errors.ECODE_INVAL)
243
      iname = self.cfg.ExpandInstanceName(self.op.name)
244
      if iname is not None:
245
        raise errors.OpPrereqError("Instance '%s' already in the cluster" %
246
                                   iname, errors.ECODE_EXISTS)
247
      if not isinstance(self.op.nics, list):
248
        raise errors.OpPrereqError("Invalid parameter 'nics'",
249
                                   errors.ECODE_INVAL)
250
      if not isinstance(self.op.disks, list):
251
        raise errors.OpPrereqError("Invalid parameter 'disks'",
252
                                   errors.ECODE_INVAL)
253
      for row in self.op.disks:
254
        if (not isinstance(row, dict) or
255
            constants.IDISK_SIZE not in row or
256
            not isinstance(row[constants.IDISK_SIZE], int) or
257
            constants.IDISK_MODE not in row or
258
            row[constants.IDISK_MODE] not in constants.DISK_ACCESS_SET):
259
          raise errors.OpPrereqError("Invalid contents of the 'disks'"
260
                                     " parameter", errors.ECODE_INVAL)
261
      if self.op.hypervisor is None:
262
        self.op.hypervisor = self.cfg.GetHypervisorType()
263
    elif self.op.mode == constants.IALLOCATOR_MODE_RELOC:
264
      fname = _ExpandInstanceName(self.cfg, self.op.name)
265
      self.op.name = fname
266
      self.relocate_from = \
267
          list(self.cfg.GetInstanceInfo(fname).secondary_nodes)
268
    elif self.op.mode in (constants.IALLOCATOR_MODE_CHG_GROUP,
269
                          constants.IALLOCATOR_MODE_NODE_EVAC):
270
      if not self.op.instances:
271
        raise errors.OpPrereqError("Missing instances", errors.ECODE_INVAL)
272
      self.op.instances = _GetWantedInstances(self, self.op.instances)
273
    else:
274
      raise errors.OpPrereqError("Invalid test allocator mode '%s'" %
275
                                 self.op.mode, errors.ECODE_INVAL)
276

    
277
    if self.op.direction == constants.IALLOCATOR_DIR_OUT:
278
      if self.op.iallocator is None:
279
        raise errors.OpPrereqError("Missing allocator name",
280
                                   errors.ECODE_INVAL)
281
    elif self.op.direction != constants.IALLOCATOR_DIR_IN:
282
      raise errors.OpPrereqError("Wrong allocator test '%s'" %
283
                                 self.op.direction, errors.ECODE_INVAL)
284

    
285
  def Exec(self, feedback_fn):
286
    """Run the allocator test.
287

288
    """
289
    if self.op.mode == constants.IALLOCATOR_MODE_ALLOC:
290
      req = iallocator.IAReqInstanceAlloc(name=self.op.name,
291
                                          memory=self.op.memory,
292
                                          disks=self.op.disks,
293
                                          disk_template=self.op.disk_template,
294
                                          os=self.op.os,
295
                                          tags=self.op.tags,
296
                                          nics=self.op.nics,
297
                                          vcpus=self.op.vcpus,
298
                                          spindle_use=self.op.spindle_use,
299
                                          hypervisor=self.op.hypervisor,
300
                                          node_whitelist=None)
301
    elif self.op.mode == constants.IALLOCATOR_MODE_RELOC:
302
      req = iallocator.IAReqRelocate(name=self.op.name,
303
                                     relocate_from=list(self.relocate_from))
304
    elif self.op.mode == constants.IALLOCATOR_MODE_CHG_GROUP:
305
      req = iallocator.IAReqGroupChange(instances=self.op.instances,
306
                                        target_groups=self.op.target_groups)
307
    elif self.op.mode == constants.IALLOCATOR_MODE_NODE_EVAC:
308
      req = iallocator.IAReqNodeEvac(instances=self.op.instances,
309
                                     evac_mode=self.op.evac_mode)
310
    elif self.op.mode == constants.IALLOCATOR_MODE_MULTI_ALLOC:
311
      disk_template = self.op.disk_template
312
      insts = [iallocator.IAReqInstanceAlloc(name="%s%s" % (self.op.name, idx),
313
                                             memory=self.op.memory,
314
                                             disks=self.op.disks,
315
                                             disk_template=disk_template,
316
                                             os=self.op.os,
317
                                             tags=self.op.tags,
318
                                             nics=self.op.nics,
319
                                             vcpus=self.op.vcpus,
320
                                             spindle_use=self.op.spindle_use,
321
                                             hypervisor=self.op.hypervisor)
322
               for idx in range(self.op.count)]
323
      req = iallocator.IAReqMultiInstanceAlloc(instances=insts)
324
    else:
325
      raise errors.ProgrammerError("Uncatched mode %s in"
326
                                   " LUTestAllocator.Exec", self.op.mode)
327

    
328
    ial = iallocator.IAllocator(self.cfg, self.rpc, req)
329
    if self.op.direction == constants.IALLOCATOR_DIR_IN:
330
      result = ial.in_text
331
    else:
332
      ial.Run(self.op.iallocator, validate=False)
333
      result = ial.out_text
334
    return result