Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / test.py @ 84ad6b78

History | View | Annotate | Download (11.5 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)[0]:
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
      (self.inst_uuid, iname) = self.cfg.ExpandInstanceName(self.op.name)
241
      if iname is not None:
242
        raise errors.OpPrereqError("Instance '%s' already in the cluster" %
243
                                   iname, errors.ECODE_EXISTS)
244
      for row in self.op.disks:
245
        if (not isinstance(row, dict) or
246
            constants.IDISK_SIZE not in row or
247
            not isinstance(row[constants.IDISK_SIZE], int) or
248
            constants.IDISK_MODE not in row or
249
            row[constants.IDISK_MODE] not in constants.DISK_ACCESS_SET):
250
          raise errors.OpPrereqError("Invalid contents of the 'disks'"
251
                                     " parameter", errors.ECODE_INVAL)
252
      if self.op.hypervisor is None:
253
        self.op.hypervisor = self.cfg.GetHypervisorType()
254
    elif self.op.mode == constants.IALLOCATOR_MODE_RELOC:
255
      (self.inst_uuid, self.op.name) = ExpandInstanceUuidAndName(self.cfg, None,
256
                                                                 self.op.name)
257
      self.relocate_from_node_uuids = \
258
          list(self.cfg.GetInstanceInfo(self.inst_uuid).secondary_nodes)
259
    elif self.op.mode in (constants.IALLOCATOR_MODE_CHG_GROUP,
260
                          constants.IALLOCATOR_MODE_NODE_EVAC):
261
      if not self.op.instances:
262
        raise errors.OpPrereqError("Missing instances", errors.ECODE_INVAL)
263
      (_, self.op.instances) = GetWantedInstances(self, self.op.instances)
264
    else:
265
      raise errors.OpPrereqError("Invalid test allocator mode '%s'" %
266
                                 self.op.mode, errors.ECODE_INVAL)
267

    
268
    if self.op.direction == constants.IALLOCATOR_DIR_OUT:
269
      if self.op.iallocator is None:
270
        raise errors.OpPrereqError("Missing allocator name",
271
                                   errors.ECODE_INVAL)
272

    
273
  def Exec(self, feedback_fn):
274
    """Run the allocator test.
275

276
    """
277
    if self.op.mode == constants.IALLOCATOR_MODE_ALLOC:
278
      req = iallocator.IAReqInstanceAlloc(name=self.op.name,
279
                                          memory=self.op.memory,
280
                                          disks=self.op.disks,
281
                                          disk_template=self.op.disk_template,
282
                                          os=self.op.os,
283
                                          tags=self.op.tags,
284
                                          nics=self.op.nics,
285
                                          vcpus=self.op.vcpus,
286
                                          spindle_use=self.op.spindle_use,
287
                                          hypervisor=self.op.hypervisor,
288
                                          node_whitelist=None)
289
    elif self.op.mode == constants.IALLOCATOR_MODE_RELOC:
290
      req = iallocator.IAReqRelocate(
291
            inst_uuid=self.inst_uuid,
292
            relocate_from_node_uuids=list(self.relocate_from_node_uuids))
293
    elif self.op.mode == constants.IALLOCATOR_MODE_CHG_GROUP:
294
      req = iallocator.IAReqGroupChange(instances=self.op.instances,
295
                                        target_groups=self.op.target_groups)
296
    elif self.op.mode == constants.IALLOCATOR_MODE_NODE_EVAC:
297
      req = iallocator.IAReqNodeEvac(instances=self.op.instances,
298
                                     evac_mode=self.op.evac_mode)
299
    elif self.op.mode == constants.IALLOCATOR_MODE_MULTI_ALLOC:
300
      disk_template = self.op.disk_template
301
      insts = [iallocator.IAReqInstanceAlloc(name="%s%s" % (self.op.name, idx),
302
                                             memory=self.op.memory,
303
                                             disks=self.op.disks,
304
                                             disk_template=disk_template,
305
                                             os=self.op.os,
306
                                             tags=self.op.tags,
307
                                             nics=self.op.nics,
308
                                             vcpus=self.op.vcpus,
309
                                             spindle_use=self.op.spindle_use,
310
                                             hypervisor=self.op.hypervisor,
311
                                             node_whitelist=None)
312
               for idx in range(self.op.count)]
313
      req = iallocator.IAReqMultiInstanceAlloc(instances=insts)
314
    else:
315
      raise errors.ProgrammerError("Uncatched mode %s in"
316
                                   " LUTestAllocator.Exec", self.op.mode)
317

    
318
    ial = iallocator.IAllocator(self.cfg, self.rpc, req)
319
    if self.op.direction == constants.IALLOCATOR_DIR_IN:
320
      result = ial.in_text
321
    else:
322
      ial.Run(self.op.iallocator, validate=False)
323
      result = ial.out_text
324
    return result