Merge branch 'stable-2.9' into stable-2.10
[ganeti-local] / qa / qa_cluster.py
1 #
2 #
3
4 # Copyright (C) 2007, 2010, 2011, 2012, 2013 Google Inc.
5 #
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 # General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 # 02110-1301, USA.
20
21
22 """Cluster related QA tests.
23
24 """
25
26 import re
27 import tempfile
28 import os.path
29
30 from ganeti import _constants
31 from ganeti import constants
32 from ganeti import compat
33 from ganeti import utils
34 from ganeti import pathutils
35
36 import qa_config
37 import qa_daemon
38 import qa_error
39 import qa_instance
40 import qa_logging
41 import qa_utils
42
43 from qa_utils import AssertEqual, AssertCommand, GetCommandOutput
44
45
46 # Prefix for LVM volumes created by QA code during tests
47 _QA_LV_PREFIX = "qa-"
48
49 #: cluster verify command
50 _CLUSTER_VERIFY = ["gnt-cluster", "verify"]
51
52
53 def _RemoveFileFromAllNodes(filename):
54   """Removes a file from all nodes.
55
56   """
57   for node in qa_config.get("nodes"):
58     AssertCommand(["rm", "-f", filename], node=node)
59
60
61 def _CheckFileOnAllNodes(filename, content):
62   """Verifies the content of the given file on all nodes.
63
64   """
65   cmd = utils.ShellQuoteArgs(["cat", filename])
66   for node in qa_config.get("nodes"):
67     AssertEqual(qa_utils.GetCommandOutput(node.primary, cmd), content)
68
69
70 def _GetClusterField(field_path):
71   """Get the value of a cluster field.
72
73   @type field_path: list of strings
74   @param field_path: Names of the groups/fields to navigate to get the desired
75       value, e.g. C{["Default node parameters", "oob_program"]}
76   @return: The effective value of the field (the actual type depends on the
77       chosen field)
78
79   """
80   assert isinstance(field_path, list)
81   assert field_path
82   ret = qa_utils.GetObjectInfo(["gnt-cluster", "info"])
83   for key in field_path:
84     ret = ret[key]
85   return ret
86
87
88 # Cluster-verify errors (date, "ERROR", then error code)
89 _CVERROR_RE = re.compile(r"^[\w\s:]+\s+- (ERROR|WARNING):([A-Z0-9_-]+):")
90
91
92 def _GetCVErrorCodes(cvout):
93   errs = set()
94   warns = set()
95   for l in cvout.splitlines():
96     m = _CVERROR_RE.match(l)
97     if m:
98       etype = m.group(1)
99       ecode = m.group(2)
100       if etype == "ERROR":
101         errs.add(ecode)
102       elif etype == "WARNING":
103         warns.add(ecode)
104   return (errs, warns)
105
106
107 def _CheckVerifyErrors(actual, expected, etype):
108   exp_codes = compat.UniqueFrozenset(e for (_, e, _) in expected)
109   if not actual.issuperset(exp_codes):
110     missing = exp_codes.difference(actual)
111     raise qa_error.Error("Cluster-verify didn't return these expected"
112                          " %ss: %s" % (etype, utils.CommaJoin(missing)))
113
114
115 def AssertClusterVerify(fail=False, errors=None, warnings=None):
116   """Run cluster-verify and check the result
117
118   @type fail: bool
119   @param fail: if cluster-verify is expected to fail instead of succeeding
120   @type errors: list of tuples
121   @param errors: List of CV_XXX errors that are expected; if specified, all the
122       errors listed must appear in cluster-verify output. A non-empty value
123       implies C{fail=True}.
124   @type warnings: list of tuples
125   @param warnings: Same as C{errors} but for warnings.
126
127   """
128   cvcmd = "gnt-cluster verify"
129   mnode = qa_config.GetMasterNode()
130   if errors or warnings:
131     cvout = GetCommandOutput(mnode.primary, cvcmd + " --error-codes",
132                              fail=(fail or errors))
133     (act_errs, act_warns) = _GetCVErrorCodes(cvout)
134     if errors:
135       _CheckVerifyErrors(act_errs, errors, "error")
136     if warnings:
137       _CheckVerifyErrors(act_warns, warnings, "warning")
138   else:
139     AssertCommand(cvcmd, fail=fail, node=mnode)
140
141
142 # data for testing failures due to bad keys/values for disk parameters
143 _FAIL_PARAMS = ["nonexistent:resync-rate=1",
144                 "drbd:nonexistent=1",
145                 "drbd:resync-rate=invalid",
146                 ]
147
148
149 def TestClusterInitDisk():
150   """gnt-cluster init -D"""
151   name = qa_config.get("name")
152   for param in _FAIL_PARAMS:
153     AssertCommand(["gnt-cluster", "init", "-D", param, name], fail=True)
154
155
156 def TestClusterInit(rapi_user, rapi_secret):
157   """gnt-cluster init"""
158   master = qa_config.GetMasterNode()
159
160   rapi_users_path = qa_utils.MakeNodePath(master, pathutils.RAPI_USERS_FILE)
161   rapi_dir = os.path.dirname(rapi_users_path)
162
163   # First create the RAPI credentials
164   fh = tempfile.NamedTemporaryFile()
165   try:
166     fh.write("%s %s write\n" % (rapi_user, rapi_secret))
167     fh.flush()
168
169     tmpru = qa_utils.UploadFile(master.primary, fh.name)
170     try:
171       AssertCommand(["mkdir", "-p", rapi_dir])
172       AssertCommand(["mv", tmpru, rapi_users_path])
173     finally:
174       AssertCommand(["rm", "-f", tmpru])
175   finally:
176     fh.close()
177
178   # Initialize cluster
179   enabled_disk_templates = qa_config.GetEnabledDiskTemplates()
180   cmd = [
181     "gnt-cluster", "init",
182     "--primary-ip-version=%d" % qa_config.get("primary_ip_version", 4),
183     "--enabled-hypervisors=%s" % ",".join(qa_config.GetEnabledHypervisors()),
184     "--enabled-disk-templates=%s" %
185       ",".join(enabled_disk_templates),
186     ]
187   if constants.DT_FILE in enabled_disk_templates:
188     cmd.append(
189         "--file-storage-dir=%s" %
190         qa_config.get("default-file-storage-dir",
191                       pathutils.DEFAULT_FILE_STORAGE_DIR))
192
193   for spec_type in ("mem-size", "disk-size", "disk-count", "cpu-count",
194                     "nic-count"):
195     for spec_val in ("min", "max", "std"):
196       spec = qa_config.get("ispec_%s_%s" %
197                            (spec_type.replace("-", "_"), spec_val), None)
198       if spec is not None:
199         cmd.append("--specs-%s=%s=%d" % (spec_type, spec_val, spec))
200
201   if master.secondary:
202     cmd.append("--secondary-ip=%s" % master.secondary)
203
204   if utils.IsLvmEnabled(qa_config.GetEnabledDiskTemplates()):
205     vgname = qa_config.get("vg-name", constants.DEFAULT_VG)
206     if vgname:
207       cmd.append("--vg-name=%s" % vgname)
208     else:
209       raise qa_error.Error("Please specify a volume group if you enable"
210                            " lvm-based disk templates in the QA.")
211
212   master_netdev = qa_config.get("master-netdev", None)
213   if master_netdev:
214     cmd.append("--master-netdev=%s" % master_netdev)
215
216   nicparams = qa_config.get("default-nicparams", None)
217   if nicparams:
218     cmd.append("--nic-parameters=%s" %
219                ",".join(utils.FormatKeyValue(nicparams)))
220
221   # Cluster value of the exclusive-storage node parameter
222   e_s = qa_config.get("exclusive-storage")
223   if e_s is not None:
224     cmd.extend(["--node-parameters", "exclusive_storage=%s" % e_s])
225   else:
226     e_s = False
227   qa_config.SetExclusiveStorage(e_s)
228
229   extra_args = qa_config.get("cluster-init-args")
230
231   if extra_args:
232     # This option was removed in 2.10, but in order to not break QA of older
233     # branches we remove it from the extra_args if it is in there.
234     opt_drbd_storage = "--no-drbd-storage"
235     if opt_drbd_storage in extra_args:
236       extra_args.remove(opt_drbd_storage)
237     cmd.extend(extra_args)
238
239   cmd.append(qa_config.get("name"))
240
241   AssertCommand(cmd)
242
243   cmd = ["gnt-cluster", "modify"]
244
245   # hypervisor parameter modifications
246   hvp = qa_config.get("hypervisor-parameters", {})
247   for k, v in hvp.items():
248     cmd.extend(["-H", "%s:%s" % (k, v)])
249   # backend parameter modifications
250   bep = qa_config.get("backend-parameters", "")
251   if bep:
252     cmd.extend(["-B", bep])
253
254   if len(cmd) > 2:
255     AssertCommand(cmd)
256
257   # OS parameters
258   osp = qa_config.get("os-parameters", {})
259   for k, v in osp.items():
260     AssertCommand(["gnt-os", "modify", "-O", v, k])
261
262   # OS hypervisor parameters
263   os_hvp = qa_config.get("os-hvp", {})
264   for os_name in os_hvp:
265     for hv, hvp in os_hvp[os_name].items():
266       AssertCommand(["gnt-os", "modify", "-H", "%s:%s" % (hv, hvp), os_name])
267
268
269 def TestClusterRename():
270   """gnt-cluster rename"""
271   cmd = ["gnt-cluster", "rename", "-f"]
272
273   original_name = qa_config.get("name")
274   rename_target = qa_config.get("rename", None)
275   if rename_target is None:
276     print qa_logging.FormatError('"rename" entry is missing')
277     return
278
279   for data in [
280     cmd + [rename_target],
281     _CLUSTER_VERIFY,
282     cmd + [original_name],
283     _CLUSTER_VERIFY,
284     ]:
285     AssertCommand(data)
286
287
288 def TestClusterOob():
289   """out-of-band framework"""
290   oob_path_exists = "/tmp/ganeti-qa-oob-does-exist-%s" % utils.NewUUID()
291
292   AssertCommand(_CLUSTER_VERIFY)
293   AssertCommand(["gnt-cluster", "modify", "--node-parameters",
294                  "oob_program=/tmp/ganeti-qa-oob-does-not-exist-%s" %
295                  utils.NewUUID()])
296
297   AssertCommand(_CLUSTER_VERIFY, fail=True)
298
299   AssertCommand(["touch", oob_path_exists])
300   AssertCommand(["chmod", "0400", oob_path_exists])
301   AssertCommand(["gnt-cluster", "copyfile", oob_path_exists])
302
303   try:
304     AssertCommand(["gnt-cluster", "modify", "--node-parameters",
305                    "oob_program=%s" % oob_path_exists])
306
307     AssertCommand(_CLUSTER_VERIFY, fail=True)
308
309     AssertCommand(["chmod", "0500", oob_path_exists])
310     AssertCommand(["gnt-cluster", "copyfile", oob_path_exists])
311
312     AssertCommand(_CLUSTER_VERIFY)
313   finally:
314     AssertCommand(["gnt-cluster", "command", "rm", oob_path_exists])
315
316   AssertCommand(["gnt-cluster", "modify", "--node-parameters",
317                  "oob_program="])
318
319
320 def TestClusterEpo():
321   """gnt-cluster epo"""
322   master = qa_config.GetMasterNode()
323
324   # Assert that OOB is unavailable for all nodes
325   result_output = GetCommandOutput(master.primary,
326                                    "gnt-node list --verbose --no-headers -o"
327                                    " powered")
328   AssertEqual(compat.all(powered == "(unavail)"
329                          for powered in result_output.splitlines()), True)
330
331   # Conflicting
332   AssertCommand(["gnt-cluster", "epo", "--groups", "--all"], fail=True)
333   # --all doesn't expect arguments
334   AssertCommand(["gnt-cluster", "epo", "--all", "some_arg"], fail=True)
335
336   # Unless --all is given master is not allowed to be in the list
337   AssertCommand(["gnt-cluster", "epo", "-f", master.primary], fail=True)
338
339   # This shouldn't fail
340   AssertCommand(["gnt-cluster", "epo", "-f", "--all"])
341
342   # All instances should have been stopped now
343   result_output = GetCommandOutput(master.primary,
344                                    "gnt-instance list --no-headers -o status")
345   # ERROR_down because the instance is stopped but not recorded as such
346   AssertEqual(compat.all(status == "ERROR_down"
347                          for status in result_output.splitlines()), True)
348
349   # Now start everything again
350   AssertCommand(["gnt-cluster", "epo", "--on", "-f", "--all"])
351
352   # All instances should have been started now
353   result_output = GetCommandOutput(master.primary,
354                                    "gnt-instance list --no-headers -o status")
355   AssertEqual(compat.all(status == "running"
356                          for status in result_output.splitlines()), True)
357
358
359 def TestClusterVerify():
360   """gnt-cluster verify"""
361   AssertCommand(_CLUSTER_VERIFY)
362   AssertCommand(["gnt-cluster", "verify-disks"])
363
364
365 def TestClusterVerifyDisksBrokenDRBD(instance, inst_nodes):
366   """gnt-cluster verify-disks with broken DRBD"""
367   qa_daemon.TestPauseWatcher()
368
369   try:
370     info = qa_instance.GetInstanceInfo(instance.name)
371     snode = inst_nodes[1]
372     for idx, minor in enumerate(info["drbd-minors"][snode.primary]):
373       if idx % 2 == 0:
374         break_drbd_cmd = \
375           "(drbdsetup %d down >/dev/null 2>&1;" \
376           " drbdsetup down resource%d >/dev/null 2>&1) || /bin/true" % \
377           (minor, minor)
378       else:
379         break_drbd_cmd = \
380           "(drbdsetup %d detach >/dev/null 2>&1;" \
381           " drbdsetup detach %d >/dev/null 2>&1) || /bin/true" % \
382           (minor, minor)
383       AssertCommand(break_drbd_cmd, node=snode)
384
385     verify_output = GetCommandOutput(qa_config.GetMasterNode().primary,
386                                      "gnt-cluster verify-disks")
387     activation_msg = "Activating disks for instance '%s'" % instance.name
388     if activation_msg not in verify_output:
389       raise qa_error.Error("gnt-cluster verify-disks did not activate broken"
390                            " DRBD disks:\n%s" % verify_output)
391
392     verify_output = GetCommandOutput(qa_config.GetMasterNode().primary,
393                                      "gnt-cluster verify-disks")
394     if activation_msg in verify_output:
395       raise qa_error.Error("gnt-cluster verify-disks wants to activate broken"
396                            " DRBD disks on second attempt:\n%s" % verify_output)
397
398     AssertCommand(_CLUSTER_VERIFY)
399   finally:
400     qa_daemon.TestResumeWatcher()
401
402
403 def TestJobqueue():
404   """gnt-debug test-jobqueue"""
405   AssertCommand(["gnt-debug", "test-jobqueue"])
406
407
408 def TestDelay(node):
409   """gnt-debug delay"""
410   AssertCommand(["gnt-debug", "delay", "1"])
411   AssertCommand(["gnt-debug", "delay", "--no-master", "1"])
412   AssertCommand(["gnt-debug", "delay", "--no-master",
413                  "-n", node.primary, "1"])
414
415
416 def TestClusterReservedLvs():
417   """gnt-cluster reserved lvs"""
418   # if no lvm-based templates are supported, skip the test
419   if not qa_config.IsStorageTypeSupported(constants.ST_LVM_VG):
420     return
421   vgname = qa_config.get("vg-name", constants.DEFAULT_VG)
422   lvname = _QA_LV_PREFIX + "test"
423   lvfullname = "/".join([vgname, lvname])
424   for fail, cmd in [
425     (False, _CLUSTER_VERIFY),
426     (False, ["gnt-cluster", "modify", "--reserved-lvs", ""]),
427     (False, ["lvcreate", "-L1G", "-n", lvname, vgname]),
428     (True, _CLUSTER_VERIFY),
429     (False, ["gnt-cluster", "modify", "--reserved-lvs",
430              "%s,.*/other-test" % lvfullname]),
431     (False, _CLUSTER_VERIFY),
432     (False, ["gnt-cluster", "modify", "--reserved-lvs",
433              ".*/%s.*" % _QA_LV_PREFIX]),
434     (False, _CLUSTER_VERIFY),
435     (False, ["gnt-cluster", "modify", "--reserved-lvs", ""]),
436     (True, _CLUSTER_VERIFY),
437     (False, ["lvremove", "-f", lvfullname]),
438     (False, _CLUSTER_VERIFY),
439     ]:
440     AssertCommand(cmd, fail=fail)
441
442
443 def TestClusterModifyEmpty():
444   """gnt-cluster modify"""
445   AssertCommand(["gnt-cluster", "modify"], fail=True)
446
447
448 def TestClusterModifyDisk():
449   """gnt-cluster modify -D"""
450   for param in _FAIL_PARAMS:
451     AssertCommand(["gnt-cluster", "modify", "-D", param], fail=True)
452
453
454 def _GetOtherEnabledDiskTemplate(undesired_disk_templates,
455                                  enabled_disk_templates):
456   """Returns one template that is not in the undesired set.
457
458   @type undesired_disk_templates: list of string
459   @param undesired_disk_templates: a list of disk templates that we want to
460       exclude when drawing one disk template from the list of enabled
461       disk templates
462   @type enabled_disk_templates: list of string
463   @param enabled_disk_templates: list of enabled disk templates (in QA)
464
465   """
466   desired_templates = list(set(enabled_disk_templates)
467                                 - set(undesired_disk_templates))
468   if desired_templates:
469     template = desired_templates[0]
470   else:
471     # If no desired disk template is available for QA, choose 'diskless' and
472     # hope for the best.
473     template = constants.ST_DISKLESS
474
475   return template
476
477
478 def TestClusterModifyFileBasedStorageDir(
479     file_disk_template, dir_config_key, default_dir, option_name):
480   """Tests gnt-cluster modify wrt to file-based directory options.
481
482   @type file_disk_template: string
483   @param file_disk_template: file-based disk template
484   @type dir_config_key: string
485   @param dir_config_key: key for the QA config to retrieve the default
486      directory value
487   @type default_dir: string
488   @param default_dir: default directory, if the QA config does not specify
489      it
490   @type option_name: string
491   @param option_name: name of the option of 'gnt-cluster modify' to
492      change the directory
493
494   """
495   enabled_disk_templates = qa_config.GetEnabledDiskTemplates()
496   assert file_disk_template in [constants.DT_FILE, constants.DT_SHARED_FILE]
497   if not qa_config.IsTemplateSupported(file_disk_template):
498     return
499
500   # Get some non-file-based disk template to disable file storage
501   other_disk_template = _GetOtherEnabledDiskTemplate(
502       utils.storage.GetDiskTemplatesOfStorageType(constants.ST_FILE),
503       enabled_disk_templates)
504
505   file_storage_dir = qa_config.get(dir_config_key, default_dir)
506   invalid_file_storage_dir = "/boot/"
507
508   for fail, cmd in [
509     (False, ["gnt-cluster", "modify",
510              "--enabled-disk-templates=%s" % file_disk_template,
511              "--ipolicy-disk-templates=%s" % file_disk_template]),
512     (False, ["gnt-cluster", "modify",
513              "--%s=%s" % (option_name, file_storage_dir)]),
514     (False, ["gnt-cluster", "modify",
515              "--%s=%s" % (option_name, invalid_file_storage_dir)]),
516     # file storage dir is set to an inacceptable path, thus verify
517     # should fail
518     (True, ["gnt-cluster", "verify"]),
519     # unsetting the storage dir while file storage is enabled
520     # should fail
521     (True, ["gnt-cluster", "modify",
522             "--%s=" % option_name]),
523     (False, ["gnt-cluster", "modify",
524              "--%s=%s" % (option_name, file_storage_dir)]),
525     (False, ["gnt-cluster", "modify",
526              "--enabled-disk-templates=%s" % other_disk_template,
527              "--ipolicy-disk-templates=%s" % other_disk_template]),
528     (False, ["gnt-cluster", "modify",
529              "--%s=%s" % (option_name, invalid_file_storage_dir)]),
530     # file storage is set to an inacceptable path, but file storage
531     # is disabled, thus verify should not fail
532     (False, ["gnt-cluster", "verify"]),
533     # unsetting the file storage dir while file storage is not enabled
534     # should be fine
535     (False, ["gnt-cluster", "modify",
536              "--%s=" % option_name]),
537     # resetting everything to sane values
538     (False, ["gnt-cluster", "modify",
539              "--%s=%s" % (option_name, file_storage_dir),
540              "--enabled-disk-templates=%s" % ",".join(enabled_disk_templates),
541              "--ipolicy-disk-templates=%s" % ",".join(enabled_disk_templates)])
542     ]:
543     AssertCommand(cmd, fail=fail)
544
545
546 def TestClusterModifyFileStorageDir():
547   """gnt-cluster modify --file-storage-dir=..."""
548   TestClusterModifyFileBasedStorageDir(
549       constants.DT_FILE, "default-file-storage-dir",
550       pathutils.DEFAULT_FILE_STORAGE_DIR,
551       "file-storage-dir")
552
553
554 def TestClusterModifySharedFileStorageDir():
555   """gnt-cluster modify --shared-file-storage-dir=..."""
556   TestClusterModifyFileBasedStorageDir(
557       constants.DT_SHARED_FILE, "default-shared-file-storage-dir",
558       pathutils.DEFAULT_SHARED_FILE_STORAGE_DIR,
559       "shared-file-storage-dir")
560
561
562 def TestClusterModifyDiskTemplates():
563   """gnt-cluster modify --enabled-disk-templates=..."""
564   enabled_disk_templates = qa_config.GetEnabledDiskTemplates()
565   default_disk_template = qa_config.GetDefaultDiskTemplate()
566
567   _TestClusterModifyDiskTemplatesArguments(default_disk_template)
568   _TestClusterModifyDiskTemplatesDrbdHelper(enabled_disk_templates)
569   _TestClusterModifyDiskTemplatesVgName(enabled_disk_templates)
570
571   _RestoreEnabledDiskTemplates()
572   nodes = qa_config.AcquireManyNodes(2)
573
574   instance_template = enabled_disk_templates[0]
575   instance = qa_instance.CreateInstanceByDiskTemplate(nodes, instance_template)
576
577   _TestClusterModifyUsedDiskTemplate(instance_template,
578                                      enabled_disk_templates)
579
580   qa_instance.TestInstanceRemove(instance)
581   _RestoreEnabledDiskTemplates()
582
583
584 def _RestoreEnabledDiskTemplates():
585   """Sets the list of enabled disk templates back to the list of enabled disk
586      templates from the QA configuration. This can be used to make sure that
587      the tests that modify the list of disk templates do not interfere with
588      other tests.
589
590   """
591   enabled_disk_templates = qa_config.GetEnabledDiskTemplates()
592   cmd = ["gnt-cluster", "modify",
593          "--enabled-disk-templates=%s" % ",".join(enabled_disk_templates),
594          "--ipolicy-disk-templates=%s" % ",".join(enabled_disk_templates),
595          ]
596
597   if utils.IsLvmEnabled(qa_config.GetEnabledDiskTemplates()):
598     vgname = qa_config.get("vg-name", constants.DEFAULT_VG)
599     cmd.append("--vg-name=%s" % vgname)
600
601   AssertCommand(cmd, fail=False)
602
603
604 def _TestClusterModifyDiskTemplatesDrbdHelper(enabled_disk_templates):
605   """Tests argument handling of 'gnt-cluster modify' with respect to
606      the parameter '--drbd-usermode-helper'. This test is independent
607      of instances.
608
609   """
610   _RestoreEnabledDiskTemplates()
611
612   if constants.DT_DRBD8 not in enabled_disk_templates:
613     return
614   if constants.DT_PLAIN not in enabled_disk_templates:
615     return
616
617   drbd_usermode_helper = qa_config.get("drbd-usermode-helper", "/bin/true")
618   bogus_usermode_helper = "/tmp/pinkbunny"
619   for command, fail in [
620     (["gnt-cluster", "modify",
621       "--enabled-disk-templates=%s" % constants.DT_DRBD8,
622       "--ipolicy-disk-templates=%s" % constants.DT_DRBD8], False),
623     (["gnt-cluster", "modify",
624       "--drbd-usermode-helper=%s" % drbd_usermode_helper], False),
625     (["gnt-cluster", "modify",
626       "--drbd-usermode-helper=%s" % bogus_usermode_helper], True),
627     # unsetting helper when DRBD is enabled should not work
628     (["gnt-cluster", "modify",
629       "--drbd-usermode-helper="], True),
630     (["gnt-cluster", "modify",
631       "--enabled-disk-templates=%s" % constants.DT_PLAIN,
632       "--ipolicy-disk-templates=%s" % constants.DT_PLAIN], False),
633     (["gnt-cluster", "modify",
634       "--drbd-usermode-helper="], False),
635     (["gnt-cluster", "modify",
636       "--drbd-usermode-helper=%s" % drbd_usermode_helper], False),
637     (["gnt-cluster", "modify",
638       "--drbd-usermode-helper=%s" % drbd_usermode_helper,
639       "--enabled-disk-templates=%s" % constants.DT_DRBD8,
640       "--ipolicy-disk-templates=%s" % constants.DT_DRBD8], False),
641     (["gnt-cluster", "modify",
642       "--drbd-usermode-helper=",
643       "--enabled-disk-templates=%s" % constants.DT_PLAIN,
644       "--ipolicy-disk-templates=%s" % constants.DT_PLAIN], False),
645     (["gnt-cluster", "modify",
646       "--drbd-usermode-helper=%s" % drbd_usermode_helper,
647       "--enabled-disk-templates=%s" % constants.DT_DRBD8,
648       "--ipolicy-disk-templates=%s" % constants.DT_DRBD8], False),
649     ]:
650     AssertCommand(command, fail=fail)
651   _RestoreEnabledDiskTemplates()
652
653
654 def _TestClusterModifyDiskTemplatesArguments(default_disk_template):
655   """Tests argument handling of 'gnt-cluster modify' with respect to
656      the parameter '--enabled-disk-templates'. This test is independent
657      of instances.
658
659   """
660   _RestoreEnabledDiskTemplates()
661
662   # bogus templates
663   AssertCommand(["gnt-cluster", "modify",
664                  "--enabled-disk-templates=pinkbunny"],
665                 fail=True)
666
667   # duplicate entries do no harm
668   AssertCommand(
669     ["gnt-cluster", "modify",
670      "--enabled-disk-templates=%s,%s" %
671       (default_disk_template, default_disk_template),
672      "--ipolicy-disk-templates=%s" % default_disk_template],
673     fail=False)
674
675
676 def _TestClusterModifyDiskTemplatesVgName(enabled_disk_templates):
677   """Tests argument handling of 'gnt-cluster modify' with respect to
678      the parameter '--enabled-disk-templates' and '--vg-name'. This test is
679      independent of instances.
680
681   """
682   if not utils.IsLvmEnabled(enabled_disk_templates):
683     # These tests only make sense if lvm is enabled for QA
684     return
685
686   # determine an LVM and a non-LVM disk template for the tests
687   non_lvm_template = _GetOtherEnabledDiskTemplate(constants.DTS_LVM,
688                                                   enabled_disk_templates)
689   lvm_template = list(set(enabled_disk_templates) & constants.DTS_LVM)[0]
690
691   vgname = qa_config.get("vg-name", constants.DEFAULT_VG)
692
693   # Clean start: unset volume group name, disable lvm storage
694   AssertCommand(
695     ["gnt-cluster", "modify",
696      "--enabled-disk-templates=%s" % non_lvm_template,
697      "--ipolicy-disk-templates=%s" % non_lvm_template,
698      "--vg-name="],
699     fail=False)
700
701   # Try to enable lvm, when no volume group is given
702   AssertCommand(
703     ["gnt-cluster", "modify",
704      "--enabled-disk-templates=%s" % lvm_template,
705      "--ipolicy-disk-templates=%s" % lvm_template],
706     fail=True)
707
708   # Set volume group, with lvm still disabled: just a warning
709   AssertCommand(["gnt-cluster", "modify", "--vg-name=%s" % vgname], fail=False)
710
711   # Try unsetting vg name and enabling lvm at the same time
712   AssertCommand(
713     ["gnt-cluster", "modify",
714      "--enabled-disk-templates=%s" % lvm_template,
715      "--ipolicy-disk-templates=%s" % lvm_template,
716      "--vg-name="],
717     fail=True)
718
719   # Enable lvm with vg name present
720   AssertCommand(
721     ["gnt-cluster", "modify",
722      "--enabled-disk-templates=%s" % lvm_template,
723      "--ipolicy-disk-templates=%s" % lvm_template],
724     fail=False)
725
726   # Try unsetting vg name with lvm still enabled
727   AssertCommand(["gnt-cluster", "modify", "--vg-name="], fail=True)
728
729   # Disable lvm with vg name still set
730   AssertCommand(
731     ["gnt-cluster", "modify",
732      "--enabled-disk-templates=%s" % non_lvm_template,
733      "--ipolicy-disk-templates=%s" % non_lvm_template,
734      ],
735     fail=False)
736
737   # Try unsetting vg name with lvm disabled
738   AssertCommand(["gnt-cluster", "modify", "--vg-name="], fail=False)
739
740   # Set vg name and enable lvm at the same time
741   AssertCommand(
742     ["gnt-cluster", "modify",
743      "--enabled-disk-templates=%s" % lvm_template,
744      "--ipolicy-disk-templates=%s" % lvm_template,
745      "--vg-name=%s" % vgname],
746     fail=False)
747
748   # Unset vg name and disable lvm at the same time
749   AssertCommand(
750     ["gnt-cluster", "modify",
751      "--enabled-disk-templates=%s" % non_lvm_template,
752      "--ipolicy-disk-templates=%s" % non_lvm_template,
753      "--vg-name="],
754     fail=False)
755
756   _RestoreEnabledDiskTemplates()
757
758
759 def _TestClusterModifyUsedDiskTemplate(instance_template,
760                                        enabled_disk_templates):
761   """Tests that disk templates that are currently in use by instances cannot
762      be disabled on the cluster.
763
764   """
765   # If the list of enabled disk templates contains only one template
766   # we need to add some other templates, because the list of enabled disk
767   # templates can only be set to a non-empty list.
768   new_disk_templates = list(set(enabled_disk_templates)
769                               - set([instance_template]))
770   if not new_disk_templates:
771     new_disk_templates = list(set([constants.DT_DISKLESS, constants.DT_BLOCK])
772                                 - set([instance_template]))
773   AssertCommand(
774     ["gnt-cluster", "modify",
775      "--enabled-disk-templates=%s" % ",".join(new_disk_templates),
776      "--ipolicy-disk-templates=%s" % ",".join(new_disk_templates)],
777     fail=True)
778
779
780 def TestClusterModifyBe():
781   """gnt-cluster modify -B"""
782   for fail, cmd in [
783     # max/min mem
784     (False, ["gnt-cluster", "modify", "-B", "maxmem=256"]),
785     (False, ["sh", "-c", "gnt-cluster info|grep '^ *maxmem: 256$'"]),
786     (False, ["gnt-cluster", "modify", "-B", "minmem=256"]),
787     (False, ["sh", "-c", "gnt-cluster info|grep '^ *minmem: 256$'"]),
788     (True, ["gnt-cluster", "modify", "-B", "maxmem=a"]),
789     (False, ["sh", "-c", "gnt-cluster info|grep '^ *maxmem: 256$'"]),
790     (True, ["gnt-cluster", "modify", "-B", "minmem=a"]),
791     (False, ["sh", "-c", "gnt-cluster info|grep '^ *minmem: 256$'"]),
792     (False, ["gnt-cluster", "modify", "-B", "maxmem=128,minmem=128"]),
793     (False, ["sh", "-c", "gnt-cluster info|grep '^ *maxmem: 128$'"]),
794     (False, ["sh", "-c", "gnt-cluster info|grep '^ *minmem: 128$'"]),
795     # vcpus
796     (False, ["gnt-cluster", "modify", "-B", "vcpus=4"]),
797     (False, ["sh", "-c", "gnt-cluster info|grep '^ *vcpus: 4$'"]),
798     (True, ["gnt-cluster", "modify", "-B", "vcpus=a"]),
799     (False, ["gnt-cluster", "modify", "-B", "vcpus=1"]),
800     (False, ["sh", "-c", "gnt-cluster info|grep '^ *vcpus: 1$'"]),
801     # auto_balance
802     (False, ["gnt-cluster", "modify", "-B", "auto_balance=False"]),
803     (False, ["sh", "-c", "gnt-cluster info|grep '^ *auto_balance: False$'"]),
804     (True, ["gnt-cluster", "modify", "-B", "auto_balance=1"]),
805     (False, ["gnt-cluster", "modify", "-B", "auto_balance=True"]),
806     (False, ["sh", "-c", "gnt-cluster info|grep '^ *auto_balance: True$'"]),
807     ]:
808     AssertCommand(cmd, fail=fail)
809
810   # redo the original-requested BE parameters, if any
811   bep = qa_config.get("backend-parameters", "")
812   if bep:
813     AssertCommand(["gnt-cluster", "modify", "-B", bep])
814
815
816 def _GetClusterIPolicy():
817   """Return the run-time values of the cluster-level instance policy.
818
819   @rtype: tuple
820   @return: (policy, specs), where:
821       - policy is a dictionary of the policy values, instance specs excluded
822       - specs is a dictionary containing only the specs, using the internal
823         format (see L{constants.IPOLICY_DEFAULTS} for an example)
824
825   """
826   info = qa_utils.GetObjectInfo(["gnt-cluster", "info"])
827   policy = info["Instance policy - limits for instances"]
828   (ret_policy, ret_specs) = qa_utils.ParseIPolicy(policy)
829
830   # Sanity checks
831   assert "minmax" in ret_specs and "std" in ret_specs
832   assert len(ret_specs["minmax"]) > 0
833   assert len(ret_policy) > 0
834   return (ret_policy, ret_specs)
835
836
837 def TestClusterModifyIPolicy():
838   """gnt-cluster modify --ipolicy-*"""
839   basecmd = ["gnt-cluster", "modify"]
840   (old_policy, old_specs) = _GetClusterIPolicy()
841   for par in ["vcpu-ratio", "spindle-ratio"]:
842     curr_val = float(old_policy[par])
843     test_values = [
844       (True, 1.0),
845       (True, 1.5),
846       (True, 2),
847       (False, "a"),
848       # Restore the old value
849       (True, curr_val),
850       ]
851     for (good, val) in test_values:
852       cmd = basecmd + ["--ipolicy-%s=%s" % (par, val)]
853       AssertCommand(cmd, fail=not good)
854       if good:
855         curr_val = val
856       # Check the affected parameter
857       (eff_policy, eff_specs) = _GetClusterIPolicy()
858       AssertEqual(float(eff_policy[par]), curr_val)
859       # Check everything else
860       AssertEqual(eff_specs, old_specs)
861       for p in eff_policy.keys():
862         if p == par:
863           continue
864         AssertEqual(eff_policy[p], old_policy[p])
865
866   # Allowing disk templates via ipolicy requires them to be
867   # enabled on the cluster.
868   if not (qa_config.IsTemplateSupported(constants.DT_PLAIN)
869           and qa_config.IsTemplateSupported(constants.DT_DRBD8)):
870     return
871   # Disk templates are treated slightly differently
872   par = "disk-templates"
873   disp_str = "allowed disk templates"
874   curr_val = old_policy[disp_str]
875   test_values = [
876     (True, constants.DT_PLAIN),
877     (True, "%s,%s" % (constants.DT_PLAIN, constants.DT_DRBD8)),
878     (False, "thisisnotadisktemplate"),
879     (False, ""),
880     # Restore the old value
881     (True, curr_val.replace(" ", "")),
882     ]
883   for (good, val) in test_values:
884     cmd = basecmd + ["--ipolicy-%s=%s" % (par, val)]
885     AssertCommand(cmd, fail=not good)
886     if good:
887       curr_val = val
888     # Check the affected parameter
889     (eff_policy, eff_specs) = _GetClusterIPolicy()
890     AssertEqual(eff_policy[disp_str].replace(" ", ""), curr_val)
891     # Check everything else
892     AssertEqual(eff_specs, old_specs)
893     for p in eff_policy.keys():
894       if p == disp_str:
895         continue
896       AssertEqual(eff_policy[p], old_policy[p])
897
898
899 def TestClusterSetISpecs(new_specs=None, diff_specs=None, fail=False,
900                          old_values=None):
901   """Change instance specs.
902
903   At most one of new_specs or diff_specs can be specified.
904
905   @type new_specs: dict
906   @param new_specs: new complete specs, in the same format returned by
907       L{_GetClusterIPolicy}
908   @type diff_specs: dict
909   @param diff_specs: partial specs, it can be an incomplete specifications, but
910       if min/max specs are specified, their number must match the number of the
911       existing specs
912   @type fail: bool
913   @param fail: if the change is expected to fail
914   @type old_values: tuple
915   @param old_values: (old_policy, old_specs), as returned by
916       L{_GetClusterIPolicy}
917   @return: same as L{_GetClusterIPolicy}
918
919   """
920   build_cmd = lambda opts: ["gnt-cluster", "modify"] + opts
921   return qa_utils.TestSetISpecs(
922     new_specs=new_specs, diff_specs=diff_specs,
923     get_policy_fn=_GetClusterIPolicy, build_cmd_fn=build_cmd,
924     fail=fail, old_values=old_values)
925
926
927 def TestClusterModifyISpecs():
928   """gnt-cluster modify --specs-*"""
929   params = ["memory-size", "disk-size", "disk-count", "cpu-count", "nic-count"]
930   (cur_policy, cur_specs) = _GetClusterIPolicy()
931   # This test assumes that there is only one min/max bound
932   assert len(cur_specs[constants.ISPECS_MINMAX]) == 1
933   for par in params:
934     test_values = [
935       (True, 0, 4, 12),
936       (True, 4, 4, 12),
937       (True, 4, 12, 12),
938       (True, 4, 4, 4),
939       (False, 4, 0, 12),
940       (False, 4, 16, 12),
941       (False, 4, 4, 0),
942       (False, 12, 4, 4),
943       (False, 12, 4, 0),
944       (False, "a", 4, 12),
945       (False, 0, "a", 12),
946       (False, 0, 4, "a"),
947       # This is to restore the old values
948       (True,
949        cur_specs[constants.ISPECS_MINMAX][0][constants.ISPECS_MIN][par],
950        cur_specs[constants.ISPECS_STD][par],
951        cur_specs[constants.ISPECS_MINMAX][0][constants.ISPECS_MAX][par])
952       ]
953     for (good, mn, st, mx) in test_values:
954       new_vals = {
955         constants.ISPECS_MINMAX: [{
956           constants.ISPECS_MIN: {par: mn},
957           constants.ISPECS_MAX: {par: mx}
958           }],
959         constants.ISPECS_STD: {par: st}
960         }
961       cur_state = (cur_policy, cur_specs)
962       # We update cur_specs, as we've copied the values to restore already
963       (cur_policy, cur_specs) = TestClusterSetISpecs(
964         diff_specs=new_vals, fail=not good, old_values=cur_state)
965
966     # Get the ipolicy command
967     mnode = qa_config.GetMasterNode()
968     initcmd = GetCommandOutput(mnode.primary, "gnt-cluster show-ispecs-cmd")
969     modcmd = ["gnt-cluster", "modify"]
970     opts = initcmd.split()
971     assert opts[0:2] == ["gnt-cluster", "init"]
972     for k in range(2, len(opts) - 1):
973       if opts[k].startswith("--ipolicy-"):
974         assert k + 2 <= len(opts)
975         modcmd.extend(opts[k:k + 2])
976     # Re-apply the ipolicy (this should be a no-op)
977     AssertCommand(modcmd)
978     new_initcmd = GetCommandOutput(mnode.primary, "gnt-cluster show-ispecs-cmd")
979     AssertEqual(initcmd, new_initcmd)
980
981
982 def TestClusterInfo():
983   """gnt-cluster info"""
984   AssertCommand(["gnt-cluster", "info"])
985
986
987 def TestClusterRedistConf():
988   """gnt-cluster redist-conf"""
989   AssertCommand(["gnt-cluster", "redist-conf"])
990
991
992 def TestClusterGetmaster():
993   """gnt-cluster getmaster"""
994   AssertCommand(["gnt-cluster", "getmaster"])
995
996
997 def TestClusterVersion():
998   """gnt-cluster version"""
999   AssertCommand(["gnt-cluster", "version"])
1000
1001
1002 def TestClusterRenewCrypto():
1003   """gnt-cluster renew-crypto"""
1004   master = qa_config.GetMasterNode()
1005
1006   # Conflicting options
1007   cmd = ["gnt-cluster", "renew-crypto", "--force",
1008          "--new-cluster-certificate", "--new-confd-hmac-key"]
1009   conflicting = [
1010     ["--new-rapi-certificate", "--rapi-certificate=/dev/null"],
1011     ["--new-cluster-domain-secret", "--cluster-domain-secret=/dev/null"],
1012     ]
1013   for i in conflicting:
1014     AssertCommand(cmd + i, fail=True)
1015
1016   # Invalid RAPI certificate
1017   cmd = ["gnt-cluster", "renew-crypto", "--force",
1018          "--rapi-certificate=/dev/null"]
1019   AssertCommand(cmd, fail=True)
1020
1021   rapi_cert_backup = qa_utils.BackupFile(master.primary,
1022                                          pathutils.RAPI_CERT_FILE)
1023   try:
1024     # Custom RAPI certificate
1025     fh = tempfile.NamedTemporaryFile()
1026
1027     # Ensure certificate doesn't cause "gnt-cluster verify" to complain
1028     validity = constants.SSL_CERT_EXPIRATION_WARN * 3
1029
1030     utils.GenerateSelfSignedSslCert(fh.name, validity=validity)
1031
1032     tmpcert = qa_utils.UploadFile(master.primary, fh.name)
1033     try:
1034       AssertCommand(["gnt-cluster", "renew-crypto", "--force",
1035                      "--rapi-certificate=%s" % tmpcert])
1036     finally:
1037       AssertCommand(["rm", "-f", tmpcert])
1038
1039     # Custom cluster domain secret
1040     cds_fh = tempfile.NamedTemporaryFile()
1041     cds_fh.write(utils.GenerateSecret())
1042     cds_fh.write("\n")
1043     cds_fh.flush()
1044
1045     tmpcds = qa_utils.UploadFile(master.primary, cds_fh.name)
1046     try:
1047       AssertCommand(["gnt-cluster", "renew-crypto", "--force",
1048                      "--cluster-domain-secret=%s" % tmpcds])
1049     finally:
1050       AssertCommand(["rm", "-f", tmpcds])
1051
1052     # Normal case
1053     AssertCommand(["gnt-cluster", "renew-crypto", "--force",
1054                    "--new-cluster-certificate", "--new-confd-hmac-key",
1055                    "--new-rapi-certificate", "--new-cluster-domain-secret"])
1056
1057     # Restore RAPI certificate
1058     AssertCommand(["gnt-cluster", "renew-crypto", "--force",
1059                    "--rapi-certificate=%s" % rapi_cert_backup])
1060   finally:
1061     AssertCommand(["rm", "-f", rapi_cert_backup])
1062
1063
1064 def TestClusterBurnin():
1065   """Burnin"""
1066   master = qa_config.GetMasterNode()
1067
1068   options = qa_config.get("options", {})
1069   disk_template = options.get("burnin-disk-template", constants.DT_DRBD8)
1070   parallel = options.get("burnin-in-parallel", False)
1071   check_inst = options.get("burnin-check-instances", False)
1072   do_rename = options.get("burnin-rename", "")
1073   do_reboot = options.get("burnin-reboot", True)
1074   reboot_types = options.get("reboot-types", constants.REBOOT_TYPES)
1075
1076   # Get as many instances as we need
1077   instances = []
1078   try:
1079     try:
1080       num = qa_config.get("options", {}).get("burnin-instances", 1)
1081       for _ in range(0, num):
1082         instances.append(qa_config.AcquireInstance())
1083     except qa_error.OutOfInstancesError:
1084       print "Not enough instances, continuing anyway."
1085
1086     if len(instances) < 1:
1087       raise qa_error.Error("Burnin needs at least one instance")
1088
1089     script = qa_utils.UploadFile(master.primary, "../tools/burnin")
1090     try:
1091       disks = qa_config.GetDiskOptions()
1092       # Run burnin
1093       cmd = ["env",
1094              "PYTHONPATH=%s" % _constants.VERSIONEDSHAREDIR,
1095              script,
1096              "--os=%s" % qa_config.get("os"),
1097              "--minmem-size=%s" % qa_config.get(constants.BE_MINMEM),
1098              "--maxmem-size=%s" % qa_config.get(constants.BE_MAXMEM),
1099              "--disk-size=%s" % ",".join([d.get("size") for d in disks]),
1100              "--disk-growth=%s" % ",".join([d.get("growth") for d in disks]),
1101              "--disk-template=%s" % disk_template]
1102       if parallel:
1103         cmd.append("--parallel")
1104         cmd.append("--early-release")
1105       if check_inst:
1106         cmd.append("--http-check")
1107       if do_rename:
1108         cmd.append("--rename=%s" % do_rename)
1109       if not do_reboot:
1110         cmd.append("--no-reboot")
1111       else:
1112         cmd.append("--reboot-types=%s" % ",".join(reboot_types))
1113       cmd += [inst.name for inst in instances]
1114       AssertCommand(cmd)
1115     finally:
1116       AssertCommand(["rm", "-f", script])
1117
1118   finally:
1119     for inst in instances:
1120       inst.Release()
1121
1122
1123 def TestClusterMasterFailover():
1124   """gnt-cluster master-failover"""
1125   master = qa_config.GetMasterNode()
1126   failovermaster = qa_config.AcquireNode(exclude=master)
1127
1128   cmd = ["gnt-cluster", "master-failover"]
1129   node_list_cmd = ["gnt-node", "list"]
1130   try:
1131     AssertCommand(cmd, node=failovermaster)
1132     AssertCommand(node_list_cmd, node=failovermaster)
1133     # Back to original master node
1134     AssertCommand(cmd, node=master)
1135     AssertCommand(node_list_cmd, node=master)
1136   finally:
1137     failovermaster.Release()
1138
1139
1140 def _NodeQueueDrainFile(node):
1141   """Returns path to queue drain file for a node.
1142
1143   """
1144   return qa_utils.MakeNodePath(node, pathutils.JOB_QUEUE_DRAIN_FILE)
1145
1146
1147 def _AssertDrainFile(node, **kwargs):
1148   """Checks for the queue drain file.
1149
1150   """
1151   AssertCommand(["test", "-f", _NodeQueueDrainFile(node)], node=node, **kwargs)
1152
1153
1154 def TestClusterMasterFailoverWithDrainedQueue():
1155   """gnt-cluster master-failover with drained queue"""
1156   master = qa_config.GetMasterNode()
1157   failovermaster = qa_config.AcquireNode(exclude=master)
1158
1159   # Ensure queue is not drained
1160   for node in [master, failovermaster]:
1161     _AssertDrainFile(node, fail=True)
1162
1163   # Drain queue on failover master
1164   AssertCommand(["touch", _NodeQueueDrainFile(failovermaster)],
1165                 node=failovermaster)
1166
1167   cmd = ["gnt-cluster", "master-failover"]
1168   try:
1169     _AssertDrainFile(failovermaster)
1170     AssertCommand(cmd, node=failovermaster)
1171     _AssertDrainFile(master, fail=True)
1172     _AssertDrainFile(failovermaster, fail=True)
1173
1174     # Back to original master node
1175     AssertCommand(cmd, node=master)
1176   finally:
1177     failovermaster.Release()
1178
1179   # Ensure queue is not drained
1180   for node in [master, failovermaster]:
1181     _AssertDrainFile(node, fail=True)
1182
1183
1184 def TestClusterCopyfile():
1185   """gnt-cluster copyfile"""
1186   master = qa_config.GetMasterNode()
1187
1188   uniqueid = utils.NewUUID()
1189
1190   # Create temporary file
1191   f = tempfile.NamedTemporaryFile()
1192   f.write(uniqueid)
1193   f.flush()
1194   f.seek(0)
1195
1196   # Upload file to master node
1197   testname = qa_utils.UploadFile(master.primary, f.name)
1198   try:
1199     # Copy file to all nodes
1200     AssertCommand(["gnt-cluster", "copyfile", testname])
1201     _CheckFileOnAllNodes(testname, uniqueid)
1202   finally:
1203     _RemoveFileFromAllNodes(testname)
1204
1205
1206 def TestClusterCommand():
1207   """gnt-cluster command"""
1208   uniqueid = utils.NewUUID()
1209   rfile = "/tmp/gnt%s" % utils.NewUUID()
1210   rcmd = utils.ShellQuoteArgs(["echo", "-n", uniqueid])
1211   cmd = utils.ShellQuoteArgs(["gnt-cluster", "command",
1212                               "%s >%s" % (rcmd, rfile)])
1213
1214   try:
1215     AssertCommand(cmd)
1216     _CheckFileOnAllNodes(rfile, uniqueid)
1217   finally:
1218     _RemoveFileFromAllNodes(rfile)
1219
1220
1221 def TestClusterDestroy():
1222   """gnt-cluster destroy"""
1223   AssertCommand(["gnt-cluster", "destroy", "--yes-do-it"])
1224
1225
1226 def TestClusterRepairDiskSizes():
1227   """gnt-cluster repair-disk-sizes"""
1228   AssertCommand(["gnt-cluster", "repair-disk-sizes"])
1229
1230
1231 def TestSetExclStorCluster(newvalue):
1232   """Set the exclusive_storage node parameter at the cluster level.
1233
1234   @type newvalue: bool
1235   @param newvalue: New value of exclusive_storage
1236   @rtype: bool
1237   @return: The old value of exclusive_storage
1238
1239   """
1240   es_path = ["Default node parameters", "exclusive_storage"]
1241   oldvalue = _GetClusterField(es_path)
1242   AssertCommand(["gnt-cluster", "modify", "--node-parameters",
1243                  "exclusive_storage=%s" % newvalue])
1244   effvalue = _GetClusterField(es_path)
1245   if effvalue != newvalue:
1246     raise qa_error.Error("exclusive_storage has the wrong value: %s instead"
1247                          " of %s" % (effvalue, newvalue))
1248   qa_config.SetExclusiveStorage(newvalue)
1249   return oldvalue
1250
1251
1252 def TestExclStorSharedPv(node):
1253   """cluster-verify reports LVs that share the same PV with exclusive_storage.
1254
1255   """
1256   vgname = qa_config.get("vg-name", constants.DEFAULT_VG)
1257   lvname1 = _QA_LV_PREFIX + "vol1"
1258   lvname2 = _QA_LV_PREFIX + "vol2"
1259   node_name = node.primary
1260   AssertCommand(["lvcreate", "-L1G", "-n", lvname1, vgname], node=node_name)
1261   AssertClusterVerify(fail=True, errors=[constants.CV_ENODEORPHANLV])
1262   AssertCommand(["lvcreate", "-L1G", "-n", lvname2, vgname], node=node_name)
1263   AssertClusterVerify(fail=True, errors=[constants.CV_ENODELVM,
1264                                          constants.CV_ENODEORPHANLV])
1265   AssertCommand(["lvremove", "-f", "/".join([vgname, lvname1])], node=node_name)
1266   AssertCommand(["lvremove", "-f", "/".join([vgname, lvname2])], node=node_name)
1267   AssertClusterVerify()