Statistics
| Branch: | Tag: | Revision:

root / qa / qa_cluster.py @ b24b52d9

History | View | Annotate | Download (40.2 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
  if extra_args:
229
    cmd.extend(extra_args)
230

    
231
  cmd.append(qa_config.get("name"))
232

    
233
  AssertCommand(cmd)
234

    
235
  cmd = ["gnt-cluster", "modify"]
236

    
237
  # hypervisor parameter modifications
238
  hvp = qa_config.get("hypervisor-parameters", {})
239
  for k, v in hvp.items():
240
    cmd.extend(["-H", "%s:%s" % (k, v)])
241
  # backend parameter modifications
242
  bep = qa_config.get("backend-parameters", "")
243
  if bep:
244
    cmd.extend(["-B", bep])
245

    
246
  if len(cmd) > 2:
247
    AssertCommand(cmd)
248

    
249
  # OS parameters
250
  osp = qa_config.get("os-parameters", {})
251
  for k, v in osp.items():
252
    AssertCommand(["gnt-os", "modify", "-O", v, k])
253

    
254
  # OS hypervisor parameters
255
  os_hvp = qa_config.get("os-hvp", {})
256
  for os_name in os_hvp:
257
    for hv, hvp in os_hvp[os_name].items():
258
      AssertCommand(["gnt-os", "modify", "-H", "%s:%s" % (hv, hvp), os_name])
259

    
260

    
261
def TestClusterRename():
262
  """gnt-cluster rename"""
263
  cmd = ["gnt-cluster", "rename", "-f"]
264

    
265
  original_name = qa_config.get("name")
266
  rename_target = qa_config.get("rename", None)
267
  if rename_target is None:
268
    print qa_utils.FormatError('"rename" entry is missing')
269
    return
270

    
271
  for data in [
272
    cmd + [rename_target],
273
    _CLUSTER_VERIFY,
274
    cmd + [original_name],
275
    _CLUSTER_VERIFY,
276
    ]:
277
    AssertCommand(data)
278

    
279

    
280
def TestClusterOob():
281
  """out-of-band framework"""
282
  oob_path_exists = "/tmp/ganeti-qa-oob-does-exist-%s" % utils.NewUUID()
283

    
284
  AssertCommand(_CLUSTER_VERIFY)
285
  AssertCommand(["gnt-cluster", "modify", "--node-parameters",
286
                 "oob_program=/tmp/ganeti-qa-oob-does-not-exist-%s" %
287
                 utils.NewUUID()])
288

    
289
  AssertCommand(_CLUSTER_VERIFY, fail=True)
290

    
291
  AssertCommand(["touch", oob_path_exists])
292
  AssertCommand(["chmod", "0400", oob_path_exists])
293
  AssertCommand(["gnt-cluster", "copyfile", oob_path_exists])
294

    
295
  try:
296
    AssertCommand(["gnt-cluster", "modify", "--node-parameters",
297
                   "oob_program=%s" % oob_path_exists])
298

    
299
    AssertCommand(_CLUSTER_VERIFY, fail=True)
300

    
301
    AssertCommand(["chmod", "0500", oob_path_exists])
302
    AssertCommand(["gnt-cluster", "copyfile", oob_path_exists])
303

    
304
    AssertCommand(_CLUSTER_VERIFY)
305
  finally:
306
    AssertCommand(["gnt-cluster", "command", "rm", oob_path_exists])
307

    
308
  AssertCommand(["gnt-cluster", "modify", "--node-parameters",
309
                 "oob_program="])
310

    
311

    
312
def TestClusterEpo():
313
  """gnt-cluster epo"""
314
  master = qa_config.GetMasterNode()
315

    
316
  # Assert that OOB is unavailable for all nodes
317
  result_output = GetCommandOutput(master.primary,
318
                                   "gnt-node list --verbose --no-headers -o"
319
                                   " powered")
320
  AssertEqual(compat.all(powered == "(unavail)"
321
                         for powered in result_output.splitlines()), True)
322

    
323
  # Conflicting
324
  AssertCommand(["gnt-cluster", "epo", "--groups", "--all"], fail=True)
325
  # --all doesn't expect arguments
326
  AssertCommand(["gnt-cluster", "epo", "--all", "some_arg"], fail=True)
327

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

    
331
  # This shouldn't fail
332
  AssertCommand(["gnt-cluster", "epo", "-f", "--all"])
333

    
334
  # All instances should have been stopped now
335
  result_output = GetCommandOutput(master.primary,
336
                                   "gnt-instance list --no-headers -o status")
337
  # ERROR_down because the instance is stopped but not recorded as such
338
  AssertEqual(compat.all(status == "ERROR_down"
339
                         for status in result_output.splitlines()), True)
340

    
341
  # Now start everything again
342
  AssertCommand(["gnt-cluster", "epo", "--on", "-f", "--all"])
343

    
344
  # All instances should have been started now
345
  result_output = GetCommandOutput(master.primary,
346
                                   "gnt-instance list --no-headers -o status")
347
  AssertEqual(compat.all(status == "running"
348
                         for status in result_output.splitlines()), True)
349

    
350

    
351
def TestClusterVerify():
352
  """gnt-cluster verify"""
353
  AssertCommand(_CLUSTER_VERIFY)
354
  AssertCommand(["gnt-cluster", "verify-disks"])
355

    
356

    
357
def TestClusterVerifyDisksBrokenDRBD(instance, inst_nodes):
358
  """gnt-cluster verify-disks with broken DRBD"""
359
  qa_daemon.TestPauseWatcher()
360

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

    
377
    verify_output = GetCommandOutput(qa_config.GetMasterNode().primary,
378
                                     "gnt-cluster verify-disks")
379
    activation_msg = "Activating disks for instance '%s'" % instance.name
380
    if activation_msg not in verify_output:
381
      raise qa_error.Error("gnt-cluster verify-disks did not activate broken"
382
                           " DRBD disks:\n%s" % verify_output)
383

    
384
    verify_output = GetCommandOutput(qa_config.GetMasterNode().primary,
385
                                     "gnt-cluster verify-disks")
386
    if activation_msg in verify_output:
387
      raise qa_error.Error("gnt-cluster verify-disks wants to activate broken"
388
                           " DRBD disks on second attempt:\n%s" % verify_output)
389

    
390
    AssertCommand(_CLUSTER_VERIFY)
391
  finally:
392
    qa_daemon.TestResumeWatcher()
393

    
394

    
395
def TestJobqueue():
396
  """gnt-debug test-jobqueue"""
397
  AssertCommand(["gnt-debug", "test-jobqueue"])
398

    
399

    
400
def TestDelay(node):
401
  """gnt-debug delay"""
402
  AssertCommand(["gnt-debug", "delay", "1"])
403
  AssertCommand(["gnt-debug", "delay", "--no-master", "1"])
404
  AssertCommand(["gnt-debug", "delay", "--no-master",
405
                 "-n", node.primary, "1"])
406

    
407

    
408
def TestClusterReservedLvs():
409
  """gnt-cluster reserved lvs"""
410
  vgname = qa_config.get("vg-name", constants.DEFAULT_VG)
411
  lvname = _QA_LV_PREFIX + "test"
412
  lvfullname = "/".join([vgname, lvname])
413
  for fail, cmd in [
414
    (False, _CLUSTER_VERIFY),
415
    (False, ["gnt-cluster", "modify", "--reserved-lvs", ""]),
416
    (False, ["lvcreate", "-L1G", "-n", lvname, vgname]),
417
    (True, _CLUSTER_VERIFY),
418
    (False, ["gnt-cluster", "modify", "--reserved-lvs",
419
             "%s,.*/other-test" % lvfullname]),
420
    (False, _CLUSTER_VERIFY),
421
    (False, ["gnt-cluster", "modify", "--reserved-lvs",
422
             ".*/%s.*" % _QA_LV_PREFIX]),
423
    (False, _CLUSTER_VERIFY),
424
    (False, ["gnt-cluster", "modify", "--reserved-lvs", ""]),
425
    (True, _CLUSTER_VERIFY),
426
    (False, ["lvremove", "-f", lvfullname]),
427
    (False, _CLUSTER_VERIFY),
428
    ]:
429
    AssertCommand(cmd, fail=fail)
430

    
431

    
432
def TestClusterModifyEmpty():
433
  """gnt-cluster modify"""
434
  AssertCommand(["gnt-cluster", "modify"], fail=True)
435

    
436

    
437
def TestClusterModifyDisk():
438
  """gnt-cluster modify -D"""
439
  for param in _FAIL_PARAMS:
440
    AssertCommand(["gnt-cluster", "modify", "-D", param], fail=True)
441

    
442

    
443
def _GetOtherEnabledDiskTemplate(undesired_disk_templates,
444
                                 enabled_disk_templates):
445
  """Returns one template that is not in the undesired set.
446

447
  @type undesired_disk_templates: list of string
448
  @param undesired_disk_templates: a list of disk templates that we want to
449
      exclude when drawing one disk template from the list of enabled
450
      disk templates
451
  @type enabled_disk_templates: list of string
452
  @param enabled_disk_templates: list of enabled disk templates (in QA)
453

454
  """
455
  desired_templates = list(set(enabled_disk_templates)
456
                                - set(undesired_disk_templates))
457
  if desired_templates:
458
    template = desired_templates[0]
459
  else:
460
    # If no desired disk template is available for QA, choose 'diskless' and
461
    # hope for the best.
462
    template = constants.ST_DISKLESS
463

    
464
  return template
465

    
466

    
467
def TestClusterModifyFileBasedStorageDir(
468
    file_disk_template, dir_config_key, default_dir, option_name):
469
  """Tests gnt-cluster modify wrt to file-based directory options.
470

471
  @type file_disk_template: string
472
  @param file_disk_template: file-based disk template
473
  @type dir_config_key: string
474
  @param dir_config_key: key for the QA config to retrieve the default
475
     directory value
476
  @type default_dir: string
477
  @param default_dir: default directory, if the QA config does not specify
478
     it
479
  @type option_name: string
480
  @param option_name: name of the option of 'gnt-cluster modify' to
481
     change the directory
482

483
  """
484
  enabled_disk_templates = qa_config.GetEnabledDiskTemplates()
485
  assert file_disk_template in [constants.DT_FILE, constants.DT_SHARED_FILE]
486
  if not qa_config.IsTemplateSupported(file_disk_template):
487
    return
488

    
489
  # Get some non-file-based disk template to disable file storage
490
  other_disk_template = _GetOtherEnabledDiskTemplate(
491
      utils.storage.GetDiskTemplatesOfStorageType(constants.ST_FILE),
492
      enabled_disk_templates)
493

    
494
  file_storage_dir = qa_config.get(dir_config_key, default_dir)
495
  invalid_file_storage_dir = "/boot/"
496

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

    
531

    
532
def TestClusterModifyFileStorageDir():
533
  """gnt-cluster modify --file-storage-dir=..."""
534
  TestClusterModifyFileBasedStorageDir(
535
      constants.DT_FILE, "default-file-storage-dir",
536
      pathutils.DEFAULT_FILE_STORAGE_DIR,
537
      "file-storage-dir")
538

    
539

    
540
def TestClusterModifySharedFileStorageDir():
541
  """gnt-cluster modify --shared-file-storage-dir=..."""
542
  TestClusterModifyFileBasedStorageDir(
543
      constants.DT_SHARED_FILE, "default-shared-file-storage-dir",
544
      pathutils.DEFAULT_SHARED_FILE_STORAGE_DIR,
545
      "shared-file-storage-dir")
546

    
547

    
548
def TestClusterModifyDiskTemplates():
549
  """gnt-cluster modify --enabled-disk-templates=..."""
550
  enabled_disk_templates = qa_config.GetEnabledDiskTemplates()
551
  default_disk_template = qa_config.GetDefaultDiskTemplate()
552

    
553
  _TestClusterModifyDiskTemplatesArguments(default_disk_template,
554
                                           enabled_disk_templates)
555
  _TestClusterModifyDiskTemplatesVgName(enabled_disk_templates)
556

    
557
  _RestoreEnabledDiskTemplates()
558
  nodes = qa_config.AcquireManyNodes(2)
559

    
560
  instance_template = enabled_disk_templates[0]
561
  instance = qa_instance.CreateInstanceByDiskTemplate(nodes, instance_template)
562

    
563
  _TestClusterModifyUnusedDiskTemplate(instance_template)
564
  _TestClusterModifyUsedDiskTemplate(instance_template,
565
                                     enabled_disk_templates)
566

    
567
  qa_instance.TestInstanceRemove(instance)
568
  _RestoreEnabledDiskTemplates()
569

    
570

    
571
def _RestoreEnabledDiskTemplates():
572
  """Sets the list of enabled disk templates back to the list of enabled disk
573
     templates from the QA configuration. This can be used to make sure that
574
     the tests that modify the list of disk templates do not interfere with
575
     other tests.
576

577
  """
578
  cmd = ["gnt-cluster", "modify", "--enabled-disk-templates=%s" %
579
         ",".join(qa_config.GetEnabledDiskTemplates())]
580

    
581
  if utils.IsLvmEnabled(qa_config.GetEnabledDiskTemplates()):
582
    vgname = qa_config.get("vg-name", constants.DEFAULT_VG)
583
    cmd.append("--vg-name=%s" % vgname)
584

    
585
  AssertCommand(cmd, fail=False)
586

    
587

    
588
def _TestClusterModifyDiskTemplatesArguments(default_disk_template,
589
                                             enabled_disk_templates):
590
  """Tests argument handling of 'gnt-cluster modify' with respect to
591
     the parameter '--enabled-disk-templates'. This test is independent
592
     of instances.
593

594
  """
595
  _RestoreEnabledDiskTemplates()
596

    
597
  # bogus templates
598
  AssertCommand(["gnt-cluster", "modify",
599
                 "--enabled-disk-templates=pinkbunny"],
600
                fail=True)
601

    
602
  # duplicate entries do no harm
603
  AssertCommand(
604
    ["gnt-cluster", "modify",
605
     "--enabled-disk-templates=%s,%s" %
606
      (default_disk_template, default_disk_template)],
607
    fail=False)
608

    
609
  if constants.DT_DRBD8 in enabled_disk_templates:
610
    # interaction with --drbd-usermode-helper option
611
    drbd_usermode_helper = qa_config.get("drbd-usermode-helper", None)
612
    if not drbd_usermode_helper:
613
      drbd_usermode_helper = "/bin/true"
614
    # specifying a helper when drbd gets disabled is ok. Note that drbd still
615
    # has to be installed on the nodes in this case
616
    AssertCommand(["gnt-cluster", "modify",
617
                   "--drbd-usermode-helper=%s" % drbd_usermode_helper,
618
                   "--enabled-disk-templates=%s" % constants.DT_DISKLESS],
619
                   fail=False)
620
    # specifying a helper when drbd is re-enabled
621
    AssertCommand(["gnt-cluster", "modify",
622
                   "--drbd-usermode-helper=%s" % drbd_usermode_helper,
623
                   "--enabled-disk-templates=%s" %
624
                     ",".join(enabled_disk_templates)],
625
                  fail=False)
626

    
627

    
628
def _TestClusterModifyDiskTemplatesVgName(enabled_disk_templates):
629
  """Tests argument handling of 'gnt-cluster modify' with respect to
630
     the parameter '--enabled-disk-templates' and '--vg-name'. This test is
631
     independent of instances.
632

633
  """
634
  if not utils.IsLvmEnabled(enabled_disk_templates):
635
    # These tests only make sense if lvm is enabled for QA
636
    return
637

    
638
  # determine an LVM and a non-LVM disk template for the tests
639
  non_lvm_template = _GetOtherEnabledDiskTemplate(utils.GetLvmDiskTemplates(),
640
                                                  enabled_disk_templates)
641
  lvm_template = list(set(enabled_disk_templates)
642
                      .intersection(set(utils.GetLvmDiskTemplates())))[0]
643

    
644
  vgname = qa_config.get("vg-name", constants.DEFAULT_VG)
645

    
646
  # Clean start: unset volume group name, disable lvm storage
647
  AssertCommand(
648
    ["gnt-cluster", "modify",
649
     "--enabled-disk-templates=%s" % non_lvm_template,
650
     "--vg-name="],
651
    fail=False)
652

    
653
  # Try to enable lvm, when no volume group is given
654
  AssertCommand(
655
    ["gnt-cluster", "modify",
656
     "--enabled-disk-templates=%s" % lvm_template],
657
    fail=True)
658

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

    
662
  # Try unsetting vg name and enabling lvm at the same time
663
  AssertCommand(
664
    ["gnt-cluster", "modify",
665
     "--enabled-disk-templates=%s" % lvm_template,
666
     "--vg-name="],
667
    fail=True)
668

    
669
  # Enable lvm with vg name present
670
  AssertCommand(
671
    ["gnt-cluster", "modify",
672
     "--enabled-disk-templates=%s" % lvm_template],
673
    fail=False)
674

    
675
  # Try unsetting vg name with lvm still enabled
676
  AssertCommand(["gnt-cluster", "modify", "--vg-name="], fail=True)
677

    
678
  # Disable lvm with vg name still set
679
  AssertCommand(
680
    ["gnt-cluster", "modify", "--enabled-disk-templates=%s" % non_lvm_template],
681
    fail=False)
682

    
683
  # Try unsetting vg name with lvm disabled
684
  AssertCommand(["gnt-cluster", "modify", "--vg-name="], fail=False)
685

    
686
  # Set vg name and enable lvm at the same time
687
  AssertCommand(
688
    ["gnt-cluster", "modify",
689
     "--enabled-disk-templates=%s" % lvm_template,
690
     "--vg-name=%s" % vgname],
691
    fail=False)
692

    
693
  # Unset vg name and disable lvm at the same time
694
  AssertCommand(
695
    ["gnt-cluster", "modify",
696
     "--enabled-disk-templates=%s" % non_lvm_template,
697
     "--vg-name="],
698
    fail=False)
699

    
700
  _RestoreEnabledDiskTemplates()
701

    
702

    
703
def _TestClusterModifyUsedDiskTemplate(instance_template,
704
                                       enabled_disk_templates):
705
  """Tests that disk templates that are currently in use by instances cannot
706
     be disabled on the cluster.
707

708
  """
709
  # If the list of enabled disk templates contains only one template
710
  # we need to add some other templates, because the list of enabled disk
711
  # templates can only be set to a non-empty list.
712
  new_disk_templates = list(set(enabled_disk_templates)
713
                              - set([instance_template]))
714
  if not new_disk_templates:
715
    new_disk_templates = list(set([constants.DT_DISKLESS, constants.DT_BLOCK])
716
                                - set([instance_template]))
717
  AssertCommand(
718
    ["gnt-cluster", "modify",
719
     "--enabled-disk-templates=%s" %
720
       ",".join(new_disk_templates)],
721
    fail=True)
722

    
723

    
724
def _TestClusterModifyUnusedDiskTemplate(instance_template):
725
  """Tests that unused disk templates can be disabled safely."""
726
  all_disk_templates = constants.DISK_TEMPLATES
727
  if not utils.IsLvmEnabled(qa_config.GetEnabledDiskTemplates()):
728
    all_disk_templates = list(set(all_disk_templates) -
729
                              set(utils.GetLvmDiskTemplates()))
730

    
731
  AssertCommand(
732
    ["gnt-cluster", "modify",
733
     "--enabled-disk-templates=%s" %
734
       ",".join(all_disk_templates)],
735
    fail=False)
736
  new_disk_templates = [instance_template]
737
  AssertCommand(
738
    ["gnt-cluster", "modify",
739
     "--enabled-disk-templates=%s" %
740
       ",".join(new_disk_templates)],
741
    fail=False)
742

    
743

    
744
def TestClusterModifyBe():
745
  """gnt-cluster modify -B"""
746
  for fail, cmd in [
747
    # max/min mem
748
    (False, ["gnt-cluster", "modify", "-B", "maxmem=256"]),
749
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *maxmem: 256$'"]),
750
    (False, ["gnt-cluster", "modify", "-B", "minmem=256"]),
751
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *minmem: 256$'"]),
752
    (True, ["gnt-cluster", "modify", "-B", "maxmem=a"]),
753
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *maxmem: 256$'"]),
754
    (True, ["gnt-cluster", "modify", "-B", "minmem=a"]),
755
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *minmem: 256$'"]),
756
    (False, ["gnt-cluster", "modify", "-B", "maxmem=128,minmem=128"]),
757
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *maxmem: 128$'"]),
758
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *minmem: 128$'"]),
759
    # vcpus
760
    (False, ["gnt-cluster", "modify", "-B", "vcpus=4"]),
761
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *vcpus: 4$'"]),
762
    (True, ["gnt-cluster", "modify", "-B", "vcpus=a"]),
763
    (False, ["gnt-cluster", "modify", "-B", "vcpus=1"]),
764
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *vcpus: 1$'"]),
765
    # auto_balance
766
    (False, ["gnt-cluster", "modify", "-B", "auto_balance=False"]),
767
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *auto_balance: False$'"]),
768
    (True, ["gnt-cluster", "modify", "-B", "auto_balance=1"]),
769
    (False, ["gnt-cluster", "modify", "-B", "auto_balance=True"]),
770
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *auto_balance: True$'"]),
771
    ]:
772
    AssertCommand(cmd, fail=fail)
773

    
774
  # redo the original-requested BE parameters, if any
775
  bep = qa_config.get("backend-parameters", "")
776
  if bep:
777
    AssertCommand(["gnt-cluster", "modify", "-B", bep])
778

    
779

    
780
def _GetClusterIPolicy():
781
  """Return the run-time values of the cluster-level instance policy.
782

783
  @rtype: tuple
784
  @return: (policy, specs), where:
785
      - policy is a dictionary of the policy values, instance specs excluded
786
      - specs is a dictionary containing only the specs, using the internal
787
        format (see L{constants.IPOLICY_DEFAULTS} for an example)
788

789
  """
790
  info = qa_utils.GetObjectInfo(["gnt-cluster", "info"])
791
  policy = info["Instance policy - limits for instances"]
792
  (ret_policy, ret_specs) = qa_utils.ParseIPolicy(policy)
793

    
794
  # Sanity checks
795
  assert "minmax" in ret_specs and "std" in ret_specs
796
  assert len(ret_specs["minmax"]) > 0
797
  assert len(ret_policy) > 0
798
  return (ret_policy, ret_specs)
799

    
800

    
801
def TestClusterModifyIPolicy():
802
  """gnt-cluster modify --ipolicy-*"""
803
  basecmd = ["gnt-cluster", "modify"]
804
  (old_policy, old_specs) = _GetClusterIPolicy()
805
  for par in ["vcpu-ratio", "spindle-ratio"]:
806
    curr_val = float(old_policy[par])
807
    test_values = [
808
      (True, 1.0),
809
      (True, 1.5),
810
      (True, 2),
811
      (False, "a"),
812
      # Restore the old value
813
      (True, curr_val),
814
      ]
815
    for (good, val) in test_values:
816
      cmd = basecmd + ["--ipolicy-%s=%s" % (par, val)]
817
      AssertCommand(cmd, fail=not good)
818
      if good:
819
        curr_val = val
820
      # Check the affected parameter
821
      (eff_policy, eff_specs) = _GetClusterIPolicy()
822
      AssertEqual(float(eff_policy[par]), curr_val)
823
      # Check everything else
824
      AssertEqual(eff_specs, old_specs)
825
      for p in eff_policy.keys():
826
        if p == par:
827
          continue
828
        AssertEqual(eff_policy[p], old_policy[p])
829

    
830
  # Disk templates are treated slightly differently
831
  par = "disk-templates"
832
  disp_str = "allowed disk templates"
833
  curr_val = old_policy[disp_str]
834
  test_values = [
835
    (True, constants.DT_PLAIN),
836
    (True, "%s,%s" % (constants.DT_PLAIN, constants.DT_DRBD8)),
837
    (False, "thisisnotadisktemplate"),
838
    (False, ""),
839
    # Restore the old value
840
    (True, curr_val.replace(" ", "")),
841
    ]
842
  for (good, val) in test_values:
843
    cmd = basecmd + ["--ipolicy-%s=%s" % (par, val)]
844
    AssertCommand(cmd, fail=not good)
845
    if good:
846
      curr_val = val
847
    # Check the affected parameter
848
    (eff_policy, eff_specs) = _GetClusterIPolicy()
849
    AssertEqual(eff_policy[disp_str].replace(" ", ""), curr_val)
850
    # Check everything else
851
    AssertEqual(eff_specs, old_specs)
852
    for p in eff_policy.keys():
853
      if p == disp_str:
854
        continue
855
      AssertEqual(eff_policy[p], old_policy[p])
856

    
857

    
858
def TestClusterSetISpecs(new_specs=None, diff_specs=None, fail=False,
859
                         old_values=None):
860
  """Change instance specs.
861

862
  At most one of new_specs or diff_specs can be specified.
863

864
  @type new_specs: dict
865
  @param new_specs: new complete specs, in the same format returned by
866
      L{_GetClusterIPolicy}
867
  @type diff_specs: dict
868
  @param diff_specs: partial specs, it can be an incomplete specifications, but
869
      if min/max specs are specified, their number must match the number of the
870
      existing specs
871
  @type fail: bool
872
  @param fail: if the change is expected to fail
873
  @type old_values: tuple
874
  @param old_values: (old_policy, old_specs), as returned by
875
      L{_GetClusterIPolicy}
876
  @return: same as L{_GetClusterIPolicy}
877

878
  """
879
  build_cmd = lambda opts: ["gnt-cluster", "modify"] + opts
880
  return qa_utils.TestSetISpecs(
881
    new_specs=new_specs, diff_specs=diff_specs,
882
    get_policy_fn=_GetClusterIPolicy, build_cmd_fn=build_cmd,
883
    fail=fail, old_values=old_values)
884

    
885

    
886
def TestClusterModifyISpecs():
887
  """gnt-cluster modify --specs-*"""
888
  params = ["memory-size", "disk-size", "disk-count", "cpu-count", "nic-count"]
889
  (cur_policy, cur_specs) = _GetClusterIPolicy()
890
  # This test assumes that there is only one min/max bound
891
  assert len(cur_specs[constants.ISPECS_MINMAX]) == 1
892
  for par in params:
893
    test_values = [
894
      (True, 0, 4, 12),
895
      (True, 4, 4, 12),
896
      (True, 4, 12, 12),
897
      (True, 4, 4, 4),
898
      (False, 4, 0, 12),
899
      (False, 4, 16, 12),
900
      (False, 4, 4, 0),
901
      (False, 12, 4, 4),
902
      (False, 12, 4, 0),
903
      (False, "a", 4, 12),
904
      (False, 0, "a", 12),
905
      (False, 0, 4, "a"),
906
      # This is to restore the old values
907
      (True,
908
       cur_specs[constants.ISPECS_MINMAX][0][constants.ISPECS_MIN][par],
909
       cur_specs[constants.ISPECS_STD][par],
910
       cur_specs[constants.ISPECS_MINMAX][0][constants.ISPECS_MAX][par])
911
      ]
912
    for (good, mn, st, mx) in test_values:
913
      new_vals = {
914
        constants.ISPECS_MINMAX: [{
915
          constants.ISPECS_MIN: {par: mn},
916
          constants.ISPECS_MAX: {par: mx}
917
          }],
918
        constants.ISPECS_STD: {par: st}
919
        }
920
      cur_state = (cur_policy, cur_specs)
921
      # We update cur_specs, as we've copied the values to restore already
922
      (cur_policy, cur_specs) = TestClusterSetISpecs(
923
        diff_specs=new_vals, fail=not good, old_values=cur_state)
924

    
925
    # Get the ipolicy command
926
    mnode = qa_config.GetMasterNode()
927
    initcmd = GetCommandOutput(mnode.primary, "gnt-cluster show-ispecs-cmd")
928
    modcmd = ["gnt-cluster", "modify"]
929
    opts = initcmd.split()
930
    assert opts[0:2] == ["gnt-cluster", "init"]
931
    for k in range(2, len(opts) - 1):
932
      if opts[k].startswith("--ipolicy-"):
933
        assert k + 2 <= len(opts)
934
        modcmd.extend(opts[k:k + 2])
935
    # Re-apply the ipolicy (this should be a no-op)
936
    AssertCommand(modcmd)
937
    new_initcmd = GetCommandOutput(mnode.primary, "gnt-cluster show-ispecs-cmd")
938
    AssertEqual(initcmd, new_initcmd)
939

    
940

    
941
def TestClusterInfo():
942
  """gnt-cluster info"""
943
  AssertCommand(["gnt-cluster", "info"])
944

    
945

    
946
def TestClusterRedistConf():
947
  """gnt-cluster redist-conf"""
948
  AssertCommand(["gnt-cluster", "redist-conf"])
949

    
950

    
951
def TestClusterGetmaster():
952
  """gnt-cluster getmaster"""
953
  AssertCommand(["gnt-cluster", "getmaster"])
954

    
955

    
956
def TestClusterVersion():
957
  """gnt-cluster version"""
958
  AssertCommand(["gnt-cluster", "version"])
959

    
960

    
961
def TestClusterRenewCrypto():
962
  """gnt-cluster renew-crypto"""
963
  master = qa_config.GetMasterNode()
964

    
965
  # Conflicting options
966
  cmd = ["gnt-cluster", "renew-crypto", "--force",
967
         "--new-cluster-certificate", "--new-confd-hmac-key"]
968
  conflicting = [
969
    ["--new-rapi-certificate", "--rapi-certificate=/dev/null"],
970
    ["--new-cluster-domain-secret", "--cluster-domain-secret=/dev/null"],
971
    ]
972
  for i in conflicting:
973
    AssertCommand(cmd + i, fail=True)
974

    
975
  # Invalid RAPI certificate
976
  cmd = ["gnt-cluster", "renew-crypto", "--force",
977
         "--rapi-certificate=/dev/null"]
978
  AssertCommand(cmd, fail=True)
979

    
980
  rapi_cert_backup = qa_utils.BackupFile(master.primary,
981
                                         pathutils.RAPI_CERT_FILE)
982
  try:
983
    # Custom RAPI certificate
984
    fh = tempfile.NamedTemporaryFile()
985

    
986
    # Ensure certificate doesn't cause "gnt-cluster verify" to complain
987
    validity = constants.SSL_CERT_EXPIRATION_WARN * 3
988

    
989
    utils.GenerateSelfSignedSslCert(fh.name, validity=validity)
990

    
991
    tmpcert = qa_utils.UploadFile(master.primary, fh.name)
992
    try:
993
      AssertCommand(["gnt-cluster", "renew-crypto", "--force",
994
                     "--rapi-certificate=%s" % tmpcert])
995
    finally:
996
      AssertCommand(["rm", "-f", tmpcert])
997

    
998
    # Custom cluster domain secret
999
    cds_fh = tempfile.NamedTemporaryFile()
1000
    cds_fh.write(utils.GenerateSecret())
1001
    cds_fh.write("\n")
1002
    cds_fh.flush()
1003

    
1004
    tmpcds = qa_utils.UploadFile(master.primary, cds_fh.name)
1005
    try:
1006
      AssertCommand(["gnt-cluster", "renew-crypto", "--force",
1007
                     "--cluster-domain-secret=%s" % tmpcds])
1008
    finally:
1009
      AssertCommand(["rm", "-f", tmpcds])
1010

    
1011
    # Normal case
1012
    AssertCommand(["gnt-cluster", "renew-crypto", "--force",
1013
                   "--new-cluster-certificate", "--new-confd-hmac-key",
1014
                   "--new-rapi-certificate", "--new-cluster-domain-secret"])
1015

    
1016
    # Restore RAPI certificate
1017
    AssertCommand(["gnt-cluster", "renew-crypto", "--force",
1018
                   "--rapi-certificate=%s" % rapi_cert_backup])
1019
  finally:
1020
    AssertCommand(["rm", "-f", rapi_cert_backup])
1021

    
1022

    
1023
def TestClusterBurnin():
1024
  """Burnin"""
1025
  master = qa_config.GetMasterNode()
1026

    
1027
  options = qa_config.get("options", {})
1028
  disk_template = options.get("burnin-disk-template", constants.DT_DRBD8)
1029
  parallel = options.get("burnin-in-parallel", False)
1030
  check_inst = options.get("burnin-check-instances", False)
1031
  do_rename = options.get("burnin-rename", "")
1032
  do_reboot = options.get("burnin-reboot", True)
1033
  reboot_types = options.get("reboot-types", constants.REBOOT_TYPES)
1034

    
1035
  # Get as many instances as we need
1036
  instances = []
1037
  try:
1038
    try:
1039
      num = qa_config.get("options", {}).get("burnin-instances", 1)
1040
      for _ in range(0, num):
1041
        instances.append(qa_config.AcquireInstance())
1042
    except qa_error.OutOfInstancesError:
1043
      print "Not enough instances, continuing anyway."
1044

    
1045
    if len(instances) < 1:
1046
      raise qa_error.Error("Burnin needs at least one instance")
1047

    
1048
    script = qa_utils.UploadFile(master.primary, "../tools/burnin")
1049
    try:
1050
      disks = qa_config.GetDiskOptions()
1051
      # Run burnin
1052
      cmd = [script,
1053
             "--os=%s" % qa_config.get("os"),
1054
             "--minmem-size=%s" % qa_config.get(constants.BE_MINMEM),
1055
             "--maxmem-size=%s" % qa_config.get(constants.BE_MAXMEM),
1056
             "--disk-size=%s" % ",".join([d.get("size") for d in disks]),
1057
             "--disk-growth=%s" % ",".join([d.get("growth") for d in disks]),
1058
             "--disk-template=%s" % disk_template]
1059
      if parallel:
1060
        cmd.append("--parallel")
1061
        cmd.append("--early-release")
1062
      if check_inst:
1063
        cmd.append("--http-check")
1064
      if do_rename:
1065
        cmd.append("--rename=%s" % do_rename)
1066
      if not do_reboot:
1067
        cmd.append("--no-reboot")
1068
      else:
1069
        cmd.append("--reboot-types=%s" % ",".join(reboot_types))
1070
      cmd += [inst.name for inst in instances]
1071
      AssertCommand(cmd)
1072
    finally:
1073
      AssertCommand(["rm", "-f", script])
1074

    
1075
  finally:
1076
    for inst in instances:
1077
      inst.Release()
1078

    
1079

    
1080
def TestClusterMasterFailover():
1081
  """gnt-cluster master-failover"""
1082
  master = qa_config.GetMasterNode()
1083
  failovermaster = qa_config.AcquireNode(exclude=master)
1084

    
1085
  cmd = ["gnt-cluster", "master-failover"]
1086
  try:
1087
    AssertCommand(cmd, node=failovermaster)
1088
    # Back to original master node
1089
    AssertCommand(cmd, node=master)
1090
  finally:
1091
    failovermaster.Release()
1092

    
1093

    
1094
def _NodeQueueDrainFile(node):
1095
  """Returns path to queue drain file for a node.
1096

1097
  """
1098
  return qa_utils.MakeNodePath(node, pathutils.JOB_QUEUE_DRAIN_FILE)
1099

    
1100

    
1101
def _AssertDrainFile(node, **kwargs):
1102
  """Checks for the queue drain file.
1103

1104
  """
1105
  AssertCommand(["test", "-f", _NodeQueueDrainFile(node)], node=node, **kwargs)
1106

    
1107

    
1108
def TestClusterMasterFailoverWithDrainedQueue():
1109
  """gnt-cluster master-failover with drained queue"""
1110
  master = qa_config.GetMasterNode()
1111
  failovermaster = qa_config.AcquireNode(exclude=master)
1112

    
1113
  # Ensure queue is not drained
1114
  for node in [master, failovermaster]:
1115
    _AssertDrainFile(node, fail=True)
1116

    
1117
  # Drain queue on failover master
1118
  AssertCommand(["touch", _NodeQueueDrainFile(failovermaster)],
1119
                node=failovermaster)
1120

    
1121
  cmd = ["gnt-cluster", "master-failover"]
1122
  try:
1123
    _AssertDrainFile(failovermaster)
1124
    AssertCommand(cmd, node=failovermaster)
1125
    _AssertDrainFile(master, fail=True)
1126
    _AssertDrainFile(failovermaster, fail=True)
1127

    
1128
    # Back to original master node
1129
    AssertCommand(cmd, node=master)
1130
  finally:
1131
    failovermaster.Release()
1132

    
1133
  # Ensure queue is not drained
1134
  for node in [master, failovermaster]:
1135
    _AssertDrainFile(node, fail=True)
1136

    
1137

    
1138
def TestClusterCopyfile():
1139
  """gnt-cluster copyfile"""
1140
  master = qa_config.GetMasterNode()
1141

    
1142
  uniqueid = utils.NewUUID()
1143

    
1144
  # Create temporary file
1145
  f = tempfile.NamedTemporaryFile()
1146
  f.write(uniqueid)
1147
  f.flush()
1148
  f.seek(0)
1149

    
1150
  # Upload file to master node
1151
  testname = qa_utils.UploadFile(master.primary, f.name)
1152
  try:
1153
    # Copy file to all nodes
1154
    AssertCommand(["gnt-cluster", "copyfile", testname])
1155
    _CheckFileOnAllNodes(testname, uniqueid)
1156
  finally:
1157
    _RemoveFileFromAllNodes(testname)
1158

    
1159

    
1160
def TestClusterCommand():
1161
  """gnt-cluster command"""
1162
  uniqueid = utils.NewUUID()
1163
  rfile = "/tmp/gnt%s" % utils.NewUUID()
1164
  rcmd = utils.ShellQuoteArgs(["echo", "-n", uniqueid])
1165
  cmd = utils.ShellQuoteArgs(["gnt-cluster", "command",
1166
                              "%s >%s" % (rcmd, rfile)])
1167

    
1168
  try:
1169
    AssertCommand(cmd)
1170
    _CheckFileOnAllNodes(rfile, uniqueid)
1171
  finally:
1172
    _RemoveFileFromAllNodes(rfile)
1173

    
1174

    
1175
def TestClusterDestroy():
1176
  """gnt-cluster destroy"""
1177
  AssertCommand(["gnt-cluster", "destroy", "--yes-do-it"])
1178

    
1179

    
1180
def TestClusterRepairDiskSizes():
1181
  """gnt-cluster repair-disk-sizes"""
1182
  AssertCommand(["gnt-cluster", "repair-disk-sizes"])
1183

    
1184

    
1185
def TestSetExclStorCluster(newvalue):
1186
  """Set the exclusive_storage node parameter at the cluster level.
1187

1188
  @type newvalue: bool
1189
  @param newvalue: New value of exclusive_storage
1190
  @rtype: bool
1191
  @return: The old value of exclusive_storage
1192

1193
  """
1194
  es_path = ["Default node parameters", "exclusive_storage"]
1195
  oldvalue = _GetClusterField(es_path)
1196
  AssertCommand(["gnt-cluster", "modify", "--node-parameters",
1197
                 "exclusive_storage=%s" % newvalue])
1198
  effvalue = _GetClusterField(es_path)
1199
  if effvalue != newvalue:
1200
    raise qa_error.Error("exclusive_storage has the wrong value: %s instead"
1201
                         " of %s" % (effvalue, newvalue))
1202
  qa_config.SetExclusiveStorage(newvalue)
1203
  return oldvalue
1204

    
1205

    
1206
def TestExclStorSharedPv(node):
1207
  """cluster-verify reports LVs that share the same PV with exclusive_storage.
1208

1209
  """
1210
  vgname = qa_config.get("vg-name", constants.DEFAULT_VG)
1211
  lvname1 = _QA_LV_PREFIX + "vol1"
1212
  lvname2 = _QA_LV_PREFIX + "vol2"
1213
  node_name = node.primary
1214
  AssertCommand(["lvcreate", "-L1G", "-n", lvname1, vgname], node=node_name)
1215
  AssertClusterVerify(fail=True, errors=[constants.CV_ENODEORPHANLV])
1216
  AssertCommand(["lvcreate", "-L1G", "-n", lvname2, vgname], node=node_name)
1217
  AssertClusterVerify(fail=True, errors=[constants.CV_ENODELVM,
1218
                                         constants.CV_ENODEORPHANLV])
1219
  AssertCommand(["lvremove", "-f", "/".join([vgname, lvname1])], node=node_name)
1220
  AssertCommand(["lvremove", "-f", "/".join([vgname, lvname2])], node=node_name)
1221
  AssertClusterVerify()