Revision 0e79564a

b/lib/client/gnt_cluster.py
367 367
  return 0
368 368

  
369 369

  
370
def _PrintGroupedParams(paramsdict, level=1, roman=False):
371
  """Print Grouped parameters (be, nic, disk) by group.
370
def _FormatGroupedParams(paramsdict, roman=False):
371
  """Format Grouped parameters (be, nic, disk) by group.
372 372

  
373 373
  @type paramsdict: dict of dicts
374 374
  @param paramsdict: {group: {param: value, ...}, ...}
375
  @type level: int
376
  @param level: Level of indention
375
  @rtype: dict of dicts
376
  @return: copy of the input dictionaries with strings as values
377 377

  
378 378
  """
379
  indent = "  " * level
380
  for item, val in sorted(paramsdict.items()):
379
  ret = {}
380
  for (item, val) in paramsdict.items():
381 381
    if isinstance(val, dict):
382
      ToStdout("%s- %s:", indent, item)
383
      _PrintGroupedParams(val, level=level + 1, roman=roman)
382
      ret[item] = _FormatGroupedParams(val, roman=roman)
384 383
    elif roman and isinstance(val, int):
385
      ToStdout("%s  %s: %s", indent, item, compat.TryToRoman(val))
384
      ret[item] = compat.TryToRoman(val)
386 385
    else:
387
      ToStdout("%s  %s: %s", indent, item, val)
386
      ret[item] = str(val)
387
  return ret
388 388

  
389 389

  
390 390
def ShowClusterConfig(opts, args):
......
400 400
  cl = GetClient(query=True)
401 401
  result = cl.QueryClusterInfo()
402 402

  
403
  ToStdout("Cluster name: %s", result["name"])
404
  ToStdout("Cluster UUID: %s", result["uuid"])
405

  
406
  ToStdout("Creation time: %s", utils.FormatTime(result["ctime"]))
407
  ToStdout("Modification time: %s", utils.FormatTime(result["mtime"]))
408

  
409
  ToStdout("Master node: %s", result["master"])
410

  
411
  ToStdout("Architecture (this node): %s (%s)",
412
           result["architecture"][0], result["architecture"][1])
413

  
414 403
  if result["tags"]:
415 404
    tags = utils.CommaJoin(utils.NiceSort(result["tags"]))
416 405
  else:
417 406
    tags = "(none)"
418

  
419
  ToStdout("Tags: %s", tags)
420

  
421
  ToStdout("Default hypervisor: %s", result["default_hypervisor"])
422
  ToStdout("Enabled hypervisors: %s",
423
           utils.CommaJoin(result["enabled_hypervisors"]))
424

  
425
  ToStdout("Hypervisor parameters:")
426
  _PrintGroupedParams(result["hvparams"])
427

  
428
  ToStdout("OS-specific hypervisor parameters:")
429
  _PrintGroupedParams(result["os_hvp"])
430

  
431
  ToStdout("OS parameters:")
432
  _PrintGroupedParams(result["osparams"])
433

  
434
  ToStdout("Hidden OSes: %s", utils.CommaJoin(result["hidden_os"]))
435
  ToStdout("Blacklisted OSes: %s", utils.CommaJoin(result["blacklisted_os"]))
436

  
437
  ToStdout("Cluster parameters:")
438
  ToStdout("  - candidate pool size: %s",
439
            compat.TryToRoman(result["candidate_pool_size"],
440
                              convert=opts.roman_integers))
441
  ToStdout("  - master netdev: %s", result["master_netdev"])
442
  ToStdout("  - master netmask: %s", result["master_netmask"])
443
  ToStdout("  - use external master IP address setup script: %s",
444
           result["use_external_mip_script"])
445
  ToStdout("  - lvm volume group: %s", result["volume_group_name"])
446 407
  if result["reserved_lvs"]:
447 408
    reserved_lvs = utils.CommaJoin(result["reserved_lvs"])
448 409
  else:
449 410
    reserved_lvs = "(none)"
450
  ToStdout("  - lvm reserved volumes: %s", reserved_lvs)
451
  ToStdout("  - drbd usermode helper: %s", result["drbd_usermode_helper"])
452
  ToStdout("  - file storage path: %s", result["file_storage_dir"])
453
  ToStdout("  - shared file storage path: %s",
454
           result["shared_file_storage_dir"])
455
  ToStdout("  - maintenance of node health: %s",
456
           result["maintain_node_health"])
457
  ToStdout("  - uid pool: %s", uidpool.FormatUidPool(result["uid_pool"]))
458
  ToStdout("  - default instance allocator: %s", result["default_iallocator"])
459
  ToStdout("  - primary ip version: %d", result["primary_ip_version"])
460
  ToStdout("  - preallocation wipe disks: %s", result["prealloc_wipe_disks"])
461
  ToStdout("  - OS search path: %s", utils.CommaJoin(pathutils.OS_SEARCH_PATH))
462
  ToStdout("  - ExtStorage Providers search path: %s",
463
           utils.CommaJoin(pathutils.ES_SEARCH_PATH))
464
  ToStdout("  - enabled storage types: %s",
465
           utils.CommaJoin(result["enabled_storage_types"]))
466

  
467
  ToStdout("Default node parameters:")
468
  _PrintGroupedParams(result["ndparams"], roman=opts.roman_integers)
469

  
470
  ToStdout("Default instance parameters:")
471
  _PrintGroupedParams(result["beparams"], roman=opts.roman_integers)
472

  
473
  ToStdout("Default nic parameters:")
474
  _PrintGroupedParams(result["nicparams"], roman=opts.roman_integers)
475

  
476
  ToStdout("Default disk parameters:")
477
  _PrintGroupedParams(result["diskparams"], roman=opts.roman_integers)
478

  
479
  ToStdout("Instance policy - limits for instances:")
480
  for key in constants.IPOLICY_ISPECS:
481
    ToStdout("  - %s", key)
482
    _PrintGroupedParams(result["ipolicy"][key], roman=opts.roman_integers)
483
  ToStdout("  - enabled disk templates: %s",
484
           utils.CommaJoin(result["ipolicy"][constants.IPOLICY_DTS]))
485
  for key in constants.IPOLICY_PARAMETERS:
486
    ToStdout("  - %s: %s", key, result["ipolicy"][key])
487 411

  
412
  info = [
413
    ("Cluster name", result["name"]),
414
    ("Cluster UUID", result["uuid"]),
415

  
416
    ("Creation time", utils.FormatTime(result["ctime"])),
417
    ("Modification time", utils.FormatTime(result["mtime"])),
418

  
419
    ("Master node", result["master"]),
420

  
421
    ("Architecture (this node)",
422
     "%s (%s)" % (result["architecture"][0], result["architecture"][1])),
423

  
424
    ("Tags", tags),
425

  
426
    ("Default hypervisor", result["default_hypervisor"]),
427
    ("Enabled hypervisors",
428
     utils.CommaJoin(result["enabled_hypervisors"])),
429

  
430
    ("Hypervisor parameters", _FormatGroupedParams(result["hvparams"])),
431

  
432
    ("OS-specific hypervisor parameters",
433
     _FormatGroupedParams(result["os_hvp"])),
434

  
435
    ("OS parameters", _FormatGroupedParams(result["osparams"])),
436

  
437
    ("Hidden OSes", utils.CommaJoin(result["hidden_os"])),
438
    ("Blacklisted OSes", utils.CommaJoin(result["blacklisted_os"])),
439

  
440
    ("Cluster parameters", [
441
      ("candidate pool size",
442
       compat.TryToRoman(result["candidate_pool_size"],
443
                         convert=opts.roman_integers)),
444
      ("master netdev", result["master_netdev"]),
445
      ("master netmask", result["master_netmask"]),
446
      ("use external master IP address setup script",
447
       result["use_external_mip_script"]),
448
      ("lvm volume group", result["volume_group_name"]),
449
      ("lvm reserved volumes", reserved_lvs),
450
      ("drbd usermode helper", result["drbd_usermode_helper"]),
451
      ("file storage path", result["file_storage_dir"]),
452
      ("shared file storage path", result["shared_file_storage_dir"]),
453
      ("maintenance of node health", result["maintain_node_health"]),
454
      ("uid pool", uidpool.FormatUidPool(result["uid_pool"])),
455
      ("default instance allocator", result["default_iallocator"]),
456
      ("primary ip version", result["primary_ip_version"]),
457
      ("preallocation wipe disks", result["prealloc_wipe_disks"]),
458
      ("OS search path", utils.CommaJoin(pathutils.OS_SEARCH_PATH)),
459
      ("ExtStorage Providers search path",
460
       utils.CommaJoin(pathutils.ES_SEARCH_PATH)),
461
      ("enabled storage types",
462
       utils.CommaJoin(result["enabled_storage_types"])),
463
      ]),
464

  
465
    ("Default node parameters",
466
     _FormatGroupedParams(result["ndparams"], roman=opts.roman_integers)),
467

  
468
    ("Default instance parameters",
469
     _FormatGroupedParams(result["beparams"], roman=opts.roman_integers)),
470

  
471
    ("Default nic parameters",
472
     _FormatGroupedParams(result["nicparams"], roman=opts.roman_integers)),
473

  
474
    ("Default disk parameters",
475
     _FormatGroupedParams(result["diskparams"], roman=opts.roman_integers)),
476

  
477
    ("Instance policy - limits for instances",
478
     [
479
       (key,
480
        _FormatGroupedParams(result["ipolicy"][key], roman=opts.roman_integers))
481
       for key in constants.IPOLICY_ISPECS
482
       ] +
483
     [
484
       ("enabled disk templates",
485
        utils.CommaJoin(result["ipolicy"][constants.IPOLICY_DTS])),
486
       ] +
487
     [
488
       (key, result["ipolicy"][key])
489
       for key in constants.IPOLICY_PARAMETERS
490
       ]),
491
    ]
492

  
493
  PrintGenericInfo(info)
488 494
  return 0
489 495

  
490 496

  
b/qa/qa_cluster.py
63 63
    AssertEqual(qa_utils.GetCommandOutput(node.primary, cmd), content)
64 64

  
65 65

  
66
# "gnt-cluster info" fields
67
_CIFIELD_RE = re.compile(r"^[-\s]*(?P<field>[^\s:]+):\s*(?P<value>\S.*)$")
66
def _GetClusterField(field_path):
67
  """Get the value of a cluster field.
68 68

  
69

  
70
def _GetBoolClusterField(field):
71
  """Get the Boolean value of a cluster field.
72

  
73
  This function currently assumes that the field name is unique in the cluster
74
  configuration. An assertion checks this assumption.
75

  
76
  @type field: string
77
  @param field: Name of the field
78
  @rtype: bool
79
  @return: The effective value of the field
69
  @type field_path: list of strings
70
  @param field_path: Names of the groups/fields to navigate to get the desired
71
      value, e.g. C{["Default node parameters", "oob_program"]}
72
  @return: The effective value of the field (the actual type depends on the
73
      chosen field)
80 74

  
81 75
  """
82
  master = qa_config.GetMasterNode()
83
  infocmd = "gnt-cluster info"
84
  info_out = qa_utils.GetCommandOutput(master.primary, infocmd)
85
  ret = None
86
  for l in info_out.splitlines():
87
    m = _CIFIELD_RE.match(l)
88
    # FIXME: There should be a way to specify a field through a hierarchy
89
    if m and m.group("field") == field:
90
      # Make sure that ignoring the hierarchy doesn't cause a double match
91
      assert ret is None
92
      ret = (m.group("value").lower() == "true")
93
  if ret is not None:
94
    return ret
95
  raise qa_error.Error("Field not found in cluster configuration: %s" % field)
76
  assert isinstance(field_path, list)
77
  assert field_path
78
  ret = qa_utils.GetObjectInfo(["gnt-cluster", "info"])
79
  for key in field_path:
80
    ret = ret[key]
81
  return ret
96 82

  
97 83

  
98 84
# Cluster-verify errors (date, "ERROR", then error code)
......
467 453
    AssertCommand(["gnt-cluster", "modify", "-B", bep])
468 454

  
469 455

  
470
_START_IPOLICY_RE = re.compile(r"^(\s*)Instance policy")
471
_START_ISPEC_RE = re.compile(r"^\s+-\s+(std|min|max)")
472
_VALUE_RE = r"([^\s:][^:]*):\s+(\S.*)$"
473
_IPOLICY_PARAM_RE = re.compile(r"^\s+-\s+" + _VALUE_RE)
474
_ISPEC_VALUE_RE = re.compile(r"^\s+" + _VALUE_RE)
475

  
476

  
477 456
def _GetClusterIPolicy():
478 457
  """Return the run-time values of the cluster-level instance policy.
479 458

  
......
484 463
        "min", "max", or "std"
485 464

  
486 465
  """
487
  mnode = qa_config.GetMasterNode()
488
  info = GetCommandOutput(mnode.primary, "gnt-cluster info")
489
  inside_policy = False
490
  end_ispec_re = None
491
  curr_spec = ""
492
  specs = {}
493
  policy = {}
494
  for line in info.splitlines():
495
    if inside_policy:
496
      # The order of the matching is important, as some REs overlap
497
      m = _START_ISPEC_RE.match(line)
498
      if m:
499
        curr_spec = m.group(1)
500
        continue
501
      m = _IPOLICY_PARAM_RE.match(line)
502
      if m:
503
        policy[m.group(1)] = m.group(2).strip()
504
        continue
505
      m = _ISPEC_VALUE_RE.match(line)
506
      if m:
507
        assert curr_spec
508
        par = m.group(1)
466
  info = qa_utils.GetObjectInfo(["gnt-cluster", "info"])
467
  policy = info["Instance policy - limits for instances"]
468
  ret_specs = {}
469
  ret_policy = {}
470
  for (key, val) in policy.items():
471
    if key in constants.IPOLICY_ISPECS:
472
      for (par, pval) in val.items():
509 473
        if par == "memory-size":
510 474
          par = "mem-size"
511
        d = specs.setdefault(par, {})
512
        d[curr_spec] = m.group(2).strip()
513
        continue
514
      assert end_ispec_re is not None
515
      if end_ispec_re.match(line):
516
        inside_policy = False
475
        d = ret_specs.setdefault(par, {})
476
        d[key] = pval
517 477
    else:
518
      m = _START_IPOLICY_RE.match(line)
519
      if m:
520
        inside_policy = True
521
        # We stop parsing when we find the same indentation level
522
        re_str = r"^\s{%s}\S" % len(m.group(1))
523
        end_ispec_re = re.compile(re_str)
478
      ret_policy[key] = val
479

  
524 480
  # Sanity checks
525
  assert len(specs) > 0
526
  good = ("min" in d and "std" in d and "max" in d for d in specs)
527
  assert good, "Missing item in specs: %s" % specs
528
  assert len(policy) > 0
529
  return (policy, specs)
481
  assert len(ret_specs) > 0
482
  good = ("min" in d and "std" in d and "max" in d for d in ret_specs)
483
  assert good, "Missing item in specs: %s" % ret_specs
484
  assert len(ret_policy) > 0
485
  return (ret_policy, ret_specs)
530 486

  
531 487

  
532 488
def TestClusterModifyIPolicy():
......
912 868
  @return: The old value of exclusive_storage
913 869

  
914 870
  """
915
  oldvalue = _GetBoolClusterField("exclusive_storage")
871
  es_path = ["Default node parameters", "exclusive_storage"]
872
  oldvalue = _GetClusterField(es_path)
916 873
  AssertCommand(["gnt-cluster", "modify", "--node-parameters",
917 874
                 "exclusive_storage=%s" % newvalue])
918
  effvalue = _GetBoolClusterField("exclusive_storage")
875
  effvalue = _GetClusterField(es_path)
919 876
  if effvalue != newvalue:
920 877
    raise qa_error.Error("exclusive_storage has the wrong value: %s instead"
921 878
                         " of %s" % (effvalue, newvalue))
b/qa/qa_utils.py
23 23

  
24 24
"""
25 25

  
26
import operator
26 27
import os
28
import random
27 29
import re
28
import sys
29 30
import subprocess
30
import random
31
import sys
31 32
import tempfile
32
import operator
33
import yaml
33 34

  
34 35
try:
35 36
  import functools
......
343 344
  return p.stdout.read()
344 345

  
345 346

  
347
def GetObjectInfo(infocmd):
348
  """Get and parse information about a Ganeti object.
349

  
350
  @type infocmd: list of strings
351
  @param infocmd: command to be executed, e.g. ["gnt-cluster", "info"]
352
  @return: the information parsed, appropriately stored in dictionaries,
353
      lists...
354

  
355
  """
356
  master = qa_config.GetMasterNode()
357
  cmdline = utils.ShellQuoteArgs(infocmd)
358
  info_out = GetCommandOutput(master.primary, cmdline)
359
  return yaml.load(info_out)
360

  
361

  
346 362
def UploadFile(node, src):
347 363
  """Uploads a file to a node and returns the filename.
348 364

  

Also available in: Unified diff