Revision 1d870e0d lib/cmdlib/__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, |
Also available in: Unified diff