Revision 4a89c54a lib/config.py

b/lib/config.py
227 227

  
228 228
    return result
229 229

  
230
  @locking.ssynchronized(_config_lock, shared=1)
231
  def VerifyConfig(self):
230
  def _UnlockedVerifyConfig(self):
232 231
    """Verify function.
233 232

  
233
    @rtype: list
234
    @return: a list of error messages; a non-empty list signifies
235
        configuration errors
236

  
234 237
    """
235 238
    result = []
236 239
    seen_macs = []
......
290 293
    if not data.nodes[data.cluster.master_node].master_candidate:
291 294
      result.append("Master node is not a master candidate")
292 295

  
296
    # master candidate checks
293 297
    mc_now, mc_max = self._UnlockedGetMasterCandidateStats()
294 298
    if mc_now < mc_max:
295 299
      result.append("Not enough master candidates: actual %d, target %d" %
296 300
                    (mc_now, mc_max))
297 301

  
302
    # drbd minors check
303
    d_map, duplicates = self._UnlockedComputeDRBDMap()
304
    for node, minor, instance_a, instance_b in duplicates:
305
      result.append("DRBD minor %d on node %s is assigned twice to instances"
306
                    " %s and %s" % (minor, node, instance_a, instance_b))
307

  
298 308
    return result
299 309

  
310
  @locking.ssynchronized(_config_lock, shared=1)
311
  def VerifyConfig(self):
312
    """Verify function.
313

  
314
    This is just a wrapper over L{_UnlockedVerifyConfig}.
315

  
316
    @rtype: list
317
    @return: a list of error messages; a non-empty list signifies
318
        configuration errors
319

  
320
    """
321
    return self._UnlockedVerifyConfig()
322

  
300 323
  def _UnlockedSetDiskID(self, disk, node_name):
301 324
    """Convert the unique ID to the ID needed on the target nodes.
302 325

  
......
392 415
  def _UnlockedComputeDRBDMap(self):
393 416
    """Compute the used DRBD minor/nodes.
394 417

  
418
    @rtype: (dict, list)
395 419
    @return: dictionary of node_name: dict of minor: instance_name;
396 420
        the returned dict will have all the nodes in it (even if with
397
        an empty list).
421
        an empty list), and a list of duplicates; if the duplicates
422
        list is not empty, the configuration is corrupted and its caller
423
        should raise an exception
398 424

  
399 425
    """
400 426
    def _AppendUsedPorts(instance_name, disk, used):
427
      duplicates = []
401 428
      if disk.dev_type == constants.LD_DRBD8 and len(disk.logical_id) >= 5:
402 429
        nodeA, nodeB, dummy, minorA, minorB = disk.logical_id[:5]
403 430
        for node, port in ((nodeA, minorA), (nodeB, minorB)):
404
          assert node in used, "Instance node not found in node list"
431
          assert node in used, ("Node '%s' of instance '%s' not found"
432
                                " in node list" % (node, instance_name))
405 433
          if port in used[node]:
406
            raise errors.ProgrammerError("DRBD minor already used:"
407
                                         " %s/%s, %s/%s" %
408
                                         (node, port, instance_name,
409
                                          used[node][port]))
410

  
411
          used[node][port] = instance_name
434
            duplicates.append((node, port, instance_name, used[node][port]))
435
          else:
436
            used[node][port] = instance_name
412 437
      if disk.children:
413 438
        for child in disk.children:
414
          _AppendUsedPorts(instance_name, child, used)
439
          duplicates.extend(_AppendUsedPorts(instance_name, child, used))
440
      return duplicates
415 441

  
442
    duplicates = []
416 443
    my_dict = dict((node, {}) for node in self._config_data.nodes)
417 444
    for (node, minor), instance in self._temporary_drbds.iteritems():
418
      my_dict[node][minor] = instance
445
      if minor in my_dict[node]:
446
        duplicates.append((node, minor, instance, my_dict[node][minor]))
447
      else:
448
        my_dict[node][minor] = instance
419 449
    for instance in self._config_data.instances.itervalues():
420 450
      for disk in instance.disks:
421
        _AppendUsedPorts(instance.name, disk, my_dict)
422
    return my_dict
451
        duplicates.extend(_AppendUsedPorts(instance.name, disk, my_dict))
452
    return my_dict, duplicates
423 453

  
424 454
  @locking.ssynchronized(_config_lock)
425 455
  def ComputeDRBDMap(self):
......
432 462
        an empty list).
433 463

  
434 464
    """
435
    return self._UnlockedComputeDRBDMap()
465
    d_map, duplicates = self._UnlockedComputeDRBDMap()
466
    if duplicates:
467
      raise errors.ConfigurationError("Duplicate DRBD ports detected: %s" %
468
                                      str(duplicates))
469
    return d_map
436 470

  
437 471
  @locking.ssynchronized(_config_lock)
438 472
  def AllocateDRBDMinor(self, nodes, instance):
......
448 482

  
449 483
    """
450 484
    assert isinstance(instance, basestring), \
451
           "Invalid argument passed to AllocateDRBDMinor"
485
           "Invalid argument '%s' passed to AllocateDRBDMinor" % instance
452 486

  
453
    d_map = self._UnlockedComputeDRBDMap()
487
    d_map, duplicates = self._UnlockedComputeDRBDMap()
488
    if duplicates:
489
      raise errors.ConfigurationError("Duplicate DRBD ports detected: %s" %
490
                                      str(duplicates))
454 491
    result = []
455 492
    for nname in nodes:
456 493
      ndata = d_map[nname]
......
469 506
        minor = keys[-1] + 1
470 507
      else:
471 508
        minor = ffree
472
      result.append(minor)
509
      # double-check minor against current instances
510
      assert minor not in d_map[nname], \
511
             ("Attempt to reuse allocated DRBD minor %d on node %s,"
512
              " already allocated to instance %s" %
513
              (minor, nname, d_map[nname][minor]))
473 514
      ndata[minor] = instance
474
      assert (nname, minor) not in self._temporary_drbds, \
475
             "Attempt to reuse reserved DRBD minor"
476
      self._temporary_drbds[(nname, minor)] = instance
515
      # double-check minor against reservation
516
      r_key = (nname, minor)
517
      assert r_key not in self._temporary_drbds, \
518
             ("Attempt to reuse reserved DRBD minor %d on node %s,"
519
              " reserved for instance %s" %
520
              (minor, nname, self._temporary_drbds[r_key]))
521
      self._temporary_drbds[r_key] = instance
522
      result.append(minor)
477 523
    logging.debug("Request to allocate drbd minors, input: %s, returning %s",
478 524
                  nodes, result)
479 525
    return result
......
973 1019
    """Write the configuration data to persistent storage.
974 1020

  
975 1021
    """
1022
    config_errors = self._UnlockedVerifyConfig()
1023
    if config_errors:
1024
      raise errors.ConfigurationError("Configuration data is not"
1025
                                      " consistent: %s" %
1026
                                      (", ".join(config_errors)))
976 1027
    if destination is None:
977 1028
      destination = self._cfg_file
978 1029
    self._BumpSerialNo()

Also available in: Unified diff