root / lib / cmdlib / test.py @ 809a055b
History | View | Annotate | Download (14.7 kB)
1 | 1d870e0d | Thomas Thrainer | #
|
---|---|---|---|
2 | 1d870e0d | Thomas Thrainer | #
|
3 | 1d870e0d | Thomas Thrainer | |
4 | 054a9d17 | Hrvoje Ribicic | # Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Google Inc.
|
5 | 1d870e0d | Thomas Thrainer | #
|
6 | 1d870e0d | Thomas Thrainer | # This program is free software; you can redistribute it and/or modify
|
7 | 1d870e0d | Thomas Thrainer | # it under the terms of the GNU General Public License as published by
|
8 | 1d870e0d | Thomas Thrainer | # the Free Software Foundation; either version 2 of the License, or
|
9 | 1d870e0d | Thomas Thrainer | # (at your option) any later version.
|
10 | 1d870e0d | Thomas Thrainer | #
|
11 | 1d870e0d | Thomas Thrainer | # This program is distributed in the hope that it will be useful, but
|
12 | 1d870e0d | Thomas Thrainer | # WITHOUT ANY WARRANTY; without even the implied warranty of
|
13 | 1d870e0d | Thomas Thrainer | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
14 | 1d870e0d | Thomas Thrainer | # General Public License for more details.
|
15 | 1d870e0d | Thomas Thrainer | #
|
16 | 1d870e0d | Thomas Thrainer | # You should have received a copy of the GNU General Public License
|
17 | 1d870e0d | Thomas Thrainer | # along with this program; if not, write to the Free Software
|
18 | 1d870e0d | Thomas Thrainer | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
19 | 1d870e0d | Thomas Thrainer | # 02110-1301, USA.
|
20 | 1d870e0d | Thomas Thrainer | |
21 | 1d870e0d | Thomas Thrainer | |
22 | 1d870e0d | Thomas Thrainer | """Test logical units."""
|
23 | 1d870e0d | Thomas Thrainer | |
24 | 1d870e0d | Thomas Thrainer | import logging |
25 | 1d870e0d | Thomas Thrainer | import shutil |
26 | 1d870e0d | Thomas Thrainer | import socket |
27 | 1d870e0d | Thomas Thrainer | import tempfile |
28 | 9dc47292 | Hrvoje Ribicic | import time |
29 | 1d870e0d | Thomas Thrainer | |
30 | 1d870e0d | Thomas Thrainer | from ganeti import compat |
31 | 1d870e0d | Thomas Thrainer | from ganeti import constants |
32 | 1d870e0d | Thomas Thrainer | from ganeti import errors |
33 | 1d870e0d | Thomas Thrainer | from ganeti import locking |
34 | 1d870e0d | Thomas Thrainer | from ganeti import utils |
35 | 1d870e0d | Thomas Thrainer | from ganeti.masterd import iallocator |
36 | 1d870e0d | Thomas Thrainer | from ganeti.cmdlib.base import NoHooksLU |
37 | da4a52a3 | Thomas Thrainer | from ganeti.cmdlib.common import ExpandInstanceUuidAndName, GetWantedNodes, \ |
38 | 5eacbcae | Thomas Thrainer | GetWantedInstances
|
39 | 1d870e0d | Thomas Thrainer | |
40 | 1d870e0d | Thomas Thrainer | |
41 | c346d0ac | Hrvoje Ribicic | class TestSocketWrapper(object): |
42 | c346d0ac | Hrvoje Ribicic | """ Utility class that opens a domain socket and cleans up as needed.
|
43 | c346d0ac | Hrvoje Ribicic |
|
44 | c346d0ac | Hrvoje Ribicic | """
|
45 | c346d0ac | Hrvoje Ribicic | def __init__(self): |
46 | c346d0ac | Hrvoje Ribicic | """ Constructor cleaning up variables to be used.
|
47 | c346d0ac | Hrvoje Ribicic |
|
48 | c346d0ac | Hrvoje Ribicic | """
|
49 | c346d0ac | Hrvoje Ribicic | self.tmpdir = None |
50 | c346d0ac | Hrvoje Ribicic | self.sock = None |
51 | c346d0ac | Hrvoje Ribicic | |
52 | c346d0ac | Hrvoje Ribicic | def Create(self, max_connections=1): |
53 | c346d0ac | Hrvoje Ribicic | """ Creates a bound and ready socket, cleaning up in case of failure.
|
54 | c346d0ac | Hrvoje Ribicic |
|
55 | c346d0ac | Hrvoje Ribicic | @type max_connections: int
|
56 | c346d0ac | Hrvoje Ribicic | @param max_connections: The number of max connections allowed for the
|
57 | c346d0ac | Hrvoje Ribicic | socket.
|
58 | c346d0ac | Hrvoje Ribicic |
|
59 | c346d0ac | Hrvoje Ribicic | @rtype: tuple of socket, string
|
60 | c346d0ac | Hrvoje Ribicic | @return: The socket object and the path to reach it with.
|
61 | c346d0ac | Hrvoje Ribicic |
|
62 | c346d0ac | Hrvoje Ribicic | """
|
63 | c346d0ac | Hrvoje Ribicic | # Using a temporary directory as there's no easy way to create temporary
|
64 | c346d0ac | Hrvoje Ribicic | # sockets without writing a custom loop around tempfile.mktemp and
|
65 | c346d0ac | Hrvoje Ribicic | # socket.bind
|
66 | c346d0ac | Hrvoje Ribicic | self.tmpdir = tempfile.mkdtemp()
|
67 | c346d0ac | Hrvoje Ribicic | try:
|
68 | c346d0ac | Hrvoje Ribicic | tmpsock = utils.PathJoin(self.tmpdir, "sock") |
69 | c346d0ac | Hrvoje Ribicic | logging.debug("Creating temporary socket at %s", tmpsock)
|
70 | c346d0ac | Hrvoje Ribicic | self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
71 | c346d0ac | Hrvoje Ribicic | try:
|
72 | c346d0ac | Hrvoje Ribicic | self.sock.bind(tmpsock)
|
73 | c346d0ac | Hrvoje Ribicic | self.sock.listen(max_connections)
|
74 | c346d0ac | Hrvoje Ribicic | except:
|
75 | c346d0ac | Hrvoje Ribicic | self.sock.close()
|
76 | c346d0ac | Hrvoje Ribicic | raise
|
77 | c346d0ac | Hrvoje Ribicic | except:
|
78 | c346d0ac | Hrvoje Ribicic | shutil.rmtree(self.tmpdir)
|
79 | c346d0ac | Hrvoje Ribicic | raise
|
80 | c346d0ac | Hrvoje Ribicic | |
81 | c346d0ac | Hrvoje Ribicic | return self.sock, tmpsock |
82 | c346d0ac | Hrvoje Ribicic | |
83 | c346d0ac | Hrvoje Ribicic | def Destroy(self): |
84 | c346d0ac | Hrvoje Ribicic | """ Destroys the socket and performs all necessary cleanup.
|
85 | c346d0ac | Hrvoje Ribicic |
|
86 | c346d0ac | Hrvoje Ribicic | """
|
87 | c346d0ac | Hrvoje Ribicic | if self.tmpdir is None or self.sock is None: |
88 | c346d0ac | Hrvoje Ribicic | raise Exception("A socket must be created successfully before attempting " |
89 | c346d0ac | Hrvoje Ribicic | "its destruction")
|
90 | c346d0ac | Hrvoje Ribicic | |
91 | c346d0ac | Hrvoje Ribicic | try:
|
92 | c346d0ac | Hrvoje Ribicic | self.sock.close()
|
93 | c346d0ac | Hrvoje Ribicic | finally:
|
94 | c346d0ac | Hrvoje Ribicic | shutil.rmtree(self.tmpdir)
|
95 | c346d0ac | Hrvoje Ribicic | |
96 | c346d0ac | Hrvoje Ribicic | |
97 | 1d870e0d | Thomas Thrainer | class LUTestDelay(NoHooksLU): |
98 | 1d870e0d | Thomas Thrainer | """Sleep for a specified amount of time.
|
99 | 1d870e0d | Thomas Thrainer |
|
100 | 1d870e0d | Thomas Thrainer | This LU sleeps on the master and/or nodes for a specified amount of
|
101 | 1d870e0d | Thomas Thrainer | time.
|
102 | 1d870e0d | Thomas Thrainer |
|
103 | 1d870e0d | Thomas Thrainer | """
|
104 | 1d870e0d | Thomas Thrainer | REQ_BGL = False
|
105 | 1d870e0d | Thomas Thrainer | |
106 | 1d870e0d | Thomas Thrainer | def ExpandNames(self): |
107 | 1d870e0d | Thomas Thrainer | """Expand names and set required locks.
|
108 | 1d870e0d | Thomas Thrainer |
|
109 | 1d870e0d | Thomas Thrainer | This expands the node list, if any.
|
110 | 1d870e0d | Thomas Thrainer |
|
111 | 1d870e0d | Thomas Thrainer | """
|
112 | 809a055b | Klaus Aehlig | self.needed_locks = {}
|
113 | 8631b46b | Hrvoje Ribicic | |
114 | 8631b46b | Hrvoje Ribicic | if self.op.duration <= 0: |
115 | 8631b46b | Hrvoje Ribicic | raise errors.OpPrereqError("Duration must be greater than zero") |
116 | 8631b46b | Hrvoje Ribicic | |
117 | aa112e9f | Thomas Thrainer | if not self.op.no_locks and (self.op.on_nodes or self.op.on_master): |
118 | 054a9d17 | Hrvoje Ribicic | self.needed_locks[locking.LEVEL_NODE] = []
|
119 | 054a9d17 | Hrvoje Ribicic | |
120 | 36870aa1 | Hrvoje Ribicic | self.op.on_node_uuids = []
|
121 | 1d870e0d | Thomas Thrainer | if self.op.on_nodes: |
122 | 1d870e0d | Thomas Thrainer | # _GetWantedNodes can be used here, but is not always appropriate to use
|
123 | 1d870e0d | Thomas Thrainer | # this way in ExpandNames. Check LogicalUnit.ExpandNames docstring for
|
124 | 1d870e0d | Thomas Thrainer | # more information.
|
125 | 1c3231aa | Thomas Thrainer | (self.op.on_node_uuids, self.op.on_nodes) = \ |
126 | 1c3231aa | Thomas Thrainer | GetWantedNodes(self, self.op.on_nodes) |
127 | 054a9d17 | Hrvoje Ribicic | |
128 | 36870aa1 | Hrvoje Ribicic | master_uuid = self.cfg.GetMasterNode()
|
129 | 36870aa1 | Hrvoje Ribicic | if self.op.on_master and master_uuid not in self.op.on_node_uuids: |
130 | 36870aa1 | Hrvoje Ribicic | self.op.on_node_uuids.append(master_uuid)
|
131 | 36870aa1 | Hrvoje Ribicic | |
132 | 36870aa1 | Hrvoje Ribicic | self.needed_locks = {}
|
133 | 36870aa1 | Hrvoje Ribicic | self.needed_locks[locking.LEVEL_NODE] = self.op.on_node_uuids |
134 | 1d870e0d | Thomas Thrainer | |
135 | 9dc47292 | Hrvoje Ribicic | def _InterruptibleDelay(self): |
136 | 9dc47292 | Hrvoje Ribicic | """Delays but provides the mechanisms necessary to interrupt the delay as
|
137 | 9dc47292 | Hrvoje Ribicic | needed.
|
138 | 9dc47292 | Hrvoje Ribicic |
|
139 | 9dc47292 | Hrvoje Ribicic | """
|
140 | 9dc47292 | Hrvoje Ribicic | socket_wrapper = TestSocketWrapper() |
141 | 9dc47292 | Hrvoje Ribicic | sock, path = socket_wrapper.Create() |
142 | 9dc47292 | Hrvoje Ribicic | |
143 | 9dc47292 | Hrvoje Ribicic | self.Log(constants.ELOG_DELAY_TEST, (path,))
|
144 | 9dc47292 | Hrvoje Ribicic | |
145 | 9dc47292 | Hrvoje Ribicic | try:
|
146 | 9dc47292 | Hrvoje Ribicic | sock.settimeout(self.op.duration)
|
147 | 9dc47292 | Hrvoje Ribicic | start = time.time() |
148 | 9dc47292 | Hrvoje Ribicic | (conn, _) = sock.accept() |
149 | 9dc47292 | Hrvoje Ribicic | except socket.timeout, _:
|
150 | 9dc47292 | Hrvoje Ribicic | # If we timed out, all is well
|
151 | 9dc47292 | Hrvoje Ribicic | return False |
152 | 9dc47292 | Hrvoje Ribicic | finally:
|
153 | 9dc47292 | Hrvoje Ribicic | # Destroys the original socket, but the new connection is still usable
|
154 | 9dc47292 | Hrvoje Ribicic | socket_wrapper.Destroy() |
155 | 9dc47292 | Hrvoje Ribicic | |
156 | 9dc47292 | Hrvoje Ribicic | try:
|
157 | 9dc47292 | Hrvoje Ribicic | # Change to remaining time
|
158 | 9dc47292 | Hrvoje Ribicic | time_to_go = self.op.duration - (time.time() - start)
|
159 | 9dc47292 | Hrvoje Ribicic | self.Log(constants.ELOG_MESSAGE,
|
160 | 9dc47292 | Hrvoje Ribicic | "Received connection, time to go is %d" % time_to_go)
|
161 | 9dc47292 | Hrvoje Ribicic | if time_to_go < 0: |
162 | 9dc47292 | Hrvoje Ribicic | time_to_go = 0
|
163 | 9dc47292 | Hrvoje Ribicic | # pylint: disable=E1101
|
164 | 9dc47292 | Hrvoje Ribicic | # Instance of '_socketobject' has no ... member
|
165 | 9dc47292 | Hrvoje Ribicic | conn.settimeout(time_to_go) |
166 | 9dc47292 | Hrvoje Ribicic | conn.recv(1)
|
167 | 9dc47292 | Hrvoje Ribicic | # pylint: enable=E1101
|
168 | 9dc47292 | Hrvoje Ribicic | except socket.timeout, _:
|
169 | 9dc47292 | Hrvoje Ribicic | # A second timeout can occur if no data is sent
|
170 | 9dc47292 | Hrvoje Ribicic | return False |
171 | 9dc47292 | Hrvoje Ribicic | finally:
|
172 | 9dc47292 | Hrvoje Ribicic | conn.close() |
173 | 9dc47292 | Hrvoje Ribicic | |
174 | 9dc47292 | Hrvoje Ribicic | self.Log(constants.ELOG_MESSAGE,
|
175 | 9dc47292 | Hrvoje Ribicic | "Interrupted, time spent waiting: %d" % (time.time() - start))
|
176 | 9dc47292 | Hrvoje Ribicic | |
177 | 9dc47292 | Hrvoje Ribicic | # Reaching this point means we were interrupted
|
178 | 9dc47292 | Hrvoje Ribicic | return True |
179 | 9dc47292 | Hrvoje Ribicic | |
180 | 9dc47292 | Hrvoje Ribicic | def _UninterruptibleDelay(self): |
181 | 9dc47292 | Hrvoje Ribicic | """Delays without allowing interruptions.
|
182 | 1d870e0d | Thomas Thrainer |
|
183 | 1d870e0d | Thomas Thrainer | """
|
184 | 1c3231aa | Thomas Thrainer | if self.op.on_node_uuids: |
185 | 1c3231aa | Thomas Thrainer | result = self.rpc.call_test_delay(self.op.on_node_uuids, self.op.duration) |
186 | 1c3231aa | Thomas Thrainer | for node_uuid, node_result in result.items(): |
187 | 1c3231aa | Thomas Thrainer | node_result.Raise("Failure during rpc call to node %s" %
|
188 | 1c3231aa | Thomas Thrainer | self.cfg.GetNodeName(node_uuid))
|
189 | 36870aa1 | Hrvoje Ribicic | else:
|
190 | 36870aa1 | Hrvoje Ribicic | if not utils.TestDelay(self.op.duration)[0]: |
191 | 36870aa1 | Hrvoje Ribicic | raise errors.OpExecError("Error during master delay test") |
192 | 1d870e0d | Thomas Thrainer | |
193 | 9dc47292 | Hrvoje Ribicic | def _TestDelay(self): |
194 | 9dc47292 | Hrvoje Ribicic | """Do the actual sleep.
|
195 | 9dc47292 | Hrvoje Ribicic |
|
196 | 9dc47292 | Hrvoje Ribicic | @rtype: bool
|
197 | 9dc47292 | Hrvoje Ribicic | @return: Whether the delay was interrupted
|
198 | 9dc47292 | Hrvoje Ribicic |
|
199 | 9dc47292 | Hrvoje Ribicic | """
|
200 | 9dc47292 | Hrvoje Ribicic | if self.op.interruptible: |
201 | 9dc47292 | Hrvoje Ribicic | return self._InterruptibleDelay() |
202 | 9dc47292 | Hrvoje Ribicic | else:
|
203 | 9dc47292 | Hrvoje Ribicic | self._UninterruptibleDelay()
|
204 | 9dc47292 | Hrvoje Ribicic | return False |
205 | 9dc47292 | Hrvoje Ribicic | |
206 | 1d870e0d | Thomas Thrainer | def Exec(self, feedback_fn): |
207 | 1d870e0d | Thomas Thrainer | """Execute the test delay opcode, with the wanted repetitions.
|
208 | 1d870e0d | Thomas Thrainer |
|
209 | 1d870e0d | Thomas Thrainer | """
|
210 | 1d870e0d | Thomas Thrainer | if self.op.repeat == 0: |
211 | 9dc47292 | Hrvoje Ribicic | i = self._TestDelay()
|
212 | 1d870e0d | Thomas Thrainer | else:
|
213 | 1d870e0d | Thomas Thrainer | top_value = self.op.repeat - 1 |
214 | 1d870e0d | Thomas Thrainer | for i in range(self.op.repeat): |
215 | 1d870e0d | Thomas Thrainer | self.LogInfo("Test delay iteration %d/%d", i, top_value) |
216 | 9dc47292 | Hrvoje Ribicic | # Break in case of interruption
|
217 | 9dc47292 | Hrvoje Ribicic | if self._TestDelay(): |
218 | 9dc47292 | Hrvoje Ribicic | break
|
219 | 1d870e0d | Thomas Thrainer | |
220 | 1d870e0d | Thomas Thrainer | |
221 | 1d870e0d | Thomas Thrainer | class LUTestJqueue(NoHooksLU): |
222 | 1d870e0d | Thomas Thrainer | """Utility LU to test some aspects of the job queue.
|
223 | 1d870e0d | Thomas Thrainer |
|
224 | 1d870e0d | Thomas Thrainer | """
|
225 | 1d870e0d | Thomas Thrainer | REQ_BGL = False
|
226 | 1d870e0d | Thomas Thrainer | |
227 | 1d870e0d | Thomas Thrainer | # Must be lower than default timeout for WaitForJobChange to see whether it
|
228 | 1d870e0d | Thomas Thrainer | # notices changed jobs
|
229 | 1d870e0d | Thomas Thrainer | _CLIENT_CONNECT_TIMEOUT = 20.0
|
230 | 1d870e0d | Thomas Thrainer | _CLIENT_CONFIRM_TIMEOUT = 60.0
|
231 | 1d870e0d | Thomas Thrainer | |
232 | 1d870e0d | Thomas Thrainer | @classmethod
|
233 | 1d870e0d | Thomas Thrainer | def _NotifyUsingSocket(cls, cb, errcls): |
234 | 1d870e0d | Thomas Thrainer | """Opens a Unix socket and waits for another program to connect.
|
235 | 1d870e0d | Thomas Thrainer |
|
236 | 1d870e0d | Thomas Thrainer | @type cb: callable
|
237 | 1d870e0d | Thomas Thrainer | @param cb: Callback to send socket name to client
|
238 | 1d870e0d | Thomas Thrainer | @type errcls: class
|
239 | 1d870e0d | Thomas Thrainer | @param errcls: Exception class to use for errors
|
240 | 1d870e0d | Thomas Thrainer |
|
241 | 1d870e0d | Thomas Thrainer | """
|
242 | c346d0ac | Hrvoje Ribicic | |
243 | 1d870e0d | Thomas Thrainer | # Using a temporary directory as there's no easy way to create temporary
|
244 | 1d870e0d | Thomas Thrainer | # sockets without writing a custom loop around tempfile.mktemp and
|
245 | 1d870e0d | Thomas Thrainer | # socket.bind
|
246 | 1d870e0d | Thomas Thrainer | |
247 | c346d0ac | Hrvoje Ribicic | socket_wrapper = TestSocketWrapper() |
248 | c346d0ac | Hrvoje Ribicic | sock, path = socket_wrapper.Create() |
249 | c346d0ac | Hrvoje Ribicic | |
250 | c346d0ac | Hrvoje Ribicic | cb(path) |
251 | c346d0ac | Hrvoje Ribicic | |
252 | c346d0ac | Hrvoje Ribicic | try:
|
253 | c346d0ac | Hrvoje Ribicic | sock.settimeout(cls._CLIENT_CONNECT_TIMEOUT) |
254 | c346d0ac | Hrvoje Ribicic | (conn, _) = sock.accept() |
255 | c346d0ac | Hrvoje Ribicic | except socket.error, err:
|
256 | c346d0ac | Hrvoje Ribicic | raise errcls("Client didn't connect in time (%s)" % err) |
257 | 1d870e0d | Thomas Thrainer | finally:
|
258 | c346d0ac | Hrvoje Ribicic | socket_wrapper.Destroy() |
259 | 1d870e0d | Thomas Thrainer | |
260 | 1d870e0d | Thomas Thrainer | # Wait for client to close
|
261 | 1d870e0d | Thomas Thrainer | try:
|
262 | 1d870e0d | Thomas Thrainer | try:
|
263 | 1d870e0d | Thomas Thrainer | # pylint: disable=E1101
|
264 | 1d870e0d | Thomas Thrainer | # Instance of '_socketobject' has no ... member
|
265 | 1d870e0d | Thomas Thrainer | conn.settimeout(cls._CLIENT_CONFIRM_TIMEOUT) |
266 | 1d870e0d | Thomas Thrainer | conn.recv(1)
|
267 | 1d870e0d | Thomas Thrainer | except socket.error, err:
|
268 | 1d870e0d | Thomas Thrainer | raise errcls("Client failed to confirm notification (%s)" % err) |
269 | 1d870e0d | Thomas Thrainer | finally:
|
270 | 1d870e0d | Thomas Thrainer | conn.close() |
271 | 1d870e0d | Thomas Thrainer | |
272 | 1d870e0d | Thomas Thrainer | def _SendNotification(self, test, arg, sockname): |
273 | 1d870e0d | Thomas Thrainer | """Sends a notification to the client.
|
274 | 1d870e0d | Thomas Thrainer |
|
275 | 1d870e0d | Thomas Thrainer | @type test: string
|
276 | 1d870e0d | Thomas Thrainer | @param test: Test name
|
277 | 1d870e0d | Thomas Thrainer | @param arg: Test argument (depends on test)
|
278 | 1d870e0d | Thomas Thrainer | @type sockname: string
|
279 | 1d870e0d | Thomas Thrainer | @param sockname: Socket path
|
280 | 1d870e0d | Thomas Thrainer |
|
281 | 1d870e0d | Thomas Thrainer | """
|
282 | 1d870e0d | Thomas Thrainer | self.Log(constants.ELOG_JQUEUE_TEST, (sockname, test, arg))
|
283 | 1d870e0d | Thomas Thrainer | |
284 | 1d870e0d | Thomas Thrainer | def _Notify(self, prereq, test, arg): |
285 | 1d870e0d | Thomas Thrainer | """Notifies the client of a test.
|
286 | 1d870e0d | Thomas Thrainer |
|
287 | 1d870e0d | Thomas Thrainer | @type prereq: bool
|
288 | 1d870e0d | Thomas Thrainer | @param prereq: Whether this is a prereq-phase test
|
289 | 1d870e0d | Thomas Thrainer | @type test: string
|
290 | 1d870e0d | Thomas Thrainer | @param test: Test name
|
291 | 1d870e0d | Thomas Thrainer | @param arg: Test argument (depends on test)
|
292 | 1d870e0d | Thomas Thrainer |
|
293 | 1d870e0d | Thomas Thrainer | """
|
294 | 1d870e0d | Thomas Thrainer | if prereq:
|
295 | 1d870e0d | Thomas Thrainer | errcls = errors.OpPrereqError |
296 | 1d870e0d | Thomas Thrainer | else:
|
297 | 1d870e0d | Thomas Thrainer | errcls = errors.OpExecError |
298 | 1d870e0d | Thomas Thrainer | |
299 | 1d870e0d | Thomas Thrainer | return self._NotifyUsingSocket(compat.partial(self._SendNotification, |
300 | 1d870e0d | Thomas Thrainer | test, arg), |
301 | 1d870e0d | Thomas Thrainer | errcls) |
302 | 1d870e0d | Thomas Thrainer | |
303 | 1d870e0d | Thomas Thrainer | def CheckArguments(self): |
304 | 1d870e0d | Thomas Thrainer | self.checkargs_calls = getattr(self, "checkargs_calls", 0) + 1 |
305 | 1d870e0d | Thomas Thrainer | self.expandnames_calls = 0 |
306 | 1d870e0d | Thomas Thrainer | |
307 | 1d870e0d | Thomas Thrainer | def ExpandNames(self): |
308 | 1d870e0d | Thomas Thrainer | checkargs_calls = getattr(self, "checkargs_calls", 0) |
309 | 1d870e0d | Thomas Thrainer | if checkargs_calls < 1: |
310 | 1d870e0d | Thomas Thrainer | raise errors.ProgrammerError("CheckArguments was not called") |
311 | 1d870e0d | Thomas Thrainer | |
312 | 1d870e0d | Thomas Thrainer | self.expandnames_calls += 1 |
313 | 1d870e0d | Thomas Thrainer | |
314 | 1d870e0d | Thomas Thrainer | if self.op.notify_waitlock: |
315 | 1d870e0d | Thomas Thrainer | self._Notify(True, constants.JQT_EXPANDNAMES, None) |
316 | 1d870e0d | Thomas Thrainer | |
317 | 1d870e0d | Thomas Thrainer | self.LogInfo("Expanding names") |
318 | 1d870e0d | Thomas Thrainer | |
319 | 1d870e0d | Thomas Thrainer | # Get lock on master node (just to get a lock, not for a particular reason)
|
320 | 1d870e0d | Thomas Thrainer | self.needed_locks = {
|
321 | 1d870e0d | Thomas Thrainer | locking.LEVEL_NODE: self.cfg.GetMasterNode(),
|
322 | 1d870e0d | Thomas Thrainer | } |
323 | 1d870e0d | Thomas Thrainer | |
324 | 1d870e0d | Thomas Thrainer | def Exec(self, feedback_fn): |
325 | 1d870e0d | Thomas Thrainer | if self.expandnames_calls < 1: |
326 | 1d870e0d | Thomas Thrainer | raise errors.ProgrammerError("ExpandNames was not called") |
327 | 1d870e0d | Thomas Thrainer | |
328 | 1d870e0d | Thomas Thrainer | if self.op.notify_exec: |
329 | 1d870e0d | Thomas Thrainer | self._Notify(False, constants.JQT_EXEC, None) |
330 | 1d870e0d | Thomas Thrainer | |
331 | 1d870e0d | Thomas Thrainer | self.LogInfo("Executing") |
332 | 1d870e0d | Thomas Thrainer | |
333 | 1d870e0d | Thomas Thrainer | if self.op.log_messages: |
334 | 1d870e0d | Thomas Thrainer | self._Notify(False, constants.JQT_STARTMSG, len(self.op.log_messages)) |
335 | 1d870e0d | Thomas Thrainer | for idx, msg in enumerate(self.op.log_messages): |
336 | 1d870e0d | Thomas Thrainer | self.LogInfo("Sending log message %s", idx + 1) |
337 | 1d870e0d | Thomas Thrainer | feedback_fn(constants.JQT_MSGPREFIX + msg) |
338 | 1d870e0d | Thomas Thrainer | # Report how many test messages have been sent
|
339 | 1d870e0d | Thomas Thrainer | self._Notify(False, constants.JQT_LOGMSG, idx + 1) |
340 | 1d870e0d | Thomas Thrainer | |
341 | 1d870e0d | Thomas Thrainer | if self.op.fail: |
342 | 1d870e0d | Thomas Thrainer | raise errors.OpExecError("Opcode failure was requested") |
343 | 1d870e0d | Thomas Thrainer | |
344 | 1d870e0d | Thomas Thrainer | return True |
345 | 1d870e0d | Thomas Thrainer | |
346 | 1d870e0d | Thomas Thrainer | |
347 | 1d870e0d | Thomas Thrainer | class LUTestAllocator(NoHooksLU): |
348 | 1d870e0d | Thomas Thrainer | """Run allocator tests.
|
349 | 1d870e0d | Thomas Thrainer |
|
350 | 1d870e0d | Thomas Thrainer | This LU runs the allocator tests
|
351 | 1d870e0d | Thomas Thrainer |
|
352 | 1d870e0d | Thomas Thrainer | """
|
353 | 1d870e0d | Thomas Thrainer | def CheckPrereq(self): |
354 | 1d870e0d | Thomas Thrainer | """Check prerequisites.
|
355 | 1d870e0d | Thomas Thrainer |
|
356 | 1d870e0d | Thomas Thrainer | This checks the opcode parameters depending on the director and mode test.
|
357 | 1d870e0d | Thomas Thrainer |
|
358 | 1d870e0d | Thomas Thrainer | """
|
359 | 1d870e0d | Thomas Thrainer | if self.op.mode in (constants.IALLOCATOR_MODE_ALLOC, |
360 | 1d870e0d | Thomas Thrainer | constants.IALLOCATOR_MODE_MULTI_ALLOC): |
361 | da4a52a3 | Thomas Thrainer | (self.inst_uuid, iname) = self.cfg.ExpandInstanceName(self.op.name) |
362 | 1d870e0d | Thomas Thrainer | if iname is not None: |
363 | 1d870e0d | Thomas Thrainer | raise errors.OpPrereqError("Instance '%s' already in the cluster" % |
364 | 1d870e0d | Thomas Thrainer | iname, errors.ECODE_EXISTS) |
365 | 1d870e0d | Thomas Thrainer | for row in self.op.disks: |
366 | 1d870e0d | Thomas Thrainer | if (not isinstance(row, dict) or |
367 | 1d870e0d | Thomas Thrainer | constants.IDISK_SIZE not in row or |
368 | 1d870e0d | Thomas Thrainer | not isinstance(row[constants.IDISK_SIZE], int) or |
369 | 1d870e0d | Thomas Thrainer | constants.IDISK_MODE not in row or |
370 | 1d870e0d | Thomas Thrainer | row[constants.IDISK_MODE] not in constants.DISK_ACCESS_SET): |
371 | 1d870e0d | Thomas Thrainer | raise errors.OpPrereqError("Invalid contents of the 'disks'" |
372 | 1d870e0d | Thomas Thrainer | " parameter", errors.ECODE_INVAL)
|
373 | 1d870e0d | Thomas Thrainer | if self.op.hypervisor is None: |
374 | 1d870e0d | Thomas Thrainer | self.op.hypervisor = self.cfg.GetHypervisorType() |
375 | 1d870e0d | Thomas Thrainer | elif self.op.mode == constants.IALLOCATOR_MODE_RELOC: |
376 | e969a81f | Thomas Thrainer | (self.inst_uuid, self.op.name) = ExpandInstanceUuidAndName(self.cfg, None, |
377 | e969a81f | Thomas Thrainer | self.op.name)
|
378 | 1c3231aa | Thomas Thrainer | self.relocate_from_node_uuids = \
|
379 | 45c044f4 | Ilias Tsitsimpis | list(self.cfg.GetInstanceSecondaryNodes(self.inst_uuid)) |
380 | 1d870e0d | Thomas Thrainer | elif self.op.mode in (constants.IALLOCATOR_MODE_CHG_GROUP, |
381 | 1d870e0d | Thomas Thrainer | constants.IALLOCATOR_MODE_NODE_EVAC): |
382 | 1d870e0d | Thomas Thrainer | if not self.op.instances: |
383 | 1d870e0d | Thomas Thrainer | raise errors.OpPrereqError("Missing instances", errors.ECODE_INVAL) |
384 | da4a52a3 | Thomas Thrainer | (_, self.op.instances) = GetWantedInstances(self, self.op.instances) |
385 | 1d870e0d | Thomas Thrainer | else:
|
386 | 1d870e0d | Thomas Thrainer | raise errors.OpPrereqError("Invalid test allocator mode '%s'" % |
387 | 1d870e0d | Thomas Thrainer | self.op.mode, errors.ECODE_INVAL)
|
388 | 1d870e0d | Thomas Thrainer | |
389 | 1d870e0d | Thomas Thrainer | if self.op.direction == constants.IALLOCATOR_DIR_OUT: |
390 | 1d870e0d | Thomas Thrainer | if self.op.iallocator is None: |
391 | 1d870e0d | Thomas Thrainer | raise errors.OpPrereqError("Missing allocator name", |
392 | 1d870e0d | Thomas Thrainer | errors.ECODE_INVAL) |
393 | 1d870e0d | Thomas Thrainer | |
394 | 1d870e0d | Thomas Thrainer | def Exec(self, feedback_fn): |
395 | 1d870e0d | Thomas Thrainer | """Run the allocator test.
|
396 | 1d870e0d | Thomas Thrainer |
|
397 | 1d870e0d | Thomas Thrainer | """
|
398 | 1d870e0d | Thomas Thrainer | if self.op.mode == constants.IALLOCATOR_MODE_ALLOC: |
399 | 1d870e0d | Thomas Thrainer | req = iallocator.IAReqInstanceAlloc(name=self.op.name,
|
400 | 1d870e0d | Thomas Thrainer | memory=self.op.memory,
|
401 | 1d870e0d | Thomas Thrainer | disks=self.op.disks,
|
402 | 1d870e0d | Thomas Thrainer | disk_template=self.op.disk_template,
|
403 | 1d870e0d | Thomas Thrainer | os=self.op.os,
|
404 | 1d870e0d | Thomas Thrainer | tags=self.op.tags,
|
405 | 1d870e0d | Thomas Thrainer | nics=self.op.nics,
|
406 | 1d870e0d | Thomas Thrainer | vcpus=self.op.vcpus,
|
407 | 1d870e0d | Thomas Thrainer | spindle_use=self.op.spindle_use,
|
408 | 1d870e0d | Thomas Thrainer | hypervisor=self.op.hypervisor,
|
409 | 1d870e0d | Thomas Thrainer | node_whitelist=None)
|
410 | 1d870e0d | Thomas Thrainer | elif self.op.mode == constants.IALLOCATOR_MODE_RELOC: |
411 | 1c3231aa | Thomas Thrainer | req = iallocator.IAReqRelocate( |
412 | da4a52a3 | Thomas Thrainer | inst_uuid=self.inst_uuid,
|
413 | 1c3231aa | Thomas Thrainer | relocate_from_node_uuids=list(self.relocate_from_node_uuids)) |
414 | 1d870e0d | Thomas Thrainer | elif self.op.mode == constants.IALLOCATOR_MODE_CHG_GROUP: |
415 | 1d870e0d | Thomas Thrainer | req = iallocator.IAReqGroupChange(instances=self.op.instances,
|
416 | 1d870e0d | Thomas Thrainer | target_groups=self.op.target_groups)
|
417 | 1d870e0d | Thomas Thrainer | elif self.op.mode == constants.IALLOCATOR_MODE_NODE_EVAC: |
418 | 1d870e0d | Thomas Thrainer | req = iallocator.IAReqNodeEvac(instances=self.op.instances,
|
419 | 1d870e0d | Thomas Thrainer | evac_mode=self.op.evac_mode)
|
420 | 1d870e0d | Thomas Thrainer | elif self.op.mode == constants.IALLOCATOR_MODE_MULTI_ALLOC: |
421 | 1d870e0d | Thomas Thrainer | disk_template = self.op.disk_template
|
422 | 1d870e0d | Thomas Thrainer | insts = [iallocator.IAReqInstanceAlloc(name="%s%s" % (self.op.name, idx), |
423 | 1d870e0d | Thomas Thrainer | memory=self.op.memory,
|
424 | 1d870e0d | Thomas Thrainer | disks=self.op.disks,
|
425 | 1d870e0d | Thomas Thrainer | disk_template=disk_template, |
426 | 1d870e0d | Thomas Thrainer | os=self.op.os,
|
427 | 1d870e0d | Thomas Thrainer | tags=self.op.tags,
|
428 | 1d870e0d | Thomas Thrainer | nics=self.op.nics,
|
429 | 1d870e0d | Thomas Thrainer | vcpus=self.op.vcpus,
|
430 | 1d870e0d | Thomas Thrainer | spindle_use=self.op.spindle_use,
|
431 | e969a81f | Thomas Thrainer | hypervisor=self.op.hypervisor,
|
432 | e969a81f | Thomas Thrainer | node_whitelist=None)
|
433 | 1d870e0d | Thomas Thrainer | for idx in range(self.op.count)] |
434 | 1d870e0d | Thomas Thrainer | req = iallocator.IAReqMultiInstanceAlloc(instances=insts) |
435 | 1d870e0d | Thomas Thrainer | else:
|
436 | 1d870e0d | Thomas Thrainer | raise errors.ProgrammerError("Uncatched mode %s in" |
437 | 1d870e0d | Thomas Thrainer | " LUTestAllocator.Exec", self.op.mode) |
438 | 1d870e0d | Thomas Thrainer | |
439 | 1d870e0d | Thomas Thrainer | ial = iallocator.IAllocator(self.cfg, self.rpc, req) |
440 | 1d870e0d | Thomas Thrainer | if self.op.direction == constants.IALLOCATOR_DIR_IN: |
441 | 1d870e0d | Thomas Thrainer | result = ial.in_text |
442 | 1d870e0d | Thomas Thrainer | else:
|
443 | 1d870e0d | Thomas Thrainer | ial.Run(self.op.iallocator, validate=False) |
444 | 1d870e0d | Thomas Thrainer | result = ial.out_text |
445 | 1d870e0d | Thomas Thrainer | return result |