Check right disk template in inst set params
[ganeti-local] / lib / cmdlib / test.py
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