Statistics
| Branch: | Tag: | Revision:

root / qa / qa_cluster.py @ b0e8ed3f

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.GetDiskTemplatesOfStorageType(constants.ST_FILE),
523
      enabled_disk_templates)
524

    
525
  file_storage_dir = qa_config.get(dir_config_key, default_dir)
526
  invalid_file_storage_dir = "/boot/"
527

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

    
565

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

    
573

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

    
581

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

    
587
  _TestClusterModifyDiskTemplatesArguments(default_disk_template)
588
  _TestClusterModifyDiskTemplatesDrbdHelper(enabled_disk_templates)
589
  _TestClusterModifyDiskTemplatesVgName(enabled_disk_templates)
590

    
591
  _RestoreEnabledDiskTemplates()
592
  nodes = qa_config.AcquireManyNodes(2)
593

    
594
  instance_template = enabled_disk_templates[0]
595
  instance = qa_instance.CreateInstanceByDiskTemplate(nodes, instance_template)
596

    
597
  _TestClusterModifyUsedDiskTemplate(instance_template,
598
                                     enabled_disk_templates)
599

    
600
  qa_instance.TestInstanceRemove(instance)
601
  _RestoreEnabledDiskTemplates()
602

    
603

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

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

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

    
621
  AssertCommand(cmd, fail=False)
622

    
623

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

629
  """
630
  _RestoreEnabledDiskTemplates()
631

    
632
  if constants.DT_DRBD8 not in enabled_disk_templates:
633
    return
634
  if constants.DT_PLAIN not in enabled_disk_templates:
635
    return
636

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

    
673

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

679
  """
680
  _RestoreEnabledDiskTemplates()
681

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

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

    
695

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

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

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

    
711
  vgname = qa_config.get("vg-name", constants.DEFAULT_VG)
712

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

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

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

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

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

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

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

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

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

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

    
776
  _RestoreEnabledDiskTemplates()
777

    
778

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

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

    
799

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

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

    
835

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

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

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

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

    
856

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

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

    
918

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

923
  At most one of new_specs or diff_specs can be specified.
924

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

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

    
946

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

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

    
1001

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

    
1006

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

    
1011

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

    
1016

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

    
1021

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

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

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

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

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

    
1050
    utils.GenerateSelfSignedSslCert(fh.name, validity=validity)
1051

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

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

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

    
1072
    # Normal case
1073
    AssertCommand(["gnt-cluster", "renew-crypto", "--force",
1074
                   "--new-cluster-certificate", "--new-confd-hmac-key",
1075
                   "--new-rapi-certificate", "--new-cluster-domain-secret"])
1076

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

    
1083

    
1084
def TestClusterBurnin():
1085
  """Burnin"""
1086
  master = qa_config.GetMasterNode()
1087

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

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

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

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

    
1138
  finally:
1139
    for inst in instances:
1140
      inst.Release()
1141

    
1142

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

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

    
1159

    
1160
def TestUpgrade():
1161
  """Test gnt-cluster upgrade.
1162

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

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

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

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

    
1188
  AssertCommand(["gnt-cluster", "upgrade", "--to", other_version])
1189
  AssertCommand(["gnt-cluster", "verify"])
1190

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

    
1199
  AssertCommand(["gnt-cluster", "upgrade", "--to", this_version])
1200
  AssertCommand(["gnt-cluster", "verify"])
1201

    
1202
  for instance in live_instances:
1203
    qa_instance.TestInstanceRemove(instance)
1204
    instance.Release()
1205

    
1206

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

1210
  """
1211
  return qa_utils.MakeNodePath(node, pathutils.JOB_QUEUE_DRAIN_FILE)
1212

    
1213

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

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

    
1220

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

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

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

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

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

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

    
1250

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

    
1255
  uniqueid = utils.NewUUID()
1256

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

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

    
1272

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

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

    
1287

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

    
1292

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

    
1297

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

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

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

    
1318

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

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