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