Statistics
| Branch: | Tag: | Revision:

root / qa / qa_cluster.py @ e118deb1

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 = all("min" in d and "std" in d and "max" in d
483
             for d in ret_specs.values())
484
  assert good, "Missing item in specs: %s" % ret_specs
485
  assert len(ret_policy) > 0
486
  return (ret_policy, ret_specs)
487

    
488

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

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

    
545

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

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

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

    
589

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

    
619

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

    
624

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

    
629

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

    
634

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

    
639

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

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

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

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

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

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

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

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

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

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

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

    
701

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

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

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

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

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

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

    
757

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

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

    
771

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

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

    
778

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

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

    
785

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

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

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

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

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

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

    
815

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

    
820
  uniqueid = utils.NewUUID()
821

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

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

    
837

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

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

    
852

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

    
857

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

    
862

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

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

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

    
883

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

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