Revision 763ad5be lib/cmdlib/instance_utils.py
b/lib/cmdlib/instance_utils.py | ||
---|---|---|
31 | 31 |
from ganeti import objects |
32 | 32 |
from ganeti import pathutils |
33 | 33 |
from ganeti import utils |
34 |
|
|
35 |
from ganeti.cmdlib.common import _AnnotateDiskParams
|
|
34 |
from ganeti.cmdlib.common import _AnnotateDiskParams, \ |
|
35 |
_ComputeIPolicyInstanceViolation
|
|
36 | 36 |
|
37 | 37 |
|
38 | 38 |
def _BuildInstanceHookEnv(name, primary_node, secondary_nodes, os_type, status, |
... | ... | |
202 | 202 |
errors.ECODE_STATE) |
203 | 203 |
|
204 | 204 |
|
205 |
def _StartInstanceDisks(lu, instance, force): |
|
206 |
"""Start the disks of an instance. |
|
207 |
|
|
208 |
""" |
|
209 |
disks_ok, _ = _AssembleInstanceDisks(lu, instance, |
|
210 |
ignore_secondaries=force) |
|
211 |
if not disks_ok: |
|
212 |
_ShutdownInstanceDisks(lu, instance) |
|
213 |
if force is not None and not force: |
|
214 |
lu.LogWarning("", |
|
215 |
hint=("If the message above refers to a secondary node," |
|
216 |
" you can retry the operation using '--force'")) |
|
217 |
raise errors.OpExecError("Disk consistency error") |
|
218 |
|
|
219 |
|
|
220 |
def _ShutdownInstanceDisks(lu, instance, disks=None, ignore_primary=False): |
|
221 |
"""Shutdown block devices of an instance. |
|
205 |
def _CheckNodeVmCapable(lu, node): |
|
206 |
"""Ensure that a given node is vm capable. |
|
222 | 207 |
|
223 |
This does the shutdown on all nodes of the instance. |
|
224 |
|
|
225 |
If the ignore_primary is false, errors on the primary node are |
|
226 |
ignored. |
|
208 |
@param lu: the LU on behalf of which we make the check |
|
209 |
@param node: the node to check |
|
210 |
@raise errors.OpPrereqError: if the node is not vm capable |
|
227 | 211 |
|
228 | 212 |
""" |
229 |
all_result = True |
|
230 |
disks = _ExpandCheckDisks(instance, disks) |
|
231 |
|
|
232 |
for disk in disks: |
|
233 |
for node, top_disk in disk.ComputeNodeTree(instance.primary_node): |
|
234 |
lu.cfg.SetDiskID(top_disk, node) |
|
235 |
result = lu.rpc.call_blockdev_shutdown(node, (top_disk, instance)) |
|
236 |
msg = result.fail_msg |
|
237 |
if msg: |
|
238 |
lu.LogWarning("Could not shutdown block device %s on node %s: %s", |
|
239 |
disk.iv_name, node, msg) |
|
240 |
if ((node == instance.primary_node and not ignore_primary) or |
|
241 |
(node != instance.primary_node and not result.offline)): |
|
242 |
all_result = False |
|
243 |
return all_result |
|
213 |
if not lu.cfg.GetNodeInfo(node).vm_capable: |
|
214 |
raise errors.OpPrereqError("Can't use non-vm_capable node %s" % node, |
|
215 |
errors.ECODE_STATE) |
|
244 | 216 |
|
245 | 217 |
|
246 | 218 |
def _RemoveInstance(lu, feedback_fn, instance, ignore_failures): |
... | ... | |
265 | 237 |
lu.remove_locks[locking.LEVEL_INSTANCE] = instance.name |
266 | 238 |
|
267 | 239 |
|
268 |
def _AssembleInstanceDisks(lu, instance, disks=None, ignore_secondaries=False, |
|
269 |
ignore_size=False): |
|
270 |
"""Prepare the block devices for an instance. |
|
271 |
|
|
272 |
This sets up the block devices on all nodes. |
|
273 |
|
|
274 |
@type lu: L{LogicalUnit} |
|
275 |
@param lu: the logical unit on whose behalf we execute |
|
276 |
@type instance: L{objects.Instance} |
|
277 |
@param instance: the instance for whose disks we assemble |
|
278 |
@type disks: list of L{objects.Disk} or None |
|
279 |
@param disks: which disks to assemble (or all, if None) |
|
280 |
@type ignore_secondaries: boolean |
|
281 |
@param ignore_secondaries: if true, errors on secondary nodes |
|
282 |
won't result in an error return from the function |
|
283 |
@type ignore_size: boolean |
|
284 |
@param ignore_size: if true, the current known size of the disk |
|
285 |
will not be used during the disk activation, useful for cases |
|
286 |
when the size is wrong |
|
287 |
@return: False if the operation failed, otherwise a list of |
|
288 |
(host, instance_visible_name, node_visible_name) |
|
289 |
with the mapping from node devices to instance devices |
|
290 |
|
|
291 |
""" |
|
292 |
device_info = [] |
|
293 |
disks_ok = True |
|
294 |
iname = instance.name |
|
295 |
disks = _ExpandCheckDisks(instance, disks) |
|
296 |
|
|
297 |
# With the two passes mechanism we try to reduce the window of |
|
298 |
# opportunity for the race condition of switching DRBD to primary |
|
299 |
# before handshaking occured, but we do not eliminate it |
|
300 |
|
|
301 |
# The proper fix would be to wait (with some limits) until the |
|
302 |
# connection has been made and drbd transitions from WFConnection |
|
303 |
# into any other network-connected state (Connected, SyncTarget, |
|
304 |
# SyncSource, etc.) |
|
305 |
|
|
306 |
# 1st pass, assemble on all nodes in secondary mode |
|
307 |
for idx, inst_disk in enumerate(disks): |
|
308 |
for node, node_disk in inst_disk.ComputeNodeTree(instance.primary_node): |
|
309 |
if ignore_size: |
|
310 |
node_disk = node_disk.Copy() |
|
311 |
node_disk.UnsetSize() |
|
312 |
lu.cfg.SetDiskID(node_disk, node) |
|
313 |
result = lu.rpc.call_blockdev_assemble(node, (node_disk, instance), iname, |
|
314 |
False, idx) |
|
315 |
msg = result.fail_msg |
|
316 |
if msg: |
|
317 |
is_offline_secondary = (node in instance.secondary_nodes and |
|
318 |
result.offline) |
|
319 |
lu.LogWarning("Could not prepare block device %s on node %s" |
|
320 |
" (is_primary=False, pass=1): %s", |
|
321 |
inst_disk.iv_name, node, msg) |
|
322 |
if not (ignore_secondaries or is_offline_secondary): |
|
323 |
disks_ok = False |
|
324 |
|
|
325 |
# FIXME: race condition on drbd migration to primary |
|
326 |
|
|
327 |
# 2nd pass, do only the primary node |
|
328 |
for idx, inst_disk in enumerate(disks): |
|
329 |
dev_path = None |
|
330 |
|
|
331 |
for node, node_disk in inst_disk.ComputeNodeTree(instance.primary_node): |
|
332 |
if node != instance.primary_node: |
|
333 |
continue |
|
334 |
if ignore_size: |
|
335 |
node_disk = node_disk.Copy() |
|
336 |
node_disk.UnsetSize() |
|
337 |
lu.cfg.SetDiskID(node_disk, node) |
|
338 |
result = lu.rpc.call_blockdev_assemble(node, (node_disk, instance), iname, |
|
339 |
True, idx) |
|
340 |
msg = result.fail_msg |
|
341 |
if msg: |
|
342 |
lu.LogWarning("Could not prepare block device %s on node %s" |
|
343 |
" (is_primary=True, pass=2): %s", |
|
344 |
inst_disk.iv_name, node, msg) |
|
345 |
disks_ok = False |
|
346 |
else: |
|
347 |
dev_path = result.payload |
|
348 |
|
|
349 |
device_info.append((instance.primary_node, inst_disk.iv_name, dev_path)) |
|
350 |
|
|
351 |
# leave the disks configured for the primary node |
|
352 |
# this is a workaround that would be fixed better by |
|
353 |
# improving the logical/physical id handling |
|
354 |
for disk in disks: |
|
355 |
lu.cfg.SetDiskID(disk, instance.primary_node) |
|
356 |
|
|
357 |
return disks_ok, device_info |
|
358 |
|
|
359 |
|
|
360 | 240 |
def _RemoveDisks(lu, instance, target_node=None, ignore_failures=False): |
361 | 241 |
"""Remove all disks for an instance. |
362 | 242 |
|
... | ... | |
416 | 296 |
return all_result |
417 | 297 |
|
418 | 298 |
|
419 |
def _ExpandCheckDisks(instance, disks): |
|
420 |
"""Return the instance disks selected by the disks list |
|
421 |
|
|
422 |
@type disks: list of L{objects.Disk} or None |
|
423 |
@param disks: selected disks |
|
424 |
@rtype: list of L{objects.Disk} |
|
425 |
@return: selected instance disks to act on |
|
426 |
|
|
427 |
""" |
|
428 |
if disks is None: |
|
429 |
return instance.disks |
|
430 |
else: |
|
431 |
if not set(disks).issubset(instance.disks): |
|
432 |
raise errors.ProgrammerError("Can only act on disks belonging to the" |
|
433 |
" target instance") |
|
434 |
return disks |
|
435 |
|
|
436 |
|
|
437 | 299 |
def _NICToTuple(lu, nic): |
438 | 300 |
"""Build a tupple of nic information. |
439 | 301 |
|
... | ... | |
470 | 332 |
for nic in nics: |
471 | 333 |
hooks_nics.append(_NICToTuple(lu, nic)) |
472 | 334 |
return hooks_nics |
335 |
|
|
336 |
|
|
337 |
def _CopyLockList(names): |
|
338 |
"""Makes a copy of a list of lock names. |
|
339 |
|
|
340 |
Handles L{locking.ALL_SET} correctly. |
|
341 |
|
|
342 |
""" |
|
343 |
if names == locking.ALL_SET: |
|
344 |
return locking.ALL_SET |
|
345 |
else: |
|
346 |
return names[:] |
|
347 |
|
|
348 |
|
|
349 |
def _ReleaseLocks(lu, level, names=None, keep=None): |
|
350 |
"""Releases locks owned by an LU. |
|
351 |
|
|
352 |
@type lu: L{LogicalUnit} |
|
353 |
@param level: Lock level |
|
354 |
@type names: list or None |
|
355 |
@param names: Names of locks to release |
|
356 |
@type keep: list or None |
|
357 |
@param keep: Names of locks to retain |
|
358 |
|
|
359 |
""" |
|
360 |
assert not (keep is not None and names is not None), \ |
|
361 |
"Only one of the 'names' and the 'keep' parameters can be given" |
|
362 |
|
|
363 |
if names is not None: |
|
364 |
should_release = names.__contains__ |
|
365 |
elif keep: |
|
366 |
should_release = lambda name: name not in keep |
|
367 |
else: |
|
368 |
should_release = None |
|
369 |
|
|
370 |
owned = lu.owned_locks(level) |
|
371 |
if not owned: |
|
372 |
# Not owning any lock at this level, do nothing |
|
373 |
pass |
|
374 |
|
|
375 |
elif should_release: |
|
376 |
retain = [] |
|
377 |
release = [] |
|
378 |
|
|
379 |
# Determine which locks to release |
|
380 |
for name in owned: |
|
381 |
if should_release(name): |
|
382 |
release.append(name) |
|
383 |
else: |
|
384 |
retain.append(name) |
|
385 |
|
|
386 |
assert len(lu.owned_locks(level)) == (len(retain) + len(release)) |
|
387 |
|
|
388 |
# Release just some locks |
|
389 |
lu.glm.release(level, names=release) |
|
390 |
|
|
391 |
assert frozenset(lu.owned_locks(level)) == frozenset(retain) |
|
392 |
else: |
|
393 |
# Release everything |
|
394 |
lu.glm.release(level) |
|
395 |
|
|
396 |
assert not lu.glm.is_owned(level), "No locks should be owned" |
|
397 |
|
|
398 |
|
|
399 |
def _ComputeIPolicyNodeViolation(ipolicy, instance, current_group, |
|
400 |
target_group, cfg, |
|
401 |
_compute_fn=_ComputeIPolicyInstanceViolation): |
|
402 |
"""Compute if instance meets the specs of the new target group. |
|
403 |
|
|
404 |
@param ipolicy: The ipolicy to verify |
|
405 |
@param instance: The instance object to verify |
|
406 |
@param current_group: The current group of the instance |
|
407 |
@param target_group: The new group of the instance |
|
408 |
@type cfg: L{config.ConfigWriter} |
|
409 |
@param cfg: Cluster configuration |
|
410 |
@param _compute_fn: The function to verify ipolicy (unittest only) |
|
411 |
@see: L{ganeti.cmdlib.common._ComputeIPolicySpecViolation} |
|
412 |
|
|
413 |
""" |
|
414 |
if current_group == target_group: |
|
415 |
return [] |
|
416 |
else: |
|
417 |
return _compute_fn(ipolicy, instance, cfg) |
|
418 |
|
|
419 |
|
|
420 |
def _CheckTargetNodeIPolicy(lu, ipolicy, instance, node, cfg, ignore=False, |
|
421 |
_compute_fn=_ComputeIPolicyNodeViolation): |
|
422 |
"""Checks that the target node is correct in terms of instance policy. |
|
423 |
|
|
424 |
@param ipolicy: The ipolicy to verify |
|
425 |
@param instance: The instance object to verify |
|
426 |
@param node: The new node to relocate |
|
427 |
@type cfg: L{config.ConfigWriter} |
|
428 |
@param cfg: Cluster configuration |
|
429 |
@param ignore: Ignore violations of the ipolicy |
|
430 |
@param _compute_fn: The function to verify ipolicy (unittest only) |
|
431 |
@see: L{ganeti.cmdlib.common._ComputeIPolicySpecViolation} |
|
432 |
|
|
433 |
""" |
|
434 |
primary_node = lu.cfg.GetNodeInfo(instance.primary_node) |
|
435 |
res = _compute_fn(ipolicy, instance, primary_node.group, node.group, cfg) |
|
436 |
|
|
437 |
if res: |
|
438 |
msg = ("Instance does not meet target node group's (%s) instance" |
|
439 |
" policy: %s") % (node.group, utils.CommaJoin(res)) |
|
440 |
if ignore: |
|
441 |
lu.LogWarning(msg) |
|
442 |
else: |
|
443 |
raise errors.OpPrereqError(msg, errors.ECODE_INVAL) |
|
444 |
|
|
445 |
|
|
446 |
def _GetInstanceInfoText(instance): |
|
447 |
"""Compute that text that should be added to the disk's metadata. |
|
448 |
|
|
449 |
""" |
|
450 |
return "originstname+%s" % instance.name |
Also available in: Unified diff