Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / test.py @ 8631b46b

History | View | Annotate | Download (11.7 kB)

1
#
2
#
3

    
4
# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 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

    
56
    if self.op.duration <= 0:
57
      raise errors.OpPrereqError("Duration must be greater than zero")
58

    
59
    self.op.on_node_uuids = []
60
    if self.op.on_nodes:
61
      # _GetWantedNodes can be used here, but is not always appropriate to use
62
      # this way in ExpandNames. Check LogicalUnit.ExpandNames docstring for
63
      # more information.
64
      (self.op.on_node_uuids, self.op.on_nodes) = \
65
        GetWantedNodes(self, self.op.on_nodes)
66

    
67
    master_uuid = self.cfg.GetMasterNode()
68
    if self.op.on_master and master_uuid not in self.op.on_node_uuids:
69
      self.op.on_node_uuids.append(master_uuid)
70

    
71
    self.needed_locks = {}
72
    self.needed_locks[locking.LEVEL_NODE] = self.op.on_node_uuids
73

    
74
  def _TestDelay(self):
75
    """Do the actual sleep.
76

77
    """
78
    if self.op.on_node_uuids:
79
      result = self.rpc.call_test_delay(self.op.on_node_uuids, self.op.duration)
80
      for node_uuid, node_result in result.items():
81
        node_result.Raise("Failure during rpc call to node %s" %
82
                          self.cfg.GetNodeName(node_uuid))
83
    else:
84
      if not utils.TestDelay(self.op.duration)[0]:
85
        raise errors.OpExecError("Error during master delay test")
86

    
87
  def Exec(self, feedback_fn):
88
    """Execute the test delay opcode, with the wanted repetitions.
89

90
    """
91
    if self.op.repeat == 0:
92
      self._TestDelay()
93
    else:
94
      top_value = self.op.repeat - 1
95
      for i in range(self.op.repeat):
96
        self.LogInfo("Test delay iteration %d/%d", i, top_value)
97
        self._TestDelay()
98

    
99

    
100
class LUTestJqueue(NoHooksLU):
101
  """Utility LU to test some aspects of the job queue.
102

103
  """
104
  REQ_BGL = False
105

    
106
  # Must be lower than default timeout for WaitForJobChange to see whether it
107
  # notices changed jobs
108
  _CLIENT_CONNECT_TIMEOUT = 20.0
109
  _CLIENT_CONFIRM_TIMEOUT = 60.0
110

    
111
  @classmethod
112
  def _NotifyUsingSocket(cls, cb, errcls):
113
    """Opens a Unix socket and waits for another program to connect.
114

115
    @type cb: callable
116
    @param cb: Callback to send socket name to client
117
    @type errcls: class
118
    @param errcls: Exception class to use for errors
119

120
    """
121
    # Using a temporary directory as there's no easy way to create temporary
122
    # sockets without writing a custom loop around tempfile.mktemp and
123
    # socket.bind
124
    tmpdir = tempfile.mkdtemp()
125
    try:
126
      tmpsock = utils.PathJoin(tmpdir, "sock")
127

    
128
      logging.debug("Creating temporary socket at %s", tmpsock)
129
      sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
130
      try:
131
        sock.bind(tmpsock)
132
        sock.listen(1)
133

    
134
        # Send details to client
135
        cb(tmpsock)
136

    
137
        # Wait for client to connect before continuing
138
        sock.settimeout(cls._CLIENT_CONNECT_TIMEOUT)
139
        try:
140
          (conn, _) = sock.accept()
141
        except socket.error, err:
142
          raise errcls("Client didn't connect in time (%s)" % err)
143
      finally:
144
        sock.close()
145
    finally:
146
      # Remove as soon as client is connected
147
      shutil.rmtree(tmpdir)
148

    
149
    # Wait for client to close
150
    try:
151
      try:
152
        # pylint: disable=E1101
153
        # Instance of '_socketobject' has no ... member
154
        conn.settimeout(cls._CLIENT_CONFIRM_TIMEOUT)
155
        conn.recv(1)
156
      except socket.error, err:
157
        raise errcls("Client failed to confirm notification (%s)" % err)
158
    finally:
159
      conn.close()
160

    
161
  def _SendNotification(self, test, arg, sockname):
162
    """Sends a notification to the client.
163

164
    @type test: string
165
    @param test: Test name
166
    @param arg: Test argument (depends on test)
167
    @type sockname: string
168
    @param sockname: Socket path
169

170
    """
171
    self.Log(constants.ELOG_JQUEUE_TEST, (sockname, test, arg))
172

    
173
  def _Notify(self, prereq, test, arg):
174
    """Notifies the client of a test.
175

176
    @type prereq: bool
177
    @param prereq: Whether this is a prereq-phase test
178
    @type test: string
179
    @param test: Test name
180
    @param arg: Test argument (depends on test)
181

182
    """
183
    if prereq:
184
      errcls = errors.OpPrereqError
185
    else:
186
      errcls = errors.OpExecError
187

    
188
    return self._NotifyUsingSocket(compat.partial(self._SendNotification,
189
                                                  test, arg),
190
                                   errcls)
191

    
192
  def CheckArguments(self):
193
    self.checkargs_calls = getattr(self, "checkargs_calls", 0) + 1
194
    self.expandnames_calls = 0
195

    
196
  def ExpandNames(self):
197
    checkargs_calls = getattr(self, "checkargs_calls", 0)
198
    if checkargs_calls < 1:
199
      raise errors.ProgrammerError("CheckArguments was not called")
200

    
201
    self.expandnames_calls += 1
202

    
203
    if self.op.notify_waitlock:
204
      self._Notify(True, constants.JQT_EXPANDNAMES, None)
205

    
206
    self.LogInfo("Expanding names")
207

    
208
    # Get lock on master node (just to get a lock, not for a particular reason)
209
    self.needed_locks = {
210
      locking.LEVEL_NODE: self.cfg.GetMasterNode(),
211
      }
212

    
213
  def Exec(self, feedback_fn):
214
    if self.expandnames_calls < 1:
215
      raise errors.ProgrammerError("ExpandNames was not called")
216

    
217
    if self.op.notify_exec:
218
      self._Notify(False, constants.JQT_EXEC, None)
219

    
220
    self.LogInfo("Executing")
221

    
222
    if self.op.log_messages:
223
      self._Notify(False, constants.JQT_STARTMSG, len(self.op.log_messages))
224
      for idx, msg in enumerate(self.op.log_messages):
225
        self.LogInfo("Sending log message %s", idx + 1)
226
        feedback_fn(constants.JQT_MSGPREFIX + msg)
227
        # Report how many test messages have been sent
228
        self._Notify(False, constants.JQT_LOGMSG, idx + 1)
229

    
230
    if self.op.fail:
231
      raise errors.OpExecError("Opcode failure was requested")
232

    
233
    return True
234

    
235

    
236
class LUTestAllocator(NoHooksLU):
237
  """Run allocator tests.
238

239
  This LU runs the allocator tests
240

241
  """
242
  def CheckPrereq(self):
243
    """Check prerequisites.
244

245
    This checks the opcode parameters depending on the director and mode test.
246

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

    
278
    if self.op.direction == constants.IALLOCATOR_DIR_OUT:
279
      if self.op.iallocator is None:
280
        raise errors.OpPrereqError("Missing allocator name",
281
                                   errors.ECODE_INVAL)
282

    
283
  def Exec(self, feedback_fn):
284
    """Run the allocator test.
285

286
    """
287
    if self.op.mode == constants.IALLOCATOR_MODE_ALLOC:
288
      req = iallocator.IAReqInstanceAlloc(name=self.op.name,
289
                                          memory=self.op.memory,
290
                                          disks=self.op.disks,
291
                                          disk_template=self.op.disk_template,
292
                                          os=self.op.os,
293
                                          tags=self.op.tags,
294
                                          nics=self.op.nics,
295
                                          vcpus=self.op.vcpus,
296
                                          spindle_use=self.op.spindle_use,
297
                                          hypervisor=self.op.hypervisor,
298
                                          node_whitelist=None)
299
    elif self.op.mode == constants.IALLOCATOR_MODE_RELOC:
300
      req = iallocator.IAReqRelocate(
301
            inst_uuid=self.inst_uuid,
302
            relocate_from_node_uuids=list(self.relocate_from_node_uuids))
303
    elif self.op.mode == constants.IALLOCATOR_MODE_CHG_GROUP:
304
      req = iallocator.IAReqGroupChange(instances=self.op.instances,
305
                                        target_groups=self.op.target_groups)
306
    elif self.op.mode == constants.IALLOCATOR_MODE_NODE_EVAC:
307
      req = iallocator.IAReqNodeEvac(instances=self.op.instances,
308
                                     evac_mode=self.op.evac_mode)
309
    elif self.op.mode == constants.IALLOCATOR_MODE_MULTI_ALLOC:
310
      disk_template = self.op.disk_template
311
      insts = [iallocator.IAReqInstanceAlloc(name="%s%s" % (self.op.name, idx),
312
                                             memory=self.op.memory,
313
                                             disks=self.op.disks,
314
                                             disk_template=disk_template,
315
                                             os=self.op.os,
316
                                             tags=self.op.tags,
317
                                             nics=self.op.nics,
318
                                             vcpus=self.op.vcpus,
319
                                             spindle_use=self.op.spindle_use,
320
                                             hypervisor=self.op.hypervisor,
321
                                             node_whitelist=None)
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