Statistics
| Branch: | Tag: | Revision:

root / qa / qa_cluster.py @ 0e79564a

History | View | Annotate | Download (27.9 kB)

1
#
2
#
3

    
4
# Copyright (C) 2007, 2010, 2011, 2012, 2013 Google Inc.
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
# General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
# 02110-1301, USA.
20

    
21

    
22
"""Cluster related QA tests.
23

24
"""
25

    
26
import re
27
import tempfile
28
import os.path
29

    
30
from ganeti import constants
31
from ganeti import compat
32
from ganeti import utils
33
from ganeti import pathutils
34

    
35
import qa_config
36
import qa_utils
37
import qa_error
38

    
39
from qa_utils import AssertEqual, AssertCommand, GetCommandOutput
40

    
41

    
42
# Prefix for LVM volumes created by QA code during tests
43
_QA_LV_PREFIX = "qa-"
44

    
45
#: cluster verify command
46
_CLUSTER_VERIFY = ["gnt-cluster", "verify"]
47

    
48

    
49
def _RemoveFileFromAllNodes(filename):
50
  """Removes a file from all nodes.
51

52
  """
53
  for node in qa_config.get("nodes"):
54
    AssertCommand(["rm", "-f", filename], node=node)
55

    
56

    
57
def _CheckFileOnAllNodes(filename, content):
58
  """Verifies the content of the given file on all nodes.
59

60
  """
61
  cmd = utils.ShellQuoteArgs(["cat", filename])
62
  for node in qa_config.get("nodes"):
63
    AssertEqual(qa_utils.GetCommandOutput(node.primary, cmd), content)
64

    
65

    
66
def _GetClusterField(field_path):
67
  """Get the value of a cluster field.
68

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)
74

75
  """
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
82

    
83

    
84
# Cluster-verify errors (date, "ERROR", then error code)
85
_CVERROR_RE = re.compile(r"^[\w\s:]+\s+- (ERROR|WARNING):([A-Z0-9_-]+):")
86

    
87

    
88
def _GetCVErrorCodes(cvout):
89
  errs = set()
90
  warns = set()
91
  for l in cvout.splitlines():
92
    m = _CVERROR_RE.match(l)
93
    if m:
94
      etype = m.group(1)
95
      ecode = m.group(2)
96
      if etype == "ERROR":
97
        errs.add(ecode)
98
      elif etype == "WARNING":
99
        warns.add(ecode)
100
  return (errs, warns)
101

    
102

    
103
def _CheckVerifyErrors(actual, expected, etype):
104
  exp_codes = compat.UniqueFrozenset(e for (_, e, _) in expected)
105
  if not actual.issuperset(exp_codes):
106
    missing = exp_codes.difference(actual)
107
    raise qa_error.Error("Cluster-verify didn't return these expected"
108
                         " %ss: %s" % (etype, utils.CommaJoin(missing)))
109

    
110

    
111
def AssertClusterVerify(fail=False, errors=None, warnings=None):
112
  """Run cluster-verify and check the result
113

114
  @type fail: bool
115
  @param fail: if cluster-verify is expected to fail instead of succeeding
116
  @type errors: list of tuples
117
  @param errors: List of CV_XXX errors that are expected; if specified, all the
118
      errors listed must appear in cluster-verify output. A non-empty value
119
      implies C{fail=True}.
120
  @type warnings: list of tuples
121
  @param warnings: Same as C{errors} but for warnings.
122

123
  """
124
  cvcmd = "gnt-cluster verify"
125
  mnode = qa_config.GetMasterNode()
126
  if errors or warnings:
127
    cvout = GetCommandOutput(mnode.primary, cvcmd + " --error-codes",
128
                             fail=(fail or errors))
129
    (act_errs, act_warns) = _GetCVErrorCodes(cvout)
130
    if errors:
131
      _CheckVerifyErrors(act_errs, errors, "error")
132
    if warnings:
133
      _CheckVerifyErrors(act_warns, warnings, "warning")
134
  else:
135
    AssertCommand(cvcmd, fail=fail, node=mnode)
136

    
137

    
138
# data for testing failures due to bad keys/values for disk parameters
139
_FAIL_PARAMS = ["nonexistent:resync-rate=1",
140
                "drbd:nonexistent=1",
141
                "drbd:resync-rate=invalid",
142
                ]
143

    
144

    
145
def TestClusterInitDisk():
146
  """gnt-cluster init -D"""
147
  name = qa_config.get("name")
148
  for param in _FAIL_PARAMS:
149
    AssertCommand(["gnt-cluster", "init", "-D", param, name], fail=True)
150

    
151

    
152
def TestClusterInit(rapi_user, rapi_secret):
153
  """gnt-cluster init"""
154
  master = qa_config.GetMasterNode()
155

    
156
  rapi_users_path = qa_utils.MakeNodePath(master, pathutils.RAPI_USERS_FILE)
157
  rapi_dir = os.path.dirname(rapi_users_path)
158

    
159
  # First create the RAPI credentials
160
  fh = tempfile.NamedTemporaryFile()
161
  try:
162
    fh.write("%s %s write\n" % (rapi_user, rapi_secret))
163
    fh.flush()
164

    
165
    tmpru = qa_utils.UploadFile(master.primary, fh.name)
166
    try:
167
      AssertCommand(["mkdir", "-p", rapi_dir])
168
      AssertCommand(["mv", tmpru, rapi_users_path])
169
    finally:
170
      AssertCommand(["rm", "-f", tmpru])
171
  finally:
172
    fh.close()
173

    
174
  # Initialize cluster
175
  cmd = [
176
    "gnt-cluster", "init",
177
    "--primary-ip-version=%d" % qa_config.get("primary_ip_version", 4),
178
    "--enabled-hypervisors=%s" % ",".join(qa_config.GetEnabledHypervisors()),
179
    "--enabled-storage-types=%s" %
180
      ",".join(qa_config.GetEnabledStorageTypes())
181
    ]
182

    
183
  for spec_type in ("mem-size", "disk-size", "disk-count", "cpu-count",
184
                    "nic-count"):
185
    for spec_val in ("min", "max", "std"):
186
      spec = qa_config.get("ispec_%s_%s" %
187
                           (spec_type.replace("-", "_"), spec_val), None)
188
      if spec is not None:
189
        cmd.append("--specs-%s=%s=%d" % (spec_type, spec_val, spec))
190

    
191
  if master.secondary:
192
    cmd.append("--secondary-ip=%s" % master.secondary)
193

    
194
  vgname = qa_config.get("vg-name", None)
195
  if vgname:
196
    cmd.append("--vg-name=%s" % vgname)
197

    
198
  master_netdev = qa_config.get("master-netdev", None)
199
  if master_netdev:
200
    cmd.append("--master-netdev=%s" % master_netdev)
201

    
202
  nicparams = qa_config.get("default-nicparams", None)
203
  if nicparams:
204
    cmd.append("--nic-parameters=%s" %
205
               ",".join(utils.FormatKeyValue(nicparams)))
206

    
207
  # Cluster value of the exclusive-storage node parameter
208
  e_s = qa_config.get("exclusive-storage")
209
  if e_s is not None:
210
    cmd.extend(["--node-parameters", "exclusive_storage=%s" % e_s])
211
  else:
212
    e_s = False
213
  qa_config.SetExclusiveStorage(e_s)
214

    
215
  extra_args = qa_config.get("cluster-init-args")
216
  if extra_args:
217
    cmd.extend(extra_args)
218

    
219
  cmd.append(qa_config.get("name"))
220

    
221
  AssertCommand(cmd)
222

    
223
  cmd = ["gnt-cluster", "modify"]
224

    
225
  # hypervisor parameter modifications
226
  hvp = qa_config.get("hypervisor-parameters", {})
227
  for k, v in hvp.items():
228
    cmd.extend(["-H", "%s:%s" % (k, v)])
229
  # backend parameter modifications
230
  bep = qa_config.get("backend-parameters", "")
231
  if bep:
232
    cmd.extend(["-B", bep])
233

    
234
  if len(cmd) > 2:
235
    AssertCommand(cmd)
236

    
237
  # OS parameters
238
  osp = qa_config.get("os-parameters", {})
239
  for k, v in osp.items():
240
    AssertCommand(["gnt-os", "modify", "-O", v, k])
241

    
242
  # OS hypervisor parameters
243
  os_hvp = qa_config.get("os-hvp", {})
244
  for os_name in os_hvp:
245
    for hv, hvp in os_hvp[os_name].items():
246
      AssertCommand(["gnt-os", "modify", "-H", "%s:%s" % (hv, hvp), os_name])
247

    
248

    
249
def TestClusterRename():
250
  """gnt-cluster rename"""
251
  cmd = ["gnt-cluster", "rename", "-f"]
252

    
253
  original_name = qa_config.get("name")
254
  rename_target = qa_config.get("rename", None)
255
  if rename_target is None:
256
    print qa_utils.FormatError('"rename" entry is missing')
257
    return
258

    
259
  for data in [
260
    cmd + [rename_target],
261
    _CLUSTER_VERIFY,
262
    cmd + [original_name],
263
    _CLUSTER_VERIFY,
264
    ]:
265
    AssertCommand(data)
266

    
267

    
268
def TestClusterOob():
269
  """out-of-band framework"""
270
  oob_path_exists = "/tmp/ganeti-qa-oob-does-exist-%s" % utils.NewUUID()
271

    
272
  AssertCommand(_CLUSTER_VERIFY)
273
  AssertCommand(["gnt-cluster", "modify", "--node-parameters",
274
                 "oob_program=/tmp/ganeti-qa-oob-does-not-exist-%s" %
275
                 utils.NewUUID()])
276

    
277
  AssertCommand(_CLUSTER_VERIFY, fail=True)
278

    
279
  AssertCommand(["touch", oob_path_exists])
280
  AssertCommand(["chmod", "0400", oob_path_exists])
281
  AssertCommand(["gnt-cluster", "copyfile", oob_path_exists])
282

    
283
  try:
284
    AssertCommand(["gnt-cluster", "modify", "--node-parameters",
285
                   "oob_program=%s" % oob_path_exists])
286

    
287
    AssertCommand(_CLUSTER_VERIFY, fail=True)
288

    
289
    AssertCommand(["chmod", "0500", oob_path_exists])
290
    AssertCommand(["gnt-cluster", "copyfile", oob_path_exists])
291

    
292
    AssertCommand(_CLUSTER_VERIFY)
293
  finally:
294
    AssertCommand(["gnt-cluster", "command", "rm", oob_path_exists])
295

    
296
  AssertCommand(["gnt-cluster", "modify", "--node-parameters",
297
                 "oob_program="])
298

    
299

    
300
def TestClusterEpo():
301
  """gnt-cluster epo"""
302
  master = qa_config.GetMasterNode()
303

    
304
  # Assert that OOB is unavailable for all nodes
305
  result_output = GetCommandOutput(master.primary,
306
                                   "gnt-node list --verbose --no-headers -o"
307
                                   " powered")
308
  AssertEqual(compat.all(powered == "(unavail)"
309
                         for powered in result_output.splitlines()), True)
310

    
311
  # Conflicting
312
  AssertCommand(["gnt-cluster", "epo", "--groups", "--all"], fail=True)
313
  # --all doesn't expect arguments
314
  AssertCommand(["gnt-cluster", "epo", "--all", "some_arg"], fail=True)
315

    
316
  # Unless --all is given master is not allowed to be in the list
317
  AssertCommand(["gnt-cluster", "epo", "-f", master.primary], fail=True)
318

    
319
  # This shouldn't fail
320
  AssertCommand(["gnt-cluster", "epo", "-f", "--all"])
321

    
322
  # All instances should have been stopped now
323
  result_output = GetCommandOutput(master.primary,
324
                                   "gnt-instance list --no-headers -o status")
325
  # ERROR_down because the instance is stopped but not recorded as such
326
  AssertEqual(compat.all(status == "ERROR_down"
327
                         for status in result_output.splitlines()), True)
328

    
329
  # Now start everything again
330
  AssertCommand(["gnt-cluster", "epo", "--on", "-f", "--all"])
331

    
332
  # All instances should have been started now
333
  result_output = GetCommandOutput(master.primary,
334
                                   "gnt-instance list --no-headers -o status")
335
  AssertEqual(compat.all(status == "running"
336
                         for status in result_output.splitlines()), True)
337

    
338

    
339
def TestClusterVerify():
340
  """gnt-cluster verify"""
341
  AssertCommand(_CLUSTER_VERIFY)
342
  AssertCommand(["gnt-cluster", "verify-disks"])
343

    
344

    
345
def TestJobqueue():
346
  """gnt-debug test-jobqueue"""
347
  AssertCommand(["gnt-debug", "test-jobqueue"])
348

    
349

    
350
def TestDelay(node):
351
  """gnt-debug delay"""
352
  AssertCommand(["gnt-debug", "delay", "1"])
353
  AssertCommand(["gnt-debug", "delay", "--no-master", "1"])
354
  AssertCommand(["gnt-debug", "delay", "--no-master",
355
                 "-n", node.primary, "1"])
356

    
357

    
358
def TestClusterReservedLvs():
359
  """gnt-cluster reserved lvs"""
360
  vgname = qa_config.get("vg-name", constants.DEFAULT_VG)
361
  lvname = _QA_LV_PREFIX + "test"
362
  lvfullname = "/".join([vgname, lvname])
363
  for fail, cmd in [
364
    (False, _CLUSTER_VERIFY),
365
    (False, ["gnt-cluster", "modify", "--reserved-lvs", ""]),
366
    (False, ["lvcreate", "-L1G", "-n", lvname, vgname]),
367
    (True, _CLUSTER_VERIFY),
368
    (False, ["gnt-cluster", "modify", "--reserved-lvs",
369
             "%s,.*/other-test" % lvfullname]),
370
    (False, _CLUSTER_VERIFY),
371
    (False, ["gnt-cluster", "modify", "--reserved-lvs",
372
             ".*/%s.*" % _QA_LV_PREFIX]),
373
    (False, _CLUSTER_VERIFY),
374
    (False, ["gnt-cluster", "modify", "--reserved-lvs", ""]),
375
    (True, _CLUSTER_VERIFY),
376
    (False, ["lvremove", "-f", lvfullname]),
377
    (False, _CLUSTER_VERIFY),
378
    ]:
379
    AssertCommand(cmd, fail=fail)
380

    
381

    
382
def TestClusterModifyEmpty():
383
  """gnt-cluster modify"""
384
  AssertCommand(["gnt-cluster", "modify"], fail=True)
385

    
386

    
387
def TestClusterModifyDisk():
388
  """gnt-cluster modify -D"""
389
  for param in _FAIL_PARAMS:
390
    AssertCommand(["gnt-cluster", "modify", "-D", param], fail=True)
391

    
392

    
393
def TestClusterModifyStorageTypes():
394
  """gnt-cluster modify --enabled-storage-types=..."""
395
  default_storage_type = qa_config.GetDefaultStorageType()
396
  AssertCommand(
397
    ["gnt-cluster", "modify",
398
     "--enabled-storage-types=%s" % default_storage_type],
399
    fail=False)
400
  AssertCommand(["gnt-cluster", "info"])
401
  AssertCommand(
402
    ["gnt-cluster", "modify",
403
     "--enabled-storage-types=%s" %
404
       ",".join(qa_config.GetEnabledStorageTypes())],
405
    fail=False)
406
  AssertCommand(["gnt-cluster", "info"])
407
  # bogus types
408
  AssertCommand(["gnt-cluster", "modify",
409
                 "--enabled-storage-types=pinkbunny"],
410
                fail=True)
411
  # duplicate entries do no harm
412
  AssertCommand(
413
    ["gnt-cluster", "modify",
414
     "--enabled-storage-types=%s,%s" %
415
      (default_storage_type, default_storage_type)],
416
    fail=False)
417
  AssertCommand(["gnt-cluster", "info"])
418

    
419

    
420
def TestClusterModifyBe():
421
  """gnt-cluster modify -B"""
422
  for fail, cmd in [
423
    # max/min mem
424
    (False, ["gnt-cluster", "modify", "-B", "maxmem=256"]),
425
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *maxmem: 256$'"]),
426
    (False, ["gnt-cluster", "modify", "-B", "minmem=256"]),
427
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *minmem: 256$'"]),
428
    (True, ["gnt-cluster", "modify", "-B", "maxmem=a"]),
429
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *maxmem: 256$'"]),
430
    (True, ["gnt-cluster", "modify", "-B", "minmem=a"]),
431
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *minmem: 256$'"]),
432
    (False, ["gnt-cluster", "modify", "-B", "maxmem=128,minmem=128"]),
433
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *maxmem: 128$'"]),
434
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *minmem: 128$'"]),
435
    # vcpus
436
    (False, ["gnt-cluster", "modify", "-B", "vcpus=4"]),
437
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *vcpus: 4$'"]),
438
    (True, ["gnt-cluster", "modify", "-B", "vcpus=a"]),
439
    (False, ["gnt-cluster", "modify", "-B", "vcpus=1"]),
440
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *vcpus: 1$'"]),
441
    # auto_balance
442
    (False, ["gnt-cluster", "modify", "-B", "auto_balance=False"]),
443
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *auto_balance: False$'"]),
444
    (True, ["gnt-cluster", "modify", "-B", "auto_balance=1"]),
445
    (False, ["gnt-cluster", "modify", "-B", "auto_balance=True"]),
446
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *auto_balance: True$'"]),
447
    ]:
448
    AssertCommand(cmd, fail=fail)
449

    
450
  # redo the original-requested BE parameters, if any
451
  bep = qa_config.get("backend-parameters", "")
452
  if bep:
453
    AssertCommand(["gnt-cluster", "modify", "-B", bep])
454

    
455

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

459
  @rtype: tuple
460
  @return: (policy, specs), where:
461
      - policy is a dictionary of the policy values, instance specs excluded
462
      - specs is dict of dict, specs[par][key] is a spec value, where key is
463
        "min", "max", or "std"
464

465
  """
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():
473
        if par == "memory-size":
474
          par = "mem-size"
475
        d = ret_specs.setdefault(par, {})
476
        d[key] = pval
477
    else:
478
      ret_policy[key] = val
479

    
480
  # Sanity checks
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)
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

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

    
623

    
624
def TestClusterRedistConf():
625
  """gnt-cluster redist-conf"""
626
  AssertCommand(["gnt-cluster", "redist-conf"])
627

    
628

    
629
def TestClusterGetmaster():
630
  """gnt-cluster getmaster"""
631
  AssertCommand(["gnt-cluster", "getmaster"])
632

    
633

    
634
def TestClusterVersion():
635
  """gnt-cluster version"""
636
  AssertCommand(["gnt-cluster", "version"])
637

    
638

    
639
def TestClusterRenewCrypto():
640
  """gnt-cluster renew-crypto"""
641
  master = qa_config.GetMasterNode()
642

    
643
  # Conflicting options
644
  cmd = ["gnt-cluster", "renew-crypto", "--force",
645
         "--new-cluster-certificate", "--new-confd-hmac-key"]
646
  conflicting = [
647
    ["--new-rapi-certificate", "--rapi-certificate=/dev/null"],
648
    ["--new-cluster-domain-secret", "--cluster-domain-secret=/dev/null"],
649
    ]
650
  for i in conflicting:
651
    AssertCommand(cmd + i, fail=True)
652

    
653
  # Invalid RAPI certificate
654
  cmd = ["gnt-cluster", "renew-crypto", "--force",
655
         "--rapi-certificate=/dev/null"]
656
  AssertCommand(cmd, fail=True)
657

    
658
  rapi_cert_backup = qa_utils.BackupFile(master.primary,
659
                                         pathutils.RAPI_CERT_FILE)
660
  try:
661
    # Custom RAPI certificate
662
    fh = tempfile.NamedTemporaryFile()
663

    
664
    # Ensure certificate doesn't cause "gnt-cluster verify" to complain
665
    validity = constants.SSL_CERT_EXPIRATION_WARN * 3
666

    
667
    utils.GenerateSelfSignedSslCert(fh.name, validity=validity)
668

    
669
    tmpcert = qa_utils.UploadFile(master.primary, fh.name)
670
    try:
671
      AssertCommand(["gnt-cluster", "renew-crypto", "--force",
672
                     "--rapi-certificate=%s" % tmpcert])
673
    finally:
674
      AssertCommand(["rm", "-f", tmpcert])
675

    
676
    # Custom cluster domain secret
677
    cds_fh = tempfile.NamedTemporaryFile()
678
    cds_fh.write(utils.GenerateSecret())
679
    cds_fh.write("\n")
680
    cds_fh.flush()
681

    
682
    tmpcds = qa_utils.UploadFile(master.primary, cds_fh.name)
683
    try:
684
      AssertCommand(["gnt-cluster", "renew-crypto", "--force",
685
                     "--cluster-domain-secret=%s" % tmpcds])
686
    finally:
687
      AssertCommand(["rm", "-f", tmpcds])
688

    
689
    # Normal case
690
    AssertCommand(["gnt-cluster", "renew-crypto", "--force",
691
                   "--new-cluster-certificate", "--new-confd-hmac-key",
692
                   "--new-rapi-certificate", "--new-cluster-domain-secret"])
693

    
694
    # Restore RAPI certificate
695
    AssertCommand(["gnt-cluster", "renew-crypto", "--force",
696
                   "--rapi-certificate=%s" % rapi_cert_backup])
697
  finally:
698
    AssertCommand(["rm", "-f", rapi_cert_backup])
699

    
700

    
701
def TestClusterBurnin():
702
  """Burnin"""
703
  master = qa_config.GetMasterNode()
704

    
705
  options = qa_config.get("options", {})
706
  disk_template = options.get("burnin-disk-template", constants.DT_DRBD8)
707
  parallel = options.get("burnin-in-parallel", False)
708
  check_inst = options.get("burnin-check-instances", False)
709
  do_rename = options.get("burnin-rename", "")
710
  do_reboot = options.get("burnin-reboot", True)
711
  reboot_types = options.get("reboot-types", constants.REBOOT_TYPES)
712

    
713
  # Get as many instances as we need
714
  instances = []
715
  try:
716
    try:
717
      num = qa_config.get("options", {}).get("burnin-instances", 1)
718
      for _ in range(0, num):
719
        instances.append(qa_config.AcquireInstance())
720
    except qa_error.OutOfInstancesError:
721
      print "Not enough instances, continuing anyway."
722

    
723
    if len(instances) < 1:
724
      raise qa_error.Error("Burnin needs at least one instance")
725

    
726
    script = qa_utils.UploadFile(master.primary, "../tools/burnin")
727
    try:
728
      # Run burnin
729
      cmd = [script,
730
             "--os=%s" % qa_config.get("os"),
731
             "--minmem-size=%s" % qa_config.get(constants.BE_MINMEM),
732
             "--maxmem-size=%s" % qa_config.get(constants.BE_MAXMEM),
733
             "--disk-size=%s" % ",".join(qa_config.get("disk")),
734
             "--disk-growth=%s" % ",".join(qa_config.get("disk-growth")),
735
             "--disk-template=%s" % disk_template]
736
      if parallel:
737
        cmd.append("--parallel")
738
        cmd.append("--early-release")
739
      if check_inst:
740
        cmd.append("--http-check")
741
      if do_rename:
742
        cmd.append("--rename=%s" % do_rename)
743
      if not do_reboot:
744
        cmd.append("--no-reboot")
745
      else:
746
        cmd.append("--reboot-types=%s" % ",".join(reboot_types))
747
      cmd += [inst.name for inst in instances]
748
      AssertCommand(cmd)
749
    finally:
750
      AssertCommand(["rm", "-f", script])
751

    
752
  finally:
753
    for inst in instances:
754
      inst.Release()
755

    
756

    
757
def TestClusterMasterFailover():
758
  """gnt-cluster master-failover"""
759
  master = qa_config.GetMasterNode()
760
  failovermaster = qa_config.AcquireNode(exclude=master)
761

    
762
  cmd = ["gnt-cluster", "master-failover"]
763
  try:
764
    AssertCommand(cmd, node=failovermaster)
765
    # Back to original master node
766
    AssertCommand(cmd, node=master)
767
  finally:
768
    failovermaster.Release()
769

    
770

    
771
def _NodeQueueDrainFile(node):
772
  """Returns path to queue drain file for a node.
773

774
  """
775
  return qa_utils.MakeNodePath(node, pathutils.JOB_QUEUE_DRAIN_FILE)
776

    
777

    
778
def _AssertDrainFile(node, **kwargs):
779
  """Checks for the queue drain file.
780

781
  """
782
  AssertCommand(["test", "-f", _NodeQueueDrainFile(node)], node=node, **kwargs)
783

    
784

    
785
def TestClusterMasterFailoverWithDrainedQueue():
786
  """gnt-cluster master-failover with drained queue"""
787
  master = qa_config.GetMasterNode()
788
  failovermaster = qa_config.AcquireNode(exclude=master)
789

    
790
  # Ensure queue is not drained
791
  for node in [master, failovermaster]:
792
    _AssertDrainFile(node, fail=True)
793

    
794
  # Drain queue on failover master
795
  AssertCommand(["touch", _NodeQueueDrainFile(failovermaster)],
796
                node=failovermaster)
797

    
798
  cmd = ["gnt-cluster", "master-failover"]
799
  try:
800
    _AssertDrainFile(failovermaster)
801
    AssertCommand(cmd, node=failovermaster)
802
    _AssertDrainFile(master, fail=True)
803
    _AssertDrainFile(failovermaster, fail=True)
804

    
805
    # Back to original master node
806
    AssertCommand(cmd, node=master)
807
  finally:
808
    failovermaster.Release()
809

    
810
  # Ensure queue is not drained
811
  for node in [master, failovermaster]:
812
    _AssertDrainFile(node, fail=True)
813

    
814

    
815
def TestClusterCopyfile():
816
  """gnt-cluster copyfile"""
817
  master = qa_config.GetMasterNode()
818

    
819
  uniqueid = utils.NewUUID()
820

    
821
  # Create temporary file
822
  f = tempfile.NamedTemporaryFile()
823
  f.write(uniqueid)
824
  f.flush()
825
  f.seek(0)
826

    
827
  # Upload file to master node
828
  testname = qa_utils.UploadFile(master.primary, f.name)
829
  try:
830
    # Copy file to all nodes
831
    AssertCommand(["gnt-cluster", "copyfile", testname])
832
    _CheckFileOnAllNodes(testname, uniqueid)
833
  finally:
834
    _RemoveFileFromAllNodes(testname)
835

    
836

    
837
def TestClusterCommand():
838
  """gnt-cluster command"""
839
  uniqueid = utils.NewUUID()
840
  rfile = "/tmp/gnt%s" % utils.NewUUID()
841
  rcmd = utils.ShellQuoteArgs(["echo", "-n", uniqueid])
842
  cmd = utils.ShellQuoteArgs(["gnt-cluster", "command",
843
                              "%s >%s" % (rcmd, rfile)])
844

    
845
  try:
846
    AssertCommand(cmd)
847
    _CheckFileOnAllNodes(rfile, uniqueid)
848
  finally:
849
    _RemoveFileFromAllNodes(rfile)
850

    
851

    
852
def TestClusterDestroy():
853
  """gnt-cluster destroy"""
854
  AssertCommand(["gnt-cluster", "destroy", "--yes-do-it"])
855

    
856

    
857
def TestClusterRepairDiskSizes():
858
  """gnt-cluster repair-disk-sizes"""
859
  AssertCommand(["gnt-cluster", "repair-disk-sizes"])
860

    
861

    
862
def TestSetExclStorCluster(newvalue):
863
  """Set the exclusive_storage node parameter at the cluster level.
864

865
  @type newvalue: bool
866
  @param newvalue: New value of exclusive_storage
867
  @rtype: bool
868
  @return: The old value of exclusive_storage
869

870
  """
871
  es_path = ["Default node parameters", "exclusive_storage"]
872
  oldvalue = _GetClusterField(es_path)
873
  AssertCommand(["gnt-cluster", "modify", "--node-parameters",
874
                 "exclusive_storage=%s" % newvalue])
875
  effvalue = _GetClusterField(es_path)
876
  if effvalue != newvalue:
877
    raise qa_error.Error("exclusive_storage has the wrong value: %s instead"
878
                         " of %s" % (effvalue, newvalue))
879
  qa_config.SetExclusiveStorage(newvalue)
880
  return oldvalue
881

    
882

    
883
def TestExclStorSharedPv(node):
884
  """cluster-verify reports LVs that share the same PV with exclusive_storage.
885

886
  """
887
  vgname = qa_config.get("vg-name", constants.DEFAULT_VG)
888
  lvname1 = _QA_LV_PREFIX + "vol1"
889
  lvname2 = _QA_LV_PREFIX + "vol2"
890
  node_name = node.primary
891
  AssertCommand(["lvcreate", "-L1G", "-n", lvname1, vgname], node=node_name)
892
  AssertClusterVerify(fail=True, errors=[constants.CV_ENODEORPHANLV])
893
  AssertCommand(["lvcreate", "-L1G", "-n", lvname2, vgname], node=node_name)
894
  AssertClusterVerify(fail=True, errors=[constants.CV_ENODELVM,
895
                                         constants.CV_ENODEORPHANLV])
896
  AssertCommand(["lvremove", "-f", "/".join([vgname, lvname1])], node=node_name)
897
  AssertCommand(["lvremove", "-f", "/".join([vgname, lvname2])], node=node_name)
898
  AssertClusterVerify()