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 compat
32 from ganeti import utils
33 from ganeti import pathutils
39 from qa_utils import AssertEqual, AssertCommand, GetCommandOutput
42 # Prefix for LVM volumes created by QA code during tests
45 #: cluster verify command
46 _CLUSTER_VERIFY = ["gnt-cluster", "verify"]
49 def _RemoveFileFromAllNodes(filename):
50 """Removes a file from all nodes.
53 for node in qa_config.get("nodes"):
54 AssertCommand(["rm", "-f", filename], node=node)
57 def _CheckFileOnAllNodes(filename, content):
58 """Verifies the content of the given file on all nodes.
61 cmd = utils.ShellQuoteArgs(["cat", filename])
62 for node in qa_config.get("nodes"):
63 AssertEqual(qa_utils.GetCommandOutput(node.primary, cmd), content)
66 def _GetClusterField(field_path):
67 """Get the value of a cluster field.
69 @type field_path: list of strings
70 @param field_path: Names of the groups/fields to navigate to get the desired
71 value, e.g. C{["Default node parameters", "oob_program"]}
72 @return: The effective value of the field (the actual type depends on the
76 assert isinstance(field_path, list)
78 ret = qa_utils.GetObjectInfo(["gnt-cluster", "info"])
79 for key in field_path:
84 # Cluster-verify errors (date, "ERROR", then error code)
85 _CVERROR_RE = re.compile(r"^[\w\s:]+\s+- (ERROR|WARNING):([A-Z0-9_-]+):")
88 def _GetCVErrorCodes(cvout):
91 for l in cvout.splitlines():
92 m = _CVERROR_RE.match(l)
98 elif etype == "WARNING":
103 def _CheckVerifyErrors(actual, expected, etype):
104 exp_codes = compat.UniqueFrozenset(e for (_, e, _) in expected)
105 if not actual.issuperset(exp_codes):
106 missing = exp_codes.difference(actual)
107 raise qa_error.Error("Cluster-verify didn't return these expected"
108 " %ss: %s" % (etype, utils.CommaJoin(missing)))
111 def AssertClusterVerify(fail=False, errors=None, warnings=None):
112 """Run cluster-verify and check the result
115 @param fail: if cluster-verify is expected to fail instead of succeeding
116 @type errors: list of tuples
117 @param errors: List of CV_XXX errors that are expected; if specified, all the
118 errors listed must appear in cluster-verify output. A non-empty value
119 implies C{fail=True}.
120 @type warnings: list of tuples
121 @param warnings: Same as C{errors} but for warnings.
124 cvcmd = "gnt-cluster verify"
125 mnode = qa_config.GetMasterNode()
126 if errors or warnings:
127 cvout = GetCommandOutput(mnode.primary, cvcmd + " --error-codes",
128 fail=(fail or errors))
129 (act_errs, act_warns) = _GetCVErrorCodes(cvout)
131 _CheckVerifyErrors(act_errs, errors, "error")
133 _CheckVerifyErrors(act_warns, warnings, "warning")
135 AssertCommand(cvcmd, fail=fail, node=mnode)
138 # data for testing failures due to bad keys/values for disk parameters
139 _FAIL_PARAMS = ["nonexistent:resync-rate=1",
140 "drbd:nonexistent=1",
141 "drbd:resync-rate=invalid",
145 def TestClusterInitDisk():
146 """gnt-cluster init -D"""
147 name = qa_config.get("name")
148 for param in _FAIL_PARAMS:
149 AssertCommand(["gnt-cluster", "init", "-D", param, name], fail=True)
152 def TestClusterInit(rapi_user, rapi_secret):
153 """gnt-cluster init"""
154 master = qa_config.GetMasterNode()
156 rapi_users_path = qa_utils.MakeNodePath(master, pathutils.RAPI_USERS_FILE)
157 rapi_dir = os.path.dirname(rapi_users_path)
159 # First create the RAPI credentials
160 fh = tempfile.NamedTemporaryFile()
162 fh.write("%s %s write\n" % (rapi_user, rapi_secret))
165 tmpru = qa_utils.UploadFile(master.primary, fh.name)
167 AssertCommand(["mkdir", "-p", rapi_dir])
168 AssertCommand(["mv", tmpru, rapi_users_path])
170 AssertCommand(["rm", "-f", tmpru])
176 "gnt-cluster", "init",
177 "--primary-ip-version=%d" % qa_config.get("primary_ip_version", 4),
178 "--enabled-hypervisors=%s" % ",".join(qa_config.GetEnabledHypervisors()),
179 "--enabled-storage-types=%s" %
180 ",".join(qa_config.GetEnabledStorageTypes())
183 for spec_type in ("mem-size", "disk-size", "disk-count", "cpu-count",
185 for spec_val in ("min", "max", "std"):
186 spec = qa_config.get("ispec_%s_%s" %
187 (spec_type.replace("-", "_"), spec_val), None)
189 cmd.append("--specs-%s=%s=%d" % (spec_type, spec_val, spec))
192 cmd.append("--secondary-ip=%s" % master.secondary)
194 vgname = qa_config.get("vg-name", None)
196 cmd.append("--vg-name=%s" % vgname)
198 master_netdev = qa_config.get("master-netdev", None)
200 cmd.append("--master-netdev=%s" % master_netdev)
202 nicparams = qa_config.get("default-nicparams", None)
204 cmd.append("--nic-parameters=%s" %
205 ",".join(utils.FormatKeyValue(nicparams)))
207 # Cluster value of the exclusive-storage node parameter
208 e_s = qa_config.get("exclusive-storage")
210 cmd.extend(["--node-parameters", "exclusive_storage=%s" % e_s])
213 qa_config.SetExclusiveStorage(e_s)
215 extra_args = qa_config.get("cluster-init-args")
217 cmd.extend(extra_args)
219 cmd.append(qa_config.get("name"))
223 cmd = ["gnt-cluster", "modify"]
225 # hypervisor parameter modifications
226 hvp = qa_config.get("hypervisor-parameters", {})
227 for k, v in hvp.items():
228 cmd.extend(["-H", "%s:%s" % (k, v)])
229 # backend parameter modifications
230 bep = qa_config.get("backend-parameters", "")
232 cmd.extend(["-B", bep])
238 osp = qa_config.get("os-parameters", {})
239 for k, v in osp.items():
240 AssertCommand(["gnt-os", "modify", "-O", v, k])
242 # OS hypervisor parameters
243 os_hvp = qa_config.get("os-hvp", {})
244 for os_name in os_hvp:
245 for hv, hvp in os_hvp[os_name].items():
246 AssertCommand(["gnt-os", "modify", "-H", "%s:%s" % (hv, hvp), os_name])
249 def TestClusterRename():
250 """gnt-cluster rename"""
251 cmd = ["gnt-cluster", "rename", "-f"]
253 original_name = qa_config.get("name")
254 rename_target = qa_config.get("rename", None)
255 if rename_target is None:
256 print qa_utils.FormatError('"rename" entry is missing')
260 cmd + [rename_target],
262 cmd + [original_name],
268 def TestClusterOob():
269 """out-of-band framework"""
270 oob_path_exists = "/tmp/ganeti-qa-oob-does-exist-%s" % utils.NewUUID()
272 AssertCommand(_CLUSTER_VERIFY)
273 AssertCommand(["gnt-cluster", "modify", "--node-parameters",
274 "oob_program=/tmp/ganeti-qa-oob-does-not-exist-%s" %
277 AssertCommand(_CLUSTER_VERIFY, fail=True)
279 AssertCommand(["touch", oob_path_exists])
280 AssertCommand(["chmod", "0400", oob_path_exists])
281 AssertCommand(["gnt-cluster", "copyfile", oob_path_exists])
284 AssertCommand(["gnt-cluster", "modify", "--node-parameters",
285 "oob_program=%s" % oob_path_exists])
287 AssertCommand(_CLUSTER_VERIFY, fail=True)
289 AssertCommand(["chmod", "0500", oob_path_exists])
290 AssertCommand(["gnt-cluster", "copyfile", oob_path_exists])
292 AssertCommand(_CLUSTER_VERIFY)
294 AssertCommand(["gnt-cluster", "command", "rm", oob_path_exists])
296 AssertCommand(["gnt-cluster", "modify", "--node-parameters",
300 def TestClusterEpo():
301 """gnt-cluster epo"""
302 master = qa_config.GetMasterNode()
304 # Assert that OOB is unavailable for all nodes
305 result_output = GetCommandOutput(master.primary,
306 "gnt-node list --verbose --no-headers -o"
308 AssertEqual(compat.all(powered == "(unavail)"
309 for powered in result_output.splitlines()), True)
312 AssertCommand(["gnt-cluster", "epo", "--groups", "--all"], fail=True)
313 # --all doesn't expect arguments
314 AssertCommand(["gnt-cluster", "epo", "--all", "some_arg"], fail=True)
316 # Unless --all is given master is not allowed to be in the list
317 AssertCommand(["gnt-cluster", "epo", "-f", master.primary], fail=True)
319 # This shouldn't fail
320 AssertCommand(["gnt-cluster", "epo", "-f", "--all"])
322 # All instances should have been stopped now
323 result_output = GetCommandOutput(master.primary,
324 "gnt-instance list --no-headers -o status")
325 # ERROR_down because the instance is stopped but not recorded as such
326 AssertEqual(compat.all(status == "ERROR_down"
327 for status in result_output.splitlines()), True)
329 # Now start everything again
330 AssertCommand(["gnt-cluster", "epo", "--on", "-f", "--all"])
332 # All instances should have been started now
333 result_output = GetCommandOutput(master.primary,
334 "gnt-instance list --no-headers -o status")
335 AssertEqual(compat.all(status == "running"
336 for status in result_output.splitlines()), True)
339 def TestClusterVerify():
340 """gnt-cluster verify"""
341 AssertCommand(_CLUSTER_VERIFY)
342 AssertCommand(["gnt-cluster", "verify-disks"])
346 """gnt-debug test-jobqueue"""
347 AssertCommand(["gnt-debug", "test-jobqueue"])
351 """gnt-debug delay"""
352 AssertCommand(["gnt-debug", "delay", "1"])
353 AssertCommand(["gnt-debug", "delay", "--no-master", "1"])
354 AssertCommand(["gnt-debug", "delay", "--no-master",
355 "-n", node.primary, "1"])
358 def TestClusterReservedLvs():
359 """gnt-cluster reserved lvs"""
360 vgname = qa_config.get("vg-name", constants.DEFAULT_VG)
361 lvname = _QA_LV_PREFIX + "test"
362 lvfullname = "/".join([vgname, lvname])
364 (False, _CLUSTER_VERIFY),
365 (False, ["gnt-cluster", "modify", "--reserved-lvs", ""]),
366 (False, ["lvcreate", "-L1G", "-n", lvname, vgname]),
367 (True, _CLUSTER_VERIFY),
368 (False, ["gnt-cluster", "modify", "--reserved-lvs",
369 "%s,.*/other-test" % lvfullname]),
370 (False, _CLUSTER_VERIFY),
371 (False, ["gnt-cluster", "modify", "--reserved-lvs",
372 ".*/%s.*" % _QA_LV_PREFIX]),
373 (False, _CLUSTER_VERIFY),
374 (False, ["gnt-cluster", "modify", "--reserved-lvs", ""]),
375 (True, _CLUSTER_VERIFY),
376 (False, ["lvremove", "-f", lvfullname]),
377 (False, _CLUSTER_VERIFY),
379 AssertCommand(cmd, fail=fail)
382 def TestClusterModifyEmpty():
383 """gnt-cluster modify"""
384 AssertCommand(["gnt-cluster", "modify"], fail=True)
387 def TestClusterModifyDisk():
388 """gnt-cluster modify -D"""
389 for param in _FAIL_PARAMS:
390 AssertCommand(["gnt-cluster", "modify", "-D", param], fail=True)
393 def TestClusterModifyStorageTypes():
394 """gnt-cluster modify --enabled-storage-types=..."""
395 default_storage_type = qa_config.GetDefaultStorageType()
397 ["gnt-cluster", "modify",
398 "--enabled-storage-types=%s" % default_storage_type],
400 AssertCommand(["gnt-cluster", "info"])
402 ["gnt-cluster", "modify",
403 "--enabled-storage-types=%s" %
404 ",".join(qa_config.GetEnabledStorageTypes())],
406 AssertCommand(["gnt-cluster", "info"])
408 AssertCommand(["gnt-cluster", "modify",
409 "--enabled-storage-types=pinkbunny"],
411 # duplicate entries do no harm
413 ["gnt-cluster", "modify",
414 "--enabled-storage-types=%s,%s" %
415 (default_storage_type, default_storage_type)],
417 AssertCommand(["gnt-cluster", "info"])
420 def TestClusterModifyBe():
421 """gnt-cluster modify -B"""
424 (False, ["gnt-cluster", "modify", "-B", "maxmem=256"]),
425 (False, ["sh", "-c", "gnt-cluster info|grep '^ *maxmem: 256$'"]),
426 (False, ["gnt-cluster", "modify", "-B", "minmem=256"]),
427 (False, ["sh", "-c", "gnt-cluster info|grep '^ *minmem: 256$'"]),
428 (True, ["gnt-cluster", "modify", "-B", "maxmem=a"]),
429 (False, ["sh", "-c", "gnt-cluster info|grep '^ *maxmem: 256$'"]),
430 (True, ["gnt-cluster", "modify", "-B", "minmem=a"]),
431 (False, ["sh", "-c", "gnt-cluster info|grep '^ *minmem: 256$'"]),
432 (False, ["gnt-cluster", "modify", "-B", "maxmem=128,minmem=128"]),
433 (False, ["sh", "-c", "gnt-cluster info|grep '^ *maxmem: 128$'"]),
434 (False, ["sh", "-c", "gnt-cluster info|grep '^ *minmem: 128$'"]),
436 (False, ["gnt-cluster", "modify", "-B", "vcpus=4"]),
437 (False, ["sh", "-c", "gnt-cluster info|grep '^ *vcpus: 4$'"]),
438 (True, ["gnt-cluster", "modify", "-B", "vcpus=a"]),
439 (False, ["gnt-cluster", "modify", "-B", "vcpus=1"]),
440 (False, ["sh", "-c", "gnt-cluster info|grep '^ *vcpus: 1$'"]),
442 (False, ["gnt-cluster", "modify", "-B", "auto_balance=False"]),
443 (False, ["sh", "-c", "gnt-cluster info|grep '^ *auto_balance: False$'"]),
444 (True, ["gnt-cluster", "modify", "-B", "auto_balance=1"]),
445 (False, ["gnt-cluster", "modify", "-B", "auto_balance=True"]),
446 (False, ["sh", "-c", "gnt-cluster info|grep '^ *auto_balance: True$'"]),
448 AssertCommand(cmd, fail=fail)
450 # redo the original-requested BE parameters, if any
451 bep = qa_config.get("backend-parameters", "")
453 AssertCommand(["gnt-cluster", "modify", "-B", bep])
456 def _GetClusterIPolicy():
457 """Return the run-time values of the cluster-level instance policy.
460 @return: (policy, specs), where:
461 - policy is a dictionary of the policy values, instance specs excluded
462 - specs is dict of dict, specs[par][key] is a spec value, where key is
463 "min", "max", or "std"
466 info = qa_utils.GetObjectInfo(["gnt-cluster", "info"])
467 policy = info["Instance policy - limits for instances"]
470 ispec_keys = constants.ISPECS_MINMAX_KEYS | frozenset([constants.ISPECS_STD])
471 for (key, val) in policy.items():
472 if key in ispec_keys:
473 for (par, pval) in val.items():
474 if par == "memory-size":
476 d = ret_specs.setdefault(par, {})
479 ret_policy[key] = val
482 assert len(ret_specs) > 0
483 good = all("min" in d and "std" in d and "max" in d
484 for d in ret_specs.values())
485 assert good, "Missing item in specs: %s" % ret_specs
486 assert len(ret_policy) > 0
487 return (ret_policy, ret_specs)
490 def TestClusterModifyIPolicy():
491 """gnt-cluster modify --ipolicy-*"""
492 basecmd = ["gnt-cluster", "modify"]
493 (old_policy, old_specs) = _GetClusterIPolicy()
494 for par in ["vcpu-ratio", "spindle-ratio"]:
495 curr_val = float(old_policy[par])
501 # Restore the old value
504 for (good, val) in test_values:
505 cmd = basecmd + ["--ipolicy-%s=%s" % (par, val)]
506 AssertCommand(cmd, fail=not good)
509 # Check the affected parameter
510 (eff_policy, eff_specs) = _GetClusterIPolicy()
511 AssertEqual(float(eff_policy[par]), curr_val)
512 # Check everything else
513 AssertEqual(eff_specs, old_specs)
514 for p in eff_policy.keys():
517 AssertEqual(eff_policy[p], old_policy[p])
519 # Disk templates are treated slightly differently
520 par = "disk-templates"
521 disp_str = "enabled disk templates"
522 curr_val = old_policy[disp_str]
524 (True, constants.DT_PLAIN),
525 (True, "%s,%s" % (constants.DT_PLAIN, constants.DT_DRBD8)),
526 (False, "thisisnotadisktemplate"),
528 # Restore the old value
529 (True, curr_val.replace(" ", "")),
531 for (good, val) in test_values:
532 cmd = basecmd + ["--ipolicy-%s=%s" % (par, val)]
533 AssertCommand(cmd, fail=not good)
536 # Check the affected parameter
537 (eff_policy, eff_specs) = _GetClusterIPolicy()
538 AssertEqual(eff_policy[disp_str].replace(" ", ""), curr_val)
539 # Check everything else
540 AssertEqual(eff_specs, old_specs)
541 for p in eff_policy.keys():
544 AssertEqual(eff_policy[p], old_policy[p])
547 def TestClusterSetISpecs(new_specs, fail=False, old_values=None):
548 """Change instance specs.
550 @type new_specs: dict of dict
551 @param new_specs: new_specs[par][key], where key is "min", "max", "std". It
552 can be an empty dictionary.
554 @param fail: if the change is expected to fail
555 @type old_values: tuple
556 @param old_values: (old_policy, old_specs), as returned by
557 L{_GetClusterIPolicy}
558 @return: same as L{_GetClusterIPolicy}
562 (old_policy, old_specs) = old_values
564 (old_policy, old_specs) = _GetClusterIPolicy()
566 cmd = ["gnt-cluster", "modify"]
567 for (par, keyvals) in new_specs.items():
568 if par == "spindle-use":
569 # ignore spindle-use, which is not settable
573 ",".join(["%s=%s" % (k, v) for (k, v) in keyvals.items()]),
575 AssertCommand(cmd, fail=fail)
576 # Check the new state
577 (eff_policy, eff_specs) = _GetClusterIPolicy()
578 AssertEqual(eff_policy, old_policy)
580 AssertEqual(eff_specs, old_specs)
582 for par in eff_specs:
583 for key in eff_specs[par]:
584 if par in new_specs and key in new_specs[par]:
585 AssertEqual(int(eff_specs[par][key]), int(new_specs[par][key]))
587 AssertEqual(int(eff_specs[par][key]), int(old_specs[par][key]))
588 return (eff_policy, eff_specs)
591 def TestClusterModifyISpecs():
592 """gnt-cluster modify --specs-*"""
593 params = ["mem-size", "disk-size", "disk-count", "cpu-count", "nic-count"]
594 (cur_policy, cur_specs) = _GetClusterIPolicy()
609 # This is to restore the old values
611 cur_specs[par]["min"], cur_specs[par]["std"], cur_specs[par]["max"])
613 for (good, mn, st, mx) in test_values:
614 new_vals = {par: {"min": str(mn), "std": str(st), "max": str(mx)}}
615 cur_state = (cur_policy, cur_specs)
616 # We update cur_specs, as we've copied the values to restore already
617 (cur_policy, cur_specs) = TestClusterSetISpecs(new_vals, fail=not good,
618 old_values=cur_state)
621 def TestClusterInfo():
622 """gnt-cluster info"""
623 AssertCommand(["gnt-cluster", "info"])
626 def TestClusterRedistConf():
627 """gnt-cluster redist-conf"""
628 AssertCommand(["gnt-cluster", "redist-conf"])
631 def TestClusterGetmaster():
632 """gnt-cluster getmaster"""
633 AssertCommand(["gnt-cluster", "getmaster"])
636 def TestClusterVersion():
637 """gnt-cluster version"""
638 AssertCommand(["gnt-cluster", "version"])
641 def TestClusterRenewCrypto():
642 """gnt-cluster renew-crypto"""
643 master = qa_config.GetMasterNode()
645 # Conflicting options
646 cmd = ["gnt-cluster", "renew-crypto", "--force",
647 "--new-cluster-certificate", "--new-confd-hmac-key"]
649 ["--new-rapi-certificate", "--rapi-certificate=/dev/null"],
650 ["--new-cluster-domain-secret", "--cluster-domain-secret=/dev/null"],
652 for i in conflicting:
653 AssertCommand(cmd + i, fail=True)
655 # Invalid RAPI certificate
656 cmd = ["gnt-cluster", "renew-crypto", "--force",
657 "--rapi-certificate=/dev/null"]
658 AssertCommand(cmd, fail=True)
660 rapi_cert_backup = qa_utils.BackupFile(master.primary,
661 pathutils.RAPI_CERT_FILE)
663 # Custom RAPI certificate
664 fh = tempfile.NamedTemporaryFile()
666 # Ensure certificate doesn't cause "gnt-cluster verify" to complain
667 validity = constants.SSL_CERT_EXPIRATION_WARN * 3
669 utils.GenerateSelfSignedSslCert(fh.name, validity=validity)
671 tmpcert = qa_utils.UploadFile(master.primary, fh.name)
673 AssertCommand(["gnt-cluster", "renew-crypto", "--force",
674 "--rapi-certificate=%s" % tmpcert])
676 AssertCommand(["rm", "-f", tmpcert])
678 # Custom cluster domain secret
679 cds_fh = tempfile.NamedTemporaryFile()
680 cds_fh.write(utils.GenerateSecret())
684 tmpcds = qa_utils.UploadFile(master.primary, cds_fh.name)
686 AssertCommand(["gnt-cluster", "renew-crypto", "--force",
687 "--cluster-domain-secret=%s" % tmpcds])
689 AssertCommand(["rm", "-f", tmpcds])
692 AssertCommand(["gnt-cluster", "renew-crypto", "--force",
693 "--new-cluster-certificate", "--new-confd-hmac-key",
694 "--new-rapi-certificate", "--new-cluster-domain-secret"])
696 # Restore RAPI certificate
697 AssertCommand(["gnt-cluster", "renew-crypto", "--force",
698 "--rapi-certificate=%s" % rapi_cert_backup])
700 AssertCommand(["rm", "-f", rapi_cert_backup])
703 def TestClusterBurnin():
705 master = qa_config.GetMasterNode()
707 options = qa_config.get("options", {})
708 disk_template = options.get("burnin-disk-template", constants.DT_DRBD8)
709 parallel = options.get("burnin-in-parallel", False)
710 check_inst = options.get("burnin-check-instances", False)
711 do_rename = options.get("burnin-rename", "")
712 do_reboot = options.get("burnin-reboot", True)
713 reboot_types = options.get("reboot-types", constants.REBOOT_TYPES)
715 # Get as many instances as we need
719 num = qa_config.get("options", {}).get("burnin-instances", 1)
720 for _ in range(0, num):
721 instances.append(qa_config.AcquireInstance())
722 except qa_error.OutOfInstancesError:
723 print "Not enough instances, continuing anyway."
725 if len(instances) < 1:
726 raise qa_error.Error("Burnin needs at least one instance")
728 script = qa_utils.UploadFile(master.primary, "../tools/burnin")
732 "--os=%s" % qa_config.get("os"),
733 "--minmem-size=%s" % qa_config.get(constants.BE_MINMEM),
734 "--maxmem-size=%s" % qa_config.get(constants.BE_MAXMEM),
735 "--disk-size=%s" % ",".join(qa_config.get("disk")),
736 "--disk-growth=%s" % ",".join(qa_config.get("disk-growth")),
737 "--disk-template=%s" % disk_template]
739 cmd.append("--parallel")
740 cmd.append("--early-release")
742 cmd.append("--http-check")
744 cmd.append("--rename=%s" % do_rename)
746 cmd.append("--no-reboot")
748 cmd.append("--reboot-types=%s" % ",".join(reboot_types))
749 cmd += [inst.name for inst in instances]
752 AssertCommand(["rm", "-f", script])
755 for inst in instances:
759 def TestClusterMasterFailover():
760 """gnt-cluster master-failover"""
761 master = qa_config.GetMasterNode()
762 failovermaster = qa_config.AcquireNode(exclude=master)
764 cmd = ["gnt-cluster", "master-failover"]
766 AssertCommand(cmd, node=failovermaster)
767 # Back to original master node
768 AssertCommand(cmd, node=master)
770 failovermaster.Release()
773 def _NodeQueueDrainFile(node):
774 """Returns path to queue drain file for a node.
777 return qa_utils.MakeNodePath(node, pathutils.JOB_QUEUE_DRAIN_FILE)
780 def _AssertDrainFile(node, **kwargs):
781 """Checks for the queue drain file.
784 AssertCommand(["test", "-f", _NodeQueueDrainFile(node)], node=node, **kwargs)
787 def TestClusterMasterFailoverWithDrainedQueue():
788 """gnt-cluster master-failover with drained queue"""
789 master = qa_config.GetMasterNode()
790 failovermaster = qa_config.AcquireNode(exclude=master)
792 # Ensure queue is not drained
793 for node in [master, failovermaster]:
794 _AssertDrainFile(node, fail=True)
796 # Drain queue on failover master
797 AssertCommand(["touch", _NodeQueueDrainFile(failovermaster)],
800 cmd = ["gnt-cluster", "master-failover"]
802 _AssertDrainFile(failovermaster)
803 AssertCommand(cmd, node=failovermaster)
804 _AssertDrainFile(master, fail=True)
805 _AssertDrainFile(failovermaster, fail=True)
807 # Back to original master node
808 AssertCommand(cmd, node=master)
810 failovermaster.Release()
812 # Ensure queue is not drained
813 for node in [master, failovermaster]:
814 _AssertDrainFile(node, fail=True)
817 def TestClusterCopyfile():
818 """gnt-cluster copyfile"""
819 master = qa_config.GetMasterNode()
821 uniqueid = utils.NewUUID()
823 # Create temporary file
824 f = tempfile.NamedTemporaryFile()
829 # Upload file to master node
830 testname = qa_utils.UploadFile(master.primary, f.name)
832 # Copy file to all nodes
833 AssertCommand(["gnt-cluster", "copyfile", testname])
834 _CheckFileOnAllNodes(testname, uniqueid)
836 _RemoveFileFromAllNodes(testname)
839 def TestClusterCommand():
840 """gnt-cluster command"""
841 uniqueid = utils.NewUUID()
842 rfile = "/tmp/gnt%s" % utils.NewUUID()
843 rcmd = utils.ShellQuoteArgs(["echo", "-n", uniqueid])
844 cmd = utils.ShellQuoteArgs(["gnt-cluster", "command",
845 "%s >%s" % (rcmd, rfile)])
849 _CheckFileOnAllNodes(rfile, uniqueid)
851 _RemoveFileFromAllNodes(rfile)
854 def TestClusterDestroy():
855 """gnt-cluster destroy"""
856 AssertCommand(["gnt-cluster", "destroy", "--yes-do-it"])
859 def TestClusterRepairDiskSizes():
860 """gnt-cluster repair-disk-sizes"""
861 AssertCommand(["gnt-cluster", "repair-disk-sizes"])
864 def TestSetExclStorCluster(newvalue):
865 """Set the exclusive_storage node parameter at the cluster level.
868 @param newvalue: New value of exclusive_storage
870 @return: The old value of exclusive_storage
873 es_path = ["Default node parameters", "exclusive_storage"]
874 oldvalue = _GetClusterField(es_path)
875 AssertCommand(["gnt-cluster", "modify", "--node-parameters",
876 "exclusive_storage=%s" % newvalue])
877 effvalue = _GetClusterField(es_path)
878 if effvalue != newvalue:
879 raise qa_error.Error("exclusive_storage has the wrong value: %s instead"
880 " of %s" % (effvalue, newvalue))
881 qa_config.SetExclusiveStorage(newvalue)
885 def TestExclStorSharedPv(node):
886 """cluster-verify reports LVs that share the same PV with exclusive_storage.
889 vgname = qa_config.get("vg-name", constants.DEFAULT_VG)
890 lvname1 = _QA_LV_PREFIX + "vol1"
891 lvname2 = _QA_LV_PREFIX + "vol2"
892 node_name = node.primary
893 AssertCommand(["lvcreate", "-L1G", "-n", lvname1, vgname], node=node_name)
894 AssertClusterVerify(fail=True, errors=[constants.CV_ENODEORPHANLV])
895 AssertCommand(["lvcreate", "-L1G", "-n", lvname2, vgname], node=node_name)
896 AssertClusterVerify(fail=True, errors=[constants.CV_ENODELVM,
897 constants.CV_ENODEORPHANLV])
898 AssertCommand(["lvremove", "-f", "/".join([vgname, lvname1])], node=node_name)
899 AssertCommand(["lvremove", "-f", "/".join([vgname, lvname2])], node=node_name)
900 AssertClusterVerify()