Merge branch 'stable-2.9' into master
[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 compat
32 from ganeti import utils
33 from ganeti import pathutils
34
35 import qa_config
36 import qa_daemon
37 import qa_utils
38 import qa_error
39 import qa_instance
40
41 from qa_utils import AssertEqual, AssertCommand, GetCommandOutput
42
43
44 # Prefix for LVM volumes created by QA code during tests
45 _QA_LV_PREFIX = "qa-"
46
47 #: cluster verify command
48 _CLUSTER_VERIFY = ["gnt-cluster", "verify"]
49
50
51 def _RemoveFileFromAllNodes(filename):
52   """Removes a file from all nodes.
53
54   """
55   for node in qa_config.get("nodes"):
56     AssertCommand(["rm", "-f", filename], node=node)
57
58
59 def _CheckFileOnAllNodes(filename, content):
60   """Verifies the content of the given file on all nodes.
61
62   """
63   cmd = utils.ShellQuoteArgs(["cat", filename])
64   for node in qa_config.get("nodes"):
65     AssertEqual(qa_utils.GetCommandOutput(node.primary, cmd), content)
66
67
68 def _GetClusterField(field_path):
69   """Get the value of a cluster field.
70
71   @type field_path: list of strings
72   @param field_path: Names of the groups/fields to navigate to get the desired
73       value, e.g. C{["Default node parameters", "oob_program"]}
74   @return: The effective value of the field (the actual type depends on the
75       chosen field)
76
77   """
78   assert isinstance(field_path, list)
79   assert field_path
80   ret = qa_utils.GetObjectInfo(["gnt-cluster", "info"])
81   for key in field_path:
82     ret = ret[key]
83   return ret
84
85
86 # Cluster-verify errors (date, "ERROR", then error code)
87 _CVERROR_RE = re.compile(r"^[\w\s:]+\s+- (ERROR|WARNING):([A-Z0-9_-]+):")
88
89
90 def _GetCVErrorCodes(cvout):
91   errs = set()
92   warns = set()
93   for l in cvout.splitlines():
94     m = _CVERROR_RE.match(l)
95     if m:
96       etype = m.group(1)
97       ecode = m.group(2)
98       if etype == "ERROR":
99         errs.add(ecode)
100       elif etype == "WARNING":
101         warns.add(ecode)
102   return (errs, warns)
103
104
105 def _CheckVerifyErrors(actual, expected, etype):
106   exp_codes = compat.UniqueFrozenset(e for (_, e, _) in expected)
107   if not actual.issuperset(exp_codes):
108     missing = exp_codes.difference(actual)
109     raise qa_error.Error("Cluster-verify didn't return these expected"
110                          " %ss: %s" % (etype, utils.CommaJoin(missing)))
111
112
113 def AssertClusterVerify(fail=False, errors=None, warnings=None):
114   """Run cluster-verify and check the result
115
116   @type fail: bool
117   @param fail: if cluster-verify is expected to fail instead of succeeding
118   @type errors: list of tuples
119   @param errors: List of CV_XXX errors that are expected; if specified, all the
120       errors listed must appear in cluster-verify output. A non-empty value
121       implies C{fail=True}.
122   @type warnings: list of tuples
123   @param warnings: Same as C{errors} but for warnings.
124
125   """
126   cvcmd = "gnt-cluster verify"
127   mnode = qa_config.GetMasterNode()
128   if errors or warnings:
129     cvout = GetCommandOutput(mnode.primary, cvcmd + " --error-codes",
130                              fail=(fail or errors))
131     (act_errs, act_warns) = _GetCVErrorCodes(cvout)
132     if errors:
133       _CheckVerifyErrors(act_errs, errors, "error")
134     if warnings:
135       _CheckVerifyErrors(act_warns, warnings, "warning")
136   else:
137     AssertCommand(cvcmd, fail=fail, node=mnode)
138
139
140 # data for testing failures due to bad keys/values for disk parameters
141 _FAIL_PARAMS = ["nonexistent:resync-rate=1",
142                 "drbd:nonexistent=1",
143                 "drbd:resync-rate=invalid",
144                 ]
145
146
147 def TestClusterInitDisk():
148   """gnt-cluster init -D"""
149   name = qa_config.get("name")
150   for param in _FAIL_PARAMS:
151     AssertCommand(["gnt-cluster", "init", "-D", param, name], fail=True)
152
153
154 def TestClusterInit(rapi_user, rapi_secret):
155   """gnt-cluster init"""
156   master = qa_config.GetMasterNode()
157
158   rapi_users_path = qa_utils.MakeNodePath(master, pathutils.RAPI_USERS_FILE)
159   rapi_dir = os.path.dirname(rapi_users_path)
160
161   # First create the RAPI credentials
162   fh = tempfile.NamedTemporaryFile()
163   try:
164     fh.write("%s %s write\n" % (rapi_user, rapi_secret))
165     fh.flush()
166
167     tmpru = qa_utils.UploadFile(master.primary, fh.name)
168     try:
169       AssertCommand(["mkdir", "-p", rapi_dir])
170       AssertCommand(["mv", tmpru, rapi_users_path])
171     finally:
172       AssertCommand(["rm", "-f", tmpru])
173   finally:
174     fh.close()
175
176   # Initialize cluster
177   enabled_disk_templates = qa_config.GetEnabledDiskTemplates()
178   cmd = [
179     "gnt-cluster", "init",
180     "--primary-ip-version=%d" % qa_config.get("primary_ip_version", 4),
181     "--enabled-hypervisors=%s" % ",".join(qa_config.GetEnabledHypervisors()),
182     "--enabled-disk-templates=%s" %
183       ",".join(enabled_disk_templates),
184     ]
185   if constants.DT_FILE in enabled_disk_templates:
186     cmd.append(
187         "--file-storage-dir=%s" %
188         qa_config.get("default-file-storage-dir",
189                       pathutils.DEFAULT_FILE_STORAGE_DIR))
190
191   for spec_type in ("mem-size", "disk-size", "disk-count", "cpu-count",
192                     "nic-count"):
193     for spec_val in ("min", "max", "std"):
194       spec = qa_config.get("ispec_%s_%s" %
195                            (spec_type.replace("-", "_"), spec_val), None)
196       if spec is not None:
197         cmd.append("--specs-%s=%s=%d" % (spec_type, spec_val, spec))
198
199   if master.secondary:
200     cmd.append("--secondary-ip=%s" % master.secondary)
201
202   if utils.IsLvmEnabled(qa_config.GetEnabledDiskTemplates()):
203     vgname = qa_config.get("vg-name", constants.DEFAULT_VG)
204     if vgname:
205       cmd.append("--vg-name=%s" % vgname)
206     else:
207       raise qa_error.Error("Please specify a volume group if you enable"
208                            " lvm-based disk templates in the QA.")
209
210   master_netdev = qa_config.get("master-netdev", None)
211   if master_netdev:
212     cmd.append("--master-netdev=%s" % master_netdev)
213
214   nicparams = qa_config.get("default-nicparams", None)
215   if nicparams:
216     cmd.append("--nic-parameters=%s" %
217                ",".join(utils.FormatKeyValue(nicparams)))
218
219   # Cluster value of the exclusive-storage node parameter
220   e_s = qa_config.get("exclusive-storage")
221   if e_s is not None:
222     cmd.extend(["--node-parameters", "exclusive_storage=%s" % e_s])
223   else:
224     e_s = False
225   qa_config.SetExclusiveStorage(e_s)
226
227   extra_args = qa_config.get("cluster-init-args")
228
229   if extra_args:
230     # This option was removed in 2.10, but in order to not break QA of older
231     # branches we remove it from the extra_args if it is in there.
232     opt_drbd_storage = "--no-drbd-storage"
233     if opt_drbd_storage in extra_args:
234       extra_args.remove(opt_drbd_storage)
235     cmd.extend(extra_args)
236
237   cmd.append(qa_config.get("name"))
238
239   AssertCommand(cmd)
240
241   cmd = ["gnt-cluster", "modify"]
242
243   # hypervisor parameter modifications
244   hvp = qa_config.get("hypervisor-parameters", {})
245   for k, v in hvp.items():
246     cmd.extend(["-H", "%s:%s" % (k, v)])
247   # backend parameter modifications
248   bep = qa_config.get("backend-parameters", "")
249   if bep:
250     cmd.extend(["-B", bep])
251
252   if len(cmd) > 2:
253     AssertCommand(cmd)
254
255   # OS parameters
256   osp = qa_config.get("os-parameters", {})
257   for k, v in osp.items():
258     AssertCommand(["gnt-os", "modify", "-O", v, k])
259
260   # OS hypervisor parameters
261   os_hvp = qa_config.get("os-hvp", {})
262   for os_name in os_hvp:
263     for hv, hvp in os_hvp[os_name].items():
264       AssertCommand(["gnt-os", "modify", "-H", "%s:%s" % (hv, hvp), os_name])
265
266
267 def TestClusterRename():
268   """gnt-cluster rename"""
269   cmd = ["gnt-cluster", "rename", "-f"]
270
271   original_name = qa_config.get("name")
272   rename_target = qa_config.get("rename", None)
273   if rename_target is None:
274     print qa_utils.FormatError('"rename" entry is missing')
275     return
276
277   for data in [
278     cmd + [rename_target],
279     _CLUSTER_VERIFY,
280     cmd + [original_name],
281     _CLUSTER_VERIFY,
282     ]:
283     AssertCommand(data)
284
285
286 def TestClusterOob():
287   """out-of-band framework"""
288   oob_path_exists = "/tmp/ganeti-qa-oob-does-exist-%s" % utils.NewUUID()
289
290   AssertCommand(_CLUSTER_VERIFY)
291   AssertCommand(["gnt-cluster", "modify", "--node-parameters",
292                  "oob_program=/tmp/ganeti-qa-oob-does-not-exist-%s" %
293                  utils.NewUUID()])
294
295   AssertCommand(_CLUSTER_VERIFY, fail=True)
296
297   AssertCommand(["touch", oob_path_exists])
298   AssertCommand(["chmod", "0400", oob_path_exists])
299   AssertCommand(["gnt-cluster", "copyfile", oob_path_exists])
300
301   try:
302     AssertCommand(["gnt-cluster", "modify", "--node-parameters",
303                    "oob_program=%s" % oob_path_exists])
304
305     AssertCommand(_CLUSTER_VERIFY, fail=True)
306
307     AssertCommand(["chmod", "0500", oob_path_exists])
308     AssertCommand(["gnt-cluster", "copyfile", oob_path_exists])
309
310     AssertCommand(_CLUSTER_VERIFY)
311   finally:
312     AssertCommand(["gnt-cluster", "command", "rm", oob_path_exists])
313
314   AssertCommand(["gnt-cluster", "modify", "--node-parameters",
315                  "oob_program="])
316
317
318 def TestClusterEpo():
319   """gnt-cluster epo"""
320   master = qa_config.GetMasterNode()
321
322   # Assert that OOB is unavailable for all nodes
323   result_output = GetCommandOutput(master.primary,
324                                    "gnt-node list --verbose --no-headers -o"
325                                    " powered")
326   AssertEqual(compat.all(powered == "(unavail)"
327                          for powered in result_output.splitlines()), True)
328
329   # Conflicting
330   AssertCommand(["gnt-cluster", "epo", "--groups", "--all"], fail=True)
331   # --all doesn't expect arguments
332   AssertCommand(["gnt-cluster", "epo", "--all", "some_arg"], fail=True)
333
334   # Unless --all is given master is not allowed to be in the list
335   AssertCommand(["gnt-cluster", "epo", "-f", master.primary], fail=True)
336
337   # This shouldn't fail
338   AssertCommand(["gnt-cluster", "epo", "-f", "--all"])
339
340   # All instances should have been stopped now
341   result_output = GetCommandOutput(master.primary,
342                                    "gnt-instance list --no-headers -o status")
343   # ERROR_down because the instance is stopped but not recorded as such
344   AssertEqual(compat.all(status == "ERROR_down"
345                          for status in result_output.splitlines()), True)
346
347   # Now start everything again
348   AssertCommand(["gnt-cluster", "epo", "--on", "-f", "--all"])
349
350   # All instances should have been started now
351   result_output = GetCommandOutput(master.primary,
352                                    "gnt-instance list --no-headers -o status")
353   AssertEqual(compat.all(status == "running"
354                          for status in result_output.splitlines()), True)
355
356
357 def TestClusterVerify():
358   """gnt-cluster verify"""
359   AssertCommand(_CLUSTER_VERIFY)
360   AssertCommand(["gnt-cluster", "verify-disks"])
361
362
363 def TestClusterVerifyDisksBrokenDRBD(instance, inst_nodes):
364   """gnt-cluster verify-disks with broken DRBD"""
365   qa_daemon.TestPauseWatcher()
366
367   try:
368     info = qa_instance.GetInstanceInfo(instance.name)
369     snode = inst_nodes[1]
370     for idx, minor in enumerate(info["drbd-minors"][snode.primary]):
371       if idx % 2 == 0:
372         break_drbd_cmd = \
373           "(drbdsetup %d down >/dev/null 2>&1;" \
374           " drbdsetup down resource%d >/dev/null 2>&1) || /bin/true" % \
375           (minor, minor)
376       else:
377         break_drbd_cmd = \
378           "(drbdsetup %d detach >/dev/null 2>&1;" \
379           " drbdsetup detach %d >/dev/null 2>&1) || /bin/true" % \
380           (minor, minor)
381       AssertCommand(break_drbd_cmd, node=snode)
382
383     verify_output = GetCommandOutput(qa_config.GetMasterNode().primary,
384                                      "gnt-cluster verify-disks")
385     activation_msg = "Activating disks for instance '%s'" % instance.name
386     if activation_msg not in verify_output:
387       raise qa_error.Error("gnt-cluster verify-disks did not activate broken"
388                            " DRBD disks:\n%s" % verify_output)
389
390     verify_output = GetCommandOutput(qa_config.GetMasterNode().primary,
391                                      "gnt-cluster verify-disks")
392     if activation_msg in verify_output:
393       raise qa_error.Error("gnt-cluster verify-disks wants to activate broken"
394                            " DRBD disks on second attempt:\n%s" % verify_output)
395
396     AssertCommand(_CLUSTER_VERIFY)
397   finally:
398     qa_daemon.TestResumeWatcher()
399
400
401 def TestJobqueue():
402   """gnt-debug test-jobqueue"""
403   AssertCommand(["gnt-debug", "test-jobqueue"])
404
405
406 def TestDelay(node):
407   """gnt-debug delay"""
408   AssertCommand(["gnt-debug", "delay", "1"])
409   AssertCommand(["gnt-debug", "delay", "--no-master", "1"])
410   AssertCommand(["gnt-debug", "delay", "--no-master",
411                  "-n", node.primary, "1"])
412
413
414 def TestClusterReservedLvs():
415   """gnt-cluster reserved lvs"""
416   # if no lvm-based templates are supported, skip the test
417   if not qa_config.IsStorageTypeSupported(constants.ST_LVM_VG):
418     return
419   vgname = qa_config.get("vg-name", constants.DEFAULT_VG)
420   lvname = _QA_LV_PREFIX + "test"
421   lvfullname = "/".join([vgname, lvname])
422   for fail, cmd in [
423     (False, _CLUSTER_VERIFY),
424     (False, ["gnt-cluster", "modify", "--reserved-lvs", ""]),
425     (False, ["lvcreate", "-L1G", "-n", lvname, vgname]),
426     (True, _CLUSTER_VERIFY),
427     (False, ["gnt-cluster", "modify", "--reserved-lvs",
428              "%s,.*/other-test" % lvfullname]),
429     (False, _CLUSTER_VERIFY),
430     (False, ["gnt-cluster", "modify", "--reserved-lvs",
431              ".*/%s.*" % _QA_LV_PREFIX]),
432     (False, _CLUSTER_VERIFY),
433     (False, ["gnt-cluster", "modify", "--reserved-lvs", ""]),
434     (True, _CLUSTER_VERIFY),
435     (False, ["lvremove", "-f", lvfullname]),
436     (False, _CLUSTER_VERIFY),
437     ]:
438     AssertCommand(cmd, fail=fail)
439
440
441 def TestClusterModifyEmpty():
442   """gnt-cluster modify"""
443   AssertCommand(["gnt-cluster", "modify"], fail=True)
444
445
446 def TestClusterModifyDisk():
447   """gnt-cluster modify -D"""
448   for param in _FAIL_PARAMS:
449     AssertCommand(["gnt-cluster", "modify", "-D", param], fail=True)
450
451
452 def _GetOtherEnabledDiskTemplate(undesired_disk_templates,
453                                  enabled_disk_templates):
454   """Returns one template that is not in the undesired set.
455
456   @type undesired_disk_templates: list of string
457   @param undesired_disk_templates: a list of disk templates that we want to
458       exclude when drawing one disk template from the list of enabled
459       disk templates
460   @type enabled_disk_templates: list of string
461   @param enabled_disk_templates: list of enabled disk templates (in QA)
462
463   """
464   desired_templates = list(set(enabled_disk_templates)
465                                 - set(undesired_disk_templates))
466   if desired_templates:
467     template = desired_templates[0]
468   else:
469     # If no desired disk template is available for QA, choose 'diskless' and
470     # hope for the best.
471     template = constants.ST_DISKLESS
472
473   return template
474
475
476 def TestClusterModifyFileBasedStorageDir(
477     file_disk_template, dir_config_key, default_dir, option_name):
478   """Tests gnt-cluster modify wrt to file-based directory options.
479
480   @type file_disk_template: string
481   @param file_disk_template: file-based disk template
482   @type dir_config_key: string
483   @param dir_config_key: key for the QA config to retrieve the default
484      directory value
485   @type default_dir: string
486   @param default_dir: default directory, if the QA config does not specify
487      it
488   @type option_name: string
489   @param option_name: name of the option of 'gnt-cluster modify' to
490      change the directory
491
492   """
493   enabled_disk_templates = qa_config.GetEnabledDiskTemplates()
494   assert file_disk_template in [constants.DT_FILE, constants.DT_SHARED_FILE]
495   if not qa_config.IsTemplateSupported(file_disk_template):
496     return
497
498   # Get some non-file-based disk template to disable file storage
499   other_disk_template = _GetOtherEnabledDiskTemplate(
500       utils.storage.GetDiskTemplatesOfStorageType(constants.ST_FILE),
501       enabled_disk_templates)
502
503   file_storage_dir = qa_config.get(dir_config_key, default_dir)
504   invalid_file_storage_dir = "/boot/"
505
506   for fail, cmd in [
507     (False, ["gnt-cluster", "modify",
508             "--enabled-disk-templates=%s" % file_disk_template,
509             "--ipolicy-disk-templates=%s" % file_disk_template]),
510     (False, ["gnt-cluster", "modify",
511             "--%s=%s" % (option_name, file_storage_dir)]),
512     (False, ["gnt-cluster", "modify",
513             "--%s=%s" % (option_name, invalid_file_storage_dir)]),
514     # file storage dir is set to an inacceptable path, thus verify
515     # should fail
516     (True, ["gnt-cluster", "verify"]),
517     # unsetting the storage dir while file storage is enabled
518     # should fail
519     (True, ["gnt-cluster", "modify",
520             "--%s=" % option_name]),
521     (False, ["gnt-cluster", "modify",
522             "--%s=%s" % (option_name, file_storage_dir)]),
523     (False, ["gnt-cluster", "modify",
524             "--enabled-disk-templates=%s" % other_disk_template,
525             "--ipolicy-disk-templates=%s" % other_disk_template]),
526     (False, ["gnt-cluster", "modify",
527             "--%s=%s" % (option_name, invalid_file_storage_dir)]),
528     # file storage is set to an inacceptable path, but file storage
529     # is disabled, thus verify should not fail
530     (False, ["gnt-cluster", "verify"]),
531     # unsetting the file storage dir while file storage is not enabled
532     # should be fine
533     (False, ["gnt-cluster", "modify",
534             "--%s=" % option_name]),
535     # resetting everything to sane values
536     (False, ["gnt-cluster", "modify",
537             "--%s=%s" % (option_name, file_storage_dir),
538             "--enabled-disk-templates=%s" % ",".join(enabled_disk_templates),
539             "--ipolicy-disk-templates=%s" % ",".join(enabled_disk_templates)])
540     ]:
541     AssertCommand(cmd, fail=fail)
542
543
544 def TestClusterModifyFileStorageDir():
545   """gnt-cluster modify --file-storage-dir=..."""
546   TestClusterModifyFileBasedStorageDir(
547       constants.DT_FILE, "default-file-storage-dir",
548       pathutils.DEFAULT_FILE_STORAGE_DIR,
549       "file-storage-dir")
550
551
552 def TestClusterModifySharedFileStorageDir():
553   """gnt-cluster modify --shared-file-storage-dir=..."""
554   TestClusterModifyFileBasedStorageDir(
555       constants.DT_SHARED_FILE, "default-shared-file-storage-dir",
556       pathutils.DEFAULT_SHARED_FILE_STORAGE_DIR,
557       "shared-file-storage-dir")
558
559
560 def TestClusterModifyDiskTemplates():
561   """gnt-cluster modify --enabled-disk-templates=..."""
562   enabled_disk_templates = qa_config.GetEnabledDiskTemplates()
563   default_disk_template = qa_config.GetDefaultDiskTemplate()
564
565   _TestClusterModifyDiskTemplatesArguments(default_disk_template)
566   _TestClusterModifyDiskTemplatesDrbdHelper(enabled_disk_templates)
567   _TestClusterModifyDiskTemplatesVgName(enabled_disk_templates)
568
569   _RestoreEnabledDiskTemplates()
570   nodes = qa_config.AcquireManyNodes(2)
571
572   instance_template = enabled_disk_templates[0]
573   instance = qa_instance.CreateInstanceByDiskTemplate(nodes, instance_template)
574
575   _TestClusterModifyUnusedDiskTemplate(instance_template)
576   _TestClusterModifyUsedDiskTemplate(instance_template,
577                                      enabled_disk_templates)
578
579   qa_instance.TestInstanceRemove(instance)
580   _RestoreEnabledDiskTemplates()
581
582
583 def _RestoreEnabledDiskTemplates():
584   """Sets the list of enabled disk templates back to the list of enabled disk
585      templates from the QA configuration. This can be used to make sure that
586      the tests that modify the list of disk templates do not interfere with
587      other tests.
588
589   """
590   enabled_disk_templates = qa_config.GetEnabledDiskTemplates()
591   cmd = ["gnt-cluster", "modify",
592          "--enabled-disk-templates=%s" % ",".join(enabled_disk_templates),
593          "--ipolicy-disk-templates=%s" % ",".join(enabled_disk_templates),
594          ]
595
596   if utils.IsLvmEnabled(qa_config.GetEnabledDiskTemplates()):
597     vgname = qa_config.get("vg-name", constants.DEFAULT_VG)
598     cmd.append("--vg-name=%s" % vgname)
599
600   AssertCommand(cmd, fail=False)
601
602
603 def _TestClusterModifyDiskTemplatesDrbdHelper(enabled_disk_templates):
604   """Tests argument handling of 'gnt-cluster modify' with respect to
605      the parameter '--drbd-usermode-helper'. This test is independent
606      of instances.
607
608   """
609   _RestoreEnabledDiskTemplates()
610
611   if constants.DT_DRBD8 not in enabled_disk_templates:
612     return
613   if constants.DT_PLAIN not in enabled_disk_templates:
614     return
615
616   drbd_usermode_helper = qa_config.get("drbd-usermode-helper", "/bin/true")
617   bogus_usermode_helper = "/tmp/pinkbunny"
618   for command, fail in \
619       [(["gnt-cluster", "modify",
620          "--enabled-disk-templates=%s" % constants.DT_DRBD8,
621          "--ipolicy-disk-templates=%s" % constants.DT_DRBD8], False),
622        (["gnt-cluster", "modify",
623          "--drbd-usermode-helper=%s" % drbd_usermode_helper], False),
624        (["gnt-cluster", "modify",
625          "--drbd-usermode-helper=%s" % bogus_usermode_helper], True),
626        # unsetting helper when DRBD is enabled should not work
627        (["gnt-cluster", "modify",
628          "--drbd-usermode-helper="], True),
629        (["gnt-cluster", "modify",
630          "--enabled-disk-templates=%s" % constants.DT_PLAIN,
631          "--ipolicy-disk-templates=%s" % constants.DT_PLAIN], False),
632        (["gnt-cluster", "modify",
633          "--drbd-usermode-helper="], False),
634        (["gnt-cluster", "modify",
635          "--drbd-usermode-helper=%s" % drbd_usermode_helper], False),
636        (["gnt-cluster", "modify",
637          "--drbd-usermode-helper=%s" % drbd_usermode_helper,
638          "--enabled-disk-templates=%s" % constants.DT_DRBD8,
639          "--ipolicy-disk-templates=%s" % constants.DT_DRBD8], False),
640        (["gnt-cluster", "modify",
641          "--drbd-usermode-helper=",
642          "--enabled-disk-templates=%s" % constants.DT_PLAIN,
643          "--ipolicy-disk-templates=%s" % constants.DT_PLAIN], False),
644        (["gnt-cluster", "modify",
645          "--drbd-usermode-helper=%s" % drbd_usermode_helper,
646          "--enabled-disk-templates=%s" % constants.DT_DRBD8,
647          "--ipolicy-disk-templates=%s" % constants.DT_DRBD8], False),
648       ]:
649     AssertCommand(command, fail=fail)
650   _RestoreEnabledDiskTemplates()
651
652
653 def _TestClusterModifyDiskTemplatesArguments(default_disk_template):
654   """Tests argument handling of 'gnt-cluster modify' with respect to
655      the parameter '--enabled-disk-templates'. This test is independent
656      of instances.
657
658   """
659   _RestoreEnabledDiskTemplates()
660
661   # bogus templates
662   AssertCommand(["gnt-cluster", "modify",
663                  "--enabled-disk-templates=pinkbunny"],
664                 fail=True)
665
666   # duplicate entries do no harm
667   AssertCommand(
668     ["gnt-cluster", "modify",
669      "--enabled-disk-templates=%s,%s" %
670       (default_disk_template, default_disk_template),
671      "--ipolicy-disk-templates=%s" % default_disk_template],
672     fail=False)
673
674
675 def _TestClusterModifyDiskTemplatesVgName(enabled_disk_templates):
676   """Tests argument handling of 'gnt-cluster modify' with respect to
677      the parameter '--enabled-disk-templates' and '--vg-name'. This test is
678      independent of instances.
679
680   """
681   if not utils.IsLvmEnabled(enabled_disk_templates):
682     # These tests only make sense if lvm is enabled for QA
683     return
684
685   # determine an LVM and a non-LVM disk template for the tests
686   non_lvm_template = _GetOtherEnabledDiskTemplate(utils.GetLvmDiskTemplates(),
687                                                   enabled_disk_templates)
688   lvm_template = list(set(enabled_disk_templates)
689                       .intersection(set(utils.GetLvmDiskTemplates())))[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 _TestClusterModifyUnusedDiskTemplate(instance_template):
781   """Tests that unused disk templates can be disabled safely."""
782   all_disk_templates = constants.DISK_TEMPLATES
783   if not utils.IsLvmEnabled(qa_config.GetEnabledDiskTemplates()):
784     all_disk_templates = list(set(all_disk_templates) -
785                               set(utils.GetLvmDiskTemplates()))
786
787   AssertCommand(
788     ["gnt-cluster", "modify",
789      "--enabled-disk-templates=%s" % ",".join(all_disk_templates),
790      "--ipolicy-disk-templates=%s" % ",".join(all_disk_templates)],
791     fail=False)
792   new_disk_templates = [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=False)
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 = [script,
1114              "--os=%s" % qa_config.get("os"),
1115              "--minmem-size=%s" % qa_config.get(constants.BE_MINMEM),
1116              "--maxmem-size=%s" % qa_config.get(constants.BE_MAXMEM),
1117              "--disk-size=%s" % ",".join([d.get("size") for d in disks]),
1118              "--disk-growth=%s" % ",".join([d.get("growth") for d in disks]),
1119              "--disk-template=%s" % disk_template]
1120       if parallel:
1121         cmd.append("--parallel")
1122         cmd.append("--early-release")
1123       if check_inst:
1124         cmd.append("--http-check")
1125       if do_rename:
1126         cmd.append("--rename=%s" % do_rename)
1127       if not do_reboot:
1128         cmd.append("--no-reboot")
1129       else:
1130         cmd.append("--reboot-types=%s" % ",".join(reboot_types))
1131       cmd += [inst.name for inst in instances]
1132       AssertCommand(cmd)
1133     finally:
1134       AssertCommand(["rm", "-f", script])
1135
1136   finally:
1137     for inst in instances:
1138       inst.Release()
1139
1140
1141 def TestClusterMasterFailover():
1142   """gnt-cluster master-failover"""
1143   master = qa_config.GetMasterNode()
1144   failovermaster = qa_config.AcquireNode(exclude=master)
1145
1146   cmd = ["gnt-cluster", "master-failover"]
1147   try:
1148     AssertCommand(cmd, node=failovermaster)
1149     # Back to original master node
1150     AssertCommand(cmd, node=master)
1151   finally:
1152     failovermaster.Release()
1153
1154
1155 def _NodeQueueDrainFile(node):
1156   """Returns path to queue drain file for a node.
1157
1158   """
1159   return qa_utils.MakeNodePath(node, pathutils.JOB_QUEUE_DRAIN_FILE)
1160
1161
1162 def _AssertDrainFile(node, **kwargs):
1163   """Checks for the queue drain file.
1164
1165   """
1166   AssertCommand(["test", "-f", _NodeQueueDrainFile(node)], node=node, **kwargs)
1167
1168
1169 def TestClusterMasterFailoverWithDrainedQueue():
1170   """gnt-cluster master-failover with drained queue"""
1171   master = qa_config.GetMasterNode()
1172   failovermaster = qa_config.AcquireNode(exclude=master)
1173
1174   # Ensure queue is not drained
1175   for node in [master, failovermaster]:
1176     _AssertDrainFile(node, fail=True)
1177
1178   # Drain queue on failover master
1179   AssertCommand(["touch", _NodeQueueDrainFile(failovermaster)],
1180                 node=failovermaster)
1181
1182   cmd = ["gnt-cluster", "master-failover"]
1183   try:
1184     _AssertDrainFile(failovermaster)
1185     AssertCommand(cmd, node=failovermaster)
1186     _AssertDrainFile(master, fail=True)
1187     _AssertDrainFile(failovermaster, fail=True)
1188
1189     # Back to original master node
1190     AssertCommand(cmd, node=master)
1191   finally:
1192     failovermaster.Release()
1193
1194   # Ensure queue is not drained
1195   for node in [master, failovermaster]:
1196     _AssertDrainFile(node, fail=True)
1197
1198
1199 def TestClusterCopyfile():
1200   """gnt-cluster copyfile"""
1201   master = qa_config.GetMasterNode()
1202
1203   uniqueid = utils.NewUUID()
1204
1205   # Create temporary file
1206   f = tempfile.NamedTemporaryFile()
1207   f.write(uniqueid)
1208   f.flush()
1209   f.seek(0)
1210
1211   # Upload file to master node
1212   testname = qa_utils.UploadFile(master.primary, f.name)
1213   try:
1214     # Copy file to all nodes
1215     AssertCommand(["gnt-cluster", "copyfile", testname])
1216     _CheckFileOnAllNodes(testname, uniqueid)
1217   finally:
1218     _RemoveFileFromAllNodes(testname)
1219
1220
1221 def TestClusterCommand():
1222   """gnt-cluster command"""
1223   uniqueid = utils.NewUUID()
1224   rfile = "/tmp/gnt%s" % utils.NewUUID()
1225   rcmd = utils.ShellQuoteArgs(["echo", "-n", uniqueid])
1226   cmd = utils.ShellQuoteArgs(["gnt-cluster", "command",
1227                               "%s >%s" % (rcmd, rfile)])
1228
1229   try:
1230     AssertCommand(cmd)
1231     _CheckFileOnAllNodes(rfile, uniqueid)
1232   finally:
1233     _RemoveFileFromAllNodes(rfile)
1234
1235
1236 def TestClusterDestroy():
1237   """gnt-cluster destroy"""
1238   AssertCommand(["gnt-cluster", "destroy", "--yes-do-it"])
1239
1240
1241 def TestClusterRepairDiskSizes():
1242   """gnt-cluster repair-disk-sizes"""
1243   AssertCommand(["gnt-cluster", "repair-disk-sizes"])
1244
1245
1246 def TestSetExclStorCluster(newvalue):
1247   """Set the exclusive_storage node parameter at the cluster level.
1248
1249   @type newvalue: bool
1250   @param newvalue: New value of exclusive_storage
1251   @rtype: bool
1252   @return: The old value of exclusive_storage
1253
1254   """
1255   es_path = ["Default node parameters", "exclusive_storage"]
1256   oldvalue = _GetClusterField(es_path)
1257   AssertCommand(["gnt-cluster", "modify", "--node-parameters",
1258                  "exclusive_storage=%s" % newvalue])
1259   effvalue = _GetClusterField(es_path)
1260   if effvalue != newvalue:
1261     raise qa_error.Error("exclusive_storage has the wrong value: %s instead"
1262                          " of %s" % (effvalue, newvalue))
1263   qa_config.SetExclusiveStorage(newvalue)
1264   return oldvalue
1265
1266
1267 def TestExclStorSharedPv(node):
1268   """cluster-verify reports LVs that share the same PV with exclusive_storage.
1269
1270   """
1271   vgname = qa_config.get("vg-name", constants.DEFAULT_VG)
1272   lvname1 = _QA_LV_PREFIX + "vol1"
1273   lvname2 = _QA_LV_PREFIX + "vol2"
1274   node_name = node.primary
1275   AssertCommand(["lvcreate", "-L1G", "-n", lvname1, vgname], node=node_name)
1276   AssertClusterVerify(fail=True, errors=[constants.CV_ENODEORPHANLV])
1277   AssertCommand(["lvcreate", "-L1G", "-n", lvname2, vgname], node=node_name)
1278   AssertClusterVerify(fail=True, errors=[constants.CV_ENODELVM,
1279                                          constants.CV_ENODEORPHANLV])
1280   AssertCommand(["lvremove", "-f", "/".join([vgname, lvname1])], node=node_name)
1281   AssertCommand(["lvremove", "-f", "/".join([vgname, lvname2])], node=node_name)
1282   AssertClusterVerify()