4 # Copyright (C) 2007, 2010, 2011, 2012, 2013 Google Inc.
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.
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.
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
22 """Cluster related QA tests.
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
42 from qa_utils import AssertEqual, AssertCommand, GetCommandOutput
45 # Prefix for LVM volumes created by QA code during tests
48 #: cluster verify command
49 _CLUSTER_VERIFY = ["gnt-cluster", "verify"]
52 def _RemoveFileFromAllNodes(filename):
53 """Removes a file from all nodes.
56 for node in qa_config.get("nodes"):
57 AssertCommand(["rm", "-f", filename], node=node)
60 def _CheckFileOnAllNodes(filename, content):
61 """Verifies the content of the given file on all nodes.
64 cmd = utils.ShellQuoteArgs(["cat", filename])
65 for node in qa_config.get("nodes"):
66 AssertEqual(qa_utils.GetCommandOutput(node.primary, cmd), content)
69 def _GetClusterField(field_path):
70 """Get the value of a cluster field.
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
79 assert isinstance(field_path, list)
81 ret = qa_utils.GetObjectInfo(["gnt-cluster", "info"])
82 for key in field_path:
87 # Cluster-verify errors (date, "ERROR", then error code)
88 _CVERROR_RE = re.compile(r"^[\w\s:]+\s+- (ERROR|WARNING):([A-Z0-9_-]+):")
91 def _GetCVErrorCodes(cvout):
94 for l in cvout.splitlines():
95 m = _CVERROR_RE.match(l)
101 elif etype == "WARNING":
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)))
114 def AssertClusterVerify(fail=False, errors=None, warnings=None):
115 """Run cluster-verify and check the result
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.
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)
134 _CheckVerifyErrors(act_errs, errors, "error")
136 _CheckVerifyErrors(act_warns, warnings, "warning")
138 AssertCommand(cvcmd, fail=fail, node=mnode)
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",
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)
155 def TestClusterInit(rapi_user, rapi_secret):
156 """gnt-cluster init"""
157 master = qa_config.GetMasterNode()
159 rapi_users_path = qa_utils.MakeNodePath(master, pathutils.RAPI_USERS_FILE)
160 rapi_dir = os.path.dirname(rapi_users_path)
162 # First create the RAPI credentials
163 fh = tempfile.NamedTemporaryFile()
165 fh.write("%s %s write\n" % (rapi_user, rapi_secret))
168 tmpru = qa_utils.UploadFile(master.primary, fh.name)
170 AssertCommand(["mkdir", "-p", rapi_dir])
171 AssertCommand(["mv", tmpru, rapi_users_path])
173 AssertCommand(["rm", "-f", tmpru])
178 enabled_disk_templates = qa_config.GetEnabledDiskTemplates()
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),
186 if constants.DT_FILE in enabled_disk_templates:
188 "--file-storage-dir=%s" %
189 qa_config.get("default-file-storage-dir",
190 pathutils.DEFAULT_FILE_STORAGE_DIR))
192 for spec_type in ("mem-size", "disk-size", "disk-count", "cpu-count",
194 for spec_val in ("min", "max", "std"):
195 spec = qa_config.get("ispec_%s_%s" %
196 (spec_type.replace("-", "_"), spec_val), None)
198 cmd.append("--specs-%s=%s=%d" % (spec_type, spec_val, spec))
201 cmd.append("--secondary-ip=%s" % master.secondary)
203 if utils.IsLvmEnabled(qa_config.GetEnabledDiskTemplates()):
204 vgname = qa_config.get("vg-name", constants.DEFAULT_VG)
206 cmd.append("--vg-name=%s" % vgname)
208 raise qa_error.Error("Please specify a volume group if you enable"
209 " lvm-based disk templates in the QA.")
211 master_netdev = qa_config.get("master-netdev", None)
213 cmd.append("--master-netdev=%s" % master_netdev)
215 nicparams = qa_config.get("default-nicparams", None)
217 cmd.append("--nic-parameters=%s" %
218 ",".join(utils.FormatKeyValue(nicparams)))
220 # Cluster value of the exclusive-storage node parameter
221 e_s = qa_config.get("exclusive-storage")
223 cmd.extend(["--node-parameters", "exclusive_storage=%s" % e_s])
226 qa_config.SetExclusiveStorage(e_s)
228 extra_args = qa_config.get("cluster-init-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)
238 cmd.append(qa_config.get("name"))
242 cmd = ["gnt-cluster", "modify"]
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", "")
251 cmd.extend(["-B", bep])
257 osp = qa_config.get("os-parameters", {})
258 for k, v in osp.items():
259 AssertCommand(["gnt-os", "modify", "-O", v, k])
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])
268 def TestClusterRename():
269 """gnt-cluster rename"""
270 cmd = ["gnt-cluster", "rename", "-f"]
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')
279 cmd + [rename_target],
281 cmd + [original_name],
287 def TestClusterOob():
288 """out-of-band framework"""
289 oob_path_exists = "/tmp/ganeti-qa-oob-does-exist-%s" % utils.NewUUID()
291 AssertCommand(_CLUSTER_VERIFY)
292 AssertCommand(["gnt-cluster", "modify", "--node-parameters",
293 "oob_program=/tmp/ganeti-qa-oob-does-not-exist-%s" %
296 AssertCommand(_CLUSTER_VERIFY, fail=True)
298 AssertCommand(["touch", oob_path_exists])
299 AssertCommand(["chmod", "0400", oob_path_exists])
300 AssertCommand(["gnt-cluster", "copyfile", oob_path_exists])
303 AssertCommand(["gnt-cluster", "modify", "--node-parameters",
304 "oob_program=%s" % oob_path_exists])
306 AssertCommand(_CLUSTER_VERIFY, fail=True)
308 AssertCommand(["chmod", "0500", oob_path_exists])
309 AssertCommand(["gnt-cluster", "copyfile", oob_path_exists])
311 AssertCommand(_CLUSTER_VERIFY)
313 AssertCommand(["gnt-cluster", "command", "rm", oob_path_exists])
315 AssertCommand(["gnt-cluster", "modify", "--node-parameters",
319 def TestClusterEpo():
320 """gnt-cluster epo"""
321 master = qa_config.GetMasterNode()
323 # Assert that OOB is unavailable for all nodes
324 result_output = GetCommandOutput(master.primary,
325 "gnt-node list --verbose --no-headers -o"
327 AssertEqual(compat.all(powered == "(unavail)"
328 for powered in result_output.splitlines()), True)
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)
335 # Unless --all is given master is not allowed to be in the list
336 AssertCommand(["gnt-cluster", "epo", "-f", master.primary], fail=True)
338 # This shouldn't fail
339 AssertCommand(["gnt-cluster", "epo", "-f", "--all"])
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)
348 # Now start everything again
349 AssertCommand(["gnt-cluster", "epo", "--on", "-f", "--all"])
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)
358 def TestClusterVerify():
359 """gnt-cluster verify"""
360 AssertCommand(_CLUSTER_VERIFY)
361 AssertCommand(["gnt-cluster", "verify-disks"])
364 def TestClusterVerifyDisksBrokenDRBD(instance, inst_nodes):
365 """gnt-cluster verify-disks with broken DRBD"""
366 qa_daemon.TestPauseWatcher()
369 info = qa_instance.GetInstanceInfo(instance.name)
370 snode = inst_nodes[1]
371 for idx, minor in enumerate(info["drbd-minors"][snode.primary]):
374 "(drbdsetup %d down >/dev/null 2>&1;" \
375 " drbdsetup down resource%d >/dev/null 2>&1) || /bin/true" % \
379 "(drbdsetup %d detach >/dev/null 2>&1;" \
380 " drbdsetup detach %d >/dev/null 2>&1) || /bin/true" % \
382 AssertCommand(break_drbd_cmd, node=snode)
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)
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)
397 AssertCommand(_CLUSTER_VERIFY)
399 qa_daemon.TestResumeWatcher()
403 """gnt-debug test-jobqueue"""
404 AssertCommand(["gnt-debug", "test-jobqueue"])
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"])
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):
420 vgname = qa_config.get("vg-name", constants.DEFAULT_VG)
421 lvname = _QA_LV_PREFIX + "test"
422 lvfullname = "/".join([vgname, lvname])
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),
439 AssertCommand(cmd, fail=fail)
442 def TestClusterModifyEmpty():
443 """gnt-cluster modify"""
444 AssertCommand(["gnt-cluster", "modify"], fail=True)
447 def TestClusterModifyDisk():
448 """gnt-cluster modify -D"""
449 for param in _FAIL_PARAMS:
450 AssertCommand(["gnt-cluster", "modify", "-D", param], fail=True)
453 def _GetOtherEnabledDiskTemplate(undesired_disk_templates,
454 enabled_disk_templates):
455 """Returns one template that is not in the undesired set.
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
461 @type enabled_disk_templates: list of string
462 @param enabled_disk_templates: list of enabled disk templates (in QA)
465 desired_templates = list(set(enabled_disk_templates)
466 - set(undesired_disk_templates))
467 if desired_templates:
468 template = desired_templates[0]
470 # If no desired disk template is available for QA, choose 'diskless' and
472 template = constants.ST_DISKLESS
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.
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
486 @type default_dir: string
487 @param default_dir: default directory, if the QA config does not specify
489 @type option_name: string
490 @param option_name: name of the option of 'gnt-cluster modify' to
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):
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)
504 file_storage_dir = qa_config.get(dir_config_key, default_dir)
505 invalid_file_storage_dir = "/boot/"
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
517 (True, ["gnt-cluster", "verify"]),
518 # unsetting the storage dir while file storage is enabled
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
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)])
542 AssertCommand(cmd, fail=fail)
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,
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")
561 def TestClusterModifyDiskTemplates():
562 """gnt-cluster modify --enabled-disk-templates=..."""
563 enabled_disk_templates = qa_config.GetEnabledDiskTemplates()
564 default_disk_template = qa_config.GetDefaultDiskTemplate()
566 _TestClusterModifyDiskTemplatesArguments(default_disk_template)
567 _TestClusterModifyDiskTemplatesDrbdHelper(enabled_disk_templates)
568 _TestClusterModifyDiskTemplatesVgName(enabled_disk_templates)
570 _RestoreEnabledDiskTemplates()
571 nodes = qa_config.AcquireManyNodes(2)
573 instance_template = enabled_disk_templates[0]
574 instance = qa_instance.CreateInstanceByDiskTemplate(nodes, instance_template)
576 _TestClusterModifyUnusedDiskTemplate(instance_template)
577 _TestClusterModifyUsedDiskTemplate(instance_template,
578 enabled_disk_templates)
580 qa_instance.TestInstanceRemove(instance)
581 _RestoreEnabledDiskTemplates()
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
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),
597 if utils.IsLvmEnabled(qa_config.GetEnabledDiskTemplates()):
598 vgname = qa_config.get("vg-name", constants.DEFAULT_VG)
599 cmd.append("--vg-name=%s" % vgname)
601 AssertCommand(cmd, fail=False)
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
610 _RestoreEnabledDiskTemplates()
612 if constants.DT_DRBD8 not in enabled_disk_templates:
614 if constants.DT_PLAIN not in enabled_disk_templates:
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),
650 AssertCommand(command, fail=fail)
651 _RestoreEnabledDiskTemplates()
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
660 _RestoreEnabledDiskTemplates()
663 AssertCommand(["gnt-cluster", "modify",
664 "--enabled-disk-templates=pinkbunny"],
667 # duplicate entries do no harm
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],
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.
682 if not utils.IsLvmEnabled(enabled_disk_templates):
683 # These tests only make sense if lvm is enabled for QA
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]
692 vgname = qa_config.get("vg-name", constants.DEFAULT_VG)
694 # Clean start: unset volume group name, disable lvm storage
696 ["gnt-cluster", "modify",
697 "--enabled-disk-templates=%s" % non_lvm_template,
698 "--ipolicy-disk-templates=%s" % non_lvm_template,
702 # Try to enable lvm, when no volume group is given
704 ["gnt-cluster", "modify",
705 "--enabled-disk-templates=%s" % lvm_template,
706 "--ipolicy-disk-templates=%s" % lvm_template],
709 # Set volume group, with lvm still disabled: just a warning
710 AssertCommand(["gnt-cluster", "modify", "--vg-name=%s" % vgname], fail=False)
712 # Try unsetting vg name and enabling lvm at the same time
714 ["gnt-cluster", "modify",
715 "--enabled-disk-templates=%s" % lvm_template,
716 "--ipolicy-disk-templates=%s" % lvm_template,
720 # Enable lvm with vg name present
722 ["gnt-cluster", "modify",
723 "--enabled-disk-templates=%s" % lvm_template,
724 "--ipolicy-disk-templates=%s" % lvm_template],
727 # Try unsetting vg name with lvm still enabled
728 AssertCommand(["gnt-cluster", "modify", "--vg-name="], fail=True)
730 # Disable lvm with vg name still set
732 ["gnt-cluster", "modify",
733 "--enabled-disk-templates=%s" % non_lvm_template,
734 "--ipolicy-disk-templates=%s" % non_lvm_template,
738 # Try unsetting vg name with lvm disabled
739 AssertCommand(["gnt-cluster", "modify", "--vg-name="], fail=False)
741 # Set vg name and enable lvm at the same time
743 ["gnt-cluster", "modify",
744 "--enabled-disk-templates=%s" % lvm_template,
745 "--ipolicy-disk-templates=%s" % lvm_template,
746 "--vg-name=%s" % vgname],
749 # Unset vg name and disable lvm at the same time
751 ["gnt-cluster", "modify",
752 "--enabled-disk-templates=%s" % non_lvm_template,
753 "--ipolicy-disk-templates=%s" % non_lvm_template,
757 _RestoreEnabledDiskTemplates()
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.
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]))
775 ["gnt-cluster", "modify",
776 "--enabled-disk-templates=%s" % ",".join(new_disk_templates),
777 "--ipolicy-disk-templates=%s" % ",".join(new_disk_templates)],
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()))
789 ["gnt-cluster", "modify",
790 "--enabled-disk-templates=%s" % ",".join(all_disk_templates),
791 "--ipolicy-disk-templates=%s" % ",".join(all_disk_templates)],
793 new_disk_templates = [instance_template]
795 ["gnt-cluster", "modify",
796 "--enabled-disk-templates=%s" % ",".join(new_disk_templates),
797 "--ipolicy-disk-templates=%s" % ",".join(new_disk_templates)],
801 def TestClusterModifyBe():
802 """gnt-cluster modify -B"""
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$'"]),
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$'"]),
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$'"]),
829 AssertCommand(cmd, fail=fail)
831 # redo the original-requested BE parameters, if any
832 bep = qa_config.get("backend-parameters", "")
834 AssertCommand(["gnt-cluster", "modify", "-B", bep])
837 def _GetClusterIPolicy():
838 """Return the run-time values of the cluster-level instance policy.
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)
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)
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)
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])
869 # Restore the old value
872 for (good, val) in test_values:
873 cmd = basecmd + ["--ipolicy-%s=%s" % (par, val)]
874 AssertCommand(cmd, fail=not good)
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():
885 AssertEqual(eff_policy[p], old_policy[p])
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)):
892 # Disk templates are treated slightly differently
893 par = "disk-templates"
894 disp_str = "allowed disk templates"
895 curr_val = old_policy[disp_str]
897 (True, constants.DT_PLAIN),
898 (True, "%s,%s" % (constants.DT_PLAIN, constants.DT_DRBD8)),
899 (False, "thisisnotadisktemplate"),
901 # Restore the old value
902 (True, curr_val.replace(" ", "")),
904 for (good, val) in test_values:
905 cmd = basecmd + ["--ipolicy-%s=%s" % (par, val)]
906 AssertCommand(cmd, fail=not good)
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():
917 AssertEqual(eff_policy[p], old_policy[p])
920 def TestClusterSetISpecs(new_specs=None, diff_specs=None, fail=False,
922 """Change instance specs.
924 At most one of new_specs or diff_specs can be specified.
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
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}
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)
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
968 # This is to restore the old values
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])
974 for (good, mn, st, mx) in test_values:
976 constants.ISPECS_MINMAX: [{
977 constants.ISPECS_MIN: {par: mn},
978 constants.ISPECS_MAX: {par: mx}
980 constants.ISPECS_STD: {par: st}
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)
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)
1003 def TestClusterInfo():
1004 """gnt-cluster info"""
1005 AssertCommand(["gnt-cluster", "info"])
1008 def TestClusterRedistConf():
1009 """gnt-cluster redist-conf"""
1010 AssertCommand(["gnt-cluster", "redist-conf"])
1013 def TestClusterGetmaster():
1014 """gnt-cluster getmaster"""
1015 AssertCommand(["gnt-cluster", "getmaster"])
1018 def TestClusterVersion():
1019 """gnt-cluster version"""
1020 AssertCommand(["gnt-cluster", "version"])
1023 def TestClusterRenewCrypto():
1024 """gnt-cluster renew-crypto"""
1025 master = qa_config.GetMasterNode()
1027 # Conflicting options
1028 cmd = ["gnt-cluster", "renew-crypto", "--force",
1029 "--new-cluster-certificate", "--new-confd-hmac-key"]
1031 ["--new-rapi-certificate", "--rapi-certificate=/dev/null"],
1032 ["--new-cluster-domain-secret", "--cluster-domain-secret=/dev/null"],
1034 for i in conflicting:
1035 AssertCommand(cmd + i, fail=True)
1037 # Invalid RAPI certificate
1038 cmd = ["gnt-cluster", "renew-crypto", "--force",
1039 "--rapi-certificate=/dev/null"]
1040 AssertCommand(cmd, fail=True)
1042 rapi_cert_backup = qa_utils.BackupFile(master.primary,
1043 pathutils.RAPI_CERT_FILE)
1045 # Custom RAPI certificate
1046 fh = tempfile.NamedTemporaryFile()
1048 # Ensure certificate doesn't cause "gnt-cluster verify" to complain
1049 validity = constants.SSL_CERT_EXPIRATION_WARN * 3
1051 utils.GenerateSelfSignedSslCert(fh.name, validity=validity)
1053 tmpcert = qa_utils.UploadFile(master.primary, fh.name)
1055 AssertCommand(["gnt-cluster", "renew-crypto", "--force",
1056 "--rapi-certificate=%s" % tmpcert])
1058 AssertCommand(["rm", "-f", tmpcert])
1060 # Custom cluster domain secret
1061 cds_fh = tempfile.NamedTemporaryFile()
1062 cds_fh.write(utils.GenerateSecret())
1066 tmpcds = qa_utils.UploadFile(master.primary, cds_fh.name)
1068 AssertCommand(["gnt-cluster", "renew-crypto", "--force",
1069 "--cluster-domain-secret=%s" % tmpcds])
1071 AssertCommand(["rm", "-f", tmpcds])
1074 AssertCommand(["gnt-cluster", "renew-crypto", "--force",
1075 "--new-cluster-certificate", "--new-confd-hmac-key",
1076 "--new-rapi-certificate", "--new-cluster-domain-secret"])
1078 # Restore RAPI certificate
1079 AssertCommand(["gnt-cluster", "renew-crypto", "--force",
1080 "--rapi-certificate=%s" % rapi_cert_backup])
1082 AssertCommand(["rm", "-f", rapi_cert_backup])
1085 def TestClusterBurnin():
1087 master = qa_config.GetMasterNode()
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)
1097 # Get as many instances as we need
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."
1107 if len(instances) < 1:
1108 raise qa_error.Error("Burnin needs at least one instance")
1110 script = qa_utils.UploadFile(master.primary, "../tools/burnin")
1112 disks = qa_config.GetDiskOptions()
1115 "PYTHONPATH=%s" % _constants.VERSIONEDSHAREDIR,
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]
1124 cmd.append("--parallel")
1125 cmd.append("--early-release")
1127 cmd.append("--http-check")
1129 cmd.append("--rename=%s" % do_rename)
1131 cmd.append("--no-reboot")
1133 cmd.append("--reboot-types=%s" % ",".join(reboot_types))
1134 cmd += [inst.name for inst in instances]
1137 AssertCommand(["rm", "-f", script])
1140 for inst in instances:
1144 def TestClusterMasterFailover():
1145 """gnt-cluster master-failover"""
1146 master = qa_config.GetMasterNode()
1147 failovermaster = qa_config.AcquireNode(exclude=master)
1149 cmd = ["gnt-cluster", "master-failover"]
1151 AssertCommand(cmd, node=failovermaster)
1152 # Back to original master node
1153 AssertCommand(cmd, node=master)
1155 failovermaster.Release()
1158 def _NodeQueueDrainFile(node):
1159 """Returns path to queue drain file for a node.
1162 return qa_utils.MakeNodePath(node, pathutils.JOB_QUEUE_DRAIN_FILE)
1165 def _AssertDrainFile(node, **kwargs):
1166 """Checks for the queue drain file.
1169 AssertCommand(["test", "-f", _NodeQueueDrainFile(node)], node=node, **kwargs)
1172 def TestClusterMasterFailoverWithDrainedQueue():
1173 """gnt-cluster master-failover with drained queue"""
1174 master = qa_config.GetMasterNode()
1175 failovermaster = qa_config.AcquireNode(exclude=master)
1177 # Ensure queue is not drained
1178 for node in [master, failovermaster]:
1179 _AssertDrainFile(node, fail=True)
1181 # Drain queue on failover master
1182 AssertCommand(["touch", _NodeQueueDrainFile(failovermaster)],
1183 node=failovermaster)
1185 cmd = ["gnt-cluster", "master-failover"]
1187 _AssertDrainFile(failovermaster)
1188 AssertCommand(cmd, node=failovermaster)
1189 _AssertDrainFile(master, fail=True)
1190 _AssertDrainFile(failovermaster, fail=True)
1192 # Back to original master node
1193 AssertCommand(cmd, node=master)
1195 failovermaster.Release()
1197 # Ensure queue is not drained
1198 for node in [master, failovermaster]:
1199 _AssertDrainFile(node, fail=True)
1202 def TestClusterCopyfile():
1203 """gnt-cluster copyfile"""
1204 master = qa_config.GetMasterNode()
1206 uniqueid = utils.NewUUID()
1208 # Create temporary file
1209 f = tempfile.NamedTemporaryFile()
1214 # Upload file to master node
1215 testname = qa_utils.UploadFile(master.primary, f.name)
1217 # Copy file to all nodes
1218 AssertCommand(["gnt-cluster", "copyfile", testname])
1219 _CheckFileOnAllNodes(testname, uniqueid)
1221 _RemoveFileFromAllNodes(testname)
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)])
1234 _CheckFileOnAllNodes(rfile, uniqueid)
1236 _RemoveFileFromAllNodes(rfile)
1239 def TestClusterDestroy():
1240 """gnt-cluster destroy"""
1241 AssertCommand(["gnt-cluster", "destroy", "--yes-do-it"])
1244 def TestClusterRepairDiskSizes():
1245 """gnt-cluster repair-disk-sizes"""
1246 AssertCommand(["gnt-cluster", "repair-disk-sizes"])
1249 def TestSetExclStorCluster(newvalue):
1250 """Set the exclusive_storage node parameter at the cluster level.
1252 @type newvalue: bool
1253 @param newvalue: New value of exclusive_storage
1255 @return: The old value of exclusive_storage
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)
1270 def TestExclStorSharedPv(node):
1271 """cluster-verify reports LVs that share the same PV with exclusive_storage.
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()