Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / test.py @ 18397489

History | View | Annotate | Download (12.1 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 ExpandInstanceUuidAndName, 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_node_uuids, self.op.on_nodes) = \
61
        GetWantedNodes(self, self.op.on_nodes)
62
      self.needed_locks[locking.LEVEL_NODE] = self.op.on_node_uuids
63

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

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

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

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

    
89

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

93
  """
94
  REQ_BGL = False
95

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

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

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

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

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

    
124
        # Send details to client
125
        cb(tmpsock)
126

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

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

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

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

160
    """
161
    self.Log(constants.ELOG_JQUEUE_TEST, (sockname, test, arg))
162

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

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

172
    """
173
    if prereq:
174
      errcls = errors.OpPrereqError
175
    else:
176
      errcls = errors.OpExecError
177

    
178
    return self._NotifyUsingSocket(compat.partial(self._SendNotification,
179
                                                  test, arg),
180
                                   errcls)
181

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

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

    
191
    self.expandnames_calls += 1
192

    
193
    if self.op.notify_waitlock:
194
      self._Notify(True, constants.JQT_EXPANDNAMES, None)
195

    
196
    self.LogInfo("Expanding names")
197

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

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

    
207
    if self.op.notify_exec:
208
      self._Notify(False, constants.JQT_EXEC, None)
209

    
210
    self.LogInfo("Executing")
211

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

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

    
223
    return True
224

    
225

    
226
class LUTestAllocator(NoHooksLU):
227
  """Run allocator tests.
228

229
  This LU runs the allocator tests
230

231
  """
232
  def CheckPrereq(self):
233
    """Check prerequisites.
234

235
    This checks the opcode parameters depending on the director and mode test.
236

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

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

    
287
  def Exec(self, feedback_fn):
288
    """Run the allocator test.
289

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

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