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