Statistics
| Branch: | Tag: | Revision:

root / qa / qa_cluster.py @ ab4b1cf2

History | View | Annotate | Download (44.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_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

    
1079
    # Restore RAPI certificate
1080
    AssertCommand(["gnt-cluster", "renew-crypto", "--force",
1081
                   "--rapi-certificate=%s" % rapi_cert_backup])
1082
  finally:
1083
    AssertCommand(["rm", "-f", rapi_cert_backup])
1084

    
1085

    
1086
def TestClusterBurnin():
1087
  """Burnin"""
1088
  master = qa_config.GetMasterNode()
1089

    
1090
  options = qa_config.get("options", {})
1091
  disk_template = options.get("burnin-disk-template", constants.DT_DRBD8)
1092
  parallel = options.get("burnin-in-parallel", False)
1093
  check_inst = options.get("burnin-check-instances", False)
1094
  do_rename = options.get("burnin-rename", "")
1095
  do_reboot = options.get("burnin-reboot", True)
1096
  reboot_types = options.get("reboot-types", constants.REBOOT_TYPES)
1097

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

    
1108
    if len(instances) < 1:
1109
      raise qa_error.Error("Burnin needs at least one instance")
1110

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

    
1140
  finally:
1141
    for inst in instances:
1142
      inst.Release()
1143

    
1144

    
1145
def TestClusterMasterFailover():
1146
  """gnt-cluster master-failover"""
1147
  master = qa_config.GetMasterNode()
1148
  failovermaster = qa_config.AcquireNode(exclude=master)
1149

    
1150
  cmd = ["gnt-cluster", "master-failover"]
1151
  node_list_cmd = ["gnt-node", "list"]
1152
  try:
1153
    AssertCommand(cmd, node=failovermaster)
1154
    AssertCommand(node_list_cmd, node=failovermaster)
1155
    # Back to original master node
1156
    AssertCommand(cmd, node=master)
1157
    AssertCommand(node_list_cmd, node=master)
1158
  finally:
1159
    failovermaster.Release()
1160

    
1161

    
1162
def TestUpgrade():
1163
  """Test gnt-cluster upgrade.
1164

1165
  This tests the 'gnt-cluster upgrade' command by flipping
1166
  between the current and a different version of Ganeti.
1167
  To also recover subtile points in the configuration up/down
1168
  grades, instances are left over both upgrades.
1169

1170
  """
1171
  this_version = qa_config.get("dir-version")
1172
  other_version = qa_config.get("other-dir-version")
1173
  if this_version is None or other_version is None:
1174
    print qa_utils.FormatInfo("Test not run, as versions not specified")
1175
    return
1176

    
1177
  inst_creates = []
1178
  upgrade_instances = qa_config.get("upgrade-instances", [])
1179
  live_instances = []
1180
  for (test_name, templ, cf, n) in qa_instance.available_instance_tests:
1181
    if (qa_config.TestEnabled(test_name) and
1182
        qa_config.IsTemplateSupported(templ) and
1183
        templ in upgrade_instances):
1184
      inst_creates.append((cf, n))
1185

    
1186
  for (cf, n) in inst_creates:
1187
    nodes = qa_config.AcquireManyNodes(n)
1188
    live_instances.append(cf(nodes))
1189

    
1190
  AssertCommand(["gnt-cluster", "upgrade", "--to", other_version])
1191
  AssertCommand(["gnt-cluster", "verify"])
1192

    
1193
  for instance in live_instances:
1194
    qa_instance.TestInstanceRemove(instance)
1195
    instance.Release()
1196
  live_instances = []
1197
  for (cf, n) in inst_creates:
1198
    nodes = qa_config.AcquireManyNodes(n)
1199
    live_instances.append(cf(nodes))
1200

    
1201
  AssertCommand(["gnt-cluster", "upgrade", "--to", this_version])
1202
  AssertCommand(["gnt-cluster", "verify"])
1203

    
1204
  for instance in live_instances:
1205
    qa_instance.TestInstanceRemove(instance)
1206
    instance.Release()
1207

    
1208

    
1209
def _NodeQueueDrainFile(node):
1210
  """Returns path to queue drain file for a node.
1211

1212
  """
1213
  return qa_utils.MakeNodePath(node, pathutils.JOB_QUEUE_DRAIN_FILE)
1214

    
1215

    
1216
def _AssertDrainFile(node, **kwargs):
1217
  """Checks for the queue drain file.
1218

1219
  """
1220
  AssertCommand(["test", "-f", _NodeQueueDrainFile(node)], node=node, **kwargs)
1221

    
1222

    
1223
def TestClusterMasterFailoverWithDrainedQueue():
1224
  """gnt-cluster master-failover with drained queue"""
1225
  master = qa_config.GetMasterNode()
1226
  failovermaster = qa_config.AcquireNode(exclude=master)
1227

    
1228
  # Ensure queue is not drained
1229
  for node in [master, failovermaster]:
1230
    _AssertDrainFile(node, fail=True)
1231

    
1232
  # Drain queue on failover master
1233
  AssertCommand(["touch", _NodeQueueDrainFile(failovermaster)],
1234
                node=failovermaster)
1235

    
1236
  cmd = ["gnt-cluster", "master-failover"]
1237
  try:
1238
    _AssertDrainFile(failovermaster)
1239
    AssertCommand(cmd, node=failovermaster)
1240
    _AssertDrainFile(master, fail=True)
1241
    _AssertDrainFile(failovermaster, fail=True)
1242

    
1243
    # Back to original master node
1244
    AssertCommand(cmd, node=master)
1245
  finally:
1246
    failovermaster.Release()
1247

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

    
1252

    
1253
def TestClusterCopyfile():
1254
  """gnt-cluster copyfile"""
1255
  master = qa_config.GetMasterNode()
1256

    
1257
  uniqueid = utils.NewUUID()
1258

    
1259
  # Create temporary file
1260
  f = tempfile.NamedTemporaryFile()
1261
  f.write(uniqueid)
1262
  f.flush()
1263
  f.seek(0)
1264

    
1265
  # Upload file to master node
1266
  testname = qa_utils.UploadFile(master.primary, f.name)
1267
  try:
1268
    # Copy file to all nodes
1269
    AssertCommand(["gnt-cluster", "copyfile", testname])
1270
    _CheckFileOnAllNodes(testname, uniqueid)
1271
  finally:
1272
    _RemoveFileFromAllNodes(testname)
1273

    
1274

    
1275
def TestClusterCommand():
1276
  """gnt-cluster command"""
1277
  uniqueid = utils.NewUUID()
1278
  rfile = "/tmp/gnt%s" % utils.NewUUID()
1279
  rcmd = utils.ShellQuoteArgs(["echo", "-n", uniqueid])
1280
  cmd = utils.ShellQuoteArgs(["gnt-cluster", "command",
1281
                              "%s >%s" % (rcmd, rfile)])
1282

    
1283
  try:
1284
    AssertCommand(cmd)
1285
    _CheckFileOnAllNodes(rfile, uniqueid)
1286
  finally:
1287
    _RemoveFileFromAllNodes(rfile)
1288

    
1289

    
1290
def TestClusterDestroy():
1291
  """gnt-cluster destroy"""
1292
  AssertCommand(["gnt-cluster", "destroy", "--yes-do-it"])
1293

    
1294

    
1295
def TestClusterRepairDiskSizes():
1296
  """gnt-cluster repair-disk-sizes"""
1297
  AssertCommand(["gnt-cluster", "repair-disk-sizes"])
1298

    
1299

    
1300
def TestSetExclStorCluster(newvalue):
1301
  """Set the exclusive_storage node parameter at the cluster level.
1302

1303
  @type newvalue: bool
1304
  @param newvalue: New value of exclusive_storage
1305
  @rtype: bool
1306
  @return: The old value of exclusive_storage
1307

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

    
1320

    
1321
def TestExclStorSharedPv(node):
1322
  """cluster-verify reports LVs that share the same PV with exclusive_storage.
1323

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