diff --git a/cts/Makefile.am b/cts/Makefile.am
index 3e128dfc81..3ad7d204fe 100644
--- a/cts/Makefile.am
+++ b/cts/Makefile.am
@@ -1,77 +1,80 @@
#
-# Copyright 2001-2024 the Pacemaker project contributors
+# Copyright 2001-2025 the Pacemaker project contributors
#
# The version control history for this file may have further details.
#
# This source code is licensed under the GNU General Public License version 2
# or later (GPLv2+) WITHOUT ANY WARRANTY.
#
include $(top_srcdir)/mk/common.mk
include $(top_srcdir)/mk/python.mk
# Test commands and globally applicable test files should be in $(testdir),
# and command-specific test data should be in a command-specific subdirectory.
testdir = $(datadir)/$(PACKAGE)/tests
test_SCRIPTS = cts-attrd \
cts-cli \
cts-exec \
cts-fencing \
cts-lab \
cts-regression \
cts-scheduler \
cts-schemas
dist_test_DATA = README.md \
valgrind-pcmk.suppressions
clidir = $(testdir)/cli
dist_cli_DATA = $(wildcard cli/*.xml cli/*.exp)
ctsdir = $(datadir)/$(PACKAGE)/tests/cts
cts_SCRIPTS = cts
schemasdir = $(testdir)/schemas
dist_schemas_DATA = $(wildcard schemas/*/ref/*.ref*) \
$(wildcard schemas/*/ref.err/*.ref.err*) \
$(wildcard schemas/*/xml/*.xml)
noinst_SCRIPTS = cluster_test
.PHONY: scheduler-list
scheduler-list:
@for T in "$(srcdir)"/scheduler/xml/*.xml; do \
echo $$(basename $$T .xml); \
done
CLEANFILES = $(builddir)/.regression.failed.diff
.PHONY: clean-local
clean-local:
rm -f scheduler/*/*.pe
rm -f schemas/*/*.up{,.err}
SUBDIRS = benchmark \
scheduler \
support
.PHONY: cts-support-install
cts-support-install:
$(MAKE) $(AM_MAKEFLAGS) -C support cts-support
$(builddir)/support/cts-support install
.PHONY: cts-support-uninstall
cts-support-uninstall:
$(MAKE) $(AM_MAKEFLAGS) -C support cts-support
$(builddir)/support/cts-support uninstall
# Everything listed here is a python script, typically generated from a .in file
# (though that is not a requirement). We want to run pylint on all of these
# things after they've been built.
+# FIXME: When cts-schemas is converted to python, this can be removed because
+# it will duplicate test_SCRIPTS above.
python_files = cts-attrd \
+ cts-cli \
cts-exec \
cts-fencing \
cts-lab \
cts-regression \
cts-scheduler
PYCHECKFILES ?= $(python_files)
diff --git a/cts/cts-cli.in b/cts/cts-cli.in
index 7233486860..d2816d0410 100644
--- a/cts/cts-cli.in
+++ b/cts/cts-cli.in
@@ -1,3486 +1,3488 @@
#!@PYTHON@
"""Regression tests for Pacemaker's command line tools."""
# pylint doesn't like the module name "cts-cli" which is an invalid complaint for this file
# but probably something we want to continue warning about elsewhere
# pylint: disable=invalid-name
# pacemaker imports need to come after we modify sys.path, which pylint will complain about.
# pylint: disable=wrong-import-position
# We know this is a very long file.
# pylint: disable=too-many-lines
__copyright__ = "Copyright 2024-2025 the Pacemaker project contributors"
__license__ = "GNU General Public License version 2 or later (GPLv2+) WITHOUT ANY WARRANTY"
import argparse
from contextlib import contextmanager
from datetime import datetime, timedelta
import fileinput
from functools import partial
from gettext import ngettext
from multiprocessing import Pool, cpu_count
import os
import pathlib
import re
from shutil import copyfile
import signal
from string import Formatter
import subprocess
import sys
from tempfile import NamedTemporaryFile, TemporaryDirectory, mkstemp
import types
# These imports allow running from a source checkout after running `make`.
if os.path.exists("@abs_top_srcdir@/python"):
sys.path.insert(0, "@abs_top_srcdir@/python")
# pylint: disable=comparison-of-constants,comparison-with-itself,condition-evals-to-constant
if os.path.exists("@abs_top_builddir@/python") and "@abs_top_builddir@" != "@abs_top_srcdir@":
sys.path.insert(0, "@abs_top_builddir@/python")
from pacemaker._cts.errors import XmlValidationError
from pacemaker._cts.validate import validate
from pacemaker.buildoptions import BuildOptions
from pacemaker.exitstatus import ExitStatus
# Individual tool tests are split out, but can also be accessed as a group with "tools"
tools_tests = ["cibadmin", "crm_attribute", "crm_standby", "crm_resource",
"crm_ticket", "crmadmin", "crm_shadow", "crm_verify", "crm_simulate"]
# The default list of tests to run, in the order they should be run
default_tests = ["access_render", "daemons", "dates", "error_codes"] + tools_tests + \
["crm_mon", "acls", "validity", "upgrade", "rules", "feature_set"]
other_tests = ["agents"]
# The directory containing this program
test_home = os.path.dirname(os.path.realpath(__file__))
+# Where test data is stored
+cts_cli_data = f"{test_home}/cli"
+
# The name of the shadow CIB
SHADOW_NAME = "cts-cli"
# Arguments to pass to valgrind
VALGRIND_ARGS = ["-q", "--gen-suppressions=all", "--show-reachable=no", "--leak-check=full",
"--trace-children=no", "--time-stamp=yes", "--num-callers=20",
"--suppressions=%s/valgrind-pcmk.suppressions" % test_home]
class PluralFormatter(Formatter):
"""
Special string formatting class for selecting singular vs. plurals.
Use like so:
fmt = PluralFormatter()
print(fmt.format("{0} {0}:plural,test,tests} succeeded", n_tests))
"""
def format_field(self, value, format_spec):
"""Convert a value to a formatted representation."""
if format_spec.startswith("plural,"):
eles = format_spec.split(',')
if len(eles) == 2:
singular = eles[1]
plural = singular + "s"
else:
singular = eles[1]
plural = eles[2]
return ngettext(singular, plural, value)
return super().format_field(value, format_spec)
def apply_substitutions(s, extra=None):
"""Apply text substitutions to an input string and return it."""
substitutions = {
"cts_cli_data": "%s/cli" % test_home,
"shadow": SHADOW_NAME,
- "test_home": test_home,
}
if extra is not None:
substitutions.update(extra)
return s.format(**substitutions)
def cleanup_shadow_dir():
"""Remove any previously created shadow CIB directory."""
subprocess.run(["crm_shadow", "--force", "--delete", SHADOW_NAME],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
check=True)
def copy_existing_cib(existing):
"""
Generate a CIB by copying an existing one to a temporary location.
This is suitable for use with the cib_gen= parameter to the TestGroup class.
"""
(fp, new) = mkstemp(prefix="cts-cli.cib.xml.")
os.close(fp)
copyfile(apply_substitutions(existing), new)
return new
def current_cib():
"""Return the complete current CIB."""
with environ({"CIB_user": "root"}):
return subprocess.check_output(["cibadmin", "-Q"], encoding="utf-8")
def make_test_group(desc, cmd, classes, **kwargs):
"""
Create a TestGroup that replicates the same test for multiple classes.
The given description, cmd, and kwargs will be passed as arguments to each
Test subclass in the classes parameter. The resulting objects will then be
added to a TestGroup and returned.
The main purpose of this function is to be able to run the same test for
both text and XML formats without having to duplicate everything. Thus, the
cmd string may contain "{fmt}", which will have any --output-as= class
variable substituted in.
"""
tests = []
for c in classes:
obj = c(desc, apply_substitutions(cmd, extra={"fmt": c.format_args}),
**kwargs)
tests.append(obj)
return TestGroup(tests)
def create_shadow_cib(shadow_dir, create_empty=True, validate_with=None,
valgrind=False):
"""
Create a shadow CIB file.
Keyword arguments:
create_empty -- If True, the shadow CIB will be empty. Otherwise, the
shadow CIB will be a copy of the currently active
cluster configuration.
validate_with -- If not None, the schema version to validate the CIB
against
valgrind -- If True, run the create operation under valgrind
"""
args = ["crm_shadow", "--batch", "--force"]
if create_empty:
args += ["--create-empty", SHADOW_NAME]
else:
args += ["--create", SHADOW_NAME]
if validate_with is not None:
args += ["--validate-with", validate_with]
if valgrind:
args = ["valgrind"] + VALGRIND_ARGS + args
os.environ["CIB_shadow_dir"] = shadow_dir
os.environ["CIB_shadow"] = SHADOW_NAME
subprocess.run(args, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
check=True)
delete_shadow_resource_defaults()
def delete_shadow_resource_defaults():
"""Clear out the rsc_defaults section from a shadow CIB file."""
# A newly created empty CIB might or might not have a rsc_defaults section
# depending on whether the --with-resource-stickiness-default configure
# option was used. To ensure regression tests behave the same either way,
# delete any rsc_defaults after creating or erasing a CIB.
subprocess.run(["cibadmin", "--delete", "--xml-text", ""],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
check=True)
# The above command might or might not bump the CIB version, so reset it
# to ensure future changes result in the same version for comparison.
reset_shadow_cib_version()
def reset_shadow_cib_version():
"""Set various version numbers in a shadow CIB file back to 0."""
with fileinput.input(files=[shadow_path()], inplace=True) as f:
for line in f:
line = re.sub('epoch="[0-9]*"', 'epoch="1"', line)
line = re.sub('num_updates="[0-9]*"', 'num_updates="0"', line)
line = re.sub('admin_epoch="[0-9]*"', 'admin_epoch="0"', line)
print(line, end='')
def run_cmd_list(cmds):
"""
Run one or more shell commands.
cmds can be:
* A string
* A Python function
* A list of the above
Raises subprocess.CalledProcessError on error.
"""
if cmds is None:
return
if isinstance(cmds, (str, types.FunctionType)):
cmds = [cmds]
for c in cmds:
if isinstance(c, types.FunctionType):
c()
else:
subprocess.run(apply_substitutions(c), stdout=subprocess.PIPE, stderr=subprocess.PIPE,
shell=True, universal_newlines=True, check=True)
def sanitize_output(s):
"""
Replace content in the output expected to change between test runs.
This is stuff like version numbers, timestamps, source line numbers,
build options, system names and messages, etc.
"""
# A list of tuples of regular expressions and their replacements.
replacements = [
(r'Created new pacemaker-.* configuration', r'Created new pacemaker configuration'),
(r'Device not configured', r'No such device or address'),
(r'^Entity: line [0-9]+: ', r''),
(r'(Injecting attribute last-failure-ping#monitor_10000=)[0-9]*', r'\1'),
(r'Last change: .*', r'Last change:'),
(r'Last updated: .*', r'Last updated:'),
(r'^Migration will take effect until: .*', r'Migration will take effect until:'),
(r'(\* Possible values.*: .*)\(default: [^)]*\)', r'\1(default: )'),
(r"""-X '.*'""", r"""-X '...'"""),
(r' api-version="[^"]*"', r' api-version="X"'),
(r'\(apply_upgrade@.*\.c:[0-9]+\)', r'apply_upgrade'),
(r'\(invert_action@.*\.c:[0-9]+\)', r'invert_action'),
(r'\(pcmk__update_schema@.*\.c:[0-9]+\)', r'pcmk__update_schema'),
(r'(
"""
# Create a test CIB that has ACL roles
basic_tests = [
Test("Configure some ACLs", "cibadmin -M -o acls -p", update_cib=True,
stdin=acl_cib),
Test("Enable ACLs", "crm_attribute -n enable-acl -v true",
update_cib=True),
# Run cibadmin --show-access on the test CIB as an ACL-restricted user
Test("An instance of ACLs render (into color)",
"cibadmin --force --show-access=color -Q --user tony"),
Test("An instance of ACLs render (into namespacing)",
"cibadmin --force --show-access=namespace -Q --user tony"),
Test("An instance of ACLs render (into text)",
"cibadmin --force --show-access=text -Q --user tony"),
]
return [
ShadowTestGroup(basic_tests),
]
class DaemonsRegressionTest(RegressionTest):
"""A class for testing command line options of pacemaker daemons."""
@property
def name(self):
"""Return the name of this regression test."""
return "daemons"
@property
def tests(self):
"""A list of Test instances to be run as part of this regression test."""
return [
Test("Get CIB manager metadata", "pacemaker-based metadata"),
Test("Get controller metadata", "pacemaker-controld metadata"),
Test("Get fencer metadata", "pacemaker-fenced metadata"),
Test("Get scheduler metadata", "pacemaker-schedulerd metadata"),
]
class DatesRegressionTest(RegressionTest):
"""A class for testing handling of ISO8601 dates."""
@property
def name(self):
"""Return the name of this regression test."""
return "dates"
@property
def tests(self):
"""A list of Test instances to be run as part of this regression test."""
invalid_periods = [
"",
"2019-01-01 00:00:00Z", # Start with no end
"2019-01-01 00:00:00Z/", # Start with only a trailing slash
"PT2S/P1M", # Two durations
"2019-13-01 00:00:00Z/P1M", # Out-of-range month
"20191077T15/P1M", # Out-of-range day
"2019-10-01T25:00:00Z/P1M", # Out-of-range hour
"2019-10-01T24:00:01Z/P1M", # Hour 24 with anything but :00:00
"PT5H/20191001T007000Z", # Out-of-range minute
"2019-10-01 00:00:80Z/P1M", # Out-of-range second
"2019-10-01 00:00:10 +25:00/P1M", # Out-of-range offset hour
"20191001T000010 -00:61/P1M", # Out-of-range offset minute
"P1Y/2019-02-29 00:00:00Z", # Feb. 29 in non-leap-year
"2019-01-01 00:00:00Z/P", # Duration with no values
"P1Z/2019-02-20 00:00:00Z", # Invalid duration unit
"P1YM/2019-02-20 00:00:00Z", # No number for duration unit
]
# Ensure invalid period specifications are rejected
invalid_period_tests = []
for p in invalid_periods:
invalid_period_tests.append(Test("Invalid period - [%s]" % p,
"iso8601 -p '%s'" % p,
expected_rc=ExitStatus.INVALID_PARAM))
year_tests = []
for y in ["06", "07", "08", "09", "10", "11", "12", "13", "14", "15", "16", "17", "18", "40"]:
year_tests.extend([
Test("20%s-W01-7" % y,
"iso8601 -d '20%s-W01-7 00Z'" % y),
Test("20%s-W01-7 - round-trip" % y,
"iso8601 -d '20%s-W01-7 00Z' -W -E '20%s-W01-7 00:00:00Z'" % (y, y)),
Test("20%s-W01-1" % y,
"iso8601 -d '20%s-W01-1 00Z'" % y),
Test("20%s-W01-1 - round-trip" % y,
"iso8601 -d '20%s-W01-1 00Z' -W -E '20%s-W01-1 00:00:00Z'" % (y, y))
])
return invalid_period_tests + [
make_test_group("'2005-040/2005-043' period", "iso8601 {fmt} -p '2005-040/2005-043'",
[Test, ValidatingTest]),
Test("2014-01-01 00:30:00 - 1 Hour",
"iso8601 -d '2014-01-01 00:30:00Z' -D P-1H -E '2013-12-31 23:30:00Z'"),
Test("Valid date - Feb 29 in leap year",
"iso8601 -d '2020-02-29 00:00:00Z' -E '2020-02-29 00:00:00Z'"),
Test("Valid date - using 'T' and offset",
"iso8601 -d '20191201T131211 -05:00' -E '2019-12-01 18:12:11Z'"),
Test("24:00:00 equivalent to 00:00:00 of next day",
"iso8601 -d '2019-12-31 24:00:00Z' -E '2020-01-01 00:00:00Z'"),
] + year_tests + [
make_test_group("2009-W53-07",
"iso8601 {fmt} -d '2009-W53-7 00:00:00Z' -W -E '2009-W53-7 00:00:00Z'",
[Test, ValidatingTest]),
Test("epoch + 2 Years 5 Months 6 Minutes",
"iso8601 -d 'epoch' -D P2Y5MT6M -E '1972-06-01 00:06:00Z'"),
Test("2009-01-31 + 1 Month",
"iso8601 -d '20090131T000000Z' -D P1M -E '2009-02-28 00:00:00Z'"),
Test("2009-01-31 + 2 Months",
"iso8601 -d '2009-01-31 00:00:00Z' -D P2M -E '2009-03-31 00:00:00Z'"),
Test("2009-01-31 + 3 Months",
"iso8601 -d '2009-01-31 00:00:00Z' -D P3M -E '2009-04-30 00:00:00Z'"),
make_test_group("2009-03-31 - 1 Month",
"iso8601 {fmt} -d '2009-03-31 01:00:00 +01:00' -D P-1M -E '2009-02-28 00:00:00Z'",
[Test, ValidatingTest]),
make_test_group("2038-01-01 + 3 Months",
"iso8601 {fmt} -d '2038-01-01 00:00:00Z' -D P3M -E '2038-04-01 00:00:00Z'",
[Test, ValidatingTest]),
]
class ErrorCodeRegressionTest(RegressionTest):
"""A class for testing error code reporting."""
@property
def name(self):
"""Return the name of this regression test."""
return "error_codes"
@property
def tests(self):
"""A list of Test instances to be run as part of this regression test."""
# Legacy return codes
#
# Don't test unknown legacy code. FreeBSD includes a colon in strerror(),
# while other distros do not.
legacy_tests = [
make_test_group("Get legacy return code", "crm_error {fmt} 201",
[Test, ValidatingTest]),
make_test_group("Get legacy return code (with name)", "crm_error -n {fmt} 201",
[Test, ValidatingTest]),
make_test_group("Get multiple legacy return codes", "crm_error {fmt} 201 202",
[Test, ValidatingTest]),
make_test_group("Get multiple legacy return codes (with names)",
"crm_error -n {fmt} 201 202",
[Test, ValidatingTest]),
# We can only rely on our custom codes, so we'll spot-check codes 201-209
Test("List legacy return codes (spot check)",
"crm_error -l | grep 20[1-9]"),
ValidatingTest("List legacy return codes (spot check)",
"crm_error -l --output-as=xml | grep -Ev '&1 | sed -e 's/Digest:.*/Digest:/'"),
Test("Require --force for CIB erasure", "cibadmin -E",
expected_rc=ExitStatus.UNSAFE, update_cib=True),
Test("Allow CIB erasure with --force", "cibadmin -E --force"),
# Verify the output after erasure
Test("Query CIB", "cibadmin -Q",
setup=delete_shadow_resource_defaults,
update_cib=True),
]
# Add some stuff to the empty CIB so we know that erasing it did something.
basic_tests_setup = [
"""cibadmin -C -o nodes --xml-text ''""",
"""cibadmin -C -o crm_config --xml-text ''""",
"""cibadmin -C -o resources --xml-text ''"""
]
return [
ShadowTestGroup(basic_tests, setup=basic_tests_setup),
]
class CrmAttributeRegressionTest(RegressionTest):
"""A class for testing crm_attribute."""
@property
def name(self):
"""Return the name of this regression test."""
return "crm_attribute"
@property
def tests(self):
"""A list of Test instances to be run as part of this regression test."""
options_tests = [
make_test_group("List all available options (invalid type)",
"crm_attribute --list-options=asdf {fmt}",
[Test, ValidatingTest],
expected_rc=ExitStatus.USAGE),
make_test_group("List non-advanced cluster options",
"crm_attribute --list-options=cluster {fmt}",
[Test, ValidatingTest]),
make_test_group("List all available cluster options",
"crm_attribute --list-options=cluster --all {fmt}",
[Test, ValidatingTest]),
Test("Return usage error if both -p and OCF_RESOURCE_INSTANCE are empty strings",
"crm_attribute -N cluster01 -p '' -G",
expected_rc=ExitStatus.USAGE),
]
value_update_tests = [
Test("Query the value of an attribute that does not exist",
"crm_attribute -n ABCD --query --quiet",
expected_rc=ExitStatus.NOSUCH),
Test("Configure something before erasing",
"crm_attribute -n test_attr -v 5", update_cib=True),
Test("Test '++' XML attribute update syntax",
"""cibadmin -M --score --xml-text=''""",
update_cib=True),
Test("Test '+=' XML attribute update syntax",
"""cibadmin -M --score --xml-text=''""",
update_cib=True),
make_test_group("Test '++' nvpair value update syntax",
"crm_attribute -n test_attr -v 'value++' --score {fmt}",
[Test, ValidatingTest], update_cib=True),
make_test_group("Test '+=' nvpair value update syntax",
"crm_attribute -n test_attr -v 'value+=2' --score {fmt}",
[Test, ValidatingTest], update_cib=True),
Test("Test '++' XML attribute update syntax (--score not set)",
"""cibadmin -M --xml-text=''""",
update_cib=True),
Test("Test '+=' XML attribute update syntax (--score not set)",
"""cibadmin -M --xml-text=''""",
update_cib=True),
make_test_group("Test '++' nvpair value update syntax (--score not set)",
"crm_attribute -n test_attr -v 'value++' {fmt}",
[Test, ValidatingTest], update_cib=True),
make_test_group("Test '+=' nvpair value update syntax (--score not set)",
"crm_attribute -n test_attr -v 'value+=2' {fmt}",
[Test, ValidatingTest], update_cib=True),
]
query_set_tests = [
Test("Set cluster option", "crm_attribute -n cluster-delay -v 60s",
update_cib=True),
Test("Query new cluster option",
"cibadmin -Q -o crm_config | grep cib-bootstrap-options-cluster-delay"),
Test("Set no-quorum policy",
"crm_attribute -n no-quorum-policy -v ignore", update_cib=True),
Test("Delete nvpair",
"""cibadmin -D -o crm_config --xml-text ''""",
update_cib=True),
Test("Create operation should fail",
"""cibadmin -C -o crm_config --xml-text ''""",
expected_rc=ExitStatus.EXISTS, update_cib=True),
Test("Modify cluster options section",
"""cibadmin -M -o crm_config --xml-text ''""",
update_cib=True),
Test("Query updated cluster option",
"cibadmin -Q -o crm_config | grep cib-bootstrap-options-cluster-delay",
update_cib=True),
Test("Set duplicate cluster option",
"crm_attribute -n cluster-delay -v 40s -s duplicate",
update_cib=True),
Test("Setting multiply defined cluster option should fail",
"crm_attribute -n cluster-delay -v 30s",
expected_rc=ExitStatus.MULTIPLE, update_cib=True),
Test("Set cluster option with -s",
"crm_attribute -n cluster-delay -v 30s -s duplicate",
update_cib=True),
Test("Delete cluster option with -i",
"crm_attribute -n cluster-delay -D -i cib-bootstrap-options-cluster-delay",
update_cib=True),
Test("Create node1 and bring it online",
"crm_simulate --live-check --in-place --node-up=node1",
update_cib=True),
Test("Create node attribute",
"crm_attribute -n ram -v 1024M -N node1 -t nodes",
update_cib=True),
Test("Query new node attribute",
"cibadmin -Q -o nodes | grep node1-ram",
update_cib=True),
Test("Create second node attribute",
"crm_attribute -n rattr -v XYZ -N node1 -t nodes",
update_cib=True),
Test("Query node attributes by pattern",
"crm_attribute -t nodes -P 'ra.*' -N node1 --query"),
Test("Update node attributes by pattern",
"crm_attribute -t nodes -P 'rat.*' -N node1 -v 10",
update_cib=True),
Test("Delete node attributes by pattern",
"crm_attribute -t nodes -P 'rat.*' -N node1 -D",
update_cib=True),
Test("Set a transient (fail-count) node attribute",
"crm_attribute -n fail-count-foo -v 3 -N node1 -t status",
update_cib=True),
Test("Query a fail count", "crm_failcount --query -r foo -N node1",
update_cib=True),
Test("Show node attributes with crm_simulate",
"crm_simulate --live-check --show-attrs"),
Test("Set a second transient node attribute",
"crm_attribute -n fail-count-bar -v 5 -N node1 -t status",
update_cib=True),
Test("Query transient node attributes by pattern",
"crm_attribute -t status -P fail-count -N node1 --query"),
Test("Update transient node attributes by pattern",
"crm_attribute -t status -P fail-count -N node1 -v 10",
update_cib=True),
Test("Delete transient node attributes by pattern",
"crm_attribute -t status -P fail-count -N node1 -D",
update_cib=True),
Test("crm_attribute given invalid delete usage",
"crm_attribute -t nodes -N node1 -D",
expected_rc=ExitStatus.USAGE),
Test("Set a utilization node attribute",
"crm_attribute -n cpu -v 1 -N node1 -z",
update_cib=True),
Test("Query utilization node attribute",
"crm_attribute --query -n cpu -N node1 -z"),
# This update will fail because it has version numbers
Test("Replace operation should fail",
"""cibadmin -Q | sed -e 's/epoch="[^"]*"/epoch="1"/' | cibadmin -R -p""",
expected_rc=ExitStatus.OLD),
]
promotable_tests = [
make_test_group("Query a nonexistent promotable score attribute",
"crm_attribute -N cluster01 -p promotable-rsc -G {fmt}",
[Test, ValidatingTest], expected_rc=ExitStatus.NOSUCH),
make_test_group("Delete a nonexistent promotable score attribute",
"crm_attribute -N cluster01 -p promotable-rsc -D {fmt}",
[Test, ValidatingTest]),
make_test_group("Query after deleting a nonexistent promotable score attribute",
"crm_attribute -N cluster01 -p promotable-rsc -G {fmt}",
[Test, ValidatingTest], expected_rc=ExitStatus.NOSUCH),
make_test_group("Update a nonexistent promotable score attribute",
"crm_attribute -N cluster01 -p promotable-rsc -v 1 {fmt}",
[Test, ValidatingTest]),
make_test_group("Query after updating a nonexistent promotable score attribute",
"crm_attribute -N cluster01 -p promotable-rsc -G {fmt}",
[Test, ValidatingTest]),
make_test_group("Update an existing promotable score attribute",
"crm_attribute -N cluster01 -p promotable-rsc -v 5 {fmt}",
[Test, ValidatingTest]),
make_test_group("Query after updating an existing promotable score attribute",
"crm_attribute -N cluster01 -p promotable-rsc -G {fmt}",
[Test, ValidatingTest]),
make_test_group("Delete an existing promotable score attribute",
"crm_attribute -N cluster01 -p promotable-rsc -D {fmt}",
[Test, ValidatingTest]),
make_test_group("Query after deleting an existing promotable score attribute",
"crm_attribute -N cluster01 -p promotable-rsc -G {fmt}",
[Test, ValidatingTest], expected_rc=ExitStatus.NOSUCH),
]
# Test for an issue with legacy command line parsing when the resource is
# specified in the environment (CLBZ#5509)
ocf_rsc_instance_tests = [
make_test_group("Update a promotable score attribute to -INFINITY",
"crm_attribute -N cluster01 -p -v -INFINITY {fmt}",
[Test, ValidatingTest],
env={"OCF_RESOURCE_INSTANCE": "promotable-rsc"}),
make_test_group("Query after updating a promotable score attribute to -INFINITY",
"crm_attribute -N cluster01 -p -G {fmt}",
[Test, ValidatingTest],
env={"OCF_RESOURCE_INSTANCE": "promotable-rsc"}),
Test("Try OCF_RESOURCE_INSTANCE if -p is specified with an empty string",
"crm_attribute -N cluster01 -p '' -G",
env={"OCF_RESOURCE_INSTANCE": "promotable-rsc"}),
]
return options_tests + [
ShadowTestGroup(value_update_tests),
ShadowTestGroup(query_set_tests),
TestGroup(promotable_tests + ocf_rsc_instance_tests,
env={"OCF_RESOURCE_INSTANCE": "promotable-rsc"},
- cib_gen=partial(copy_existing_cib, "{cts_cli_data}/crm_mon.xml")),
+ cib_gen=partial(copy_existing_cib, f"{cts_cli_data}/crm_mon.xml")),
]
class CrmStandbyRegressionTest(RegressionTest):
"""A class for testing crm_standby."""
@property
def name(self):
"""Return the name of this regression test."""
return "crm_standby"
@property
def tests(self):
"""A list of Test instances to be run as part of this regression test."""
basic_tests = [
Test("Default standby value", "crm_standby -N node1 -G"),
Test("Set standby status", "crm_standby -N node1 -v true",
update_cib=True),
Test("Query standby value", "crm_standby -N node1 -G"),
Test("Delete standby value", "crm_standby -N node1 -D",
update_cib=True),
]
return [
ShadowTestGroup(basic_tests,
- setup="""cibadmin -C -o nodes --xml-text ''"""),
+ setup="""cibadmin -C -o nodes --xml-text ''"""),
]
class CrmResourceRegressionTest(RegressionTest):
"""A class for testing crm_resource."""
@property
def name(self):
"""Return the name of this regression test."""
return "crm_resource"
@property
def tests(self):
"""A list of Test instances to be run as part of this regression test."""
options_tests = [
Test("crm_resource run with extra arguments", "crm_resource foo bar",
expected_rc=ExitStatus.USAGE),
Test("List all available resource options (invalid type)",
"crm_resource --list-options=asdf",
expected_rc=ExitStatus.USAGE),
Test("List all available resource options (invalid type)",
"crm_resource --list-options=asdf --output-as=xml",
expected_rc=ExitStatus.USAGE),
make_test_group("List non-advanced primitive meta-attributes",
"crm_resource --list-options=primitive {fmt}",
[Test, ValidatingTest]),
make_test_group("List all available primitive meta-attributes",
"crm_resource --list-options=primitive --all {fmt}",
[Test, ValidatingTest]),
make_test_group("List non-advanced fencing parameters",
"crm_resource --list-options=fencing {fmt}",
[Test, ValidatingTest]),
make_test_group("List all available fencing parameters",
"crm_resource --list-options=fencing --all {fmt}",
[Test, ValidatingTest]),
]
basic_tests = [
Test("Create a resource",
"""cibadmin -C -o resources --xml-text ''""",
update_cib=True),
Test("crm_resource given both -r and resource config",
"crm_resource -r xyz --class ocf --provider pacemaker --agent Dummy",
expected_rc=ExitStatus.USAGE),
Test("crm_resource given resource config with invalid action",
"crm_resource --class ocf --provider pacemaker --agent Dummy -D",
expected_rc=ExitStatus.USAGE),
Test("Create a resource meta attribute",
"crm_resource -r dummy --meta -p is-managed -v false",
update_cib=True),
Test("Query a resource meta attribute",
"crm_resource -r dummy --meta -g is-managed",
update_cib=True),
Test("Remove a resource meta attribute",
"crm_resource -r dummy --meta -d is-managed",
update_cib=True),
ValidatingTest("Create another resource meta attribute",
"crm_resource -r dummy --meta -p target-role -v Stopped --output-as=xml"),
ValidatingTest("Show why a resource is not running",
"crm_resource -Y -r dummy --output-as=xml"),
ValidatingTest("Remove another resource meta attribute",
"crm_resource -r dummy --meta -d target-role --output-as=xml"),
ValidatingTest("Get a non-existent attribute from a resource element",
"crm_resource -r dummy --get-parameter nonexistent --element --output-as=xml"),
make_test_group("Get a non-existent attribute from a resource element",
"crm_resource -r dummy --get-parameter nonexistent --element {fmt}",
[Test, ValidatingTest], update_cib=True),
Test("Get an existent attribute from a resource element",
"crm_resource -r dummy --get-parameter class --element",
update_cib=True),
ValidatingTest("Set a non-existent attribute for a resource element",
"crm_resource -r dummy --set-parameter=description -v test_description --element --output-as=xml",
update_cib=True),
ValidatingTest("Set an existent attribute for a resource element",
"crm_resource -r dummy --set-parameter=description -v test_description --element --output-as=xml",
update_cib=True),
ValidatingTest("Delete an existent attribute for a resource element",
"crm_resource -r dummy -d description --element --output-as=xml",
update_cib=True),
ValidatingTest("Delete a non-existent attribute for a resource element",
"crm_resource -r dummy -d description --element --output-as=xml",
update_cib=True),
Test("Set a non-existent attribute for a resource element",
"crm_resource -r dummy --set-parameter=description -v test_description --element",
update_cib=True),
Test("Set an existent attribute for a resource element",
"crm_resource -r dummy --set-parameter=description -v test_description --element",
update_cib=True),
Test("Delete an existent attribute for a resource element",
"crm_resource -r dummy -d description --element",
update_cib=True),
Test("Delete a non-existent attribute for a resource element",
"crm_resource -r dummy -d description --element",
update_cib=True),
Test("Create a resource attribute", "crm_resource -r dummy -p delay -v 10s",
update_cib=True),
make_test_group("List the configured resources", "crm_resource -L {fmt}",
[Test, ValidatingTest], update_cib=True),
Test("Implicitly list the configured resources", "crm_resource"),
Test("List IDs of instantiated resources", "crm_resource -l"),
make_test_group("Show XML configuration of resource", "crm_resource -q -r dummy {fmt}",
[Test, ValidatingTest]),
Test("Require a destination when migrating a resource that is stopped",
"crm_resource -r dummy -M",
update_cib=True, expected_rc=ExitStatus.USAGE),
Test("Don't support migration to non-existent locations",
"crm_resource -r dummy -M -N i.do.not.exist",
update_cib=True, expected_rc=ExitStatus.NOSUCH),
Test("Create a fencing resource",
"""cibadmin -C -o resources --xml-text ''""",
update_cib=True),
Test("Bring resources online", "crm_simulate --live-check --in-place",
update_cib=True),
Test("Try to move a resource to its existing location",
"crm_resource -r dummy --move --node node1",
update_cib=True, expected_rc=ExitStatus.EXISTS),
Test("Try to move a resource that doesn't exist",
"crm_resource -r xyz --move --node node1",
expected_rc=ExitStatus.NOSUCH),
Test("Move a resource from its existing location",
"crm_resource -r dummy --move",
update_cib=True),
Test("Clear out constraints generated by --move",
"crm_resource -r dummy --clear",
update_cib=True),
Test("Ban a resource on unknown node",
"crm_resource -r dummy -B -N host1",
expected_rc=ExitStatus.NOSUCH),
Test("Create two more nodes and bring them online",
"crm_simulate --live-check --in-place --node-up=node2 --node-up=node3",
update_cib=True),
Test("Ban dummy from node1", "crm_resource -r dummy -B -N node1",
update_cib=True),
Test("Show where a resource is running", "crm_resource -r dummy -W"),
Test("Show constraints on a resource", "crm_resource -a -r dummy"),
ValidatingTest("Ban dummy from node2",
"crm_resource -r dummy -B -N node2 --output-as=xml",
update_cib=True),
Test("Relocate resources due to ban",
"crm_simulate --live-check --in-place -S",
update_cib=True),
ValidatingTest("Move dummy to node1",
"crm_resource -r dummy -M -N node1 --output-as=xml",
update_cib=True),
Test("Clear implicit constraints for dummy on node2",
"crm_resource -r dummy -U -N node2",
update_cib=True),
Test("Drop the status section",
"cibadmin -R -o status --xml-text ''"),
Test("Create a clone",
"""cibadmin -C -o resources --xml-text ''"""),
Test("Create a resource meta attribute",
"crm_resource -r test-primitive --meta -p is-managed -v false",
update_cib=True),
Test("Create a resource meta attribute in the primitive",
"crm_resource -r test-primitive --meta -p is-managed -v false --force",
update_cib=True),
Test("Update resource meta attribute with duplicates",
"crm_resource -r test-clone --meta -p is-managed -v true",
update_cib=True),
Test("Update resource meta attribute with duplicates (force clone)",
"crm_resource -r test-clone --meta -p is-managed -v true --force",
update_cib=True),
Test("Update child resource meta attribute with duplicates",
"crm_resource -r test-primitive --meta -p is-managed -v false",
update_cib=True),
Test("Delete resource meta attribute with duplicates",
"crm_resource -r test-clone --meta -d is-managed",
update_cib=True),
Test("Delete resource meta attribute in parent",
"crm_resource -r test-primitive --meta -d is-managed",
update_cib=True),
Test("Create a resource meta attribute in the primitive",
"crm_resource -r test-primitive --meta -p is-managed -v false --force",
update_cib=True),
Test("Update existing resource meta attribute",
"crm_resource -r test-clone --meta -p is-managed -v true",
update_cib=True),
Test("Create a resource meta attribute in the parent",
"crm_resource -r test-clone --meta -p is-managed -v true --force",
update_cib=True),
Test("Delete resource parent meta attribute (force)",
"crm_resource -r test-clone --meta -d is-managed --force",
update_cib=True),
# Restore meta-attributes before running this test
Test("Delete resource child meta attribute",
"crm_resource -r test-primitive --meta -d is-managed",
setup=["crm_resource -r test-primitive --meta -p is-managed -v true --force",
"crm_resource -r test-clone --meta -p is-managed -v true --force"],
update_cib=True),
Test("Create the dummy-group resource group",
"""cibadmin -C -o resources --xml-text '"""
""""""
""""""
"""'""",
update_cib=True),
Test("Create a resource meta attribute in dummy1",
"crm_resource -r dummy1 --meta -p is-managed -v true",
update_cib=True),
Test("Create a resource meta attribute in dummy-group",
"crm_resource -r dummy-group --meta -p is-managed -v false",
update_cib=True),
Test("Delete the dummy-group resource group",
"cibadmin -D -o resources --xml-text ''",
update_cib=True),
Test("Specify a lifetime when moving a resource",
"crm_resource -r dummy --move --node node2 --lifetime=PT1H",
update_cib=True),
Test("Try to move a resource previously moved with a lifetime",
"crm_resource -r dummy --move --node node1",
update_cib=True),
Test("Ban dummy from node1 for a short time",
"crm_resource -r dummy -B -N node1 --lifetime=PT1S",
update_cib=True),
Test("Remove expired constraints",
"sleep 2 && crm_resource --clear --expired",
update_cib=True),
# Clear has already been tested elsewhere, but we need to get rid of the
# constraints so testing delete works. It won't delete if there's still
# a reference to the resource somewhere.
Test("Clear all implicit constraints for dummy",
"crm_resource -r dummy -U",
update_cib=True),
Test("Set a node health strategy",
"crm_attribute -n node-health-strategy -v migrate-on-red",
update_cib=True),
Test("Set a node health attribute",
"crm_attribute -N node3 -n '#health-cts-cli' -v red",
update_cib=True),
ValidatingTest("Show why a resource is not running on an unhealthy node",
"crm_resource -N node3 -Y -r dummy --output-as=xml"),
Test("Delete a resource",
"crm_resource -D -r dummy -t primitive",
update_cib=True),
]
constraint_tests = []
for rsc in ["prim1", "prim2", "prim3", "prim4", "prim5", "prim6", "prim7",
"prim8", "prim9", "prim10", "prim11", "prim12", "prim13",
"group", "clone"]:
constraint_tests.extend([
make_test_group("Check locations and constraints for %s" % rsc,
"crm_resource -a -r %s {fmt}" % rsc,
[Test, ValidatingTest]),
make_test_group("Recursively check locations and constraints for %s" % rsc,
"crm_resource -A -r %s {fmt}" % rsc,
[Test, ValidatingTest]),
])
constraint_tests.extend([
Test("Check locations and constraints for group member (referring to group)",
"crm_resource -a -r gr2"),
Test("Check locations and constraints for group member (without referring to group)",
"crm_resource -a -r gr2 --force"),
])
colocation_tests = [
ValidatingTest("Set a meta-attribute for primitive and resources colocated with it",
"crm_resource -r prim5 --meta --set-parameter=target-role -v Stopped --recursive --output-as=xml"),
Test("Set a meta-attribute for group and resource colocated with it",
"crm_resource -r group --meta --set-parameter=target-role -v Stopped --recursive"),
ValidatingTest("Set a meta-attribute for clone and resource colocated with it",
"crm_resource -r clone --meta --set-parameter=target-role -v Stopped --recursive --output-as=xml"),
]
digest_tests = [
ValidatingTest("Show resource digests",
"crm_resource --digests -r rsc1 -N node1 --output-as=xml"),
Test("Show resource digests with overrides",
"crm_resource --digests -r rsc1 -N node1 --output-as=xml CRM_meta_interval=10000 CRM_meta_timeout=20000"),
make_test_group("Show resource operations", "crm_resource --list-operations {fmt}",
[Test, ValidatingTest]),
]
basic2_tests = [
make_test_group("List a promotable clone resource",
"crm_resource --locate -r promotable-clone {fmt}",
[Test, ValidatingTest]),
make_test_group("List the primitive of a promotable clone resource",
"crm_resource --locate -r promotable-rsc {fmt}",
[Test, ValidatingTest]),
make_test_group("List a single instance of a promotable clone resource",
"crm_resource --locate -r promotable-rsc:0 {fmt}",
[Test, ValidatingTest]),
make_test_group("List another instance of a promotable clone resource",
"crm_resource --locate -r promotable-rsc:1 {fmt}",
[Test, ValidatingTest]),
Test("Try to move an instance of a cloned resource",
"crm_resource -r promotable-rsc:0 --move --node node1",
expected_rc=ExitStatus.INVALID_PARAM),
]
basic_tests_setup = [
"crm_attribute -n no-quorum-policy -v ignore",
"crm_simulate --live-check --in-place --node-up=node1"
]
return options_tests + [
ShadowTestGroup(basic_tests, setup=basic_tests_setup),
- TestGroup(constraint_tests, env={"CIB_file": "{cts_cli_data}/constraints.xml"}),
- TestGroup(colocation_tests, cib_gen=partial(copy_existing_cib, "{cts_cli_data}/constraints.xml")),
- TestGroup(digest_tests, env={"CIB_file": "{cts_cli_data}/crm_resource_digests.xml"}),
- TestGroup(basic2_tests, env={"CIB_file": "{cts_cli_data}/crm_mon.xml"}),
+ TestGroup(constraint_tests, env={"CIB_file": f"{cts_cli_data}/constraints.xml"}),
+ TestGroup(colocation_tests, cib_gen=partial(copy_existing_cib, f"{cts_cli_data}/constraints.xml")),
+ TestGroup(digest_tests, env={"CIB_file": f"{cts_cli_data}/crm_resource_digests.xml"}),
+ TestGroup(basic2_tests, env={"CIB_file": f"{cts_cli_data}/crm_mon.xml"}),
ValidatingTest("Check that CIB_file=\"-\" works - crm_resource",
"crm_resource --digests -r rsc1 -N node1 --output-as=xml",
env={"CIB_file": "-"},
- stdin=pathlib.Path(apply_substitutions("{cts_cli_data}/crm_resource_digests.xml"))),
+ stdin=pathlib.Path(f"{cts_cli_data}/crm_resource_digests.xml")),
]
class CrmTicketRegressionTest(RegressionTest):
"""A class for testing crm_ticket."""
@property
def name(self):
"""Return the name of this regression test."""
return "crm_ticket"
@property
def tests(self):
"""A list of Test instances to be run as part of this regression test."""
basic_tests = [
Test("Default ticket granted state",
"crm_ticket -t ticketA -G granted -d false"),
Test("Set ticket granted state", "crm_ticket -t ticketA -r --force",
update_cib=True),
make_test_group("List ticket IDs", "crm_ticket -w {fmt}",
[Test, ValidatingTest]),
make_test_group("Query ticket state", "crm_ticket -t ticketA -q {fmt}",
[Test, ValidatingTest]),
make_test_group("Query ticket granted state",
"crm_ticket -t ticketA -G granted {fmt}",
[Test, ValidatingTest]),
Test("Delete ticket granted state",
"crm_ticket -t ticketA -D granted --force",
update_cib=True),
Test("Make a ticket standby", "crm_ticket -t ticketA -s",
update_cib=True),
Test("Query ticket standby state", "crm_ticket -t ticketA -G standby"),
Test("Activate a ticket", "crm_ticket -t ticketA -a",
update_cib=True),
make_test_group("List ticket details", "crm_ticket -L -t ticketA {fmt}",
[Test, ValidatingTest]),
Test("Add a second ticket", "crm_ticket -t ticketB -G granted -d false",
update_cib=True),
Test("Set second ticket granted state",
"crm_ticket -t ticketB -r --force",
update_cib=True),
make_test_group("List tickets", "crm_ticket -l {fmt}",
[Test, ValidatingTest]),
Test("Delete second ticket",
"""cibadmin --delete --xml-text ''""",
update_cib=True),
Test("Delete ticket standby state", "crm_ticket -t ticketA -D standby",
update_cib=True),
Test("Add a constraint to a ticket",
"""cibadmin -C -o constraints --xml-text ''""",
update_cib=True),
make_test_group("Query ticket constraints", "crm_ticket -t ticketA -c {fmt}",
[Test, ValidatingTest]),
Test("Delete ticket constraint",
"""cibadmin --delete --xml-text ''""",
update_cib=True),
]
basic_tests_setup = [
"""cibadmin -C -o crm_config --xml-text ''""",
"""cibadmin -C -o resources --xml-text ''"""
]
return [
ShadowTestGroup(basic_tests, setup=basic_tests_setup),
]
class CrmadminRegressionTest(RegressionTest):
"""A class for testing crmadmin."""
@property
def name(self):
"""Return the name of this regression test."""
return "crmadmin"
@property
def tests(self):
"""A list of Test instances to be run as part of this regression test."""
basic_tests = [
make_test_group("List all nodes", "crmadmin -N {fmt}",
[Test, ValidatingTest]),
make_test_group("Minimally list all nodes", "crmadmin -N -q {fmt}",
[Test, ValidatingTest]),
Test("List all nodes as bash exports", "crmadmin -N -B"),
make_test_group("List cluster nodes",
"crmadmin -N cluster {fmt}",
[Test, ValidatingTest]),
make_test_group("List guest nodes",
"crmadmin -N guest {fmt}",
[Test, ValidatingTest]),
make_test_group("List remote nodes",
"crmadmin -N remote {fmt}",
[Test, ValidatingTest]),
make_test_group("List cluster,remote nodes",
"crmadmin -N cluster,remote {fmt}",
[Test, ValidatingTest]),
make_test_group("List guest,remote nodes",
"crmadmin -N guest,remote {fmt}",
[Test, ValidatingTest]),
]
return [
TestGroup(basic_tests,
- env={"CIB_file": "{cts_cli_data}/crmadmin-cluster-remote-guest-nodes.xml"}),
+ env={"CIB_file": f"{cts_cli_data}/crmadmin-cluster-remote-guest-nodes.xml"}),
Test("Check that CIB_file=\"-\" works", "crmadmin -N",
env={"CIB_file": "-"},
- stdin=pathlib.Path(apply_substitutions("{cts_cli_data}/crmadmin-cluster-remote-guest-nodes.xml"))),
+ stdin=pathlib.Path(f"{cts_cli_data}/crmadmin-cluster-remote-guest-nodes.xml")),
]
class CrmShadowRegressionTest(RegressionTest):
"""A class for testing crm_shadow."""
@property
def name(self):
"""Return the name of this regression test."""
return "crm_shadow"
@property
def tests(self):
"""A list of Test instances to be run as part of this regression test."""
no_instance_tests = [
make_test_group("Get active shadow instance (no active instance)",
"crm_shadow --which {fmt}",
[Test, ValidatingTest], expected_rc=ExitStatus.NOSUCH),
make_test_group("Get active shadow instance's file name (no active instance)",
"crm_shadow --file {fmt}",
[Test, ValidatingTest], expected_rc=ExitStatus.NOSUCH),
make_test_group("Get active shadow instance's contents (no active instance)",
"crm_shadow --display {fmt}",
[Test, ValidatingTest], expected_rc=ExitStatus.NOSUCH),
make_test_group("Get active shadow instance's diff (no active instance)",
"crm_shadow --diff {fmt}",
[Test, ValidatingTest], expected_rc=ExitStatus.NOSUCH),
]
# Create new shadow instance based on active CIB
# Don't use create_shadow_cib() here; test explicitly
new_instance_tests = [
make_test_group("Create copied shadow instance",
"crm_shadow --create {shadow} --batch {fmt}",
[Test, ValidatingTest],
- setup="crm_shadow --delete {shadow} --force"),
+ setup=f"crm_shadow --delete {SHADOW_NAME} --force"),
# Query shadow instance based on active CIB
make_test_group("Get active shadow instance (copied)",
"crm_shadow --which {fmt}",
[Test, ValidatingTest]),
make_test_group("Get active shadow instance's file name (copied)",
"crm_shadow --file {fmt}",
[Test, ValidatingTest]),
make_test_group("Get active shadow instance's contents (copied)",
"crm_shadow --display {fmt}",
[Test, ValidatingTest]),
make_test_group("Get active shadow instance's diff (copied)",
"crm_shadow --diff {fmt}",
[Test, ValidatingTest]),
]
# Make some changes to the shadow file
modify_cib = """export CIB_file=$(crm_shadow --file) && """ \
"""cibadmin --modify --xml-text '' && """ \
"""cibadmin --delete --xml-text '' && """ \
"""cibadmin --create -o resources --xml-text '' && """ \
"""cibadmin --create -o status --xml-text ''"""
more_tests = [
# We can't use make_test_group() here because we only want to run
# the modify_cib setup code once, and make_test_group will pass all
# kwargs to every instance it creates.
Test("Get active shadow instance's diff (after changes)",
"crm_shadow --diff",
setup=modify_cib, expected_rc=ExitStatus.ERROR),
ValidatingTest("Get active shadow instance's diff (after changes)",
"crm_shadow --diff --output-as=xml",
expected_rc=ExitStatus.ERROR),
TestGroup([
# Commit the modified shadow CIB to a temp active CIB file
Test("Commit shadow instance",
- "crm_shadow --commit {shadow}",
+ f"crm_shadow --commit {SHADOW_NAME}",
expected_rc=ExitStatus.USAGE),
Test("Commit shadow instance (force)",
- "crm_shadow --commit {shadow} --force"),
+ f"crm_shadow --commit {SHADOW_NAME} --force"),
Test("Get active shadow instance's diff (after commit)",
"crm_shadow --diff",
expected_rc=ExitStatus.ERROR),
Test("Commit shadow instance (force) (all)",
- "crm_shadow --commit {shadow} --force --all"),
+ f"crm_shadow --commit {SHADOW_NAME} --force --all"),
Test("Get active shadow instance's diff (after commit all)",
"crm_shadow --diff",
expected_rc=ExitStatus.ERROR),
- ], cib_gen=partial(copy_existing_cib, "{cts_cli_data}/crm_mon.xml")),
+ ], cib_gen=partial(copy_existing_cib, f"{cts_cli_data}/crm_mon.xml")),
TestGroup([
# Repeat sequence with XML output
ValidatingTest("Commit shadow instance",
- "crm_shadow --commit {shadow} --output-as=xml",
+ f"crm_shadow --commit {SHADOW_NAME} --output-as=xml",
expected_rc=ExitStatus.USAGE),
ValidatingTest("Commit shadow instance (force)",
- "crm_shadow --commit {shadow} --force --output-as=xml"),
+ f"crm_shadow --commit {SHADOW_NAME} --force --output-as=xml"),
ValidatingTest("Get active shadow instance's diff (after commit)",
"crm_shadow --diff --output-as=xml",
expected_rc=ExitStatus.ERROR),
ValidatingTest("Commit shadow instance (force) (all)",
- "crm_shadow --commit {shadow} --force --all --output-as=xml"),
+ f"crm_shadow --commit {SHADOW_NAME} --force --all --output-as=xml"),
ValidatingTest("Get active shadow instance's diff (after commit all)",
"crm_shadow --diff --output-as=xml",
expected_rc=ExitStatus.ERROR),
# Commit an inactive shadow instance with no active instance
make_test_group("Commit shadow instance (no active instance)",
"crm_shadow --commit {shadow} {fmt}",
[Test, ValidatingTest],
env={"CIB_shadow": None},
expected_rc=ExitStatus.USAGE),
make_test_group("Commit shadow instance (no active instance) (force)",
"crm_shadow --commit {shadow} --force {fmt}",
[Test, ValidatingTest],
env={"CIB_shadow": None}),
# Commit an inactive shadow instance with an active instance
make_test_group("Commit shadow instance (mismatch)",
"crm_shadow --commit {shadow} {fmt}",
[Test, ValidatingTest],
env={"CIB_shadow": "nonexistent_shadow"},
expected_rc=ExitStatus.USAGE),
make_test_group("Commit shadow instance (mismatch) (force)",
"crm_shadow --commit {shadow} --force {fmt}",
[Test, ValidatingTest],
env={"CIB_shadow": "nonexistent_shadow"}),
# Commit an active shadow instance whose shadow file is missing
make_test_group("Commit shadow instance (nonexistent shadow file)",
"crm_shadow --commit nonexistent_shadow {fmt}",
[Test, ValidatingTest],
env={"CIB_shadow": "nonexistent_shadow"},
expected_rc=ExitStatus.USAGE),
make_test_group("Commit shadow instance (nonexistent shadow file) (force)",
"crm_shadow --commit nonexistent_shadow --force {fmt}",
[Test, ValidatingTest],
env={"CIB_shadow": "nonexistent_shadow"},
expected_rc=ExitStatus.NOSUCH),
make_test_group("Get active shadow instance's diff (nonexistent shadow file)",
"crm_shadow --diff {fmt}",
[Test, ValidatingTest],
env={"CIB_shadow": "nonexistent_shadow"},
expected_rc=ExitStatus.NOSUCH),
# Commit an active shadow instance when the CIB file is missing
make_test_group("Commit shadow instance (nonexistent CIB file)",
"crm_shadow --commit {shadow} {fmt}",
[Test, ValidatingTest],
- env={"CIB_file": "{cts_cli_data}/nonexistent_cib.xml"},
+ env={"CIB_file": f"{cts_cli_data}/nonexistent_cib.xml"},
expected_rc=ExitStatus.USAGE),
make_test_group("Commit shadow instance (nonexistent CIB file) (force)",
"crm_shadow --commit {shadow} --force {fmt}",
[Test, ValidatingTest],
- env={"CIB_file": "{cts_cli_data}/nonexistent_cib.xml"},
+ env={"CIB_file": f"{cts_cli_data}/nonexistent_cib.xml"},
expected_rc=ExitStatus.NOSUCH),
make_test_group("Get active shadow instance's diff (nonexistent CIB file)",
"crm_shadow --diff {fmt}",
[Test, ValidatingTest],
- env={"CIB_file": "{cts_cli_data}/nonexistent_cib.xml"},
+ env={"CIB_file": f"{cts_cli_data}/nonexistent_cib.xml"},
expected_rc=ExitStatus.NOSUCH),
- ], cib_gen=partial(copy_existing_cib, "{cts_cli_data}/crm_mon.xml")),
+ ], cib_gen=partial(copy_existing_cib, f"{cts_cli_data}/crm_mon.xml")),
]
delete_1_tests = [
# Delete an active shadow instance
- Test("Delete shadow instance", "crm_shadow --delete {shadow}",
+ Test("Delete shadow instance", f"crm_shadow --delete {SHADOW_NAME}",
expected_rc=ExitStatus.USAGE),
- Test("Delete shadow instance (force)", "crm_shadow --delete {shadow} --force"),
+ Test("Delete shadow instance (force)", f"crm_shadow --delete {SHADOW_NAME} --force"),
ShadowTestGroup([
ValidatingTest("Delete shadow instance",
- "crm_shadow --delete {shadow} --output-as=xml",
+ f"crm_shadow --delete {SHADOW_NAME} --output-as=xml",
expected_rc=ExitStatus.USAGE),
ValidatingTest("Delete shadow instance (force)",
- "crm_shadow --delete {shadow} --force --output-as=xml"),
+ f"crm_shadow --delete {SHADOW_NAME} --force --output-as=xml"),
])
]
delete_2_tests = [
# Delete an inactive shadow instance with no active instance
Test("Delete shadow instance (no active instance)",
- "crm_shadow --delete {shadow}",
+ f"crm_shadow --delete {SHADOW_NAME}",
expected_rc=ExitStatus.USAGE),
Test("Delete shadow instance (no active instance) (force)",
- "crm_shadow --delete {shadow} --force"),
+ f"crm_shadow --delete {SHADOW_NAME} --force"),
]
delete_3_tests = [
ValidatingTest("Delete shadow instance (no active instance)",
- "crm_shadow --delete {shadow} --output-as=xml",
+ f"crm_shadow --delete {SHADOW_NAME} --output-as=xml",
expected_rc=ExitStatus.USAGE),
ValidatingTest("Delete shadow instance (no active instance) (force)",
- "crm_shadow --delete {shadow} --force --output-as=xml"),
+ f"crm_shadow --delete {SHADOW_NAME} --force --output-as=xml"),
]
delete_4_tests = [
# Delete an inactive shadow instance with an active instance
Test("Delete shadow instance (mismatch)",
- "crm_shadow --delete {shadow}",
+ f"crm_shadow --delete {SHADOW_NAME}",
expected_rc=ExitStatus.USAGE),
Test("Delete shadow instance (mismatch) (force)",
- "crm_shadow --delete {shadow} --force"),
+ f"crm_shadow --delete {SHADOW_NAME} --force"),
]
delete_5_tests = [
ValidatingTest("Delete shadow instance (mismatch)",
- "crm_shadow --delete {shadow} --output-as=xml",
+ f"crm_shadow --delete {SHADOW_NAME} --output-as=xml",
expected_rc=ExitStatus.USAGE),
ValidatingTest("Delete shadow instance (mismatch) (force)",
- "crm_shadow --delete {shadow} --force --output-as=xml"),
+ f"crm_shadow --delete {SHADOW_NAME} --force --output-as=xml"),
# Delete an active shadow instance whose shadow file is missing
Test("Delete shadow instance (nonexistent shadow file)",
"crm_shadow --delete nonexistent_shadow",
expected_rc=ExitStatus.USAGE),
Test("Delete shadow instance (nonexistent shadow file) (force)",
"crm_shadow --delete nonexistent_shadow --force"),
ValidatingTest("Delete shadow instance (nonexistent shadow file)",
"crm_shadow --delete nonexistent_shadow --output-as=xml",
expected_rc=ExitStatus.USAGE),
ValidatingTest("Delete shadow instance (nonexistent shadow file) (force)",
"crm_shadow --delete nonexistent_shadow --force --output-as=xml"),
]
delete_6_tests = [
# Delete an active shadow instance when the CIB file is missing
Test("Delete shadow instance (nonexistent CIB file)",
- "crm_shadow --delete {shadow}",
+ f"crm_shadow --delete {SHADOW_NAME}",
expected_rc=ExitStatus.USAGE),
Test("Delete shadow instance (nonexistent CIB file) (force)",
- "crm_shadow --delete {shadow} --force"),
+ f"crm_shadow --delete {SHADOW_NAME} --force"),
]
delete_7_tests = [
ValidatingTest("Delete shadow instance (nonexistent CIB file)",
- "crm_shadow --delete {shadow} --output-as=xml",
+ f"crm_shadow --delete {SHADOW_NAME} --output-as=xml",
expected_rc=ExitStatus.USAGE),
ValidatingTest("Delete shadow instance (nonexistent CIB file) (force)",
- "crm_shadow --delete {shadow} --force --output-as=xml"),
+ f"crm_shadow --delete {SHADOW_NAME} --force --output-as=xml"),
]
create_1_tests = [
# Create new shadow instance based on active CIB with no instance active
make_test_group("Create copied shadow instance (no active instance)",
"crm_shadow --create {shadow} --batch {fmt}",
[Test, ValidatingTest],
- setup="crm_shadow --delete {shadow} --force",
+ setup=f"crm_shadow --delete {SHADOW_NAME} --force",
env={"CIB_shadow": None}),
# Create new shadow instance based on active CIB with other instance active
make_test_group("Create copied shadow instance (mismatch)",
"crm_shadow --create {shadow} --batch {fmt}",
[Test, ValidatingTest],
- setup="crm_shadow --delete {shadow} --force",
+ setup=f"crm_shadow --delete {SHADOW_NAME} --force",
env={"CIB_shadow": "nonexistent_shadow"}),
# Create new shadow instance based on CIB (shadow file already exists)
make_test_group("Create copied shadow instance (file already exists)",
"crm_shadow --create {shadow} --batch {fmt}",
[Test, ValidatingTest],
expected_rc=ExitStatus.CANTCREAT),
make_test_group("Create copied shadow instance (file already exists) (force)",
"crm_shadow --create {shadow} --batch --force {fmt}",
[Test, ValidatingTest]),
# Create new shadow instance based on active CIB when the CIB file is missing
make_test_group("Create copied shadow instance (nonexistent CIB file) (force)",
"crm_shadow --create {shadow} --batch --force {fmt}",
[Test, ValidatingTest],
expected_rc=ExitStatus.NOSUCH,
- setup="crm_shadow --delete {shadow} --force",
- env={"CIB_file": "{cts_cli_data}/nonexistent_cib.xml"}),
+ setup=f"crm_shadow --delete {SHADOW_NAME} --force",
+ env={"CIB_file": f"{cts_cli_data}/nonexistent_cib.xml"}),
]
create_2_tests = [
# Create new empty shadow instance
make_test_group("Create empty shadow instance",
"crm_shadow --create-empty {shadow} --batch {fmt}",
[Test, ValidatingTest],
- setup="crm_shadow --delete {shadow} --force"),
+ setup=f"crm_shadow --delete {SHADOW_NAME} --force"),
# Create empty shadow instance with no active instance
make_test_group("Create empty shadow instance (no active instance)",
"crm_shadow --create-empty {shadow} --batch {fmt}",
[Test, ValidatingTest],
- setup="crm_shadow --delete {shadow} --force",
+ setup=f"crm_shadow --delete {SHADOW_NAME} --force",
env={"CIB_shadow": None}),
# Create empty shadow instance with other instance active
make_test_group("Create empty shadow instance (mismatch)",
"crm_shadow --create-empty {shadow} --batch {fmt}",
[Test, ValidatingTest],
- setup="crm_shadow --delete {shadow} --force",
+ setup=f"crm_shadow --delete {SHADOW_NAME} --force",
env={"CIB_shadow": "nonexistent_shadow"}),
# Create empty shadow instance when the CIB file is missing
make_test_group("Create empty shadow instance (nonexistent CIB file)",
"crm_shadow --create-empty {shadow} --batch {fmt}",
[Test, ValidatingTest],
- setup="crm_shadow --delete {shadow} --force",
- env={"CIB_file": "{cts_cli_data}/nonexistent_cib.xml"}),
+ setup=f"crm_shadow --delete {SHADOW_NAME} --force",
+ env={"CIB_file": f"{cts_cli_data}/nonexistent_cib.xml"}),
# Create empty shadow instance (shadow file already exists)
make_test_group("Create empty shadow instance (file already exists)",
"crm_shadow --create-empty {shadow} --batch {fmt}",
[Test, ValidatingTest],
expected_rc=ExitStatus.CANTCREAT),
make_test_group("Create empty shadow instance (file already exists) (force)",
"crm_shadow --create-empty {shadow} --batch --force {fmt}",
[Test, ValidatingTest]),
# Query shadow instance with an empty CIB.
# --which and --file queries were done earlier.
TestGroup([
make_test_group("Get active shadow instance's contents (empty CIB)",
"crm_shadow --display {fmt}",
[Test, ValidatingTest]),
make_test_group("Get active shadow instance's diff (empty CIB)",
"crm_shadow --diff {fmt}",
[Test, ValidatingTest],
expected_rc=ExitStatus.ERROR),
], setup=delete_shadow_resource_defaults),
]
reset_1_tests = [
Test("Resetting active shadow instance to active CIB requires force",
- "crm_shadow --reset {shadow} --batch",
+ f"crm_shadow --reset {SHADOW_NAME} --batch",
expected_rc=ExitStatus.USAGE),
Test("Reset active shadow instance to active CIB",
- "crm_shadow --reset {shadow} --batch --force"),
+ f"crm_shadow --reset {SHADOW_NAME} --batch --force"),
Test("Active shadow instance no different from active CIB after reset",
"crm_shadow --diff"),
Test("Active shadow instance differs from active CIB after change",
"crm_shadow --diff",
setup="crm_attribute -n admin_epoch -v 99",
expected_rc=ExitStatus.ERROR),
ValidatingTest("Reset active shadow instance to active CIB",
- "crm_shadow --reset {shadow} --batch --force --output-as=xml"),
+ f"crm_shadow --reset {SHADOW_NAME} --batch --force --output-as=xml"),
ValidatingTest("Active shadow instance no different from active CIB after reset",
"crm_shadow --diff --output-as=xml"),
ValidatingTest("Active shadow instance differs from active CIB after change",
"crm_shadow --diff --output-as=xml",
setup="crm_attribute -n admin_epoch -v 199",
expected_rc=ExitStatus.ERROR),
make_test_group("Reset shadow instance to active CIB with nonexistent shadow file",
"crm_shadow --reset {shadow} --batch --force {fmt}",
[Test, ValidatingTest],
- setup="crm_shadow --delete {shadow} --force"),
+ setup=f"crm_shadow --delete {SHADOW_NAME} --force"),
Test("Active shadow instance no different from active CIB after force-reset",
"crm_shadow --diff"),
]
reset_2_tests = [
make_test_group("Reset inactive shadow instance (none active) to active CIB",
"crm_shadow --reset {shadow} --force --batch {fmt}",
[Test, ValidatingTest]),
]
reset_3_tests = [
make_test_group("Reset inactive shadow instance while another instance active",
"crm_shadow --reset {shadow} --batch --force {fmt}",
[Test, ValidatingTest]),
]
reset_4_tests = [
make_test_group("Reset shadow instance with nonexistent CIB",
"crm_shadow --reset {shadow} --batch --force {fmt}",
[Test, ValidatingTest],
expected_rc=ExitStatus.NOSUCH),
]
# Switch shadow instances
switch_tests = [
make_test_group("Switch to new shadow instance",
"crm_shadow --switch {shadow} --batch {fmt}",
[Test, ValidatingTest]),
TestGroup([
make_test_group("Switch to nonexistent shadow instance",
"crm_shadow --switch {shadow} --batch {fmt}",
[Test, ValidatingTest],
expected_rc=ExitStatus.NOSUCH),
make_test_group("Switch to nonexistent shadow instance (force)",
"crm_shadow --switch {shadow} --batch --force {fmt}",
[Test, ValidatingTest],
expected_rc=ExitStatus.NOSUCH),
- ], setup="crm_shadow --delete {shadow} --force"),
+ ], setup=f"crm_shadow --delete {SHADOW_NAME} --force"),
]
return no_instance_tests + [
ShadowTestGroup(new_instance_tests + more_tests,
- env={"CIB_file": "{cts_cli_data}/crm_mon.xml"},
+ env={"CIB_file": f"{cts_cli_data}/crm_mon.xml"},
create=False),
ShadowTestGroup(delete_1_tests,
- env={"CIB_file": "{cts_cli_data}/crm_mon.xml"}),
+ env={"CIB_file": f"{cts_cli_data}/crm_mon.xml"}),
ShadowTestGroup(delete_2_tests,
- env={"CIB_file": "{cts_cli_data}/crm_mon.xml",
+ env={"CIB_file": f"{cts_cli_data}/crm_mon.xml",
"CIB_shadow": None}),
ShadowTestGroup(delete_3_tests,
- env={"CIB_file": "{cts_cli_data}/crm_mon.xml",
+ env={"CIB_file": f"{cts_cli_data}/crm_mon.xml",
"CIB_shadow": None}),
ShadowTestGroup(delete_4_tests,
- env={"CIB_file": "{cts_cli_data}/crm_mon.xml",
+ env={"CIB_file": f"{cts_cli_data}/crm_mon.xml",
"CIB_shadow": "nonexistent_shadow"}),
ShadowTestGroup(delete_5_tests,
- env={"CIB_file": "{cts_cli_data}/crm_mon.xml",
+ env={"CIB_file": f"{cts_cli_data}/crm_mon.xml",
"CIB_shadow": "nonexistent_shadow"}),
ShadowTestGroup(delete_6_tests,
- env={"CIB_file": "{cts_cli_data}/nonexistent_cib.xml"}),
+ env={"CIB_file": f"{cts_cli_data}/nonexistent_cib.xml"}),
ShadowTestGroup(delete_7_tests,
- env={"CIB_file": "{cts_cli_data}/nonexistent_cib.xml"}),
+ env={"CIB_file": f"{cts_cli_data}/nonexistent_cib.xml"}),
ShadowTestGroup(create_1_tests,
- env={"CIB_file": "{cts_cli_data}/crm_mon.xml"},
+ env={"CIB_file": f"{cts_cli_data}/crm_mon.xml"},
create=False),
ShadowTestGroup(create_2_tests,
- env={"CIB_file": "{cts_cli_data}/crm_mon.xml"},
+ env={"CIB_file": f"{cts_cli_data}/crm_mon.xml"},
create=False),
ShadowTestGroup(reset_1_tests,
- env={"CIB_file": "{cts_cli_data}/crm_mon.xml"}),
+ env={"CIB_file": f"{cts_cli_data}/crm_mon.xml"}),
ShadowTestGroup(reset_2_tests,
- env={"CIB_file": "{cts_cli_data}/crm_mon.xml",
+ env={"CIB_file": f"{cts_cli_data}/crm_mon.xml",
"CIB_shadow": None}),
ShadowTestGroup(reset_3_tests,
- env={"CIB_file": "{cts_cli_data}/crm_mon.xml",
+ env={"CIB_file": f"{cts_cli_data}/crm_mon.xml",
"CIB_shadow": "nonexistent_shadow"}),
ShadowTestGroup(reset_4_tests,
- env={"CIB_file": "{cts_cli_data}/nonexistent_cib.xml"}),
+ env={"CIB_file": f"{cts_cli_data}/nonexistent_cib.xml"}),
ShadowTestGroup(switch_tests,
env={"CIB_shadow": "nonexistent_shadow"},
create_empty=True),
]
class CrmVerifyRegressionTest(RegressionTest):
"""A class for testing crm_verify."""
@property
def name(self):
"""Return the name of this regression test."""
return "crm_verify"
@property
def tests(self):
"""A list of Test instances to be run as part of this regression test."""
invalid_tests = [
make_test_group("Verify a file-specified invalid configuration",
"crm_verify --xml-file {cts_cli_data}/crm_verify_invalid_bz.xml {fmt}",
[Test, ValidatingTest],
expected_rc=ExitStatus.CONFIG),
make_test_group("Verify a file-specified invalid configuration (verbose)",
"crm_verify --xml-file {cts_cli_data}/crm_verify_invalid_bz.xml --verbose {fmt}",
[Test, ValidatingTest],
expected_rc=ExitStatus.CONFIG),
make_test_group("Verify a file-specified invalid configuration (quiet)",
"crm_verify --xml-file {cts_cli_data}/crm_verify_invalid_bz.xml --quiet {fmt}",
[Test, ValidatingTest],
expected_rc=ExitStatus.CONFIG),
ValidatingTest("Verify another file-specified invalid configuration",
- "crm_verify --xml-file {cts_cli_data}/crm_verify_invalid_no_stonith.xml --output-as=xml",
+ f"crm_verify --xml-file {cts_cli_data}/crm_verify_invalid_no_stonith.xml --output-as=xml",
expected_rc=ExitStatus.CONFIG),
]
with open("%s/cli/crm_mon.xml" % test_home, encoding="utf-8") as f:
cib_contents = f.read()
valid_tests = [
ValidatingTest("Verify a file-specified valid configuration",
- "crm_verify --xml-file {cts_cli_data}/crm_mon.xml --output-as=xml"),
+ f"crm_verify --xml-file {cts_cli_data}/crm_mon.xml --output-as=xml"),
ValidatingTest("Verify a piped-in valid configuration",
"crm_verify -p --output-as=xml",
- stdin=pathlib.Path(apply_substitutions("{cts_cli_data}/crm_mon.xml"))),
+ stdin=pathlib.Path(f"{cts_cli_data}/crm_mon.xml")),
ValidatingTest("Verbosely verify a file-specified valid configuration",
- "crm_verify --xml-file {cts_cli_data}/crm_mon.xml --output-as=xml --verbose"),
+ f"crm_verify --xml-file {cts_cli_data}/crm_mon.xml --output-as=xml --verbose"),
ValidatingTest("Verbosely verify a piped-in valid configuration",
"crm_verify -p --output-as=xml --verbose",
- stdin=pathlib.Path(apply_substitutions("{cts_cli_data}/crm_mon.xml"))),
+ stdin=pathlib.Path(f"{cts_cli_data}/crm_mon.xml")),
ValidatingTest("Verify a string-supplied valid configuration",
"crm_verify -X '%s' --output-as=xml" % cib_contents),
ValidatingTest("Verbosely verify a string-supplied valid configuration",
"crm_verify -X '%s' --output-as=xml --verbose" % cib_contents),
]
return invalid_tests + valid_tests
class CrmSimulateRegressionTest(RegressionTest):
"""A class for testing crm_simulate."""
@property
def name(self):
"""Return the name of this regression test."""
return "crm_simulate"
@property
def tests(self):
"""A list of Test instances to be run as part of this regression test."""
good_cib = """
"""
bad_cib = good_cib.replace("start", "break")
bad_version_cib = good_cib.replace("pacemaker-1.2", "pacemaker-9999.0")
recoverable_cib = good_cib.replace("", "")
no_version_cib = good_cib.replace('validate-with="pacemaker-1.2" ', "")
no_version_bad_cib = bad_version_cib.replace('epoch="3"', 'epoch="30"').replace("start", "break")
basic_tests = [
Test("Show allocation scores with crm_simulate",
"crm_simulate -x {cts_cli_data}/crm_mon.xml --show-scores --output-as=xml"),
Test("Show utilization with crm_simulate",
"crm_simulate -x {cts_cli_data}/crm_mon.xml --show-utilization"),
Test("Simulate injecting a failure",
"crm_simulate -x {cts_cli_data}/crm_mon.xml -S -i ping_monitor_10000@cluster02=1"),
Test("Simulate bringing a node down",
"crm_simulate -x {cts_cli_data}/crm_mon.xml -S --node-down=cluster01"),
Test("Simulate a node failing",
"crm_simulate -x {cts_cli_data}/crm_mon.xml -S --node-fail=cluster02"),
Test("Run crm_simulate with invalid CIB (enum violation)",
"crm_simulate -p -S",
stdin=bad_cib,
env={"PCMK_trace_functions": "apply_upgrade,pcmk__update_schema"},
expected_rc=ExitStatus.CONFIG),
Test("Run crm_simulate with invalid CIB (unrecognized validate-with)",
"crm_simulate -p -S",
stdin=bad_version_cib,
env={"PCMK_trace_functions": "apply_upgrade,pcmk__update_schema"},
expected_rc=ExitStatus.CONFIG),
Test("Run crm_simulate with invalid, but possibly recoverable CIB (valid with X.Y+1)",
"crm_simulate -p -S",
stdin=recoverable_cib,
env={"PCMK_trace_functions": "apply_upgrade,pcmk__update_schema"}),
Test("Run crm_simulate with valid CIB, but without validate-with attribute",
"crm_simulate -p -S",
stdin=no_version_cib,
env={"PCMK_trace_functions": "apply_upgrade,pcmk__update_schema"},
expected_rc=ExitStatus.CONFIG),
Test("Run crm_simulate with invalid CIB, also without validate-with attribute",
"crm_simulate -p -S",
stdin=no_version_bad_cib,
env={"PCMK_trace_functions": "apply_upgrade,pcmk__update_schema"},
expected_rc=ExitStatus.CONFIG),
]
return [
ShadowTestGroup(basic_tests, create=False,
env={"CIB_shadow": None}),
]
class CrmMonRegressionTest(RegressionTest):
"""A class for testing crm_mon."""
@property
def name(self):
"""Return the name of this regression test."""
return "crm_mon"
@property
def tests(self):
"""A list of Test instances to be run as part of this regression test."""
basic_tests = [
make_test_group("Basic output", "crm_mon -1 {fmt}",
[Test, ValidatingTest]),
make_test_group("Output without node section",
"crm_mon -1 --exclude=nodes {fmt}",
[Test, ValidatingTest]),
# The next test doesn't need to be performed for other output formats. It's
# really just a test to make sure that blank lines are correct.
Test("Output with only the node section",
"crm_mon -1 --exclude=all --include=nodes"),
# XML includes everything already so there's no need for a complete test
Test("Complete text output", "crm_mon -1 --include=all"),
# XML includes detailed output already
Test("Complete text output with detail", "crm_mon -1R --include=all"),
Test("Complete brief text output", "crm_mon -1 --include=all --brief"),
Test("Complete text output grouped by node",
"crm_mon -1 --include=all --group-by-node"),
# XML does not have a brief output option
Test("Complete brief text output grouped by node",
"crm_mon -1 --include=all --group-by-node --brief"),
ValidatingTest("Output grouped by node",
"crm_mon --output-as=xml --group-by-node"),
make_test_group("Complete output filtered by node",
"crm_mon -1 --include=all --node=cluster01 {fmt}",
[Test, ValidatingTest]),
make_test_group("Complete output filtered by tag",
"crm_mon -1 --include=all --node=even-nodes {fmt}",
[Test, ValidatingTest]),
make_test_group("Complete output filtered by resource tag",
"crm_mon -1 --include=all --resource=fencing-rscs {fmt}",
[Test, ValidatingTest]),
make_test_group("Output filtered by node that doesn't exist",
"crm_mon -1 --node=blah {fmt}",
[Test, ValidatingTest]),
Test("Basic text output with inactive resources", "crm_mon -1 -r"),
# XML already includes inactive resources
Test("Basic text output with inactive resources, filtered by node",
"crm_mon -1 -r --node=cluster02"),
make_test_group("Complete output filtered by primitive resource",
"crm_mon -1 --include=all --resource=Fencing {fmt}",
[Test, ValidatingTest]),
make_test_group("Complete output filtered by group resource",
"crm_mon -1 --include=all --resource=exim-group {fmt}",
[Test, ValidatingTest]),
Test("Complete text output filtered by group resource member",
"crm_mon -1 --include=all --resource=Public-IP"),
ValidatingTest("Output filtered by group resource member",
"crm_mon --output-as=xml --resource=Email"),
make_test_group("Complete output filtered by clone resource",
"crm_mon -1 --include=all --resource=ping-clone {fmt}",
[Test, ValidatingTest]),
make_test_group("Complete output filtered by clone resource instance",
"crm_mon -1 --include=all --resource=ping {fmt}",
[Test, ValidatingTest]),
Test("Complete text output filtered by exact clone resource instance",
"crm_mon -1 --include=all --show-detail --resource=ping:0"),
ValidatingTest("Output filtered by exact clone resource instance",
"crm_mon --output-as=xml --resource=ping:1"),
make_test_group("Output filtered by resource that doesn't exist",
"crm_mon -1 --resource=blah {fmt}",
[Test, ValidatingTest]),
Test("Basic text output with inactive resources, filtered by tag",
"crm_mon -1 -r --resource=inactive-rscs"),
Test("Basic text output with inactive resources, filtered by bundle resource",
"crm_mon -1 -r --resource=httpd-bundle"),
ValidatingTest("Output filtered by inactive bundle resource",
"crm_mon --output-as=xml --resource=httpd-bundle"),
Test("Basic text output with inactive resources, filtered by bundled IP address resource",
"crm_mon -1 -r --resource=httpd-bundle-ip-192.168.122.131"),
ValidatingTest("Output filtered by bundled IP address resource",
"crm_mon --output-as=xml --resource=httpd-bundle-ip-192.168.122.132"),
Test("Basic text output with inactive resources, filtered by bundled container",
"crm_mon -1 -r --resource=httpd-bundle-docker-1"),
ValidatingTest("Output filtered by bundled container",
"crm_mon --output-as=xml --resource=httpd-bundle-docker-2"),
Test("Basic text output with inactive resources, filtered by bundle connection",
"crm_mon -1 -r --resource=httpd-bundle-0"),
ValidatingTest("Output filtered by bundle connection",
"crm_mon --output-as=xml --resource=httpd-bundle-0"),
Test("Basic text output with inactive resources, filtered by bundled primitive resource",
"crm_mon -1 -r --resource=httpd"),
ValidatingTest("Output filtered by bundled primitive resource",
"crm_mon --output-as=xml --resource=httpd"),
Test("Complete text output, filtered by clone name in cloned group",
"crm_mon -1 --include=all --show-detail --resource=mysql-clone-group"),
ValidatingTest("Output, filtered by clone name in cloned group",
"crm_mon --output-as=xml --resource=mysql-clone-group"),
Test("Complete text output, filtered by group name in cloned group",
"crm_mon -1 --include=all --show-detail --resource=mysql-group"),
ValidatingTest("Output, filtered by group name in cloned group",
"crm_mon --output-as=xml --resource=mysql-group"),
Test("Complete text output, filtered by exact group instance name in cloned group",
"crm_mon -1 --include=all --show-detail --resource=mysql-group:1"),
ValidatingTest("Output, filtered by exact group instance name in cloned group",
"crm_mon --output-as=xml --resource=mysql-group:1"),
Test("Complete text output, filtered by primitive name in cloned group",
"crm_mon -1 --include=all --show-detail --resource=mysql-proxy"),
ValidatingTest("Output, filtered by primitive name in cloned group",
"crm_mon --output-as=xml --resource=mysql-proxy"),
Test("Complete text output, filtered by exact primitive instance name in cloned group",
"crm_mon -1 --include=all --show-detail --resource=mysql-proxy:1"),
ValidatingTest("Output, filtered by exact primitive instance name in cloned group",
"crm_mon --output-as=xml --resource=mysql-proxy:1"),
]
partial_tests = [
Test("Output of partially active resources", "crm_mon -1 --show-detail"),
ValidatingTest("Output of partially active resources", "crm_mon --output-as=xml"),
Test("Output of partially active resources, with inactive resources",
"crm_mon -1 -r --show-detail"),
# XML already includes inactive resources
Test("Complete brief text output, with inactive resources",
"crm_mon -1 -r --include=all --brief --show-detail"),
# XML does not have a brief output option
Test("Text output of partially active group", "crm_mon -1 --resource=partially-active-group"),
Test("Text output of partially active group, with inactive resources",
"crm_mon -1 --resource=partially-active-group -r"),
Test("Text output of active member of partially active group",
"crm_mon -1 --resource=dummy-1"),
Test("Text output of inactive member of partially active group",
"crm_mon -1 --resource=dummy-2 --show-detail"),
Test("Complete brief text output grouped by node, with inactive resources",
"crm_mon -1 -r --include=all --group-by-node --brief --show-detail"),
Test("Text output of partially active resources, with inactive resources, filtered by node",
"crm_mon -1 -r --node=cluster01"),
ValidatingTest("Output of partially active resources, filtered by node",
"crm_mon --output-as=xml --node=cluster01"),
]
unmanaged_tests = [
make_test_group("Output of active unmanaged resource on offline node",
"crm_mon -1 {fmt}",
[Test, ValidatingTest]),
Test("Brief text output of active unmanaged resource on offline node",
"crm_mon -1 --brief"),
Test("Brief text output of active unmanaged resource on offline node, grouped by node",
"crm_mon -1 --brief --group-by-node"),
]
maint1_tests = [
make_test_group("Output of all resources with maintenance-mode enabled",
"crm_mon -1 -r {fmt}",
[Test, ValidatingTest],
setup="crm_attribute -n maintenance-mode -v true",
teardown="crm_attribute -n maintenance-mode -v false"),
make_test_group("Output of all resources with maintenance enabled for a node",
"crm_mon -1 -r {fmt}",
[Test, ValidatingTest],
setup="crm_attribute -n maintenance -N cluster02 -v true",
teardown="crm_attribute -n maintenance -N cluster02 -v false"),
]
maint2_tests = [
# The fence resource is excluded, for comparison
make_test_group("Output of all resources with maintenance meta attribute true",
"crm_mon -1 -r {fmt}",
[Test, ValidatingTest]),
]
t180_tests = [
Test("Text output of guest node's container on different node from its remote resource",
"crm_mon -1"),
Test("Complete text output of guest node's container on different node from its remote resource",
"crm_mon -1 --show-detail"),
]
return [
TestGroup(basic_tests,
- env={"CIB_file": "{cts_cli_data}/crm_mon.xml"}),
+ env={"CIB_file": f"{cts_cli_data}/crm_mon.xml"}),
Test("Check that CIB_file=\"-\" works", "crm_mon -1",
env={"CIB_file": "-"},
- stdin=pathlib.Path(apply_substitutions("{cts_cli_data}/crm_mon.xml"))),
+ stdin=pathlib.Path(apply_substitutions(f"{cts_cli_data}/crm_mon.xml"))),
TestGroup(partial_tests,
- env={"CIB_file": "{cts_cli_data}/crm_mon-partial.xml"}),
+ env={"CIB_file": f"{cts_cli_data}/crm_mon-partial.xml"}),
TestGroup(unmanaged_tests,
- env={"CIB_file": "{cts_cli_data}/crm_mon-unmanaged.xml"}),
+ env={"CIB_file": f"{cts_cli_data}/crm_mon-unmanaged.xml"}),
TestGroup(maint1_tests,
- cib_gen=partial(copy_existing_cib, "{cts_cli_data}/crm_mon.xml")),
+ cib_gen=partial(copy_existing_cib, f"{cts_cli_data}/crm_mon.xml")),
TestGroup(maint2_tests,
- env={"CIB_file": "{cts_cli_data}/crm_mon-rsc-maint.xml"}),
+ env={"CIB_file": f"{cts_cli_data}/crm_mon-rsc-maint.xml"}),
TestGroup(t180_tests,
- env={"CIB_file": "{cts_cli_data}/crm_mon-T180.xml"}),
+ env={"CIB_file": f"{cts_cli_data}/crm_mon-T180.xml"}),
]
class AclsRegressionTest(RegressionTest):
"""A class for testing access control lists."""
@property
def name(self):
"""Return the name of this regression test."""
return "acls"
@property
def tests(self):
"""A list of Test instances to be run as part of this regression test."""
acl_cib = """
"""
basic_tests = [
Test("Configure some ACLs", "cibadmin -M -o acls -p",
update_cib=True, stdin=acl_cib),
Test("Enable ACLs", "crm_attribute -n enable-acl -v true",
update_cib=True),
Test("Set cluster option", "crm_attribute -n no-quorum-policy -v ignore",
update_cib=True),
Test("New ACL role",
"""cibadmin --create -o acls --xml-text ''""",
update_cib=True),
Test("New ACL target",
"""cibadmin --create -o acls --xml-text ''""",
update_cib=True),
Test("Another ACL role",
"""cibadmin --create -o acls --xml-text ''""",
update_cib=True),
Test("Another ACL target",
"""cibadmin --create -o acls --xml-text ''""",
update_cib=True),
Test("Updated ACL",
"""cibadmin --replace -o acls --xml-text ''""",
update_cib=True),
]
no_acl_tests = [
Test("unknownguy: Query configuration", "cibadmin -Q",
expected_rc=ExitStatus.INSUFFICIENT_PRIV),
Test("unknownguy: Set enable-acl",
"crm_attribute -n enable-acl -v false",
expected_rc=ExitStatus.INSUFFICIENT_PRIV),
Test("unknownguy: Set stonith-enabled",
"crm_attribute -n stonith-enabled -v false",
expected_rc=ExitStatus.INSUFFICIENT_PRIV),
Test("unknownguy: Create a resource",
"""cibadmin -C -o resources --xml-text ''""",
expected_rc=ExitStatus.INSUFFICIENT_PRIV),
]
deny_cib_tests = [
Test("l33t-haxor: Query configuration",
"cibadmin -Q",
expected_rc=ExitStatus.INSUFFICIENT_PRIV),
Test("l33t-haxor: Set enable-acl",
"crm_attribute -n enable-acl -v false",
expected_rc=ExitStatus.INSUFFICIENT_PRIV),
Test("l33t-haxor: Set stonith-enabled",
"crm_attribute -n stonith-enabled -v false",
expected_rc=ExitStatus.INSUFFICIENT_PRIV),
Test("l33t-haxor: Create a resource",
"""cibadmin -C -o resources --xml-text ''""",
expected_rc=ExitStatus.INSUFFICIENT_PRIV),
]
observer_tests = [
Test("niceguy: Query configuration", "cibadmin -Q"),
Test("niceguy: Set enable-acl",
"crm_attribute -n enable-acl -v false",
expected_rc=ExitStatus.INSUFFICIENT_PRIV),
Test("niceguy: Set stonith-enabled",
"crm_attribute -n stonith-enabled -v false",
update_cib=True),
Test("niceguy: Create a resource",
"""cibadmin -C -o resources --xml-text ''""",
expected_rc=ExitStatus.INSUFFICIENT_PRIV),
Test("root: Query configuration", "cibadmin -Q",
env={"CIB_user": "root"}),
Test("root: Set stonith-enabled", "crm_attribute -n stonith-enabled -v true",
update_cib=True, env={"CIB_user": "root"}),
Test("root: Create a resource",
"""cibadmin -C -o resources --xml-text ''""",
update_cib=True, env={"CIB_user": "root"}),
# For use with later tests
Test("root: Create another resource (with description)",
"""cibadmin -C -o resources --xml-text ''""",
update_cib=True, env={"CIB_user": "root"}),
]
deny_cib_2_tests = [
Test("l33t-haxor: Create a resource meta attribute",
"crm_resource -r dummy --meta -p target-role -v Stopped",
expected_rc=ExitStatus.INSUFFICIENT_PRIV),
Test("l33t-haxor: Query a resource meta attribute",
"crm_resource -r dummy --meta -g target-role",
expected_rc=ExitStatus.INSUFFICIENT_PRIV),
Test("l33t-haxor: Remove a resource meta attribute",
"crm_resource -r dummy --meta -d target-role",
expected_rc=ExitStatus.INSUFFICIENT_PRIV),
]
observer_2_tests = [
Test("niceguy: Create a resource meta attribute",
"crm_resource -r dummy --meta -p target-role -v Stopped",
update_cib=True),
Test("niceguy: Query a resource meta attribute",
"crm_resource -r dummy --meta -g target-role",
update_cib=True),
Test("niceguy: Remove a resource meta attribute",
"crm_resource -r dummy --meta -d target-role",
update_cib=True),
Test("niceguy: Create a resource meta attribute",
"crm_resource -r dummy --meta -p target-role -v Started",
update_cib=True),
]
read_meta_tests = [
Test("badidea: Query configuration - implied deny", "cibadmin -Q"),
]
deny_cib_3_tests = [
Test("betteridea: Query configuration - explicit deny", "cibadmin -Q"),
]
replace_tests = [
TestGroup([
AclTest("niceguy: Replace - remove acls",
"cibadmin --replace -p",
setup="cibadmin --delete --xml-text ''",
expected_rc=ExitStatus.INSUFFICIENT_PRIV),
AclTest("niceguy: Replace - create resource",
"cibadmin --replace -p",
setup="""cibadmin -C -o resources --xml-text ''""",
expected_rc=ExitStatus.INSUFFICIENT_PRIV),
AclTest("niceguy: Replace - modify attribute (deny)",
"cibadmin --replace -p",
setup="crm_attribute -n enable-acl -v false",
expected_rc=ExitStatus.INSUFFICIENT_PRIV),
AclTest("niceguy: Replace - delete attribute (deny)",
"cibadmin --replace -p",
setup="""cibadmin --replace --xml-text ''""",
expected_rc=ExitStatus.INSUFFICIENT_PRIV),
AclTest("niceguy: Replace - create attribute (deny)",
"cibadmin --replace -p",
setup="""cibadmin --modify --xml-text ''""",
expected_rc=ExitStatus.INSUFFICIENT_PRIV),
], env={"CIB_user": "niceguy"}),
# admin role
TestGroup([
AclTest("bob: Replace - create attribute (direct allow)",
"cibadmin --replace -o resources -p",
setup="""cibadmin --modify --xml-text ''"""),
AclTest("bob: Replace - modify attribute (direct allow)",
"cibadmin --replace -o resources -p",
setup="""cibadmin --modify --xml-text ''"""),
AclTest("bob: Replace - delete attribute (direct allow)",
"cibadmin --replace -o resources -p",
setup="""cibadmin --replace -o resources --xml-text ''"""),
], env={"CIB_user": "bob"}),
# super_user role
TestGroup([
AclTest("joe: Replace - create attribute (inherited allow)",
"cibadmin --replace -o resources -p",
setup="""cibadmin --modify --xml-text ''"""),
AclTest("joe: Replace - modify attribute (inherited allow)",
"cibadmin --replace -o resources -p",
setup="""cibadmin --modify --xml-text ''"""),
AclTest("joe: Replace - delete attribute (inherited allow)",
"cibadmin --replace -o resources -p",
setup="""cibadmin --replace -o resources --xml-text ''"""),
], env={"CIB_user": "joe"}),
# rsc_writer role
TestGroup([
AclTest("mike: Replace - create attribute (allow overrides deny)",
"cibadmin --replace -o resources -p",
setup="""cibadmin --modify --xml-text ''"""),
AclTest("mike: Replace - modify attribute (allow overrides deny)",
"cibadmin --replace -o resources -p",
setup="""cibadmin --modify --xml-text ''"""),
AclTest("mike: Replace - delete attribute (allow overrides deny)",
"cibadmin --replace -o resources -p",
setup="""cibadmin --replace -o resources --xml-text ''"""),
# Create an additional resource for deny-overrides-allow testing
AclTest("mike: Create another resource",
"""cibadmin -C -o resources --xml-text ''""",
update_cib=True),
], env={"CIB_user": "mike"}),
# rsc_denied role
TestGroup([
AclTest("chris: Replace - create attribute (deny overrides allow)",
"cibadmin --replace -o resources -p",
setup="""cibadmin --modify --xml-text ''""",
expected_rc=ExitStatus.INSUFFICIENT_PRIV),
AclTest("chris: Replace - modify attribute (deny overrides allow)",
"cibadmin --replace -o resources -p",
setup="""cibadmin --modify --xml-text ''""",
expected_rc=ExitStatus.INSUFFICIENT_PRIV),
AclTest("chris: Replace - delete attribute (deny overrides allow)",
"cibadmin --replace -o resources -p",
setup="""cibadmin --replace -o resources --xml-text ''""",
expected_rc=ExitStatus.INSUFFICIENT_PRIV),
], env={"CIB_user": "chris"}),
]
loop_tests = [
# no ACL
TestGroup(no_acl_tests, env={"CIB_user": "unknownguy"}),
# deny /cib permission
TestGroup(deny_cib_tests, env={"CIB_user": "l33t-haxor"}),
# observer role
TestGroup(observer_tests, env={"CIB_user": "niceguy"}),
# deny /cib permission
TestGroup(deny_cib_2_tests, env={"CIB_user": "l33t-haxor"}),
# observer role
TestGroup(observer_2_tests, env={"CIB_user": "niceguy"}),
# read //meta_attributes
TestGroup(read_meta_tests, env={"CIB_user": "badidea"}),
# deny /cib, read //meta_attributes
TestGroup(deny_cib_3_tests, env={"CIB_user": "betteridea"}),
] + replace_tests
return [
ShadowTestGroup(basic_tests + [
TestGroup(loop_tests,
env={"PCMK_trace_functions": "pcmk__check_acl,pcmk__apply_creation_acl"})]),
]
class ValidityRegressionTest(RegressionTest):
"""A class for testing CIB validity."""
@property
def name(self):
"""Return the name of this regression test."""
return "validity"
@property
def tests(self):
"""A list of Test instances to be run as part of this regression test."""
basic_tests = [
# sanitize_output() strips out validate-with, so there's no point in
# outputting the CIB after tests that modify it
Test("Try to set unrecognized validate-with",
"cibadmin -M --xml-text ''",
expected_rc=ExitStatus.CONFIG),
Test("Try to remove validate-with attribute",
"cibadmin -R -p",
stdin=StdinCmd("""cibadmin -Q | sed 's#validate-with="[^"]*"##'"""),
expected_rc=ExitStatus.CONFIG),
Test("Try to use rsc_order first-action value disallowed by schema",
"cibadmin -M -o constraints --xml-text ''",
expected_rc=ExitStatus.CONFIG, update_cib=True),
Test("Try to use configuration legal only with schema after configured one",
"cibadmin -C -o configuration --xml-text ''",
expected_rc=ExitStatus.CONFIG, update_cib=True),
Test("Disable schema validation",
"cibadmin -M --xml-text ''",
expected_rc=ExitStatus.OK),
Test("Set invalid rsc_order first-action value (schema validation disabled)",
"cibadmin -M -o constraints --xml-text ''",
expected_rc=ExitStatus.OK, update_cib=True),
Test("Run crm_simulate with invalid rsc_order first-action "
"(schema validation disabled)",
"crm_simulate -SL",
expected_rc=ExitStatus.OK),
]
basic_tests_setup = [
"""cibadmin -C -o resources --xml-text ''""",
"""cibadmin -C -o resources --xml-text ''""",
"""cibadmin -C -o constraints --xml-text ''""",
]
return [
ShadowTestGroup(basic_tests, validate_with="pacemaker-1.2",
setup=basic_tests_setup,
env={"PCMK_trace_functions": "apply_upgrade,pcmk__update_schema,invert_action"}),
]
class UpgradeRegressionTest(RegressionTest):
"""A class for testing upgrading the CIB."""
@property
def name(self):
"""Return the name of this regression test."""
return "upgrade"
@property
def tests(self):
"""A list of Test instances to be run as part of this regression test."""
resource_cib = """
"""
basic_tests = [
Test("Set stonith-enabled=false", "crm_attribute -n stonith-enabled -v false",
update_cib=True),
Test("Configure the initial resource", "cibadmin -M -o resources -p",
update_cib=True, stdin=resource_cib),
Test("Upgrade to latest CIB schema (trigger 2.10.xsl + the wrapping)",
"cibadmin --upgrade --force -V -V",
update_cib=True),
Test("Query a resource instance attribute (shall survive)",
"crm_resource -r mySmartFuse -g requires",
update_cib=True),
]
return [
ShadowTestGroup(basic_tests, validate_with="pacemaker-2.10",
env={"PCMK_trace_functions": "apply_upgrade,pcmk__update_schema"})
]
class RulesRegressionTest(RegressionTest):
"""A class for testing support for CIB rules."""
@property
def name(self):
"""Return the name of this regression test."""
return "rules"
@property
def tests(self):
"""A list of Test instances to be run as part of this regression test."""
tomorrow = datetime.now() + timedelta(days=1)
rule_cib = """
""" % tomorrow.strftime("%F %T %z")
usage_tests = [
make_test_group("crm_rule given no arguments", "crm_rule {fmt}",
[Test, ValidatingTest], expected_rc=ExitStatus.USAGE),
make_test_group("crm_rule given no rule to check", "crm_rule -c {fmt}",
[Test, ValidatingTest], expected_rc=ExitStatus.USAGE),
make_test_group("crm_rule given invalid input XML",
"crm_rule -c -r blahblah -X invalidxml {fmt}",
[Test, ValidatingTest], expected_rc=ExitStatus.DATAERR),
make_test_group("crm_rule given invalid input XML on stdin",
"crm_rule -c -r blahblah -X - {fmt}",
[Test, ValidatingTest],
stdin=StdinCmd("echo invalidxml"),
expected_rc=ExitStatus.DATAERR),
]
basic_tests = [
make_test_group("Try to check a rule that doesn't exist",
"crm_rule -c -r blahblah {fmt}",
[Test, ValidatingTest], expected_rc=ExitStatus.NOSUCH),
make_test_group("Try to check a rule that has too many date_expressions",
"crm_rule -c -r cli-rule-too-many-date-expressions {fmt}",
[Test, ValidatingTest], expected_rc=ExitStatus.UNIMPLEMENT_FEATURE),
make_test_group("Verify basic rule is expired",
"crm_rule -c -r cli-prefer-rule-dummy-expired {fmt}",
[Test, ValidatingTest],
expected_rc=ExitStatus.EXPIRED),
make_test_group("Verify basic rule worked in the past",
"crm_rule -c -r cli-prefer-rule-dummy-expired -d 20180101 {fmt}",
[Test, ValidatingTest]),
make_test_group("Verify basic rule is not yet in effect",
"crm_rule -c -r cli-prefer-rule-dummy-not-yet {fmt}",
[Test, ValidatingTest],
expected_rc=ExitStatus.NOT_YET_IN_EFFECT),
make_test_group("Verify date_spec rule with years has expired",
"crm_rule -c -r cli-prefer-rule-dummy-date_spec-only-years {fmt}",
[Test, ValidatingTest],
expected_rc=ExitStatus.EXPIRED),
make_test_group("Verify multiple rules at once",
"crm_rule -c -r cli-prefer-rule-dummy-not-yet -r cli-prefer-rule-dummy-date_spec-only-years {fmt}",
[Test, ValidatingTest],
expected_rc=ExitStatus.EXPIRED),
make_test_group("Verify date_spec rule with years is in effect",
"crm_rule -c -r cli-prefer-rule-dummy-date_spec-only-years -d 20190201 {fmt}",
[Test, ValidatingTest]),
make_test_group("Try to check a rule whose date_spec does not contain years=",
"crm_rule -c -r cli-prefer-rule-dummy-date_spec-without-years {fmt}",
[Test, ValidatingTest],
expected_rc=ExitStatus.UNIMPLEMENT_FEATURE),
make_test_group("Try to check a rule with no date_expression",
"crm_rule -c -r cli-no-date_expression-rule {fmt}",
[Test, ValidatingTest],
expected_rc=ExitStatus.UNIMPLEMENT_FEATURE),
]
return usage_tests + [
TestGroup(basic_tests, cib_gen=partial(write_cib, rule_cib))
]
class FeatureSetRegressionTest(RegressionTest):
"""A class for testing support for version-specific features."""
@property
def name(self):
"""Return the name of this regression test."""
return "feature_set"
@property
def tests(self):
"""A list of Test instances to be run as part of this regression test."""
basic_tests = [
# Import the test CIB
Test("Import the test CIB",
- "cibadmin --replace --xml-file {cts_cli_data}/crm_mon-feature_set.xml",
+ f"cibadmin --replace --xml-file {cts_cli_data}/crm_mon-feature_set.xml",
update_cib=True),
Test("Complete text output, no mixed status",
"crm_mon -1 --show-detail"),
ValidatingTest("Output, no mixed status", "crm_mon --output-as=xml"),
# Modify the CIB to fake that the cluster has mixed versions
Test("Fake inconsistent feature set",
"crm_attribute --node=cluster02 --name=#feature-set --update=3.15.0 --lifetime=reboot",
update_cib=True),
Test("Complete text output, mixed status",
"crm_mon -1 --show-detail"),
ValidatingTest("Output, mixed status", "crm_mon --output-as=xml"),
]
return [
ShadowTestGroup(basic_tests),
]
# Tests that depend on resource agents and must be run in an installed
# environment
class AgentRegressionTest(RegressionTest):
"""A class for testing resource agents."""
@property
def name(self):
"""Return the name of this regression test."""
return "agents"
@property
def tests(self):
"""A list of Test instances to be run as part of this regression test."""
return [
make_test_group("Validate a valid resource configuration",
"crm_resource --validate --class ocf --provider pacemaker --agent Dummy {fmt}",
[Test, ValidatingTest]),
# Make the Dummy configuration invalid (op_sleep can't be a generic string)
make_test_group("Validate an invalid resource configuration",
"crm_resource --validate --class ocf --provider pacemaker --agent Dummy {fmt}",
[Test, ValidatingTest],
expected_rc=ExitStatus.NOT_CONFIGURED,
env={"OCF_RESKEY_op_sleep": "asdf"}),
]
def build_options():
"""Handle command line arguments."""
parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter,
description="Command line tool regression tests",
epilog="Default tests: %s\n"
"Other tests: agents (must be run in an installed environment)" %
" ".join(default_tests))
parser.add_argument("-j", "--jobs", metavar="JOBS", default=cpu_count() - 1, type=int,
help="The number of tests to run simultaneously")
parser.add_argument("-p", "--path", metavar="DIR", action="append",
help="Look for executables in DIR (may be specified multiple times)")
parser.add_argument("-r", "--run-only", metavar="TEST", choices=default_tests + ["tools"] + other_tests,
action="append",
help="Run only specified tests (may be specified multiple times)")
parser.add_argument("-s", "--save", action="store_true",
help="Save actual output as expected output")
parser.add_argument("-v", "--valgrind", action="store_true",
help="Run all commands under valgrind")
parser.add_argument("-V", "--verbose", action="store_true",
help="Display any differences from expected output")
args = parser.parse_args()
if args.path is None:
args.path = []
return args
def setup_environment(valgrind):
"""Set various environment variables needed for operation."""
if valgrind:
os.environ["G_SLICE"] = "always-malloc"
# Ensure all command output is in portable locale for comparison
os.environ["LC_ALL"] = "C"
# Log test errors to stderr
os.environ["PCMK_stderr"] = "1"
# Because we will change the value of PCMK_trace_functions and then reset it
# back to some initial value at various points, it's easiest to assume it is
# defined but empty by default
if "PCMK_trace_functions" not in os.environ:
os.environ["PCMK_trace_functions"] = ""
def path_prepend(p):
"""Add another directory to the front of $PATH."""
old = os.environ["PATH"]
os.environ["PATH"] = "%s:%s" % (p, old)
def setup_path(opts_path):
"""Set the PATH environment variable appropriately for the tests."""
srcdir = os.path.dirname(test_home)
# Add any search paths given on the command line
for p in opts_path:
path_prepend(p)
if os.path.exists("%s/tools/crm_simulate" % srcdir):
print("Using local binaries from: %s" % srcdir)
path_prepend("%s/tools" % srcdir)
for daemon in ["based", "controld", "fenced", "schedulerd"]:
path_prepend("%s/daemons/%s" % (srcdir, daemon))
print("Using local schemas from: %s/xml" % srcdir)
os.environ["PCMK_schema_directory"] = "%s/xml" % srcdir
else:
path_prepend(BuildOptions.DAEMON_DIR)
os.environ["PCMK_schema_directory"] = BuildOptions.SCHEMA_DIR
def _run_one(valgrind, r):
"""Run and return a TestGroup object."""
# See comments in run_regression_tests.
r.run(valgrind=valgrind)
return r
def run_regression_tests(regs, jobs, valgrind=False):
"""Run the given tests and return the modified objects."""
executed = []
with Pool(processes=jobs) as pool:
# What we really want to do here is:
# pool.map(lambda r: r.run(),regs)
#
# However, multiprocessing uses pickle somehow in its operation, and python
# doesn't want to pickle a lambda (nor a nested function within this one).
# Thus, we need to use the _run_one wrapper at the file level just to call
# run(). Further, if we don't return the modified object from that and then
# return the list of modified objects here, it looks like the rest of the
# program will use the originals, before this was ever run.
executed = pool.map(partial(_run_one, valgrind), regs)
return executed
def results(regs, save, verbose):
"""Print the output from each regression test, returning the number whose output differs."""
output_differs = 0
if verbose:
print("\n\nResults")
sys.stdout.flush()
for r in regs:
r.write()
if save:
dest = "%s/cli/regression.%s.exp" % (test_home, r.name)
copyfile(r.results_file, dest)
r.diff(verbose)
if not r.identical:
output_differs += 1
return output_differs
def summary(regs, output_differs, verbose):
"""Print the summary output for the entire test run."""
test_failures = 0
test_successes = 0
for r in regs:
test_failures += r.failures
test_successes += r.successes
print("\n\nSummary")
sys.stdout.flush()
# First, print all the Passed/Failed lines from each Test run.
for r in regs:
print("\n".join(r.summary))
fmt = PluralFormatter()
# Then, print information specific to each result possibility. Basically,
# if there were failures then we print the output differences, leave the
# failed output files in place, and exit with an error. Otherwise, clean up
# anything that passed.
if test_failures > 0 and output_differs > 0:
print(fmt.format("{0} {0:plural,test} failed; see output in:",
test_failures))
for r in regs:
r.process_results(verbose)
return ExitStatus.ERROR
if test_failures > 0:
print(fmt.format("{0} {0:plural,test} failed", test_failures))
for r in regs:
r.process_results(verbose)
return ExitStatus.ERROR
if output_differs:
print(fmt.format("{0} {0:plural,test} passed but output was "
"unexpected; see output in:", test_successes))
for r in regs:
r.process_results(verbose)
return ExitStatus.DIGEST
print(fmt.format("{0} {0:plural,test} passed", test_successes))
for r in regs:
r.cleanup()
return ExitStatus.OK
regression_classes = [
AccessRenderRegressionTest,
DaemonsRegressionTest,
DatesRegressionTest,
ErrorCodeRegressionTest,
CibadminRegressionTest,
CrmAttributeRegressionTest,
CrmStandbyRegressionTest,
CrmResourceRegressionTest,
CrmTicketRegressionTest,
CrmadminRegressionTest,
CrmShadowRegressionTest,
CrmVerifyRegressionTest,
CrmSimulateRegressionTest,
CrmMonRegressionTest,
AclsRegressionTest,
ValidityRegressionTest,
UpgradeRegressionTest,
RulesRegressionTest,
FeatureSetRegressionTest,
AgentRegressionTest,
]
def main():
"""Run command line regression tests as specified by arguments."""
opts = build_options()
setup_environment(opts.valgrind)
setup_path(opts.path)
# Filter the list of all regression test classes to include only those that
# were requested on the command line. If empty, this defaults to default_tests.
if not opts.run_only:
opts.run_only = default_tests
if opts.run_only == ["tools"]:
opts.run_only = tools_tests
regs = []
for cls in regression_classes:
obj = cls()
if obj.name in opts.run_only:
regs.append(obj)
regs = run_regression_tests(regs, max(1, opts.jobs), valgrind=opts.valgrind)
output_differs = results(regs, opts.save, opts.verbose)
rc = summary(regs, output_differs, opts.verbose)
sys.exit(rc)
if __name__ == "__main__":
main()
# vim: set filetype=python expandtab tabstop=4 softtabstop=4 shiftwidth=4 textwidth=120:
diff --git a/cts/cts-regression.in b/cts/cts-regression.in
index 69fb7a9a02..3f1119d3ed 100644
--- a/cts/cts-regression.in
+++ b/cts/cts-regression.in
@@ -1,291 +1,300 @@
#!@PYTHON@
"""Convenience wrapper for running Pacemaker regression tests.
Usage: cts-regression [-h] [-V] [-v] [COMPONENT ...]
"""
+# pylint doesn't like the module name "cts-regression" which is an invalid complaint for this file
+# but probably something we want to continue warning about elsewhere
+# pylint: disable=invalid-name
+# pacemaker imports need to come after we modify sys.path, which pylint will complain about.
+# pylint: disable=wrong-import-position
+
__copyright__ = 'Copyright 2012-2025 the Pacemaker project contributors'
__license__ = 'GNU General Public License version 2 or later (GPLv2+) WITHOUT ANY WARRANTY'
import argparse
import os
import subprocess
import sys
import textwrap
# These imports allow running from a source checkout after running `make`.
# Note that while this doesn't necessarily mean it will successfully run tests,
# but being able to see --help output can be useful.
if os.path.exists("@abs_top_srcdir@/python"):
sys.path.insert(0, "@abs_top_srcdir@/python")
+# pylint: disable=comparison-of-constants,comparison-with-itself,condition-evals-to-constant
if os.path.exists("@abs_top_builddir@/python") and "@abs_top_builddir@" != "@abs_top_srcdir@":
sys.path.insert(0, "@abs_top_builddir@/python")
from pacemaker.buildoptions import BuildOptions
from pacemaker.exitstatus import ExitStatus
+
class Component():
"""A class for running regression tests on a component.
"Component" refers to a Pacemaker component, such as the scheduler.
:attribute name: The name of the component.
:type name: str
:attribute description: The description of the component.
:type description: str
:attribute requires_root: Whether the component's tests must be run
as root.
:type requires_root: bool
:attribute supports_valgrind: Whether the component's tests support
running under valgrind.
:type supports_valgrind: bool
:attribute cmd: The command to run the component's tests, along with
any required options.
:type cmd: list[str]
:method run([verbose=False], [valgrind=False]): Run the component's
regression tests and return the result.
"""
def __init__(self, name, description, test_home, requires_root=False,
supports_valgrind=False):
- """Constructor for the :class:`Component` class.
+ """Create a new :class:`Component` instance.
:param name: The name of the component.
:type name: str
:param description: The description of the component.
:type description: str
:param test_home: The directory where the component's tests
reside.
:type test_home: str
:param requires_root: Whether the component's tests must be run
as root.
:type requires_root: bool
:param supports_valgrind: Whether the component's tests support
running under valgrind.
:type supports_valgrind: bool
"""
self.name = name
self.description = description
self.requires_root = requires_root
self.supports_valgrind = supports_valgrind
if self.name == 'pacemaker_remote':
self.cmd = [os.path.join(test_home, 'cts-exec'), '-R']
else:
self.cmd = [os.path.join(test_home, f"cts-{self.name}")]
def run(self, verbose=False, valgrind=False):
"""Run the component's regression tests and return the result.
:param verbose: Whether to increase test output verbosity.
:type verbose: bool
:param valgrind: Whether to run the test under valgrind.
:type valgrind: bool
:return: The exit code from the component's test suite.
:rtype: :class:`ExitStatus`
"""
print(f"Executing the {self.name} regression tests")
print('=' * 60)
cmd = self.cmd
if self.requires_root and os.geteuid() != 0:
print('Enter the sudo password if prompted')
cmd = ['sudo'] + self.cmd
if verbose:
cmd.append('--verbose')
if self.supports_valgrind and valgrind:
cmd.append('--valgrind')
try:
rc = ExitStatus(subprocess.call(cmd))
except OSError as err:
error_print(f"Failed to execute {self.name} tests: {err}")
rc = ExitStatus.NOT_INSTALLED
print('=' * 60 + '\n\n')
return rc
class ComponentsArgAction(argparse.Action):
"""A class to handle `components` arguments.
This class handles special cases and cleans up the `components`
list. Specifically, it does the following:
* Enforce a default value of ['cli', 'scheduler'].
* Replace the 'all' alias with the components that it represents.
* Get rid of duplicates.
The main motivation is that when the `choices` argument of
:meth:`parser.add_argument()` is specified, the `default` argument
must contain exactly one value (not `None` and not a list). We want
our default to be a list of components, namely `cli` and
`scheduler`.
"""
def __call__(self, parser, namespace, values, option_string=None):
+ """Process `components` arguments."""
all_components = ['attrd', 'cli', 'exec', 'fencing', 'scheduler']
default_components = ['cli', 'scheduler']
if not values:
setattr(namespace, self.dest, default_components)
return
# If no argument is specified, the default gets passed as a
# string 'default' instead of as a list ['default']. Probably
# a bug in argparse. The below gives us a list.
if not isinstance(values, list):
values = [values]
components = set(values)
# If 'all', is found, replace it with the components it represents.
try:
components.remove('all')
components.update(set(all_components))
except KeyError:
pass
# Same for 'default'
try:
components.remove('default')
components.update(set(default_components))
except KeyError:
pass
setattr(namespace, self.dest, sorted(list(components)))
def error_print(msg):
"""Print an error message.
:param msg: Message to print.
:type msg: str
"""
print(f" * ERROR: {msg}")
def run_components(components, verbose=False, valgrind=False):
"""Run components' regression tests and report results for each.
:param components: A list of names of components for which to run
tests.
:type components: list[:class:`Component`]
:return: :attr:`ExitStatus.OK` if all tests were successful,
:attr:`ExitStatus.ERROR` otherwise.
:rtype: :class:`ExitStatus`
"""
failed = []
for comp in components:
rc = comp.run(verbose, valgrind)
if rc != ExitStatus.OK:
error_print(f"{comp.name} regression tests failed ({rc})")
failed.append(comp.name)
if failed:
print('Failed regression tests:', end='')
for comp in failed:
print(f" {comp}", end='')
print()
return ExitStatus.ERROR
return ExitStatus.OK
def main():
"""Run Pacemaker regression tests as specified by arguments."""
try:
test_home = os.path.dirname(os.readlink(sys.argv[0]))
except OSError:
test_home = os.path.dirname(sys.argv[0])
# Available components
components = {
'attrd': Component(
'attrd',
'Attribute manager',
test_home,
requires_root=True,
supports_valgrind=False,
),
'cli': Component(
'cli',
'Command-line tools',
test_home,
requires_root=False,
supports_valgrind=True,
),
'exec': Component(
'exec',
'Local resource agent executor',
test_home,
requires_root=True,
supports_valgrind=False,
),
'fencing': Component(
'fencing',
'Fencer',
test_home,
requires_root=True,
supports_valgrind=False,
),
'scheduler': Component(
'scheduler',
'Action scheduler',
test_home,
requires_root=False,
supports_valgrind=True,
),
}
if BuildOptions.REMOTE_ENABLED:
components['pacemaker_remote'] = Component(
'pacemaker_remote',
'Resource agent executor in remote mode',
test_home,
requires_root=True,
supports_valgrind=False,
)
# Build up program description
description = textwrap.dedent('''\
Run Pacemaker regression tests.
Available components (default components are 'cli scheduler'):
''')
for name, comp in sorted(components.items()):
description += f"\n {name:<20} {comp.description}"
description += f'\n {"all":<20} Synonym for "cli exec fencing scheduler"'
# Parse the arguments
parser = argparse.ArgumentParser(
description=description,
formatter_class=argparse.RawDescriptionHelpFormatter,
)
choices = sorted(components.keys()) + ['all', 'default']
parser.add_argument('-V', '--verbose', action='store_true',
help='Increase test verbosity')
parser.add_argument('-v', '--valgrind', action='store_true',
help='Run test commands under valgrind')
parser.add_argument('components', nargs='*', choices=choices,
default='default',
action=ComponentsArgAction, metavar='COMPONENT',
help="One of the components to test, or 'all'")
args = parser.parse_args()
# Run the tests
selected = [components[x] for x in args.components]
rc = run_components(selected, args.verbose, args.valgrind)
sys.exit(rc)
if __name__ == '__main__':
main()
diff --git a/cts/cts-scheduler.in b/cts/cts-scheduler.in
index 450f0bd4d7..f5d4ed5c38 100644
--- a/cts/cts-scheduler.in
+++ b/cts/cts-scheduler.in
@@ -1,1718 +1,1737 @@
#!@PYTHON@
-""" Regression tests for Pacemaker's scheduler
-"""
+"""Regression tests for Pacemaker's scheduler."""
+
+# pylint doesn't like the module name "cts-scheduler" which is an invalid complaint for this file
+# but probably something we want to continue warning about elsewhere
+# pylint: disable=invalid-name
+# pacemaker imports need to come after we modify sys.path, which pylint will complain about.
+# pylint: disable=wrong-import-position
__copyright__ = "Copyright 2004-2025 the Pacemaker project contributors"
__license__ = "GNU General Public License version 2 or later (GPLv2+) WITHOUT ANY WARRANTY"
import io
import os
import re
import sys
import stat
import shlex
import shutil
import argparse
import subprocess
import platform
import tempfile
# These imports allow running from a source checkout after running `make`.
# Note that while this doesn't necessarily mean it will successfully run tests,
# but being able to see --help output can be useful.
if os.path.exists("@abs_top_srcdir@/python"):
sys.path.insert(0, "@abs_top_srcdir@/python")
+# pylint: disable=comparison-of-constants,comparison-with-itself,condition-evals-to-constant
if os.path.exists("@abs_top_builddir@/python") and "@abs_top_builddir@" != "@abs_top_srcdir@":
sys.path.insert(0, "@abs_top_builddir@/python")
from pacemaker.buildoptions import BuildOptions
from pacemaker.exitstatus import ExitStatus
-DESC = """Regression tests for Pacemaker's scheduler"""
class SchedulerTest:
+ """A single scheduler test."""
+
def __init__(self, name, desc, args=None):
+ """
+ Create a new SchedulerTest instance.
+
+ Arguments:
+ name -- A unique name for this test.
+ desc -- A meaningful description for the test.
+ args -- Additional arguments to pass when running this test
+ """
self.name = name
self.desc = desc
if args is None:
self.args = []
else:
self.args = args
+
class SchedulerTestGroup:
+ """Collection of scheduler regression tests."""
+
def __init__(self, tests):
+ """
+ Create a new SchedulerTestGroup instance.
+
+ Arguments:
+ tests -- A list of SchedulerTest instances to be executed as part of
+ this group.
+ """
self.tests = tests
+
# Each entry in TESTS is a group of tests, where each test consists of a
# test base name, test description, and additional test arguments.
# Test groups will be separated by newlines in output.
TESTS = [
SchedulerTestGroup([
SchedulerTest("simple1", "Offline"),
SchedulerTest("simple2", "Start"),
SchedulerTest("simple3", "Start 2"),
SchedulerTest("simple4", "Start Failed"),
SchedulerTest("simple6", "Stop Start"),
SchedulerTest("simple7", "Shutdown"),
SchedulerTest("simple11", "Priority (ne)"),
SchedulerTest("simple12", "Priority (eq)"),
SchedulerTest("simple8", "Stickiness"),
]),
SchedulerTestGroup([
SchedulerTest("group1", "Group"),
SchedulerTest("group2", "Group + Native"),
SchedulerTest("group3", "Group + Group"),
SchedulerTest("group4", "Group + Native (nothing)"),
SchedulerTest("group5", "Group + Native (move)"),
SchedulerTest("group6", "Group + Group (move)"),
SchedulerTest("group7", "Group colocation"),
SchedulerTest("group13", "Group colocation (cant run)"),
SchedulerTest("group8", "Group anti-colocation"),
SchedulerTest("group9", "Group recovery"),
SchedulerTest("group10", "Group partial recovery"),
SchedulerTest("group11", "Group target_role"),
SchedulerTest("group14", "Group stop (graph terminated)"),
SchedulerTest("group15", "Negative group colocation"),
SchedulerTest("bug-1573", "Partial stop of a group with two children"),
SchedulerTest("bug-1718", "Mandatory group ordering - Stop group_FUN"),
SchedulerTest("failed-sticky-group", "Move group on last member failure despite infinite stickiness"),
SchedulerTest("failed-sticky-anticolocated-group",
"Move group on last member failure despite infinite stickiness and optional anti-colocation"),
SchedulerTest("bug-lf-2619", "Move group on clone failure"),
SchedulerTest("group-fail", "Ensure stop order is preserved for partially active groups"),
SchedulerTest("group-unmanaged", "No need to restart r115 because r114 is unmanaged"),
SchedulerTest("group-unmanaged-stopped", "Make sure r115 is stopped when r114 fails"),
SchedulerTest("partial-unmanaged-group", "New member in partially unmanaged group"),
SchedulerTest("group-dependents", "Account for the location preferences of things colocated with a group"),
SchedulerTest("group-stop-ordering", "Ensure blocked group member stop does not force other member stops"),
SchedulerTest("colocate-unmanaged-group", "Respect mandatory colocations even if earlier group member is unmanaged"),
SchedulerTest("coloc-with-inner-group-member", "Consider explicit colocations with inner group members"),
SchedulerTest("banned-group-inner-constraints",
"Group banned from current node, inner member constrained"),
]),
SchedulerTestGroup([
SchedulerTest("rsc_dep1", "Must not"),
SchedulerTest("rsc_dep3", "Must"),
SchedulerTest("rsc_dep5", "Must not 3"),
SchedulerTest("rsc_dep7", "Must 3"),
SchedulerTest("rsc_dep10", "Must (but cant)"),
SchedulerTest("rsc_dep2", "Must (running)"),
SchedulerTest("rsc_dep8", "Must (running : alt)"),
SchedulerTest("rsc_dep4", "Must (running + move)"),
SchedulerTest("asymmetric", "Asymmetric - require explicit location constraints"),
]),
SchedulerTestGroup([
SchedulerTest("orphan-0", "Orphan ignore"),
SchedulerTest("orphan-1", "Orphan stop"),
SchedulerTest("orphan-2", "Orphan stop, remove failcount"),
]),
SchedulerTestGroup([
SchedulerTest("params-0", "Params: No change"),
SchedulerTest("params-1", "Params: Changed"),
SchedulerTest("params-2", "Params: Resource definition"),
SchedulerTest("params-3", "Params: Restart instead of reload if start pending"),
SchedulerTest("params-4", "Params: Reload"),
SchedulerTest("params-5", "Params: Restart based on probe digest"),
SchedulerTest("novell-251689", "Resource definition change + target_role=stopped"),
SchedulerTest("bug-lf-2106", "Restart all anonymous clone instances after config change"),
SchedulerTest("params-6", "Params: Detect reload in previously migrated resource"),
SchedulerTest("nvpair-id-ref", "Support id-ref in nvpair with optional name"),
SchedulerTest("not-reschedule-unneeded-monitor",
"Do not reschedule unneeded monitors while resource definitions have changed"),
SchedulerTest("reload-becomes-restart", "Cancel reload if restart becomes required"),
SchedulerTest("restart-with-extra-op-params", "Restart if with extra operation parameters upon changes of any"),
]),
SchedulerTestGroup([
SchedulerTest("target-0", "Target Role : baseline"),
SchedulerTest("target-1", "Target Role : promoted"),
SchedulerTest("target-2", "Target Role : invalid"),
]),
SchedulerTestGroup([
SchedulerTest("base-score", "Set a node's default score for all nodes"),
]),
SchedulerTestGroup([
SchedulerTest("date-1", "Dates", ["-t", "2005-020"]),
SchedulerTest("date-2", "Date Spec - Pass", ["-t", "2005-020T12:30"]),
SchedulerTest("date-3", "Date Spec - Fail", ["-t", "2005-020T11:30"]),
SchedulerTest("origin", "Timing of recurring operations", ["-t", "2014-05-07 00:28:00"]),
SchedulerTest("probe-0", "Probe (anon clone)"),
SchedulerTest("probe-1", "Pending Probe"),
SchedulerTest("probe-2", "Correctly re-probe cloned groups"),
SchedulerTest("probe-3", "Probe (pending node)"),
SchedulerTest("probe-4", "Probe (pending node + stopped resource)"),
SchedulerTest("probe-pending-node", "Probe (pending node + unmanaged resource)"),
SchedulerTest("failed-probe-primitive", "Maskable vs. unmaskable probe failures on primitive resources"),
SchedulerTest("failed-probe-clone", "Maskable vs. unmaskable probe failures on cloned resources"),
SchedulerTest("expired-failed-probe-primitive", "Maskable, expired probe failure on primitive resources"),
SchedulerTest("standby", "Standby"),
SchedulerTest("comments", "Comments"),
]),
SchedulerTestGroup([
SchedulerTest("one-or-more-0", "Everything starts"),
SchedulerTest("one-or-more-1", "Nothing starts because of A"),
SchedulerTest("one-or-more-2", "D can start because of C"),
SchedulerTest("one-or-more-3", "D cannot start because of B and C"),
SchedulerTest("one-or-more-4", "D cannot start because of target-role"),
SchedulerTest("one-or-more-5", "Start A and F even though C and D are stopped"),
SchedulerTest("one-or-more-6", "Leave A running even though B is stopped"),
SchedulerTest("one-or-more-7", "Leave A running even though C is stopped"),
SchedulerTest("bug-5140-require-all-false", "Allow basegrp:0 to stop"),
SchedulerTest("clone-require-all-1", "clone B starts node 3 and 4"),
SchedulerTest("clone-require-all-2", "clone B remains stopped everywhere"),
SchedulerTest("clone-require-all-3", "clone B stops everywhere because A stops everywhere"),
SchedulerTest("clone-require-all-4", "clone B remains on node 3 and 4 with only one instance of A remaining"),
SchedulerTest("clone-require-all-5", "clone B starts on node 1 3 and 4"),
SchedulerTest("clone-require-all-6", "clone B remains active after shutting down instances of A"),
SchedulerTest("clone-require-all-7",
"clone A and B both start at the same time. all instances of A start before B"),
SchedulerTest("clone-require-all-no-interleave-1", "C starts everywhere after A and B"),
SchedulerTest("clone-require-all-no-interleave-2",
"C starts on nodes 1, 2, and 4 with only one active instance of B"),
SchedulerTest("clone-require-all-no-interleave-3",
"C remains active when instance of B is stopped on one node and started on another"),
SchedulerTest("one-or-more-unrunnable-instances", "Avoid dependencies on instances that won't ever be started"),
]),
SchedulerTestGroup([
SchedulerTest("location-date-rules-1", "Use location constraints with ineffective date-based rules"),
SchedulerTest("location-date-rules-2", "Use location constraints with effective date-based rules"),
SchedulerTest("nvpair-date-rules-1", "Use nvpair blocks with a variety of date-based rules"),
SchedulerTest("value-source", "Use location constraints with node attribute expressions using value-source"),
SchedulerTest("rule-dbl-as-auto-number-match",
"Floating-point rule values default to number comparison: match"),
SchedulerTest("rule-dbl-as-auto-number-no-match",
"Floating-point rule values default to number comparison: no match"),
SchedulerTest("rule-dbl-as-integer-match",
"Floating-point rule values set to integer comparison: match"),
SchedulerTest("rule-dbl-as-integer-no-match",
"Floating-point rule values set to integer comparison: no match"),
SchedulerTest("rule-dbl-as-number-match",
"Floating-point rule values set to number comparison: match"),
SchedulerTest("rule-dbl-as-number-no-match",
"Floating-point rule values set to number comparison: no match"),
SchedulerTest("rule-dbl-parse-fail-default-str-match",
- "Floating-point rule values fail to parse, default to string "
- "comparison: match"),
+ "Floating-point rule values fail to parse, default to string comparison: match"),
SchedulerTest("rule-dbl-parse-fail-default-str-no-match",
- "Floating-point rule values fail to parse, default to string "
- "comparison: no match"),
+ "Floating-point rule values fail to parse, default to string comparison: no match"),
SchedulerTest("rule-int-as-auto-integer-match",
"Integer rule values default to integer comparison: match"),
SchedulerTest("rule-int-as-auto-integer-no-match",
"Integer rule values default to integer comparison: no match"),
SchedulerTest("rule-int-as-integer-match",
"Integer rule values set to integer comparison: match"),
SchedulerTest("rule-int-as-integer-no-match",
"Integer rule values set to integer comparison: no match"),
SchedulerTest("rule-int-as-number-match",
"Integer rule values set to number comparison: match"),
SchedulerTest("rule-int-as-number-no-match",
"Integer rule values set to number comparison: no match"),
SchedulerTest("rule-int-parse-fail-default-str-match",
- "Integer rule values fail to parse, default to string "
- "comparison: match"),
+ "Integer rule values fail to parse, default to string comparison: match"),
SchedulerTest("rule-int-parse-fail-default-str-no-match",
- "Integer rule values fail to parse, default to string "
- "comparison: no match"),
+ "Integer rule values fail to parse, default to string comparison: no match"),
]),
SchedulerTestGroup([
SchedulerTest("order1", "Order start 1"),
SchedulerTest("order2", "Order start 2"),
SchedulerTest("order3", "Order stop"),
SchedulerTest("order4", "Order (multiple)"),
SchedulerTest("order5", "Order (move)"),
SchedulerTest("order6", "Order (move w/ restart)"),
SchedulerTest("order7", "Order (mandatory)"),
SchedulerTest("order-optional", "Order (score=0)"),
SchedulerTest("order-required", "Order (score=INFINITY)"),
SchedulerTest("bug-lf-2171", "Prevent group start when clone is stopped"),
SchedulerTest("order-clone", "Clone ordering should be able to prevent startup of dependent clones"),
SchedulerTest("order-sets", "Ordering for resource sets"),
SchedulerTest("order-serialize", "Serialize resources without inhibiting migration"),
SchedulerTest("order-serialize-set", "Serialize a set of resources without inhibiting migration"),
SchedulerTest("clone-order-primitive", "Order clone start after a primitive"),
SchedulerTest("clone-order-16instances", "Verify ordering of 16 cloned resources"),
SchedulerTest("order-optional-keyword", "Order (optional keyword)"),
SchedulerTest("order-mandatory", "Order (mandatory keyword)"),
SchedulerTest("bug-lf-2493", "Don't imply colocation requirements when applying ordering constraints with clones"),
SchedulerTest("ordered-set-basic-startup", "Constraint set with default order settings"),
SchedulerTest("ordered-set-natural", "Allow natural set ordering"),
SchedulerTest("order-wrong-kind", "Order (error)"),
]),
SchedulerTestGroup([
SchedulerTest("coloc-loop", "Colocation - loop"),
SchedulerTest("coloc-many-one", "Colocation - many-to-one"),
SchedulerTest("coloc-list", "Colocation - many-to-one with list"),
SchedulerTest("coloc-group", "Colocation - groups"),
SchedulerTest("coloc-unpromoted-anti", "Anti-colocation with unpromoted shouldn't prevent promoted colocation"),
SchedulerTest("coloc-attr", "Colocation based on node attributes"),
SchedulerTest("coloc-negative-group", "Negative colocation with a group"),
SchedulerTest("coloc-intra-set", "Intra-set colocation"),
SchedulerTest("bug-lf-2435", "Colocation sets with a negative score"),
SchedulerTest("coloc-clone-stays-active",
"Ensure clones don't get stopped/demoted because a dependent must stop"),
SchedulerTest("coloc_fp_logic", "Verify floating point calculations in colocation are working"),
SchedulerTest("colo_promoted_w_native",
"cl#5070 - Verify promotion order is affected when colocating promoted with primitive"),
SchedulerTest("colo_unpromoted_w_native",
"cl#5070 - Verify promotion order is affected when colocating unpromoted with primitive"),
SchedulerTest("anti-colocation-order",
"cl#5187 - Prevent resources in an anti-colocation from even temporarily running on a same node"),
SchedulerTest("anti-colocation-promoted", "Organize order of actions for promoted resources in anti-colocations"),
SchedulerTest("anti-colocation-unpromoted", "Organize order of actions for unpromoted resources in anti-colocations"),
SchedulerTest("group-anticolocation", "Group with failed last member anti-colocated with another group"),
SchedulerTest("group-anticolocation-2",
"Group with failed last member anti-colocated with another sticky group"),
SchedulerTest("group-anticolocation-3",
"Group with failed last member mandatorily anti-colocated with another group"),
SchedulerTest("group-anticolocation-4",
"Group with failed last member anti-colocated without influence with another group"),
SchedulerTest("group-anticolocation-5",
"Group with failed last member anti-colocated with another group (third node allowed)"),
SchedulerTest("group-colocation-failure",
"Group with sole member failed, colocated with another group"),
SchedulerTest("enforce-colo1", "Always enforce B with A INFINITY"),
SchedulerTest("complex_enforce_colo", "Always enforce B with A INFINITY. (make sure heat-engine stops)"),
SchedulerTest("coloc-dependee-should-stay", "Stickiness outweighs group colocation"),
SchedulerTest("coloc-dependee-should-move", "Group colocation outweighs stickiness"),
SchedulerTest("colocation-influence", "Respect colocation influence"),
SchedulerTest("colocation-priority-group", "Apply group colocations in order of primary priority"),
SchedulerTest("colocation-vs-stickiness", "Group stickiness outweighs anti-colocation score"),
SchedulerTest("promoted-with-blocked", "Promoted role colocated with a resource with blocked start"),
SchedulerTest("primitive-with-group-with-clone",
"Consider group dependent when colocating with clone"),
SchedulerTest("primitive-with-group-with-promoted",
"Consider group dependent when colocating with promoted role"),
SchedulerTest("primitive-with-unrunnable-group",
"Block primitive colocated with group that can't start"),
SchedulerTest("coloc-cloned-group-promoted-dependent1",
"Cloned group promoted role with primitive (mandatory)"),
SchedulerTest("coloc-cloned-group-promoted-dependent2",
"Cloned group promoted role with primitive (optional)"),
SchedulerTest("coloc-optional-promoted-dependent-moves-1",
- "Colocation score less than promotion score "
- + "difference: move"),
+ "Colocation score less than promotion score difference: move"),
SchedulerTest("coloc-optional-promoted-dependent-moves-2",
- "Colocation score greater than promotion score "
- + "difference: move"),
+ "Colocation score greater than promotion score difference: move"),
SchedulerTest("coloc-optional-promoted-dependent-stays-1",
- "Colocation score greater than promotion score "
- + "difference: stay"),
+ "Colocation score greater than promotion score difference: stay"),
SchedulerTest("coloc-optional-promoted-dependent-stays-2",
- "Colocation score less than promotion score "
- + "difference: stay"),
+ "Colocation score less than promotion score difference: stay"),
]),
SchedulerTestGroup([
SchedulerTest("rsc-sets-seq-true", "Resource Sets - sequential=false"),
SchedulerTest("rsc-sets-seq-false", "Resource Sets - sequential=true"),
SchedulerTest("rsc-sets-clone", "Resource Sets - Clone"),
SchedulerTest("rsc-sets-promoted", "Resource Sets - Promoted"),
SchedulerTest("rsc-sets-clone-1", "Resource Sets - Clone (lf#2404)"),
]),
SchedulerTestGroup([
SchedulerTest("attrs1", "string: eq (and)"),
SchedulerTest("attrs2", "string: lt / gt (and)"),
SchedulerTest("attrs3", "string: ne (or)"),
SchedulerTest("attrs4", "string: exists"),
SchedulerTest("attrs5", "string: not_exists"),
SchedulerTest("attrs6", "is_dc: true"),
SchedulerTest("attrs7", "is_dc: false"),
SchedulerTest("attrs8", "score_attribute"),
SchedulerTest("per-node-attrs", "Per node resource parameters"),
]),
SchedulerTestGroup([
SchedulerTest("mon-rsc-1", "Schedule Monitor - start"),
SchedulerTest("mon-rsc-2", "Schedule Monitor - move"),
SchedulerTest("mon-rsc-3", "Schedule Monitor - pending start"),
SchedulerTest("mon-rsc-4", "Schedule Monitor - move/pending start"),
]),
SchedulerTestGroup([
SchedulerTest("rec-rsc-0", "Resource Recover - no start"),
SchedulerTest("rec-rsc-1", "Resource Recover - start"),
SchedulerTest("rec-rsc-2", "Resource Recover - monitor"),
SchedulerTest("rec-rsc-3", "Resource Recover - stop - ignore"),
SchedulerTest("rec-rsc-4", "Resource Recover - stop - block"),
SchedulerTest("rec-rsc-5", "Resource Recover - stop - fence"),
SchedulerTest("rec-rsc-6", "Resource Recover - multiple - restart"),
SchedulerTest("rec-rsc-7", "Resource Recover - multiple - stop"),
SchedulerTest("rec-rsc-8", "Resource Recover - multiple - block"),
SchedulerTest("rec-rsc-9", "Resource Recover - group/group"),
SchedulerTest("stop-unexpected", "Recover multiply active group with stop_unexpected"),
SchedulerTest("stop-unexpected-2", "Resource multiply active primitve with stop_unexpected"),
SchedulerTest("monitor-recovery", "on-fail=block + resource recovery detected by recurring monitor"),
SchedulerTest("stop-failure-no-quorum", "Stop failure without quorum"),
SchedulerTest("stop-failure-no-fencing", "Stop failure without fencing available"),
SchedulerTest("stop-failure-with-fencing", "Stop failure with fencing available"),
SchedulerTest("multiple-active-block-group", "Support of multiple-active=block for resource groups"),
SchedulerTest("multiple-monitor-one-failed",
"Consider resource failed if any of the configured monitor operations failed"),
]),
SchedulerTestGroup([
SchedulerTest("quorum-1", "No quorum - ignore"),
SchedulerTest("quorum-2", "No quorum - freeze"),
SchedulerTest("quorum-3", "No quorum - stop"),
SchedulerTest("quorum-4", "No quorum - start anyway"),
SchedulerTest("quorum-5", "No quorum - start anyway (group)"),
SchedulerTest("quorum-6", "No quorum - start anyway (clone)"),
SchedulerTest("bug-cl-5212", "No promotion with no-quorum-policy=freeze"),
SchedulerTest("suicide-needed-inquorate", "no-quorum-policy=suicide: suicide necessary"),
SchedulerTest("suicide-not-needed-initial-quorum",
"no-quorum-policy=suicide: suicide not necessary at initial quorum"),
SchedulerTest("suicide-not-needed-never-quorate",
"no-quorum-policy=suicide: suicide not necessary if never quorate"),
SchedulerTest("suicide-not-needed-quorate", "no-quorum-policy=suicide: suicide necessary if quorate"),
]),
SchedulerTestGroup([
SchedulerTest("rec-node-1", "Node Recover - Startup - no fence"),
SchedulerTest("rec-node-2", "Node Recover - Startup - fence"),
SchedulerTest("rec-node-3", "Node Recover - HA down - no fence"),
SchedulerTest("rec-node-4", "Node Recover - HA down - fence"),
SchedulerTest("rec-node-5", "Node Recover - CRM down - no fence"),
SchedulerTest("rec-node-6", "Node Recover - CRM down - fence"),
SchedulerTest("rec-node-7", "Node Recover - no quorum - ignore"),
SchedulerTest("rec-node-8", "Node Recover - no quorum - freeze"),
SchedulerTest("rec-node-9", "Node Recover - no quorum - stop"),
SchedulerTest("rec-node-10", "Node Recover - no quorum - stop w/fence"),
SchedulerTest("rec-node-11", "Node Recover - CRM down w/ group - fence"),
SchedulerTest("rec-node-12", "Node Recover - nothing active - fence"),
SchedulerTest("rec-node-13", "Node Recover - failed resource + shutdown - fence"),
SchedulerTest("rec-node-15", "Node Recover - unknown lrm section"),
SchedulerTest("rec-node-14", "Serialize all stonith's"),
]),
SchedulerTestGroup([
SchedulerTest("multi1", "Multiple Active (stop/start)"),
]),
SchedulerTestGroup([
SchedulerTest("migrate-begin", "Normal migration"),
SchedulerTest("migrate-success", "Completed migration"),
SchedulerTest("migrate-partial-1", "Completed migration, missing stop on source"),
SchedulerTest("migrate-partial-2", "Successful migrate_to only"),
SchedulerTest("migrate-partial-3", "Successful migrate_to only, target down"),
SchedulerTest("migrate-partial-4", "Migrate from the correct host after migrate_to+migrate_from"),
SchedulerTest("bug-5186-partial-migrate", "Handle partial migration when src node loses membership"),
SchedulerTest("migrate-fail-2", "Failed migrate_from"),
SchedulerTest("migrate-fail-3", "Failed migrate_from + stop on source"),
SchedulerTest("migrate-fail-4",
"Failed migrate_from + stop on target - ideally we wouldn't need to re-stop on target"),
SchedulerTest("migrate-fail-5", "Failed migrate_from + stop on source and target"),
SchedulerTest("migrate-fail-6", "Failed migrate_to"),
SchedulerTest("migrate-fail-7", "Failed migrate_to + stop on source"),
SchedulerTest("migrate-fail-8",
"Failed migrate_to + stop on target - ideally we wouldn't need to re-stop on target"),
SchedulerTest("migrate-fail-9", "Failed migrate_to + stop on source and target"),
SchedulerTest("migration-ping-pong", "Old migrate_to failure + successful migrate_from on same node"),
SchedulerTest("migrate-stop", "Migration in a stopping stack"),
SchedulerTest("migrate-start", "Migration in a starting stack"),
SchedulerTest("migrate-stop_start", "Migration in a restarting stack"),
SchedulerTest("migrate-stop-complex", "Migration in a complex stopping stack"),
SchedulerTest("migrate-start-complex", "Migration in a complex starting stack"),
SchedulerTest("migrate-stop-start-complex", "Migration in a complex moving stack"),
SchedulerTest("migrate-shutdown", "Order the post-migration 'stop' before node shutdown"),
SchedulerTest("migrate-1", "Migrate (migrate)"),
SchedulerTest("migrate-2", "Migrate (stable)"),
SchedulerTest("migrate-3", "Migrate (failed migrate_to)"),
SchedulerTest("migrate-4", "Migrate (failed migrate_from)"),
SchedulerTest("novell-252693", "Migration in a stopping stack"),
SchedulerTest("novell-252693-2", "Migration in a starting stack"),
SchedulerTest("novell-252693-3", "Non-Migration in a starting and stopping stack"),
SchedulerTest("bug-1820", "Migration in a group"),
SchedulerTest("bug-1820-1", "Non-migration in a group"),
SchedulerTest("migrate-5", "Primitive migration with a clone"),
SchedulerTest("migrate-fencing", "Migration after Fencing"),
SchedulerTest("migrate-both-vms", "Migrate two VMs that have no colocation"),
SchedulerTest("migration-behind-migrating-remote", "Migrate resource behind migrating remote connection"),
SchedulerTest("1-a-then-bm-move-b", "Advanced migrate logic. A then B. migrate B"),
SchedulerTest("2-am-then-b-move-a", "Advanced migrate logic, A then B, migrate A without stopping B"),
SchedulerTest("3-am-then-bm-both-migrate", "Advanced migrate logic. A then B. migrate both"),
SchedulerTest("4-am-then-bm-b-not-migratable", "Advanced migrate logic, A then B, B not migratable"),
SchedulerTest("5-am-then-bm-a-not-migratable", "Advanced migrate logic. A then B. move both, a not migratable"),
SchedulerTest("6-migrate-group", "Advanced migrate logic, migrate a group"),
SchedulerTest("7-migrate-group-one-unmigratable",
"Advanced migrate logic, migrate group mixed with allow-migrate true/false"),
SchedulerTest("8-am-then-bm-a-migrating-b-stopping",
"Advanced migrate logic, A then B, A migrating, B stopping"),
SchedulerTest("9-am-then-bm-b-migrating-a-stopping",
"Advanced migrate logic, A then B, B migrate, A stopping"),
SchedulerTest("10-a-then-bm-b-move-a-clone",
"Advanced migrate logic, A clone then B, migrate B while stopping A"),
SchedulerTest("11-a-then-bm-b-move-a-clone-starting",
"Advanced migrate logic, A clone then B, B moving while A is start/stopping"),
SchedulerTest("a-promote-then-b-migrate", "A promote then B start. migrate B"),
SchedulerTest("a-demote-then-b-migrate", "A demote then B stop. migrate B"),
SchedulerTest("probe-target-of-failed-migrate_to-1", "Failed migrate_to, target rejoins"),
SchedulerTest("probe-target-of-failed-migrate_to-2", "Failed migrate_to, target rejoined and probed"),
SchedulerTest("partial-live-migration-multiple-active", "Prevent running on multiple nodes due to partial live migration"),
SchedulerTest("migration-intermediary-cleaned",
"Probe live-migration intermediary with no history"),
SchedulerTest("bug-lf-2422", "Dependency on partially active group - stop ocfs:*"),
]),
SchedulerTestGroup([
SchedulerTest("clone-anon-probe-1", "Probe the correct (anonymous) clone instance for each node"),
SchedulerTest("clone-anon-probe-2", "Avoid needless re-probing of anonymous clones"),
SchedulerTest("clone-anon-failcount", "Merge failcounts for anonymous clones"),
SchedulerTest("force-anon-clone-max", "Update clone-max properly when forcing a clone to be anonymous"),
SchedulerTest("anon-instance-pending", "Assign anonymous clone instance numbers properly when action pending"),
SchedulerTest("inc0", "Incarnation start"),
SchedulerTest("inc1", "Incarnation start order"),
SchedulerTest("inc2", "Incarnation silent restart, stop, move"),
SchedulerTest("inc3", "Inter-incarnation ordering, silent restart, stop, move"),
SchedulerTest("inc4", "Inter-incarnation ordering, silent restart, stop, move (ordered)"),
SchedulerTest("inc5", "Inter-incarnation ordering, silent restart, stop, move (restart 1)"),
SchedulerTest("inc6", "Inter-incarnation ordering, silent restart, stop, move (restart 2)"),
SchedulerTest("inc7", "Clone colocation"),
SchedulerTest("inc8", "Clone anti-colocation"),
SchedulerTest("inc9", "Non-unique clone"),
SchedulerTest("inc10", "Non-unique clone (stop)"),
SchedulerTest("inc11", "Primitive colocation with clones"),
SchedulerTest("inc12", "Clone shutdown"),
SchedulerTest("cloned-group", "Make sure only the correct number of cloned groups are started"),
SchedulerTest("cloned-group-stop", "Ensure stopping qpidd also stops glance and cinder"),
SchedulerTest("clone-no-shuffle", "Don't prioritize allocation of instances that must be moved"),
SchedulerTest("clone-recover-no-shuffle-1",
"Don't shuffle instances when starting a new primitive instance"),
SchedulerTest("clone-recover-no-shuffle-2",
"Don't shuffle instances when starting a new group instance"),
SchedulerTest("clone-recover-no-shuffle-3",
"Don't shuffle instances when starting a new bundle instance"),
SchedulerTest("clone-recover-no-shuffle-4",
- "Don't shuffle instances when starting a new primitive instance with "
- "location preference "),
+ "Don't shuffle instances when starting a new primitive instance with location preference"),
SchedulerTest("clone-recover-no-shuffle-5",
- "Don't shuffle instances when starting a new group instance with "
- "location preference"),
+ "Don't shuffle instances when starting a new group instance with location preference"),
SchedulerTest("clone-recover-no-shuffle-6",
- "Don't shuffle instances when starting a new bundle instance with "
- "location preference"),
+ "Don't shuffle instances when starting a new bundle instance with location preference"),
SchedulerTest("clone-recover-no-shuffle-7",
- "Don't shuffle instances when starting a new primitive instance that "
- "will be promoted"),
+ "Don't shuffle instances when starting a new primitive instance that will be promoted"),
SchedulerTest("clone-recover-no-shuffle-8",
- "Don't shuffle instances when starting a new group instance that "
- "will be promoted "),
+ "Don't shuffle instances when starting a new group instance that will be promoted"),
SchedulerTest("clone-recover-no-shuffle-9",
- "Don't shuffle instances when starting a new bundle instance that "
- "will be promoted "),
+ "Don't shuffle instances when starting a new bundle instance that will be promoted"),
SchedulerTest("clone-recover-no-shuffle-10",
- "Don't shuffle instances when starting a new primitive instance that "
- "won't be promoted"),
+ "Don't shuffle instances when starting a new primitive instance that won't be promoted"),
SchedulerTest("clone-recover-no-shuffle-11",
- "Don't shuffle instances when starting a new group instance that "
- "won't be promoted "),
+ "Don't shuffle instances when starting a new group instance that won't be promoted"),
SchedulerTest("clone-recover-no-shuffle-12",
- "Don't shuffle instances when starting a new bundle instance that "
- "won't be promoted "),
+ "Don't shuffle instances when starting a new bundle instance that won't be promoted"),
SchedulerTest("clone-max-zero", "Orphan processing with clone-max=0"),
SchedulerTest("clone-anon-dup",
"Bug LF#2087 - Correctly parse the state of anonymous clones that are active more than once per node"),
SchedulerTest("bug-lf-2160", "Don't shuffle clones due to colocation"),
SchedulerTest("bug-lf-2213", "clone-node-max enforcement for cloned groups"),
SchedulerTest("bug-lf-2153", "Clone ordering constraints"),
SchedulerTest("bug-lf-2361", "Ensure clones observe mandatory ordering constraints if the LHS is unrunnable"),
SchedulerTest("bug-lf-2317", "Avoid needless restart of primitive depending on a clone"),
SchedulerTest("bug-lf-2453", "Enforce mandatory clone ordering without colocation"),
SchedulerTest("bug-lf-2508", "Correctly reconstruct the status of anonymous cloned groups"),
SchedulerTest("bug-lf-2544", "Balanced clone placement"),
SchedulerTest("bug-lf-2445", "Redistribute clones with node-max > 1 and stickiness = 0"),
SchedulerTest("bug-lf-2574", "Avoid clone shuffle"),
SchedulerTest("bug-lf-2581", "Avoid group restart due to unrelated clone (re)start"),
SchedulerTest("bug-cl-5168", "Don't shuffle clones"),
SchedulerTest("bug-cl-5170", "Prevent clone from starting with on-fail=block"),
SchedulerTest("clone-fail-block-colocation", "Move colocated group when failed clone has on-fail=block"),
SchedulerTest("clone-interleave-1",
"Clone-3 cannot start on pcmk-1 due to interleaved ordering (no colocation)"),
SchedulerTest("clone-interleave-2", "Clone-3 must stop on pcmk-1 due to interleaved ordering (no colocation)"),
SchedulerTest("clone-interleave-3",
"Clone-3 must be recovered on pcmk-1 due to interleaved ordering (no colocation)"),
SchedulerTest("rebalance-unique-clones", "Rebalance unique clone instances with no stickiness"),
SchedulerTest("clone-requires-quorum-recovery", "Clone with requires=quorum on failed node needing recovery"),
SchedulerTest("clone-requires-quorum",
"Clone with requires=quorum with presumed-inactive instance on failed node"),
]),
SchedulerTestGroup([
SchedulerTest("cloned_start_one", "order first clone then clone... first clone_min=2"),
SchedulerTest("cloned_start_two", "order first clone then clone... first clone_min=2"),
SchedulerTest("cloned_stop_one", "order first clone then clone... first clone_min=2"),
SchedulerTest("cloned_stop_two", "order first clone then clone... first clone_min=2"),
SchedulerTest("clone_min_interleave_start_one",
"order first clone then clone... first clone_min=2 and then has interleave=true"),
SchedulerTest("clone_min_interleave_start_two",
"order first clone then clone... first clone_min=2 and then has interleave=true"),
SchedulerTest("clone_min_interleave_stop_one",
"order first clone then clone... first clone_min=2 and then has interleave=true"),
SchedulerTest("clone_min_interleave_stop_two",
"order first clone then clone... first clone_min=2 and then has interleave=true"),
SchedulerTest("clone_min_start_one", "order first clone then primitive... first clone_min=2"),
SchedulerTest("clone_min_start_two", "order first clone then primitive... first clone_min=2"),
SchedulerTest("clone_min_stop_all", "order first clone then primitive... first clone_min=2"),
SchedulerTest("clone_min_stop_one", "order first clone then primitive... first clone_min=2"),
SchedulerTest("clone_min_stop_two", "order first clone then primitive... first clone_min=2"),
]),
SchedulerTestGroup([
SchedulerTest("unfence-startup", "Clean unfencing"),
SchedulerTest("unfence-definition", "Unfencing when the agent changes"),
SchedulerTest("unfence-parameters", "Unfencing when the agent parameters changes"),
SchedulerTest("unfence-device", "Unfencing when a cluster has only fence devices"),
]),
SchedulerTestGroup([
SchedulerTest("promoted-0", "Stopped -> Unpromoted"),
SchedulerTest("promoted-1", "Stopped -> Promote"),
SchedulerTest("promoted-2", "Stopped -> Promote : notify"),
SchedulerTest("promoted-3", "Stopped -> Promote : promoted location"),
SchedulerTest("promoted-4", "Started -> Promote : promoted location"),
SchedulerTest("promoted-5", "Promoted -> Promoted"),
SchedulerTest("promoted-6", "Promoted -> Promoted (2)"),
SchedulerTest("promoted-7", "Promoted -> Fenced"),
SchedulerTest("promoted-8", "Promoted -> Fenced -> Moved"),
SchedulerTest("promoted-9", "Stopped + Promotable + No quorum"),
SchedulerTest("promoted-10", "Stopped -> Promotable : notify with monitor"),
SchedulerTest("promoted-11", "Stopped -> Promote : colocation"),
SchedulerTest("novell-239082", "Demote/Promote ordering"),
SchedulerTest("novell-239087", "Stable promoted placement"),
SchedulerTest("promoted-12", "Promotion based solely on rsc_location constraints"),
SchedulerTest("promoted-13", "Include preferences of colocated resources when placing promoted"),
SchedulerTest("promoted-demote", "Ordering when actions depends on demoting an unpromoted resource"),
SchedulerTest("promoted-ordering", "Prevent resources from starting that need a promoted"),
SchedulerTest("bug-1765", "Verify promoted-with-promoted colocation does not stop unpromoted instances"),
SchedulerTest("promoted-group", "Promotion of cloned groups"),
SchedulerTest("bug-lf-1852", "Don't shuffle promotable instances unnecessarily"),
SchedulerTest("promoted-failed-demote", "Don't retry failed demote actions"),
SchedulerTest("promoted-failed-demote-2", "Don't retry failed demote actions (notify=false)"),
SchedulerTest("promoted-depend",
"Ensure resources that depend on promoted instance don't get allocated until that does"),
SchedulerTest("promoted-reattach", "Re-attach to a running promoted"),
SchedulerTest("promoted-allow-start", "Don't include promoted score if it would prevent allocation"),
SchedulerTest("promoted-colocation",
"Allow promoted instances placemaker to be influenced by colocation constraints"),
SchedulerTest("promoted-pseudo", "Make sure promote/demote pseudo actions are created correctly"),
SchedulerTest("promoted-role", "Prevent target-role from promoting more than promoted-max instances"),
SchedulerTest("bug-lf-2358", "Anti-colocation of promoted instances"),
SchedulerTest("promoted-promotion-constraint", "Mandatory promoted colocation constraints"),
SchedulerTest("unmanaged-promoted", "Ensure role is preserved for unmanaged resources"),
SchedulerTest("promoted-unmanaged-monitor", "Start correct monitor for unmanaged promoted instances"),
SchedulerTest("promoted-demote-2", "Demote does not clear past failure"),
SchedulerTest("promoted-move", "Move promoted based on failure of colocated group"),
SchedulerTest("promoted-probed-score", "Observe the promotion score of probed resources"),
SchedulerTest("colocation_constraint_stops_promoted",
"cl#5054 - Ensure promoted is demoted when stopped by colocation constraint"),
SchedulerTest("colocation_constraint_stops_unpromoted",
"cl#5054 - Ensure unpromoted is not demoted when stopped by colocation constraint"),
SchedulerTest("order_constraint_stops_promoted",
"cl#5054 - Ensure promoted is demoted when stopped by order constraint"),
SchedulerTest("order_constraint_stops_unpromoted",
"cl#5054 - Ensure unpromoted is not demoted when stopped by order constraint"),
SchedulerTest("promoted_monitor_restart", "cl#5072 - Ensure promoted monitor operation will start after promotion"),
SchedulerTest("bug-rh-880249", "Handle replacement of an m/s resource with a primitive"),
SchedulerTest("bug-5143-ms-shuffle", "Prevent promoted instance shuffling due to promotion score"),
SchedulerTest("promoted-demote-block", "Block promotion if demote fails with on-fail=block"),
SchedulerTest("promoted-dependent-ban",
"Don't stop instances from being active because a dependent is banned from that host"),
SchedulerTest("promoted-stop", "Stop instances due to location constraint with role=Started"),
SchedulerTest("promoted-partially-demoted-group", "Allow partially demoted group to finish demoting"),
SchedulerTest("bug-cl-5213", "Ensure role colocation with -INFINITY is enforced"),
SchedulerTest("bug-cl-5219", "Allow unrelated resources with a common colocation target to remain promoted"),
SchedulerTest("promoted-asymmetrical-order",
"Fix the behaviors of multi-state resources with asymmetrical ordering"),
SchedulerTest("promoted-notify", "Promotion with notifications"),
SchedulerTest("promoted-score-startup", "Use permanent promoted scores without LRM history"),
SchedulerTest("failed-demote-recovery", "Recover resource in unpromoted role after demote fails"),
SchedulerTest("failed-demote-recovery-promoted", "Recover resource in promoted role after demote fails"),
SchedulerTest("on_fail_demote1", "Recovery with on-fail=\"demote\" on healthy cluster, remote, guest, and bundle nodes"),
SchedulerTest("on_fail_demote2", "Recovery with on-fail=\"demote\" with promotion on different node"),
SchedulerTest("on_fail_demote3", "Recovery with on-fail=\"demote\" with no promotion"),
SchedulerTest("on_fail_demote4", "Recovery with on-fail=\"demote\" on failed cluster, remote, guest, and bundle nodes"),
SchedulerTest("no_quorum_demote", "Promotable demotion and primitive stop with no-quorum-policy=\"demote\""),
SchedulerTest("no-promote-on-unrunnable-guest", "Don't select bundle instance for promotion when container can't run"),
SchedulerTest("leftover-pending-monitor", "Prevent a leftover pending monitor from causing unexpected stop of other instances"),
]),
SchedulerTestGroup([
SchedulerTest("history-1", "Correctly parse stateful-1 resource state"),
]),
SchedulerTestGroup([
SchedulerTest("managed-0", "Managed (reference)"),
SchedulerTest("managed-1", "Not managed - down"),
SchedulerTest("managed-2", "Not managed - up"),
SchedulerTest("bug-5028", "Shutdown should block if anything depends on an unmanaged resource"),
SchedulerTest("bug-5028-detach", "Ensure detach still works"),
SchedulerTest("bug-5028-bottom",
"Ensure shutdown still blocks if the blocked resource is at the bottom of the stack"),
SchedulerTest("unmanaged-stop-1",
"cl#5155 - Block the stop of resources if any depending resource is unmanaged"),
SchedulerTest("unmanaged-stop-2",
"cl#5155 - Block the stop of resources if the first resource in a mandatory stop order is unmanaged"),
SchedulerTest("unmanaged-stop-3",
"cl#5155 - Block the stop of resources if any depending resource in a group is unmanaged"),
SchedulerTest("unmanaged-stop-4",
"cl#5155 - Block the stop of resources if any depending resource in the middle of a group is unmanaged"),
SchedulerTest("unmanaged-block-restart",
"Block restart of resources if any dependent resource in a group is unmanaged"),
]),
SchedulerTestGroup([
SchedulerTest("interleave-0", "Interleave (reference)"),
SchedulerTest("interleave-1", "coloc - not interleaved"),
SchedulerTest("interleave-2", "coloc - interleaved"),
SchedulerTest("interleave-3", "coloc - interleaved (2)"),
SchedulerTest("interleave-pseudo-stop", "Interleaved clone during stonith"),
SchedulerTest("interleave-stop", "Interleaved clone during stop"),
SchedulerTest("interleave-restart", "Interleaved clone during dependency restart"),
]),
SchedulerTestGroup([
SchedulerTest("notify-0", "Notify reference"),
SchedulerTest("notify-1", "Notify simple"),
SchedulerTest("notify-2", "Notify simple, confirm"),
SchedulerTest("notify-3", "Notify move, confirm"),
SchedulerTest("novell-239079", "Notification priority"),
SchedulerTest("notifs-for-unrunnable", "Don't schedule notifications for an unrunnable action"),
SchedulerTest("route-remote-notify", "Route remote notify actions through correct cluster node"),
SchedulerTest("notify-behind-stopping-remote", "Don't schedule notifications behind stopped remote"),
]),
SchedulerTestGroup([
SchedulerTest("594", "OSDL #594 - Unrunnable actions scheduled in transition"),
SchedulerTest("662", "OSDL #662 - Two resources start on one node when incarnation_node_max = 1"),
SchedulerTest("696", "OSDL #696 - CRM starts stonith RA without monitor"),
SchedulerTest("726", "OSDL #726 - Attempting to schedule rsc_posic041_monitor_5000 _after_ a stop"),
SchedulerTest("735", "OSDL #735 - Correctly detect that rsc_hadev1 is stopped on hadev3"),
SchedulerTest("764", "OSDL #764 - Missing monitor op for DoFencing:child_DoFencing:1"),
SchedulerTest("797", "OSDL #797 - Assert triggered: task_id_i > max_call_id"),
SchedulerTest("829", "OSDL #829"),
SchedulerTest("994",
"OSDL #994 - Stopping the last resource in a resource group causes the entire group to be restarted"),
SchedulerTest("994-2", "OSDL #994 - with a dependent resource"),
SchedulerTest("1360", "OSDL #1360 - Clone stickiness"),
SchedulerTest("1484", "OSDL #1484 - on_fail=stop"),
SchedulerTest("1494", "OSDL #1494 - Clone stability"),
SchedulerTest("unrunnable-1", "Unrunnable"),
SchedulerTest("unrunnable-2", "Unrunnable 2"),
SchedulerTest("stonith-0", "Stonith loop - 1"),
SchedulerTest("stonith-1", "Stonith loop - 2"),
SchedulerTest("stonith-2", "Stonith loop - 3"),
SchedulerTest("stonith-3", "Stonith startup"),
SchedulerTest("stonith-4", "Stonith node state"),
SchedulerTest("dc-fence-ordering", "DC needs fencing while other nodes are shutting down"),
SchedulerTest("bug-1572-1", "Recovery of groups depending on promotable role"),
SchedulerTest("bug-1572-2", "Recovery of groups depending on promotable role when promoted is not re-promoted"),
SchedulerTest("bug-1685", "Depends-on-promoted ordering"),
SchedulerTest("bug-1822", "Don't promote partially active groups"),
SchedulerTest("bug-pm-11", "New resource added to a m/s group"),
SchedulerTest("bug-pm-12", "Recover only the failed portion of a cloned group"),
SchedulerTest("bug-n-387749", "Don't shuffle clone instances"),
SchedulerTest("bug-n-385265",
"Don't ignore the failure stickiness of group children - resource_idvscommon should stay stopped"),
SchedulerTest("bug-n-385265-2",
"Ensure groups are migrated instead of remaining partially active on the current node"),
SchedulerTest("bug-lf-1920", "Correctly handle probes that find active resources"),
SchedulerTest("bnc-515172", "Location constraint with multiple expressions"),
SchedulerTest("colocate-primitive-with-clone", "Optional colocation with a clone"),
SchedulerTest("use-after-free-merge", "Use-after-free in native_merge_weights"),
SchedulerTest("bug-lf-2551", "STONITH ordering for stop"),
SchedulerTest("bug-lf-2606", "Stonith implies demote"),
SchedulerTest("bug-lf-2474", "Ensure resource op timeout takes precedence over op_defaults"),
SchedulerTest("bug-suse-707150", "Prevent vm-01 from starting due to colocation/ordering"),
SchedulerTest("bug-5014-A-start-B-start", "Verify when A starts B starts using symmetrical=false"),
SchedulerTest("bug-5014-A-stop-B-started",
"Verify when A stops B does not stop if it has already started using symmetric=false"),
SchedulerTest("bug-5014-A-stopped-B-stopped",
"Verify when A is stopped and B has not started, B does not start before A using symmetric=false"),
SchedulerTest("bug-5014-CthenAthenB-C-stopped",
"Verify when C then A is symmetrical=true, A then B is symmetric=false, and C is stopped that nothing starts"),
SchedulerTest("bug-5014-CLONE-A-start-B-start",
"Verify when A starts B starts using clone resources with symmetric=false"),
SchedulerTest("bug-5014-CLONE-A-stop-B-started",
"Verify when A stops B does not stop if it has already started using clone resources with symmetric=false"),
SchedulerTest("bug-5014-GROUP-A-start-B-start",
"Verify when A starts B starts when using group resources with symmetric=false"),
SchedulerTest("bug-5014-GROUP-A-stopped-B-started",
"Verify when A stops B does not stop if it has already started using group resources with symmetric=false"),
SchedulerTest("bug-5014-GROUP-A-stopped-B-stopped",
"Verify when A is stopped and B has not started, B does not start before A using group resources with symmetric=false"),
SchedulerTest("bug-5014-ordered-set-symmetrical-false",
"Verify ordered sets work with symmetrical=false"),
SchedulerTest("bug-5014-ordered-set-symmetrical-true",
"Verify ordered sets work with symmetrical=true"),
SchedulerTest("clbz5007-promotable-colocation",
"Verify use of colocation scores other than INFINITY and -INFINITY work on multi-state resources"),
SchedulerTest("bug-5038", "Prevent restart of anonymous clones when clone-max decreases"),
SchedulerTest("bug-5025-1", "Automatically clean up failcount after resource config change with reload"),
SchedulerTest("bug-5025-2", "Make sure clear failcount action isn't set when config does not change"),
SchedulerTest("bug-5025-3", "Automatically clean up failcount after resource config change with restart"),
SchedulerTest("bug-5025-4", "Clear failcount when last failure is a start op and rsc attributes changed"),
SchedulerTest("failcount", "Ensure failcounts are correctly expired"),
SchedulerTest("failcount-block", "Ensure failcounts are not expired when on-fail=block is present"),
SchedulerTest("per-op-failcount", "Ensure per-operation failcount is handled and not passed to fence agent"),
SchedulerTest("on-fail-ignore", "Ensure on-fail=ignore works even beyond migration-threshold"),
SchedulerTest("monitor-onfail-restart", "bug-5058 - Monitor failure with on-fail set to restart"),
SchedulerTest("monitor-onfail-stop", "bug-5058 - Monitor failure wiht on-fail set to stop"),
SchedulerTest("bug-5059", "No need to restart p_stateful1:*"),
SchedulerTest("bug-5069-op-enabled", "Test on-fail=ignore with failure when monitor is enabled"),
SchedulerTest("bug-5069-op-disabled", "Test on-fail-ignore with failure when monitor is disabled"),
SchedulerTest("obsolete-lrm-resource", "cl#5115 - Do not use obsolete lrm_resource sections"),
SchedulerTest("expire-non-blocked-failure",
"Ignore failure-timeout only if the failed operation has on-fail=block"),
SchedulerTest("asymmetrical-order-move", "Respect asymmetrical ordering when trying to move resources"),
SchedulerTest("asymmetrical-order-restart", "Respect asymmetrical ordering when restarting dependent resource"),
SchedulerTest("start-then-stop-with-unfence", "Avoid graph loop with start-then-stop constraint plus unfencing"),
SchedulerTest("order-expired-failure", "Order failcount cleanup after remote fencing"),
SchedulerTest("expired-stop-1", "Expired stop failure should not block resource"),
SchedulerTest("ignore_stonith_rsc_order1",
"cl#5056- Ignore order constraint between stonith and non-stonith rsc"),
SchedulerTest("ignore_stonith_rsc_order2",
"cl#5056- Ignore order constraint with group rsc containing mixed stonith and non-stonith"),
SchedulerTest("ignore_stonith_rsc_order3", "cl#5056- Ignore order constraint, stonith clone and mixed group"),
SchedulerTest("ignore_stonith_rsc_order4",
"cl#5056- Ignore order constraint, stonith clone and clone with nested mixed group"),
SchedulerTest("honor_stonith_rsc_order1",
"cl#5056- Honor order constraint, stonith clone and pure stonith group(single rsc)"),
SchedulerTest("honor_stonith_rsc_order2",
"cl#5056- Honor order constraint, stonith clone and pure stonith group(multiple rsc)"),
SchedulerTest("honor_stonith_rsc_order3",
"cl#5056- Honor order constraint, stonith clones with nested pure stonith group"),
SchedulerTest("honor_stonith_rsc_order4",
"cl#5056- Honor order constraint, between two native stonith rscs"),
SchedulerTest("multiply-active-stonith", "Multiply active stonith"),
SchedulerTest("probe-timeout", "cl#5099 - Default probe timeout"),
SchedulerTest("order-first-probes",
"cl#5301 - respect order constraints when relevant resources are being probed"),
SchedulerTest("concurrent-fencing", "Allow performing fencing operations in parallel"),
SchedulerTest("priority-fencing-delay", "Delay fencing targeting the more significant node"),
SchedulerTest("pending-node-no-uname", "Do not fence a pending node that doesn't have an uname in node state yet"),
SchedulerTest("node-pending-timeout", "Fence a pending node that has reached `node-pending-timeout`"),
]),
SchedulerTestGroup([
SchedulerTest("systemhealth1", "System Health () #1"),
SchedulerTest("systemhealth2", "System Health () #2"),
SchedulerTest("systemhealth3", "System Health () #3"),
SchedulerTest("systemhealthn1", "System Health (None) #1"),
SchedulerTest("systemhealthn2", "System Health (None) #2"),
SchedulerTest("systemhealthn3", "System Health (None) #3"),
SchedulerTest("systemhealthm1", "System Health (Migrate On Red) #1"),
SchedulerTest("systemhealthm2", "System Health (Migrate On Red) #2"),
SchedulerTest("systemhealthm3", "System Health (Migrate On Red) #3"),
SchedulerTest("systemhealtho1", "System Health (Only Green) #1"),
SchedulerTest("systemhealtho2", "System Health (Only Green) #2"),
SchedulerTest("systemhealtho3", "System Health (Only Green) #3"),
SchedulerTest("systemhealthp1", "System Health (Progessive) #1"),
SchedulerTest("systemhealthp2", "System Health (Progessive) #2"),
SchedulerTest("systemhealthp3", "System Health (Progessive) #3"),
SchedulerTest("allow-unhealthy-nodes", "System Health (migrate-on-red + allow-unhealth-nodes)"),
]),
SchedulerTestGroup([
SchedulerTest("utilization", "Placement Strategy - utilization"),
SchedulerTest("minimal", "Placement Strategy - minimal"),
SchedulerTest("balanced", "Placement Strategy - balanced"),
]),
SchedulerTestGroup([
SchedulerTest("placement-stickiness", "Optimized Placement Strategy - stickiness"),
SchedulerTest("placement-priority", "Optimized Placement Strategy - priority"),
SchedulerTest("placement-location", "Optimized Placement Strategy - location"),
SchedulerTest("placement-capacity", "Optimized Placement Strategy - capacity"),
]),
SchedulerTestGroup([
SchedulerTest("utilization-order1", "Utilization Order - Simple"),
SchedulerTest("utilization-order2", "Utilization Order - Complex"),
SchedulerTest("utilization-order3", "Utilization Order - Migrate"),
SchedulerTest("utilization-order4", "Utilization Order - Live Migration (bnc#695440)"),
SchedulerTest("utilization-complex", "Utilization with complex relationships"),
SchedulerTest("utilization-shuffle",
"Don't displace prmExPostgreSQLDB2 on act2, Start prmExPostgreSQLDB1 on act3"),
SchedulerTest("load-stopped-loop", "Avoid transition loop due to load_stopped (cl#5044)"),
SchedulerTest("load-stopped-loop-2",
"cl#5235 - Prevent graph loops that can be introduced by load_stopped -> migrate_to ordering"),
]),
SchedulerTestGroup([
SchedulerTest("colocated-utilization-primitive-1", "Colocated Utilization - Primitive"),
SchedulerTest("colocated-utilization-primitive-2", "Colocated Utilization - Choose the most capable node"),
SchedulerTest("colocated-utilization-group", "Colocated Utilization - Group"),
SchedulerTest("colocated-utilization-clone", "Colocated Utilization - Clone"),
SchedulerTest("utilization-check-allowed-nodes",
"Only check the capacities of the nodes that can run the resource"),
]),
SchedulerTestGroup([
SchedulerTest("node-maintenance-1", "cl#5128 - Node maintenance"),
SchedulerTest("node-maintenance-2", "cl#5128 - Node maintenance (coming out of maintenance mode)"),
SchedulerTest("shutdown-maintenance-node", "Do not fence a maintenance node if it shuts down cleanly"),
SchedulerTest("rsc-maintenance", "Per-resource maintenance"),
]),
SchedulerTestGroup([
SchedulerTest("not-installed-agent", "The resource agent is missing"),
SchedulerTest("not-installed-tools", "Something the resource agent needs is missing"),
]),
SchedulerTestGroup([
SchedulerTest("stopped-monitor-00", "Stopped Monitor - initial start"),
SchedulerTest("stopped-monitor-01", "Stopped Monitor - failed started"),
SchedulerTest("stopped-monitor-02", "Stopped Monitor - started multi-up"),
SchedulerTest("stopped-monitor-03", "Stopped Monitor - stop started"),
SchedulerTest("stopped-monitor-04", "Stopped Monitor - failed stop"),
SchedulerTest("stopped-monitor-05", "Stopped Monitor - start unmanaged"),
SchedulerTest("stopped-monitor-06", "Stopped Monitor - unmanaged multi-up"),
SchedulerTest("stopped-monitor-07", "Stopped Monitor - start unmanaged multi-up"),
SchedulerTest("stopped-monitor-08", "Stopped Monitor - migrate"),
SchedulerTest("stopped-monitor-09", "Stopped Monitor - unmanage started"),
SchedulerTest("stopped-monitor-10", "Stopped Monitor - unmanaged started multi-up"),
SchedulerTest("stopped-monitor-11", "Stopped Monitor - stop unmanaged started"),
SchedulerTest("stopped-monitor-12", "Stopped Monitor - unmanaged started multi-up (target-role=Stopped)"),
SchedulerTest("stopped-monitor-20", "Stopped Monitor - initial stop"),
SchedulerTest("stopped-monitor-21", "Stopped Monitor - stopped single-up"),
SchedulerTest("stopped-monitor-22", "Stopped Monitor - stopped multi-up"),
SchedulerTest("stopped-monitor-23", "Stopped Monitor - start stopped"),
SchedulerTest("stopped-monitor-24", "Stopped Monitor - unmanage stopped"),
SchedulerTest("stopped-monitor-25", "Stopped Monitor - unmanaged stopped multi-up"),
SchedulerTest("stopped-monitor-26", "Stopped Monitor - start unmanaged stopped"),
SchedulerTest("stopped-monitor-27", "Stopped Monitor - unmanaged stopped multi-up (target-role=Started)"),
SchedulerTest("stopped-monitor-30", "Stopped Monitor - new node started"),
SchedulerTest("stopped-monitor-31", "Stopped Monitor - new node stopped"),
]),
SchedulerTestGroup([
# This is a combo test to check:
# - probe timeout defaults to the minimum-interval monitor's
# - duplicate recurring operations are ignored
# - if timeout spec is bad, the default timeout is used
# - failure is blocked with on-fail=block even if ISO8601 interval is specified
# - started/stopped role monitors are started/stopped on right nodes
SchedulerTest("intervals", "Recurring monitor interval handling"),
]),
SchedulerTestGroup([
SchedulerTest("ticket-primitive-1", "Ticket - Primitive (loss-policy=stop, initial)"),
SchedulerTest("ticket-primitive-2", "Ticket - Primitive (loss-policy=stop, granted)"),
SchedulerTest("ticket-primitive-3", "Ticket - Primitive (loss-policy-stop, revoked)"),
SchedulerTest("ticket-primitive-4", "Ticket - Primitive (loss-policy=demote, initial)"),
SchedulerTest("ticket-primitive-5", "Ticket - Primitive (loss-policy=demote, granted)"),
SchedulerTest("ticket-primitive-6", "Ticket - Primitive (loss-policy=demote, revoked)"),
SchedulerTest("ticket-primitive-7", "Ticket - Primitive (loss-policy=fence, initial)"),
SchedulerTest("ticket-primitive-8", "Ticket - Primitive (loss-policy=fence, granted)"),
SchedulerTest("ticket-primitive-9", "Ticket - Primitive (loss-policy=fence, revoked)"),
SchedulerTest("ticket-primitive-10", "Ticket - Primitive (loss-policy=freeze, initial)"),
SchedulerTest("ticket-primitive-11", "Ticket - Primitive (loss-policy=freeze, granted)"),
SchedulerTest("ticket-primitive-12", "Ticket - Primitive (loss-policy=freeze, revoked)"),
SchedulerTest("ticket-primitive-13", "Ticket - Primitive (loss-policy=stop, standby, granted)"),
SchedulerTest("ticket-primitive-14", "Ticket - Primitive (loss-policy=stop, granted, standby)"),
SchedulerTest("ticket-primitive-15", "Ticket - Primitive (loss-policy=stop, standby, revoked)"),
SchedulerTest("ticket-primitive-16", "Ticket - Primitive (loss-policy=demote, standby, granted)"),
SchedulerTest("ticket-primitive-17", "Ticket - Primitive (loss-policy=demote, granted, standby)"),
SchedulerTest("ticket-primitive-18", "Ticket - Primitive (loss-policy=demote, standby, revoked)"),
SchedulerTest("ticket-primitive-19", "Ticket - Primitive (loss-policy=fence, standby, granted)"),
SchedulerTest("ticket-primitive-20", "Ticket - Primitive (loss-policy=fence, granted, standby)"),
SchedulerTest("ticket-primitive-21", "Ticket - Primitive (loss-policy=fence, standby, revoked)"),
SchedulerTest("ticket-primitive-22", "Ticket - Primitive (loss-policy=freeze, standby, granted)"),
SchedulerTest("ticket-primitive-23", "Ticket - Primitive (loss-policy=freeze, granted, standby)"),
SchedulerTest("ticket-primitive-24", "Ticket - Primitive (loss-policy=freeze, standby, revoked)"),
]),
SchedulerTestGroup([
SchedulerTest("ticket-group-1", "Ticket - Group (loss-policy=stop, initial)"),
SchedulerTest("ticket-group-2", "Ticket - Group (loss-policy=stop, granted)"),
SchedulerTest("ticket-group-3", "Ticket - Group (loss-policy-stop, revoked)"),
SchedulerTest("ticket-group-4", "Ticket - Group (loss-policy=demote, initial)"),
SchedulerTest("ticket-group-5", "Ticket - Group (loss-policy=demote, granted)"),
SchedulerTest("ticket-group-6", "Ticket - Group (loss-policy=demote, revoked)"),
SchedulerTest("ticket-group-7", "Ticket - Group (loss-policy=fence, initial)"),
SchedulerTest("ticket-group-8", "Ticket - Group (loss-policy=fence, granted)"),
SchedulerTest("ticket-group-9", "Ticket - Group (loss-policy=fence, revoked)"),
SchedulerTest("ticket-group-10", "Ticket - Group (loss-policy=freeze, initial)"),
SchedulerTest("ticket-group-11", "Ticket - Group (loss-policy=freeze, granted)"),
SchedulerTest("ticket-group-12", "Ticket - Group (loss-policy=freeze, revoked)"),
SchedulerTest("ticket-group-13", "Ticket - Group (loss-policy=stop, standby, granted)"),
SchedulerTest("ticket-group-14", "Ticket - Group (loss-policy=stop, granted, standby)"),
SchedulerTest("ticket-group-15", "Ticket - Group (loss-policy=stop, standby, revoked)"),
SchedulerTest("ticket-group-16", "Ticket - Group (loss-policy=demote, standby, granted)"),
SchedulerTest("ticket-group-17", "Ticket - Group (loss-policy=demote, granted, standby)"),
SchedulerTest("ticket-group-18", "Ticket - Group (loss-policy=demote, standby, revoked)"),
SchedulerTest("ticket-group-19", "Ticket - Group (loss-policy=fence, standby, granted)"),
SchedulerTest("ticket-group-20", "Ticket - Group (loss-policy=fence, granted, standby)"),
SchedulerTest("ticket-group-21", "Ticket - Group (loss-policy=fence, standby, revoked)"),
SchedulerTest("ticket-group-22", "Ticket - Group (loss-policy=freeze, standby, granted)"),
SchedulerTest("ticket-group-23", "Ticket - Group (loss-policy=freeze, granted, standby)"),
SchedulerTest("ticket-group-24", "Ticket - Group (loss-policy=freeze, standby, revoked)"),
]),
SchedulerTestGroup([
SchedulerTest("ticket-clone-1", "Ticket - Clone (loss-policy=stop, initial)"),
SchedulerTest("ticket-clone-2", "Ticket - Clone (loss-policy=stop, granted)"),
SchedulerTest("ticket-clone-3", "Ticket - Clone (loss-policy-stop, revoked)"),
SchedulerTest("ticket-clone-4", "Ticket - Clone (loss-policy=demote, initial)"),
SchedulerTest("ticket-clone-5", "Ticket - Clone (loss-policy=demote, granted)"),
SchedulerTest("ticket-clone-6", "Ticket - Clone (loss-policy=demote, revoked)"),
SchedulerTest("ticket-clone-7", "Ticket - Clone (loss-policy=fence, initial)"),
SchedulerTest("ticket-clone-8", "Ticket - Clone (loss-policy=fence, granted)"),
SchedulerTest("ticket-clone-9", "Ticket - Clone (loss-policy=fence, revoked)"),
SchedulerTest("ticket-clone-10", "Ticket - Clone (loss-policy=freeze, initial)"),
SchedulerTest("ticket-clone-11", "Ticket - Clone (loss-policy=freeze, granted)"),
SchedulerTest("ticket-clone-12", "Ticket - Clone (loss-policy=freeze, revoked)"),
SchedulerTest("ticket-clone-13", "Ticket - Clone (loss-policy=stop, standby, granted)"),
SchedulerTest("ticket-clone-14", "Ticket - Clone (loss-policy=stop, granted, standby)"),
SchedulerTest("ticket-clone-15", "Ticket - Clone (loss-policy=stop, standby, revoked)"),
SchedulerTest("ticket-clone-16", "Ticket - Clone (loss-policy=demote, standby, granted)"),
SchedulerTest("ticket-clone-17", "Ticket - Clone (loss-policy=demote, granted, standby)"),
SchedulerTest("ticket-clone-18", "Ticket - Clone (loss-policy=demote, standby, revoked)"),
SchedulerTest("ticket-clone-19", "Ticket - Clone (loss-policy=fence, standby, granted)"),
SchedulerTest("ticket-clone-20", "Ticket - Clone (loss-policy=fence, granted, standby)"),
SchedulerTest("ticket-clone-21", "Ticket - Clone (loss-policy=fence, standby, revoked)"),
SchedulerTest("ticket-clone-22", "Ticket - Clone (loss-policy=freeze, standby, granted)"),
SchedulerTest("ticket-clone-23", "Ticket - Clone (loss-policy=freeze, granted, standby)"),
SchedulerTest("ticket-clone-24", "Ticket - Clone (loss-policy=freeze, standby, revoked)"),
]),
SchedulerTestGroup([
SchedulerTest("ticket-promoted-1", "Ticket - Promoted (loss-policy=stop, initial)"),
SchedulerTest("ticket-promoted-2", "Ticket - Promoted (loss-policy=stop, granted)"),
SchedulerTest("ticket-promoted-3", "Ticket - Promoted (loss-policy-stop, revoked)"),
SchedulerTest("ticket-promoted-4", "Ticket - Promoted (loss-policy=demote, initial)"),
SchedulerTest("ticket-promoted-5", "Ticket - Promoted (loss-policy=demote, granted)"),
SchedulerTest("ticket-promoted-6", "Ticket - Promoted (loss-policy=demote, revoked)"),
SchedulerTest("ticket-promoted-7", "Ticket - Promoted (loss-policy=fence, initial)"),
SchedulerTest("ticket-promoted-8", "Ticket - Promoted (loss-policy=fence, granted)"),
SchedulerTest("ticket-promoted-9", "Ticket - Promoted (loss-policy=fence, revoked)"),
SchedulerTest("ticket-promoted-10", "Ticket - Promoted (loss-policy=freeze, initial)"),
SchedulerTest("ticket-promoted-11", "Ticket - Promoted (loss-policy=freeze, granted)"),
SchedulerTest("ticket-promoted-12", "Ticket - Promoted (loss-policy=freeze, revoked)"),
SchedulerTest("ticket-promoted-13", "Ticket - Promoted (loss-policy=stop, standby, granted)"),
SchedulerTest("ticket-promoted-14", "Ticket - Promoted (loss-policy=stop, granted, standby)"),
SchedulerTest("ticket-promoted-15", "Ticket - Promoted (loss-policy=stop, standby, revoked)"),
SchedulerTest("ticket-promoted-16", "Ticket - Promoted (loss-policy=demote, standby, granted)"),
SchedulerTest("ticket-promoted-17", "Ticket - Promoted (loss-policy=demote, granted, standby)"),
SchedulerTest("ticket-promoted-18", "Ticket - Promoted (loss-policy=demote, standby, revoked)"),
SchedulerTest("ticket-promoted-19", "Ticket - Promoted (loss-policy=fence, standby, granted)"),
SchedulerTest("ticket-promoted-20", "Ticket - Promoted (loss-policy=fence, granted, standby)"),
SchedulerTest("ticket-promoted-21", "Ticket - Promoted (loss-policy=fence, standby, revoked)"),
SchedulerTest("ticket-promoted-22", "Ticket - Promoted (loss-policy=freeze, standby, granted)"),
SchedulerTest("ticket-promoted-23", "Ticket - Promoted (loss-policy=freeze, granted, standby)"),
SchedulerTest("ticket-promoted-24", "Ticket - Promoted (loss-policy=freeze, standby, revoked)"),
]),
SchedulerTestGroup([
SchedulerTest("ticket-rsc-sets-1", "Ticket - Resource sets (1 ticket, initial)"),
SchedulerTest("ticket-rsc-sets-2", "Ticket - Resource sets (1 ticket, granted)"),
SchedulerTest("ticket-rsc-sets-3", "Ticket - Resource sets (1 ticket, revoked)"),
SchedulerTest("ticket-rsc-sets-4", "Ticket - Resource sets (2 tickets, initial)"),
SchedulerTest("ticket-rsc-sets-5", "Ticket - Resource sets (2 tickets, granted)"),
SchedulerTest("ticket-rsc-sets-6", "Ticket - Resource sets (2 tickets, granted)"),
SchedulerTest("ticket-rsc-sets-7", "Ticket - Resource sets (2 tickets, revoked)"),
SchedulerTest("ticket-rsc-sets-8", "Ticket - Resource sets (1 ticket, standby, granted)"),
SchedulerTest("ticket-rsc-sets-9", "Ticket - Resource sets (1 ticket, granted, standby)"),
SchedulerTest("ticket-rsc-sets-10", "Ticket - Resource sets (1 ticket, standby, revoked)"),
SchedulerTest("ticket-rsc-sets-11", "Ticket - Resource sets (2 tickets, standby, granted)"),
SchedulerTest("ticket-rsc-sets-12", "Ticket - Resource sets (2 tickets, standby, granted)"),
SchedulerTest("ticket-rsc-sets-13", "Ticket - Resource sets (2 tickets, granted, standby)"),
SchedulerTest("ticket-rsc-sets-14", "Ticket - Resource sets (2 tickets, standby, revoked)"),
SchedulerTest("cluster-specific-params", "Cluster-specific instance attributes based on rules"),
SchedulerTest("site-specific-params", "Site-specific instance attributes based on rules"),
]),
SchedulerTestGroup([
SchedulerTest("template-1", "Template - 1"),
SchedulerTest("template-2", "Template - 2"),
SchedulerTest("template-3", "Template - 3 (merge operations)"),
SchedulerTest("template-coloc-1", "Template - Colocation 1"),
SchedulerTest("template-coloc-2", "Template - Colocation 2"),
SchedulerTest("template-coloc-3", "Template - Colocation 3"),
SchedulerTest("template-order-1", "Template - Order 1"),
SchedulerTest("template-order-2", "Template - Order 2"),
SchedulerTest("template-order-3", "Template - Order 3"),
SchedulerTest("template-ticket", "Template - Ticket"),
SchedulerTest("template-rsc-sets-1", "Template - Resource Sets 1"),
SchedulerTest("template-rsc-sets-2", "Template - Resource Sets 2"),
SchedulerTest("template-rsc-sets-3", "Template - Resource Sets 3"),
SchedulerTest("template-rsc-sets-4", "Template - Resource Sets 4"),
SchedulerTest("template-clone-primitive", "Cloned primitive from template"),
SchedulerTest("template-clone-group", "Cloned group from template"),
SchedulerTest("location-sets-templates", "Resource sets and templates - Location"),
SchedulerTest("tags-coloc-order-1", "Tags - Colocation and Order (Simple)"),
SchedulerTest("tags-coloc-order-2", "Tags - Colocation and Order (Resource Sets with Templates)"),
SchedulerTest("tags-location", "Tags - Location"),
SchedulerTest("tags-ticket", "Tags - Ticket"),
]),
SchedulerTestGroup([
SchedulerTest("container-1", "Container - initial"),
SchedulerTest("container-2", "Container - monitor failed"),
SchedulerTest("container-3", "Container - stop failed"),
SchedulerTest("container-4", "Container - reached migration-threshold"),
SchedulerTest("container-group-1", "Container in group - initial"),
SchedulerTest("container-group-2", "Container in group - monitor failed"),
SchedulerTest("container-group-3", "Container in group - stop failed"),
SchedulerTest("container-group-4", "Container in group - reached migration-threshold"),
SchedulerTest("container-is-remote-node", "Place resource within container when container is remote-node"),
SchedulerTest("bug-rh-1097457", "Kill user defined container/contents ordering"),
SchedulerTest("bug-cl-5247", "Graph loop when recovering m/s resource in a container"),
SchedulerTest("bundle-order-startup", "Bundle startup ordering"),
SchedulerTest("bundle-order-partial-start",
"Bundle startup ordering when some dependencies are already running"),
SchedulerTest("bundle-order-partial-start-2",
"Bundle startup ordering when some dependencies and the container are already running"),
SchedulerTest("bundle-order-stop", "Bundle stop ordering"),
SchedulerTest("bundle-order-partial-stop", "Bundle startup ordering when some dependencies are already stopped"),
SchedulerTest("bundle-order-stop-on-remote", "Stop nested resource after bringing up the connection"),
SchedulerTest("bundle-order-startup-clone", "Prevent startup because bundle isn't promoted"),
SchedulerTest("bundle-order-startup-clone-2", "Bundle startup with clones"),
SchedulerTest("bundle-order-stop-clone", "Stop bundle because clone is stopping"),
SchedulerTest("bundle-interleave-start", "Interleave bundle starts"),
SchedulerTest("bundle-interleave-promote", "Interleave bundle promotes"),
SchedulerTest("bundle-nested-colocation", "Colocation of nested connection resources"),
SchedulerTest("bundle-order-fencing",
"Order pseudo bundle fencing after parent node fencing if both are happening"),
SchedulerTest("bundle-probe-order-1", "order 1"),
SchedulerTest("bundle-probe-order-2", "order 2"),
SchedulerTest("bundle-probe-order-3", "order 3"),
SchedulerTest("bundle-probe-remotes", "Ensure remotes get probed too"),
SchedulerTest("bundle-replicas-change", "Change bundle from 1 replica to multiple"),
SchedulerTest("bundle-connection-with-container", "Don't move a container due to connection preferences"),
SchedulerTest("nested-remote-recovery", "Recover bundle's container hosted on remote node"),
SchedulerTest("bundle-promoted-location-1",
"Promotable bundle, positive location"),
SchedulerTest("bundle-promoted-location-2",
"Promotable bundle, negative location"),
SchedulerTest("bundle-promoted-location-3",
"Promotable bundle, positive location for promoted role"),
SchedulerTest("bundle-promoted-location-4",
"Promotable bundle, negative location for promoted role"),
SchedulerTest("bundle-promoted-location-5",
"Promotable bundle, positive location for unpromoted role"),
SchedulerTest("bundle-promoted-location-6",
"Promotable bundle, negative location for unpromoted role"),
SchedulerTest("bundle-promoted-colocation-1",
"Primary promoted bundle, dependent primitive (mandatory coloc)"),
SchedulerTest("bundle-promoted-colocation-2",
"Primary promoted bundle, dependent primitive (optional coloc)"),
SchedulerTest("bundle-promoted-colocation-3",
"Dependent promoted bundle, primary primitive (mandatory coloc)"),
SchedulerTest("bundle-promoted-colocation-4",
"Dependent promoted bundle, primary primitive (optional coloc)"),
SchedulerTest("bundle-promoted-colocation-5",
"Primary and dependent promoted bundle instances (mandatory coloc)"),
SchedulerTest("bundle-promoted-colocation-6",
"Primary and dependent promoted bundle instances (optional coloc)"),
SchedulerTest("bundle-promoted-anticolocation-1",
"Primary promoted bundle, dependent primitive (mandatory anti)"),
SchedulerTest("bundle-promoted-anticolocation-2",
"Primary promoted bundle, dependent primitive (optional anti)"),
SchedulerTest("bundle-promoted-anticolocation-3",
"Dependent promoted bundle, primary primitive (mandatory anti)"),
SchedulerTest("bundle-promoted-anticolocation-4",
"Dependent promoted bundle, primary primitive (optional anti)"),
SchedulerTest("bundle-promoted-anticolocation-5",
"Primary and dependent promoted bundle instances (mandatory anti)"),
SchedulerTest("bundle-promoted-anticolocation-6",
"Primary and dependent promoted bundle instances (optional anti)"),
]),
SchedulerTestGroup([
SchedulerTest("whitebox-fail1", "Fail whitebox container rsc"),
SchedulerTest("whitebox-fail2", "Fail cluster connection to guest node"),
SchedulerTest("whitebox-fail3", "Failed containers should not run nested on remote nodes"),
SchedulerTest("whitebox-start", "Start whitebox container with resources assigned to it"),
SchedulerTest("whitebox-stop", "Stop whitebox container with resources assigned to it"),
SchedulerTest("whitebox-move", "Move whitebox container with resources assigned to it"),
SchedulerTest("whitebox-asymmetric", "Verify connection rsc opts-in based on container resource"),
SchedulerTest("whitebox-ms-ordering", "Verify promote/demote can not occur before connection is established"),
SchedulerTest("whitebox-ms-ordering-move", "Stop/Start cycle within a moving container"),
SchedulerTest("whitebox-orphaned", "Properly shutdown orphaned whitebox container"),
SchedulerTest("whitebox-orphan-ms", "Properly tear down orphan ms resources on remote-nodes"),
SchedulerTest("whitebox-unexpectedly-running", "Recover container nodes the cluster did not start"),
SchedulerTest("whitebox-migrate1", "Migrate both container and connection resource"),
SchedulerTest("whitebox-imply-stop-on-fence",
"imply stop action on container node rsc when host node is fenced"),
SchedulerTest("whitebox-nested-group", "Verify guest remote-node works nested in a group"),
SchedulerTest("guest-node-host-dies", "Verify guest node is recovered if host goes away"),
SchedulerTest("guest-node-cleanup", "Order guest node connection recovery after container probe"),
SchedulerTest("guest-host-not-fenceable", "Actions on guest node are unrunnable if host is unclean and cannot be fenced"),
]),
SchedulerTestGroup([
SchedulerTest("remote-startup-probes", "Baremetal remote-node startup probes"),
SchedulerTest("remote-startup", "Startup a newly discovered remote-nodes with no status"),
SchedulerTest("remote-fence-unclean", "Fence unclean baremetal remote-node"),
SchedulerTest("remote-fence-unclean2",
"Fence baremetal remote-node after cluster node fails and connection can not be recovered"),
SchedulerTest("remote-fence-unclean-3", "Probe failed remote nodes (triggers fencing)"),
SchedulerTest("remote-move", "Move remote-node connection resource"),
SchedulerTest("remote-disable", "Disable a baremetal remote-node"),
SchedulerTest("remote-probe-disable", "Probe then stop a baremetal remote-node"),
SchedulerTest("remote-orphaned", "Properly shutdown orphaned connection resource"),
SchedulerTest("remote-orphaned2",
"verify we can handle orphaned remote connections with active resources on the remote"),
SchedulerTest("remote-recover", "Recover connection resource after cluster-node fails"),
SchedulerTest("remote-stale-node-entry",
"Make sure we properly handle leftover remote-node entries in the node section"),
SchedulerTest("remote-partial-migrate",
"Make sure partial migrations are handled before ops on the remote node"),
SchedulerTest("remote-partial-migrate2",
"Make sure partial migration target is prefered for remote connection"),
SchedulerTest("remote-recover-fail", "Make sure start failure causes fencing if rsc are active on remote"),
SchedulerTest("remote-start-fail",
"Make sure a start failure does not result in fencing if no active resources are on remote"),
SchedulerTest("remote-unclean2",
"Make monitor failure always results in fencing, even if no rsc are active on remote"),
SchedulerTest("remote-fence-before-reconnect", "Fence before clearing recurring monitor failure"),
SchedulerTest("remote-recovery", "Recover remote connections before attempting demotion"),
SchedulerTest("remote-recover-connection", "Optimistically recovery of only the connection"),
SchedulerTest("remote-recover-all", "Fencing when the connection has no home"),
SchedulerTest("remote-recover-no-resources", "Fencing when the connection has no home and no active resources"),
SchedulerTest("remote-recover-unknown",
"Fencing when the connection has no home and the remote has no operation history"),
SchedulerTest("remote-reconnect-delay", "Waiting for remote reconnect interval to expire"),
SchedulerTest("remote-connection-unrecoverable",
"Remote connection host must be fenced, with connection unrecoverable"),
SchedulerTest("remote-connection-shutdown", "Remote connection shutdown"),
SchedulerTest("cancel-behind-moving-remote",
"Route recurring monitor cancellations through original node of a moving remote connection"),
]),
SchedulerTestGroup([
SchedulerTest("resource-discovery", "Exercises resource-discovery location constraint option"),
SchedulerTest("rsc-discovery-per-node", "Disable resource discovery per node"),
SchedulerTest("shutdown-lock", "Ensure shutdown lock works properly"),
SchedulerTest("shutdown-lock-expiration", "Ensure shutdown lock expiration works properly"),
]),
SchedulerTestGroup([
SchedulerTest("op-defaults", "Test op_defaults conditional expressions"),
SchedulerTest("op-defaults-2", "Test op_defaults AND'ed conditional expressions"),
SchedulerTest("op-defaults-3", "Test op_defaults precedence"),
SchedulerTest("rsc-defaults", "Test rsc_defaults conditional expressions"),
SchedulerTest("rsc-defaults-2", "Test rsc_defaults conditional expressions without type"),
]),
SchedulerTestGroup([
- SchedulerTest("stop-all-resources", "Test stop-all-resources=true "),
+ SchedulerTest("stop-all-resources", "Test stop-all-resources=true"),
]),
SchedulerTestGroup([
SchedulerTest("ocf_degraded-remap-ocf_ok", "Test degraded remapped to OK"),
SchedulerTest("ocf_degraded_promoted-remap-ocf_ok", "Test degraded promoted remapped to OK"),
]),
]
TESTS_64BIT = [
SchedulerTestGroup([
SchedulerTest("year-2038", "Check handling of timestamps beyond 2038-01-19 03:14:08 UTC"),
]),
]
def is_executable(path):
- """ Check whether a file at a given path is executable. """
-
+ """Check whether a file at a given path is executable."""
try:
return os.stat(path)[stat.ST_MODE] & stat.S_IXUSR
except OSError:
return False
def diff(file1, file2, **kwargs):
- """ Call diff on two files """
-
- return subprocess.call([ "diff", "-u", "-N", "--ignore-all-space",
- "--ignore-blank-lines", file1, file2 ], **kwargs)
+ """Call diff on two files."""
+ return subprocess.call(["diff", "-u", "-N", "--ignore-all-space",
+ "--ignore-blank-lines", file1, file2], **kwargs)
def sort_file(filename):
- """ Sort a file alphabetically """
-
- with io.open(filename, "rt") as f:
+ """Sort a file alphabetically."""
+ with io.open(filename, "rt", encoding="utf-8") as f:
lines = sorted(f)
- with io.open(filename, "wt") as f:
+
+ with io.open(filename, "wt", encoding="utf-8") as f:
f.writelines(lines)
def remove_files(filenames):
- """ Remove a list of files """
-
+ """Remove a list of files."""
for filename in filenames:
try:
os.remove(filename)
except OSError:
pass
def normalize(filename):
- """ Remove text from a file that isn't important for comparison """
-
+ """Remove text from a file that isn't important for comparison."""
if not hasattr(normalize, "patterns"):
normalize.patterns = [
re.compile(r'crm_feature_set="[^"]*"'),
re.compile(r'batch-limit="[0-9]*"')
]
- if os.path.isfile(filename):
- with io.open(filename, "rt") as f:
- lines = f.readlines()
- with io.open(filename, "wt") as f:
- for line in lines:
- for pattern in normalize.patterns:
- line = pattern.sub("", line)
- f.write(line)
+ if not os.path.isfile(filename):
+ return
+
+ with io.open(filename, "rt", encoding="utf-8") as f:
+ lines = f.readlines()
+
+ with io.open(filename, "wt", encoding="utf-8") as f:
+ for line in lines:
+ for pattern in normalize.patterns:
+ line = pattern.sub("", line)
+
+ f.write(line)
-def cat(filename, dest=sys.stdout):
- """ Copy a file to a destination file descriptor """
- with io.open(filename, "rt") as f:
+def cat(filename, dest=sys.stdout):
+ """Copy a file to a destination file descriptor."""
+ with io.open(filename, "rt", encoding="utf-8") as f:
shutil.copyfileobj(f, dest)
-class CtsScheduler(object):
- """ Regression tests for Pacemaker's scheduler """
+class CtsScheduler:
+ """Regression tests for Pacemaker's scheduler."""
def _parse_args(self, argv):
- """ Parse command-line arguments """
-
- parser = argparse.ArgumentParser(description=DESC)
+ """Parse command-line arguments."""
+ parser = argparse.ArgumentParser(description="Regression tests for Pacemaker's scheduler")
parser.add_argument('-V', '--verbose', action='count',
help='Display any differences from expected output')
-
parser.add_argument('--run', metavar='TEST',
help=('Run only single specified test (any further '
'arguments will be passed to crm_simulate)'))
-
parser.add_argument('--update', action='store_true',
help='Update expected results with actual results')
-
parser.add_argument('-b', '--binary', metavar='PATH',
help='Specify path to crm_simulate')
-
parser.add_argument('-i', '--io-dir', metavar='PATH',
help='Specify path to regression test data directory')
-
parser.add_argument('-o', '--out-dir', metavar='PATH',
help='Specify where intermediate and output files should go')
-
parser.add_argument('-v', '--valgrind', action='store_true',
help='Run all commands under valgrind')
-
parser.add_argument('--valgrind-dhat', action='store_true',
help='Run all commands under valgrind with heap analyzer')
-
parser.add_argument('--valgrind-skip-output', action='store_true',
help='If running under valgrind, do not display output')
-
parser.add_argument('--testcmd-options', metavar='OPTIONS', default='',
help='Additional options for command under test')
# argparse can't handle "everything after --run TEST", so grab that
self.single_test_args = []
narg = 0
for arg in argv:
- narg = narg + 1
+ narg += 1
if arg == '--run':
- (argv, self.single_test_args) = (argv[:narg+1], argv[narg+1:])
+ (argv, self.single_test_args) = (argv[:narg + 1], argv[narg + 1:])
break
self.args = parser.parse_args(argv[1:])
def _error(self, s):
+ """Print an error message."""
print(f" * ERROR: {s}")
def _failed(self, s):
+ """Print a failure message."""
print(f" * FAILED: {s}")
def _get_valgrind_cmd(self):
- """ Return command arguments needed (or not) to run valgrind """
-
+ """Return command arguments needed (or not) to run valgrind."""
if self.args.valgrind:
os.environ['G_SLICE'] = "always-malloc"
return [
"valgrind",
"-q",
"--gen-suppressions=all",
"--time-stamp=yes",
"--trace-children=no",
"--show-reachable=no",
"--leak-check=full",
"--num-callers=20",
f"--suppressions={self.test_home}/valgrind-pcmk.suppressions"
]
if self.args.valgrind_dhat:
os.environ['G_SLICE'] = "always-malloc"
return [
"valgrind",
"--tool=exp-dhat",
"--time-stamp=yes",
"--trace-children=no",
"--show-top-n=100",
"--num-callers=4"
]
return []
def _get_simulator_cmd(self):
- """ Locate the simulation binary """
-
+ """Locate the simulation binary."""
if self.args.binary is None:
- self.args.binary = BuildOptions._BUILD_DIR + "/tools/crm_simulate"
+ # pylint: disable=protected-access
+ self.args.binary = f"{BuildOptions._BUILD_DIR}/tools/crm_simulate"
+
if not is_executable(self.args.binary):
- self.args.binary = BuildOptions.SBIN_DIR + "/crm_simulate"
+ self.args.binary = f"{BuildOptions.SBIN_DIR}/crm_simulate"
if not is_executable(self.args.binary):
# @TODO it would be more pythonic to raise an exception
- self._error("Test binary " + self.args.binary + " not found")
+ self._error(f"Test binary {self.args.binary} not found")
sys.exit(ExitStatus.NOT_INSTALLED)
- return [ self.args.binary ] + shlex.split(self.args.testcmd_options)
+ return [self.args.binary] + shlex.split(self.args.testcmd_options)
def set_schema_env(self):
- """ Ensure schema directory environment variable is set, if possible """
-
+ """Ensure schema directory environment variable is set, if possible."""
try:
return os.environ['PCMK_schema_directory']
except KeyError:
- for d in [ os.path.join(BuildOptions._BUILD_DIR, "xml"),
- BuildOptions.SCHEMA_DIR ]:
- if os.path.isdir(d):
- os.environ['PCMK_schema_directory'] = d
- return d
+ # pylint: disable=protected-access
+ for d in [os.path.join(BuildOptions._BUILD_DIR, "xml"),
+ BuildOptions.SCHEMA_DIR]:
+ if not os.path.isdir(d):
+ continue
+
+ os.environ['PCMK_schema_directory'] = d
+ return d
+
return None
def __init__(self, argv=sys.argv):
-
+ """Create a new CtsScheduler instance."""
# Ensure all command output is in portable locale for comparison
os.environ['LC_ALL'] = "C"
self._parse_args(argv)
# Where this executable lives
self.test_home = os.path.dirname(os.path.realpath(argv[0]))
# Where test data resides
if self.args.io_dir is None:
self.args.io_dir = os.path.join(self.test_home, "scheduler")
self.xml_input_dir = os.path.join(self.args.io_dir, "xml")
self.expected_dir = os.path.join(self.args.io_dir, "exp")
self.dot_expected_dir = os.path.join(self.args.io_dir, "dot")
self.scores_dir = os.path.join(self.args.io_dir, "scores")
self.summary_dir = os.path.join(self.args.io_dir, "summary")
self.stderr_expected_dir = os.path.join(self.args.io_dir, "stderr")
# Create a temporary directory to store diff file
self.failed_dir = tempfile.mkdtemp(prefix='cts-scheduler_')
# Where to store generated files
if self.args.out_dir is None:
self.args.out_dir = self.args.io_dir
self.failed_filename = os.path.join(self.failed_dir, "test-output.diff")
else:
self.failed_filename = os.path.join(self.args.out_dir, "test-output.diff")
+
os.environ['CIB_shadow_dir'] = self.args.out_dir
-
+
self.failed_file = None
self.outfile_out_dir = os.path.join(self.args.out_dir, "out")
self.dot_out_dir = os.path.join(self.args.out_dir, "dot")
self.scores_out_dir = os.path.join(self.args.out_dir, "scores")
self.summary_out_dir = os.path.join(self.args.out_dir, "summary")
self.stderr_out_dir = os.path.join(self.args.out_dir, "stderr")
self.valgrind_out_dir = os.path.join(self.args.out_dir, "valgrind")
# Single test mode (if requested)
try:
# User can give test base name or file name of a test input
self.args.run = os.path.splitext(os.path.basename(self.args.run))[0]
except (AttributeError, TypeError):
- pass # --run was not specified
+ pass # --run was not specified
self.set_schema_env()
# Arguments needed (or not) to run commands
self.valgrind_args = self._get_valgrind_cmd()
self.simulate_args = self._get_simulator_cmd()
# Test counters
self.num_failed = 0
self.num_tests = 0
# Ensure that the main output directory exists
# We don't want to create it with os.makedirs below
if not os.path.isdir(self.args.out_dir):
self._error("Output directory missing; can't create output files")
sys.exit(ExitStatus.CANTCREAT)
# Create output subdirectories if they don't exist
try:
os.makedirs(self.outfile_out_dir, 0o755, True)
os.makedirs(self.dot_out_dir, 0o755, True)
os.makedirs(self.scores_out_dir, 0o755, True)
os.makedirs(self.summary_out_dir, 0o755, True)
os.makedirs(self.stderr_out_dir, 0o755, True)
+
if self.valgrind_args:
os.makedirs(self.valgrind_out_dir, 0o755, True)
except OSError as ex:
self._error(f"Unable to create output subdirectory: {ex}")
remove_files([
self.outfile_out_dir,
self.dot_out_dir,
self.scores_out_dir,
self.summary_out_dir,
self.stderr_out_dir,
])
+
sys.exit(ExitStatus.CANTCREAT)
def _compare_files(self, filename1, filename2):
- """ Add any file differences to failed results """
-
+ """Add any file differences to failed results."""
if diff(filename1, filename2, stdout=subprocess.DEVNULL) != 0:
diff(filename1, filename2, stdout=self.failed_file, stderr=subprocess.DEVNULL)
self.failed_file.write("\n")
return True
+
return False
- def run_one(self, test_name, test_desc, test_args):
- """ Run one scheduler test """
+ def _file_missing(self, path):
+ """Return True if path does not exist or is empty."""
+ return not os.path.isfile(path) or os.path.getsize(path) == 0
- s = test_name + ":"
- print(f" Test {s:41} {test_desc}")
+ def run_one(self, test_name, test_desc, test_args):
+ """Run one scheduler test."""
+ # pylint: disable=too-many-locals
+ print(f" Test {f'{test_name}:':41} {test_desc}")
did_fail = False
- self.num_tests = self.num_tests + 1
+ self.num_tests += 1
# Test inputs
input_filename = os.path.join(self.xml_input_dir, f"{test_name}.xml")
expected_filename = os.path.join(self.expected_dir, f"{test_name}.exp")
dot_expected_filename = os.path.join(self.dot_expected_dir, f"{test_name}.dot")
scores_filename = os.path.join(self.scores_dir, f"{test_name}.scores")
summary_filename = os.path.join(self.summary_dir, f"{test_name}.summary")
stderr_expected_filename = os.path.join(self.stderr_expected_dir, f"{test_name}.stderr")
# (Intermediate) test outputs
output_filename = os.path.join(self.outfile_out_dir, f"{test_name}.out")
dot_output_filename = os.path.join(self.dot_out_dir, f"{test_name}.dot.pe")
score_output_filename = os.path.join(self.scores_out_dir, f"{test_name}.scores.pe")
summary_output_filename = os.path.join(self.summary_out_dir, f"{test_name}.summary.pe")
stderr_output_filename = os.path.join(self.stderr_out_dir, f"{test_name}.stderr.pe")
valgrind_output_filename = os.path.join(self.valgrind_out_dir, f"{test_name}.valgrind")
# Common arguments for running test
test_cmd = []
if self.valgrind_args:
- test_cmd = self.valgrind_args + [ f"--log-file={valgrind_output_filename}" ]
- test_cmd = test_cmd + self.simulate_args
+ test_cmd = self.valgrind_args + [f"--log-file={valgrind_output_filename}"]
+
+ test_cmd += self.simulate_args
# @TODO It would be more pythonic to raise exceptions for errors,
# then perhaps it would be nice to make a single-test class
# Ensure necessary test inputs exist
if not os.path.isfile(input_filename):
self._error("No input")
- self.num_failed = self.num_failed + 1
+ self.num_failed += 1
return ExitStatus.NOINPUT
+
if not self.args.update and not os.path.isfile(expected_filename):
self._error("no stored output")
return ExitStatus.NOINPUT
# Run simulation to generate summary output
- test_cmd_full = test_cmd + [ '-x', input_filename, '-S' ] + test_args
- if self.args.run: # Single test mode
+ test_cmd_full = test_cmd + ['-x', input_filename, '-S'] + test_args
+ if self.args.run: # Single test mode
print(" ".join(test_cmd_full))
- with io.open(summary_output_filename, "wt") as f:
+ with io.open(summary_output_filename, "wt", encoding="utf-8") as f:
subprocess.run(test_cmd_full, stdout=f, stderr=subprocess.STDOUT,
env=os.environ, check=False)
if self.args.run:
cat(summary_output_filename)
# Re-run simulation to generate dot, graph, and scores
- test_cmd_full = test_cmd + [
- '-x', input_filename,
- '-D', dot_output_filename,
- '-G', output_filename,
- '-sSQ' ] + test_args
- with io.open(stderr_output_filename, "wt") as f_stderr, \
- io.open(score_output_filename, "wt") as f_score:
+ test_cmd_full = test_cmd + ['-x', input_filename,
+ '-D', dot_output_filename,
+ '-G', output_filename,
+ '-sSQ'] + test_args
+
+ with io.open(stderr_output_filename, "wt", encoding="utf-8") as f_stderr, \
+ io.open(score_output_filename, "wt", encoding="utf-8") as f_score:
rc = subprocess.call(test_cmd_full, stdout=f_score, stderr=f_stderr, env=os.environ)
# Check for test command failure
if rc != ExitStatus.OK:
self._failed(f"Test returned: {rc}")
did_fail = True
print(" ".join(test_cmd_full))
# Check for valgrind errors
if self.valgrind_args and not self.args.valgrind_skip_output:
- if os.stat(valgrind_output_filename).st_size > 0:
+ if os.path.getsize(valgrind_output_filename) > 0:
self._failed("Valgrind reported errors")
did_fail = True
cat(valgrind_output_filename)
- remove_files([ valgrind_output_filename ])
+
+ remove_files([valgrind_output_filename])
# Check for core dump
if os.path.isfile("core"):
- self._failed("Core-file detected: core." + test_name)
+ self._failed(f"Core-file detected: core.{test_name}")
did_fail = True
os.rename("core", f"{self.test_home}/core.{test_name}")
# Check any stderr output
if os.path.isfile(stderr_expected_filename):
if self._compare_files(stderr_expected_filename, stderr_output_filename):
self._failed("stderr changed")
did_fail = True
- elif os.stat(stderr_output_filename).st_size > 0:
+ elif os.path.getsize(stderr_output_filename) > 0:
self._failed("Output was written to stderr")
did_fail = True
cat(stderr_output_filename)
- remove_files([ stderr_output_filename ])
+
+ remove_files([stderr_output_filename])
# Check whether output graph exists, and normalize it
- if (not os.path.isfile(output_filename)
- or os.stat(output_filename).st_size == 0):
+ if self._file_missing(output_filename):
self._error("No graph produced")
did_fail = True
- self.num_failed = self.num_failed + 1
- remove_files([ output_filename ])
+ self.num_failed += 1
+ remove_files([output_filename])
return ExitStatus.ERROR
+
normalize(output_filename)
# Check whether dot output exists, and sort it
- if (not os.path.isfile(dot_output_filename) or
- os.stat(dot_output_filename).st_size == 0):
+ if self._file_missing(dot_output_filename):
self._error("No dot-file summary produced")
did_fail = True
- self.num_failed = self.num_failed + 1
- remove_files([ dot_output_filename, output_filename ])
+ self.num_failed += 1
+ remove_files([dot_output_filename, output_filename])
return ExitStatus.ERROR
- with io.open(dot_output_filename, "rt") as f:
- first_line = f.readline() # "digraph" line with opening brace
+
+ with io.open(dot_output_filename, "rt", encoding="utf-8") as f:
+ first_line = f.readline() # "digraph" line with opening brace
lines = f.readlines()
- last_line = lines[-1] # closing brace
+ last_line = lines[-1] # closing brace
del lines[-1]
- lines = sorted(set(lines)) # unique sort
- with io.open(dot_output_filename, "wt") as f:
+ lines = sorted(set(lines)) # unique sort
+
+ with io.open(dot_output_filename, "wt", encoding="utf-8") as f:
f.write(first_line)
f.writelines(lines)
f.write(last_line)
# Check whether score output exists, and sort it
- if (not os.path.isfile(score_output_filename)
- or os.stat(score_output_filename).st_size == 0):
+ if self._file_missing(score_output_filename):
self._error("No allocation scores produced")
did_fail = True
- self.num_failed = self.num_failed + 1
- remove_files([ score_output_filename, output_filename ])
+ self.num_failed += 1
+ remove_files([score_output_filename, output_filename])
return ExitStatus.ERROR
- else:
- sort_file(score_output_filename)
+
+ sort_file(score_output_filename)
if self.args.update:
shutil.copyfile(output_filename, expected_filename)
shutil.copyfile(dot_output_filename, dot_expected_filename)
shutil.copyfile(score_output_filename, scores_filename)
shutil.copyfile(summary_output_filename, summary_filename)
print(" Updated expected outputs")
if self._compare_files(summary_filename, summary_output_filename):
self._failed("summary changed")
did_fail = True
if self._compare_files(dot_expected_filename, dot_output_filename):
self._failed("dot-file summary changed")
did_fail = True
else:
- remove_files([ dot_output_filename ])
+ remove_files([dot_output_filename])
if self._compare_files(expected_filename, output_filename):
self._failed("xml-file changed")
did_fail = True
if self._compare_files(scores_filename, score_output_filename):
self._failed("scores-file changed")
did_fail = True
- remove_files([ output_filename,
- dot_output_filename,
- score_output_filename,
- summary_output_filename])
+ remove_files([output_filename,
+ dot_output_filename,
+ score_output_filename,
+ summary_output_filename])
if did_fail:
- self.num_failed = self.num_failed + 1
+ self.num_failed += 1
return ExitStatus.ERROR
return ExitStatus.OK
def run_all(self):
- """ Run all defined tests """
-
+ """Run all defined tests."""
if platform.architecture()[0] == "64bit":
TESTS.extend(TESTS_64BIT)
for group in TESTS:
for test in group.tests:
self.run_one(test.name, test.desc, test.args)
+
print()
def _print_summary(self):
- """ Print a summary of parameters for this test run """
+ """Print a summary of parameters for this test run."""
+ print(f"Test home is:\t{self.test_home}")
+ print(f"Test binary is:\t{self.args.binary}")
- print("Test home is:\t" + self.test_home)
- print("Test binary is:\t" + self.args.binary)
if 'PCMK_schema_directory' in os.environ:
- print("Schema home is:\t" + os.environ['PCMK_schema_directory'])
- if self.valgrind_args != []:
+ print(f"Schema home is:\t{os.environ['PCMK_schema_directory']}")
+
+ if self.valgrind_args:
print("Activating memory testing with valgrind")
+
print()
def _test_results(self):
+ """Report test results."""
if self.num_failed == 0:
shutil.rmtree(self.failed_dir)
return ExitStatus.OK
- if os.path.isfile(self.failed_filename) and os.stat(self.failed_filename).st_size != 0:
- if self.args.verbose:
- self._error(f"Results of {self.num_failed} failed tests (out of {self.num_tests}):")
- cat(self.failed_filename)
- else:
- self._error(f"Results of {self.num_failed} failed tests (out of {self.num_tests}) "
- f"are in {self.failed_filename}")
- self._error("Use -V to display them after running the tests")
- else:
+ if self._file_missing(self.failed_filename):
self._error(f"{self.num_failed} (of {self.num_tests}) tests failed (no diff results)")
if os.path.isfile(self.failed_filename):
shutil.rmtree(self.failed_dir)
+ elif self.args.verbose:
+ self._error(f"Results of {self.num_failed} failed tests (out of {self.num_tests}):")
+ cat(self.failed_filename)
+ else:
+ self._error(f"Results of {self.num_failed} failed tests (out of {self.num_tests}) "
+ f"are in {self.failed_filename}")
+ self._error("Use -V to display them after running the tests")
+
return ExitStatus.ERROR
def find_test(self, name):
+ """Return the SchedulerTest object with the given name."""
if platform.architecture()[0] == "64bit":
TESTS.extend(TESTS_64BIT)
for group in TESTS:
for test in group.tests:
if test.name == name:
return test
return None
def run(self):
- """ Run test(s) as specified """
-
+ """Run test(s) as specified."""
# Check for pre-existing core so we don't think it's from us
if os.path.exists("core"):
- self._failed("Can't run with core already present in " + self.test_home)
+ self._failed(f"Can't run with core already present in {self.test_home}")
return ExitStatus.OSFILE
self._print_summary()
# Zero out the error log
- self.failed_file = io.open(self.failed_filename, "wt")
+ # pylint: disable=consider-using-with
+ self.failed_file = io.open(self.failed_filename, "wt", encoding="utf-8")
if self.args.run is None:
- print("Performing the following tests from " + self.args.io_dir)
+ print(f"Performing the following tests from {self.args.io_dir}")
print()
self.run_all()
print()
self.failed_file.close()
rc = self._test_results()
else:
# Find the test we were asked to run
test = self.find_test(self.args.run)
if test is None:
print(f"No test named {self.args.run}")
return ExitStatus.INVALID_PARAM
# If no arguments were given on the command line, default to the ones
# contained in the test
if self.single_test_args:
args = self.single_test_args
else:
args = test.args
rc = self.run_one(test.name, test.desc, args)
self.failed_file.close()
+
if self.num_failed > 0:
- print("\nFailures:\nThese have also been written to: " + self.failed_filename + "\n")
- cat(self.failed_filename)
- shutil.rmtree(self.failed_dir)
+ print(f"\nFailures:\nThese have also been written to: {self.failed_filename}\n")
+ cat(self.failed_filename)
+ shutil.rmtree(self.failed_dir)
return rc
if __name__ == "__main__":
sys.exit(CtsScheduler().run())
# vim: set filetype=python expandtab tabstop=4 softtabstop=4 shiftwidth=4 textwidth=120:
diff --git a/python/pylintrc b/python/pylintrc
index bb453f32a8..81f63fea95 100644
--- a/python/pylintrc
+++ b/python/pylintrc
@@ -1,557 +1,558 @@
# NOTE: Any line with CHANGED: describes something that we changed from the
# default pylintrc configuration.
[MAIN]
# Python code to execute, usually for sys.path manipulation such as
# pygtk.require().
#init-hook=
# Files or directories to be skipped. They should be base names, not
# paths.
ignore=CVS
# Add files or directories matching the regex patterns to the ignore-list. The
# regex matches against paths and can be in Posix or Windows format.
ignore-paths=
# Files or directories matching the regex patterns are skipped. The regex
# matches against base names, not paths.
ignore-patterns=^\.#
# Pickle collected data for later comparisons.
persistent=yes
# List of plugins (as comma separated values of python modules names) to load,
# usually to register additional checkers.
load-plugins=
# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the
# number of processors available to use.
jobs=1
# When enabled, pylint would attempt to guess common misconfiguration and emit
# user-friendly hints instead of false-positive error messages.
suggestion-mode=yes
# Allow loading of arbitrary C extensions. Extensions are imported into the
# active Python interpreter and may run arbitrary code.
unsafe-load-any-extension=no
# A comma-separated list of package or module names from where C extensions may
# be loaded. Extensions are loading into the active Python interpreter and may
# run arbitrary code
extension-pkg-allow-list=
# Minimum supported python version
# CHANGED
py-version = 3.6
# Control the amount of potential inferred values when inferring a single
# object. This can help the performance when dealing with large functions or
# complex, nested conditions.
limit-inference-results=100
# Specify a score threshold under which the program will exit with error.
fail-under=10.0
# Return non-zero exit code if any of these messages/categories are detected,
# even if score is above --fail-under value. Syntax same as enable. Messages
# specified are enabled, while categories only check already-enabled messages.
fail-on=
# Clear in-memory caches upon conclusion of linting. Useful if running pylint in
# a server-like mode.
clear-cache-post-run=no
[MESSAGES CONTROL]
# Only show warnings with the listed confidence levels. Leave empty to show
# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED
# confidence=
# Enable the message, report, category or checker with the given id(s). You can
# either give multiple identifier separated by comma (,) or put this option
# multiple time (only on the command line, not in the configuration file where
# it should appear only once). See also the "--disable" option for examples.
enable=
use-symbolic-message-instead,
useless-suppression,
# Disable the message, report, category or checker with the given id(s). You
# can either give multiple identifiers separated by comma (,) or put this
# option multiple times (only on the command line, not in the configuration
# file where it should appear only once).You can also use "--disable=all" to
# disable everything first and then re-enable specific checks. For example, if
# you want to run only the similarities checker, you can use "--disable=all
# --enable=similarities". If you want to run only the classes checker, but have
# no Warning level messages displayed, use"--disable=all --enable=classes
# --disable=W"
# CHANGED
-disable=line-too-long,
+disable=R0801,
+ line-too-long,
too-few-public-methods,
too-many-arguments,
too-many-branches,
too-many-instance-attributes,
too-many-statements,
unrecognized-option,
useless-option-value
[REPORTS]
# Set the output format. Available formats are text, parseable, colorized, msvs
# (visual studio) and html. You can also give a reporter class, eg
# mypackage.mymodule.MyReporterClass.
output-format=text
# Tells whether to display a full report or only the messages
reports=no
# Python expression which should return a note less than 10 (10 is the highest
# note). You have access to the variables 'fatal', 'error', 'warning', 'refactor', 'convention'
# and 'info', which contain the number of messages in each category, as
# well as 'statement', which is the total number of statements analyzed. This
# score is used by the global evaluation report (RP0004).
evaluation=max(0, 0 if fatal else 10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10))
# Template used to display messages. This is a python new-style format string
# used to format the message information. See doc for all details
#msg-template=
# Activate the evaluation score.
score=yes
[LOGGING]
# Logging modules to check that the string format arguments are in logging
# function parameter format
logging-modules=logging
# The type of string formatting that logging methods do. `old` means using %
# formatting, `new` is for `{}` formatting.
logging-format-style=old
[MISCELLANEOUS]
# List of note tags to take in consideration, separated by a comma.
# CHANGED: Don't do anything about FIXME, XXX, or TODO
notes=
# Regular expression of note tags to take in consideration.
#notes-rgx=
[SIMILARITIES]
# Minimum lines number of a similarity.
min-similarity-lines=6
# Ignore comments when computing similarities.
ignore-comments=yes
# Ignore docstrings when computing similarities.
ignore-docstrings=yes
# Ignore imports when computing similarities.
ignore-imports=yes
# Signatures are removed from the similarity computation
ignore-signatures=yes
[VARIABLES]
# Tells whether we should check for unused import in __init__ files.
init-import=no
# List of additional names supposed to be defined in builtins. Remember that
# you should avoid defining new builtins when possible.
additional-builtins=
# List of strings which can identify a callback function by name. A callback
# name must start or end with one of those strings.
callbacks=cb_,_cb
# Tells whether unused global variables should be treated as a violation.
allow-global-unused-variables=yes
# List of names allowed to shadow builtins
allowed-redefined-builtins=
# List of qualified module names which can have objects that can redefine
# builtins.
redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io
[FORMAT]
# Maximum number of characters on a single line.
max-line-length=100
# Regexp for a line that is allowed to be longer than the limit.
ignore-long-lines=^\s*(# )??$
# Allow the body of an if to be on the same line as the test if there is no
# else.
single-line-if-stmt=no
# Allow the body of a class to be on the same line as the declaration if body
# contains single statement.
single-line-class-stmt=no
# Maximum number of lines in a module
max-module-lines=2000
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
# tab).
indent-string=' '
# Number of spaces of indent required inside a hanging or continued line.
indent-after-paren=4
# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
expected-line-ending-format=
[BASIC]
# Good variable names which should always be accepted, separated by a comma
# CHANGED: Single variable names are handled by variable-rgx below, leaving
# _ here as the name for any variable that should be ignored.
good-names=_
# Good variable names regexes, separated by a comma. If names match any regex,
# they will always be accepted
good-names-rgxs=
# Bad variable names which should always be refused, separated by a comma
bad-names=foo,bar,baz,toto,tutu,tata
# Bad variable names regexes, separated by a comma. If names match any regex,
# they will always be refused
bad-names-rgxs=
# Colon-delimited sets of names that determine each other's naming style when
# the name regexes allow several styles.
name-group=
# Include a hint for the correct naming format with invalid-name
include-naming-hint=no
# Naming style matching correct function names.
function-naming-style=snake_case
# Regular expression matching correct function names
function-rgx=[a-z_][a-z0-9_]{2,30}$
# Naming style matching correct variable names.
variable-naming-style=snake_case
# Regular expression matching correct variable names
# CHANGED: One letter variables are fine
variable-rgx=[a-z_][a-z0-9_]{,30}$
# Naming style matching correct constant names.
const-naming-style=UPPER_CASE
# Regular expression matching correct constant names
const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$
# Naming style matching correct attribute names.
attr-naming-style=snake_case
# Regular expression matching correct attribute names
attr-rgx=[a-z_][a-z0-9_]{2,}$
# Naming style matching correct argument names.
argument-naming-style=snake_case
# Regular expression matching correct argument names
argument-rgx=[a-z_][a-z0-9_]{2,30}$
# Naming style matching correct class attribute names.
class-attribute-naming-style=any
# Regular expression matching correct class attribute names
class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
# Naming style matching correct class constant names.
class-const-naming-style=UPPER_CASE
# Regular expression matching correct class constant names. Overrides class-
# const-naming-style.
#class-const-rgx=
# Naming style matching correct inline iteration names.
inlinevar-naming-style=any
# Regular expression matching correct inline iteration names
inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
# Naming style matching correct class names.
class-naming-style=PascalCase
# Regular expression matching correct class names
class-rgx=[A-Z_][a-zA-Z0-9]+$
# Naming style matching correct module names.
module-naming-style=snake_case
# Regular expression matching correct module names
module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
# Naming style matching correct method names.
method-naming-style=snake_case
# Regular expression matching correct method names
method-rgx=[a-z_][a-z0-9_]{2,}$
# Regular expression matching correct type variable names
#typevar-rgx=
# Regular expression which should only match function or class names that do
# not require a docstring. Use ^(?!__init__$)_ to also check __init__.
no-docstring-rgx=__.*__
# Minimum line length for functions/classes that require docstrings, shorter
# ones are exempt.
docstring-min-length=-1
# List of decorators that define properties, such as abc.abstractproperty.
property-classes=abc.abstractproperty
[TYPECHECK]
# Regex pattern to define which classes are considered mixins if ignore-mixin-
# members is set to 'yes'
mixin-class-rgx=.*MixIn
# List of module names for which member attributes should not be checked
# (useful for modules/projects where namespaces are manipulated during runtime
# and thus existing member attributes cannot be deduced by static analysis). It
# supports qualified module names, as well as Unix pattern matching.
ignored-modules=
# List of class names for which member attributes should not be checked (useful
# for classes with dynamically set attributes). This supports the use of
# qualified names.
ignored-classes=SQLObject, optparse.Values, thread._local, _thread._local
# List of members which are set dynamically and missed by pylint inference
# system, and so shouldn't trigger E1101 when accessed. Python regular
# expressions are accepted.
generated-members=REQUEST,acl_users,aq_parent,argparse.Namespace
# List of decorators that create context managers from functions, such as
# contextlib.contextmanager.
contextmanager-decorators=contextlib.contextmanager
# Tells whether to warn about missing members when the owner of the attribute
# is inferred to be None.
ignore-none=yes
# This flag controls whether pylint should warn about no-member and similar
# checks whenever an opaque object is returned when inferring. The inference
# can return multiple potential results while evaluating a Python object, but
# some branches might not be evaluated, which results in partial inference. In
# that case, it might be useful to still emit no-member and other checks for
# the rest of the inferred objects.
ignore-on-opaque-inference=yes
# Show a hint with possible names when a member name was not found. The aspect
# of finding the hint is based on edit distance.
missing-member-hint=yes
# The minimum edit distance a name should have in order to be considered a
# similar match for a missing member name.
missing-member-hint-distance=1
# The total number of similar names that should be taken in consideration when
# showing a hint for a missing member.
missing-member-max-choices=1
[SPELLING]
# Spelling dictionary name. Available dictionaries: none. To make it working
# install python-enchant package.
spelling-dict=
# List of comma separated words that should not be checked.
spelling-ignore-words=
# List of comma separated words that should be considered directives if they
# appear and the beginning of a comment and should not be checked.
spelling-ignore-comment-directives=fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy:,pragma:,# noinspection
# A path to a file that contains private dictionary; one word per line.
spelling-private-dict-file=.pyenchant_pylint_custom_dict.txt
# Tells whether to store unknown words to indicated private dictionary in
# --spelling-private-dict-file option instead of raising a message.
spelling-store-unknown-words=no
# Limits count of emitted suggestions for spelling mistakes.
max-spelling-suggestions=2
[DESIGN]
# Maximum number of arguments for function / method
max-args=10
# Maximum number of locals for function / method body
max-locals=25
# Maximum number of return / yield for function / method body
max-returns=11
# Maximum number of branch for function / method body
max-branches=27
# Maximum number of statements in function / method body
max-statements=100
# Maximum number of parents for a class (see R0901).
max-parents=7
# List of qualified class names to ignore when counting class parents (see R0901).
ignored-parents=
# Maximum number of attributes for a class (see R0902).
max-attributes=11
# Minimum number of public methods for a class (see R0903).
min-public-methods=2
# Maximum number of public methods for a class (see R0904).
max-public-methods=25
# Maximum number of boolean expressions in an if statement (see R0916).
max-bool-expr=5
# Maximum number of statements in a try-block
max-try-statements = 14
# List of regular expressions of class ancestor names to
# ignore when counting public methods (see R0903).
exclude-too-few-public-methods=
[CLASSES]
# List of method names used to declare (i.e. assign) instance attributes.
# CHANGED: Remove setUp and __post_init__, add reset
defining-attr-methods=__init__,__new__,reset
# List of valid names for the first argument in a class method.
valid-classmethod-first-arg=cls
# List of valid names for the first argument in a metaclass class method.
valid-metaclass-classmethod-first-arg=mcs
# List of member names, which should be excluded from the protected access
# warning.
exclude-protected=_asdict,_fields,_replace,_source,_make
# Warn about protected attribute access inside special methods
check-protected-access-in-special-methods=no
[IMPORTS]
# List of modules that can be imported at any level, not just the top level
# one.
allow-any-import-level=
# Allow wildcard imports from modules that define __all__.
allow-wildcard-with-all=no
# Analyse import fallback blocks. This can be used to support both Python 2 and
# 3 compatible code, which means that the block might have code that exists
# only in one or another interpreter, leading to false positives when analysed.
analyse-fallback-blocks=no
# Deprecated modules which should not be used, separated by a comma
deprecated-modules=regsub,TERMIOS,Bastion,rexec
# Create a graph of every (i.e. internal and external) dependencies in the
# given file (report RP0402 must not be disabled)
import-graph=
# Create a graph of external dependencies in the given file (report RP0402 must
# not be disabled)
ext-import-graph=
# Create a graph of internal dependencies in the given file (report RP0402 must
# not be disabled)
int-import-graph=
# Force import order to recognize a module as part of the standard
# compatibility libraries.
known-standard-library=
# Force import order to recognize a module as part of a third party library.
known-third-party=enchant
# Couples of modules and preferred modules, separated by a comma.
preferred-modules=
[EXCEPTIONS]
# Exceptions that will emit a warning when being caught. Defaults to
# "Exception"
overgeneral-exceptions=builtins.Exception
[TYPING]
# Set to ``no`` if the app / library does **NOT** need to support runtime
# introspection of type annotations. If you use type annotations
# **exclusively** for type checking of an application, you're probably fine.
# For libraries, evaluate if some users what to access the type hints at
# runtime first, e.g., through ``typing.get_type_hints``. Applies to Python
# versions 3.7 - 3.9
runtime-typing = no
[DEPRECATED_BUILTINS]
# List of builtins function names that should not be used, separated by a comma
bad-functions=map,input
[REFACTORING]
# Maximum number of nested blocks for function / method body
max-nested-blocks=5
# Complete name of functions that never returns. When checking for
# inconsistent-return-statements if a never returning function is called then
# it will be considered as an explicit return statement and no message will be
# printed.
never-returning-functions=sys.exit,argparse.parse_error
[STRING]
# This flag controls whether inconsistent-quotes generates a warning when the
# character used as a quote delimiter is used inconsistently within a module.
check-quote-consistency=no
# This flag controls whether the implicit-str-concat should generate a warning
# on implicit string concatenation in sequences defined over several lines.
check-str-concat-over-line-jumps=no
[CODE_STYLE]
# Max line length for which to sill emit suggestions. Used to prevent optional
# suggestions which would get split by a code formatter (e.g., black). Will
# default to the setting for ``max-line-length``.
#max-line-length-suggestions=