Statistics
| Branch: | Tag: | Revision:

root / qa / qa_cluster.py @ 67bd83ae

History | View | Annotate | Download (50.1 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 constants
32
from ganeti import compat
33
from ganeti import utils
34
from ganeti import pathutils
35

    
36
import qa_config
37
import qa_daemon
38
import qa_utils
39
import qa_error
40
import qa_instance
41

    
42
from qa_utils import AssertEqual, AssertCommand, GetCommandOutput
43

    
44

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

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

    
51

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

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

    
59

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

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

    
68

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

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

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

    
86

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

    
90

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

    
105

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

    
113

    
114
def _CheckVerifyNoWarnings(actual, expected):
115
  exp_codes = compat.UniqueFrozenset(e for (_, e, _) in expected)
116
  excess = actual.intersection(exp_codes)
117
  if excess:
118
    raise qa_error.Error("Cluster-verify returned these warnings:"
119
                         " %s" % (utils.CommaJoin(excess)))
120

    
121

    
122
def AssertClusterVerify(fail=False, errors=None,
123
                        warnings=None, no_warnings=None):
124
  """Run cluster-verify and check the result, ignoring warnings by default.
125

126
  @type fail: bool
127
  @param fail: if cluster-verify is expected to fail instead of succeeding.
128
  @type errors: list of tuples
129
  @param errors: List of CV_XXX errors that are expected; if specified, all the
130
      errors listed must appear in cluster-verify output. A non-empty value
131
      implies C{fail=True}.
132
  @type warnings: list of tuples
133
  @param warnings: List of CV_XXX warnings that are expected to be raised; if
134
      specified, all the errors listed must appear in cluster-verify output.
135
  @type no_warnings: list of tuples
136
  @param no_warnings: List of CV_XXX warnings that we expect NOT to be raised.
137
  """
138
  cvcmd = "gnt-cluster verify"
139
  mnode = qa_config.GetMasterNode()
140
  if errors or warnings or no_warnings:
141
    cvout = GetCommandOutput(mnode.primary, cvcmd + " --error-codes",
142
                             fail=(fail or errors))
143
    print cvout
144
    (act_errs, act_warns) = _GetCVErrorCodes(cvout)
145
    if errors:
146
      _CheckVerifyErrors(act_errs, errors, "error")
147
    if warnings:
148
      _CheckVerifyErrors(act_warns, warnings, "warning")
149
    if no_warnings:
150
      _CheckVerifyNoWarnings(act_warns, no_warnings)
151

    
152
  else:
153
    AssertCommand(cvcmd, fail=fail, node=mnode)
154

    
155

    
156
# data for testing failures due to bad keys/values for disk parameters
157
_FAIL_PARAMS = ["nonexistent:resync-rate=1",
158
                "drbd:nonexistent=1",
159
                "drbd:resync-rate=invalid",
160
                ]
161

    
162

    
163
def TestClusterInitDisk():
164
  """gnt-cluster init -D"""
165
  name = qa_config.get("name")
166
  for param in _FAIL_PARAMS:
167
    AssertCommand(["gnt-cluster", "init", "-D", param, name], fail=True)
168

    
169

    
170
def TestClusterInit(rapi_user, rapi_secret):
171
  """gnt-cluster init"""
172
  master = qa_config.GetMasterNode()
173

    
174
  rapi_users_path = qa_utils.MakeNodePath(master, pathutils.RAPI_USERS_FILE)
175
  rapi_dir = os.path.dirname(rapi_users_path)
176

    
177
  # First create the RAPI credentials
178
  fh = tempfile.NamedTemporaryFile()
179
  try:
180
    fh.write("%s %s write\n" % (rapi_user, rapi_secret))
181
    fh.flush()
182

    
183
    tmpru = qa_utils.UploadFile(master.primary, fh.name)
184
    try:
185
      AssertCommand(["mkdir", "-p", rapi_dir])
186
      AssertCommand(["mv", tmpru, rapi_users_path])
187
    finally:
188
      AssertCommand(["rm", "-f", tmpru])
189
  finally:
190
    fh.close()
191

    
192
  # Initialize cluster
193
  enabled_disk_templates = qa_config.GetEnabledDiskTemplates()
194
  cmd = [
195
    "gnt-cluster", "init",
196
    "--primary-ip-version=%d" % qa_config.get("primary_ip_version", 4),
197
    "--enabled-hypervisors=%s" % ",".join(qa_config.GetEnabledHypervisors()),
198
    "--enabled-disk-templates=%s" %
199
      ",".join(enabled_disk_templates),
200
    ]
201
  if constants.DT_FILE in enabled_disk_templates:
202
    cmd.append(
203
        "--file-storage-dir=%s" %
204
        qa_config.get("default-file-storage-dir",
205
                      pathutils.DEFAULT_FILE_STORAGE_DIR))
206

    
207
  for spec_type in ("mem-size", "disk-size", "disk-count", "cpu-count",
208
                    "nic-count"):
209
    for spec_val in ("min", "max", "std"):
210
      spec = qa_config.get("ispec_%s_%s" %
211
                           (spec_type.replace("-", "_"), spec_val), None)
212
      if spec is not None:
213
        cmd.append("--specs-%s=%s=%d" % (spec_type, spec_val, spec))
214

    
215
  if master.secondary:
216
    cmd.append("--secondary-ip=%s" % master.secondary)
217

    
218
  if utils.IsLvmEnabled(qa_config.GetEnabledDiskTemplates()):
219
    vgname = qa_config.get("vg-name", constants.DEFAULT_VG)
220
    if vgname:
221
      cmd.append("--vg-name=%s" % vgname)
222
    else:
223
      raise qa_error.Error("Please specify a volume group if you enable"
224
                           " lvm-based disk templates in the QA.")
225

    
226
  master_netdev = qa_config.get("master-netdev", None)
227
  if master_netdev:
228
    cmd.append("--master-netdev=%s" % master_netdev)
229

    
230
  nicparams = qa_config.get("default-nicparams", None)
231
  if nicparams:
232
    cmd.append("--nic-parameters=%s" %
233
               ",".join(utils.FormatKeyValue(nicparams)))
234

    
235
  # Cluster value of the exclusive-storage node parameter
236
  e_s = qa_config.get("exclusive-storage")
237
  if e_s is not None:
238
    cmd.extend(["--node-parameters", "exclusive_storage=%s" % e_s])
239
  else:
240
    e_s = False
241
  qa_config.SetExclusiveStorage(e_s)
242

    
243
  extra_args = qa_config.get("cluster-init-args")
244

    
245
  if extra_args:
246
    # This option was removed in 2.10, but in order to not break QA of older
247
    # branches we remove it from the extra_args if it is in there.
248
    opt_drbd_storage = "--no-drbd-storage"
249
    if opt_drbd_storage in extra_args:
250
      extra_args.remove(opt_drbd_storage)
251
    cmd.extend(extra_args)
252

    
253
  cmd.append(qa_config.get("name"))
254

    
255
  AssertCommand(cmd)
256

    
257
  cmd = ["gnt-cluster", "modify"]
258

    
259
  # hypervisor parameter modifications
260
  hvp = qa_config.get("hypervisor-parameters", {})
261
  for k, v in hvp.items():
262
    cmd.extend(["-H", "%s:%s" % (k, v)])
263
  # backend parameter modifications
264
  bep = qa_config.get("backend-parameters", "")
265
  if bep:
266
    cmd.extend(["-B", bep])
267

    
268
  if len(cmd) > 2:
269
    AssertCommand(cmd)
270

    
271
  # OS parameters
272
  osp = qa_config.get("os-parameters", {})
273
  for k, v in osp.items():
274
    AssertCommand(["gnt-os", "modify", "-O", v, k])
275

    
276
  # OS hypervisor parameters
277
  os_hvp = qa_config.get("os-hvp", {})
278
  for os_name in os_hvp:
279
    for hv, hvp in os_hvp[os_name].items():
280
      AssertCommand(["gnt-os", "modify", "-H", "%s:%s" % (hv, hvp), os_name])
281

    
282

    
283
def TestClusterRename():
284
  """gnt-cluster rename"""
285
  cmd = ["gnt-cluster", "rename", "-f"]
286

    
287
  original_name = qa_config.get("name")
288
  rename_target = qa_config.get("rename", None)
289
  if rename_target is None:
290
    print qa_utils.FormatError('"rename" entry is missing')
291
    return
292

    
293
  for data in [
294
    cmd + [rename_target],
295
    _CLUSTER_VERIFY,
296
    cmd + [original_name],
297
    _CLUSTER_VERIFY,
298
    ]:
299
    AssertCommand(data)
300

    
301

    
302
def TestClusterOob():
303
  """out-of-band framework"""
304
  oob_path_exists = "/tmp/ganeti-qa-oob-does-exist-%s" % utils.NewUUID()
305

    
306
  AssertCommand(_CLUSTER_VERIFY)
307
  AssertCommand(["gnt-cluster", "modify", "--node-parameters",
308
                 "oob_program=/tmp/ganeti-qa-oob-does-not-exist-%s" %
309
                 utils.NewUUID()])
310

    
311
  AssertCommand(_CLUSTER_VERIFY, fail=True)
312

    
313
  AssertCommand(["touch", oob_path_exists])
314
  AssertCommand(["chmod", "0400", oob_path_exists])
315
  AssertCommand(["gnt-cluster", "copyfile", oob_path_exists])
316

    
317
  try:
318
    AssertCommand(["gnt-cluster", "modify", "--node-parameters",
319
                   "oob_program=%s" % oob_path_exists])
320

    
321
    AssertCommand(_CLUSTER_VERIFY, fail=True)
322

    
323
    AssertCommand(["chmod", "0500", oob_path_exists])
324
    AssertCommand(["gnt-cluster", "copyfile", oob_path_exists])
325

    
326
    AssertCommand(_CLUSTER_VERIFY)
327
  finally:
328
    AssertCommand(["gnt-cluster", "command", "rm", oob_path_exists])
329

    
330
  AssertCommand(["gnt-cluster", "modify", "--node-parameters",
331
                 "oob_program="])
332

    
333

    
334
def TestClusterEpo():
335
  """gnt-cluster epo"""
336
  master = qa_config.GetMasterNode()
337

    
338
  # Assert that OOB is unavailable for all nodes
339
  result_output = GetCommandOutput(master.primary,
340
                                   "gnt-node list --verbose --no-headers -o"
341
                                   " powered")
342
  AssertEqual(compat.all(powered == "(unavail)"
343
                         for powered in result_output.splitlines()), True)
344

    
345
  # Conflicting
346
  AssertCommand(["gnt-cluster", "epo", "--groups", "--all"], fail=True)
347
  # --all doesn't expect arguments
348
  AssertCommand(["gnt-cluster", "epo", "--all", "some_arg"], fail=True)
349

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

    
353
  # This shouldn't fail
354
  AssertCommand(["gnt-cluster", "epo", "-f", "--all"])
355

    
356
  # All instances should have been stopped now
357
  result_output = GetCommandOutput(master.primary,
358
                                   "gnt-instance list --no-headers -o status")
359
  # ERROR_down because the instance is stopped but not recorded as such
360
  AssertEqual(compat.all(status == "ERROR_down"
361
                         for status in result_output.splitlines()), True)
362

    
363
  # Now start everything again
364
  AssertCommand(["gnt-cluster", "epo", "--on", "-f", "--all"])
365

    
366
  # All instances should have been started now
367
  result_output = GetCommandOutput(master.primary,
368
                                   "gnt-instance list --no-headers -o status")
369
  AssertEqual(compat.all(status == "running"
370
                         for status in result_output.splitlines()), True)
371

    
372

    
373
def TestClusterVerify():
374
  """gnt-cluster verify"""
375
  AssertCommand(_CLUSTER_VERIFY)
376
  AssertCommand(["gnt-cluster", "verify-disks"])
377

    
378

    
379
def TestClusterVerifyDisksBrokenDRBD(instance, inst_nodes):
380
  """gnt-cluster verify-disks with broken DRBD"""
381
  qa_daemon.TestPauseWatcher()
382

    
383
  try:
384
    info = qa_instance.GetInstanceInfo(instance.name)
385
    snode = inst_nodes[1]
386
    for idx, minor in enumerate(info["drbd-minors"][snode.primary]):
387
      if idx % 2 == 0:
388
        break_drbd_cmd = \
389
          "(drbdsetup %d down >/dev/null 2>&1;" \
390
          " drbdsetup down resource%d >/dev/null 2>&1) || /bin/true" % \
391
          (minor, minor)
392
      else:
393
        break_drbd_cmd = \
394
          "(drbdsetup %d detach >/dev/null 2>&1;" \
395
          " drbdsetup detach %d >/dev/null 2>&1) || /bin/true" % \
396
          (minor, minor)
397
      AssertCommand(break_drbd_cmd, node=snode)
398

    
399
    verify_output = GetCommandOutput(qa_config.GetMasterNode().primary,
400
                                     "gnt-cluster verify-disks")
401
    activation_msg = "Activating disks for instance '%s'" % instance.name
402
    if activation_msg not in verify_output:
403
      raise qa_error.Error("gnt-cluster verify-disks did not activate broken"
404
                           " DRBD disks:\n%s" % verify_output)
405

    
406
    verify_output = GetCommandOutput(qa_config.GetMasterNode().primary,
407
                                     "gnt-cluster verify-disks")
408
    if activation_msg in verify_output:
409
      raise qa_error.Error("gnt-cluster verify-disks wants to activate broken"
410
                           " DRBD disks on second attempt:\n%s" % verify_output)
411

    
412
    AssertCommand(_CLUSTER_VERIFY)
413
  finally:
414
    qa_daemon.TestResumeWatcher()
415

    
416

    
417
def TestJobqueue():
418
  """gnt-debug test-jobqueue"""
419
  AssertCommand(["gnt-debug", "test-jobqueue"])
420

    
421

    
422
def TestDelay(node):
423
  """gnt-debug delay"""
424
  AssertCommand(["gnt-debug", "delay", "1"])
425
  AssertCommand(["gnt-debug", "delay", "--no-master", "1"])
426
  AssertCommand(["gnt-debug", "delay", "--no-master",
427
                 "-n", node.primary, "1"])
428

    
429

    
430
def TestClusterReservedLvs():
431
  """gnt-cluster reserved lvs"""
432
  # if no lvm-based templates are supported, skip the test
433
  if not qa_config.IsStorageTypeSupported(constants.ST_LVM_VG):
434
    return
435
  vgname = qa_config.get("vg-name", constants.DEFAULT_VG)
436
  lvname = _QA_LV_PREFIX + "test"
437
  lvfullname = "/".join([vgname, lvname])
438

    
439
  # Clean cluster
440
  AssertClusterVerify()
441

    
442
  AssertCommand(["gnt-cluster", "modify", "--reserved-lvs", ""])
443
  AssertCommand(["lvcreate", "-L1G", "-n", lvname, vgname])
444
  AssertClusterVerify(fail=False,
445
                      warnings=[constants.CV_ENODEORPHANLV])
446

    
447
  AssertCommand(["gnt-cluster", "modify", "--reserved-lvs",
448
                 "%s,.*/other-test" % lvfullname])
449
  AssertClusterVerify(no_warnings=[constants.CV_ENODEORPHANLV])
450

    
451
  AssertCommand(["gnt-cluster", "modify", "--reserved-lvs",
452
                ".*/%s.*" % _QA_LV_PREFIX])
453
  AssertClusterVerify(no_warnings=[constants.CV_ENODEORPHANLV])
454

    
455
  AssertCommand(["gnt-cluster", "modify", "--reserved-lvs", ""])
456
  AssertClusterVerify(fail=False,
457
                      warnings=[constants.CV_ENODEORPHANLV])
458

    
459
  AssertCommand(["lvremove", "-f", lvfullname])
460
  AssertClusterVerify()
461

    
462

    
463
def TestClusterModifyEmpty():
464
  """gnt-cluster modify"""
465
  AssertCommand(["gnt-cluster", "modify"], fail=True)
466

    
467

    
468
def TestClusterModifyDisk():
469
  """gnt-cluster modify -D"""
470
  for param in _FAIL_PARAMS:
471
    AssertCommand(["gnt-cluster", "modify", "-D", param], fail=True)
472

    
473

    
474
def _GetOtherEnabledDiskTemplate(undesired_disk_templates,
475
                                 enabled_disk_templates):
476
  """Returns one template that is not in the undesired set.
477

478
  @type undesired_disk_templates: list of string
479
  @param undesired_disk_templates: a list of disk templates that we want to
480
      exclude when drawing one disk template from the list of enabled
481
      disk templates
482
  @type enabled_disk_templates: list of string
483
  @param enabled_disk_templates: list of enabled disk templates (in QA)
484

485
  """
486
  desired_templates = list(set(enabled_disk_templates)
487
                                - set(undesired_disk_templates))
488
  if desired_templates:
489
    template = desired_templates[0]
490
  else:
491
    # If no desired disk template is available for QA, choose 'diskless' and
492
    # hope for the best.
493
    template = constants.ST_DISKLESS
494

    
495
  return template
496

    
497

    
498
def TestClusterModifyFileBasedStorageDir(
499
    file_disk_template, dir_config_key, default_dir, option_name):
500
  """Tests gnt-cluster modify wrt to file-based directory options.
501

502
  @type file_disk_template: string
503
  @param file_disk_template: file-based disk template
504
  @type dir_config_key: string
505
  @param dir_config_key: key for the QA config to retrieve the default
506
     directory value
507
  @type default_dir: string
508
  @param default_dir: default directory, if the QA config does not specify
509
     it
510
  @type option_name: string
511
  @param option_name: name of the option of 'gnt-cluster modify' to
512
     change the directory
513

514
  """
515
  enabled_disk_templates = qa_config.GetEnabledDiskTemplates()
516
  assert file_disk_template in constants.DTS_FILEBASED
517
  if not qa_config.IsTemplateSupported(file_disk_template):
518
    return
519

    
520
  # Get some non-file-based disk template to disable file storage
521
  other_disk_template = _GetOtherEnabledDiskTemplate(
522
    utils.storage.GetDiskTemplatesOfStorageTypes(constants.ST_FILE,
523
                                                 constants.ST_SHARED_FILE),
524
    enabled_disk_templates
525
  )
526

    
527
  file_storage_dir = qa_config.get(dir_config_key, default_dir)
528
  invalid_file_storage_dir = "/boot/"
529

    
530
  for fail, cmd in [
531
    (False, ["gnt-cluster", "modify",
532
             "--enabled-disk-templates=%s" % file_disk_template,
533
             "--ipolicy-disk-templates=%s" % file_disk_template]),
534
    (False, ["gnt-cluster", "modify",
535
             "--%s=%s" % (option_name, file_storage_dir)]),
536
    (False, ["gnt-cluster", "modify",
537
             "--%s=%s" % (option_name, invalid_file_storage_dir)]),
538
    # file storage dir is set to an inacceptable path, thus verify
539
    # should fail
540
    (True, ["gnt-cluster", "verify"]),
541
    # unsetting the storage dir while file storage is enabled
542
    # should fail
543
    (True, ["gnt-cluster", "modify",
544
            "--%s=" % option_name]),
545
    (False, ["gnt-cluster", "modify",
546
             "--%s=%s" % (option_name, file_storage_dir)]),
547
    (False, ["gnt-cluster", "modify",
548
             "--enabled-disk-templates=%s" % other_disk_template,
549
             "--ipolicy-disk-templates=%s" % other_disk_template]),
550
    (False, ["gnt-cluster", "modify",
551
             "--%s=%s" % (option_name, invalid_file_storage_dir)]),
552
    # file storage is set to an inacceptable path, but file storage
553
    # is disabled, thus verify should not fail
554
    (False, ["gnt-cluster", "verify"]),
555
    # unsetting the file storage dir while file storage is not enabled
556
    # should be fine
557
    (False, ["gnt-cluster", "modify",
558
             "--%s=" % option_name]),
559
    # resetting everything to sane values
560
    (False, ["gnt-cluster", "modify",
561
             "--%s=%s" % (option_name, file_storage_dir),
562
             "--enabled-disk-templates=%s" % ",".join(enabled_disk_templates),
563
             "--ipolicy-disk-templates=%s" % ",".join(enabled_disk_templates)])
564
    ]:
565
    AssertCommand(cmd, fail=fail)
566

    
567

    
568
def TestClusterModifyFileStorageDir():
569
  """gnt-cluster modify --file-storage-dir=..."""
570
  TestClusterModifyFileBasedStorageDir(
571
      constants.DT_FILE, "default-file-storage-dir",
572
      pathutils.DEFAULT_FILE_STORAGE_DIR,
573
      "file-storage-dir")
574

    
575

    
576
def TestClusterModifySharedFileStorageDir():
577
  """gnt-cluster modify --shared-file-storage-dir=..."""
578
  TestClusterModifyFileBasedStorageDir(
579
      constants.DT_SHARED_FILE, "default-shared-file-storage-dir",
580
      pathutils.DEFAULT_SHARED_FILE_STORAGE_DIR,
581
      "shared-file-storage-dir")
582

    
583

    
584
def TestClusterModifyDiskTemplates():
585
  """gnt-cluster modify --enabled-disk-templates=..."""
586
  enabled_disk_templates = qa_config.GetEnabledDiskTemplates()
587
  default_disk_template = qa_config.GetDefaultDiskTemplate()
588

    
589
  _TestClusterModifyDiskTemplatesArguments(default_disk_template)
590
  _TestClusterModifyDiskTemplatesDrbdHelper(enabled_disk_templates)
591
  _TestClusterModifyDiskTemplatesVgName(enabled_disk_templates)
592

    
593
  _RestoreEnabledDiskTemplates()
594
  nodes = qa_config.AcquireManyNodes(2)
595

    
596
  instance_template = enabled_disk_templates[0]
597
  instance = qa_instance.CreateInstanceByDiskTemplate(nodes, instance_template)
598

    
599
  _TestClusterModifyUsedDiskTemplate(instance_template,
600
                                     enabled_disk_templates)
601

    
602
  qa_instance.TestInstanceRemove(instance)
603
  _RestoreEnabledDiskTemplates()
604

    
605

    
606
def _RestoreEnabledDiskTemplates():
607
  """Sets the list of enabled disk templates back to the list of enabled disk
608
     templates from the QA configuration. This can be used to make sure that
609
     the tests that modify the list of disk templates do not interfere with
610
     other tests.
611

612
  """
613
  enabled_disk_templates = qa_config.GetEnabledDiskTemplates()
614
  cmd = ["gnt-cluster", "modify",
615
         "--enabled-disk-templates=%s" % ",".join(enabled_disk_templates),
616
         "--ipolicy-disk-templates=%s" % ",".join(enabled_disk_templates),
617
         ]
618

    
619
  if utils.IsLvmEnabled(qa_config.GetEnabledDiskTemplates()):
620
    vgname = qa_config.get("vg-name", constants.DEFAULT_VG)
621
    cmd.append("--vg-name=%s" % vgname)
622

    
623
  AssertCommand(cmd, fail=False)
624

    
625

    
626
def _TestClusterModifyDiskTemplatesDrbdHelper(enabled_disk_templates):
627
  """Tests argument handling of 'gnt-cluster modify' with respect to
628
     the parameter '--drbd-usermode-helper'. This test is independent
629
     of instances.
630

631
  """
632
  _RestoreEnabledDiskTemplates()
633

    
634
  if constants.DT_DRBD8 not in enabled_disk_templates:
635
    return
636
  if constants.DT_PLAIN not in enabled_disk_templates:
637
    return
638

    
639
  drbd_usermode_helper = qa_config.get("drbd-usermode-helper", "/bin/true")
640
  bogus_usermode_helper = "/tmp/pinkbunny"
641
  for command, fail in [
642
    (["gnt-cluster", "modify",
643
      "--enabled-disk-templates=%s" % constants.DT_DRBD8,
644
      "--ipolicy-disk-templates=%s" % constants.DT_DRBD8], False),
645
    (["gnt-cluster", "modify",
646
      "--drbd-usermode-helper=%s" % drbd_usermode_helper], False),
647
    (["gnt-cluster", "modify",
648
      "--drbd-usermode-helper=%s" % bogus_usermode_helper], True),
649
    # unsetting helper when DRBD is enabled should not work
650
    (["gnt-cluster", "modify",
651
      "--drbd-usermode-helper="], True),
652
    (["gnt-cluster", "modify",
653
      "--enabled-disk-templates=%s" % constants.DT_PLAIN,
654
      "--ipolicy-disk-templates=%s" % constants.DT_PLAIN], False),
655
    (["gnt-cluster", "modify",
656
      "--drbd-usermode-helper="], False),
657
    (["gnt-cluster", "modify",
658
      "--drbd-usermode-helper=%s" % drbd_usermode_helper], False),
659
    (["gnt-cluster", "modify",
660
      "--drbd-usermode-helper=%s" % drbd_usermode_helper,
661
      "--enabled-disk-templates=%s" % constants.DT_DRBD8,
662
      "--ipolicy-disk-templates=%s" % constants.DT_DRBD8], False),
663
    (["gnt-cluster", "modify",
664
      "--drbd-usermode-helper=",
665
      "--enabled-disk-templates=%s" % constants.DT_PLAIN,
666
      "--ipolicy-disk-templates=%s" % constants.DT_PLAIN], False),
667
    (["gnt-cluster", "modify",
668
      "--drbd-usermode-helper=%s" % drbd_usermode_helper,
669
      "--enabled-disk-templates=%s" % constants.DT_DRBD8,
670
      "--ipolicy-disk-templates=%s" % constants.DT_DRBD8], False),
671
    ]:
672
    AssertCommand(command, fail=fail)
673
  _RestoreEnabledDiskTemplates()
674

    
675

    
676
def _TestClusterModifyDiskTemplatesArguments(default_disk_template):
677
  """Tests argument handling of 'gnt-cluster modify' with respect to
678
     the parameter '--enabled-disk-templates'. This test is independent
679
     of instances.
680

681
  """
682
  _RestoreEnabledDiskTemplates()
683

    
684
  # bogus templates
685
  AssertCommand(["gnt-cluster", "modify",
686
                 "--enabled-disk-templates=pinkbunny"],
687
                fail=True)
688

    
689
  # duplicate entries do no harm
690
  AssertCommand(
691
    ["gnt-cluster", "modify",
692
     "--enabled-disk-templates=%s,%s" %
693
      (default_disk_template, default_disk_template),
694
     "--ipolicy-disk-templates=%s" % default_disk_template],
695
    fail=False)
696

    
697

    
698
def _TestClusterModifyDiskTemplatesVgName(enabled_disk_templates):
699
  """Tests argument handling of 'gnt-cluster modify' with respect to
700
     the parameter '--enabled-disk-templates' and '--vg-name'. This test is
701
     independent of instances.
702

703
  """
704
  if not utils.IsLvmEnabled(enabled_disk_templates):
705
    # These tests only make sense if lvm is enabled for QA
706
    return
707

    
708
  # determine an LVM and a non-LVM disk template for the tests
709
  non_lvm_template = _GetOtherEnabledDiskTemplate(constants.DTS_LVM,
710
                                                  enabled_disk_templates)
711
  lvm_template = list(set(enabled_disk_templates) & constants.DTS_LVM)[0]
712

    
713
  vgname = qa_config.get("vg-name", constants.DEFAULT_VG)
714

    
715
  # Clean start: unset volume group name, disable lvm storage
716
  AssertCommand(
717
    ["gnt-cluster", "modify",
718
     "--enabled-disk-templates=%s" % non_lvm_template,
719
     "--ipolicy-disk-templates=%s" % non_lvm_template,
720
     "--vg-name="],
721
    fail=False)
722

    
723
  # Try to enable lvm, when no volume group is given
724
  AssertCommand(
725
    ["gnt-cluster", "modify",
726
     "--enabled-disk-templates=%s" % lvm_template,
727
     "--ipolicy-disk-templates=%s" % lvm_template],
728
    fail=True)
729

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

    
733
  # Try unsetting vg name and enabling lvm at the same time
734
  AssertCommand(
735
    ["gnt-cluster", "modify",
736
     "--enabled-disk-templates=%s" % lvm_template,
737
     "--ipolicy-disk-templates=%s" % lvm_template,
738
     "--vg-name="],
739
    fail=True)
740

    
741
  # Enable lvm with vg name present
742
  AssertCommand(
743
    ["gnt-cluster", "modify",
744
     "--enabled-disk-templates=%s" % lvm_template,
745
     "--ipolicy-disk-templates=%s" % lvm_template],
746
    fail=False)
747

    
748
  # Try unsetting vg name with lvm still enabled
749
  AssertCommand(["gnt-cluster", "modify", "--vg-name="], fail=True)
750

    
751
  # Disable lvm with vg name still set
752
  AssertCommand(
753
    ["gnt-cluster", "modify",
754
     "--enabled-disk-templates=%s" % non_lvm_template,
755
     "--ipolicy-disk-templates=%s" % non_lvm_template,
756
     ],
757
    fail=False)
758

    
759
  # Try unsetting vg name with lvm disabled
760
  AssertCommand(["gnt-cluster", "modify", "--vg-name="], fail=False)
761

    
762
  # Set vg name and enable lvm at the same time
763
  AssertCommand(
764
    ["gnt-cluster", "modify",
765
     "--enabled-disk-templates=%s" % lvm_template,
766
     "--ipolicy-disk-templates=%s" % lvm_template,
767
     "--vg-name=%s" % vgname],
768
    fail=False)
769

    
770
  # Unset vg name and disable lvm at the same time
771
  AssertCommand(
772
    ["gnt-cluster", "modify",
773
     "--enabled-disk-templates=%s" % non_lvm_template,
774
     "--ipolicy-disk-templates=%s" % non_lvm_template,
775
     "--vg-name="],
776
    fail=False)
777

    
778
  _RestoreEnabledDiskTemplates()
779

    
780

    
781
def _TestClusterModifyUsedDiskTemplate(instance_template,
782
                                       enabled_disk_templates):
783
  """Tests that disk templates that are currently in use by instances cannot
784
     be disabled on the cluster.
785

786
  """
787
  # If the list of enabled disk templates contains only one template
788
  # we need to add some other templates, because the list of enabled disk
789
  # templates can only be set to a non-empty list.
790
  new_disk_templates = list(set(enabled_disk_templates)
791
                              - set([instance_template]))
792
  if not new_disk_templates:
793
    new_disk_templates = list(set([constants.DT_DISKLESS, constants.DT_BLOCK])
794
                                - set([instance_template]))
795
  AssertCommand(
796
    ["gnt-cluster", "modify",
797
     "--enabled-disk-templates=%s" % ",".join(new_disk_templates),
798
     "--ipolicy-disk-templates=%s" % ",".join(new_disk_templates)],
799
    fail=True)
800

    
801

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

    
832
  # redo the original-requested BE parameters, if any
833
  bep = qa_config.get("backend-parameters", "")
834
  if bep:
835
    AssertCommand(["gnt-cluster", "modify", "-B", bep])
836

    
837

    
838
def _GetClusterIPolicy():
839
  """Return the run-time values of the cluster-level instance policy.
840

841
  @rtype: tuple
842
  @return: (policy, specs), where:
843
      - policy is a dictionary of the policy values, instance specs excluded
844
      - specs is a dictionary containing only the specs, using the internal
845
        format (see L{constants.IPOLICY_DEFAULTS} for an example)
846

847
  """
848
  info = qa_utils.GetObjectInfo(["gnt-cluster", "info"])
849
  policy = info["Instance policy - limits for instances"]
850
  (ret_policy, ret_specs) = qa_utils.ParseIPolicy(policy)
851

    
852
  # Sanity checks
853
  assert "minmax" in ret_specs and "std" in ret_specs
854
  assert len(ret_specs["minmax"]) > 0
855
  assert len(ret_policy) > 0
856
  return (ret_policy, ret_specs)
857

    
858

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

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

    
920

    
921
def TestClusterSetISpecs(new_specs=None, diff_specs=None, fail=False,
922
                         old_values=None):
923
  """Change instance specs.
924

925
  At most one of new_specs or diff_specs can be specified.
926

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

941
  """
942
  build_cmd = lambda opts: ["gnt-cluster", "modify"] + opts
943
  return qa_utils.TestSetISpecs(
944
    new_specs=new_specs, diff_specs=diff_specs,
945
    get_policy_fn=_GetClusterIPolicy, build_cmd_fn=build_cmd,
946
    fail=fail, old_values=old_values)
947

    
948

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

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

    
1003

    
1004
def TestClusterInfo():
1005
  """gnt-cluster info"""
1006
  AssertCommand(["gnt-cluster", "info"])
1007

    
1008

    
1009
def TestClusterRedistConf():
1010
  """gnt-cluster redist-conf"""
1011
  AssertCommand(["gnt-cluster", "redist-conf"])
1012

    
1013

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

    
1018

    
1019
def TestClusterVersion():
1020
  """gnt-cluster version"""
1021
  AssertCommand(["gnt-cluster", "version"])
1022

    
1023

    
1024
def TestClusterRenewCrypto():
1025
  """gnt-cluster renew-crypto"""
1026
  master = qa_config.GetMasterNode()
1027

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

    
1038
  # Invalid RAPI certificate
1039
  cmd = ["gnt-cluster", "renew-crypto", "--force",
1040
         "--rapi-certificate=/dev/null"]
1041
  AssertCommand(cmd, fail=True)
1042

    
1043
  rapi_cert_backup = qa_utils.BackupFile(master.primary,
1044
                                         pathutils.RAPI_CERT_FILE)
1045
  try:
1046
    # Custom RAPI certificate
1047
    fh = tempfile.NamedTemporaryFile()
1048

    
1049
    # Ensure certificate doesn't cause "gnt-cluster verify" to complain
1050
    validity = constants.SSL_CERT_EXPIRATION_WARN * 3
1051

    
1052
    utils.GenerateSelfSignedSslCert(fh.name, 1, validity=validity)
1053

    
1054
    tmpcert = qa_utils.UploadFile(master.primary, fh.name)
1055
    try:
1056
      AssertCommand(["gnt-cluster", "renew-crypto", "--force",
1057
                     "--rapi-certificate=%s" % tmpcert])
1058
    finally:
1059
      AssertCommand(["rm", "-f", tmpcert])
1060

    
1061
    # Custom cluster domain secret
1062
    cds_fh = tempfile.NamedTemporaryFile()
1063
    cds_fh.write(utils.GenerateSecret())
1064
    cds_fh.write("\n")
1065
    cds_fh.flush()
1066

    
1067
    tmpcds = qa_utils.UploadFile(master.primary, cds_fh.name)
1068
    try:
1069
      AssertCommand(["gnt-cluster", "renew-crypto", "--force",
1070
                     "--cluster-domain-secret=%s" % tmpcds])
1071
    finally:
1072
      AssertCommand(["rm", "-f", tmpcds])
1073

    
1074
    # Normal case
1075
    AssertCommand(["gnt-cluster", "renew-crypto", "--force",
1076
                   "--new-cluster-certificate", "--new-confd-hmac-key",
1077
                   "--new-rapi-certificate", "--new-cluster-domain-secret",
1078
                   "--new-node-certificates"])
1079

    
1080
    # Only renew node certificates
1081
    AssertCommand(["gnt-cluster", "renew-crypto", "--force",
1082
                   "--new-node-certificates"])
1083

    
1084
    # Restore RAPI certificate
1085
    AssertCommand(["gnt-cluster", "renew-crypto", "--force",
1086
                   "--rapi-certificate=%s" % rapi_cert_backup])
1087
  finally:
1088
    AssertCommand(["rm", "-f", rapi_cert_backup])
1089

    
1090

    
1091
def TestClusterBurnin():
1092
  """Burnin"""
1093
  master = qa_config.GetMasterNode()
1094

    
1095
  options = qa_config.get("options", {})
1096
  disk_template = options.get("burnin-disk-template", constants.DT_DRBD8)
1097
  parallel = options.get("burnin-in-parallel", False)
1098
  check_inst = options.get("burnin-check-instances", False)
1099
  do_rename = options.get("burnin-rename", "")
1100
  do_reboot = options.get("burnin-reboot", True)
1101
  reboot_types = options.get("reboot-types", constants.REBOOT_TYPES)
1102

    
1103
  # Get as many instances as we need
1104
  instances = []
1105
  try:
1106
    try:
1107
      num = qa_config.get("options", {}).get("burnin-instances", 1)
1108
      for _ in range(0, num):
1109
        instances.append(qa_config.AcquireInstance())
1110
    except qa_error.OutOfInstancesError:
1111
      print "Not enough instances, continuing anyway."
1112

    
1113
    if len(instances) < 1:
1114
      raise qa_error.Error("Burnin needs at least one instance")
1115

    
1116
    script = qa_utils.UploadFile(master.primary, "../tools/burnin")
1117
    try:
1118
      disks = qa_config.GetDiskOptions()
1119
      # Run burnin
1120
      cmd = ["env",
1121
             "PYTHONPATH=%s" % _constants.VERSIONEDSHAREDIR,
1122
             script,
1123
             "--os=%s" % qa_config.get("os"),
1124
             "--minmem-size=%s" % qa_config.get(constants.BE_MINMEM),
1125
             "--maxmem-size=%s" % qa_config.get(constants.BE_MAXMEM),
1126
             "--disk-size=%s" % ",".join([d.get("size") for d in disks]),
1127
             "--disk-growth=%s" % ",".join([d.get("growth") for d in disks]),
1128
             "--disk-template=%s" % disk_template]
1129
      if parallel:
1130
        cmd.append("--parallel")
1131
        cmd.append("--early-release")
1132
      if check_inst:
1133
        cmd.append("--http-check")
1134
      if do_rename:
1135
        cmd.append("--rename=%s" % do_rename)
1136
      if not do_reboot:
1137
        cmd.append("--no-reboot")
1138
      else:
1139
        cmd.append("--reboot-types=%s" % ",".join(reboot_types))
1140
      cmd += [inst.name for inst in instances]
1141
      AssertCommand(cmd)
1142
    finally:
1143
      AssertCommand(["rm", "-f", script])
1144

    
1145
  finally:
1146
    for inst in instances:
1147
      inst.Release()
1148

    
1149

    
1150
def TestClusterMasterFailover():
1151
  """gnt-cluster master-failover"""
1152
  master = qa_config.GetMasterNode()
1153
  failovermaster = qa_config.AcquireNode(exclude=master)
1154

    
1155
  cmd = ["gnt-cluster", "master-failover"]
1156
  node_list_cmd = ["gnt-node", "list"]
1157
  try:
1158
    AssertCommand(cmd, node=failovermaster)
1159
    AssertCommand(node_list_cmd, node=failovermaster)
1160
    # Back to original master node
1161
    AssertCommand(cmd, node=master)
1162
    AssertCommand(node_list_cmd, node=master)
1163
  finally:
1164
    failovermaster.Release()
1165

    
1166

    
1167
def TestUpgrade():
1168
  """Test gnt-cluster upgrade.
1169

1170
  This tests the 'gnt-cluster upgrade' command by flipping
1171
  between the current and a different version of Ganeti.
1172
  To also recover subtile points in the configuration up/down
1173
  grades, instances are left over both upgrades.
1174

1175
  """
1176
  this_version = qa_config.get("dir-version")
1177
  other_version = qa_config.get("other-dir-version")
1178
  if this_version is None or other_version is None:
1179
    print qa_utils.FormatInfo("Test not run, as versions not specified")
1180
    return
1181

    
1182
  inst_creates = []
1183
  upgrade_instances = qa_config.get("upgrade-instances", [])
1184
  live_instances = []
1185
  for (test_name, templ, cf, n) in qa_instance.available_instance_tests:
1186
    if (qa_config.TestEnabled(test_name) and
1187
        qa_config.IsTemplateSupported(templ) and
1188
        templ in upgrade_instances):
1189
      inst_creates.append((cf, n))
1190

    
1191
  for (cf, n) in inst_creates:
1192
    nodes = qa_config.AcquireManyNodes(n)
1193
    live_instances.append(cf(nodes))
1194

    
1195
  AssertCommand(["gnt-cluster", "upgrade", "--to", other_version])
1196
  AssertCommand(["gnt-cluster", "verify"])
1197

    
1198
  for instance in live_instances:
1199
    qa_instance.TestInstanceRemove(instance)
1200
    instance.Release()
1201
  live_instances = []
1202
  for (cf, n) in inst_creates:
1203
    nodes = qa_config.AcquireManyNodes(n)
1204
    live_instances.append(cf(nodes))
1205

    
1206
  AssertCommand(["gnt-cluster", "upgrade", "--to", this_version])
1207
  AssertCommand(["gnt-cluster", "verify"])
1208

    
1209
  for instance in live_instances:
1210
    qa_instance.TestInstanceRemove(instance)
1211
    instance.Release()
1212

    
1213

    
1214
def _NodeQueueDrainFile(node):
1215
  """Returns path to queue drain file for a node.
1216

1217
  """
1218
  return qa_utils.MakeNodePath(node, pathutils.JOB_QUEUE_DRAIN_FILE)
1219

    
1220

    
1221
def _AssertDrainFile(node, **kwargs):
1222
  """Checks for the queue drain file.
1223

1224
  """
1225
  AssertCommand(["test", "-f", _NodeQueueDrainFile(node)], node=node, **kwargs)
1226

    
1227

    
1228
def TestClusterMasterFailoverWithDrainedQueue():
1229
  """gnt-cluster master-failover with drained queue"""
1230
  master = qa_config.GetMasterNode()
1231
  failovermaster = qa_config.AcquireNode(exclude=master)
1232

    
1233
  # Ensure queue is not drained
1234
  for node in [master, failovermaster]:
1235
    _AssertDrainFile(node, fail=True)
1236

    
1237
  # Drain queue on failover master
1238
  AssertCommand(["touch", _NodeQueueDrainFile(failovermaster)],
1239
                node=failovermaster)
1240

    
1241
  cmd = ["gnt-cluster", "master-failover"]
1242
  try:
1243
    _AssertDrainFile(failovermaster)
1244
    AssertCommand(cmd, node=failovermaster)
1245
    _AssertDrainFile(master, fail=True)
1246
    _AssertDrainFile(failovermaster, fail=True)
1247

    
1248
    # Back to original master node
1249
    AssertCommand(cmd, node=master)
1250
  finally:
1251
    failovermaster.Release()
1252

    
1253
  # Ensure queue is not drained
1254
  for node in [master, failovermaster]:
1255
    _AssertDrainFile(node, fail=True)
1256

    
1257

    
1258
def TestClusterCopyfile():
1259
  """gnt-cluster copyfile"""
1260
  master = qa_config.GetMasterNode()
1261

    
1262
  uniqueid = utils.NewUUID()
1263

    
1264
  # Create temporary file
1265
  f = tempfile.NamedTemporaryFile()
1266
  f.write(uniqueid)
1267
  f.flush()
1268
  f.seek(0)
1269

    
1270
  # Upload file to master node
1271
  testname = qa_utils.UploadFile(master.primary, f.name)
1272
  try:
1273
    # Copy file to all nodes
1274
    AssertCommand(["gnt-cluster", "copyfile", testname])
1275
    _CheckFileOnAllNodes(testname, uniqueid)
1276
  finally:
1277
    _RemoveFileFromAllNodes(testname)
1278

    
1279

    
1280
def TestClusterCommand():
1281
  """gnt-cluster command"""
1282
  uniqueid = utils.NewUUID()
1283
  rfile = "/tmp/gnt%s" % utils.NewUUID()
1284
  rcmd = utils.ShellQuoteArgs(["echo", "-n", uniqueid])
1285
  cmd = utils.ShellQuoteArgs(["gnt-cluster", "command",
1286
                              "%s >%s" % (rcmd, rfile)])
1287

    
1288
  try:
1289
    AssertCommand(cmd)
1290
    _CheckFileOnAllNodes(rfile, uniqueid)
1291
  finally:
1292
    _RemoveFileFromAllNodes(rfile)
1293

    
1294

    
1295
def TestClusterDestroy():
1296
  """gnt-cluster destroy"""
1297
  AssertCommand(["gnt-cluster", "destroy", "--yes-do-it"])
1298

    
1299

    
1300
def TestClusterRepairDiskSizes():
1301
  """gnt-cluster repair-disk-sizes"""
1302
  AssertCommand(["gnt-cluster", "repair-disk-sizes"])
1303

    
1304

    
1305
def TestSetExclStorCluster(newvalue):
1306
  """Set the exclusive_storage node parameter at the cluster level.
1307

1308
  @type newvalue: bool
1309
  @param newvalue: New value of exclusive_storage
1310
  @rtype: bool
1311
  @return: The old value of exclusive_storage
1312

1313
  """
1314
  es_path = ["Default node parameters", "exclusive_storage"]
1315
  oldvalue = _GetClusterField(es_path)
1316
  AssertCommand(["gnt-cluster", "modify", "--node-parameters",
1317
                 "exclusive_storage=%s" % newvalue])
1318
  effvalue = _GetClusterField(es_path)
1319
  if effvalue != newvalue:
1320
    raise qa_error.Error("exclusive_storage has the wrong value: %s instead"
1321
                         " of %s" % (effvalue, newvalue))
1322
  qa_config.SetExclusiveStorage(newvalue)
1323
  return oldvalue
1324

    
1325

    
1326
def TestExclStorSharedPv(node):
1327
  """cluster-verify reports LVs that share the same PV with exclusive_storage.
1328

1329
  """
1330
  vgname = qa_config.get("vg-name", constants.DEFAULT_VG)
1331
  lvname1 = _QA_LV_PREFIX + "vol1"
1332
  lvname2 = _QA_LV_PREFIX + "vol2"
1333
  node_name = node.primary
1334
  AssertCommand(["lvcreate", "-L1G", "-n", lvname1, vgname], node=node_name)
1335
  AssertClusterVerify(fail=False,
1336
                      warnings=[constants.CV_ENODEORPHANLV])
1337
  AssertCommand(["lvcreate", "-L1G", "-n", lvname2, vgname], node=node_name)
1338
  AssertClusterVerify(fail=True,
1339
                      errors=[constants.CV_ENODELVM],
1340
                      warnings=[constants.CV_ENODEORPHANLV])
1341
  AssertCommand(["lvremove", "-f", "/".join([vgname, lvname1])], node=node_name)
1342
  AssertCommand(["lvremove", "-f", "/".join([vgname, lvname2])], node=node_name)
1343
  AssertClusterVerify()
1344

    
1345

    
1346
def TestInstanceCommunication():
1347
  """Tests instance communication"""
1348
  master = qa_config.GetMasterNode()
1349

    
1350
  # Check that the 'default' node group exists
1351
  cmd = ["gnt-group", "list", "--no-headers", "-o", "name"]
1352
  result_output = GetCommandOutput(master.primary, utils.ShellQuoteArgs(cmd))
1353
  AssertEqual(result_output.strip(), "default", msg="Checking 'default' group")
1354

    
1355
  # Check that no networks exist
1356
  cmd = ["gnt-network", "list", "--no-headers", "-o", "name"]
1357
  result_output = GetCommandOutput(master.primary, utils.ShellQuoteArgs(cmd))
1358
  AssertEqual(result_output.strip(), "", msg="Checking networks")
1359

    
1360
  # Modify cluster parameter 'instance-communication-network' and
1361
  # check whether the cluster creates the instance communication
1362
  # network and connects it to the 'default' node group
1363
  network_name = "mynetwork"
1364

    
1365
  cmd = "gnt-cluster modify --instance-communication-network=%s" % network_name
1366
  result_output = qa_utils.GetCommandOutput(master.primary, cmd)
1367
  print result_output
1368

    
1369
  cmd = ["gnt-network", "list", "--no-headers", "-o", "name", network_name]
1370
  result_output = qa_utils.GetCommandOutput(master.primary,
1371
                                            utils.ShellQuoteArgs(cmd))
1372
  AssertEqual(result_output.strip(), "mynetwork", msg="Checking 'mynetwork'")
1373

    
1374
  cmd = ["gnt-network", "list", "--no-headers", "-o", "group_list",
1375
         network_name]
1376
  result_output = qa_utils.GetCommandOutput(master.primary,
1377
                                            utils.ShellQuoteArgs(cmd))
1378
  AssertEqual(result_output.strip(), "default (routed, communication_rt)",
1379
              msg="Checking network connected groups")
1380

    
1381
  # Check that the network has the parameters necessary for instance
1382
  # communication
1383
  cmd = ["gnt-network", "list", "--no-headers", "-o", "gateway",
1384
         network_name]
1385
  result_output = qa_utils.GetCommandOutput(master.primary,
1386
                                            utils.ShellQuoteArgs(cmd))
1387
  AssertEqual(result_output.strip(), "-", msg="Checking gateway")
1388

    
1389
  cmd = ["gnt-network", "list", "--no-headers", "-o", "gateway6",
1390
         network_name]
1391
  result_output = qa_utils.GetCommandOutput(master.primary,
1392
                                            utils.ShellQuoteArgs(cmd))
1393
  AssertEqual(result_output.strip(), "-", msg="Checking gateway6")
1394

    
1395
  cmd = ["gnt-network", "list", "--no-headers", "-o", "network",
1396
         network_name]
1397
  result_output = qa_utils.GetCommandOutput(master.primary,
1398
                                            utils.ShellQuoteArgs(cmd))
1399
  AssertEqual(result_output.strip(), constants.INSTANCE_COMMUNICATION_NETWORK4,
1400
              msg="Checking network")
1401

    
1402
  cmd = ["gnt-network", "list", "--no-headers", "-o", "network6",
1403
         network_name]
1404
  result_output = qa_utils.GetCommandOutput(master.primary,
1405
                                            utils.ShellQuoteArgs(cmd))
1406
  AssertEqual(result_output.strip(), constants.INSTANCE_COMMUNICATION_NETWORK6,
1407
              msg="Checking network6")
1408

    
1409
  # Add a new group and check whether the instance communication
1410
  # network connects to this new group
1411
  #
1412
  # We don't assume any particular group order and allow the output of
1413
  # 'gnt-network list' to print the 'default' and 'mygroup' groups in
1414
  # any order.
1415
  group = "mygroup"
1416

    
1417
  cmd = ["gnt-group", "add", group]
1418
  result_output = qa_utils.GetCommandOutput(master.primary,
1419
                                            utils.ShellQuoteArgs(cmd))
1420
  print result_output
1421

    
1422
  cmd = ["gnt-network", "list", "--no-headers", "-o", "group_list",
1423
         network_name]
1424
  result_output = qa_utils.GetCommandOutput(master.primary,
1425
                                            utils.ShellQuoteArgs(cmd))
1426

    
1427
  try:
1428
    r1 = "mygroup (routed, communication_rt)," \
1429
         " default (routed, communication_rt)"
1430
    AssertEqual(result_output.strip(), r1,
1431
                msg="Checking network connected groups")
1432
  except qa_error.Error:
1433
    r2 = "default (routed, communication_rt)," \
1434
         " mygroup (routed, communication_rt)"
1435
    AssertEqual(result_output.strip(), r2,
1436
                msg="Checking network connected groups")
1437

    
1438
  # Modify cluster parameter 'instance-communication-network' to the
1439
  # same value and check that nothing happens.
1440
  cmd = "gnt-cluster modify --instance-communication-network=%s" % network_name
1441
  result_output = qa_utils.GetCommandOutput(master.primary, cmd)
1442
  print result_output
1443

    
1444
  # Disable instance communication network, disconnect the instance
1445
  # communication network and remove it, and remove the group
1446
  cmd = "gnt-cluster modify --instance-communication-network="
1447
  result_output = qa_utils.GetCommandOutput(master.primary, cmd)
1448
  print result_output
1449

    
1450
  cmd = ["gnt-network", "disconnect", network_name]
1451
  AssertCommand(utils.ShellQuoteArgs(cmd))
1452

    
1453
  cmd = ["gnt-network", "remove", network_name]
1454
  AssertCommand(utils.ShellQuoteArgs(cmd))
1455

    
1456
  cmd = ["gnt-group", "remove", group]
1457
  AssertCommand(utils.ShellQuoteArgs(cmd))
1458

    
1459
  # Check that the 'default' node group exists
1460
  cmd = ["gnt-group", "list", "--no-headers", "-o", "name"]
1461
  result_output = GetCommandOutput(master.primary, utils.ShellQuoteArgs(cmd))
1462
  AssertEqual(result_output.strip(), "default", msg="Checking 'default' group")
1463

    
1464
  # Check that no networks exist
1465
  cmd = ["gnt-network", "list", "--no-headers", "-o", "name"]
1466
  result_output = GetCommandOutput(master.primary, utils.ShellQuoteArgs(cmd))
1467
  AssertEqual(result_output.strip(), "", msg="Checking networks")