Statistics
| Branch: | Tag: | Revision:

root / qa / qa_cluster.py @ 587f8ff6

History | View | Annotate | Download (18.4 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
#: cluster verify command
43
_CLUSTER_VERIFY = ["gnt-cluster", "verify"]
44

    
45

    
46
def _RemoveFileFromAllNodes(filename):
47
  """Removes a file from all nodes.
48

49
  """
50
  for node in qa_config.get("nodes"):
51
    AssertCommand(["rm", "-f", filename], node=node)
52

    
53

    
54
def _CheckFileOnAllNodes(filename, content):
55
  """Verifies the content of the given file on all nodes.
56

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

    
62

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

    
66

    
67
def _GetCVErrorCodes(cvout):
68
  ret = set()
69
  for l in cvout.splitlines():
70
    m = _CVERROR_RE.match(l)
71
    if m:
72
      ecode = m.group(1)
73
      ret.add(ecode)
74
  return ret
75

    
76

    
77
def AssertClusterVerify(fail=False, errors=None):
78
  """Run cluster-verify and check the result
79

80
  @type fail: bool
81
  @param fail: if cluster-verify is expected to fail instead of succeeding
82
  @type errors: list of tuples
83
  @param errors: List of CV_XXX errors that are expected; if specified, all the
84
      errors listed must appear in cluster-verify output. A non-empty value
85
      implies C{fail=True}.
86

87
  """
88
  cvcmd = "gnt-cluster verify"
89
  mnode = qa_config.GetMasterNode()
90
  if errors:
91
    cvout = GetCommandOutput(mnode["primary"], cvcmd + " --error-codes",
92
                             fail=True)
93
    actual = _GetCVErrorCodes(cvout)
94
    expected = compat.UniqueFrozenset(e for (_, e, _) in errors)
95
    if not actual.issuperset(expected):
96
      missing = expected.difference(actual)
97
      raise qa_error.Error("Cluster-verify didn't return these expected"
98
                           " errors: %s" % utils.CommaJoin(missing))
99
  else:
100
    AssertCommand(cvcmd, fail=fail, node=mnode)
101

    
102

    
103
# data for testing failures due to bad keys/values for disk parameters
104
_FAIL_PARAMS = ["nonexistent:resync-rate=1",
105
                "drbd:nonexistent=1",
106
                "drbd:resync-rate=invalid",
107
                ]
108

    
109

    
110
def TestClusterInitDisk():
111
  """gnt-cluster init -D"""
112
  name = qa_config.get("name")
113
  for param in _FAIL_PARAMS:
114
    AssertCommand(["gnt-cluster", "init", "-D", param, name], fail=True)
115

    
116

    
117
def TestClusterInit(rapi_user, rapi_secret):
118
  """gnt-cluster init"""
119
  master = qa_config.GetMasterNode()
120

    
121
  rapi_dir = os.path.dirname(pathutils.RAPI_USERS_FILE)
122

    
123
  # First create the RAPI credentials
124
  fh = tempfile.NamedTemporaryFile()
125
  try:
126
    fh.write("%s %s write\n" % (rapi_user, rapi_secret))
127
    fh.flush()
128

    
129
    tmpru = qa_utils.UploadFile(master["primary"], fh.name)
130
    try:
131
      AssertCommand(["mkdir", "-p", rapi_dir])
132
      AssertCommand(["mv", tmpru, pathutils.RAPI_USERS_FILE])
133
    finally:
134
      AssertCommand(["rm", "-f", tmpru])
135
  finally:
136
    fh.close()
137

    
138
  # Initialize cluster
139
  cmd = [
140
    "gnt-cluster", "init",
141
    "--primary-ip-version=%d" % qa_config.get("primary_ip_version", 4),
142
    "--enabled-hypervisors=%s" % ",".join(qa_config.GetEnabledHypervisors()),
143
    ]
144

    
145
  for spec_type in ("mem-size", "disk-size", "disk-count", "cpu-count",
146
                    "nic-count"):
147
    for spec_val in ("min", "max", "std"):
148
      spec = qa_config.get("ispec_%s_%s" %
149
                           (spec_type.replace("-", "_"), spec_val), None)
150
      if spec:
151
        cmd.append("--specs-%s=%s=%d" % (spec_type, spec_val, spec))
152

    
153
  if master.get("secondary", None):
154
    cmd.append("--secondary-ip=%s" % master["secondary"])
155

    
156
  master_netdev = qa_config.get("master-netdev", None)
157
  if master_netdev:
158
    cmd.append("--master-netdev=%s" % master_netdev)
159

    
160
  nicparams = qa_config.get("default-nicparams", None)
161
  if nicparams:
162
    cmd.append("--nic-parameters=%s" %
163
               ",".join(utils.FormatKeyValue(nicparams)))
164

    
165
  cmd.append(qa_config.get("name"))
166
  AssertCommand(cmd)
167

    
168
  cmd = ["gnt-cluster", "modify"]
169

    
170
  # hypervisor parameter modifications
171
  hvp = qa_config.get("hypervisor-parameters", {})
172
  for k, v in hvp.items():
173
    cmd.extend(["-H", "%s:%s" % (k, v)])
174
  # backend parameter modifications
175
  bep = qa_config.get("backend-parameters", "")
176
  if bep:
177
    cmd.extend(["-B", bep])
178

    
179
  if len(cmd) > 2:
180
    AssertCommand(cmd)
181

    
182
  # OS parameters
183
  osp = qa_config.get("os-parameters", {})
184
  for k, v in osp.items():
185
    AssertCommand(["gnt-os", "modify", "-O", v, k])
186

    
187
  # OS hypervisor parameters
188
  os_hvp = qa_config.get("os-hvp", {})
189
  for os_name in os_hvp:
190
    for hv, hvp in os_hvp[os_name].items():
191
      AssertCommand(["gnt-os", "modify", "-H", "%s:%s" % (hv, hvp), os_name])
192

    
193

    
194
def TestClusterRename():
195
  """gnt-cluster rename"""
196
  cmd = ["gnt-cluster", "rename", "-f"]
197

    
198
  original_name = qa_config.get("name")
199
  rename_target = qa_config.get("rename", None)
200
  if rename_target is None:
201
    print qa_utils.FormatError('"rename" entry is missing')
202
    return
203

    
204
  for data in [
205
    cmd + [rename_target],
206
    _CLUSTER_VERIFY,
207
    cmd + [original_name],
208
    _CLUSTER_VERIFY,
209
    ]:
210
    AssertCommand(data)
211

    
212

    
213
def TestClusterOob():
214
  """out-of-band framework"""
215
  oob_path_exists = "/tmp/ganeti-qa-oob-does-exist-%s" % utils.NewUUID()
216

    
217
  AssertCommand(_CLUSTER_VERIFY)
218
  AssertCommand(["gnt-cluster", "modify", "--node-parameters",
219
                 "oob_program=/tmp/ganeti-qa-oob-does-not-exist-%s" %
220
                 utils.NewUUID()])
221

    
222
  AssertCommand(_CLUSTER_VERIFY, fail=True)
223

    
224
  AssertCommand(["touch", oob_path_exists])
225
  AssertCommand(["chmod", "0400", oob_path_exists])
226
  AssertCommand(["gnt-cluster", "copyfile", oob_path_exists])
227

    
228
  try:
229
    AssertCommand(["gnt-cluster", "modify", "--node-parameters",
230
                   "oob_program=%s" % oob_path_exists])
231

    
232
    AssertCommand(_CLUSTER_VERIFY, fail=True)
233

    
234
    AssertCommand(["chmod", "0500", oob_path_exists])
235
    AssertCommand(["gnt-cluster", "copyfile", oob_path_exists])
236

    
237
    AssertCommand(_CLUSTER_VERIFY)
238
  finally:
239
    AssertCommand(["gnt-cluster", "command", "rm", oob_path_exists])
240

    
241
  AssertCommand(["gnt-cluster", "modify", "--node-parameters",
242
                 "oob_program="])
243

    
244

    
245
def TestClusterEpo():
246
  """gnt-cluster epo"""
247
  master = qa_config.GetMasterNode()
248

    
249
  # Assert that OOB is unavailable for all nodes
250
  result_output = GetCommandOutput(master["primary"],
251
                                   "gnt-node list --verbose --no-headers -o"
252
                                   " powered")
253
  AssertEqual(compat.all(powered == "(unavail)"
254
                         for powered in result_output.splitlines()), True)
255

    
256
  # Conflicting
257
  AssertCommand(["gnt-cluster", "epo", "--groups", "--all"], fail=True)
258
  # --all doesn't expect arguments
259
  AssertCommand(["gnt-cluster", "epo", "--all", "some_arg"], fail=True)
260

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

    
264
  # This shouldn't fail
265
  AssertCommand(["gnt-cluster", "epo", "-f", "--all"])
266

    
267
  # All instances should have been stopped now
268
  result_output = GetCommandOutput(master["primary"],
269
                                   "gnt-instance list --no-headers -o status")
270
  # ERROR_down because the instance is stopped but not recorded as such
271
  AssertEqual(compat.all(status == "ERROR_down"
272
                         for status in result_output.splitlines()), True)
273

    
274
  # Now start everything again
275
  AssertCommand(["gnt-cluster", "epo", "--on", "-f", "--all"])
276

    
277
  # All instances should have been started now
278
  result_output = GetCommandOutput(master["primary"],
279
                                   "gnt-instance list --no-headers -o status")
280
  AssertEqual(compat.all(status == "running"
281
                         for status in result_output.splitlines()), True)
282

    
283

    
284
def TestClusterVerify():
285
  """gnt-cluster verify"""
286
  AssertCommand(_CLUSTER_VERIFY)
287
  AssertCommand(["gnt-cluster", "verify-disks"])
288

    
289

    
290
def TestJobqueue():
291
  """gnt-debug test-jobqueue"""
292
  AssertCommand(["gnt-debug", "test-jobqueue"])
293

    
294

    
295
def TestDelay(node):
296
  """gnt-debug delay"""
297
  AssertCommand(["gnt-debug", "delay", "1"])
298
  AssertCommand(["gnt-debug", "delay", "--no-master", "1"])
299
  AssertCommand(["gnt-debug", "delay", "--no-master",
300
                 "-n", node["primary"], "1"])
301

    
302

    
303
def TestClusterReservedLvs():
304
  """gnt-cluster reserved lvs"""
305
  for fail, cmd in [
306
    (False, _CLUSTER_VERIFY),
307
    (False, ["gnt-cluster", "modify", "--reserved-lvs", ""]),
308
    (False, ["lvcreate", "-L1G", "-nqa-test", "xenvg"]),
309
    (True, _CLUSTER_VERIFY),
310
    (False, ["gnt-cluster", "modify", "--reserved-lvs",
311
             "xenvg/qa-test,.*/other-test"]),
312
    (False, _CLUSTER_VERIFY),
313
    (False, ["gnt-cluster", "modify", "--reserved-lvs", ".*/qa-.*"]),
314
    (False, _CLUSTER_VERIFY),
315
    (False, ["gnt-cluster", "modify", "--reserved-lvs", ""]),
316
    (True, _CLUSTER_VERIFY),
317
    (False, ["lvremove", "-f", "xenvg/qa-test"]),
318
    (False, _CLUSTER_VERIFY),
319
    ]:
320
    AssertCommand(cmd, fail=fail)
321

    
322

    
323
def TestClusterModifyEmpty():
324
  """gnt-cluster modify"""
325
  AssertCommand(["gnt-cluster", "modify"], fail=True)
326

    
327

    
328
def TestClusterModifyDisk():
329
  """gnt-cluster modify -D"""
330
  for param in _FAIL_PARAMS:
331
    AssertCommand(["gnt-cluster", "modify", "-D", param], fail=True)
332

    
333

    
334
def TestClusterModifyBe():
335
  """gnt-cluster modify -B"""
336
  for fail, cmd in [
337
    # max/min mem
338
    (False, ["gnt-cluster", "modify", "-B", "maxmem=256"]),
339
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *maxmem: 256$'"]),
340
    (False, ["gnt-cluster", "modify", "-B", "minmem=256"]),
341
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *minmem: 256$'"]),
342
    (True, ["gnt-cluster", "modify", "-B", "maxmem=a"]),
343
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *maxmem: 256$'"]),
344
    (True, ["gnt-cluster", "modify", "-B", "minmem=a"]),
345
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *minmem: 256$'"]),
346
    (False, ["gnt-cluster", "modify", "-B", "maxmem=128,minmem=128"]),
347
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *maxmem: 128$'"]),
348
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *minmem: 128$'"]),
349
    # vcpus
350
    (False, ["gnt-cluster", "modify", "-B", "vcpus=4"]),
351
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *vcpus: 4$'"]),
352
    (True, ["gnt-cluster", "modify", "-B", "vcpus=a"]),
353
    (False, ["gnt-cluster", "modify", "-B", "vcpus=1"]),
354
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *vcpus: 1$'"]),
355
    # auto_balance
356
    (False, ["gnt-cluster", "modify", "-B", "auto_balance=False"]),
357
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *auto_balance: False$'"]),
358
    (True, ["gnt-cluster", "modify", "-B", "auto_balance=1"]),
359
    (False, ["gnt-cluster", "modify", "-B", "auto_balance=True"]),
360
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *auto_balance: True$'"]),
361
    ]:
362
    AssertCommand(cmd, fail=fail)
363

    
364
  # redo the original-requested BE parameters, if any
365
  bep = qa_config.get("backend-parameters", "")
366
  if bep:
367
    AssertCommand(["gnt-cluster", "modify", "-B", bep])
368

    
369

    
370
def TestClusterInfo():
371
  """gnt-cluster info"""
372
  AssertCommand(["gnt-cluster", "info"])
373

    
374

    
375
def TestClusterRedistConf():
376
  """gnt-cluster redist-conf"""
377
  AssertCommand(["gnt-cluster", "redist-conf"])
378

    
379

    
380
def TestClusterGetmaster():
381
  """gnt-cluster getmaster"""
382
  AssertCommand(["gnt-cluster", "getmaster"])
383

    
384

    
385
def TestClusterVersion():
386
  """gnt-cluster version"""
387
  AssertCommand(["gnt-cluster", "version"])
388

    
389

    
390
def TestClusterRenewCrypto():
391
  """gnt-cluster renew-crypto"""
392
  master = qa_config.GetMasterNode()
393

    
394
  # Conflicting options
395
  cmd = ["gnt-cluster", "renew-crypto", "--force",
396
         "--new-cluster-certificate", "--new-confd-hmac-key"]
397
  conflicting = [
398
    ["--new-rapi-certificate", "--rapi-certificate=/dev/null"],
399
    ["--new-cluster-domain-secret", "--cluster-domain-secret=/dev/null"],
400
    ]
401
  for i in conflicting:
402
    AssertCommand(cmd + i, fail=True)
403

    
404
  # Invalid RAPI certificate
405
  cmd = ["gnt-cluster", "renew-crypto", "--force",
406
         "--rapi-certificate=/dev/null"]
407
  AssertCommand(cmd, fail=True)
408

    
409
  rapi_cert_backup = qa_utils.BackupFile(master["primary"],
410
                                         pathutils.RAPI_CERT_FILE)
411
  try:
412
    # Custom RAPI certificate
413
    fh = tempfile.NamedTemporaryFile()
414

    
415
    # Ensure certificate doesn't cause "gnt-cluster verify" to complain
416
    validity = constants.SSL_CERT_EXPIRATION_WARN * 3
417

    
418
    utils.GenerateSelfSignedSslCert(fh.name, validity=validity)
419

    
420
    tmpcert = qa_utils.UploadFile(master["primary"], fh.name)
421
    try:
422
      AssertCommand(["gnt-cluster", "renew-crypto", "--force",
423
                     "--rapi-certificate=%s" % tmpcert])
424
    finally:
425
      AssertCommand(["rm", "-f", tmpcert])
426

    
427
    # Custom cluster domain secret
428
    cds_fh = tempfile.NamedTemporaryFile()
429
    cds_fh.write(utils.GenerateSecret())
430
    cds_fh.write("\n")
431
    cds_fh.flush()
432

    
433
    tmpcds = qa_utils.UploadFile(master["primary"], cds_fh.name)
434
    try:
435
      AssertCommand(["gnt-cluster", "renew-crypto", "--force",
436
                     "--cluster-domain-secret=%s" % tmpcds])
437
    finally:
438
      AssertCommand(["rm", "-f", tmpcds])
439

    
440
    # Normal case
441
    AssertCommand(["gnt-cluster", "renew-crypto", "--force",
442
                   "--new-cluster-certificate", "--new-confd-hmac-key",
443
                   "--new-rapi-certificate", "--new-cluster-domain-secret"])
444

    
445
    # Restore RAPI certificate
446
    AssertCommand(["gnt-cluster", "renew-crypto", "--force",
447
                   "--rapi-certificate=%s" % rapi_cert_backup])
448
  finally:
449
    AssertCommand(["rm", "-f", rapi_cert_backup])
450

    
451

    
452
def TestClusterBurnin():
453
  """Burnin"""
454
  master = qa_config.GetMasterNode()
455

    
456
  options = qa_config.get("options", {})
457
  disk_template = options.get("burnin-disk-template", "drbd")
458
  parallel = options.get("burnin-in-parallel", False)
459
  check_inst = options.get("burnin-check-instances", False)
460
  do_rename = options.get("burnin-rename", "")
461
  do_reboot = options.get("burnin-reboot", True)
462
  reboot_types = options.get("reboot-types", constants.REBOOT_TYPES)
463

    
464
  # Get as many instances as we need
465
  instances = []
466
  try:
467
    try:
468
      num = qa_config.get("options", {}).get("burnin-instances", 1)
469
      for _ in range(0, num):
470
        instances.append(qa_config.AcquireInstance())
471
    except qa_error.OutOfInstancesError:
472
      print "Not enough instances, continuing anyway."
473

    
474
    if len(instances) < 1:
475
      raise qa_error.Error("Burnin needs at least one instance")
476

    
477
    script = qa_utils.UploadFile(master["primary"], "../tools/burnin")
478
    try:
479
      # Run burnin
480
      cmd = [script,
481
             "--os=%s" % qa_config.get("os"),
482
             "--minmem-size=%s" % qa_config.get(constants.BE_MINMEM),
483
             "--maxmem-size=%s" % qa_config.get(constants.BE_MAXMEM),
484
             "--disk-size=%s" % ",".join(qa_config.get("disk")),
485
             "--disk-growth=%s" % ",".join(qa_config.get("disk-growth")),
486
             "--disk-template=%s" % disk_template]
487
      if parallel:
488
        cmd.append("--parallel")
489
        cmd.append("--early-release")
490
      if check_inst:
491
        cmd.append("--http-check")
492
      if do_rename:
493
        cmd.append("--rename=%s" % do_rename)
494
      if not do_reboot:
495
        cmd.append("--no-reboot")
496
      else:
497
        cmd.append("--reboot-types=%s" % ",".join(reboot_types))
498
      cmd += [inst["name"] for inst in instances]
499
      AssertCommand(cmd)
500
    finally:
501
      AssertCommand(["rm", "-f", script])
502

    
503
  finally:
504
    for inst in instances:
505
      qa_config.ReleaseInstance(inst)
506

    
507

    
508
def TestClusterMasterFailover():
509
  """gnt-cluster master-failover"""
510
  master = qa_config.GetMasterNode()
511
  failovermaster = qa_config.AcquireNode(exclude=master)
512

    
513
  cmd = ["gnt-cluster", "master-failover"]
514
  try:
515
    AssertCommand(cmd, node=failovermaster)
516
    # Back to original master node
517
    AssertCommand(cmd, node=master)
518
  finally:
519
    qa_config.ReleaseNode(failovermaster)
520

    
521

    
522
def TestClusterMasterFailoverWithDrainedQueue():
523
  """gnt-cluster master-failover with drained queue"""
524
  drain_check = ["test", "-f", pathutils.JOB_QUEUE_DRAIN_FILE]
525

    
526
  master = qa_config.GetMasterNode()
527
  failovermaster = qa_config.AcquireNode(exclude=master)
528

    
529
  # Ensure queue is not drained
530
  for node in [master, failovermaster]:
531
    AssertCommand(drain_check, node=node, fail=True)
532

    
533
  # Drain queue on failover master
534
  AssertCommand(["touch", pathutils.JOB_QUEUE_DRAIN_FILE], node=failovermaster)
535

    
536
  cmd = ["gnt-cluster", "master-failover"]
537
  try:
538
    AssertCommand(drain_check, node=failovermaster)
539
    AssertCommand(cmd, node=failovermaster)
540
    AssertCommand(drain_check, fail=True)
541
    AssertCommand(drain_check, node=failovermaster, fail=True)
542

    
543
    # Back to original master node
544
    AssertCommand(cmd, node=master)
545
  finally:
546
    qa_config.ReleaseNode(failovermaster)
547

    
548
  AssertCommand(drain_check, fail=True)
549
  AssertCommand(drain_check, node=failovermaster, fail=True)
550

    
551

    
552
def TestClusterCopyfile():
553
  """gnt-cluster copyfile"""
554
  master = qa_config.GetMasterNode()
555

    
556
  uniqueid = utils.NewUUID()
557

    
558
  # Create temporary file
559
  f = tempfile.NamedTemporaryFile()
560
  f.write(uniqueid)
561
  f.flush()
562
  f.seek(0)
563

    
564
  # Upload file to master node
565
  testname = qa_utils.UploadFile(master["primary"], f.name)
566
  try:
567
    # Copy file to all nodes
568
    AssertCommand(["gnt-cluster", "copyfile", testname])
569
    _CheckFileOnAllNodes(testname, uniqueid)
570
  finally:
571
    _RemoveFileFromAllNodes(testname)
572

    
573

    
574
def TestClusterCommand():
575
  """gnt-cluster command"""
576
  uniqueid = utils.NewUUID()
577
  rfile = "/tmp/gnt%s" % utils.NewUUID()
578
  rcmd = utils.ShellQuoteArgs(["echo", "-n", uniqueid])
579
  cmd = utils.ShellQuoteArgs(["gnt-cluster", "command",
580
                              "%s >%s" % (rcmd, rfile)])
581

    
582
  try:
583
    AssertCommand(cmd)
584
    _CheckFileOnAllNodes(rfile, uniqueid)
585
  finally:
586
    _RemoveFileFromAllNodes(rfile)
587

    
588

    
589
def TestClusterDestroy():
590
  """gnt-cluster destroy"""
591
  AssertCommand(["gnt-cluster", "destroy", "--yes-do-it"])
592

    
593

    
594
def TestClusterRepairDiskSizes():
595
  """gnt-cluster repair-disk-sizes"""
596
  AssertCommand(["gnt-cluster", "repair-disk-sizes"])