Statistics
| Branch: | Tag: | Revision:

root / qa / qa_cluster.py @ 68c8c3df

History | View | Annotate | Download (21.5 kB)

1
#
2
#
3

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

    
21

    
22
"""Cluster related QA tests.
23

24
"""
25

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

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

    
35
import qa_config
36
import qa_utils
37
import qa_error
38

    
39
from qa_utils import AssertEqual, AssertCommand, GetCommandOutput
40

    
41

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

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

    
48

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

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

    
56

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

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

    
65

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

    
69

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

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

76
  @type field: string
77
  @param field: Name of the field
78
  @rtype: bool
79
  @return: The effective value of the field
80

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

    
97

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

    
101

    
102
def _GetCVErrorCodes(cvout):
103
  ret = set()
104
  for l in cvout.splitlines():
105
    m = _CVERROR_RE.match(l)
106
    if m:
107
      ecode = m.group(1)
108
      ret.add(ecode)
109
  return ret
110

    
111

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

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

122
  """
123
  cvcmd = "gnt-cluster verify"
124
  mnode = qa_config.GetMasterNode()
125
  if errors:
126
    cvout = GetCommandOutput(mnode.primary, cvcmd + " --error-codes",
127
                             fail=True)
128
    actual = _GetCVErrorCodes(cvout)
129
    expected = compat.UniqueFrozenset(e for (_, e, _) in errors)
130
    if not actual.issuperset(expected):
131
      missing = expected.difference(actual)
132
      raise qa_error.Error("Cluster-verify didn't return these expected"
133
                           " errors: %s" % utils.CommaJoin(missing))
134
  else:
135
    AssertCommand(cvcmd, fail=fail, node=mnode)
136

    
137

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

    
144

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

    
151

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

    
156
  rapi_dir = os.path.dirname(pathutils.RAPI_USERS_FILE)
157

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

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

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

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

    
188
  if master.secondary:
189
    cmd.append("--secondary-ip=%s" % master.secondary)
190

    
191
  vgname = qa_config.get("vg-name", None)
192
  if vgname:
193
    cmd.append("--vg-name=%s" % vgname)
194

    
195
  master_netdev = qa_config.get("master-netdev", None)
196
  if master_netdev:
197
    cmd.append("--master-netdev=%s" % master_netdev)
198

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

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

    
212
  extra_args = qa_config.get("cluster-init-args")
213
  if extra_args:
214
    cmd.extend(extra_args)
215

    
216
  cmd.append(qa_config.get("name"))
217

    
218
  AssertCommand(cmd)
219

    
220
  cmd = ["gnt-cluster", "modify"]
221

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

    
231
  if len(cmd) > 2:
232
    AssertCommand(cmd)
233

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

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

    
245

    
246
def TestClusterRename():
247
  """gnt-cluster rename"""
248
  cmd = ["gnt-cluster", "rename", "-f"]
249

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

    
256
  for data in [
257
    cmd + [rename_target],
258
    _CLUSTER_VERIFY,
259
    cmd + [original_name],
260
    _CLUSTER_VERIFY,
261
    ]:
262
    AssertCommand(data)
263

    
264

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

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

    
274
  AssertCommand(_CLUSTER_VERIFY, fail=True)
275

    
276
  AssertCommand(["touch", oob_path_exists])
277
  AssertCommand(["chmod", "0400", oob_path_exists])
278
  AssertCommand(["gnt-cluster", "copyfile", oob_path_exists])
279

    
280
  try:
281
    AssertCommand(["gnt-cluster", "modify", "--node-parameters",
282
                   "oob_program=%s" % oob_path_exists])
283

    
284
    AssertCommand(_CLUSTER_VERIFY, fail=True)
285

    
286
    AssertCommand(["chmod", "0500", oob_path_exists])
287
    AssertCommand(["gnt-cluster", "copyfile", oob_path_exists])
288

    
289
    AssertCommand(_CLUSTER_VERIFY)
290
  finally:
291
    AssertCommand(["gnt-cluster", "command", "rm", oob_path_exists])
292

    
293
  AssertCommand(["gnt-cluster", "modify", "--node-parameters",
294
                 "oob_program="])
295

    
296

    
297
def TestClusterEpo():
298
  """gnt-cluster epo"""
299
  master = qa_config.GetMasterNode()
300

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

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

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

    
316
  # This shouldn't fail
317
  AssertCommand(["gnt-cluster", "epo", "-f", "--all"])
318

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

    
326
  # Now start everything again
327
  AssertCommand(["gnt-cluster", "epo", "--on", "-f", "--all"])
328

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

    
335

    
336
def TestClusterVerify():
337
  """gnt-cluster verify"""
338
  AssertCommand(_CLUSTER_VERIFY)
339
  AssertCommand(["gnt-cluster", "verify-disks"])
340

    
341

    
342
def TestJobqueue():
343
  """gnt-debug test-jobqueue"""
344
  AssertCommand(["gnt-debug", "test-jobqueue"])
345

    
346

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

    
354

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

    
378

    
379
def TestClusterModifyEmpty():
380
  """gnt-cluster modify"""
381
  AssertCommand(["gnt-cluster", "modify"], fail=True)
382

    
383

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

    
389

    
390
def TestClusterModifyBe():
391
  """gnt-cluster modify -B"""
392
  for fail, cmd in [
393
    # max/min mem
394
    (False, ["gnt-cluster", "modify", "-B", "maxmem=256"]),
395
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *maxmem: 256$'"]),
396
    (False, ["gnt-cluster", "modify", "-B", "minmem=256"]),
397
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *minmem: 256$'"]),
398
    (True, ["gnt-cluster", "modify", "-B", "maxmem=a"]),
399
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *maxmem: 256$'"]),
400
    (True, ["gnt-cluster", "modify", "-B", "minmem=a"]),
401
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *minmem: 256$'"]),
402
    (False, ["gnt-cluster", "modify", "-B", "maxmem=128,minmem=128"]),
403
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *maxmem: 128$'"]),
404
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *minmem: 128$'"]),
405
    # vcpus
406
    (False, ["gnt-cluster", "modify", "-B", "vcpus=4"]),
407
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *vcpus: 4$'"]),
408
    (True, ["gnt-cluster", "modify", "-B", "vcpus=a"]),
409
    (False, ["gnt-cluster", "modify", "-B", "vcpus=1"]),
410
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *vcpus: 1$'"]),
411
    # auto_balance
412
    (False, ["gnt-cluster", "modify", "-B", "auto_balance=False"]),
413
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *auto_balance: False$'"]),
414
    (True, ["gnt-cluster", "modify", "-B", "auto_balance=1"]),
415
    (False, ["gnt-cluster", "modify", "-B", "auto_balance=True"]),
416
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *auto_balance: True$'"]),
417
    ]:
418
    AssertCommand(cmd, fail=fail)
419

    
420
  # redo the original-requested BE parameters, if any
421
  bep = qa_config.get("backend-parameters", "")
422
  if bep:
423
    AssertCommand(["gnt-cluster", "modify", "-B", bep])
424

    
425

    
426
def TestClusterInfo():
427
  """gnt-cluster info"""
428
  AssertCommand(["gnt-cluster", "info"])
429

    
430

    
431
def TestClusterRedistConf():
432
  """gnt-cluster redist-conf"""
433
  AssertCommand(["gnt-cluster", "redist-conf"])
434

    
435

    
436
def TestClusterGetmaster():
437
  """gnt-cluster getmaster"""
438
  AssertCommand(["gnt-cluster", "getmaster"])
439

    
440

    
441
def TestClusterVersion():
442
  """gnt-cluster version"""
443
  AssertCommand(["gnt-cluster", "version"])
444

    
445

    
446
def TestClusterRenewCrypto():
447
  """gnt-cluster renew-crypto"""
448
  master = qa_config.GetMasterNode()
449

    
450
  # Conflicting options
451
  cmd = ["gnt-cluster", "renew-crypto", "--force",
452
         "--new-cluster-certificate", "--new-confd-hmac-key"]
453
  conflicting = [
454
    ["--new-rapi-certificate", "--rapi-certificate=/dev/null"],
455
    ["--new-cluster-domain-secret", "--cluster-domain-secret=/dev/null"],
456
    ]
457
  for i in conflicting:
458
    AssertCommand(cmd + i, fail=True)
459

    
460
  # Invalid RAPI certificate
461
  cmd = ["gnt-cluster", "renew-crypto", "--force",
462
         "--rapi-certificate=/dev/null"]
463
  AssertCommand(cmd, fail=True)
464

    
465
  rapi_cert_backup = qa_utils.BackupFile(master.primary,
466
                                         pathutils.RAPI_CERT_FILE)
467
  try:
468
    # Custom RAPI certificate
469
    fh = tempfile.NamedTemporaryFile()
470

    
471
    # Ensure certificate doesn't cause "gnt-cluster verify" to complain
472
    validity = constants.SSL_CERT_EXPIRATION_WARN * 3
473

    
474
    utils.GenerateSelfSignedSslCert(fh.name, validity=validity)
475

    
476
    tmpcert = qa_utils.UploadFile(master.primary, fh.name)
477
    try:
478
      AssertCommand(["gnt-cluster", "renew-crypto", "--force",
479
                     "--rapi-certificate=%s" % tmpcert])
480
    finally:
481
      AssertCommand(["rm", "-f", tmpcert])
482

    
483
    # Custom cluster domain secret
484
    cds_fh = tempfile.NamedTemporaryFile()
485
    cds_fh.write(utils.GenerateSecret())
486
    cds_fh.write("\n")
487
    cds_fh.flush()
488

    
489
    tmpcds = qa_utils.UploadFile(master.primary, cds_fh.name)
490
    try:
491
      AssertCommand(["gnt-cluster", "renew-crypto", "--force",
492
                     "--cluster-domain-secret=%s" % tmpcds])
493
    finally:
494
      AssertCommand(["rm", "-f", tmpcds])
495

    
496
    # Normal case
497
    AssertCommand(["gnt-cluster", "renew-crypto", "--force",
498
                   "--new-cluster-certificate", "--new-confd-hmac-key",
499
                   "--new-rapi-certificate", "--new-cluster-domain-secret"])
500

    
501
    # Restore RAPI certificate
502
    AssertCommand(["gnt-cluster", "renew-crypto", "--force",
503
                   "--rapi-certificate=%s" % rapi_cert_backup])
504
  finally:
505
    AssertCommand(["rm", "-f", rapi_cert_backup])
506

    
507

    
508
def TestClusterBurnin():
509
  """Burnin"""
510
  master = qa_config.GetMasterNode()
511

    
512
  options = qa_config.get("options", {})
513
  disk_template = options.get("burnin-disk-template", constants.DT_DRBD8)
514
  parallel = options.get("burnin-in-parallel", False)
515
  check_inst = options.get("burnin-check-instances", False)
516
  do_rename = options.get("burnin-rename", "")
517
  do_reboot = options.get("burnin-reboot", True)
518
  reboot_types = options.get("reboot-types", constants.REBOOT_TYPES)
519

    
520
  # Get as many instances as we need
521
  instances = []
522
  try:
523
    try:
524
      num = qa_config.get("options", {}).get("burnin-instances", 1)
525
      for _ in range(0, num):
526
        instances.append(qa_config.AcquireInstance())
527
    except qa_error.OutOfInstancesError:
528
      print "Not enough instances, continuing anyway."
529

    
530
    if len(instances) < 1:
531
      raise qa_error.Error("Burnin needs at least one instance")
532

    
533
    script = qa_utils.UploadFile(master.primary, "../tools/burnin")
534
    try:
535
      # Run burnin
536
      cmd = [script,
537
             "--os=%s" % qa_config.get("os"),
538
             "--minmem-size=%s" % qa_config.get(constants.BE_MINMEM),
539
             "--maxmem-size=%s" % qa_config.get(constants.BE_MAXMEM),
540
             "--disk-size=%s" % ",".join(qa_config.get("disk")),
541
             "--disk-growth=%s" % ",".join(qa_config.get("disk-growth")),
542
             "--disk-template=%s" % disk_template]
543
      if parallel:
544
        cmd.append("--parallel")
545
        cmd.append("--early-release")
546
      if check_inst:
547
        cmd.append("--http-check")
548
      if do_rename:
549
        cmd.append("--rename=%s" % do_rename)
550
      if not do_reboot:
551
        cmd.append("--no-reboot")
552
      else:
553
        cmd.append("--reboot-types=%s" % ",".join(reboot_types))
554
      cmd += [inst.name for inst in instances]
555
      AssertCommand(cmd)
556
    finally:
557
      AssertCommand(["rm", "-f", script])
558

    
559
  finally:
560
    for inst in instances:
561
      inst.Release()
562

    
563

    
564
def TestClusterMasterFailover():
565
  """gnt-cluster master-failover"""
566
  master = qa_config.GetMasterNode()
567
  failovermaster = qa_config.AcquireNode(exclude=master)
568

    
569
  cmd = ["gnt-cluster", "master-failover"]
570
  try:
571
    AssertCommand(cmd, node=failovermaster)
572
    # Back to original master node
573
    AssertCommand(cmd, node=master)
574
  finally:
575
    failovermaster.Release()
576

    
577

    
578
def TestClusterMasterFailoverWithDrainedQueue():
579
  """gnt-cluster master-failover with drained queue"""
580
  drain_check = ["test", "-f", pathutils.JOB_QUEUE_DRAIN_FILE]
581

    
582
  master = qa_config.GetMasterNode()
583
  failovermaster = qa_config.AcquireNode(exclude=master)
584

    
585
  # Ensure queue is not drained
586
  for node in [master, failovermaster]:
587
    AssertCommand(drain_check, node=node, fail=True)
588

    
589
  # Drain queue on failover master
590
  AssertCommand(["touch", pathutils.JOB_QUEUE_DRAIN_FILE], node=failovermaster)
591

    
592
  cmd = ["gnt-cluster", "master-failover"]
593
  try:
594
    AssertCommand(drain_check, node=failovermaster)
595
    AssertCommand(cmd, node=failovermaster)
596
    AssertCommand(drain_check, fail=True)
597
    AssertCommand(drain_check, node=failovermaster, fail=True)
598

    
599
    # Back to original master node
600
    AssertCommand(cmd, node=master)
601
  finally:
602
    failovermaster.Release()
603

    
604
  AssertCommand(drain_check, fail=True)
605
  AssertCommand(drain_check, node=failovermaster, fail=True)
606

    
607

    
608
def TestClusterCopyfile():
609
  """gnt-cluster copyfile"""
610
  master = qa_config.GetMasterNode()
611

    
612
  uniqueid = utils.NewUUID()
613

    
614
  # Create temporary file
615
  f = tempfile.NamedTemporaryFile()
616
  f.write(uniqueid)
617
  f.flush()
618
  f.seek(0)
619

    
620
  # Upload file to master node
621
  testname = qa_utils.UploadFile(master.primary, f.name)
622
  try:
623
    # Copy file to all nodes
624
    AssertCommand(["gnt-cluster", "copyfile", testname])
625
    _CheckFileOnAllNodes(testname, uniqueid)
626
  finally:
627
    _RemoveFileFromAllNodes(testname)
628

    
629

    
630
def TestClusterCommand():
631
  """gnt-cluster command"""
632
  uniqueid = utils.NewUUID()
633
  rfile = "/tmp/gnt%s" % utils.NewUUID()
634
  rcmd = utils.ShellQuoteArgs(["echo", "-n", uniqueid])
635
  cmd = utils.ShellQuoteArgs(["gnt-cluster", "command",
636
                              "%s >%s" % (rcmd, rfile)])
637

    
638
  try:
639
    AssertCommand(cmd)
640
    _CheckFileOnAllNodes(rfile, uniqueid)
641
  finally:
642
    _RemoveFileFromAllNodes(rfile)
643

    
644

    
645
def TestClusterDestroy():
646
  """gnt-cluster destroy"""
647
  AssertCommand(["gnt-cluster", "destroy", "--yes-do-it"])
648

    
649

    
650
def TestClusterRepairDiskSizes():
651
  """gnt-cluster repair-disk-sizes"""
652
  AssertCommand(["gnt-cluster", "repair-disk-sizes"])
653

    
654

    
655
def TestSetExclStorCluster(newvalue):
656
  """Set the exclusive_storage node parameter at the cluster level.
657

658
  @type newvalue: bool
659
  @param newvalue: New value of exclusive_storage
660
  @rtype: bool
661
  @return: The old value of exclusive_storage
662

663
  """
664
  oldvalue = _GetBoolClusterField("exclusive_storage")
665
  AssertCommand(["gnt-cluster", "modify", "--node-parameters",
666
                 "exclusive_storage=%s" % newvalue])
667
  effvalue = _GetBoolClusterField("exclusive_storage")
668
  if effvalue != newvalue:
669
    raise qa_error.Error("exclusive_storage has the wrong value: %s instead"
670
                         " of %s" % (effvalue, newvalue))
671
  qa_config.SetExclusiveStorage(newvalue)
672
  return oldvalue
673

    
674

    
675
def TestExclStorSharedPv(node):
676
  """cluster-verify reports LVs that share the same PV with exclusive_storage.
677

678
  """
679
  vgname = qa_config.get("vg-name", constants.DEFAULT_VG)
680
  lvname1 = _QA_LV_PREFIX + "vol1"
681
  lvname2 = _QA_LV_PREFIX + "vol2"
682
  node_name = node.primary
683
  AssertCommand(["lvcreate", "-L1G", "-n", lvname1, vgname], node=node_name)
684
  AssertClusterVerify(fail=True, errors=[constants.CV_ENODEORPHANLV])
685
  AssertCommand(["lvcreate", "-L1G", "-n", lvname2, vgname], node=node_name)
686
  AssertClusterVerify(fail=True, errors=[constants.CV_ENODELVM,
687
                                         constants.CV_ENODEORPHANLV])
688
  AssertCommand(["lvremove", "-f", "/".join([vgname, lvname1])], node=node_name)
689
  AssertCommand(["lvremove", "-f", "/".join([vgname, lvname2])], node=node_name)
690
  AssertClusterVerify()