Statistics
| Branch: | Tag: | Revision:

root / qa / qa_cluster.py @ 2c88200b

History | View | Annotate | Download (42.7 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_daemon
37
import qa_utils
38
import qa_error
39
import qa_instance
40

    
41
from qa_utils import AssertEqual, AssertCommand, GetCommandOutput
42

    
43

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

    
47
#: cluster verify command
48
_CLUSTER_VERIFY = ["gnt-cluster", "verify"]
49

    
50

    
51
def _RemoveFileFromAllNodes(filename):
52
  """Removes a file from all nodes.
53

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

    
58

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

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

    
67

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

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

77
  """
78
  assert isinstance(field_path, list)
79
  assert field_path
80
  ret = qa_utils.GetObjectInfo(["gnt-cluster", "info"])
81
  for key in field_path:
82
    ret = ret[key]
83
  return ret
84

    
85

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

    
89

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

    
104

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

    
112

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

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

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

    
139

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

    
146

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

    
153

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

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

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

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

    
176
  # Initialize cluster
177
  enabled_disk_templates = qa_config.GetEnabledDiskTemplates()
178
  cmd = [
179
    "gnt-cluster", "init",
180
    "--primary-ip-version=%d" % qa_config.get("primary_ip_version", 4),
181
    "--enabled-hypervisors=%s" % ",".join(qa_config.GetEnabledHypervisors()),
182
    "--enabled-disk-templates=%s" %
183
      ",".join(enabled_disk_templates),
184
    ]
185
  if constants.DT_FILE in enabled_disk_templates:
186
    cmd.append(
187
        "--file-storage-dir=%s" %
188
        qa_config.get("default-file-storage-dir",
189
                      pathutils.DEFAULT_FILE_STORAGE_DIR))
190

    
191
  for spec_type in ("mem-size", "disk-size", "disk-count", "cpu-count",
192
                    "nic-count"):
193
    for spec_val in ("min", "max", "std"):
194
      spec = qa_config.get("ispec_%s_%s" %
195
                           (spec_type.replace("-", "_"), spec_val), None)
196
      if spec is not None:
197
        cmd.append("--specs-%s=%s=%d" % (spec_type, spec_val, spec))
198

    
199
  if master.secondary:
200
    cmd.append("--secondary-ip=%s" % master.secondary)
201

    
202
  if utils.IsLvmEnabled(qa_config.GetEnabledDiskTemplates()):
203
    vgname = qa_config.get("vg-name", constants.DEFAULT_VG)
204
    if vgname:
205
      cmd.append("--vg-name=%s" % vgname)
206
    else:
207
      raise qa_error.Error("Please specify a volume group if you enable"
208
                           " lvm-based disk templates in the QA.")
209

    
210
  master_netdev = qa_config.get("master-netdev", None)
211
  if master_netdev:
212
    cmd.append("--master-netdev=%s" % master_netdev)
213

    
214
  nicparams = qa_config.get("default-nicparams", None)
215
  if nicparams:
216
    cmd.append("--nic-parameters=%s" %
217
               ",".join(utils.FormatKeyValue(nicparams)))
218

    
219
  # Cluster value of the exclusive-storage node parameter
220
  e_s = qa_config.get("exclusive-storage")
221
  if e_s is not None:
222
    cmd.extend(["--node-parameters", "exclusive_storage=%s" % e_s])
223
  else:
224
    e_s = False
225
  qa_config.SetExclusiveStorage(e_s)
226

    
227
  extra_args = qa_config.get("cluster-init-args")
228

    
229
  if extra_args:
230
    # This option was removed in 2.10, but in order to not break QA of older
231
    # branches we remove it from the extra_args if it is in there.
232
    opt_drbd_storage = "--no-drbd-storage"
233
    if opt_drbd_storage in extra_args:
234
      extra_args.remove(opt_drbd_storage)
235
    cmd.extend(extra_args)
236

    
237
  cmd.append(qa_config.get("name"))
238

    
239
  AssertCommand(cmd)
240

    
241
  cmd = ["gnt-cluster", "modify"]
242

    
243
  # hypervisor parameter modifications
244
  hvp = qa_config.get("hypervisor-parameters", {})
245
  for k, v in hvp.items():
246
    cmd.extend(["-H", "%s:%s" % (k, v)])
247
  # backend parameter modifications
248
  bep = qa_config.get("backend-parameters", "")
249
  if bep:
250
    cmd.extend(["-B", bep])
251

    
252
  if len(cmd) > 2:
253
    AssertCommand(cmd)
254

    
255
  # OS parameters
256
  osp = qa_config.get("os-parameters", {})
257
  for k, v in osp.items():
258
    AssertCommand(["gnt-os", "modify", "-O", v, k])
259

    
260
  # OS hypervisor parameters
261
  os_hvp = qa_config.get("os-hvp", {})
262
  for os_name in os_hvp:
263
    for hv, hvp in os_hvp[os_name].items():
264
      AssertCommand(["gnt-os", "modify", "-H", "%s:%s" % (hv, hvp), os_name])
265

    
266

    
267
def TestClusterRename():
268
  """gnt-cluster rename"""
269
  cmd = ["gnt-cluster", "rename", "-f"]
270

    
271
  original_name = qa_config.get("name")
272
  rename_target = qa_config.get("rename", None)
273
  if rename_target is None:
274
    print qa_utils.FormatError('"rename" entry is missing')
275
    return
276

    
277
  for data in [
278
    cmd + [rename_target],
279
    _CLUSTER_VERIFY,
280
    cmd + [original_name],
281
    _CLUSTER_VERIFY,
282
    ]:
283
    AssertCommand(data)
284

    
285

    
286
def TestClusterOob():
287
  """out-of-band framework"""
288
  oob_path_exists = "/tmp/ganeti-qa-oob-does-exist-%s" % utils.NewUUID()
289

    
290
  AssertCommand(_CLUSTER_VERIFY)
291
  AssertCommand(["gnt-cluster", "modify", "--node-parameters",
292
                 "oob_program=/tmp/ganeti-qa-oob-does-not-exist-%s" %
293
                 utils.NewUUID()])
294

    
295
  AssertCommand(_CLUSTER_VERIFY, fail=True)
296

    
297
  AssertCommand(["touch", oob_path_exists])
298
  AssertCommand(["chmod", "0400", oob_path_exists])
299
  AssertCommand(["gnt-cluster", "copyfile", oob_path_exists])
300

    
301
  try:
302
    AssertCommand(["gnt-cluster", "modify", "--node-parameters",
303
                   "oob_program=%s" % oob_path_exists])
304

    
305
    AssertCommand(_CLUSTER_VERIFY, fail=True)
306

    
307
    AssertCommand(["chmod", "0500", oob_path_exists])
308
    AssertCommand(["gnt-cluster", "copyfile", oob_path_exists])
309

    
310
    AssertCommand(_CLUSTER_VERIFY)
311
  finally:
312
    AssertCommand(["gnt-cluster", "command", "rm", oob_path_exists])
313

    
314
  AssertCommand(["gnt-cluster", "modify", "--node-parameters",
315
                 "oob_program="])
316

    
317

    
318
def TestClusterEpo():
319
  """gnt-cluster epo"""
320
  master = qa_config.GetMasterNode()
321

    
322
  # Assert that OOB is unavailable for all nodes
323
  result_output = GetCommandOutput(master.primary,
324
                                   "gnt-node list --verbose --no-headers -o"
325
                                   " powered")
326
  AssertEqual(compat.all(powered == "(unavail)"
327
                         for powered in result_output.splitlines()), True)
328

    
329
  # Conflicting
330
  AssertCommand(["gnt-cluster", "epo", "--groups", "--all"], fail=True)
331
  # --all doesn't expect arguments
332
  AssertCommand(["gnt-cluster", "epo", "--all", "some_arg"], fail=True)
333

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

    
337
  # This shouldn't fail
338
  AssertCommand(["gnt-cluster", "epo", "-f", "--all"])
339

    
340
  # All instances should have been stopped now
341
  result_output = GetCommandOutput(master.primary,
342
                                   "gnt-instance list --no-headers -o status")
343
  # ERROR_down because the instance is stopped but not recorded as such
344
  AssertEqual(compat.all(status == "ERROR_down"
345
                         for status in result_output.splitlines()), True)
346

    
347
  # Now start everything again
348
  AssertCommand(["gnt-cluster", "epo", "--on", "-f", "--all"])
349

    
350
  # All instances should have been started now
351
  result_output = GetCommandOutput(master.primary,
352
                                   "gnt-instance list --no-headers -o status")
353
  AssertEqual(compat.all(status == "running"
354
                         for status in result_output.splitlines()), True)
355

    
356

    
357
def TestClusterVerify():
358
  """gnt-cluster verify"""
359
  AssertCommand(_CLUSTER_VERIFY)
360
  AssertCommand(["gnt-cluster", "verify-disks"])
361

    
362

    
363
def TestClusterVerifyDisksBrokenDRBD(instance, inst_nodes):
364
  """gnt-cluster verify-disks with broken DRBD"""
365
  qa_daemon.TestPauseWatcher()
366

    
367
  try:
368
    info = qa_instance.GetInstanceInfo(instance.name)
369
    snode = inst_nodes[1]
370
    for idx, minor in enumerate(info["drbd-minors"][snode.primary]):
371
      if idx % 2 == 0:
372
        break_drbd_cmd = \
373
          "(drbdsetup %d down >/dev/null 2>&1;" \
374
          " drbdsetup down resource%d >/dev/null 2>&1) || /bin/true" % \
375
          (minor, minor)
376
      else:
377
        break_drbd_cmd = \
378
          "(drbdsetup %d detach >/dev/null 2>&1;" \
379
          " drbdsetup detach %d >/dev/null 2>&1) || /bin/true" % \
380
          (minor, minor)
381
      AssertCommand(break_drbd_cmd, node=snode)
382

    
383
    verify_output = GetCommandOutput(qa_config.GetMasterNode().primary,
384
                                     "gnt-cluster verify-disks")
385
    activation_msg = "Activating disks for instance '%s'" % instance.name
386
    if activation_msg not in verify_output:
387
      raise qa_error.Error("gnt-cluster verify-disks did not activate broken"
388
                           " DRBD disks:\n%s" % verify_output)
389

    
390
    verify_output = GetCommandOutput(qa_config.GetMasterNode().primary,
391
                                     "gnt-cluster verify-disks")
392
    if activation_msg in verify_output:
393
      raise qa_error.Error("gnt-cluster verify-disks wants to activate broken"
394
                           " DRBD disks on second attempt:\n%s" % verify_output)
395

    
396
    AssertCommand(_CLUSTER_VERIFY)
397
  finally:
398
    qa_daemon.TestResumeWatcher()
399

    
400

    
401
def TestJobqueue():
402
  """gnt-debug test-jobqueue"""
403
  AssertCommand(["gnt-debug", "test-jobqueue"])
404

    
405

    
406
def TestDelay(node):
407
  """gnt-debug delay"""
408
  AssertCommand(["gnt-debug", "delay", "1"])
409
  AssertCommand(["gnt-debug", "delay", "--no-master", "1"])
410
  AssertCommand(["gnt-debug", "delay", "--no-master",
411
                 "-n", node.primary, "1"])
412

    
413

    
414
def TestClusterReservedLvs():
415
  """gnt-cluster reserved lvs"""
416
  # if no lvm-based templates are supported, skip the test
417
  if not qa_config.IsStorageTypeSupported(constants.ST_LVM_VG):
418
    return
419
  vgname = qa_config.get("vg-name", constants.DEFAULT_VG)
420
  lvname = _QA_LV_PREFIX + "test"
421
  lvfullname = "/".join([vgname, lvname])
422
  for fail, cmd in [
423
    (False, _CLUSTER_VERIFY),
424
    (False, ["gnt-cluster", "modify", "--reserved-lvs", ""]),
425
    (False, ["lvcreate", "-L1G", "-n", lvname, vgname]),
426
    (True, _CLUSTER_VERIFY),
427
    (False, ["gnt-cluster", "modify", "--reserved-lvs",
428
             "%s,.*/other-test" % lvfullname]),
429
    (False, _CLUSTER_VERIFY),
430
    (False, ["gnt-cluster", "modify", "--reserved-lvs",
431
             ".*/%s.*" % _QA_LV_PREFIX]),
432
    (False, _CLUSTER_VERIFY),
433
    (False, ["gnt-cluster", "modify", "--reserved-lvs", ""]),
434
    (True, _CLUSTER_VERIFY),
435
    (False, ["lvremove", "-f", lvfullname]),
436
    (False, _CLUSTER_VERIFY),
437
    ]:
438
    AssertCommand(cmd, fail=fail)
439

    
440

    
441
def TestClusterModifyEmpty():
442
  """gnt-cluster modify"""
443
  AssertCommand(["gnt-cluster", "modify"], fail=True)
444

    
445

    
446
def TestClusterModifyDisk():
447
  """gnt-cluster modify -D"""
448
  for param in _FAIL_PARAMS:
449
    AssertCommand(["gnt-cluster", "modify", "-D", param], fail=True)
450

    
451

    
452
def _GetOtherEnabledDiskTemplate(undesired_disk_templates,
453
                                 enabled_disk_templates):
454
  """Returns one template that is not in the undesired set.
455

456
  @type undesired_disk_templates: list of string
457
  @param undesired_disk_templates: a list of disk templates that we want to
458
      exclude when drawing one disk template from the list of enabled
459
      disk templates
460
  @type enabled_disk_templates: list of string
461
  @param enabled_disk_templates: list of enabled disk templates (in QA)
462

463
  """
464
  desired_templates = list(set(enabled_disk_templates)
465
                                - set(undesired_disk_templates))
466
  if desired_templates:
467
    template = desired_templates[0]
468
  else:
469
    # If no desired disk template is available for QA, choose 'diskless' and
470
    # hope for the best.
471
    template = constants.ST_DISKLESS
472

    
473
  return template
474

    
475

    
476
def TestClusterModifyFileBasedStorageDir(
477
    file_disk_template, dir_config_key, default_dir, option_name):
478
  """Tests gnt-cluster modify wrt to file-based directory options.
479

480
  @type file_disk_template: string
481
  @param file_disk_template: file-based disk template
482
  @type dir_config_key: string
483
  @param dir_config_key: key for the QA config to retrieve the default
484
     directory value
485
  @type default_dir: string
486
  @param default_dir: default directory, if the QA config does not specify
487
     it
488
  @type option_name: string
489
  @param option_name: name of the option of 'gnt-cluster modify' to
490
     change the directory
491

492
  """
493
  enabled_disk_templates = qa_config.GetEnabledDiskTemplates()
494
  assert file_disk_template in [constants.DT_FILE, constants.DT_SHARED_FILE]
495
  if not qa_config.IsTemplateSupported(file_disk_template):
496
    return
497

    
498
  # Get some non-file-based disk template to disable file storage
499
  other_disk_template = _GetOtherEnabledDiskTemplate(
500
      utils.storage.GetDiskTemplatesOfStorageType(constants.ST_FILE),
501
      enabled_disk_templates)
502

    
503
  file_storage_dir = qa_config.get(dir_config_key, default_dir)
504
  invalid_file_storage_dir = "/boot/"
505

    
506
  for fail, cmd in [
507
    (False, ["gnt-cluster", "modify",
508
            "--enabled-disk-templates=%s" % file_disk_template]),
509
    (False, ["gnt-cluster", "modify",
510
            "--%s=%s" % (option_name, file_storage_dir)]),
511
    (False, ["gnt-cluster", "modify",
512
            "--%s=%s" % (option_name, invalid_file_storage_dir)]),
513
    # file storage dir is set to an inacceptable path, thus verify
514
    # should fail
515
    (True, ["gnt-cluster", "verify"]),
516
    # unsetting the storage dir while file storage is enabled
517
    # should fail
518
    (True, ["gnt-cluster", "modify",
519
            "--%s=" % option_name]),
520
    (False, ["gnt-cluster", "modify",
521
            "--%s=%s" % (option_name, file_storage_dir)]),
522
    (False, ["gnt-cluster", "modify",
523
            "--enabled-disk-templates=%s" % other_disk_template]),
524
    (False, ["gnt-cluster", "modify",
525
            "--%s=%s" % (option_name, invalid_file_storage_dir)]),
526
    # file storage is set to an inacceptable path, but file storage
527
    # is disabled, thus verify should not fail
528
    (False, ["gnt-cluster", "verify"]),
529
    # unsetting the file storage dir while file storage is not enabled
530
    # should be fine
531
    (False, ["gnt-cluster", "modify",
532
            "--%s=" % option_name]),
533
    # resetting everything to sane values
534
    (False, ["gnt-cluster", "modify",
535
            "--%s=%s" % (option_name, file_storage_dir),
536
            "--enabled-disk-templates=%s" % ",".join(enabled_disk_templates)])
537
    ]:
538
    AssertCommand(cmd, fail=fail)
539

    
540

    
541
def TestClusterModifyFileStorageDir():
542
  """gnt-cluster modify --file-storage-dir=..."""
543
  TestClusterModifyFileBasedStorageDir(
544
      constants.DT_FILE, "default-file-storage-dir",
545
      pathutils.DEFAULT_FILE_STORAGE_DIR,
546
      "file-storage-dir")
547

    
548

    
549
def TestClusterModifySharedFileStorageDir():
550
  """gnt-cluster modify --shared-file-storage-dir=..."""
551
  TestClusterModifyFileBasedStorageDir(
552
      constants.DT_SHARED_FILE, "default-shared-file-storage-dir",
553
      pathutils.DEFAULT_SHARED_FILE_STORAGE_DIR,
554
      "shared-file-storage-dir")
555

    
556

    
557
def TestClusterModifyDiskTemplates():
558
  """gnt-cluster modify --enabled-disk-templates=..."""
559
  enabled_disk_templates = qa_config.GetEnabledDiskTemplates()
560
  default_disk_template = qa_config.GetDefaultDiskTemplate()
561

    
562
  _TestClusterModifyDiskTemplatesArguments(default_disk_template)
563
  _TestClusterModifyDiskTemplatesDrbdHelper(enabled_disk_templates)
564
  _TestClusterModifyDiskTemplatesVgName(enabled_disk_templates)
565

    
566
  _RestoreEnabledDiskTemplates()
567
  nodes = qa_config.AcquireManyNodes(2)
568

    
569
  instance_template = enabled_disk_templates[0]
570
  instance = qa_instance.CreateInstanceByDiskTemplate(nodes, instance_template)
571

    
572
  _TestClusterModifyUnusedDiskTemplate(instance_template)
573
  _TestClusterModifyUsedDiskTemplate(instance_template,
574
                                     enabled_disk_templates)
575

    
576
  qa_instance.TestInstanceRemove(instance)
577
  _RestoreEnabledDiskTemplates()
578

    
579

    
580
def _RestoreEnabledDiskTemplates():
581
  """Sets the list of enabled disk templates back to the list of enabled disk
582
     templates from the QA configuration. This can be used to make sure that
583
     the tests that modify the list of disk templates do not interfere with
584
     other tests.
585

586
  """
587
  enabled_disk_templates = qa_config.GetEnabledDiskTemplates()
588
  cmd = ["gnt-cluster", "modify",
589
         "--enabled-disk-templates=%s" % ",".join(enabled_disk_templates),
590
         "--ipolicy-disk-templates=%s" % ",".join(enabled_disk_templates),
591
         ]
592

    
593
  if utils.IsLvmEnabled(qa_config.GetEnabledDiskTemplates()):
594
    vgname = qa_config.get("vg-name", constants.DEFAULT_VG)
595
    cmd.append("--vg-name=%s" % vgname)
596

    
597
  AssertCommand(cmd, fail=False)
598

    
599

    
600
def _TestClusterModifyDiskTemplatesDrbdHelper(enabled_disk_templates):
601
  """Tests argument handling of 'gnt-cluster modify' with respect to
602
     the parameter '--drbd-usermode-helper'. This test is independent
603
     of instances.
604

605
  """
606
  _RestoreEnabledDiskTemplates()
607

    
608
  if constants.DT_DRBD8 not in enabled_disk_templates:
609
    return
610
  if constants.DT_PLAIN not in enabled_disk_templates:
611
    return
612

    
613
  drbd_usermode_helper = qa_config.get("drbd-usermode-helper", "/bin/true")
614
  bogus_usermode_helper = "/tmp/pinkbunny"
615
  for command, fail in \
616
      [(["gnt-cluster", "modify",
617
         "--enabled-disk-templates=%s" % constants.DT_DRBD8,
618
         "--ipolicy-disk-templates=%s" % constants.DT_DRBD8], False),
619
       (["gnt-cluster", "modify",
620
         "--drbd-usermode-helper=%s" % drbd_usermode_helper], False),
621
       (["gnt-cluster", "modify",
622
         "--drbd-usermode-helper=%s" % bogus_usermode_helper], True),
623
       # unsetting helper when DRBD is enabled should not work
624
       (["gnt-cluster", "modify",
625
         "--drbd-usermode-helper="], True),
626
       (["gnt-cluster", "modify",
627
         "--enabled-disk-templates=%s" % constants.DT_PLAIN,
628
         "--ipolicy-disk-templates=%s" % constants.DT_PLAIN], False),
629
       (["gnt-cluster", "modify",
630
         "--drbd-usermode-helper="], False),
631
       (["gnt-cluster", "modify",
632
         "--drbd-usermode-helper=%s" % drbd_usermode_helper], False),
633
       (["gnt-cluster", "modify",
634
         "--drbd-usermode-helper=%s" % drbd_usermode_helper,
635
         "--enabled-disk-templates=%s" % constants.DT_DRBD8,
636
         "--ipolicy-disk-templates=%s" % constants.DT_DRBD8], False),
637
       (["gnt-cluster", "modify",
638
         "--drbd-usermode-helper=",
639
         "--enabled-disk-templates=%s" % constants.DT_PLAIN,
640
         "--ipolicy-disk-templates=%s" % constants.DT_PLAIN], False),
641
       (["gnt-cluster", "modify",
642
         "--drbd-usermode-helper=%s" % drbd_usermode_helper,
643
         "--enabled-disk-templates=%s" % constants.DT_DRBD8,
644
         "--ipolicy-disk-templates=%s" % constants.DT_DRBD8], False),
645
      ]:
646
    AssertCommand(command, fail=fail)
647
  _RestoreEnabledDiskTemplates()
648

    
649

    
650
def _TestClusterModifyDiskTemplatesArguments(default_disk_template):
651
  """Tests argument handling of 'gnt-cluster modify' with respect to
652
     the parameter '--enabled-disk-templates'. This test is independent
653
     of instances.
654

655
  """
656
  _RestoreEnabledDiskTemplates()
657

    
658
  # bogus templates
659
  AssertCommand(["gnt-cluster", "modify",
660
                 "--enabled-disk-templates=pinkbunny"],
661
                fail=True)
662

    
663
  # duplicate entries do no harm
664
  AssertCommand(
665
    ["gnt-cluster", "modify",
666
     "--enabled-disk-templates=%s,%s" %
667
      (default_disk_template, default_disk_template),
668
     "--ipolicy-disk-templates=%s" % default_disk_template],
669
    fail=False)
670

    
671

    
672
def _TestClusterModifyDiskTemplatesVgName(enabled_disk_templates):
673
  """Tests argument handling of 'gnt-cluster modify' with respect to
674
     the parameter '--enabled-disk-templates' and '--vg-name'. This test is
675
     independent of instances.
676

677
  """
678
  if not utils.IsLvmEnabled(enabled_disk_templates):
679
    # These tests only make sense if lvm is enabled for QA
680
    return
681

    
682
  # determine an LVM and a non-LVM disk template for the tests
683
  non_lvm_template = _GetOtherEnabledDiskTemplate(utils.GetLvmDiskTemplates(),
684
                                                  enabled_disk_templates)
685
  lvm_template = list(set(enabled_disk_templates)
686
                      .intersection(set(utils.GetLvmDiskTemplates())))[0]
687

    
688
  vgname = qa_config.get("vg-name", constants.DEFAULT_VG)
689

    
690
  # Clean start: unset volume group name, disable lvm storage
691
  AssertCommand(
692
    ["gnt-cluster", "modify",
693
     "--enabled-disk-templates=%s" % non_lvm_template,
694
     "--ipolicy-disk-templates=%s" % non_lvm_template,
695
     "--vg-name="],
696
    fail=False)
697

    
698
  # Try to enable lvm, when no volume group is given
699
  AssertCommand(
700
    ["gnt-cluster", "modify",
701
     "--enabled-disk-templates=%s" % lvm_template,
702
     "--ipolicy-disk-templates=%s" % lvm_template],
703
    fail=True)
704

    
705
  # Set volume group, with lvm still disabled: just a warning
706
  AssertCommand(["gnt-cluster", "modify", "--vg-name=%s" % vgname], fail=False)
707

    
708
  # Try unsetting vg name and enabling lvm at the same time
709
  AssertCommand(
710
    ["gnt-cluster", "modify",
711
     "--enabled-disk-templates=%s" % lvm_template,
712
     "--ipolicy-disk-templates=%s" % lvm_template,
713
     "--vg-name="],
714
    fail=True)
715

    
716
  # Enable lvm with vg name present
717
  AssertCommand(
718
    ["gnt-cluster", "modify",
719
     "--enabled-disk-templates=%s" % lvm_template,
720
     "--ipolicy-disk-templates=%s" % lvm_template],
721
    fail=False)
722

    
723
  # Try unsetting vg name with lvm still enabled
724
  AssertCommand(["gnt-cluster", "modify", "--vg-name="], fail=True)
725

    
726
  # Disable lvm with vg name still set
727
  AssertCommand(
728
    ["gnt-cluster", "modify",
729
     "--enabled-disk-templates=%s" % non_lvm_template,
730
     "--ipolicy-disk-templates=%s" % non_lvm_template,
731
     ],
732
    fail=False)
733

    
734
  # Try unsetting vg name with lvm disabled
735
  AssertCommand(["gnt-cluster", "modify", "--vg-name="], fail=False)
736

    
737
  # Set vg name and enable lvm at the same time
738
  AssertCommand(
739
    ["gnt-cluster", "modify",
740
     "--enabled-disk-templates=%s" % lvm_template,
741
     "--ipolicy-disk-templates=%s" % lvm_template,
742
     "--vg-name=%s" % vgname],
743
    fail=False)
744

    
745
  # Unset vg name and disable lvm at the same time
746
  AssertCommand(
747
    ["gnt-cluster", "modify",
748
     "--enabled-disk-templates=%s" % non_lvm_template,
749
     "--ipolicy-disk-templates=%s" % non_lvm_template,
750
     "--vg-name="],
751
    fail=False)
752

    
753
  _RestoreEnabledDiskTemplates()
754

    
755

    
756
def _TestClusterModifyUsedDiskTemplate(instance_template,
757
                                       enabled_disk_templates):
758
  """Tests that disk templates that are currently in use by instances cannot
759
     be disabled on the cluster.
760

761
  """
762
  # If the list of enabled disk templates contains only one template
763
  # we need to add some other templates, because the list of enabled disk
764
  # templates can only be set to a non-empty list.
765
  new_disk_templates = list(set(enabled_disk_templates)
766
                              - set([instance_template]))
767
  if not new_disk_templates:
768
    new_disk_templates = list(set([constants.DT_DISKLESS, constants.DT_BLOCK])
769
                                - set([instance_template]))
770
  AssertCommand(
771
    ["gnt-cluster", "modify",
772
     "--enabled-disk-templates=%s" % ",".join(new_disk_templates),
773
     "--ipolicy-disk-templates=%s" % ",".join(new_disk_templates)],
774
    fail=True)
775

    
776

    
777
def _TestClusterModifyUnusedDiskTemplate(instance_template):
778
  """Tests that unused disk templates can be disabled safely."""
779
  all_disk_templates = constants.DISK_TEMPLATES
780
  if not utils.IsLvmEnabled(qa_config.GetEnabledDiskTemplates()):
781
    all_disk_templates = list(set(all_disk_templates) -
782
                              set(utils.GetLvmDiskTemplates()))
783

    
784
  AssertCommand(
785
    ["gnt-cluster", "modify",
786
     "--enabled-disk-templates=%s" % ",".join(all_disk_templates),
787
     "--ipolicy-disk-templates=%s" % ",".join(all_disk_templates)],
788
    fail=False)
789
  new_disk_templates = [instance_template]
790
  AssertCommand(
791
    ["gnt-cluster", "modify",
792
     "--enabled-disk-templates=%s" % ",".join(new_disk_templates),
793
     "--ipolicy-disk-templates=%s" % ",".join(new_disk_templates)],
794
    fail=False)
795

    
796

    
797
def TestClusterModifyBe():
798
  """gnt-cluster modify -B"""
799
  for fail, cmd in [
800
    # max/min mem
801
    (False, ["gnt-cluster", "modify", "-B", "maxmem=256"]),
802
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *maxmem: 256$'"]),
803
    (False, ["gnt-cluster", "modify", "-B", "minmem=256"]),
804
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *minmem: 256$'"]),
805
    (True, ["gnt-cluster", "modify", "-B", "maxmem=a"]),
806
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *maxmem: 256$'"]),
807
    (True, ["gnt-cluster", "modify", "-B", "minmem=a"]),
808
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *minmem: 256$'"]),
809
    (False, ["gnt-cluster", "modify", "-B", "maxmem=128,minmem=128"]),
810
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *maxmem: 128$'"]),
811
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *minmem: 128$'"]),
812
    # vcpus
813
    (False, ["gnt-cluster", "modify", "-B", "vcpus=4"]),
814
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *vcpus: 4$'"]),
815
    (True, ["gnt-cluster", "modify", "-B", "vcpus=a"]),
816
    (False, ["gnt-cluster", "modify", "-B", "vcpus=1"]),
817
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *vcpus: 1$'"]),
818
    # auto_balance
819
    (False, ["gnt-cluster", "modify", "-B", "auto_balance=False"]),
820
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *auto_balance: False$'"]),
821
    (True, ["gnt-cluster", "modify", "-B", "auto_balance=1"]),
822
    (False, ["gnt-cluster", "modify", "-B", "auto_balance=True"]),
823
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *auto_balance: True$'"]),
824
    ]:
825
    AssertCommand(cmd, fail=fail)
826

    
827
  # redo the original-requested BE parameters, if any
828
  bep = qa_config.get("backend-parameters", "")
829
  if bep:
830
    AssertCommand(["gnt-cluster", "modify", "-B", bep])
831

    
832

    
833
def _GetClusterIPolicy():
834
  """Return the run-time values of the cluster-level instance policy.
835

836
  @rtype: tuple
837
  @return: (policy, specs), where:
838
      - policy is a dictionary of the policy values, instance specs excluded
839
      - specs is a dictionary containing only the specs, using the internal
840
        format (see L{constants.IPOLICY_DEFAULTS} for an example)
841

842
  """
843
  info = qa_utils.GetObjectInfo(["gnt-cluster", "info"])
844
  policy = info["Instance policy - limits for instances"]
845
  (ret_policy, ret_specs) = qa_utils.ParseIPolicy(policy)
846

    
847
  # Sanity checks
848
  assert "minmax" in ret_specs and "std" in ret_specs
849
  assert len(ret_specs["minmax"]) > 0
850
  assert len(ret_policy) > 0
851
  return (ret_policy, ret_specs)
852

    
853

    
854
def TestClusterModifyIPolicy():
855
  """gnt-cluster modify --ipolicy-*"""
856
  basecmd = ["gnt-cluster", "modify"]
857
  (old_policy, old_specs) = _GetClusterIPolicy()
858
  for par in ["vcpu-ratio", "spindle-ratio"]:
859
    curr_val = float(old_policy[par])
860
    test_values = [
861
      (True, 1.0),
862
      (True, 1.5),
863
      (True, 2),
864
      (False, "a"),
865
      # Restore the old value
866
      (True, curr_val),
867
      ]
868
    for (good, val) in test_values:
869
      cmd = basecmd + ["--ipolicy-%s=%s" % (par, val)]
870
      AssertCommand(cmd, fail=not good)
871
      if good:
872
        curr_val = val
873
      # Check the affected parameter
874
      (eff_policy, eff_specs) = _GetClusterIPolicy()
875
      AssertEqual(float(eff_policy[par]), curr_val)
876
      # Check everything else
877
      AssertEqual(eff_specs, old_specs)
878
      for p in eff_policy.keys():
879
        if p == par:
880
          continue
881
        AssertEqual(eff_policy[p], old_policy[p])
882

    
883
  # Allowing disk templates via ipolicy requires them to be
884
  # enabled on the cluster.
885
  if not (qa_config.IsTemplateSupported(constants.DT_PLAIN)
886
          and qa_config.IsTemplateSupported(constants.DT_DRBD8)):
887
    return
888
  # Disk templates are treated slightly differently
889
  par = "disk-templates"
890
  disp_str = "allowed disk templates"
891
  curr_val = old_policy[disp_str]
892
  test_values = [
893
    (True, constants.DT_PLAIN),
894
    (True, "%s,%s" % (constants.DT_PLAIN, constants.DT_DRBD8)),
895
    (False, "thisisnotadisktemplate"),
896
    (False, ""),
897
    # Restore the old value
898
    (True, curr_val.replace(" ", "")),
899
    ]
900
  for (good, val) in test_values:
901
    cmd = basecmd + ["--ipolicy-%s=%s" % (par, val)]
902
    AssertCommand(cmd, fail=not good)
903
    if good:
904
      curr_val = val
905
    # Check the affected parameter
906
    (eff_policy, eff_specs) = _GetClusterIPolicy()
907
    AssertEqual(eff_policy[disp_str].replace(" ", ""), curr_val)
908
    # Check everything else
909
    AssertEqual(eff_specs, old_specs)
910
    for p in eff_policy.keys():
911
      if p == disp_str:
912
        continue
913
      AssertEqual(eff_policy[p], old_policy[p])
914

    
915

    
916
def TestClusterSetISpecs(new_specs=None, diff_specs=None, fail=False,
917
                         old_values=None):
918
  """Change instance specs.
919

920
  At most one of new_specs or diff_specs can be specified.
921

922
  @type new_specs: dict
923
  @param new_specs: new complete specs, in the same format returned by
924
      L{_GetClusterIPolicy}
925
  @type diff_specs: dict
926
  @param diff_specs: partial specs, it can be an incomplete specifications, but
927
      if min/max specs are specified, their number must match the number of the
928
      existing specs
929
  @type fail: bool
930
  @param fail: if the change is expected to fail
931
  @type old_values: tuple
932
  @param old_values: (old_policy, old_specs), as returned by
933
      L{_GetClusterIPolicy}
934
  @return: same as L{_GetClusterIPolicy}
935

936
  """
937
  build_cmd = lambda opts: ["gnt-cluster", "modify"] + opts
938
  return qa_utils.TestSetISpecs(
939
    new_specs=new_specs, diff_specs=diff_specs,
940
    get_policy_fn=_GetClusterIPolicy, build_cmd_fn=build_cmd,
941
    fail=fail, old_values=old_values)
942

    
943

    
944
def TestClusterModifyISpecs():
945
  """gnt-cluster modify --specs-*"""
946
  params = ["memory-size", "disk-size", "disk-count", "cpu-count", "nic-count"]
947
  (cur_policy, cur_specs) = _GetClusterIPolicy()
948
  # This test assumes that there is only one min/max bound
949
  assert len(cur_specs[constants.ISPECS_MINMAX]) == 1
950
  for par in params:
951
    test_values = [
952
      (True, 0, 4, 12),
953
      (True, 4, 4, 12),
954
      (True, 4, 12, 12),
955
      (True, 4, 4, 4),
956
      (False, 4, 0, 12),
957
      (False, 4, 16, 12),
958
      (False, 4, 4, 0),
959
      (False, 12, 4, 4),
960
      (False, 12, 4, 0),
961
      (False, "a", 4, 12),
962
      (False, 0, "a", 12),
963
      (False, 0, 4, "a"),
964
      # This is to restore the old values
965
      (True,
966
       cur_specs[constants.ISPECS_MINMAX][0][constants.ISPECS_MIN][par],
967
       cur_specs[constants.ISPECS_STD][par],
968
       cur_specs[constants.ISPECS_MINMAX][0][constants.ISPECS_MAX][par])
969
      ]
970
    for (good, mn, st, mx) in test_values:
971
      new_vals = {
972
        constants.ISPECS_MINMAX: [{
973
          constants.ISPECS_MIN: {par: mn},
974
          constants.ISPECS_MAX: {par: mx}
975
          }],
976
        constants.ISPECS_STD: {par: st}
977
        }
978
      cur_state = (cur_policy, cur_specs)
979
      # We update cur_specs, as we've copied the values to restore already
980
      (cur_policy, cur_specs) = TestClusterSetISpecs(
981
        diff_specs=new_vals, fail=not good, old_values=cur_state)
982

    
983
    # Get the ipolicy command
984
    mnode = qa_config.GetMasterNode()
985
    initcmd = GetCommandOutput(mnode.primary, "gnt-cluster show-ispecs-cmd")
986
    modcmd = ["gnt-cluster", "modify"]
987
    opts = initcmd.split()
988
    assert opts[0:2] == ["gnt-cluster", "init"]
989
    for k in range(2, len(opts) - 1):
990
      if opts[k].startswith("--ipolicy-"):
991
        assert k + 2 <= len(opts)
992
        modcmd.extend(opts[k:k + 2])
993
    # Re-apply the ipolicy (this should be a no-op)
994
    AssertCommand(modcmd)
995
    new_initcmd = GetCommandOutput(mnode.primary, "gnt-cluster show-ispecs-cmd")
996
    AssertEqual(initcmd, new_initcmd)
997

    
998

    
999
def TestClusterInfo():
1000
  """gnt-cluster info"""
1001
  AssertCommand(["gnt-cluster", "info"])
1002

    
1003

    
1004
def TestClusterRedistConf():
1005
  """gnt-cluster redist-conf"""
1006
  AssertCommand(["gnt-cluster", "redist-conf"])
1007

    
1008

    
1009
def TestClusterGetmaster():
1010
  """gnt-cluster getmaster"""
1011
  AssertCommand(["gnt-cluster", "getmaster"])
1012

    
1013

    
1014
def TestClusterVersion():
1015
  """gnt-cluster version"""
1016
  AssertCommand(["gnt-cluster", "version"])
1017

    
1018

    
1019
def TestClusterRenewCrypto():
1020
  """gnt-cluster renew-crypto"""
1021
  master = qa_config.GetMasterNode()
1022

    
1023
  # Conflicting options
1024
  cmd = ["gnt-cluster", "renew-crypto", "--force",
1025
         "--new-cluster-certificate", "--new-confd-hmac-key"]
1026
  conflicting = [
1027
    ["--new-rapi-certificate", "--rapi-certificate=/dev/null"],
1028
    ["--new-cluster-domain-secret", "--cluster-domain-secret=/dev/null"],
1029
    ]
1030
  for i in conflicting:
1031
    AssertCommand(cmd + i, fail=True)
1032

    
1033
  # Invalid RAPI certificate
1034
  cmd = ["gnt-cluster", "renew-crypto", "--force",
1035
         "--rapi-certificate=/dev/null"]
1036
  AssertCommand(cmd, fail=True)
1037

    
1038
  rapi_cert_backup = qa_utils.BackupFile(master.primary,
1039
                                         pathutils.RAPI_CERT_FILE)
1040
  try:
1041
    # Custom RAPI certificate
1042
    fh = tempfile.NamedTemporaryFile()
1043

    
1044
    # Ensure certificate doesn't cause "gnt-cluster verify" to complain
1045
    validity = constants.SSL_CERT_EXPIRATION_WARN * 3
1046

    
1047
    utils.GenerateSelfSignedSslCert(fh.name, validity=validity)
1048

    
1049
    tmpcert = qa_utils.UploadFile(master.primary, fh.name)
1050
    try:
1051
      AssertCommand(["gnt-cluster", "renew-crypto", "--force",
1052
                     "--rapi-certificate=%s" % tmpcert])
1053
    finally:
1054
      AssertCommand(["rm", "-f", tmpcert])
1055

    
1056
    # Custom cluster domain secret
1057
    cds_fh = tempfile.NamedTemporaryFile()
1058
    cds_fh.write(utils.GenerateSecret())
1059
    cds_fh.write("\n")
1060
    cds_fh.flush()
1061

    
1062
    tmpcds = qa_utils.UploadFile(master.primary, cds_fh.name)
1063
    try:
1064
      AssertCommand(["gnt-cluster", "renew-crypto", "--force",
1065
                     "--cluster-domain-secret=%s" % tmpcds])
1066
    finally:
1067
      AssertCommand(["rm", "-f", tmpcds])
1068

    
1069
    # Normal case
1070
    AssertCommand(["gnt-cluster", "renew-crypto", "--force",
1071
                   "--new-cluster-certificate", "--new-confd-hmac-key",
1072
                   "--new-rapi-certificate", "--new-cluster-domain-secret"])
1073

    
1074
    # Restore RAPI certificate
1075
    AssertCommand(["gnt-cluster", "renew-crypto", "--force",
1076
                   "--rapi-certificate=%s" % rapi_cert_backup])
1077
  finally:
1078
    AssertCommand(["rm", "-f", rapi_cert_backup])
1079

    
1080

    
1081
def TestClusterBurnin():
1082
  """Burnin"""
1083
  master = qa_config.GetMasterNode()
1084

    
1085
  options = qa_config.get("options", {})
1086
  disk_template = options.get("burnin-disk-template", constants.DT_DRBD8)
1087
  parallel = options.get("burnin-in-parallel", False)
1088
  check_inst = options.get("burnin-check-instances", False)
1089
  do_rename = options.get("burnin-rename", "")
1090
  do_reboot = options.get("burnin-reboot", True)
1091
  reboot_types = options.get("reboot-types", constants.REBOOT_TYPES)
1092

    
1093
  # Get as many instances as we need
1094
  instances = []
1095
  try:
1096
    try:
1097
      num = qa_config.get("options", {}).get("burnin-instances", 1)
1098
      for _ in range(0, num):
1099
        instances.append(qa_config.AcquireInstance())
1100
    except qa_error.OutOfInstancesError:
1101
      print "Not enough instances, continuing anyway."
1102

    
1103
    if len(instances) < 1:
1104
      raise qa_error.Error("Burnin needs at least one instance")
1105

    
1106
    script = qa_utils.UploadFile(master.primary, "../tools/burnin")
1107
    try:
1108
      disks = qa_config.GetDiskOptions()
1109
      # Run burnin
1110
      cmd = [script,
1111
             "--os=%s" % qa_config.get("os"),
1112
             "--minmem-size=%s" % qa_config.get(constants.BE_MINMEM),
1113
             "--maxmem-size=%s" % qa_config.get(constants.BE_MAXMEM),
1114
             "--disk-size=%s" % ",".join([d.get("size") for d in disks]),
1115
             "--disk-growth=%s" % ",".join([d.get("growth") for d in disks]),
1116
             "--disk-template=%s" % disk_template]
1117
      if parallel:
1118
        cmd.append("--parallel")
1119
        cmd.append("--early-release")
1120
      if check_inst:
1121
        cmd.append("--http-check")
1122
      if do_rename:
1123
        cmd.append("--rename=%s" % do_rename)
1124
      if not do_reboot:
1125
        cmd.append("--no-reboot")
1126
      else:
1127
        cmd.append("--reboot-types=%s" % ",".join(reboot_types))
1128
      cmd += [inst.name for inst in instances]
1129
      AssertCommand(cmd)
1130
    finally:
1131
      AssertCommand(["rm", "-f", script])
1132

    
1133
  finally:
1134
    for inst in instances:
1135
      inst.Release()
1136

    
1137

    
1138
def TestClusterMasterFailover():
1139
  """gnt-cluster master-failover"""
1140
  master = qa_config.GetMasterNode()
1141
  failovermaster = qa_config.AcquireNode(exclude=master)
1142

    
1143
  cmd = ["gnt-cluster", "master-failover"]
1144
  try:
1145
    AssertCommand(cmd, node=failovermaster)
1146
    # Back to original master node
1147
    AssertCommand(cmd, node=master)
1148
  finally:
1149
    failovermaster.Release()
1150

    
1151

    
1152
def _NodeQueueDrainFile(node):
1153
  """Returns path to queue drain file for a node.
1154

1155
  """
1156
  return qa_utils.MakeNodePath(node, pathutils.JOB_QUEUE_DRAIN_FILE)
1157

    
1158

    
1159
def _AssertDrainFile(node, **kwargs):
1160
  """Checks for the queue drain file.
1161

1162
  """
1163
  AssertCommand(["test", "-f", _NodeQueueDrainFile(node)], node=node, **kwargs)
1164

    
1165

    
1166
def TestClusterMasterFailoverWithDrainedQueue():
1167
  """gnt-cluster master-failover with drained queue"""
1168
  master = qa_config.GetMasterNode()
1169
  failovermaster = qa_config.AcquireNode(exclude=master)
1170

    
1171
  # Ensure queue is not drained
1172
  for node in [master, failovermaster]:
1173
    _AssertDrainFile(node, fail=True)
1174

    
1175
  # Drain queue on failover master
1176
  AssertCommand(["touch", _NodeQueueDrainFile(failovermaster)],
1177
                node=failovermaster)
1178

    
1179
  cmd = ["gnt-cluster", "master-failover"]
1180
  try:
1181
    _AssertDrainFile(failovermaster)
1182
    AssertCommand(cmd, node=failovermaster)
1183
    _AssertDrainFile(master, fail=True)
1184
    _AssertDrainFile(failovermaster, fail=True)
1185

    
1186
    # Back to original master node
1187
    AssertCommand(cmd, node=master)
1188
  finally:
1189
    failovermaster.Release()
1190

    
1191
  # Ensure queue is not drained
1192
  for node in [master, failovermaster]:
1193
    _AssertDrainFile(node, fail=True)
1194

    
1195

    
1196
def TestClusterCopyfile():
1197
  """gnt-cluster copyfile"""
1198
  master = qa_config.GetMasterNode()
1199

    
1200
  uniqueid = utils.NewUUID()
1201

    
1202
  # Create temporary file
1203
  f = tempfile.NamedTemporaryFile()
1204
  f.write(uniqueid)
1205
  f.flush()
1206
  f.seek(0)
1207

    
1208
  # Upload file to master node
1209
  testname = qa_utils.UploadFile(master.primary, f.name)
1210
  try:
1211
    # Copy file to all nodes
1212
    AssertCommand(["gnt-cluster", "copyfile", testname])
1213
    _CheckFileOnAllNodes(testname, uniqueid)
1214
  finally:
1215
    _RemoveFileFromAllNodes(testname)
1216

    
1217

    
1218
def TestClusterCommand():
1219
  """gnt-cluster command"""
1220
  uniqueid = utils.NewUUID()
1221
  rfile = "/tmp/gnt%s" % utils.NewUUID()
1222
  rcmd = utils.ShellQuoteArgs(["echo", "-n", uniqueid])
1223
  cmd = utils.ShellQuoteArgs(["gnt-cluster", "command",
1224
                              "%s >%s" % (rcmd, rfile)])
1225

    
1226
  try:
1227
    AssertCommand(cmd)
1228
    _CheckFileOnAllNodes(rfile, uniqueid)
1229
  finally:
1230
    _RemoveFileFromAllNodes(rfile)
1231

    
1232

    
1233
def TestClusterDestroy():
1234
  """gnt-cluster destroy"""
1235
  AssertCommand(["gnt-cluster", "destroy", "--yes-do-it"])
1236

    
1237

    
1238
def TestClusterRepairDiskSizes():
1239
  """gnt-cluster repair-disk-sizes"""
1240
  AssertCommand(["gnt-cluster", "repair-disk-sizes"])
1241

    
1242

    
1243
def TestSetExclStorCluster(newvalue):
1244
  """Set the exclusive_storage node parameter at the cluster level.
1245

1246
  @type newvalue: bool
1247
  @param newvalue: New value of exclusive_storage
1248
  @rtype: bool
1249
  @return: The old value of exclusive_storage
1250

1251
  """
1252
  es_path = ["Default node parameters", "exclusive_storage"]
1253
  oldvalue = _GetClusterField(es_path)
1254
  AssertCommand(["gnt-cluster", "modify", "--node-parameters",
1255
                 "exclusive_storage=%s" % newvalue])
1256
  effvalue = _GetClusterField(es_path)
1257
  if effvalue != newvalue:
1258
    raise qa_error.Error("exclusive_storage has the wrong value: %s instead"
1259
                         " of %s" % (effvalue, newvalue))
1260
  qa_config.SetExclusiveStorage(newvalue)
1261
  return oldvalue
1262

    
1263

    
1264
def TestExclStorSharedPv(node):
1265
  """cluster-verify reports LVs that share the same PV with exclusive_storage.
1266

1267
  """
1268
  vgname = qa_config.get("vg-name", constants.DEFAULT_VG)
1269
  lvname1 = _QA_LV_PREFIX + "vol1"
1270
  lvname2 = _QA_LV_PREFIX + "vol2"
1271
  node_name = node.primary
1272
  AssertCommand(["lvcreate", "-L1G", "-n", lvname1, vgname], node=node_name)
1273
  AssertClusterVerify(fail=True, errors=[constants.CV_ENODEORPHANLV])
1274
  AssertCommand(["lvcreate", "-L1G", "-n", lvname2, vgname], node=node_name)
1275
  AssertClusterVerify(fail=True, errors=[constants.CV_ENODELVM,
1276
                                         constants.CV_ENODEORPHANLV])
1277
  AssertCommand(["lvremove", "-f", "/".join([vgname, lvname1])], node=node_name)
1278
  AssertCommand(["lvremove", "-f", "/".join([vgname, lvname2])], node=node_name)
1279
  AssertClusterVerify()