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 ExpandInstanceUuidAndName, 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_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
65 """Do the actual sleep.
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))
77 def Exec(self, feedback_fn):
78 """Execute the test delay opcode, with the wanted repetitions.
81 if self.op.repeat == 0:
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)
90 class LUTestJqueue(NoHooksLU):
91 """Utility LU to test some aspects of the job queue.
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
102 def _NotifyUsingSocket(cls, cb, errcls):
103 """Opens a Unix socket and waits for another program to connect.
106 @param cb: Callback to send socket name to client
108 @param errcls: Exception class to use for errors
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
114 tmpdir = tempfile.mkdtemp()
116 tmpsock = utils.PathJoin(tmpdir, "sock")
118 logging.debug("Creating temporary socket at %s", tmpsock)
119 sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
124 # Send details to client
127 # Wait for client to connect before continuing
128 sock.settimeout(cls._CLIENT_CONNECT_TIMEOUT)
130 (conn, _) = sock.accept()
131 except socket.error, err:
132 raise errcls("Client didn't connect in time (%s)" % err)
136 # Remove as soon as client is connected
137 shutil.rmtree(tmpdir)
139 # Wait for client to close
142 # pylint: disable=E1101
143 # Instance of '_socketobject' has no ... member
144 conn.settimeout(cls._CLIENT_CONFIRM_TIMEOUT)
146 except socket.error, err:
147 raise errcls("Client failed to confirm notification (%s)" % err)
151 def _SendNotification(self, test, arg, sockname):
152 """Sends a notification to the client.
155 @param test: Test name
156 @param arg: Test argument (depends on test)
157 @type sockname: string
158 @param sockname: Socket path
161 self.Log(constants.ELOG_JQUEUE_TEST, (sockname, test, arg))
163 def _Notify(self, prereq, test, arg):
164 """Notifies the client of a test.
167 @param prereq: Whether this is a prereq-phase test
169 @param test: Test name
170 @param arg: Test argument (depends on test)
174 errcls = errors.OpPrereqError
176 errcls = errors.OpExecError
178 return self._NotifyUsingSocket(compat.partial(self._SendNotification,
182 def CheckArguments(self):
183 self.checkargs_calls = getattr(self, "checkargs_calls", 0) + 1
184 self.expandnames_calls = 0
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")
191 self.expandnames_calls += 1
193 if self.op.notify_waitlock:
194 self._Notify(True, constants.JQT_EXPANDNAMES, None)
196 self.LogInfo("Expanding names")
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(),
203 def Exec(self, feedback_fn):
204 if self.expandnames_calls < 1:
205 raise errors.ProgrammerError("ExpandNames was not called")
207 if self.op.notify_exec:
208 self._Notify(False, constants.JQT_EXEC, None)
210 self.LogInfo("Executing")
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)
221 raise errors.OpExecError("Opcode failure was requested")
226 class LUTestAllocator(NoHooksLU):
227 """Run allocator tests.
229 This LU runs the allocator tests
232 def CheckPrereq(self):
233 """Check prerequisites.
235 This checks the opcode parameters depending on the director and mode test.
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'",
252 if not isinstance(self.op.disks, list):
253 raise errors.OpPrereqError("Invalid parameter 'disks'",
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)
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)
276 raise errors.OpPrereqError("Invalid test allocator mode '%s'" %
277 self.op.mode, errors.ECODE_INVAL)
279 if self.op.direction == constants.IALLOCATOR_DIR_OUT:
280 if self.op.iallocator is None:
281 raise errors.OpPrereqError("Missing allocator name",
283 elif self.op.direction != constants.IALLOCATOR_DIR_IN:
284 raise errors.OpPrereqError("Wrong allocator test '%s'" %
285 self.op.direction, errors.ECODE_INVAL)
287 def Exec(self, feedback_fn):
288 """Run the allocator test.
291 if self.op.mode == constants.IALLOCATOR_MODE_ALLOC:
292 req = iallocator.IAReqInstanceAlloc(name=self.op.name,
293 memory=self.op.memory,
295 disk_template=self.op.disk_template,
300 spindle_use=self.op.spindle_use,
301 hypervisor=self.op.hypervisor,
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,
318 disk_template=disk_template,
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)
328 raise errors.ProgrammerError("Uncatched mode %s in"
329 " LUTestAllocator.Exec", self.op.mode)
331 ial = iallocator.IAllocator(self.cfg, self.rpc, req)
332 if self.op.direction == constants.IALLOCATOR_DIR_IN:
335 ial.Run(self.op.iallocator, validate=False)
336 result = ial.out_text