Revision b3f3aa3d

b/qa/ganeti-qa.py
171 171
    ("cluster-reserved-lvs", qa_cluster.TestClusterReservedLvs),
172 172
    # TODO: add more cluster modify tests
173 173
    ("cluster-modify", qa_cluster.TestClusterModifyEmpty),
174
    ("cluster-modify", qa_cluster.TestClusterModifyIPolicy),
175
    ("cluster-modify", qa_cluster.TestClusterModifyISpecs),
174 176
    ("cluster-modify", qa_cluster.TestClusterModifyBe),
175 177
    ("cluster-modify", qa_cluster.TestClusterModifyDisk),
176 178
    ("cluster-rename", qa_cluster.TestClusterRename),
b/qa/qa_cluster.py
423 423
    AssertCommand(["gnt-cluster", "modify", "-B", bep])
424 424

  
425 425

  
426
_START_IPOLICY_RE = re.compile(r"^(\s*)Instance policy")
427
_START_ISPEC_RE = re.compile(r"^\s+-\s+(std|min|max)")
428
_VALUE_RE = r"([^\s:][^:]*):\s+(\S.*)$"
429
_IPOLICY_PARAM_RE = re.compile(r"^\s+-\s+" + _VALUE_RE)
430
_ISPEC_VALUE_RE = re.compile(r"^\s+" + _VALUE_RE)
431

  
432

  
433
def _GetClusterIPolicy():
434
  """Return the run-time values of the cluster-level instance policy.
435

  
436
  @rtype: tuple
437
  @return: (policy, specs), where:
438
      - policy is a dictionary of the policy values, instance specs excluded
439
      - specs is dict of dict, specs[par][key] is a spec value, where key is
440
        "min", "max", or "std"
441

  
442
  """
443
  mnode = qa_config.GetMasterNode()
444
  info = GetCommandOutput(mnode["primary"], "gnt-cluster info")
445
  inside_policy = False
446
  end_ispec_re = None
447
  curr_spec = ""
448
  specs = {}
449
  policy = {}
450
  for line in info.splitlines():
451
    if inside_policy:
452
      # The order of the matching is important, as some REs overlap
453
      m = _START_ISPEC_RE.match(line)
454
      if m:
455
        curr_spec = m.group(1)
456
        continue
457
      m = _IPOLICY_PARAM_RE.match(line)
458
      if m:
459
        policy[m.group(1)] = m.group(2).strip()
460
        continue
461
      m = _ISPEC_VALUE_RE.match(line)
462
      if m:
463
        assert curr_spec
464
        par = m.group(1)
465
        if par == "memory-size":
466
          par = "mem-size"
467
        d = specs.setdefault(par, {})
468
        d[curr_spec] = m.group(2).strip()
469
        continue
470
      assert end_ispec_re is not None
471
      if end_ispec_re.match(line):
472
        inside_policy = False
473
    else:
474
      m = _START_IPOLICY_RE.match(line)
475
      if m:
476
        inside_policy = True
477
        # We stop parsing when we find the same indentation level
478
        re_str = r"^\s{%s}\S" % len(m.group(1))
479
        end_ispec_re = re.compile(re_str)
480
  # Sanity checks
481
  assert len(specs) > 0
482
  good = ("min" in d and "std" in d and "max" in d for d in specs)
483
  assert good, "Missing item in specs: %s" % specs
484
  assert len(policy) > 0
485
  return (policy, specs)
486

  
487

  
488
def TestClusterModifyIPolicy():
489
  """gnt-cluster modify --ipolicy-*"""
490
  basecmd = ["gnt-cluster", "modify"]
491
  (old_policy, old_specs) = _GetClusterIPolicy()
492
  for par in ["vcpu-ratio", "spindle-ratio"]:
493
    curr_val = float(old_policy[par])
494
    test_values = [
495
      (True, 1.0),
496
      (True, 1.5),
497
      (True, 2),
498
      (False, "a"),
499
      # Restore the old value
500
      (True, curr_val),
501
      ]
502
    for (good, val) in test_values:
503
      cmd = basecmd + ["--ipolicy-%s=%s" % (par, val)]
504
      AssertCommand(cmd, fail=not good)
505
      if good:
506
        curr_val = val
507
      # Check the affected parameter
508
      (eff_policy, eff_specs) = _GetClusterIPolicy()
509
      AssertEqual(float(eff_policy[par]), curr_val)
510
      # Check everything else
511
      AssertEqual(eff_specs, old_specs)
512
      for p in eff_policy.keys():
513
        if p == par:
514
          continue
515
        AssertEqual(eff_policy[p], old_policy[p])
516

  
517
  # Disk templates are treated slightly differently
518
  par = "disk-templates"
519
  disp_str = "enabled disk templates"
520
  curr_val = old_policy[disp_str]
521
  test_values = [
522
    (True, constants.DT_PLAIN),
523
    (True, "%s,%s" % (constants.DT_PLAIN, constants.DT_DRBD8)),
524
    (False, "thisisnotadisktemplate"),
525
    (False, ""),
526
    # Restore the old value
527
    (True, curr_val.replace(" ", "")),
528
    ]
529
  for (good, val) in test_values:
530
    cmd = basecmd + ["--ipolicy-%s=%s" % (par, val)]
531
    AssertCommand(cmd, fail=not good)
532
    if good:
533
      curr_val = val
534
    # Check the affected parameter
535
    (eff_policy, eff_specs) = _GetClusterIPolicy()
536
    AssertEqual(eff_policy[disp_str].replace(" ", ""), curr_val)
537
    # Check everything else
538
    AssertEqual(eff_specs, old_specs)
539
    for p in eff_policy.keys():
540
      if p == disp_str:
541
        continue
542
      AssertEqual(eff_policy[p], old_policy[p])
543

  
544

  
545
def TestClusterSetISpecs(new_specs, fail=False, old_values=None):
546
  """Change instance specs.
547

  
548
  @type new_specs: dict of dict
549
  @param new_specs: new_specs[par][key], where key is "min", "max", "std". It
550
      can be an empty dictionary.
551
  @type fail: bool
552
  @param fail: if the change is expected to fail
553
  @type old_values: tuple
554
  @param old_values: (old_policy, old_specs), as returned by
555
     L{_GetClusterIPolicy}
556
  @return: same as L{_GetClusterIPolicy}
557

  
558
  """
559
  if old_values:
560
    (old_policy, old_specs) = old_values
561
  else:
562
    (old_policy, old_specs) = _GetClusterIPolicy()
563
  if new_specs:
564
    cmd = ["gnt-cluster", "modify"]
565
    for (par, keyvals) in new_specs.items():
566
      if par == "spindle-use":
567
        # ignore spindle-use, which is not settable
568
        continue
569
      cmd += [
570
        "--specs-%s" % par,
571
        ",".join(["%s=%s" % (k, v) for (k, v) in keyvals.items()]),
572
        ]
573
    AssertCommand(cmd, fail=fail)
574
  # Check the new state
575
  (eff_policy, eff_specs) = _GetClusterIPolicy()
576
  AssertEqual(eff_policy, old_policy)
577
  if fail:
578
    AssertEqual(eff_specs, old_specs)
579
  else:
580
    for par in eff_specs:
581
      for key in eff_specs[par]:
582
        if par in new_specs and key in new_specs[par]:
583
          AssertEqual(int(eff_specs[par][key]), int(new_specs[par][key]))
584
        else:
585
          AssertEqual(int(eff_specs[par][key]), int(old_specs[par][key]))
586
  return (eff_policy, eff_specs)
587

  
588

  
589
def TestClusterModifyISpecs():
590
  """gnt-cluster modify --specs-*"""
591
  params = ["mem-size", "disk-size", "disk-count", "cpu-count", "nic-count"]
592
  (cur_policy, cur_specs) = _GetClusterIPolicy()
593
  for par in params:
594
    test_values = [
595
      (True, 0, 4, 12),
596
      (True, 4, 4, 12),
597
      (True, 4, 12, 12),
598
      (True, 4, 4, 4),
599
      (False, 4, 0, 12),
600
      (False, 4, 16, 12),
601
      (False, 4, 4, 0),
602
      (False, 12, 4, 4),
603
      (False, 12, 4, 0),
604
      (False, "a", 4, 12),
605
      (False, 0, "a", 12),
606
      (False, 0, 4, "a"),
607
      # This is to restore the old values
608
      (True,
609
       cur_specs[par]["min"], cur_specs[par]["std"], cur_specs[par]["max"])
610
      ]
611
    for (good, mn, st, mx) in test_values:
612
      new_vals = {par: {"min": str(mn), "std": str(st), "max": str(mx)}}
613
      cur_state = (cur_policy, cur_specs)
614
      # We update cur_specs, as we've copied the values to restore already
615
      (cur_policy, cur_specs) = TestClusterSetISpecs(new_vals, fail=not good,
616
                                                     old_values=cur_state)
617

  
618

  
426 619
def TestClusterInfo():
427 620
  """gnt-cluster info"""
428 621
  AssertCommand(["gnt-cluster", "info"])

Also available in: Unified diff