Revision 1d870e0d
b/Makefile.am | ||
---|---|---|
312 | 312 |
lib/cmdlib/common.py \ |
313 | 313 |
lib/cmdlib/base.py \ |
314 | 314 |
lib/cmdlib/tags.py \ |
315 |
lib/cmdlib/network.py |
|
315 |
lib/cmdlib/network.py \ |
|
316 |
lib/cmdlib/test.py |
|
316 | 317 |
|
317 | 318 |
hypervisor_PYTHON = \ |
318 | 319 |
lib/hypervisor/__init__.py \ |
b/lib/cmdlib/__init__.py | ||
---|---|---|
34 | 34 |
import logging |
35 | 35 |
import copy |
36 | 36 |
import OpenSSL |
37 |
import socket |
|
38 |
import tempfile |
|
39 |
import shutil |
|
40 | 37 |
import itertools |
41 | 38 |
import operator |
42 | 39 |
|
... | ... | |
66 | 63 |
from ganeti.cmdlib.base import ResultWithJobs, LogicalUnit, NoHooksLU, \ |
67 | 64 |
Tasklet, _QueryBase |
68 | 65 |
from ganeti.cmdlib.common import _ExpandInstanceName, _ExpandItemName, \ |
69 |
_ExpandNodeName, _ShareAll, _CheckNodeGroupInstances |
|
66 |
_ExpandNodeName, _ShareAll, _CheckNodeGroupInstances, _GetWantedNodes, \ |
|
67 |
_GetWantedInstances |
|
70 | 68 |
from ganeti.cmdlib.tags import LUTagsGet, LUTagsSearch, LUTagsSet, LUTagsDel |
71 | 69 |
from ganeti.cmdlib.network import LUNetworkAdd, LUNetworkRemove, \ |
72 | 70 |
LUNetworkSetParams, _NetworkQuery, LUNetworkQuery, LUNetworkConnect, \ |
73 | 71 |
LUNetworkDisconnect |
72 |
from ganeti.cmdlib.test import LUTestDelay, LUTestJqueue, LUTestAllocator |
|
74 | 73 |
|
75 | 74 |
import ganeti.masterd.instance # pylint: disable=W0611 |
76 | 75 |
|
... | ... | |
214 | 213 |
return names[:] |
215 | 214 |
|
216 | 215 |
|
217 |
def _GetWantedNodes(lu, nodes): |
|
218 |
"""Returns list of checked and expanded node names. |
|
219 |
|
|
220 |
@type lu: L{LogicalUnit} |
|
221 |
@param lu: the logical unit on whose behalf we execute |
|
222 |
@type nodes: list |
|
223 |
@param nodes: list of node names or None for all nodes |
|
224 |
@rtype: list |
|
225 |
@return: the list of nodes, sorted |
|
226 |
@raise errors.ProgrammerError: if the nodes parameter is wrong type |
|
227 |
|
|
228 |
""" |
|
229 |
if nodes: |
|
230 |
return [_ExpandNodeName(lu.cfg, name) for name in nodes] |
|
231 |
|
|
232 |
return utils.NiceSort(lu.cfg.GetNodeList()) |
|
233 |
|
|
234 |
|
|
235 |
def _GetWantedInstances(lu, instances): |
|
236 |
"""Returns list of checked and expanded instance names. |
|
237 |
|
|
238 |
@type lu: L{LogicalUnit} |
|
239 |
@param lu: the logical unit on whose behalf we execute |
|
240 |
@type instances: list |
|
241 |
@param instances: list of instance names or None for all instances |
|
242 |
@rtype: list |
|
243 |
@return: the list of instances, sorted |
|
244 |
@raise errors.OpPrereqError: if the instances parameter is wrong type |
|
245 |
@raise errors.OpPrereqError: if any of the passed instances is not found |
|
246 |
|
|
247 |
""" |
|
248 |
if instances: |
|
249 |
wanted = [_ExpandInstanceName(lu.cfg, name) for name in instances] |
|
250 |
else: |
|
251 |
wanted = utils.NiceSort(lu.cfg.GetInstanceList()) |
|
252 |
return wanted |
|
253 |
|
|
254 |
|
|
255 | 216 |
def _GetUpdatedParams(old_params, update_dict, |
256 | 217 |
use_default=True, use_none=False): |
257 | 218 |
"""Return the new version of a parameter dictionary. |
... | ... | |
15309 | 15270 |
return ResultWithJobs(jobs) |
15310 | 15271 |
|
15311 | 15272 |
|
15312 |
class LUTestDelay(NoHooksLU): |
|
15313 |
"""Sleep for a specified amount of time. |
|
15314 |
|
|
15315 |
This LU sleeps on the master and/or nodes for a specified amount of |
|
15316 |
time. |
|
15317 |
|
|
15318 |
""" |
|
15319 |
REQ_BGL = False |
|
15320 |
|
|
15321 |
def ExpandNames(self): |
|
15322 |
"""Expand names and set required locks. |
|
15323 |
|
|
15324 |
This expands the node list, if any. |
|
15325 |
|
|
15326 |
""" |
|
15327 |
self.needed_locks = {} |
|
15328 |
if self.op.on_nodes: |
|
15329 |
# _GetWantedNodes can be used here, but is not always appropriate to use |
|
15330 |
# this way in ExpandNames. Check LogicalUnit.ExpandNames docstring for |
|
15331 |
# more information. |
|
15332 |
self.op.on_nodes = _GetWantedNodes(self, self.op.on_nodes) |
|
15333 |
self.needed_locks[locking.LEVEL_NODE] = self.op.on_nodes |
|
15334 |
|
|
15335 |
def _TestDelay(self): |
|
15336 |
"""Do the actual sleep. |
|
15337 |
|
|
15338 |
""" |
|
15339 |
if self.op.on_master: |
|
15340 |
if not utils.TestDelay(self.op.duration): |
|
15341 |
raise errors.OpExecError("Error during master delay test") |
|
15342 |
if self.op.on_nodes: |
|
15343 |
result = self.rpc.call_test_delay(self.op.on_nodes, self.op.duration) |
|
15344 |
for node, node_result in result.items(): |
|
15345 |
node_result.Raise("Failure during rpc call to node %s" % node) |
|
15346 |
|
|
15347 |
def Exec(self, feedback_fn): |
|
15348 |
"""Execute the test delay opcode, with the wanted repetitions. |
|
15349 |
|
|
15350 |
""" |
|
15351 |
if self.op.repeat == 0: |
|
15352 |
self._TestDelay() |
|
15353 |
else: |
|
15354 |
top_value = self.op.repeat - 1 |
|
15355 |
for i in range(self.op.repeat): |
|
15356 |
self.LogInfo("Test delay iteration %d/%d", i, top_value) |
|
15357 |
self._TestDelay() |
|
15358 |
|
|
15359 |
|
|
15360 | 15273 |
class LURestrictedCommand(NoHooksLU): |
15361 | 15274 |
"""Logical unit for executing restricted commands. |
15362 | 15275 |
|
... | ... | |
15404 | 15317 |
return result |
15405 | 15318 |
|
15406 | 15319 |
|
15407 |
class LUTestJqueue(NoHooksLU): |
|
15408 |
"""Utility LU to test some aspects of the job queue. |
|
15409 |
|
|
15410 |
""" |
|
15411 |
REQ_BGL = False |
|
15412 |
|
|
15413 |
# Must be lower than default timeout for WaitForJobChange to see whether it |
|
15414 |
# notices changed jobs |
|
15415 |
_CLIENT_CONNECT_TIMEOUT = 20.0 |
|
15416 |
_CLIENT_CONFIRM_TIMEOUT = 60.0 |
|
15417 |
|
|
15418 |
@classmethod |
|
15419 |
def _NotifyUsingSocket(cls, cb, errcls): |
|
15420 |
"""Opens a Unix socket and waits for another program to connect. |
|
15421 |
|
|
15422 |
@type cb: callable |
|
15423 |
@param cb: Callback to send socket name to client |
|
15424 |
@type errcls: class |
|
15425 |
@param errcls: Exception class to use for errors |
|
15426 |
|
|
15427 |
""" |
|
15428 |
# Using a temporary directory as there's no easy way to create temporary |
|
15429 |
# sockets without writing a custom loop around tempfile.mktemp and |
|
15430 |
# socket.bind |
|
15431 |
tmpdir = tempfile.mkdtemp() |
|
15432 |
try: |
|
15433 |
tmpsock = utils.PathJoin(tmpdir, "sock") |
|
15434 |
|
|
15435 |
logging.debug("Creating temporary socket at %s", tmpsock) |
|
15436 |
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) |
|
15437 |
try: |
|
15438 |
sock.bind(tmpsock) |
|
15439 |
sock.listen(1) |
|
15440 |
|
|
15441 |
# Send details to client |
|
15442 |
cb(tmpsock) |
|
15443 |
|
|
15444 |
# Wait for client to connect before continuing |
|
15445 |
sock.settimeout(cls._CLIENT_CONNECT_TIMEOUT) |
|
15446 |
try: |
|
15447 |
(conn, _) = sock.accept() |
|
15448 |
except socket.error, err: |
|
15449 |
raise errcls("Client didn't connect in time (%s)" % err) |
|
15450 |
finally: |
|
15451 |
sock.close() |
|
15452 |
finally: |
|
15453 |
# Remove as soon as client is connected |
|
15454 |
shutil.rmtree(tmpdir) |
|
15455 |
|
|
15456 |
# Wait for client to close |
|
15457 |
try: |
|
15458 |
try: |
|
15459 |
# pylint: disable=E1101 |
|
15460 |
# Instance of '_socketobject' has no ... member |
|
15461 |
conn.settimeout(cls._CLIENT_CONFIRM_TIMEOUT) |
|
15462 |
conn.recv(1) |
|
15463 |
except socket.error, err: |
|
15464 |
raise errcls("Client failed to confirm notification (%s)" % err) |
|
15465 |
finally: |
|
15466 |
conn.close() |
|
15467 |
|
|
15468 |
def _SendNotification(self, test, arg, sockname): |
|
15469 |
"""Sends a notification to the client. |
|
15470 |
|
|
15471 |
@type test: string |
|
15472 |
@param test: Test name |
|
15473 |
@param arg: Test argument (depends on test) |
|
15474 |
@type sockname: string |
|
15475 |
@param sockname: Socket path |
|
15476 |
|
|
15477 |
""" |
|
15478 |
self.Log(constants.ELOG_JQUEUE_TEST, (sockname, test, arg)) |
|
15479 |
|
|
15480 |
def _Notify(self, prereq, test, arg): |
|
15481 |
"""Notifies the client of a test. |
|
15482 |
|
|
15483 |
@type prereq: bool |
|
15484 |
@param prereq: Whether this is a prereq-phase test |
|
15485 |
@type test: string |
|
15486 |
@param test: Test name |
|
15487 |
@param arg: Test argument (depends on test) |
|
15488 |
|
|
15489 |
""" |
|
15490 |
if prereq: |
|
15491 |
errcls = errors.OpPrereqError |
|
15492 |
else: |
|
15493 |
errcls = errors.OpExecError |
|
15494 |
|
|
15495 |
return self._NotifyUsingSocket(compat.partial(self._SendNotification, |
|
15496 |
test, arg), |
|
15497 |
errcls) |
|
15498 |
|
|
15499 |
def CheckArguments(self): |
|
15500 |
self.checkargs_calls = getattr(self, "checkargs_calls", 0) + 1 |
|
15501 |
self.expandnames_calls = 0 |
|
15502 |
|
|
15503 |
def ExpandNames(self): |
|
15504 |
checkargs_calls = getattr(self, "checkargs_calls", 0) |
|
15505 |
if checkargs_calls < 1: |
|
15506 |
raise errors.ProgrammerError("CheckArguments was not called") |
|
15507 |
|
|
15508 |
self.expandnames_calls += 1 |
|
15509 |
|
|
15510 |
if self.op.notify_waitlock: |
|
15511 |
self._Notify(True, constants.JQT_EXPANDNAMES, None) |
|
15512 |
|
|
15513 |
self.LogInfo("Expanding names") |
|
15514 |
|
|
15515 |
# Get lock on master node (just to get a lock, not for a particular reason) |
|
15516 |
self.needed_locks = { |
|
15517 |
locking.LEVEL_NODE: self.cfg.GetMasterNode(), |
|
15518 |
} |
|
15519 |
|
|
15520 |
def Exec(self, feedback_fn): |
|
15521 |
if self.expandnames_calls < 1: |
|
15522 |
raise errors.ProgrammerError("ExpandNames was not called") |
|
15523 |
|
|
15524 |
if self.op.notify_exec: |
|
15525 |
self._Notify(False, constants.JQT_EXEC, None) |
|
15526 |
|
|
15527 |
self.LogInfo("Executing") |
|
15528 |
|
|
15529 |
if self.op.log_messages: |
|
15530 |
self._Notify(False, constants.JQT_STARTMSG, len(self.op.log_messages)) |
|
15531 |
for idx, msg in enumerate(self.op.log_messages): |
|
15532 |
self.LogInfo("Sending log message %s", idx + 1) |
|
15533 |
feedback_fn(constants.JQT_MSGPREFIX + msg) |
|
15534 |
# Report how many test messages have been sent |
|
15535 |
self._Notify(False, constants.JQT_LOGMSG, idx + 1) |
|
15536 |
|
|
15537 |
if self.op.fail: |
|
15538 |
raise errors.OpExecError("Opcode failure was requested") |
|
15539 |
|
|
15540 |
return True |
|
15541 |
|
|
15542 |
|
|
15543 |
class LUTestAllocator(NoHooksLU): |
|
15544 |
"""Run allocator tests. |
|
15545 |
|
|
15546 |
This LU runs the allocator tests |
|
15547 |
|
|
15548 |
""" |
|
15549 |
def CheckPrereq(self): |
|
15550 |
"""Check prerequisites. |
|
15551 |
|
|
15552 |
This checks the opcode parameters depending on the director and mode test. |
|
15553 |
|
|
15554 |
""" |
|
15555 |
if self.op.mode in (constants.IALLOCATOR_MODE_ALLOC, |
|
15556 |
constants.IALLOCATOR_MODE_MULTI_ALLOC): |
|
15557 |
for attr in ["memory", "disks", "disk_template", |
|
15558 |
"os", "tags", "nics", "vcpus"]: |
|
15559 |
if not hasattr(self.op, attr): |
|
15560 |
raise errors.OpPrereqError("Missing attribute '%s' on opcode input" % |
|
15561 |
attr, errors.ECODE_INVAL) |
|
15562 |
iname = self.cfg.ExpandInstanceName(self.op.name) |
|
15563 |
if iname is not None: |
|
15564 |
raise errors.OpPrereqError("Instance '%s' already in the cluster" % |
|
15565 |
iname, errors.ECODE_EXISTS) |
|
15566 |
if not isinstance(self.op.nics, list): |
|
15567 |
raise errors.OpPrereqError("Invalid parameter 'nics'", |
|
15568 |
errors.ECODE_INVAL) |
|
15569 |
if not isinstance(self.op.disks, list): |
|
15570 |
raise errors.OpPrereqError("Invalid parameter 'disks'", |
|
15571 |
errors.ECODE_INVAL) |
|
15572 |
for row in self.op.disks: |
|
15573 |
if (not isinstance(row, dict) or |
|
15574 |
constants.IDISK_SIZE not in row or |
|
15575 |
not isinstance(row[constants.IDISK_SIZE], int) or |
|
15576 |
constants.IDISK_MODE not in row or |
|
15577 |
row[constants.IDISK_MODE] not in constants.DISK_ACCESS_SET): |
|
15578 |
raise errors.OpPrereqError("Invalid contents of the 'disks'" |
|
15579 |
" parameter", errors.ECODE_INVAL) |
|
15580 |
if self.op.hypervisor is None: |
|
15581 |
self.op.hypervisor = self.cfg.GetHypervisorType() |
|
15582 |
elif self.op.mode == constants.IALLOCATOR_MODE_RELOC: |
|
15583 |
fname = _ExpandInstanceName(self.cfg, self.op.name) |
|
15584 |
self.op.name = fname |
|
15585 |
self.relocate_from = \ |
|
15586 |
list(self.cfg.GetInstanceInfo(fname).secondary_nodes) |
|
15587 |
elif self.op.mode in (constants.IALLOCATOR_MODE_CHG_GROUP, |
|
15588 |
constants.IALLOCATOR_MODE_NODE_EVAC): |
|
15589 |
if not self.op.instances: |
|
15590 |
raise errors.OpPrereqError("Missing instances", errors.ECODE_INVAL) |
|
15591 |
self.op.instances = _GetWantedInstances(self, self.op.instances) |
|
15592 |
else: |
|
15593 |
raise errors.OpPrereqError("Invalid test allocator mode '%s'" % |
|
15594 |
self.op.mode, errors.ECODE_INVAL) |
|
15595 |
|
|
15596 |
if self.op.direction == constants.IALLOCATOR_DIR_OUT: |
|
15597 |
if self.op.iallocator is None: |
|
15598 |
raise errors.OpPrereqError("Missing allocator name", |
|
15599 |
errors.ECODE_INVAL) |
|
15600 |
elif self.op.direction != constants.IALLOCATOR_DIR_IN: |
|
15601 |
raise errors.OpPrereqError("Wrong allocator test '%s'" % |
|
15602 |
self.op.direction, errors.ECODE_INVAL) |
|
15603 |
|
|
15604 |
def Exec(self, feedback_fn): |
|
15605 |
"""Run the allocator test. |
|
15606 |
|
|
15607 |
""" |
|
15608 |
if self.op.mode == constants.IALLOCATOR_MODE_ALLOC: |
|
15609 |
req = iallocator.IAReqInstanceAlloc(name=self.op.name, |
|
15610 |
memory=self.op.memory, |
|
15611 |
disks=self.op.disks, |
|
15612 |
disk_template=self.op.disk_template, |
|
15613 |
os=self.op.os, |
|
15614 |
tags=self.op.tags, |
|
15615 |
nics=self.op.nics, |
|
15616 |
vcpus=self.op.vcpus, |
|
15617 |
spindle_use=self.op.spindle_use, |
|
15618 |
hypervisor=self.op.hypervisor, |
|
15619 |
node_whitelist=None) |
|
15620 |
elif self.op.mode == constants.IALLOCATOR_MODE_RELOC: |
|
15621 |
req = iallocator.IAReqRelocate(name=self.op.name, |
|
15622 |
relocate_from=list(self.relocate_from)) |
|
15623 |
elif self.op.mode == constants.IALLOCATOR_MODE_CHG_GROUP: |
|
15624 |
req = iallocator.IAReqGroupChange(instances=self.op.instances, |
|
15625 |
target_groups=self.op.target_groups) |
|
15626 |
elif self.op.mode == constants.IALLOCATOR_MODE_NODE_EVAC: |
|
15627 |
req = iallocator.IAReqNodeEvac(instances=self.op.instances, |
|
15628 |
evac_mode=self.op.evac_mode) |
|
15629 |
elif self.op.mode == constants.IALLOCATOR_MODE_MULTI_ALLOC: |
|
15630 |
disk_template = self.op.disk_template |
|
15631 |
insts = [iallocator.IAReqInstanceAlloc(name="%s%s" % (self.op.name, idx), |
|
15632 |
memory=self.op.memory, |
|
15633 |
disks=self.op.disks, |
|
15634 |
disk_template=disk_template, |
|
15635 |
os=self.op.os, |
|
15636 |
tags=self.op.tags, |
|
15637 |
nics=self.op.nics, |
|
15638 |
vcpus=self.op.vcpus, |
|
15639 |
spindle_use=self.op.spindle_use, |
|
15640 |
hypervisor=self.op.hypervisor) |
|
15641 |
for idx in range(self.op.count)] |
|
15642 |
req = iallocator.IAReqMultiInstanceAlloc(instances=insts) |
|
15643 |
else: |
|
15644 |
raise errors.ProgrammerError("Uncatched mode %s in" |
|
15645 |
" LUTestAllocator.Exec", self.op.mode) |
|
15646 |
|
|
15647 |
ial = iallocator.IAllocator(self.cfg, self.rpc, req) |
|
15648 |
if self.op.direction == constants.IALLOCATOR_DIR_IN: |
|
15649 |
result = ial.in_text |
|
15650 |
else: |
|
15651 |
ial.Run(self.op.iallocator, validate=False) |
|
15652 |
result = ial.out_text |
|
15653 |
return result |
|
15654 |
|
|
15655 |
|
|
15656 | 15320 |
#: Query type implementations |
15657 | 15321 |
_QUERY_IMPL = { |
15658 | 15322 |
constants.QR_CLUSTER: _ClusterQuery, |
b/lib/cmdlib/common.py | ||
---|---|---|
82 | 82 |
errors.ECODE_STATE) |
83 | 83 |
|
84 | 84 |
return wanted_instances |
85 |
|
|
86 |
|
|
87 |
def _GetWantedNodes(lu, nodes): |
|
88 |
"""Returns list of checked and expanded node names. |
|
89 |
|
|
90 |
@type lu: L{LogicalUnit} |
|
91 |
@param lu: the logical unit on whose behalf we execute |
|
92 |
@type nodes: list |
|
93 |
@param nodes: list of node names or None for all nodes |
|
94 |
@rtype: list |
|
95 |
@return: the list of nodes, sorted |
|
96 |
@raise errors.ProgrammerError: if the nodes parameter is wrong type |
|
97 |
|
|
98 |
""" |
|
99 |
if nodes: |
|
100 |
return [_ExpandNodeName(lu.cfg, name) for name in nodes] |
|
101 |
|
|
102 |
return utils.NiceSort(lu.cfg.GetNodeList()) |
|
103 |
|
|
104 |
|
|
105 |
def _GetWantedInstances(lu, instances): |
|
106 |
"""Returns list of checked and expanded instance names. |
|
107 |
|
|
108 |
@type lu: L{LogicalUnit} |
|
109 |
@param lu: the logical unit on whose behalf we execute |
|
110 |
@type instances: list |
|
111 |
@param instances: list of instance names or None for all instances |
|
112 |
@rtype: list |
|
113 |
@return: the list of instances, sorted |
|
114 |
@raise errors.OpPrereqError: if the instances parameter is wrong type |
|
115 |
@raise errors.OpPrereqError: if any of the passed instances is not found |
|
116 |
|
|
117 |
""" |
|
118 |
if instances: |
|
119 |
wanted = [_ExpandInstanceName(lu.cfg, name) for name in instances] |
|
120 |
else: |
|
121 |
wanted = utils.NiceSort(lu.cfg.GetInstanceList()) |
|
122 |
return wanted |
b/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 _ExpandInstanceName, _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_nodes = _GetWantedNodes(self, self.op.on_nodes) |
|
61 |
self.needed_locks[locking.LEVEL_NODE] = self.op.on_nodes |
|
62 |
|
|
63 |
def _TestDelay(self): |
|
64 |
"""Do the actual sleep. |
|
65 |
|
|
66 |
""" |
|
67 |
if self.op.on_master: |
|
68 |
if not utils.TestDelay(self.op.duration): |
|
69 |
raise errors.OpExecError("Error during master delay test") |
|
70 |
if self.op.on_nodes: |
|
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) |
|
74 |
|
|
75 |
def Exec(self, feedback_fn): |
|
76 |
"""Execute the test delay opcode, with the wanted repetitions. |
|
77 |
|
|
78 |
""" |
|
79 |
if self.op.repeat == 0: |
|
80 |
self._TestDelay() |
|
81 |
else: |
|
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) |
|
85 |
self._TestDelay() |
|
86 |
|
|
87 |
|
|
88 |
class LUTestJqueue(NoHooksLU): |
|
89 |
"""Utility LU to test some aspects of the job queue. |
|
90 |
|
|
91 |
""" |
|
92 |
REQ_BGL = False |
|
93 |
|
|
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 |
|
98 |
|
|
99 |
@classmethod |
|
100 |
def _NotifyUsingSocket(cls, cb, errcls): |
|
101 |
"""Opens a Unix socket and waits for another program to connect. |
|
102 |
|
|
103 |
@type cb: callable |
|
104 |
@param cb: Callback to send socket name to client |
|
105 |
@type errcls: class |
|
106 |
@param errcls: Exception class to use for errors |
|
107 |
|
|
108 |
""" |
|
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 |
|
111 |
# socket.bind |
|
112 |
tmpdir = tempfile.mkdtemp() |
|
113 |
try: |
|
114 |
tmpsock = utils.PathJoin(tmpdir, "sock") |
|
115 |
|
|
116 |
logging.debug("Creating temporary socket at %s", tmpsock) |
|
117 |
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) |
|
118 |
try: |
|
119 |
sock.bind(tmpsock) |
|
120 |
sock.listen(1) |
|
121 |
|
|
122 |
# Send details to client |
|
123 |
cb(tmpsock) |
|
124 |
|
|
125 |
# Wait for client to connect before continuing |
|
126 |
sock.settimeout(cls._CLIENT_CONNECT_TIMEOUT) |
|
127 |
try: |
|
128 |
(conn, _) = sock.accept() |
|
129 |
except socket.error, err: |
|
130 |
raise errcls("Client didn't connect in time (%s)" % err) |
|
131 |
finally: |
|
132 |
sock.close() |
|
133 |
finally: |
|
134 |
# Remove as soon as client is connected |
|
135 |
shutil.rmtree(tmpdir) |
|
136 |
|
|
137 |
# Wait for client to close |
|
138 |
try: |
|
139 |
try: |
|
140 |
# pylint: disable=E1101 |
|
141 |
# Instance of '_socketobject' has no ... member |
|
142 |
conn.settimeout(cls._CLIENT_CONFIRM_TIMEOUT) |
|
143 |
conn.recv(1) |
|
144 |
except socket.error, err: |
|
145 |
raise errcls("Client failed to confirm notification (%s)" % err) |
|
146 |
finally: |
|
147 |
conn.close() |
|
148 |
|
|
149 |
def _SendNotification(self, test, arg, sockname): |
|
150 |
"""Sends a notification to the client. |
|
151 |
|
|
152 |
@type test: string |
|
153 |
@param test: Test name |
|
154 |
@param arg: Test argument (depends on test) |
|
155 |
@type sockname: string |
|
156 |
@param sockname: Socket path |
|
157 |
|
|
158 |
""" |
|
159 |
self.Log(constants.ELOG_JQUEUE_TEST, (sockname, test, arg)) |
|
160 |
|
|
161 |
def _Notify(self, prereq, test, arg): |
|
162 |
"""Notifies the client of a test. |
|
163 |
|
|
164 |
@type prereq: bool |
|
165 |
@param prereq: Whether this is a prereq-phase test |
|
166 |
@type test: string |
|
167 |
@param test: Test name |
|
168 |
@param arg: Test argument (depends on test) |
|
169 |
|
|
170 |
""" |
|
171 |
if prereq: |
|
172 |
errcls = errors.OpPrereqError |
|
173 |
else: |
|
174 |
errcls = errors.OpExecError |
|
175 |
|
|
176 |
return self._NotifyUsingSocket(compat.partial(self._SendNotification, |
|
177 |
test, arg), |
|
178 |
errcls) |
|
179 |
|
|
180 |
def CheckArguments(self): |
|
181 |
self.checkargs_calls = getattr(self, "checkargs_calls", 0) + 1 |
|
182 |
self.expandnames_calls = 0 |
|
183 |
|
|
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") |
|
188 |
|
|
189 |
self.expandnames_calls += 1 |
|
190 |
|
|
191 |
if self.op.notify_waitlock: |
|
192 |
self._Notify(True, constants.JQT_EXPANDNAMES, None) |
|
193 |
|
|
194 |
self.LogInfo("Expanding names") |
|
195 |
|
|
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(), |
|
199 |
} |
|
200 |
|
|
201 |
def Exec(self, feedback_fn): |
|
202 |
if self.expandnames_calls < 1: |
|
203 |
raise errors.ProgrammerError("ExpandNames was not called") |
|
204 |
|
|
205 |
if self.op.notify_exec: |
|
206 |
self._Notify(False, constants.JQT_EXEC, None) |
|
207 |
|
|
208 |
self.LogInfo("Executing") |
|
209 |
|
|
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) |
|
217 |
|
|
218 |
if self.op.fail: |
|
219 |
raise errors.OpExecError("Opcode failure was requested") |
|
220 |
|
|
221 |
return True |
|
222 |
|
|
223 |
|
|
224 |
class LUTestAllocator(NoHooksLU): |
|
225 |
"""Run allocator tests. |
|
226 |
|
|
227 |
This LU runs the allocator tests |
|
228 |
|
|
229 |
""" |
|
230 |
def CheckPrereq(self): |
|
231 |
"""Check prerequisites. |
|
232 |
|
|
233 |
This checks the opcode parameters depending on the director and mode test. |
|
234 |
|
|
235 |
""" |
|
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'", |
|
249 |
errors.ECODE_INVAL) |
|
250 |
if not isinstance(self.op.disks, list): |
|
251 |
raise errors.OpPrereqError("Invalid parameter 'disks'", |
|
252 |
errors.ECODE_INVAL) |
|
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) |
|
265 |
self.op.name = fname |
|
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) |
|
273 |
else: |
|
274 |
raise errors.OpPrereqError("Invalid test allocator mode '%s'" % |
|
275 |
self.op.mode, errors.ECODE_INVAL) |
|
276 |
|
|
277 |
if self.op.direction == constants.IALLOCATOR_DIR_OUT: |
|
278 |
if self.op.iallocator is None: |
|
279 |
raise errors.OpPrereqError("Missing allocator name", |
|
280 |
errors.ECODE_INVAL) |
|
281 |
elif self.op.direction != constants.IALLOCATOR_DIR_IN: |
|
282 |
raise errors.OpPrereqError("Wrong allocator test '%s'" % |
|
283 |
self.op.direction, errors.ECODE_INVAL) |
|
284 |
|
|
285 |
def Exec(self, feedback_fn): |
|
286 |
"""Run the allocator test. |
|
287 |
|
|
288 |
""" |
|
289 |
if self.op.mode == constants.IALLOCATOR_MODE_ALLOC: |
|
290 |
req = iallocator.IAReqInstanceAlloc(name=self.op.name, |
|
291 |
memory=self.op.memory, |
|
292 |
disks=self.op.disks, |
|
293 |
disk_template=self.op.disk_template, |
|
294 |
os=self.op.os, |
|
295 |
tags=self.op.tags, |
|
296 |
nics=self.op.nics, |
|
297 |
vcpus=self.op.vcpus, |
|
298 |
spindle_use=self.op.spindle_use, |
|
299 |
hypervisor=self.op.hypervisor, |
|
300 |
node_whitelist=None) |
|
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, |
|
314 |
disks=self.op.disks, |
|
315 |
disk_template=disk_template, |
|
316 |
os=self.op.os, |
|
317 |
tags=self.op.tags, |
|
318 |
nics=self.op.nics, |
|
319 |
vcpus=self.op.vcpus, |
|
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) |
|
324 |
else: |
|
325 |
raise errors.ProgrammerError("Uncatched mode %s in" |
|
326 |
" LUTestAllocator.Exec", self.op.mode) |
|
327 |
|
|
328 |
ial = iallocator.IAllocator(self.cfg, self.rpc, req) |
|
329 |
if self.op.direction == constants.IALLOCATOR_DIR_IN: |
|
330 |
result = ial.in_text |
|
331 |
else: |
|
332 |
ial.Run(self.op.iallocator, validate=False) |
|
333 |
result = ial.out_text |
|
334 |
return result |
Also available in: Unified diff