4 # Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Google Inc.
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.
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.
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
22 """Test logical units."""
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, \
40 class LUTestDelay(NoHooksLU):
41 """Sleep for a specified amount of time.
43 This LU sleeps on the master and/or nodes for a specified amount of
49 def ExpandNames(self):
50 """Expand names and set required locks.
52 This expands the node list, if any.
55 self.needed_locks = {}
57 # _GetWantedNodes can be used here, but is not always appropriate to use
58 # this way in ExpandNames. Check LogicalUnit.ExpandNames docstring for
60 self.op.on_nodes = GetWantedNodes(self, self.op.on_nodes)
61 self.needed_locks[locking.LEVEL_NODE] = self.op.on_nodes
64 """Do the actual sleep.
68 if not utils.TestDelay(self.op.duration):
69 raise errors.OpExecError("Error during master delay test")
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)
75 def Exec(self, feedback_fn):
76 """Execute the test delay opcode, with the wanted repetitions.
79 if self.op.repeat == 0:
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)
88 class LUTestJqueue(NoHooksLU):
89 """Utility LU to test some aspects of the job queue.
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
100 def _NotifyUsingSocket(cls, cb, errcls):
101 """Opens a Unix socket and waits for another program to connect.
104 @param cb: Callback to send socket name to client
106 @param errcls: Exception class to use for errors
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
112 tmpdir = tempfile.mkdtemp()
114 tmpsock = utils.PathJoin(tmpdir, "sock")
116 logging.debug("Creating temporary socket at %s", tmpsock)
117 sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
122 # Send details to client
125 # Wait for client to connect before continuing
126 sock.settimeout(cls._CLIENT_CONNECT_TIMEOUT)
128 (conn, _) = sock.accept()
129 except socket.error, err:
130 raise errcls("Client didn't connect in time (%s)" % err)
134 # Remove as soon as client is connected
135 shutil.rmtree(tmpdir)
137 # Wait for client to close
140 # pylint: disable=E1101
141 # Instance of '_socketobject' has no ... member
142 conn.settimeout(cls._CLIENT_CONFIRM_TIMEOUT)
144 except socket.error, err:
145 raise errcls("Client failed to confirm notification (%s)" % err)
149 def _SendNotification(self, test, arg, sockname):
150 """Sends a notification to the client.
153 @param test: Test name
154 @param arg: Test argument (depends on test)
155 @type sockname: string
156 @param sockname: Socket path
159 self.Log(constants.ELOG_JQUEUE_TEST, (sockname, test, arg))
161 def _Notify(self, prereq, test, arg):
162 """Notifies the client of a test.
165 @param prereq: Whether this is a prereq-phase test
167 @param test: Test name
168 @param arg: Test argument (depends on test)
172 errcls = errors.OpPrereqError
174 errcls = errors.OpExecError
176 return self._NotifyUsingSocket(compat.partial(self._SendNotification,
180 def CheckArguments(self):
181 self.checkargs_calls = getattr(self, "checkargs_calls", 0) + 1
182 self.expandnames_calls = 0
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")
189 self.expandnames_calls += 1
191 if self.op.notify_waitlock:
192 self._Notify(True, constants.JQT_EXPANDNAMES, None)
194 self.LogInfo("Expanding names")
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(),
201 def Exec(self, feedback_fn):
202 if self.expandnames_calls < 1:
203 raise errors.ProgrammerError("ExpandNames was not called")
205 if self.op.notify_exec:
206 self._Notify(False, constants.JQT_EXEC, None)
208 self.LogInfo("Executing")
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)
219 raise errors.OpExecError("Opcode failure was requested")
224 class LUTestAllocator(NoHooksLU):
225 """Run allocator tests.
227 This LU runs the allocator tests
230 def CheckPrereq(self):
231 """Check prerequisites.
233 This checks the opcode parameters depending on the director and mode test.
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'",
250 if not isinstance(self.op.disks, list):
251 raise errors.OpPrereqError("Invalid parameter 'disks'",
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)
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)
274 raise errors.OpPrereqError("Invalid test allocator mode '%s'" %
275 self.op.mode, errors.ECODE_INVAL)
277 if self.op.direction == constants.IALLOCATOR_DIR_OUT:
278 if self.op.iallocator is None:
279 raise errors.OpPrereqError("Missing allocator name",
281 elif self.op.direction != constants.IALLOCATOR_DIR_IN:
282 raise errors.OpPrereqError("Wrong allocator test '%s'" %
283 self.op.direction, errors.ECODE_INVAL)
285 def Exec(self, feedback_fn):
286 """Run the allocator test.
289 if self.op.mode == constants.IALLOCATOR_MODE_ALLOC:
290 req = iallocator.IAReqInstanceAlloc(name=self.op.name,
291 memory=self.op.memory,
293 disk_template=self.op.disk_template,
298 spindle_use=self.op.spindle_use,
299 hypervisor=self.op.hypervisor,
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,
315 disk_template=disk_template,
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)
325 raise errors.ProgrammerError("Uncatched mode %s in"
326 " LUTestAllocator.Exec", self.op.mode)
328 ial = iallocator.IAllocator(self.cfg, self.rpc, req)
329 if self.op.direction == constants.IALLOCATOR_DIR_IN:
332 ial.Run(self.op.iallocator, validate=False)
333 result = ial.out_text