Statistics
| Branch: | Tag: | Revision:

root / qa / qa_cluster.py @ 5c4731eb

History | View | Annotate | Download (50.6 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_error
39
import qa_instance
40
import qa_logging
41
import qa_utils
42

    
43
from qa_utils import AssertEqual, AssertCommand, GetCommandOutput
44

    
45

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

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

    
52

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

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

    
60

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

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

    
69

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

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

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

    
87

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

    
91

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

    
106

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

    
114

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

    
122

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

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

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

    
156

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

    
163

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

    
170

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

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

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

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

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

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

    
219
  if master.secondary:
220
    cmd.append("--secondary-ip=%s" % master.secondary)
221

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

    
230
  master_netdev = qa_config.get("master-netdev", None)
231
  if master_netdev:
232
    cmd.append("--master-netdev=%s" % master_netdev)
233

    
234
  nicparams = qa_config.get("default-nicparams", None)
235
  if nicparams:
236
    cmd.append("--nic-parameters=%s" %
237
               ",".join(utils.FormatKeyValue(nicparams)))
238

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

    
247
  extra_args = qa_config.get("cluster-init-args")
248

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

    
257
  cmd.append(qa_config.get("name"))
258

    
259
  AssertCommand(cmd)
260

    
261
  cmd = ["gnt-cluster", "modify"]
262

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

    
272
  if len(cmd) > 2:
273
    AssertCommand(cmd)
274

    
275
  # OS parameters
276
  osp = qa_config.get("os-parameters", {})
277
  for k, v in osp.items():
278
    AssertCommand(["gnt-os", "modify", "-O", v, k])
279

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

    
286

    
287
def TestClusterRename():
288
  """gnt-cluster rename"""
289
  cmd = ["gnt-cluster", "rename", "-f"]
290

    
291
  original_name = qa_config.get("name")
292
  rename_target = qa_config.get("rename", None)
293
  if rename_target is None:
294
    print qa_logging.FormatError('"rename" entry is missing')
295
    return
296

    
297
  for data in [
298
    cmd + [rename_target],
299
    _CLUSTER_VERIFY,
300
    cmd + [original_name],
301
    _CLUSTER_VERIFY,
302
    ]:
303
    AssertCommand(data)
304

    
305

    
306
def TestClusterOob():
307
  """out-of-band framework"""
308
  oob_path_exists = "/tmp/ganeti-qa-oob-does-exist-%s" % utils.NewUUID()
309

    
310
  AssertCommand(_CLUSTER_VERIFY)
311
  AssertCommand(["gnt-cluster", "modify", "--node-parameters",
312
                 "oob_program=/tmp/ganeti-qa-oob-does-not-exist-%s" %
313
                 utils.NewUUID()])
314

    
315
  AssertCommand(_CLUSTER_VERIFY, fail=True)
316

    
317
  AssertCommand(["touch", oob_path_exists])
318
  AssertCommand(["chmod", "0400", oob_path_exists])
319
  AssertCommand(["gnt-cluster", "copyfile", oob_path_exists])
320

    
321
  try:
322
    AssertCommand(["gnt-cluster", "modify", "--node-parameters",
323
                   "oob_program=%s" % oob_path_exists])
324

    
325
    AssertCommand(_CLUSTER_VERIFY, fail=True)
326

    
327
    AssertCommand(["chmod", "0500", oob_path_exists])
328
    AssertCommand(["gnt-cluster", "copyfile", oob_path_exists])
329

    
330
    AssertCommand(_CLUSTER_VERIFY)
331
  finally:
332
    AssertCommand(["gnt-cluster", "command", "rm", oob_path_exists])
333

    
334
  AssertCommand(["gnt-cluster", "modify", "--node-parameters",
335
                 "oob_program="])
336

    
337

    
338
def TestClusterEpo():
339
  """gnt-cluster epo"""
340
  master = qa_config.GetMasterNode()
341

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

    
349
  # Conflicting
350
  AssertCommand(["gnt-cluster", "epo", "--groups", "--all"], fail=True)
351
  # --all doesn't expect arguments
352
  AssertCommand(["gnt-cluster", "epo", "--all", "some_arg"], fail=True)
353

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

    
357
  # This shouldn't fail
358
  AssertCommand(["gnt-cluster", "epo", "-f", "--all"])
359

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

    
367
  # Now start everything again
368
  AssertCommand(["gnt-cluster", "epo", "--on", "-f", "--all"])
369

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

    
376

    
377
def TestClusterVerify():
378
  """gnt-cluster verify"""
379
  AssertCommand(_CLUSTER_VERIFY)
380
  AssertCommand(["gnt-cluster", "verify-disks"])
381

    
382

    
383
def TestClusterVerifyDisksBrokenDRBD(instance, inst_nodes):
384
  """gnt-cluster verify-disks with broken DRBD"""
385
  qa_daemon.TestPauseWatcher()
386

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

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

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

    
416
    AssertCommand(_CLUSTER_VERIFY)
417
  finally:
418
    qa_daemon.TestResumeWatcher()
419

    
420

    
421
def TestJobqueue():
422
  """gnt-debug test-jobqueue"""
423
  AssertCommand(["gnt-debug", "test-jobqueue"])
424

    
425

    
426
def TestDelay(node):
427
  """gnt-debug delay"""
428
  AssertCommand(["gnt-debug", "delay", "1"])
429
  AssertCommand(["gnt-debug", "delay", "--no-master", "1"])
430
  AssertCommand(["gnt-debug", "delay", "--no-master",
431
                 "-n", node.primary, "1"])
432

    
433

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

    
443
  # Clean cluster
444
  AssertClusterVerify()
445

    
446
  AssertCommand(["gnt-cluster", "modify", "--reserved-lvs", ""])
447
  AssertCommand(["lvcreate", "-L1G", "-n", lvname, vgname])
448
  AssertClusterVerify(fail=False,
449
                      warnings=[constants.CV_ENODEORPHANLV])
450

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

    
455
  AssertCommand(["gnt-cluster", "modify", "--reserved-lvs",
456
                ".*/%s.*" % _QA_LV_PREFIX])
457
  AssertClusterVerify(no_warnings=[constants.CV_ENODEORPHANLV])
458

    
459
  AssertCommand(["gnt-cluster", "modify", "--reserved-lvs", ""])
460
  AssertClusterVerify(fail=False,
461
                      warnings=[constants.CV_ENODEORPHANLV])
462

    
463
  AssertCommand(["lvremove", "-f", lvfullname])
464
  AssertClusterVerify()
465

    
466

    
467
def TestClusterModifyEmpty():
468
  """gnt-cluster modify"""
469
  AssertCommand(["gnt-cluster", "modify"], fail=True)
470

    
471

    
472
def TestClusterModifyDisk():
473
  """gnt-cluster modify -D"""
474
  for param in _FAIL_PARAMS:
475
    AssertCommand(["gnt-cluster", "modify", "-D", param], fail=True)
476

    
477

    
478
def _GetOtherEnabledDiskTemplate(undesired_disk_templates,
479
                                 enabled_disk_templates):
480
  """Returns one template that is not in the undesired set.
481

482
  @type undesired_disk_templates: list of string
483
  @param undesired_disk_templates: a list of disk templates that we want to
484
      exclude when drawing one disk template from the list of enabled
485
      disk templates
486
  @type enabled_disk_templates: list of string
487
  @param enabled_disk_templates: list of enabled disk templates (in QA)
488

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

    
499
  return template
500

    
501

    
502
def TestClusterModifyFileBasedStorageDir(
503
    file_disk_template, dir_config_key, default_dir, option_name):
504
  """Tests gnt-cluster modify wrt to file-based directory options.
505

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

518
  """
519
  enabled_disk_templates = qa_config.GetEnabledDiskTemplates()
520
  assert file_disk_template in constants.DTS_FILEBASED
521
  if not qa_config.IsTemplateSupported(file_disk_template):
522
    return
523

    
524
  # Get some non-file-based disk template to disable file storage
525
  other_disk_template = _GetOtherEnabledDiskTemplate(
526
    utils.storage.GetDiskTemplatesOfStorageTypes(constants.ST_FILE,
527
                                                 constants.ST_SHARED_FILE),
528
    enabled_disk_templates
529
  )
530

    
531
  file_storage_dir = qa_config.get(dir_config_key, default_dir)
532
  invalid_file_storage_dir = "/boot/"
533

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

    
571

    
572
def TestClusterModifyFileStorageDir():
573
  """gnt-cluster modify --file-storage-dir=..."""
574
  TestClusterModifyFileBasedStorageDir(
575
      constants.DT_FILE, "default-file-storage-dir",
576
      pathutils.DEFAULT_FILE_STORAGE_DIR,
577
      "file-storage-dir")
578

    
579

    
580
def TestClusterModifySharedFileStorageDir():
581
  """gnt-cluster modify --shared-file-storage-dir=..."""
582
  TestClusterModifyFileBasedStorageDir(
583
      constants.DT_SHARED_FILE, "default-shared-file-storage-dir",
584
      pathutils.DEFAULT_SHARED_FILE_STORAGE_DIR,
585
      "shared-file-storage-dir")
586

    
587

    
588
def TestClusterModifyDiskTemplates():
589
  """gnt-cluster modify --enabled-disk-templates=..."""
590
  enabled_disk_templates = qa_config.GetEnabledDiskTemplates()
591
  default_disk_template = qa_config.GetDefaultDiskTemplate()
592

    
593
  _TestClusterModifyDiskTemplatesArguments(default_disk_template)
594
  _TestClusterModifyDiskTemplatesDrbdHelper(enabled_disk_templates)
595
  _TestClusterModifyDiskTemplatesVgName(enabled_disk_templates)
596

    
597
  _RestoreEnabledDiskTemplates()
598
  nodes = qa_config.AcquireManyNodes(2)
599

    
600
  instance_template = enabled_disk_templates[0]
601
  instance = qa_instance.CreateInstanceByDiskTemplate(nodes, instance_template)
602

    
603
  _TestClusterModifyUsedDiskTemplate(instance_template,
604
                                     enabled_disk_templates)
605

    
606
  qa_instance.TestInstanceRemove(instance)
607
  _RestoreEnabledDiskTemplates()
608

    
609

    
610
def TestClusterModifyInstallImage():
611
  """gnt-cluster modify --install-image=...'"""
612
  master = qa_config.GetMasterNode()
613

    
614
  image = \
615
      GetCommandOutput(master.primary,
616
                       "mktemp --tmpdir ganeti-install-image.XXXXXX").strip()
617
  AssertCommand(["gnt-cluster", "modify", "--install-image=%s" % image])
618
  AssertCommand(["rm", image])
619

    
620

    
621
def _RestoreEnabledDiskTemplates():
622
  """Sets the list of enabled disk templates back to the list of enabled disk
623
     templates from the QA configuration. This can be used to make sure that
624
     the tests that modify the list of disk templates do not interfere with
625
     other tests.
626

627
  """
628
  enabled_disk_templates = qa_config.GetEnabledDiskTemplates()
629
  cmd = ["gnt-cluster", "modify",
630
         "--enabled-disk-templates=%s" % ",".join(enabled_disk_templates),
631
         "--ipolicy-disk-templates=%s" % ",".join(enabled_disk_templates),
632
         ]
633

    
634
  if utils.IsLvmEnabled(qa_config.GetEnabledDiskTemplates()):
635
    vgname = qa_config.get("vg-name", constants.DEFAULT_VG)
636
    cmd.append("--vg-name=%s" % vgname)
637

    
638
  AssertCommand(cmd, fail=False)
639

    
640

    
641
def _TestClusterModifyDiskTemplatesDrbdHelper(enabled_disk_templates):
642
  """Tests argument handling of 'gnt-cluster modify' with respect to
643
     the parameter '--drbd-usermode-helper'. This test is independent
644
     of instances.
645

646
  """
647
  _RestoreEnabledDiskTemplates()
648

    
649
  if constants.DT_DRBD8 not in enabled_disk_templates:
650
    return
651
  if constants.DT_PLAIN not in enabled_disk_templates:
652
    return
653

    
654
  drbd_usermode_helper = qa_config.get("drbd-usermode-helper", "/bin/true")
655
  bogus_usermode_helper = "/tmp/pinkbunny"
656
  for command, fail in [
657
    (["gnt-cluster", "modify",
658
      "--enabled-disk-templates=%s" % constants.DT_DRBD8,
659
      "--ipolicy-disk-templates=%s" % constants.DT_DRBD8], False),
660
    (["gnt-cluster", "modify",
661
      "--drbd-usermode-helper=%s" % drbd_usermode_helper], False),
662
    (["gnt-cluster", "modify",
663
      "--drbd-usermode-helper=%s" % bogus_usermode_helper], True),
664
    # unsetting helper when DRBD is enabled should not work
665
    (["gnt-cluster", "modify",
666
      "--drbd-usermode-helper="], True),
667
    (["gnt-cluster", "modify",
668
      "--enabled-disk-templates=%s" % constants.DT_PLAIN,
669
      "--ipolicy-disk-templates=%s" % constants.DT_PLAIN], False),
670
    (["gnt-cluster", "modify",
671
      "--drbd-usermode-helper="], False),
672
    (["gnt-cluster", "modify",
673
      "--drbd-usermode-helper=%s" % drbd_usermode_helper], False),
674
    (["gnt-cluster", "modify",
675
      "--drbd-usermode-helper=%s" % drbd_usermode_helper,
676
      "--enabled-disk-templates=%s" % constants.DT_DRBD8,
677
      "--ipolicy-disk-templates=%s" % constants.DT_DRBD8], False),
678
    (["gnt-cluster", "modify",
679
      "--drbd-usermode-helper=",
680
      "--enabled-disk-templates=%s" % constants.DT_PLAIN,
681
      "--ipolicy-disk-templates=%s" % constants.DT_PLAIN], False),
682
    (["gnt-cluster", "modify",
683
      "--drbd-usermode-helper=%s" % drbd_usermode_helper,
684
      "--enabled-disk-templates=%s" % constants.DT_DRBD8,
685
      "--ipolicy-disk-templates=%s" % constants.DT_DRBD8], False),
686
    ]:
687
    AssertCommand(command, fail=fail)
688
  _RestoreEnabledDiskTemplates()
689

    
690

    
691
def _TestClusterModifyDiskTemplatesArguments(default_disk_template):
692
  """Tests argument handling of 'gnt-cluster modify' with respect to
693
     the parameter '--enabled-disk-templates'. This test is independent
694
     of instances.
695

696
  """
697
  _RestoreEnabledDiskTemplates()
698

    
699
  # bogus templates
700
  AssertCommand(["gnt-cluster", "modify",
701
                 "--enabled-disk-templates=pinkbunny"],
702
                fail=True)
703

    
704
  # duplicate entries do no harm
705
  AssertCommand(
706
    ["gnt-cluster", "modify",
707
     "--enabled-disk-templates=%s,%s" %
708
      (default_disk_template, default_disk_template),
709
     "--ipolicy-disk-templates=%s" % default_disk_template],
710
    fail=False)
711

    
712

    
713
def _TestClusterModifyDiskTemplatesVgName(enabled_disk_templates):
714
  """Tests argument handling of 'gnt-cluster modify' with respect to
715
     the parameter '--enabled-disk-templates' and '--vg-name'. This test is
716
     independent of instances.
717

718
  """
719
  if not utils.IsLvmEnabled(enabled_disk_templates):
720
    # These tests only make sense if lvm is enabled for QA
721
    return
722

    
723
  # determine an LVM and a non-LVM disk template for the tests
724
  non_lvm_template = _GetOtherEnabledDiskTemplate(constants.DTS_LVM,
725
                                                  enabled_disk_templates)
726
  lvm_template = list(set(enabled_disk_templates) & constants.DTS_LVM)[0]
727

    
728
  vgname = qa_config.get("vg-name", constants.DEFAULT_VG)
729

    
730
  # Clean start: unset volume group name, disable lvm storage
731
  AssertCommand(
732
    ["gnt-cluster", "modify",
733
     "--enabled-disk-templates=%s" % non_lvm_template,
734
     "--ipolicy-disk-templates=%s" % non_lvm_template,
735
     "--vg-name="],
736
    fail=False)
737

    
738
  # Try to enable lvm, when no volume group is given
739
  AssertCommand(
740
    ["gnt-cluster", "modify",
741
     "--enabled-disk-templates=%s" % lvm_template,
742
     "--ipolicy-disk-templates=%s" % lvm_template],
743
    fail=True)
744

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

    
748
  # Try unsetting vg name and enabling lvm at the same time
749
  AssertCommand(
750
    ["gnt-cluster", "modify",
751
     "--enabled-disk-templates=%s" % lvm_template,
752
     "--ipolicy-disk-templates=%s" % lvm_template,
753
     "--vg-name="],
754
    fail=True)
755

    
756
  # Enable lvm with vg name present
757
  AssertCommand(
758
    ["gnt-cluster", "modify",
759
     "--enabled-disk-templates=%s" % lvm_template,
760
     "--ipolicy-disk-templates=%s" % lvm_template],
761
    fail=False)
762

    
763
  # Try unsetting vg name with lvm still enabled
764
  AssertCommand(["gnt-cluster", "modify", "--vg-name="], fail=True)
765

    
766
  # Disable lvm with vg name still set
767
  AssertCommand(
768
    ["gnt-cluster", "modify",
769
     "--enabled-disk-templates=%s" % non_lvm_template,
770
     "--ipolicy-disk-templates=%s" % non_lvm_template,
771
     ],
772
    fail=False)
773

    
774
  # Try unsetting vg name with lvm disabled
775
  AssertCommand(["gnt-cluster", "modify", "--vg-name="], fail=False)
776

    
777
  # Set vg name and enable lvm at the same time
778
  AssertCommand(
779
    ["gnt-cluster", "modify",
780
     "--enabled-disk-templates=%s" % lvm_template,
781
     "--ipolicy-disk-templates=%s" % lvm_template,
782
     "--vg-name=%s" % vgname],
783
    fail=False)
784

    
785
  # Unset vg name and disable lvm at the same time
786
  AssertCommand(
787
    ["gnt-cluster", "modify",
788
     "--enabled-disk-templates=%s" % non_lvm_template,
789
     "--ipolicy-disk-templates=%s" % non_lvm_template,
790
     "--vg-name="],
791
    fail=False)
792

    
793
  _RestoreEnabledDiskTemplates()
794

    
795

    
796
def _TestClusterModifyUsedDiskTemplate(instance_template,
797
                                       enabled_disk_templates):
798
  """Tests that disk templates that are currently in use by instances cannot
799
     be disabled on the cluster.
800

801
  """
802
  # If the list of enabled disk templates contains only one template
803
  # we need to add some other templates, because the list of enabled disk
804
  # templates can only be set to a non-empty list.
805
  new_disk_templates = list(set(enabled_disk_templates)
806
                              - set([instance_template]))
807
  if not new_disk_templates:
808
    new_disk_templates = list(set([constants.DT_DISKLESS, constants.DT_BLOCK])
809
                                - set([instance_template]))
810
  AssertCommand(
811
    ["gnt-cluster", "modify",
812
     "--enabled-disk-templates=%s" % ",".join(new_disk_templates),
813
     "--ipolicy-disk-templates=%s" % ",".join(new_disk_templates)],
814
    fail=True)
815

    
816

    
817
def TestClusterModifyBe():
818
  """gnt-cluster modify -B"""
819
  for fail, cmd in [
820
    # max/min mem
821
    (False, ["gnt-cluster", "modify", "-B", "maxmem=256"]),
822
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *maxmem: 256$'"]),
823
    (False, ["gnt-cluster", "modify", "-B", "minmem=256"]),
824
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *minmem: 256$'"]),
825
    (True, ["gnt-cluster", "modify", "-B", "maxmem=a"]),
826
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *maxmem: 256$'"]),
827
    (True, ["gnt-cluster", "modify", "-B", "minmem=a"]),
828
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *minmem: 256$'"]),
829
    (False, ["gnt-cluster", "modify", "-B", "maxmem=128,minmem=128"]),
830
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *maxmem: 128$'"]),
831
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *minmem: 128$'"]),
832
    # vcpus
833
    (False, ["gnt-cluster", "modify", "-B", "vcpus=4"]),
834
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *vcpus: 4$'"]),
835
    (True, ["gnt-cluster", "modify", "-B", "vcpus=a"]),
836
    (False, ["gnt-cluster", "modify", "-B", "vcpus=1"]),
837
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *vcpus: 1$'"]),
838
    # auto_balance
839
    (False, ["gnt-cluster", "modify", "-B", "auto_balance=False"]),
840
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *auto_balance: False$'"]),
841
    (True, ["gnt-cluster", "modify", "-B", "auto_balance=1"]),
842
    (False, ["gnt-cluster", "modify", "-B", "auto_balance=True"]),
843
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *auto_balance: True$'"]),
844
    ]:
845
    AssertCommand(cmd, fail=fail)
846

    
847
  # redo the original-requested BE parameters, if any
848
  bep = qa_config.get("backend-parameters", "")
849
  if bep:
850
    AssertCommand(["gnt-cluster", "modify", "-B", bep])
851

    
852

    
853
def _GetClusterIPolicy():
854
  """Return the run-time values of the cluster-level instance policy.
855

856
  @rtype: tuple
857
  @return: (policy, specs), where:
858
      - policy is a dictionary of the policy values, instance specs excluded
859
      - specs is a dictionary containing only the specs, using the internal
860
        format (see L{constants.IPOLICY_DEFAULTS} for an example)
861

862
  """
863
  info = qa_utils.GetObjectInfo(["gnt-cluster", "info"])
864
  policy = info["Instance policy - limits for instances"]
865
  (ret_policy, ret_specs) = qa_utils.ParseIPolicy(policy)
866

    
867
  # Sanity checks
868
  assert "minmax" in ret_specs and "std" in ret_specs
869
  assert len(ret_specs["minmax"]) > 0
870
  assert len(ret_policy) > 0
871
  return (ret_policy, ret_specs)
872

    
873

    
874
def TestClusterModifyIPolicy():
875
  """gnt-cluster modify --ipolicy-*"""
876
  basecmd = ["gnt-cluster", "modify"]
877
  (old_policy, old_specs) = _GetClusterIPolicy()
878
  for par in ["vcpu-ratio", "spindle-ratio"]:
879
    curr_val = float(old_policy[par])
880
    test_values = [
881
      (True, 1.0),
882
      (True, 1.5),
883
      (True, 2),
884
      (False, "a"),
885
      # Restore the old value
886
      (True, curr_val),
887
      ]
888
    for (good, val) in test_values:
889
      cmd = basecmd + ["--ipolicy-%s=%s" % (par, val)]
890
      AssertCommand(cmd, fail=not good)
891
      if good:
892
        curr_val = val
893
      # Check the affected parameter
894
      (eff_policy, eff_specs) = _GetClusterIPolicy()
895
      AssertEqual(float(eff_policy[par]), curr_val)
896
      # Check everything else
897
      AssertEqual(eff_specs, old_specs)
898
      for p in eff_policy.keys():
899
        if p == par:
900
          continue
901
        AssertEqual(eff_policy[p], old_policy[p])
902

    
903
  # Allowing disk templates via ipolicy requires them to be
904
  # enabled on the cluster.
905
  if not (qa_config.IsTemplateSupported(constants.DT_PLAIN)
906
          and qa_config.IsTemplateSupported(constants.DT_DRBD8)):
907
    return
908
  # Disk templates are treated slightly differently
909
  par = "disk-templates"
910
  disp_str = "allowed disk templates"
911
  curr_val = old_policy[disp_str]
912
  test_values = [
913
    (True, constants.DT_PLAIN),
914
    (True, "%s,%s" % (constants.DT_PLAIN, constants.DT_DRBD8)),
915
    (False, "thisisnotadisktemplate"),
916
    (False, ""),
917
    # Restore the old value
918
    (True, curr_val.replace(" ", "")),
919
    ]
920
  for (good, val) in test_values:
921
    cmd = basecmd + ["--ipolicy-%s=%s" % (par, val)]
922
    AssertCommand(cmd, fail=not good)
923
    if good:
924
      curr_val = val
925
    # Check the affected parameter
926
    (eff_policy, eff_specs) = _GetClusterIPolicy()
927
    AssertEqual(eff_policy[disp_str].replace(" ", ""), curr_val)
928
    # Check everything else
929
    AssertEqual(eff_specs, old_specs)
930
    for p in eff_policy.keys():
931
      if p == disp_str:
932
        continue
933
      AssertEqual(eff_policy[p], old_policy[p])
934

    
935

    
936
def TestClusterSetISpecs(new_specs=None, diff_specs=None, fail=False,
937
                         old_values=None):
938
  """Change instance specs.
939

940
  At most one of new_specs or diff_specs can be specified.
941

942
  @type new_specs: dict
943
  @param new_specs: new complete specs, in the same format returned by
944
      L{_GetClusterIPolicy}
945
  @type diff_specs: dict
946
  @param diff_specs: partial specs, it can be an incomplete specifications, but
947
      if min/max specs are specified, their number must match the number of the
948
      existing specs
949
  @type fail: bool
950
  @param fail: if the change is expected to fail
951
  @type old_values: tuple
952
  @param old_values: (old_policy, old_specs), as returned by
953
      L{_GetClusterIPolicy}
954
  @return: same as L{_GetClusterIPolicy}
955

956
  """
957
  build_cmd = lambda opts: ["gnt-cluster", "modify"] + opts
958
  return qa_utils.TestSetISpecs(
959
    new_specs=new_specs, diff_specs=diff_specs,
960
    get_policy_fn=_GetClusterIPolicy, build_cmd_fn=build_cmd,
961
    fail=fail, old_values=old_values)
962

    
963

    
964
def TestClusterModifyISpecs():
965
  """gnt-cluster modify --specs-*"""
966
  params = ["memory-size", "disk-size", "disk-count", "cpu-count", "nic-count"]
967
  (cur_policy, cur_specs) = _GetClusterIPolicy()
968
  # This test assumes that there is only one min/max bound
969
  assert len(cur_specs[constants.ISPECS_MINMAX]) == 1
970
  for par in params:
971
    test_values = [
972
      (True, 0, 4, 12),
973
      (True, 4, 4, 12),
974
      (True, 4, 12, 12),
975
      (True, 4, 4, 4),
976
      (False, 4, 0, 12),
977
      (False, 4, 16, 12),
978
      (False, 4, 4, 0),
979
      (False, 12, 4, 4),
980
      (False, 12, 4, 0),
981
      (False, "a", 4, 12),
982
      (False, 0, "a", 12),
983
      (False, 0, 4, "a"),
984
      # This is to restore the old values
985
      (True,
986
       cur_specs[constants.ISPECS_MINMAX][0][constants.ISPECS_MIN][par],
987
       cur_specs[constants.ISPECS_STD][par],
988
       cur_specs[constants.ISPECS_MINMAX][0][constants.ISPECS_MAX][par])
989
      ]
990
    for (good, mn, st, mx) in test_values:
991
      new_vals = {
992
        constants.ISPECS_MINMAX: [{
993
          constants.ISPECS_MIN: {par: mn},
994
          constants.ISPECS_MAX: {par: mx}
995
          }],
996
        constants.ISPECS_STD: {par: st}
997
        }
998
      cur_state = (cur_policy, cur_specs)
999
      # We update cur_specs, as we've copied the values to restore already
1000
      (cur_policy, cur_specs) = TestClusterSetISpecs(
1001
        diff_specs=new_vals, fail=not good, old_values=cur_state)
1002

    
1003
    # Get the ipolicy command
1004
    mnode = qa_config.GetMasterNode()
1005
    initcmd = GetCommandOutput(mnode.primary, "gnt-cluster show-ispecs-cmd")
1006
    modcmd = ["gnt-cluster", "modify"]
1007
    opts = initcmd.split()
1008
    assert opts[0:2] == ["gnt-cluster", "init"]
1009
    for k in range(2, len(opts) - 1):
1010
      if opts[k].startswith("--ipolicy-"):
1011
        assert k + 2 <= len(opts)
1012
        modcmd.extend(opts[k:k + 2])
1013
    # Re-apply the ipolicy (this should be a no-op)
1014
    AssertCommand(modcmd)
1015
    new_initcmd = GetCommandOutput(mnode.primary, "gnt-cluster show-ispecs-cmd")
1016
    AssertEqual(initcmd, new_initcmd)
1017

    
1018

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

    
1023

    
1024
def TestClusterRedistConf():
1025
  """gnt-cluster redist-conf"""
1026
  AssertCommand(["gnt-cluster", "redist-conf"])
1027

    
1028

    
1029
def TestClusterGetmaster():
1030
  """gnt-cluster getmaster"""
1031
  AssertCommand(["gnt-cluster", "getmaster"])
1032

    
1033

    
1034
def TestClusterVersion():
1035
  """gnt-cluster version"""
1036
  AssertCommand(["gnt-cluster", "version"])
1037

    
1038

    
1039
def TestClusterRenewCrypto():
1040
  """gnt-cluster renew-crypto"""
1041
  master = qa_config.GetMasterNode()
1042

    
1043
  # Conflicting options
1044
  cmd = ["gnt-cluster", "renew-crypto", "--force",
1045
         "--new-cluster-certificate", "--new-confd-hmac-key"]
1046
  conflicting = [
1047
    ["--new-rapi-certificate", "--rapi-certificate=/dev/null"],
1048
    ["--new-cluster-domain-secret", "--cluster-domain-secret=/dev/null"],
1049
    ]
1050
  for i in conflicting:
1051
    AssertCommand(cmd + i, fail=True)
1052

    
1053
  # Invalid RAPI certificate
1054
  cmd = ["gnt-cluster", "renew-crypto", "--force",
1055
         "--rapi-certificate=/dev/null"]
1056
  AssertCommand(cmd, fail=True)
1057

    
1058
  rapi_cert_backup = qa_utils.BackupFile(master.primary,
1059
                                         pathutils.RAPI_CERT_FILE)
1060
  try:
1061
    # Custom RAPI certificate
1062
    fh = tempfile.NamedTemporaryFile()
1063

    
1064
    # Ensure certificate doesn't cause "gnt-cluster verify" to complain
1065
    validity = constants.SSL_CERT_EXPIRATION_WARN * 3
1066

    
1067
    utils.GenerateSelfSignedSslCert(fh.name, 1, validity=validity)
1068

    
1069
    tmpcert = qa_utils.UploadFile(master.primary, fh.name)
1070
    try:
1071
      AssertCommand(["gnt-cluster", "renew-crypto", "--force",
1072
                     "--rapi-certificate=%s" % tmpcert])
1073
    finally:
1074
      AssertCommand(["rm", "-f", tmpcert])
1075

    
1076
    # Custom cluster domain secret
1077
    cds_fh = tempfile.NamedTemporaryFile()
1078
    cds_fh.write(utils.GenerateSecret())
1079
    cds_fh.write("\n")
1080
    cds_fh.flush()
1081

    
1082
    tmpcds = qa_utils.UploadFile(master.primary, cds_fh.name)
1083
    try:
1084
      AssertCommand(["gnt-cluster", "renew-crypto", "--force",
1085
                     "--cluster-domain-secret=%s" % tmpcds])
1086
    finally:
1087
      AssertCommand(["rm", "-f", tmpcds])
1088

    
1089
    # Normal case
1090
    AssertCommand(["gnt-cluster", "renew-crypto", "--force",
1091
                   "--new-cluster-certificate", "--new-confd-hmac-key",
1092
                   "--new-rapi-certificate", "--new-cluster-domain-secret",
1093
                   "--new-node-certificates"])
1094

    
1095
    # Only renew node certificates
1096
    AssertCommand(["gnt-cluster", "renew-crypto", "--force",
1097
                   "--new-node-certificates"])
1098

    
1099
    # Restore RAPI certificate
1100
    AssertCommand(["gnt-cluster", "renew-crypto", "--force",
1101
                   "--rapi-certificate=%s" % rapi_cert_backup])
1102
  finally:
1103
    AssertCommand(["rm", "-f", rapi_cert_backup])
1104

    
1105

    
1106
def TestClusterBurnin():
1107
  """Burnin"""
1108
  master = qa_config.GetMasterNode()
1109

    
1110
  options = qa_config.get("options", {})
1111
  disk_template = options.get("burnin-disk-template", constants.DT_DRBD8)
1112
  parallel = options.get("burnin-in-parallel", False)
1113
  check_inst = options.get("burnin-check-instances", False)
1114
  do_rename = options.get("burnin-rename", "")
1115
  do_reboot = options.get("burnin-reboot", True)
1116
  reboot_types = options.get("reboot-types", constants.REBOOT_TYPES)
1117

    
1118
  # Get as many instances as we need
1119
  instances = []
1120
  try:
1121
    try:
1122
      num = qa_config.get("options", {}).get("burnin-instances", 1)
1123
      for _ in range(0, num):
1124
        instances.append(qa_config.AcquireInstance())
1125
    except qa_error.OutOfInstancesError:
1126
      print "Not enough instances, continuing anyway."
1127

    
1128
    if len(instances) < 1:
1129
      raise qa_error.Error("Burnin needs at least one instance")
1130

    
1131
    script = qa_utils.UploadFile(master.primary, "../tools/burnin")
1132
    try:
1133
      disks = qa_config.GetDiskOptions()
1134
      # Run burnin
1135
      cmd = ["env",
1136
             "PYTHONPATH=%s" % _constants.VERSIONEDSHAREDIR,
1137
             script,
1138
             "--os=%s" % qa_config.get("os"),
1139
             "--minmem-size=%s" % qa_config.get(constants.BE_MINMEM),
1140
             "--maxmem-size=%s" % qa_config.get(constants.BE_MAXMEM),
1141
             "--disk-size=%s" % ",".join([d.get("size") for d in disks]),
1142
             "--disk-growth=%s" % ",".join([d.get("growth") for d in disks]),
1143
             "--disk-template=%s" % disk_template]
1144
      if parallel:
1145
        cmd.append("--parallel")
1146
        cmd.append("--early-release")
1147
      if check_inst:
1148
        cmd.append("--http-check")
1149
      if do_rename:
1150
        cmd.append("--rename=%s" % do_rename)
1151
      if not do_reboot:
1152
        cmd.append("--no-reboot")
1153
      else:
1154
        cmd.append("--reboot-types=%s" % ",".join(reboot_types))
1155
      cmd += [inst.name for inst in instances]
1156
      AssertCommand(cmd)
1157
    finally:
1158
      AssertCommand(["rm", "-f", script])
1159

    
1160
  finally:
1161
    for inst in instances:
1162
      inst.Release()
1163

    
1164

    
1165
def TestClusterMasterFailover():
1166
  """gnt-cluster master-failover"""
1167
  master = qa_config.GetMasterNode()
1168
  failovermaster = qa_config.AcquireNode(exclude=master)
1169

    
1170
  cmd = ["gnt-cluster", "master-failover"]
1171
  node_list_cmd = ["gnt-node", "list"]
1172
  try:
1173
    AssertCommand(cmd, node=failovermaster)
1174
    AssertCommand(node_list_cmd, node=failovermaster)
1175
    # Back to original master node
1176
    AssertCommand(cmd, node=master)
1177
    AssertCommand(node_list_cmd, node=master)
1178
  finally:
1179
    failovermaster.Release()
1180

    
1181

    
1182
def TestUpgrade():
1183
  """Test gnt-cluster upgrade.
1184

1185
  This tests the 'gnt-cluster upgrade' command by flipping
1186
  between the current and a different version of Ganeti.
1187
  To also recover subtile points in the configuration up/down
1188
  grades, instances are left over both upgrades.
1189

1190
  """
1191
  this_version = qa_config.get("dir-version")
1192
  other_version = qa_config.get("other-dir-version")
1193
  if this_version is None or other_version is None:
1194
    print qa_utils.FormatInfo("Test not run, as versions not specified")
1195
    return
1196

    
1197
  inst_creates = []
1198
  upgrade_instances = qa_config.get("upgrade-instances", [])
1199
  live_instances = []
1200
  for (test_name, templ, cf, n) in qa_instance.available_instance_tests:
1201
    if (qa_config.TestEnabled(test_name) and
1202
        qa_config.IsTemplateSupported(templ) and
1203
        templ in upgrade_instances):
1204
      inst_creates.append((cf, n))
1205

    
1206
  for (cf, n) in inst_creates:
1207
    nodes = qa_config.AcquireManyNodes(n)
1208
    live_instances.append(cf(nodes))
1209

    
1210
  AssertCommand(["gnt-cluster", "upgrade", "--to", other_version])
1211
  AssertCommand(["gnt-cluster", "verify"])
1212

    
1213
  for instance in live_instances:
1214
    qa_instance.TestInstanceRemove(instance)
1215
    instance.Release()
1216
  live_instances = []
1217
  for (cf, n) in inst_creates:
1218
    nodes = qa_config.AcquireManyNodes(n)
1219
    live_instances.append(cf(nodes))
1220

    
1221
  AssertCommand(["gnt-cluster", "upgrade", "--to", this_version])
1222
  AssertCommand(["gnt-cluster", "verify"])
1223

    
1224
  for instance in live_instances:
1225
    qa_instance.TestInstanceRemove(instance)
1226
    instance.Release()
1227

    
1228

    
1229
def _NodeQueueDrainFile(node):
1230
  """Returns path to queue drain file for a node.
1231

1232
  """
1233
  return qa_utils.MakeNodePath(node, pathutils.JOB_QUEUE_DRAIN_FILE)
1234

    
1235

    
1236
def _AssertDrainFile(node, **kwargs):
1237
  """Checks for the queue drain file.
1238

1239
  """
1240
  AssertCommand(["test", "-f", _NodeQueueDrainFile(node)], node=node, **kwargs)
1241

    
1242

    
1243
def TestClusterMasterFailoverWithDrainedQueue():
1244
  """gnt-cluster master-failover with drained queue"""
1245
  master = qa_config.GetMasterNode()
1246
  failovermaster = qa_config.AcquireNode(exclude=master)
1247

    
1248
  # Ensure queue is not drained
1249
  for node in [master, failovermaster]:
1250
    _AssertDrainFile(node, fail=True)
1251

    
1252
  # Drain queue on failover master
1253
  AssertCommand(["touch", _NodeQueueDrainFile(failovermaster)],
1254
                node=failovermaster)
1255

    
1256
  cmd = ["gnt-cluster", "master-failover"]
1257
  try:
1258
    _AssertDrainFile(failovermaster)
1259
    AssertCommand(cmd, node=failovermaster)
1260
    _AssertDrainFile(master, fail=True)
1261
    _AssertDrainFile(failovermaster, fail=True)
1262

    
1263
    # Back to original master node
1264
    AssertCommand(cmd, node=master)
1265
  finally:
1266
    failovermaster.Release()
1267

    
1268
  # Ensure queue is not drained
1269
  for node in [master, failovermaster]:
1270
    _AssertDrainFile(node, fail=True)
1271

    
1272

    
1273
def TestClusterCopyfile():
1274
  """gnt-cluster copyfile"""
1275
  master = qa_config.GetMasterNode()
1276

    
1277
  uniqueid = utils.NewUUID()
1278

    
1279
  # Create temporary file
1280
  f = tempfile.NamedTemporaryFile()
1281
  f.write(uniqueid)
1282
  f.flush()
1283
  f.seek(0)
1284

    
1285
  # Upload file to master node
1286
  testname = qa_utils.UploadFile(master.primary, f.name)
1287
  try:
1288
    # Copy file to all nodes
1289
    AssertCommand(["gnt-cluster", "copyfile", testname])
1290
    _CheckFileOnAllNodes(testname, uniqueid)
1291
  finally:
1292
    _RemoveFileFromAllNodes(testname)
1293

    
1294

    
1295
def TestClusterCommand():
1296
  """gnt-cluster command"""
1297
  uniqueid = utils.NewUUID()
1298
  rfile = "/tmp/gnt%s" % utils.NewUUID()
1299
  rcmd = utils.ShellQuoteArgs(["echo", "-n", uniqueid])
1300
  cmd = utils.ShellQuoteArgs(["gnt-cluster", "command",
1301
                              "%s >%s" % (rcmd, rfile)])
1302

    
1303
  try:
1304
    AssertCommand(cmd)
1305
    _CheckFileOnAllNodes(rfile, uniqueid)
1306
  finally:
1307
    _RemoveFileFromAllNodes(rfile)
1308

    
1309

    
1310
def TestClusterDestroy():
1311
  """gnt-cluster destroy"""
1312
  AssertCommand(["gnt-cluster", "destroy", "--yes-do-it"])
1313

    
1314

    
1315
def TestClusterRepairDiskSizes():
1316
  """gnt-cluster repair-disk-sizes"""
1317
  AssertCommand(["gnt-cluster", "repair-disk-sizes"])
1318

    
1319

    
1320
def TestSetExclStorCluster(newvalue):
1321
  """Set the exclusive_storage node parameter at the cluster level.
1322

1323
  @type newvalue: bool
1324
  @param newvalue: New value of exclusive_storage
1325
  @rtype: bool
1326
  @return: The old value of exclusive_storage
1327

1328
  """
1329
  es_path = ["Default node parameters", "exclusive_storage"]
1330
  oldvalue = _GetClusterField(es_path)
1331
  AssertCommand(["gnt-cluster", "modify", "--node-parameters",
1332
                 "exclusive_storage=%s" % newvalue])
1333
  effvalue = _GetClusterField(es_path)
1334
  if effvalue != newvalue:
1335
    raise qa_error.Error("exclusive_storage has the wrong value: %s instead"
1336
                         " of %s" % (effvalue, newvalue))
1337
  qa_config.SetExclusiveStorage(newvalue)
1338
  return oldvalue
1339

    
1340

    
1341
def TestExclStorSharedPv(node):
1342
  """cluster-verify reports LVs that share the same PV with exclusive_storage.
1343

1344
  """
1345
  vgname = qa_config.get("vg-name", constants.DEFAULT_VG)
1346
  lvname1 = _QA_LV_PREFIX + "vol1"
1347
  lvname2 = _QA_LV_PREFIX + "vol2"
1348
  node_name = node.primary
1349
  AssertCommand(["lvcreate", "-L1G", "-n", lvname1, vgname], node=node_name)
1350
  AssertClusterVerify(fail=False,
1351
                      warnings=[constants.CV_ENODEORPHANLV])
1352
  AssertCommand(["lvcreate", "-L1G", "-n", lvname2, vgname], node=node_name)
1353
  AssertClusterVerify(fail=True,
1354
                      errors=[constants.CV_ENODELVM],
1355
                      warnings=[constants.CV_ENODEORPHANLV])
1356
  AssertCommand(["lvremove", "-f", "/".join([vgname, lvname1])], node=node_name)
1357
  AssertCommand(["lvremove", "-f", "/".join([vgname, lvname2])], node=node_name)
1358
  AssertClusterVerify()
1359

    
1360

    
1361
def TestInstanceCommunication():
1362
  """Tests instance communication via 'gnt-cluster modify'"""
1363
  master = qa_config.GetMasterNode()
1364

    
1365
  # Check that the 'default' node group exists
1366
  cmd = ["gnt-group", "list", "--no-headers", "-o", "name"]
1367
  result_output = GetCommandOutput(master.primary, utils.ShellQuoteArgs(cmd))
1368
  AssertEqual(result_output.strip(), "default", msg="Checking 'default' group")
1369

    
1370
  # Check that no networks exist
1371
  cmd = ["gnt-network", "list", "--no-headers", "-o", "name"]
1372
  result_output = GetCommandOutput(master.primary, utils.ShellQuoteArgs(cmd))
1373
  AssertEqual(result_output.strip(), "", msg="Checking networks")
1374

    
1375
  # Modify cluster parameter 'instance-communication-network' and
1376
  # check whether the cluster creates the instance communication
1377
  # network and connects it to the 'default' node group
1378
  network_name = "mynetwork"
1379

    
1380
  cmd = "gnt-cluster modify --instance-communication-network=%s" % network_name
1381
  result_output = qa_utils.GetCommandOutput(master.primary, cmd)
1382
  print result_output
1383

    
1384
  cmd = ["gnt-network", "list", "--no-headers", "-o", "name", network_name]
1385
  result_output = qa_utils.GetCommandOutput(master.primary,
1386
                                            utils.ShellQuoteArgs(cmd))
1387
  AssertEqual(result_output.strip(), "mynetwork", msg="Checking 'mynetwork'")
1388

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

    
1396
  # Check that the network has the parameters necessary for instance
1397
  # communication
1398
  cmd = ["gnt-network", "list", "--no-headers", "-o", "gateway",
1399
         network_name]
1400
  result_output = qa_utils.GetCommandOutput(master.primary,
1401
                                            utils.ShellQuoteArgs(cmd))
1402
  AssertEqual(result_output.strip(), "-", msg="Checking gateway")
1403

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

    
1410
  cmd = ["gnt-network", "list", "--no-headers", "-o", "network",
1411
         network_name]
1412
  result_output = qa_utils.GetCommandOutput(master.primary,
1413
                                            utils.ShellQuoteArgs(cmd))
1414
  AssertEqual(result_output.strip(), constants.INSTANCE_COMMUNICATION_NETWORK4,
1415
              msg="Checking network")
1416

    
1417
  cmd = ["gnt-network", "list", "--no-headers", "-o", "network6",
1418
         network_name]
1419
  result_output = qa_utils.GetCommandOutput(master.primary,
1420
                                            utils.ShellQuoteArgs(cmd))
1421
  AssertEqual(result_output.strip(), constants.INSTANCE_COMMUNICATION_NETWORK6,
1422
              msg="Checking network6")
1423

    
1424
  # Add a new group and check whether the instance communication
1425
  # network connects to this new group
1426
  #
1427
  # We don't assume any particular group order and allow the output of
1428
  # 'gnt-network list' to print the 'default' and 'mygroup' groups in
1429
  # any order.
1430
  group = "mygroup"
1431

    
1432
  cmd = ["gnt-group", "add", group]
1433
  result_output = qa_utils.GetCommandOutput(master.primary,
1434
                                            utils.ShellQuoteArgs(cmd))
1435
  print result_output
1436

    
1437
  cmd = ["gnt-network", "list", "--no-headers", "-o", "group_list",
1438
         network_name]
1439
  result_output = qa_utils.GetCommandOutput(master.primary,
1440
                                            utils.ShellQuoteArgs(cmd))
1441

    
1442
  try:
1443
    r1 = "mygroup (routed, communication_rt)," \
1444
         " default (routed, communication_rt)"
1445
    AssertEqual(result_output.strip(), r1,
1446
                msg="Checking network connected groups")
1447
  except qa_error.Error:
1448
    r2 = "default (routed, communication_rt)," \
1449
         " mygroup (routed, communication_rt)"
1450
    AssertEqual(result_output.strip(), r2,
1451
                msg="Checking network connected groups")
1452

    
1453
  # Modify cluster parameter 'instance-communication-network' to the
1454
  # same value and check that nothing happens.
1455
  cmd = "gnt-cluster modify --instance-communication-network=%s" % network_name
1456
  result_output = qa_utils.GetCommandOutput(master.primary, cmd)
1457
  print result_output
1458

    
1459
  # Disable instance communication network, disconnect the instance
1460
  # communication network and remove it, and remove the group
1461
  cmd = "gnt-cluster modify --instance-communication-network="
1462
  result_output = qa_utils.GetCommandOutput(master.primary, cmd)
1463
  print result_output
1464

    
1465
  cmd = ["gnt-network", "disconnect", network_name]
1466
  AssertCommand(utils.ShellQuoteArgs(cmd))
1467

    
1468
  cmd = ["gnt-network", "remove", network_name]
1469
  AssertCommand(utils.ShellQuoteArgs(cmd))
1470

    
1471
  cmd = ["gnt-group", "remove", group]
1472
  AssertCommand(utils.ShellQuoteArgs(cmd))
1473

    
1474
  # Check that the 'default' node group exists
1475
  cmd = ["gnt-group", "list", "--no-headers", "-o", "name"]
1476
  result_output = GetCommandOutput(master.primary, utils.ShellQuoteArgs(cmd))
1477
  AssertEqual(result_output.strip(), "default", msg="Checking 'default' group")
1478

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