1    	import inspect
2    	from unittest import TestCase
3    	
4    	from pcs.common import file_type_codes
5    	from pcs.common.fencing_topology import (
6    	    TARGET_TYPE_ATTRIBUTE,
7    	    TARGET_TYPE_NODE,
8    	    TARGET_TYPE_REGEXP,
9    	)
10   	from pcs.common.file import RawFileError
11   	from pcs.common.reports import const
12   	from pcs.common.reports import messages as reports
13   	from pcs.common.resource_agent.dto import ResourceAgentNameDto
14   	from pcs.common.resource_status import ResourceState
15   	from pcs.common.types import CibRuleExpressionType
16   	
17   	# pylint: disable=too-many-lines
18   	
19   	
20   	class AllClassesTested(TestCase):
21   	    def test_success(self):
22   	        self.maxDiff = None
23   	        message_classes = frozenset(
24   	            name
25   	            for name, member in inspect.getmembers(reports, inspect.isclass)
26   	            if issubclass(member, reports.ReportItemMessage)
27   	            and member
28   	            not in {reports.ReportItemMessage, reports.LegacyCommonMessage}
29   	        )
30   	        test_classes = frozenset(
31   	            name
32   	            for name, member in inspect.getmembers(
33   	                inspect.getmodule(self), inspect.isclass
34   	            )
35   	            if issubclass(member, NameBuildTest)
36   	        )
37   	        untested = sorted(message_classes - test_classes)
38   	        self.assertEqual(
39   	            untested,
40   	            [],
41   	            f"It seems {len(untested)} subclass(es) of 'ReportItemMessage' are "
42   	            "missing tests. Make sure the test classes have the same name as "
43   	            "the code classes.",
44   	        )
45   	
46   	
47   	class NameBuildTest(TestCase):
48   	    """
49   	    Base class for the testing of message building.
50   	    """
51   	
52   	    def assert_message_from_report(self, message, report):
53   	        self.maxDiff = None
54   	        self.assertEqual(message, report.message)
55   	
56   	
57   	class ResourceForConstraintIsMultiinstance(NameBuildTest):
58   	    def test_success(self):
59   	        self.assertEqual(
60   	            (
61   	                "resource1 is a bundle resource, you should use the "
62   	                "bundle id: parent1 when adding constraints"
63   	            ),
64   	            reports.ResourceForConstraintIsMultiinstance(
65   	                "resource1", "bundle", "parent1"
66   	            ).message,
67   	        )
68   	
69   	
70   	class DuplicateConstraintsExist(NameBuildTest):
71   	    def test_build_singular(self):
72   	        self.assert_message_from_report(
73   	            "Duplicate constraint already exists",
74   	            reports.DuplicateConstraintsExist(["c1"]),
75   	        )
76   	
77   	    def test_build_plural(self):
78   	        self.assert_message_from_report(
79   	            "Duplicate constraints already exist",
80   	            reports.DuplicateConstraintsExist(["c1", "c3", "c0"]),
81   	        )
82   	
83   	
84   	class EmptyResourceSet(NameBuildTest):
85   	    def test_success(self):
86   	        self.assert_message_from_report(
87   	            "Resource set is empty",
88   	            reports.EmptyResourceSet(),
89   	        )
90   	
91   	
92   	class EmptyResourceSetList(NameBuildTest):
93   	    def test_success(self):
94   	        self.assert_message_from_report(
95   	            "Resource set list is empty",
96   	            reports.EmptyResourceSetList(),
97   	        )
98   	
99   	
100  	class CannotSetOrderConstraintsForResourcesInTheSameGroup(NameBuildTest):
101  	    def test_success(self):
102  	        self.assert_message_from_report(
103  	            "Cannot create an order constraint for resources in the same group",
104  	            reports.CannotSetOrderConstraintsForResourcesInTheSameGroup(),
105  	        )
106  	
107  	
108  	class RequiredOptionsAreMissing(NameBuildTest):
109  	    def test_build_message_with_type(self):
110  	        self.assert_message_from_report(
111  	            "required TYPE option 'NAME' is missing",
112  	            reports.RequiredOptionsAreMissing(["NAME"], option_type="TYPE"),
113  	        )
114  	
115  	    def test_build_message_without_type(self):
116  	        self.assert_message_from_report(
117  	            "required option 'NAME' is missing",
118  	            reports.RequiredOptionsAreMissing(["NAME"]),
119  	        )
120  	
121  	    def test_build_message_with_multiple_names(self):
122  	        self.assert_message_from_report(
123  	            "required options 'ANOTHER', 'NAME' are missing",
124  	            reports.RequiredOptionsAreMissing(["NAME", "ANOTHER"]),
125  	        )
126  	
127  	
128  	class PrerequisiteOptionIsMissing(NameBuildTest):
129  	    def test_without_type(self):
130  	        self.assert_message_from_report(
131  	            "If option 'a' is specified, option 'b' must be specified as well",
132  	            reports.PrerequisiteOptionIsMissing("a", "b"),
133  	        )
134  	
135  	    def test_with_type(self):
136  	        self.assert_message_from_report(
137  	            "If some option 'a' is specified, "
138  	            "other option 'b' must be specified as well",
139  	            reports.PrerequisiteOptionIsMissing("a", "b", "some", "other"),
140  	        )
141  	
142  	
143  	class PrerequisiteOptionMustBeEnabledAsWell(NameBuildTest):
144  	    def test_without_type(self):
145  	        self.assert_message_from_report(
146  	            "If option 'a' is enabled, option 'b' must be enabled as well",
147  	            reports.PrerequisiteOptionMustBeEnabledAsWell("a", "b"),
148  	        )
149  	
150  	    def test_with_type(self):
151  	        self.assert_message_from_report(
152  	            "If some option 'a' is enabled, "
153  	            "other option 'b' must be enabled as well",
154  	            reports.PrerequisiteOptionMustBeEnabledAsWell(
155  	                "a", "b", "some", "other"
156  	            ),
157  	        )
158  	
159  	
160  	class PrerequisiteOptionMustBeDisabled(NameBuildTest):
161  	    def test_without_type(self):
162  	        self.assert_message_from_report(
163  	            "If option 'a' is enabled, option 'b' must be disabled",
164  	            reports.PrerequisiteOptionMustBeDisabled("a", "b"),
165  	        )
166  	
167  	    def test_with_type(self):
168  	        self.assert_message_from_report(
169  	            "If some option 'a' is enabled, other option 'b' must be disabled",
170  	            reports.PrerequisiteOptionMustBeDisabled("a", "b", "some", "other"),
171  	        )
172  	
173  	
174  	class PrerequisiteOptionMustNotBeSet(NameBuildTest):
175  	    def test_without_type(self):
176  	        self.assert_message_from_report(
177  	            "Cannot set option 'a' because option 'b' is already set",
178  	            reports.PrerequisiteOptionMustNotBeSet(
179  	                "a",
180  	                "b",
181  	            ),
182  	        )
183  	
184  	    def test_with_type(self):
185  	        self.assert_message_from_report(
186  	            "Cannot set some option 'a' because other option 'b' is "
187  	            "already set",
188  	            reports.PrerequisiteOptionMustNotBeSet(
189  	                "a",
190  	                "b",
191  	                option_type="some",
192  	                prerequisite_type="other",
193  	            ),
194  	        )
195  	
196  	
197  	class RequiredOptionOfAlternativesIsMissing(NameBuildTest):
198  	    def test_minimal(self):
199  	        self.assert_message_from_report(
200  	            "option 'aAa', 'bBb' or 'cCc' has to be specified",
201  	            reports.RequiredOptionOfAlternativesIsMissing(
202  	                ["aAa", "cCc", "bBb"]
203  	            ),
204  	        )
205  	
206  	    def test_with_type(self):
207  	        self.assert_message_from_report(
208  	            "test option 'aAa' has to be specified",
209  	            reports.RequiredOptionOfAlternativesIsMissing(
210  	                ["aAa"], option_type="test"
211  	            ),
212  	        )
213  	
214  	    def test_with_deprecated(self):
215  	        self.assert_message_from_report(
216  	            (
217  	                "option 'bBb', 'aAa' (deprecated) or 'cCc' (deprecated) has "
218  	                "to be specified"
219  	            ),
220  	            reports.RequiredOptionOfAlternativesIsMissing(
221  	                ["aAa", "cCc", "bBb"], deprecated_names=["cCc", "aAa"]
222  	            ),
223  	        )
224  	
225  	
226  	class InvalidOptions(NameBuildTest):
227  	    def test_build_message_with_type(self):
228  	        self.assert_message_from_report(
229  	            "invalid TYPE option 'NAME', allowed options are: 'FIRST', "
230  	            "'SECOND'",
231  	            reports.InvalidOptions(["NAME"], ["SECOND", "FIRST"], "TYPE"),
232  	        )
233  	
234  	    def test_build_message_without_type(self):
235  	        self.assert_message_from_report(
236  	            "invalid option 'NAME', allowed options are: 'FIRST', 'SECOND'",
237  	            reports.InvalidOptions(["NAME"], ["FIRST", "SECOND"], ""),
238  	        )
239  	
240  	    def test_build_message_with_multiple_names(self):
241  	        self.assert_message_from_report(
242  	            "invalid options: 'ANOTHER', 'NAME', allowed option is 'FIRST'",
243  	            reports.InvalidOptions(["NAME", "ANOTHER"], ["FIRST"], ""),
244  	        )
245  	
246  	    def test_pattern(self):
247  	        self.assert_message_from_report(
248  	            (
249  	                "invalid option 'NAME', allowed are options matching patterns: "
250  	                "'exec_<name>'"
251  	            ),
252  	            reports.InvalidOptions(["NAME"], [], "", ["exec_<name>"]),
253  	        )
254  	
255  	    def test_allowed_and_patterns(self):
256  	        self.assert_message_from_report(
257  	            (
258  	                "invalid option 'NAME', allowed option is 'FIRST' and options "
259  	                "matching patterns: 'exec_<name>'"
260  	            ),
261  	            reports.InvalidOptions(
262  	                ["NAME"], ["FIRST"], "", allowed_patterns=["exec_<name>"]
263  	            ),
264  	        )
265  	
266  	    def test_no_allowed_options(self):
267  	        self.assert_message_from_report(
268  	            "invalid options: 'ANOTHER', 'NAME', there are no options allowed",
269  	            reports.InvalidOptions(["NAME", "ANOTHER"], [], ""),
270  	        )
271  	
272  	
273  	class InvalidUserdefinedOptions(NameBuildTest):
274  	    def test_without_type(self):
275  	        self.assert_message_from_report(
276  	            (
277  	                "invalid option 'exec_NAME', options may contain "
278  	                "a-z A-Z 0-9 /_- characters only"
279  	            ),
280  	            reports.InvalidUserdefinedOptions(["exec_NAME"], "a-z A-Z 0-9 /_-"),
281  	        )
282  	
283  	    def test_with_type(self):
284  	        self.assert_message_from_report(
285  	            (
286  	                "invalid heuristics option 'exec_NAME', heuristics options may "
287  	                "contain a-z A-Z 0-9 /_- characters only"
288  	            ),
289  	            reports.InvalidUserdefinedOptions(
290  	                ["exec_NAME"], "a-z A-Z 0-9 /_-", "heuristics"
291  	            ),
292  	        )
293  	
294  	    def test_more_options(self):
295  	        self.assert_message_from_report(
296  	            (
297  	                "invalid TYPE options: 'ANOTHER', 'NAME', TYPE options may "
298  	                "contain a-z A-Z 0-9 /_- characters only"
299  	            ),
300  	            reports.InvalidUserdefinedOptions(
301  	                ["NAME", "ANOTHER"], "a-z A-Z 0-9 /_-", "TYPE"
302  	            ),
303  	        )
304  	
305  	
306  	class InvalidOptionType(NameBuildTest):
307  	    def test_allowed_string(self):
308  	        self.assert_message_from_report(
309  	            "specified option name is not valid, use allowed types",
310  	            reports.InvalidOptionType("option name", "allowed types"),
311  	        )
312  	
313  	    def test_allowed_list(self):
314  	        self.assert_message_from_report(
315  	            "specified option name is not valid, use 'allowed', 'types'",
316  	            reports.InvalidOptionType("option name", ["types", "allowed"]),
317  	        )
318  	
319  	
320  	class InvalidOptionValue(NameBuildTest):
321  	    def test_multiple_allowed_values(self):
322  	        self.assert_message_from_report(
323  	            "'VALUE' is not a valid NAME value, use 'FIRST', 'SECOND'",
324  	            reports.InvalidOptionValue("NAME", "VALUE", ["SECOND", "FIRST"]),
325  	        )
326  	
327  	    def test_textual_hint(self):
328  	        self.assert_message_from_report(
329  	            "'VALUE' is not a valid NAME value, use some hint",
330  	            reports.InvalidOptionValue("NAME", "VALUE", "some hint"),
331  	        )
332  	
333  	    def test_cannot_be_empty(self):
334  	        self.assert_message_from_report(
335  	            "NAME cannot be empty",
336  	            reports.InvalidOptionValue(
337  	                "NAME", "VALUE", allowed_values=None, cannot_be_empty=True
338  	            ),
339  	        )
340  	
341  	    def test_cannot_be_empty_with_hint(self):
342  	        self.assert_message_from_report(
343  	            "NAME cannot be empty, use 'FIRST', 'SECOND'",
344  	            reports.InvalidOptionValue(
345  	                "NAME", "VALUE", ["SECOND", "FIRST"], cannot_be_empty=True
346  	            ),
347  	        )
348  	
349  	    def test_forbidden_characters(self):
350  	        self.assert_message_from_report(
351  	            r"NAME cannot contain }{\r\n characters",
352  	            reports.InvalidOptionValue(
353  	                "NAME",
354  	                "VALUE",
355  	                allowed_values=None,
356  	                forbidden_characters="}{\\r\\n",
357  	            ),
358  	        )
359  	
360  	    def test_forbidden_characters_with_hint(self):
361  	        self.assert_message_from_report(
362  	            r"NAME cannot contain }{\r\n characters, use 'FIRST', 'SECOND'",
363  	            reports.InvalidOptionValue(
364  	                "NAME",
365  	                "VALUE",
366  	                ["SECOND", "FIRST"],
367  	                forbidden_characters="}{\\r\\n",
368  	            ),
369  	        )
370  	
371  	    def test_cannot_be_empty_and_forbidden_characters(self):
372  	        self.assert_message_from_report(
373  	            "NAME cannot be empty, use 'FIRST', 'SECOND'",
374  	            reports.InvalidOptionValue(
375  	                "NAME", "VALUE", ["SECOND", "FIRST"], True
376  	            ),
377  	        )
378  	
379  	
380  	class DeprecatedOption(NameBuildTest):
381  	    def test_no_desc_hint_array(self):
382  	        self.assert_message_from_report(
383  	            (
384  	                "option 'option name' is deprecated and might be removed in a "
385  	                "future release, therefore it should not be used, use 'new_a', "
386  	                "'new_b' instead"
387  	            ),
388  	            reports.DeprecatedOption("option name", ["new_b", "new_a"], ""),
389  	        )
390  	
391  	    def test_desc_hint_string(self):
392  	        self.assert_message_from_report(
393  	            (
394  	                "option type option 'option name' is deprecated and might be "
395  	                "removed in a future release, therefore it should not be used, "
396  	                "use 'new option' instead"
397  	            ),
398  	            reports.DeprecatedOption(
399  	                "option name", ["new option"], "option type"
400  	            ),
401  	        )
402  	
403  	    def test_empty_hint(self):
404  	        self.assert_message_from_report(
405  	            (
406  	                "option 'option name' is deprecated and might be removed in a "
407  	                "future release, therefore it should not be used"
408  	            ),
409  	            reports.DeprecatedOption("option name", [], ""),
410  	        )
411  	
412  	
413  	class DeprecatedOptionValue(NameBuildTest):
414  	    def test_replaced_by(self):
415  	        self.assert_message_from_report(
416  	            (
417  	                "Value 'deprecatedValue' of option optionA is deprecated and "
418  	                "might be removed in a future release, therefore it should not "
419  	                "be used, use 'newValue' value instead"
420  	            ),
421  	            reports.DeprecatedOptionValue(
422  	                "optionA", "deprecatedValue", "newValue"
423  	            ),
424  	        )
425  	
426  	    def test_no_replacement(self):
427  	        self.assert_message_from_report(
428  	            (
429  	                "Value 'deprecatedValue' of option optionA is deprecated and "
430  	                "might be removed in a future release, therefore it should not "
431  	                "be used"
432  	            ),
433  	            reports.DeprecatedOptionValue("optionA", "deprecatedValue"),
434  	        )
435  	
436  	
437  	class MutuallyExclusiveOptions(NameBuildTest):
438  	    def test_build_message(self):
439  	        self.assert_message_from_report(
440  	            "Only one of some options 'a' and 'b' can be used",
441  	            reports.MutuallyExclusiveOptions(["b", "a"], "some"),
442  	        )
443  	
444  	
445  	class InvalidCibContent(NameBuildTest):
446  	    def test_message_can_be_more_verbose(self):
447  	        report = "no verbose\noutput\n"
448  	        self.assert_message_from_report(
449  	            "invalid cib:\n{0}".format(report),
450  	            reports.InvalidCibContent(report, True),
451  	        )
452  	
453  	    def test_message_cannot_be_more_verbose(self):
454  	        report = "some verbose\noutput"
455  	        self.assert_message_from_report(
456  	            "invalid cib:\n{0}".format(report),
457  	            reports.InvalidCibContent(report, False),
458  	        )
459  	
460  	
461  	class InvalidIdIsEmpty(NameBuildTest):
462  	    def test_all(self):
463  	        self.assert_message_from_report(
464  	            "description cannot be empty",
465  	            reports.InvalidIdIsEmpty("description"),
466  	        )
467  	
468  	
469  	class InvalidIdBadChar(NameBuildTest):
470  	    def test_build_message_with_first_char_invalid(self):
471  	        self.assert_message_from_report(
472  	            (
473  	                "invalid ID_DESCRIPTION 'ID', 'INVALID_CHARACTER' is not a"
474  	                " valid first character for a ID_DESCRIPTION"
475  	            ),
476  	            reports.InvalidIdBadChar(
477  	                "ID", "ID_DESCRIPTION", "INVALID_CHARACTER", is_first_char=True
478  	            ),
479  	        )
480  	
481  	    def test_build_message_with_non_first_char_invalid(self):
482  	        self.assert_message_from_report(
483  	            (
484  	                "invalid ID_DESCRIPTION 'ID', 'INVALID_CHARACTER' is not a"
485  	                " valid character for a ID_DESCRIPTION"
486  	            ),
487  	            reports.InvalidIdBadChar(
488  	                "ID", "ID_DESCRIPTION", "INVALID_CHARACTER", is_first_char=False
489  	            ),
490  	        )
491  	
492  	
493  	class InvalidIdType(NameBuildTest):
494  	    def test_success(self):
495  	        self.assert_message_from_report(
496  	            (
497  	                "'entered' is not a valid type of ID specification, "
498  	                "use 'expected1', 'expected2'"
499  	            ),
500  	            reports.InvalidIdType("entered", ["expected1", "expected2"]),
501  	        )
502  	
503  	
504  	class InvalidTimeoutValue(NameBuildTest):
505  	    def test_all(self):
506  	        self.assert_message_from_report(
507  	            "'24h' is not a valid number of seconds to wait",
508  	            reports.InvalidTimeoutValue("24h"),
509  	        )
510  	
511  	
512  	class InvalidScore(NameBuildTest):
513  	    def test_all(self):
514  	        self.assert_message_from_report(
515  	            "invalid score '1M', use integer or INFINITY or -INFINITY",
516  	            reports.InvalidScore("1M"),
517  	        )
518  	
519  	
520  	class RunExternalProcessStarted(NameBuildTest):
521  	    def test_build_message_minimal(self):
522  	        self.assert_message_from_report(
523  	            "Running: COMMAND\nEnvironment:\n",
524  	            reports.RunExternalProcessStarted("COMMAND", "", {}),
525  	        )
526  	
527  	    def test_build_message_with_stdin(self):
528  	        self.assert_message_from_report(
529  	            (
530  	                "Running: COMMAND\nEnvironment:\n"
531  	                "--Debug Input Start--\n"
532  	                "STDIN\n"
533  	                "--Debug Input End--\n"
534  	            ),
535  	            reports.RunExternalProcessStarted("COMMAND", "STDIN", {}),
536  	        )
537  	
538  	    def test_build_message_with_env(self):
539  	        self.assert_message_from_report(
540  	            ("Running: COMMAND\nEnvironment:\n  env_a=A\n  env_b=B\n"),
541  	            reports.RunExternalProcessStarted(
542  	                "COMMAND",
543  	                "",
544  	                {
545  	                    "env_a": "A",
546  	                    "env_b": "B",
547  	                },
548  	            ),
549  	        )
550  	
551  	    def test_build_message_maximal(self):
552  	        self.assert_message_from_report(
553  	            (
554  	                "Running: COMMAND\nEnvironment:\n"
555  	                "  env_a=A\n"
556  	                "  env_b=B\n"
557  	                "--Debug Input Start--\n"
558  	                "STDIN\n"
559  	                "--Debug Input End--\n"
560  	            ),
561  	            reports.RunExternalProcessStarted(
562  	                "COMMAND",
563  	                "STDIN",
564  	                {
565  	                    "env_a": "A",
566  	                    "env_b": "B",
567  	                },
568  	            ),
569  	        )
570  	
571  	    def test_insidious_environment(self):
572  	        self.assert_message_from_report(
573  	            (
574  	                "Running: COMMAND\nEnvironment:\n"
575  	                "  test=a:{green},b:{red}\n"
576  	                "--Debug Input Start--\n"
577  	                "STDIN\n"
578  	                "--Debug Input End--\n"
579  	            ),
580  	            reports.RunExternalProcessStarted(
581  	                "COMMAND",
582  	                "STDIN",
583  	                {
584  	                    "test": "a:{green},b:{red}",
585  	                },
586  	            ),
587  	        )
588  	
589  	
590  	class RunExternalProcessFinished(NameBuildTest):
591  	    def test_all(self):
592  	        self.assert_message_from_report(
593  	            (
594  	                "Finished running: com-mand\n"
595  	                "Return value: 0\n"
596  	                "--Debug Stdout Start--\n"
597  	                "STDOUT\n"
598  	                "--Debug Stdout End--\n"
599  	                "--Debug Stderr Start--\n"
600  	                "STDERR\n"
601  	                "--Debug Stderr End--\n"
602  	            ),
603  	            reports.RunExternalProcessFinished(
604  	                "com-mand", 0, "STDOUT", "STDERR"
605  	            ),
606  	        )
607  	
608  	
609  	class RunExternalProcessError(NameBuildTest):
610  	    def test_all(self):
611  	        self.assert_message_from_report(
612  	            "unable to run command com-mand: reason",
613  	            reports.RunExternalProcessError("com-mand", "reason"),
614  	        )
615  	
616  	
617  	class NoActionNecessary(NameBuildTest):
618  	    def test_all(self):
619  	        self.assert_message_from_report(
620  	            "No action necessary, requested change would have no effect",
621  	            reports.NoActionNecessary(),
622  	        )
623  	
624  	
625  	class NodeCommunicationStarted(NameBuildTest):
626  	    def test_build_message_with_data(self):
627  	        self.assert_message_from_report(
628  	            (
629  	                "Sending HTTP Request to: TARGET\n"
630  	                "--Debug Input Start--\n"
631  	                "DATA\n"
632  	                "--Debug Input End--\n"
633  	            ),
634  	            reports.NodeCommunicationStarted("TARGET", "DATA"),
635  	        )
636  	
637  	    def test_build_message_without_data(self):
638  	        self.assert_message_from_report(
639  	            "Sending HTTP Request to: TARGET",
640  	            reports.NodeCommunicationStarted("TARGET", ""),
641  	        )
642  	
643  	
644  	class NodeCommunicationFinished(NameBuildTest):
645  	    def test_all(self):
646  	        self.assert_message_from_report(
647  	            (
648  	                "Finished calling: node1\n"
649  	                "Response Code: 0\n"
650  	                "--Debug Response Start--\n"
651  	                "DATA\n"
652  	                "--Debug Response End--\n"
653  	            ),
654  	            reports.NodeCommunicationFinished("node1", 0, "DATA"),
655  	        )
656  	
657  	
658  	class NodeCommunicationDebugInfo(NameBuildTest):
659  	    def test_all(self):
660  	        self.assert_message_from_report(
661  	            (
662  	                "Communication debug info for calling: node1\n"
663  	                "--Debug Communication Info Start--\n"
664  	                "DATA\n"
665  	                "--Debug Communication Info End--\n"
666  	            ),
667  	            reports.NodeCommunicationDebugInfo("node1", "DATA"),
668  	        )
669  	
670  	
671  	class NodeCommunicationNotConnected(NameBuildTest):
672  	    def test_all(self):
673  	        self.assert_message_from_report(
674  	            "Unable to connect to node2 (this is reason)",
675  	            reports.NodeCommunicationNotConnected("node2", "this is reason"),
676  	        )
677  	
678  	
679  	class NodeCommunicationNoMoreAddresses(NameBuildTest):
680  	    def test_success(self):
681  	        self.assert_message_from_report(
682  	            "Unable to connect to 'node_name' via any of its addresses",
683  	            reports.NodeCommunicationNoMoreAddresses(
684  	                "node_name",
685  	                "my/request",
686  	            ),
687  	        )
688  	
689  	
690  	class NodeCommunicationErrorNotAuthorized(NameBuildTest):
691  	    def test_success(self):
692  	        self.assert_message_from_report(
693  	            "Unable to authenticate to node1 (some error)",
694  	            reports.NodeCommunicationErrorNotAuthorized(
695  	                "node1", "some-command", "some error"
696  	            ),
697  	        )
698  	
699  	
700  	class NodeCommunicationErrorPermissionDenied(NameBuildTest):
701  	    def test_all(self):
702  	        self.assert_message_from_report(
703  	            "node3: Permission denied (reason)",
704  	            reports.NodeCommunicationErrorPermissionDenied(
705  	                "node3", "com-mand", "reason"
706  	            ),
707  	        )
708  	
709  	
710  	class NodeCommunicationErrorUnsupportedCommand(NameBuildTest):
711  	    def test_all(self):
712  	        self.assert_message_from_report(
713  	            "node1: Unsupported command (reason), try upgrading pcsd",
714  	            reports.NodeCommunicationErrorUnsupportedCommand(
715  	                "node1", "com-mand", "reason"
716  	            ),
717  	        )
718  	
719  	
720  	class NodeCommunicationCommandUnsuccessful(NameBuildTest):
721  	    def test_all(self):
722  	        self.assert_message_from_report(
723  	            "node1: reason",
724  	            reports.NodeCommunicationCommandUnsuccessful(
725  	                "node1", "com-mand", "reason"
726  	            ),
727  	        )
728  	
729  	
730  	class NodeCommunicationError(NameBuildTest):
731  	    def test_all(self):
732  	        self.assert_message_from_report(
733  	            "Error connecting to node1 (reason)",
734  	            reports.NodeCommunicationError("node1", "com-mand", "reason"),
735  	        )
736  	
737  	
738  	class NodeCommunicationErrorUnableToConnect(NameBuildTest):
739  	    def test_all(self):
740  	        self.assert_message_from_report(
741  	            "Unable to connect to node1 (reason)",
742  	            reports.NodeCommunicationErrorUnableToConnect(
743  	                "node1", "com-mand", "reason"
744  	            ),
745  	        )
746  	
747  	
748  	class NodeCommunicationErrorTimedOut(NameBuildTest):
749  	    def test_success(self):
750  	        self.assert_message_from_report(
751  	            (
752  	                "node-1: Connection timeout (Connection timed out after 60049 "
753  	                "milliseconds)"
754  	            ),
755  	            reports.NodeCommunicationErrorTimedOut(
756  	                "node-1",
757  	                "/remote/command",
758  	                "Connection timed out after 60049 milliseconds",
759  	            ),
760  	        )
761  	
762  	
763  	class NodeCommunicationProxyIsSet(NameBuildTest):
764  	    def test_minimal(self):
765  	        self.assert_message_from_report(
766  	            "Proxy is set in environment variables, try disabling it",
767  	            reports.NodeCommunicationProxyIsSet(),
768  	        )
769  	
770  	    def test_with_node(self):
771  	        self.assert_message_from_report(
772  	            "Proxy is set in environment variables, try disabling it",
773  	            reports.NodeCommunicationProxyIsSet(node="node1"),
774  	        )
775  	
776  	    def test_with_address(self):
777  	        self.assert_message_from_report(
778  	            "Proxy is set in environment variables, try disabling it",
779  	            reports.NodeCommunicationProxyIsSet(address="aaa"),
780  	        )
781  	
782  	    def test_all(self):
783  	        self.assert_message_from_report(
784  	            "Proxy is set in environment variables, try disabling it",
785  	            reports.NodeCommunicationProxyIsSet(node="node1", address="aaa"),
786  	        )
787  	
788  	
789  	class NodeCommunicationRetrying(NameBuildTest):
790  	    def test_success(self):
791  	        self.assert_message_from_report(
792  	            (
793  	                "Unable to connect to 'node_name' via address 'failed.address' "
794  	                "and port '2224'. Retrying request 'my/request' via address "
795  	                "'next.address' and port '2225'"
796  	            ),
797  	            reports.NodeCommunicationRetrying(
798  	                "node_name",
799  	                "failed.address",
800  	                "2224",
801  	                "next.address",
802  	                "2225",
803  	                "my/request",
804  	            ),
805  	        )
806  	
807  	
808  	class DefaultsCanBeOverridden(NameBuildTest):
809  	    def test_message(self):
810  	        self.assert_message_from_report(
811  	            (
812  	                "Defaults do not apply to resources which override them with "
813  	                "their own defined values"
814  	            ),
815  	            reports.DefaultsCanBeOverridden(),
816  	        )
817  	
818  	
819  	class CorosyncAuthkeyWrongLength(NameBuildTest):
820  	    def test_at_most_allowed_singular_provided_plural(self):
821  	        self.assert_message_from_report(
822  	            (
823  	                "At least 0 and at most 1 byte key must be provided for "
824  	                "a corosync authkey, 2 bytes key provided"
825  	            ),
826  	            reports.CorosyncAuthkeyWrongLength(2, 0, 1),
827  	        )
828  	
829  	    def test_at_most_allowed_plural_provided_singular(self):
830  	        self.assert_message_from_report(
831  	            (
832  	                "At least 2 and at most 3 bytes key must be provided for "
833  	                "a corosync authkey, 1 byte key provided"
834  	            ),
835  	            reports.CorosyncAuthkeyWrongLength(1, 2, 3),
836  	        )
837  	
838  	    def test_exactly_allowed_singular_provided_plural(self):
839  	        self.assert_message_from_report(
840  	            (
841  	                "1 byte key must be provided for a corosync authkey, 2 bytes "
842  	                "key provided"
843  	            ),
844  	            reports.CorosyncAuthkeyWrongLength(2, 1, 1),
845  	        )
846  	
847  	    def test_exactly_allowed_plural_provided_singular(self):
848  	        self.assert_message_from_report(
849  	            (
850  	                "2 bytes key must be provided for a corosync authkey, 1 byte "
851  	                "key provided"
852  	            ),
853  	            reports.CorosyncAuthkeyWrongLength(1, 2, 2),
854  	        )
855  	
856  	
857  	class CorosyncConfigDistributionStarted(NameBuildTest):
858  	    def test_all(self):
859  	        self.assert_message_from_report(
860  	            "Sending updated corosync.conf to nodes...",
861  	            reports.CorosyncConfigDistributionStarted(),
862  	        )
863  	
864  	
865  	# TODO: consider generalizing
866  	class CorosyncConfigAcceptedByNode(NameBuildTest):
867  	    def test_all(self):
868  	        self.assert_message_from_report(
869  	            "node1: Succeeded", reports.CorosyncConfigAcceptedByNode("node1")
870  	        )
871  	
872  	
873  	class CorosyncConfigDistributionNodeError(NameBuildTest):
874  	    def test_all(self):
875  	        self.assert_message_from_report(
876  	            "node1: Unable to set corosync config",
877  	            reports.CorosyncConfigDistributionNodeError("node1"),
878  	        )
879  	
880  	
881  	class CorosyncNotRunningCheckStarted(NameBuildTest):
882  	    def test_all(self):
883  	        self.assert_message_from_report(
884  	            "Checking that corosync is not running on nodes...",
885  	            reports.CorosyncNotRunningCheckStarted(),
886  	        )
887  	
888  	
889  	class CorosyncNotRunningCheckFinishedRunning(NameBuildTest):
890  	    def test_one_node(self):
891  	        self.assert_message_from_report(
892  	            (
893  	                "Corosync is running on node 'node1'. Requested change can "
894  	                "only be made if the cluster is stopped. In order to proceed, "
895  	                "stop the cluster."
896  	            ),
897  	            reports.CorosyncNotRunningCheckFinishedRunning(["node1"]),
898  	        )
899  	
900  	    def test_more_nodes(self):
901  	        self.assert_message_from_report(
902  	            (
903  	                "Corosync is running on nodes 'node1', 'node2', 'node3'. "
904  	                "Requested change can only be made if the cluster is stopped. "
905  	                "In order to proceed, stop the cluster."
906  	            ),
907  	            reports.CorosyncNotRunningCheckFinishedRunning(
908  	                ["node2", "node1", "node3"]
909  	            ),
910  	        )
911  	
912  	
913  	class CorosyncNotRunningCheckNodeError(NameBuildTest):
914  	    def test_all(self):
915  	        self.assert_message_from_report(
916  	            "Unable to check if corosync is not running on node 'node1'",
917  	            reports.CorosyncNotRunningCheckNodeError("node1"),
918  	        )
919  	
920  	
921  	class CorosyncNotRunningCheckNodeStopped(NameBuildTest):
922  	    def test_all(self):
923  	        self.assert_message_from_report(
924  	            "Corosync is not running on node 'node2'",
925  	            reports.CorosyncNotRunningCheckNodeStopped("node2"),
926  	        )
927  	
928  	
929  	class CorosyncNotRunningCheckNodeRunning(NameBuildTest):
930  	    def test_all(self):
931  	        self.assert_message_from_report(
932  	            "Corosync is running on node 'node3'",
933  	            reports.CorosyncNotRunningCheckNodeRunning("node3"),
934  	        )
935  	
936  	
937  	class CorosyncQuorumGetStatusError(NameBuildTest):
938  	    def test_success(self):
939  	        self.assert_message_from_report(
940  	            "Unable to get quorum status: a reason",
941  	            reports.CorosyncQuorumGetStatusError("a reason"),
942  	        )
943  	
944  	    def test_success_with_node(self):
945  	        self.assert_message_from_report(
946  	            "node1: Unable to get quorum status: a reason",
947  	            reports.CorosyncQuorumGetStatusError("a reason", "node1"),
948  	        )
949  	
950  	
951  	class CorosyncQuorumHeuristicsEnabledWithNoExec(NameBuildTest):
952  	    def test_message(self):
953  	        self.assert_message_from_report(
954  	            (
955  	                "No exec_NAME options are specified, so heuristics are "
956  	                "effectively disabled"
957  	            ),
958  	            reports.CorosyncQuorumHeuristicsEnabledWithNoExec(),
959  	        )
960  	
961  	
962  	class CorosyncQuorumSetExpectedVotesError(NameBuildTest):
963  	    def test_all(self):
964  	        self.assert_message_from_report(
965  	            "Unable to set expected votes: reason",
966  	            reports.CorosyncQuorumSetExpectedVotesError("reason"),
967  	        )
968  	
969  	
970  	class CorosyncConfigReloaded(NameBuildTest):
971  	    def test_with_node(self):
972  	        self.assert_message_from_report(
973  	            "node1: Corosync configuration reloaded",
974  	            reports.CorosyncConfigReloaded("node1"),
975  	        )
976  	
977  	    def test_without_node(self):
978  	        self.assert_message_from_report(
979  	            "Corosync configuration reloaded",
980  	            reports.CorosyncConfigReloaded(),
981  	        )
982  	
983  	
984  	class CorosyncConfigReloadError(NameBuildTest):
985  	    def test_with_node(self):
986  	        self.assert_message_from_report(
987  	            "node1: Unable to reload corosync configuration: a reason",
988  	            reports.CorosyncConfigReloadError("a reason", "node1"),
989  	        )
990  	
991  	    def test_without_node(self):
992  	        self.assert_message_from_report(
993  	            "Unable to reload corosync configuration: different reason",
994  	            reports.CorosyncConfigReloadError("different reason"),
995  	        )
996  	
997  	
998  	class CorosyncConfigReloadNotPossible(NameBuildTest):
999  	    def test_success(self):
1000 	        self.assert_message_from_report(
1001 	            (
1002 	                "node1: Corosync is not running, therefore reload of the "
1003 	                "corosync configuration is not possible"
1004 	            ),
1005 	            reports.CorosyncConfigReloadNotPossible("node1"),
1006 	        )
1007 	
1008 	
1009 	class CorosyncConfigInvalidPreventsClusterJoin(NameBuildTest):
1010 	    def test_success(self):
1011 	        self.assert_message_from_report(
1012 	            (
1013 	                "One or more nodes failed to reload the Corosync configuration "
1014 	                "and are currently running with the previous configuration. If "
1015 	                "these nodes are restarted or fenced, they will fail to rejoin "
1016 	                "the cluster. Update the configuration and fix the issues as "
1017 	                "soon as possible."
1018 	            ),
1019 	            reports.CorosyncConfigInvalidPreventsClusterJoin(),
1020 	        )
1021 	
1022 	
1023 	class CorosyncConfigUnsupportedTransport(NameBuildTest):
1024 	    def test_success(self):
1025 	        self.assert_message_from_report(
1026 	            (
1027 	                "Transport 'netk' currently configured in corosync.conf is "
1028 	                "unsupported. Supported transport types are: 'knet', 'udp', "
1029 	                "'udpu'"
1030 	            ),
1031 	            reports.CorosyncConfigUnsupportedTransport(
1032 	                "netk", ["udp", "knet", "udpu"]
1033 	            ),
1034 	        )
1035 	
1036 	
1037 	class ParseErrorCorosyncConfMissingClosingBrace(NameBuildTest):
1038 	    def test_all(self):
1039 	        self.assert_message_from_report(
1040 	            "Unable to parse corosync config: missing closing brace",
1041 	            reports.ParseErrorCorosyncConfMissingClosingBrace(),
1042 	        )
1043 	
1044 	
1045 	class ParseErrorCorosyncConfUnexpectedClosingBrace(NameBuildTest):
1046 	    def test_all(self):
1047 	        self.assert_message_from_report(
1048 	            "Unable to parse corosync config: unexpected closing brace",
1049 	            reports.ParseErrorCorosyncConfUnexpectedClosingBrace(),
1050 	        )
1051 	
1052 	
1053 	class ParseErrorCorosyncConfMissingSectionNameBeforeOpeningBrace(NameBuildTest):
1054 	    def test_all(self):
1055 	        self.assert_message_from_report(
1056 	            "Unable to parse corosync config: missing a section name before {",
1057 	            reports.ParseErrorCorosyncConfMissingSectionNameBeforeOpeningBrace(),
1058 	        )
1059 	
1060 	
1061 	class ParseErrorCorosyncConfExtraCharactersAfterOpeningBrace(NameBuildTest):
1062 	    def test_all(self):
1063 	        self.assert_message_from_report(
1064 	            "Unable to parse corosync config: extra characters after {",
1065 	            reports.ParseErrorCorosyncConfExtraCharactersAfterOpeningBrace(),
1066 	        )
1067 	
1068 	
1069 	class ParseErrorCorosyncConfExtraCharactersBeforeOrAfterClosingBrace(
1070 	    NameBuildTest
1071 	):
1072 	    def test_all(self):
1073 	        self.assert_message_from_report(
1074 	            (
1075 	                "Unable to parse corosync config: extra characters before "
1076 	                "or after }"
1077 	            ),
1078 	            reports.ParseErrorCorosyncConfExtraCharactersBeforeOrAfterClosingBrace(),
1079 	        )
1080 	
1081 	
1082 	class ParseErrorCorosyncConfLineIsNotSectionNorKeyValue(NameBuildTest):
1083 	    def test_all(self):
1084 	        self.assert_message_from_report(
1085 	            "Unable to parse corosync config: a line is not opening or closing "
1086 	            "a section or key: value",
1087 	            reports.ParseErrorCorosyncConfLineIsNotSectionNorKeyValue(),
1088 	        )
1089 	
1090 	
1091 	class ParseErrorCorosyncConf(NameBuildTest):
1092 	    def test_all(self):
1093 	        self.assert_message_from_report(
1094 	            "Unable to parse corosync config", reports.ParseErrorCorosyncConf()
1095 	        )
1096 	
1097 	
1098 	class CorosyncConfigCannotSaveInvalidNamesValues(NameBuildTest):
1099 	    def test_empty(self):
1100 	        self.assert_message_from_report(
1101 	            "Cannot save corosync.conf containing invalid section names, "
1102 	            "option names or option values",
1103 	            reports.CorosyncConfigCannotSaveInvalidNamesValues([], [], []),
1104 	        )
1105 	
1106 	    def test_one_section(self):
1107 	        self.assert_message_from_report(
1108 	            "Cannot save corosync.conf containing "
1109 	            "invalid section name(s): 'SECTION'",
1110 	            reports.CorosyncConfigCannotSaveInvalidNamesValues(
1111 	                ["SECTION"], [], []
1112 	            ),
1113 	        )
1114 	
1115 	    def test_more_sections(self):
1116 	        self.assert_message_from_report(
1117 	            "Cannot save corosync.conf containing "
1118 	            "invalid section name(s): 'SECTION1', 'SECTION2'",
1119 	            reports.CorosyncConfigCannotSaveInvalidNamesValues(
1120 	                ["SECTION1", "SECTION2"], [], []
1121 	            ),
1122 	        )
1123 	
1124 	    def test_one_attr_name(self):
1125 	        self.assert_message_from_report(
1126 	            "Cannot save corosync.conf containing "
1127 	            "invalid option name(s): 'ATTR'",
1128 	            reports.CorosyncConfigCannotSaveInvalidNamesValues(
1129 	                [], ["ATTR"], []
1130 	            ),
1131 	        )
1132 	
1133 	    def test_more_attr_names(self):
1134 	        self.assert_message_from_report(
1135 	            "Cannot save corosync.conf containing "
1136 	            "invalid option name(s): 'ATTR1', 'ATTR2'",
1137 	            reports.CorosyncConfigCannotSaveInvalidNamesValues(
1138 	                [], ["ATTR1", "ATTR2"], []
1139 	            ),
1140 	        )
1141 	
1142 	    def test_one_attr_value(self):
1143 	        self.assert_message_from_report(
1144 	            "Cannot save corosync.conf containing "
1145 	            "invalid option value(s): 'VALUE' (option 'ATTR')",
1146 	            reports.CorosyncConfigCannotSaveInvalidNamesValues(
1147 	                [], [], [("ATTR", "VALUE")]
1148 	            ),
1149 	        )
1150 	
1151 	    def test_more_attr_values(self):
1152 	        self.assert_message_from_report(
1153 	            "Cannot save corosync.conf containing "
1154 	            "invalid option value(s): 'VALUE1' (option 'ATTR1'), "
1155 	            "'VALUE2' (option 'ATTR2')",
1156 	            reports.CorosyncConfigCannotSaveInvalidNamesValues(
1157 	                [], [], [("ATTR1", "VALUE1"), ("ATTR2", "VALUE2")]
1158 	            ),
1159 	        )
1160 	
1161 	    def test_all(self):
1162 	        self.assert_message_from_report(
1163 	            "Cannot save corosync.conf containing "
1164 	            "invalid section name(s): 'SECTION1', 'SECTION2'; "
1165 	            "invalid option name(s): 'ATTR1', 'ATTR2'; "
1166 	            "invalid option value(s): 'VALUE3' (option 'ATTR3'), "
1167 	            "'VALUE4' (option 'ATTR4')",
1168 	            reports.CorosyncConfigCannotSaveInvalidNamesValues(
1169 	                ["SECTION1", "SECTION2"],
1170 	                ["ATTR1", "ATTR2"],
1171 	                [("ATTR3", "VALUE3"), ("ATTR4", "VALUE4")],
1172 	            ),
1173 	        )
1174 	
1175 	
1176 	class CorosyncConfigMissingNamesOfNodes(NameBuildTest):
1177 	    def test_non_fatal(self):
1178 	        self.assert_message_from_report(
1179 	            "Some nodes are missing names in corosync.conf, "
1180 	            "those nodes were omitted. "
1181 	            "Edit corosync.conf and make sure all nodes have their name set.",
1182 	            reports.CorosyncConfigMissingNamesOfNodes(),
1183 	        )
1184 	
1185 	    def test_fatal(self):
1186 	        self.assert_message_from_report(
1187 	            "Some nodes are missing names in corosync.conf, "
1188 	            "unable to continue. "
1189 	            "Edit corosync.conf and make sure all nodes have their name set.",
1190 	            reports.CorosyncConfigMissingNamesOfNodes(fatal=True),
1191 	        )
1192 	
1193 	
1194 	class CorosyncConfigMissingIdsOfNodes(NameBuildTest):
1195 	    def test_success(self):
1196 	        self.assert_message_from_report(
1197 	            "Some nodes are missing IDs in corosync.conf. "
1198 	            "Edit corosync.conf and make sure all nodes have their nodeid set.",
1199 	            reports.CorosyncConfigMissingIdsOfNodes(),
1200 	        )
1201 	
1202 	
1203 	class CorosyncConfigNoNodesDefined(NameBuildTest):
1204 	    def test_success(self):
1205 	        self.assert_message_from_report(
1206 	            "No nodes found in corosync.conf",
1207 	            reports.CorosyncConfigNoNodesDefined(),
1208 	        )
1209 	
1210 	
1211 	class CorosyncOptionsIncompatibleWithQdevice(NameBuildTest):
1212 	    def test_single_option(self):
1213 	        self.assert_message_from_report(
1214 	            "These options cannot be set when the cluster uses a quorum "
1215 	            "device: 'option1'",
1216 	            reports.CorosyncOptionsIncompatibleWithQdevice(["option1"]),
1217 	        )
1218 	
1219 	    def test_multiple_options(self):
1220 	        self.assert_message_from_report(
1221 	            "These options cannot be set when the cluster uses a quorum "
1222 	            "device: 'option1', 'option2', 'option3'",
1223 	            reports.CorosyncOptionsIncompatibleWithQdevice(
1224 	                ["option3", "option1", "option2"]
1225 	            ),
1226 	        )
1227 	
1228 	
1229 	class CorosyncClusterNameInvalidForGfs2(NameBuildTest):
1230 	    def test_success(self):
1231 	        self.assert_message_from_report(
1232 	            "Chosen cluster name 'cluster name' will prevent mounting GFS2 "
1233 	            "volumes in the cluster, use at most 16 of a-z A-Z characters; "
1234 	            "you may safely override this if you do not intend to use GFS2",
1235 	            reports.CorosyncClusterNameInvalidForGfs2(
1236 	                cluster_name="cluster name",
1237 	                max_length=16,
1238 	                allowed_characters="a-z A-Z",
1239 	            ),
1240 	        )
1241 	
1242 	
1243 	class CorosyncBadNodeAddressesCount(NameBuildTest):
1244 	    def test_no_node_info(self):
1245 	        self.assert_message_from_report(
1246 	            "At least 1 and at most 4 addresses must be specified for a node, "
1247 	            "5 addresses specified",
1248 	            reports.CorosyncBadNodeAddressesCount(5, 1, 4),
1249 	        )
1250 	
1251 	    def test_node_name(self):
1252 	        self.assert_message_from_report(
1253 	            "At least 1 and at most 4 addresses must be specified for a node, "
1254 	            "5 addresses specified for node 'node1'",
1255 	            reports.CorosyncBadNodeAddressesCount(5, 1, 4, "node1"),
1256 	        )
1257 	
1258 	    def test_node_id(self):
1259 	        self.assert_message_from_report(
1260 	            "At least 1 and at most 4 addresses must be specified for a node, "
1261 	            "5 addresses specified for node '2'",
1262 	            reports.CorosyncBadNodeAddressesCount(5, 1, 4, node_index=2),
1263 	        )
1264 	
1265 	    def test_node_name_and_id(self):
1266 	        self.assert_message_from_report(
1267 	            "At least 1 and at most 4 addresses must be specified for a node, "
1268 	            "5 addresses specified for node 'node2'",
1269 	            reports.CorosyncBadNodeAddressesCount(5, 1, 4, "node2", 2),
1270 	        )
1271 	
1272 	    def test_one_address_allowed(self):
1273 	        self.assert_message_from_report(
1274 	            "At least 0 and at most 1 address must be specified for a node, "
1275 	            "2 addresses specified for node 'node2'",
1276 	            reports.CorosyncBadNodeAddressesCount(2, 0, 1, "node2", 2),
1277 	        )
1278 	
1279 	    def test_one_address_specified(self):
1280 	        self.assert_message_from_report(
1281 	            "At least 2 and at most 4 addresses must be specified for a node, "
1282 	            "1 address specified for node 'node2'",
1283 	            reports.CorosyncBadNodeAddressesCount(1, 2, 4, "node2", 2),
1284 	        )
1285 	
1286 	    def test_exactly_one_address_allowed(self):
1287 	        self.assert_message_from_report(
1288 	            "1 address must be specified for a node, "
1289 	            "2 addresses specified for node 'node2'",
1290 	            reports.CorosyncBadNodeAddressesCount(2, 1, 1, "node2", 2),
1291 	        )
1292 	
1293 	    def test_exactly_two_addresses_allowed(self):
1294 	        self.assert_message_from_report(
1295 	            "2 addresses must be specified for a node, "
1296 	            "1 address specified for node 'node2'",
1297 	            reports.CorosyncBadNodeAddressesCount(1, 2, 2, "node2", 2),
1298 	        )
1299 	
1300 	
1301 	class CorosyncIpVersionMismatchInLinks(NameBuildTest):
1302 	    def test_without_links(self):
1303 	        self.assert_message_from_report(
1304 	            "Using both IPv4 and IPv6 on one link is not allowed; please, use "
1305 	            "either IPv4 or IPv6",
1306 	            reports.CorosyncIpVersionMismatchInLinks(),
1307 	        )
1308 	
1309 	    def test_with_single_link(self):
1310 	        self.assert_message_from_report(
1311 	            "Using both IPv4 and IPv6 on one link is not allowed; please, use "
1312 	            "either IPv4 or IPv6 on link(s): '3'",
1313 	            reports.CorosyncIpVersionMismatchInLinks(["3"]),
1314 	        )
1315 	
1316 	    def test_with_links(self):
1317 	        self.assert_message_from_report(
1318 	            "Using both IPv4 and IPv6 on one link is not allowed; please, use "
1319 	            "either IPv4 or IPv6 on link(s): '0', '3', '4'",
1320 	            reports.CorosyncIpVersionMismatchInLinks(["3", "0", "4"]),
1321 	        )
1322 	
1323 	
1324 	class CorosyncAddressIpVersionWrongForLink(NameBuildTest):
1325 	    def test_without_links(self):
1326 	        self.assert_message_from_report(
1327 	            "Address '192.168.100.42' cannot be used in the link because "
1328 	            "the link uses IPv6 addresses",
1329 	            reports.CorosyncAddressIpVersionWrongForLink(
1330 	                "192.168.100.42",
1331 	                "IPv6",
1332 	            ),
1333 	        )
1334 	
1335 	    def test_with_links(self):
1336 	        self.assert_message_from_report(
1337 	            "Address '192.168.100.42' cannot be used in link '3' because "
1338 	            "the link uses IPv6 addresses",
1339 	            reports.CorosyncAddressIpVersionWrongForLink(
1340 	                "192.168.100.42",
1341 	                "IPv6",
1342 	                3,
1343 	            ),
1344 	        )
1345 	
1346 	
1347 	class CorosyncLinkNumberDuplication(NameBuildTest):
1348 	    _template = "Link numbers must be unique, duplicate link numbers: {values}"
1349 	
1350 	    def test_message(self):
1351 	        self.assert_message_from_report(
1352 	            self._template.format(values="'1', '3'"),
1353 	            reports.CorosyncLinkNumberDuplication(["1", "3"]),
1354 	        )
1355 	
1356 	    def test_sort(self):
1357 	        self.assert_message_from_report(
1358 	            self._template.format(values="'1', '3'"),
1359 	            reports.CorosyncLinkNumberDuplication(["3", "1"]),
1360 	        )
1361 	
1362 	    def test_sort_not_int(self):
1363 	        self.assert_message_from_report(
1364 	            self._template.format(values="'-5', 'x3', '1', '3'"),
1365 	            reports.CorosyncLinkNumberDuplication(["3", "1", "x3", "-5"]),
1366 	        )
1367 	
1368 	
1369 	class CorosyncNodeAddressCountMismatch(NameBuildTest):
1370 	    def test_message(self):
1371 	        self.assert_message_from_report(
1372 	            "All nodes must have the same number of addresses; "
1373 	            "nodes 'node3', 'node4', 'node6' have 1 address; "
1374 	            "nodes 'node2', 'node5' have 3 addresses; "
1375 	            "node 'node1' has 2 addresses",
1376 	            reports.CorosyncNodeAddressCountMismatch(
1377 	                {
1378 	                    "node1": 2,
1379 	                    "node2": 3,
1380 	                    "node3": 1,
1381 	                    "node4": 1,
1382 	                    "node5": 3,
1383 	                    "node6": 1,
1384 	                }
1385 	            ),
1386 	        )
1387 	
1388 	
1389 	class NodeAddressesAlreadyExist(NameBuildTest):
1390 	    def test_one_address(self):
1391 	        self.assert_message_from_report(
1392 	            "Node address 'node1' is already used by existing nodes; please, "
1393 	            "use other address",
1394 	            reports.NodeAddressesAlreadyExist(["node1"]),
1395 	        )
1396 	
1397 	    def test_more_addresses(self):
1398 	        self.assert_message_from_report(
1399 	            "Node addresses 'node1', 'node3' are already used by existing "
1400 	            "nodes; please, use other addresses",
1401 	            reports.NodeAddressesAlreadyExist(["node1", "node3"]),
1402 	        )
1403 	
1404 	
1405 	class NodeAddressesCannotBeEmpty(NameBuildTest):
1406 	    def test_one_node(self):
1407 	        self.assert_message_from_report(
1408 	            ("Empty address set for node 'node2', an address cannot be empty"),
1409 	            reports.NodeAddressesCannotBeEmpty(["node2"]),
1410 	        )
1411 	
1412 	    def test_more_nodes(self):
1413 	        self.assert_message_from_report(
1414 	            (
1415 	                "Empty address set for nodes 'node1', 'node2', "
1416 	                "an address cannot be empty"
1417 	            ),
1418 	            reports.NodeAddressesCannotBeEmpty(["node2", "node1"]),
1419 	        )
1420 	
1421 	
1422 	class NodeAddressesDuplication(NameBuildTest):
1423 	    def test_message(self):
1424 	        self.assert_message_from_report(
1425 	            "Node addresses must be unique, duplicate addresses: "
1426 	            "'node1', 'node3'",
1427 	            reports.NodeAddressesDuplication(["node1", "node3"]),
1428 	        )
1429 	
1430 	
1431 	class NodeNamesAlreadyExist(NameBuildTest):
1432 	    def test_one_address(self):
1433 	        self.assert_message_from_report(
1434 	            "Node name 'node1' is already used by existing nodes; please, "
1435 	            "use other name",
1436 	            reports.NodeNamesAlreadyExist(["node1"]),
1437 	        )
1438 	
1439 	    def test_more_addresses(self):
1440 	        self.assert_message_from_report(
1441 	            "Node names 'node1', 'node3' are already used by existing "
1442 	            "nodes; please, use other names",
1443 	            reports.NodeNamesAlreadyExist(["node1", "node3"]),
1444 	        )
1445 	
1446 	
1447 	class NodeNamesDuplication(NameBuildTest):
1448 	    def test_message(self):
1449 	        self.assert_message_from_report(
1450 	            "Node names must be unique, duplicate names: 'node1', 'node3'",
1451 	            reports.NodeNamesDuplication(["node1", "node3"]),
1452 	        )
1453 	
1454 	
1455 	class CorosyncNodesMissing(NameBuildTest):
1456 	    def test_message(self):
1457 	        self.assert_message_from_report(
1458 	            "No nodes have been specified", reports.CorosyncNodesMissing()
1459 	        )
1460 	
1461 	
1462 	class CorosyncNodeRenameOldNodeNotFound(NameBuildTest):
1463 	    def test_message(self):
1464 	        self.assert_message_from_report(
1465 	            ("Node 'node1' was not found in corosync.conf, unable to rename"),
1466 	            reports.CorosyncNodeRenameOldNodeNotFound("node1"),
1467 	        )
1468 	
1469 	
1470 	class CorosyncNodeRenameNewNodeAlreadyExists(NameBuildTest):
1471 	    def test_message(self):
1472 	        self.assert_message_from_report(
1473 	            ("Node 'node2' already exists in corosync.conf, unable to rename"),
1474 	            reports.CorosyncNodeRenameNewNodeAlreadyExists("node2"),
1475 	        )
1476 	
1477 	
1478 	class CorosyncNodeRenameAddrsMatchOldName(NameBuildTest):
1479 	    def test_message(self):
1480 	        self.assert_message_from_report(
1481 	            (
1482 	                "Node 'node1' has been renamed to 'node1-new', but the"
1483 	                " following addresses still reference the old name:"
1484 	                " node1-new: ring1_addr; node2: ring0_addr"
1485 	            ),
1486 	            reports.CorosyncNodeRenameAddrsMatchOldName(
1487 	                "node1",
1488 	                "node1-new",
1489 	                {"node1-new": ["ring1_addr"], "node2": ["ring0_addr"]},
1490 	            ),
1491 	        )
1492 	
1493 	
1494 	class CorosyncTooManyLinksOptions(NameBuildTest):
1495 	    def test_message(self):
1496 	        self.assert_message_from_report(
1497 	            (
1498 	                "Cannot specify options for more links (7) than how many is "
1499 	                "defined by number of addresses per node (3)"
1500 	            ),
1501 	            reports.CorosyncTooManyLinksOptions(7, 3),
1502 	        )
1503 	
1504 	
1505 	class CorosyncCannotAddRemoveLinksBadTransport(NameBuildTest):
1506 	    def test_add(self):
1507 	        self.assert_message_from_report(
1508 	            (
1509 	                "Cluster is using udp transport which does not support "
1510 	                "adding links"
1511 	            ),
1512 	            reports.CorosyncCannotAddRemoveLinksBadTransport(
1513 	                "udp", ["knet1", "knet2"], add_or_not_remove=True
1514 	            ),
1515 	        )
1516 	
1517 	    def test_remove(self):
1518 	        self.assert_message_from_report(
1519 	            (
1520 	                "Cluster is using udpu transport which does not support "
1521 	                "removing links"
1522 	            ),
1523 	            reports.CorosyncCannotAddRemoveLinksBadTransport(
1524 	                "udpu", ["knet"], add_or_not_remove=False
1525 	            ),
1526 	        )
1527 	
1528 	
1529 	class CorosyncCannotAddRemoveLinksNoLinksSpecified(NameBuildTest):
1530 	    def test_add(self):
1531 	        self.assert_message_from_report(
1532 	            "Cannot add links, no links to add specified",
1533 	            reports.CorosyncCannotAddRemoveLinksNoLinksSpecified(
1534 	                add_or_not_remove=True
1535 	            ),
1536 	        )
1537 	
1538 	    def test_remove(self):
1539 	        self.assert_message_from_report(
1540 	            "Cannot remove links, no links to remove specified",
1541 	            reports.CorosyncCannotAddRemoveLinksNoLinksSpecified(
1542 	                add_or_not_remove=False
1543 	            ),
1544 	        )
1545 	
1546 	
1547 	class CorosyncCannotAddRemoveLinksTooManyFewLinks(NameBuildTest):
1548 	    def test_add(self):
1549 	        self.assert_message_from_report(
1550 	            (
1551 	                "Cannot add 1 link, there would be 1 link defined which is "
1552 	                "more than allowed number of 1 link"
1553 	            ),
1554 	            reports.CorosyncCannotAddRemoveLinksTooManyFewLinks(
1555 	                1, 1, 1, add_or_not_remove=True
1556 	            ),
1557 	        )
1558 	
1559 	    def test_add_s(self):
1560 	        self.assert_message_from_report(
1561 	            (
1562 	                "Cannot add 2 links, there would be 4 links defined which is "
1563 	                "more than allowed number of 3 links"
1564 	            ),
1565 	            reports.CorosyncCannotAddRemoveLinksTooManyFewLinks(
1566 	                2, 4, 3, add_or_not_remove=True
1567 	            ),
1568 	        )
1569 	
1570 	    def test_remove(self):
1571 	        self.assert_message_from_report(
1572 	            (
1573 	                "Cannot remove 1 link, there would be 1 link defined which is "
1574 	                "less than allowed number of 1 link"
1575 	            ),
1576 	            reports.CorosyncCannotAddRemoveLinksTooManyFewLinks(
1577 	                1, 1, 1, add_or_not_remove=False
1578 	            ),
1579 	        )
1580 	
1581 	    def test_remove_s(self):
1582 	        self.assert_message_from_report(
1583 	            (
1584 	                "Cannot remove 3 links, there would be 0 links defined which "
1585 	                "is less than allowed number of 2 links"
1586 	            ),
1587 	            reports.CorosyncCannotAddRemoveLinksTooManyFewLinks(
1588 	                3, 0, 2, add_or_not_remove=False
1589 	            ),
1590 	        )
1591 	
1592 	
1593 	class CorosyncLinkAlreadyExistsCannotAdd(NameBuildTest):
1594 	    def test_message(self):
1595 	        self.assert_message_from_report(
1596 	            "Cannot add link '2', it already exists",
1597 	            reports.CorosyncLinkAlreadyExistsCannotAdd("2"),
1598 	        )
1599 	
1600 	
1601 	class CorosyncLinkDoesNotExistCannotRemove(NameBuildTest):
1602 	    def test_single_link(self):
1603 	        self.assert_message_from_report(
1604 	            ("Cannot remove non-existent link 'abc', existing links: '5'"),
1605 	            reports.CorosyncLinkDoesNotExistCannotRemove(["abc"], ["5"]),
1606 	        )
1607 	
1608 	    def test_multiple_links(self):
1609 	        self.assert_message_from_report(
1610 	            (
1611 	                "Cannot remove non-existent links '0', '1', 'abc', existing "
1612 	                "links: '2', '3', '5'"
1613 	            ),
1614 	            reports.CorosyncLinkDoesNotExistCannotRemove(
1615 	                ["1", "0", "abc"], ["3", "2", "5"]
1616 	            ),
1617 	        )
1618 	
1619 	
1620 	class CorosyncLinkDoesNotExistCannotUpdate(NameBuildTest):
1621 	    def test_link_list_several(self):
1622 	        self.assert_message_from_report(
1623 	            (
1624 	                "Cannot set options for non-existent link '3'"
1625 	                ", existing links: '0', '1', '2', '6', '7'"
1626 	            ),
1627 	            reports.CorosyncLinkDoesNotExistCannotUpdate(
1628 	                3, ["6", "7", "0", "1", "2"]
1629 	            ),
1630 	        )
1631 	
1632 	    def test_link_list_one(self):
1633 	        self.assert_message_from_report(
1634 	            (
1635 	                "Cannot set options for non-existent link '3'"
1636 	                ", existing links: '0'"
1637 	            ),
1638 	            reports.CorosyncLinkDoesNotExistCannotUpdate(3, ["0"]),
1639 	        )
1640 	
1641 	
1642 	class CorosyncTransportUnsupportedOptions(NameBuildTest):
1643 	    def test_udp(self):
1644 	        self.assert_message_from_report(
1645 	            "The udp/udpu transport does not support 'crypto' options, use "
1646 	            "'knet' transport",
1647 	            reports.CorosyncTransportUnsupportedOptions(
1648 	                "crypto", "udp/udpu", ["knet"]
1649 	            ),
1650 	        )
1651 	
1652 	    def test_multiple_supported_transports(self):
1653 	        self.assert_message_from_report(
1654 	            "The udp/udpu transport does not support 'crypto' options, use "
1655 	            "'knet', 'knet2' transport",
1656 	            reports.CorosyncTransportUnsupportedOptions(
1657 	                "crypto", "udp/udpu", ["knet", "knet2"]
1658 	            ),
1659 	        )
1660 	
1661 	
1662 	class ClusterUuidAlreadySet(NameBuildTest):
1663 	    def test_all(self):
1664 	        self.assert_message_from_report(
1665 	            "Cluster UUID has already been set", reports.ClusterUuidAlreadySet()
1666 	        )
1667 	
1668 	
1669 	class QdeviceAlreadyDefined(NameBuildTest):
1670 	    def test_all(self):
1671 	        self.assert_message_from_report(
1672 	            "quorum device is already defined", reports.QdeviceAlreadyDefined()
1673 	        )
1674 	
1675 	
1676 	class QdeviceNotDefined(NameBuildTest):
1677 	    def test_all(self):
1678 	        self.assert_message_from_report(
1679 	            "no quorum device is defined in this cluster",
1680 	            reports.QdeviceNotDefined(),
1681 	        )
1682 	
1683 	
1684 	class QdeviceClientReloadStarted(NameBuildTest):
1685 	    def test_all(self):
1686 	        self.assert_message_from_report(
1687 	            "Reloading qdevice configuration on nodes...",
1688 	            reports.QdeviceClientReloadStarted(),
1689 	        )
1690 	
1691 	
1692 	class QdeviceAlreadyInitialized(NameBuildTest):
1693 	    def test_all(self):
1694 	        self.assert_message_from_report(
1695 	            "Quorum device 'model' has been already initialized",
1696 	            reports.QdeviceAlreadyInitialized("model"),
1697 	        )
1698 	
1699 	
1700 	class QdeviceNotInitialized(NameBuildTest):
1701 	    def test_all(self):
1702 	        self.assert_message_from_report(
1703 	            "Quorum device 'model' has not been initialized yet",
1704 	            reports.QdeviceNotInitialized("model"),
1705 	        )
1706 	
1707 	
1708 	class QdeviceInitializationSuccess(NameBuildTest):
1709 	    def test_all(self):
1710 	        self.assert_message_from_report(
1711 	            "Quorum device 'model' initialized",
1712 	            reports.QdeviceInitializationSuccess("model"),
1713 	        )
1714 	
1715 	
1716 	class QdeviceInitializationError(NameBuildTest):
1717 	    def test_all(self):
1718 	        self.assert_message_from_report(
1719 	            "Unable to initialize quorum device 'model': reason",
1720 	            reports.QdeviceInitializationError("model", "reason"),
1721 	        )
1722 	
1723 	
1724 	class QdeviceCertificateDistributionStarted(NameBuildTest):
1725 	    def test_all(self):
1726 	        self.assert_message_from_report(
1727 	            "Setting up qdevice certificates on nodes...",
1728 	            reports.QdeviceCertificateDistributionStarted(),
1729 	        )
1730 	
1731 	
1732 	class QdeviceCertificateAcceptedByNode(NameBuildTest):
1733 	    def test_all(self):
1734 	        self.assert_message_from_report(
1735 	            "node1: Succeeded",
1736 	            reports.QdeviceCertificateAcceptedByNode("node1"),
1737 	        )
1738 	
1739 	
1740 	class QdeviceCertificateRemovalStarted(NameBuildTest):
1741 	    def test_all(self):
1742 	        self.assert_message_from_report(
1743 	            "Removing qdevice certificates from nodes...",
1744 	            reports.QdeviceCertificateRemovalStarted(),
1745 	        )
1746 	
1747 	
1748 	class QdeviceCertificateRemovedFromNode(NameBuildTest):
1749 	    def test_all(self):
1750 	        self.assert_message_from_report(
1751 	            "node2: Succeeded",
1752 	            reports.QdeviceCertificateRemovedFromNode("node2"),
1753 	        )
1754 	
1755 	
1756 	class QdeviceCertificateImportError(NameBuildTest):
1757 	    def test_all(self):
1758 	        self.assert_message_from_report(
1759 	            "Unable to import quorum device certificate: reason",
1760 	            reports.QdeviceCertificateImportError("reason"),
1761 	        )
1762 	
1763 	
1764 	class QdeviceCertificateSignError(NameBuildTest):
1765 	    def test_all(self):
1766 	        self.assert_message_from_report(
1767 	            "Unable to sign quorum device certificate: reason",
1768 	            reports.QdeviceCertificateSignError("reason"),
1769 	        )
1770 	
1771 	
1772 	class QdeviceCertificateBadFormat(NameBuildTest):
1773 	    def test_all(self):
1774 	        self.assert_message_from_report(
1775 	            "Unable to parse quorum device certificate",
1776 	            reports.QdeviceCertificateBadFormat(),
1777 	        )
1778 	
1779 	
1780 	class QdeviceCertificateReadError(NameBuildTest):
1781 	    def test_all(self):
1782 	        self.assert_message_from_report(
1783 	            "Unable to read quorum device certificate: reason",
1784 	            reports.QdeviceCertificateReadError("reason"),
1785 	        )
1786 	
1787 	
1788 	class QdeviceDestroySuccess(NameBuildTest):
1789 	    def test_all(self):
1790 	        self.assert_message_from_report(
1791 	            "Quorum device 'model' configuration files removed",
1792 	            reports.QdeviceDestroySuccess("model"),
1793 	        )
1794 	
1795 	
1796 	class QdeviceDestroyError(NameBuildTest):
1797 	    def test_all(self):
1798 	        self.assert_message_from_report(
1799 	            "Unable to destroy quorum device 'model': reason",
1800 	            reports.QdeviceDestroyError("model", "reason"),
1801 	        )
1802 	
1803 	
1804 	class QdeviceNotRunning(NameBuildTest):
1805 	    def test_all(self):
1806 	        self.assert_message_from_report(
1807 	            "Quorum device 'model' is not running",
1808 	            reports.QdeviceNotRunning("model"),
1809 	        )
1810 	
1811 	
1812 	class QdeviceGetStatusError(NameBuildTest):
1813 	    def test_all(self):
1814 	        self.assert_message_from_report(
1815 	            "Unable to get status of quorum device 'model': reason",
1816 	            reports.QdeviceGetStatusError("model", "reason"),
1817 	        )
1818 	
1819 	
1820 	class QdeviceUsedByClusters(NameBuildTest):
1821 	    def test_single_cluster(self):
1822 	        self.assert_message_from_report(
1823 	            "Quorum device is currently being used by cluster(s): 'c1'",
1824 	            reports.QdeviceUsedByClusters(["c1"]),
1825 	        )
1826 	
1827 	    def test_multiple_clusters(self):
1828 	        self.assert_message_from_report(
1829 	            "Quorum device is currently being used by cluster(s): 'c1', 'c2'",
1830 	            reports.QdeviceUsedByClusters(["c1", "c2"]),
1831 	        )
1832 	
1833 	
1834 	class IdAlreadyExists(NameBuildTest):
1835 	    def test_all(self):
1836 	        self.assert_message_from_report(
1837 	            "'id' already exists", reports.IdAlreadyExists("id")
1838 	        )
1839 	
1840 	
1841 	class IdBelongsToUnexpectedType(NameBuildTest):
1842 	    def test_build_message_with_single_type(self):
1843 	        self.assert_message_from_report(
1844 	            "'ID' is not an ACL permission",
1845 	            reports.IdBelongsToUnexpectedType("ID", ["acl_permission"], "op"),
1846 	        )
1847 	
1848 	    def test_build_message_with_data(self):
1849 	        self.assert_message_from_report(
1850 	            "'ID' is not a clone / resource",
1851 	            reports.IdBelongsToUnexpectedType(
1852 	                "ID", ["primitive", "clone"], "op"
1853 	            ),
1854 	        )
1855 	
1856 	    def test_build_message_with_transformation_and_article(self):
1857 	        self.assert_message_from_report(
1858 	            "'ID' is not an ACL group / ACL user",
1859 	            reports.IdBelongsToUnexpectedType(
1860 	                "ID",
1861 	                ["acl_target", "acl_group"],
1862 	                "op",
1863 	            ),
1864 	        )
1865 	
1866 	
1867 	class IdDoesNotSupportElementDescriptions(NameBuildTest):
1868 	    def test_success(self):
1869 	        self.assert_message_from_report(
1870 	            (
1871 	                "'ID' is a location constraint, descriptions are only "
1872 	                "supported for clone / resource"
1873 	            ),
1874 	            reports.IdDoesNotSupportElementDescriptions(
1875 	                "ID",
1876 	                "rsc_location",
1877 	                ["primitive", "clone"],
1878 	            ),
1879 	        )
1880 	
1881 	
1882 	class ObjectWithIdInUnexpectedContext(NameBuildTest):
1883 	    def test_with_context_id(self):
1884 	        self.assert_message_from_report(
1885 	            "resource 'R' exists but does not belong to group 'G'",
1886 	            reports.ObjectWithIdInUnexpectedContext(
1887 	                "primitive", "R", "group", "G"
1888 	            ),
1889 	        )
1890 	
1891 	    def test_without_context_id(self):
1892 	        self.assert_message_from_report(
1893 	            "group 'G' exists but does not belong to 'resource'",
1894 	            reports.ObjectWithIdInUnexpectedContext(
1895 	                "group", "G", "primitive", ""
1896 	            ),
1897 	        )
1898 	
1899 	
1900 	class IdNotFound(NameBuildTest):
1901 	    def test_id(self):
1902 	        self.assert_message_from_report(
1903 	            "'ID' does not exist", reports.IdNotFound("ID", [])
1904 	        )
1905 	
1906 	    def test_id_and_type(self):
1907 	        self.assert_message_from_report(
1908 	            "clone / resource 'ID' does not exist",
1909 	            reports.IdNotFound("ID", ["primitive", "clone"]),
1910 	        )
1911 	
1912 	    def test_context(self):
1913 	        self.assert_message_from_report(
1914 	            "there is no 'ID' in the C_TYPE 'C_ID'",
1915 	            reports.IdNotFound(
1916 	                "ID", [], context_type="C_TYPE", context_id="C_ID"
1917 	            ),
1918 	        )
1919 	
1920 	    def test_type_and_context(self):
1921 	        self.assert_message_from_report(
1922 	            "there is no ACL user 'ID' in the C_TYPE 'C_ID'",
1923 	            reports.IdNotFound(
1924 	                "ID", ["acl_target"], context_type="C_TYPE", context_id="C_ID"
1925 	            ),
1926 	        )
1927 	
1928 	
1929 	class ResourceBundleAlreadyContainsAResource(NameBuildTest):
1930 	    def test_build_message_with_data(self):
1931 	        self.assert_message_from_report(
1932 	            (
1933 	                "bundle 'test_bundle' already contains resource "
1934 	                "'test_resource', a bundle may contain at most one resource"
1935 	            ),
1936 	            reports.ResourceBundleAlreadyContainsAResource(
1937 	                "test_bundle", "test_resource"
1938 	            ),
1939 	        )
1940 	
1941 	
1942 	class CannotGroupResourceWrongType(NameBuildTest):
1943 	    def test_without_parent(self):
1944 	        self.assert_message_from_report(
1945 	            (
1946 	                "'R' is a clone resource, clone resources cannot be put into "
1947 	                "a group"
1948 	            ),
1949 	            reports.CannotGroupResourceWrongType("R", "master", None, None),
1950 	        )
1951 	
1952 	    def test_with_parent(self):
1953 	        self.assert_message_from_report(
1954 	            (
1955 	                "'R' cannot be put into a group because its parent 'B' "
1956 	                "is a bundle resource"
1957 	            ),
1958 	            reports.CannotGroupResourceWrongType(
1959 	                "R", "primitive", "B", "bundle"
1960 	            ),
1961 	        )
1962 	
1963 	
1964 	class UnableToGetResourceOperationDigests(NameBuildTest):
1965 	    def test_success(self):
1966 	        self.assert_message_from_report(
1967 	            "unable to get resource operation digests:\ncrm_resource output",
1968 	            reports.UnableToGetResourceOperationDigests("crm_resource output"),
1969 	        )
1970 	
1971 	
1972 	class StonithResourcesDoNotExist(NameBuildTest):
1973 	    def test_success(self):
1974 	        self.assert_message_from_report(
1975 	            "Stonith resource(s) 'device1', 'device2' do not exist",
1976 	            reports.StonithResourcesDoNotExist(["device2", "device1"]),
1977 	        )
1978 	
1979 	
1980 	class StonithRestartlessUpdateOfScsiDevicesNotSupported(NameBuildTest):
1981 	    def test_success(self):
1982 	        self.assert_message_from_report(
1983 	            (
1984 	                "Restartless update of scsi devices is not supported, please "
1985 	                "upgrade pacemaker"
1986 	            ),
1987 	            reports.StonithRestartlessUpdateOfScsiDevicesNotSupported(),
1988 	        )
1989 	
1990 	
1991 	class StonithRestartlessUpdateUnsupportedAgent(NameBuildTest):
1992 	    def test_plural(self):
1993 	        self.assert_message_from_report(
1994 	            (
1995 	                "Resource 'fence_sbd' is not a stonith resource or its type "
1996 	                "'wrong_type' is not supported for devices update. Supported "
1997 	                "types: 'fence_mpath', 'fence_scsi'"
1998 	            ),
1999 	            reports.StonithRestartlessUpdateUnsupportedAgent(
2000 	                "fence_sbd", "wrong_type", ["fence_scsi", "fence_mpath"]
2001 	            ),
2002 	        )
2003 	
2004 	    def test_singular(self):
2005 	        self.assert_message_from_report(
2006 	            (
2007 	                "Resource 'fence_sbd' is not a stonith resource or its type "
2008 	                "'wrong_type' is not supported for devices update. Supported "
2009 	                "type: 'fence_scsi'"
2010 	            ),
2011 	            reports.StonithRestartlessUpdateUnsupportedAgent(
2012 	                "fence_sbd", "wrong_type", ["fence_scsi"]
2013 	            ),
2014 	        )
2015 	
2016 	
2017 	class StonithUnfencingFailed(NameBuildTest):
2018 	    def test_build_message(self):
2019 	        self.assert_message_from_report(
2020 	            ("Unfencing failed:\nreason"),
2021 	            reports.StonithUnfencingFailed("reason"),
2022 	        )
2023 	
2024 	
2025 	class StonithUnfencingDeviceStatusFailed(NameBuildTest):
2026 	    def test_build_message(self):
2027 	        self.assert_message_from_report(
2028 	            "Unfencing failed, unable to check status of device 'dev1': reason",
2029 	            reports.StonithUnfencingDeviceStatusFailed("dev1", "reason"),
2030 	        )
2031 	
2032 	
2033 	class StonithUnfencingSkippedDevicesFenced(NameBuildTest):
2034 	    def test_one_device(self):
2035 	        self.assert_message_from_report(
2036 	            "Unfencing skipped, device 'dev1' is fenced",
2037 	            reports.StonithUnfencingSkippedDevicesFenced(["dev1"]),
2038 	        )
2039 	
2040 	    def test_multiple_devices(self):
2041 	        self.assert_message_from_report(
2042 	            "Unfencing skipped, devices 'dev1', 'dev2', 'dev3' are fenced",
2043 	            reports.StonithUnfencingSkippedDevicesFenced(
2044 	                ["dev2", "dev1", "dev3"]
2045 	            ),
2046 	        )
2047 	
2048 	
2049 	class StonithRestartlessUpdateUnableToPerform(NameBuildTest):
2050 	    def test_build_message(self):
2051 	        self.assert_message_from_report(
2052 	            "Unable to perform restartless update of scsi devices: reason",
2053 	            reports.StonithRestartlessUpdateUnableToPerform("reason"),
2054 	        )
2055 	
2056 	    def test_build_message_reason_type_specified(self):
2057 	        self.assert_message_from_report(
2058 	            "Unable to perform restartless update of scsi devices: reason",
2059 	            reports.StonithRestartlessUpdateUnableToPerform(
2060 	                "reason",
2061 	                const.STONITH_RESTARTLESS_UPDATE_UNABLE_TO_PERFORM_REASON_NOT_RUNNING,
2062 	            ),
2063 	        )
2064 	
2065 	
2066 	class StonithRestartlessUpdateMissingMpathKeys(NameBuildTest):
2067 	    def test_plural(self):
2068 	        self.assert_message_from_report(
2069 	            (
2070 	                "Missing mpath reservation keys for nodes: 'rh9-2', 'rh9-3', "
2071 	                "in 'pcmk_host_map' value: 'rh9-1:1'"
2072 	            ),
2073 	            reports.StonithRestartlessUpdateMissingMpathKeys(
2074 	                "rh9-1:1", ["rh9-2", "rh9-3"]
2075 	            ),
2076 	        )
2077 	
2078 	    def test_singular(self):
2079 	        self.assert_message_from_report(
2080 	            (
2081 	                "Missing mpath reservation key for node: 'rh9-2', "
2082 	                "in 'pcmk_host_map' value: 'rh9-1:1'"
2083 	            ),
2084 	            reports.StonithRestartlessUpdateMissingMpathKeys(
2085 	                "rh9-1:1", ["rh9-2"]
2086 	            ),
2087 	        )
2088 	
2089 	    def test_missing_map_and_empty_nodes(self):
2090 	        self.assert_message_from_report(
2091 	            "Missing mpath reservation keys, 'pcmk_host_map' not set",
2092 	            reports.StonithRestartlessUpdateMissingMpathKeys(None, []),
2093 	        )
2094 	
2095 	    def test_missing_map_non_empty_nodes(self):
2096 	        self.assert_message_from_report(
2097 	            "Missing mpath reservation keys, 'pcmk_host_map' not set",
2098 	            reports.StonithRestartlessUpdateMissingMpathKeys(
2099 	                None, ["rh9-1", "rh9-2"]
2100 	            ),
2101 	        )
2102 	
2103 	    def test_non_empty_map_empty_nodes(self):
2104 	        self.assert_message_from_report(
2105 	            (
2106 	                "Missing mpath reservation keys for nodes in 'pcmk_host_map' "
2107 	                "value: 'rh-1:1'"
2108 	            ),
2109 	            reports.StonithRestartlessUpdateMissingMpathKeys("rh-1:1", []),
2110 	        )
2111 	
2112 	
2113 	class ResourceRunningOnNodes(NameBuildTest):
2114 	    def test_one_node(self):
2115 	        self.assert_message_from_report(
2116 	            "resource 'R' is running on node 'node1'",
2117 	            reports.ResourceRunningOnNodes("R", {"Started": ["node1"]}),
2118 	        )
2119 	
2120 	    def test_multiple_nodes(self):
2121 	        self.assert_message_from_report(
2122 	            "resource 'R' is running on nodes 'node1', 'node2'",
2123 	            reports.ResourceRunningOnNodes(
2124 	                "R", {"Started": ["node1", "node2"]}
2125 	            ),
2126 	        )
2127 	
2128 	    def test_multiple_role_multiple_nodes(self):
2129 	        self.assert_message_from_report(
2130 	            "resource 'R' is promoted on node 'node3'"
2131 	            "; running on nodes 'node1', 'node2'",
2132 	            reports.ResourceRunningOnNodes(
2133 	                "R",
2134 	                {
2135 	                    "Started": ["node1", "node2"],
2136 	                    "Promoted": ["node3"],
2137 	                },
2138 	            ),
2139 	        )
2140 	
2141 	
2142 	class ResourceDoesNotRun(NameBuildTest):
2143 	    def test_build_message(self):
2144 	        self.assert_message_from_report(
2145 	            "resource 'R' is not running on any node",
2146 	            reports.ResourceDoesNotRun("R"),
2147 	        )
2148 	
2149 	
2150 	class ResourceIsGuestNodeAlready(NameBuildTest):
2151 	    def test_build_messages(self):
2152 	        self.assert_message_from_report(
2153 	            "the resource 'some-resource' is already a guest node",
2154 	            reports.ResourceIsGuestNodeAlready("some-resource"),
2155 	        )
2156 	
2157 	
2158 	class ResourceIsUnmanaged(NameBuildTest):
2159 	    def test_build_message(self):
2160 	        self.assert_message_from_report(
2161 	            "'R' is unmanaged", reports.ResourceIsUnmanaged("R")
2162 	        )
2163 	
2164 	
2165 	class ResourceManagedNoMonitorEnabled(NameBuildTest):
2166 	    def test_build_message(self):
2167 	        self.assert_message_from_report(
2168 	            "Resource 'R' has no enabled monitor operations",
2169 	            reports.ResourceManagedNoMonitorEnabled("R"),
2170 	        )
2171 	
2172 	
2173 	class CibLoadError(NameBuildTest):
2174 	    def test_all(self):
2175 	        self.assert_message_from_report(
2176 	            "unable to get cib", reports.CibLoadError("reason")
2177 	        )
2178 	
2179 	
2180 	class CibLoadErrorGetNodesForValidation(NameBuildTest):
2181 	    def test_all(self):
2182 	        self.assert_message_from_report(
2183 	            (
2184 	                "Unable to load CIB to get guest and remote nodes from it, "
2185 	                "those nodes cannot be considered in configuration validation"
2186 	            ),
2187 	            reports.CibLoadErrorGetNodesForValidation(),
2188 	        )
2189 	
2190 	
2191 	class CibLoadErrorScopeMissing(NameBuildTest):
2192 	    def test_all(self):
2193 	        self.assert_message_from_report(
2194 	            "unable to get cib, scope 'scope-name' not present in cib",
2195 	            reports.CibLoadErrorScopeMissing("scope-name", "reason"),
2196 	        )
2197 	
2198 	
2199 	class CibLoadErrorBadFormat(NameBuildTest):
2200 	    def test_message(self):
2201 	        self.assert_message_from_report(
2202 	            "unable to get cib, something wrong",
2203 	            reports.CibLoadErrorBadFormat("something wrong"),
2204 	        )
2205 	
2206 	
2207 	class CibCannotFindMandatorySection(NameBuildTest):
2208 	    def test_all(self):
2209 	        self.assert_message_from_report(
2210 	            "Unable to get 'section-name' section of cib",
2211 	            reports.CibCannotFindMandatorySection("section-name"),
2212 	        )
2213 	
2214 	
2215 	class CibPushError(NameBuildTest):
2216 	    def test_all(self):
2217 	        self.assert_message_from_report(
2218 	            "Unable to update cib\nreason\npushed-cib",
2219 	            reports.CibPushError("reason", "pushed-cib"),
2220 	        )
2221 	
2222 	
2223 	class CibSaveTmpError(NameBuildTest):
2224 	    def test_all(self):
2225 	        self.assert_message_from_report(
2226 	            "Unable to save CIB to a temporary file: reason",
2227 	            reports.CibSaveTmpError("reason"),
2228 	        )
2229 	
2230 	
2231 	class CibDiffError(NameBuildTest):
2232 	    def test_success(self):
2233 	        self.assert_message_from_report(
2234 	            "Unable to diff CIB: error message\n<cib-new />",
2235 	            reports.CibDiffError("error message", "<cib-old />", "<cib-new />"),
2236 	        )
2237 	
2238 	
2239 	class CibSimulateError(NameBuildTest):
2240 	    def test_success(self):
2241 	        self.assert_message_from_report(
2242 	            "Unable to simulate changes in CIB: error message",
2243 	            reports.CibSimulateError("error message"),
2244 	        )
2245 	
2246 	    def test_empty_reason(self):
2247 	        self.assert_message_from_report(
2248 	            "Unable to simulate changes in CIB",
2249 	            reports.CibSimulateError(""),
2250 	        )
2251 	
2252 	
2253 	class CrmMonError(NameBuildTest):
2254 	    def test_without_reason(self):
2255 	        self.assert_message_from_report(
2256 	            "error running crm_mon, is pacemaker running?",
2257 	            reports.CrmMonError(""),
2258 	        )
2259 	
2260 	    def test_with_reason(self):
2261 	        self.assert_message_from_report(
2262 	            (
2263 	                "error running crm_mon, is pacemaker running?"
2264 	                "\n  reason\n  spans several lines"
2265 	            ),
2266 	            reports.CrmMonError("reason\nspans several lines"),
2267 	        )
2268 	
2269 	
2270 	class BadPcmkApiResponseFormat(NameBuildTest):
2271 	    def test_all(self):
2272 	        self.assert_message_from_report(
2273 	            (
2274 	                "Cannot process pacemaker response due to a parse error: "
2275 	                "detailed parse or xml error\n"
2276 	                "pacemaker tool output"
2277 	            ),
2278 	            reports.BadPcmkApiResponseFormat(
2279 	                "detailed parse or xml error", "pacemaker tool output"
2280 	            ),
2281 	        )
2282 	
2283 	
2284 	class BadClusterStateFormat(NameBuildTest):
2285 	    def test_all(self):
2286 	        self.assert_message_from_report(
2287 	            "cannot load cluster status, xml does not conform to the schema",
2288 	            reports.BadClusterStateFormat(),
2289 	        )
2290 	
2291 	
2292 	class BadClusterStateData(NameBuildTest):
2293 	    def test_no_reason(self):
2294 	        self.assert_message_from_report(
2295 	            (
2296 	                "Cannot load cluster status, xml does not describe "
2297 	                "valid cluster status"
2298 	            ),
2299 	            reports.BadClusterStateData(),
2300 	        )
2301 	
2302 	    def test_reason(self):
2303 	        self.assert_message_from_report(
2304 	            (
2305 	                "Cannot load cluster status, xml does not describe "
2306 	                "valid cluster status: sample reason"
2307 	            ),
2308 	            reports.BadClusterStateData("sample reason"),
2309 	        )
2310 	
2311 	
2312 	class WaitForIdleStarted(NameBuildTest):
2313 	    def test_timeout(self):
2314 	        timeout = 20
2315 	        self.assert_message_from_report(
2316 	            (
2317 	                "Waiting for the cluster to apply configuration changes "
2318 	                f"(timeout: {timeout} seconds)..."
2319 	            ),
2320 	            reports.WaitForIdleStarted(timeout),
2321 	        )
2322 	
2323 	    def test_timeout_singular(self):
2324 	        timeout = 1
2325 	        self.assert_message_from_report(
2326 	            (
2327 	                "Waiting for the cluster to apply configuration changes "
2328 	                f"(timeout: {timeout} second)..."
2329 	            ),
2330 	            reports.WaitForIdleStarted(timeout),
2331 	        )
2332 	
2333 	    def test_timeout_0(self):
2334 	        self.assert_message_from_report(
2335 	            "Waiting for the cluster to apply configuration changes...",
2336 	            reports.WaitForIdleStarted(0),
2337 	        )
2338 	
2339 	    def test_timeout_negative(self):
2340 	        self.assert_message_from_report(
2341 	            "Waiting for the cluster to apply configuration changes...",
2342 	            reports.WaitForIdleStarted(-1),
2343 	        )
2344 	
2345 	
2346 	class WaitForIdleTimedOut(NameBuildTest):
2347 	    def test_all(self):
2348 	        self.assert_message_from_report(
2349 	            "waiting timeout\n\nreason", reports.WaitForIdleTimedOut("reason")
2350 	        )
2351 	
2352 	
2353 	class WaitForIdleError(NameBuildTest):
2354 	    def test_all(self):
2355 	        self.assert_message_from_report(
2356 	            "reason", reports.WaitForIdleError("reason")
2357 	        )
2358 	
2359 	
2360 	class WaitForIdleNotLiveCluster(NameBuildTest):
2361 	    def test_all(self):
2362 	        self.assert_message_from_report(
2363 	            "Cannot pass CIB together with 'wait'",
2364 	            reports.WaitForIdleNotLiveCluster(),
2365 	        )
2366 	
2367 	
2368 	class ResourceCleanupError(NameBuildTest):
2369 	    def test_minimal(self):
2370 	        self.assert_message_from_report(
2371 	            "Unable to forget failed operations of resources\nsomething wrong",
2372 	            reports.ResourceCleanupError("something wrong"),
2373 	        )
2374 	
2375 	    def test_node(self):
2376 	        self.assert_message_from_report(
2377 	            "Unable to forget failed operations of resources\nsomething wrong",
2378 	            reports.ResourceCleanupError("something wrong", node="N1"),
2379 	        )
2380 	
2381 	    def test_resource(self):
2382 	        self.assert_message_from_report(
2383 	            "Unable to forget failed operations of resource: R1\n"
2384 	            "something wrong",
2385 	            reports.ResourceCleanupError("something wrong", "R1"),
2386 	        )
2387 	
2388 	    def test_resource_and_node(self):
2389 	        self.assert_message_from_report(
2390 	            "Unable to forget failed operations of resource: R1\n"
2391 	            "something wrong",
2392 	            reports.ResourceCleanupError("something wrong", "R1", "N1"),
2393 	        )
2394 	
2395 	
2396 	class ResourceRefreshError(NameBuildTest):
2397 	    def test_minimal(self):
2398 	        self.assert_message_from_report(
2399 	            "Unable to delete history of resources\nsomething wrong",
2400 	            reports.ResourceRefreshError("something wrong"),
2401 	        )
2402 	
2403 	    def test_node(self):
2404 	        self.assert_message_from_report(
2405 	            "Unable to delete history of resources\nsomething wrong",
2406 	            reports.ResourceRefreshError(
2407 	                "something wrong",
2408 	                node="N1",
2409 	            ),
2410 	        )
2411 	
2412 	    def test_resource(self):
2413 	        self.assert_message_from_report(
2414 	            "Unable to delete history of resource: R1\nsomething wrong",
2415 	            reports.ResourceRefreshError("something wrong", "R1"),
2416 	        )
2417 	
2418 	    def test_resource_and_node(self):
2419 	        self.assert_message_from_report(
2420 	            "Unable to delete history of resource: R1\nsomething wrong",
2421 	            reports.ResourceRefreshError("something wrong", "R1", "N1"),
2422 	        )
2423 	
2424 	
2425 	class ResourceRefreshTooTimeConsuming(NameBuildTest):
2426 	    def test_success(self):
2427 	        self.assert_message_from_report(
2428 	            "Deleting history of all resources on all nodes will execute more "
2429 	            "than 25 operations in the cluster, which may negatively "
2430 	            "impact the responsiveness of the cluster. Consider specifying "
2431 	            "resource and/or node",
2432 	            reports.ResourceRefreshTooTimeConsuming(25),
2433 	        )
2434 	
2435 	
2436 	class ResourceOperationIntervalDuplication(NameBuildTest):
2437 	    def test_build_message_with_data(self):
2438 	        self.assert_message_from_report(
2439 	            "multiple specification of the same operation with the same"
2440 	            " interval:"
2441 	            "\nmonitor with intervals 3600s, 60m, 1h"
2442 	            "\nmonitor with intervals 60s, 1m",
2443 	            reports.ResourceOperationIntervalDuplication(
2444 	                {
2445 	                    "monitor": [
2446 	                        ["3600s", "60m", "1h"],
2447 	                        ["60s", "1m"],
2448 	                    ],
2449 	                }
2450 	            ),
2451 	        )
2452 	
2453 	
2454 	class ResourceOperationIntervalAdapted(NameBuildTest):
2455 	    def test_build_message_with_data(self):
2456 	        self.assert_message_from_report(
2457 	            "changing a monitor operation interval from 10 to 11 to make the"
2458 	            " operation unique",
2459 	            reports.ResourceOperationIntervalAdapted("monitor", "10", "11"),
2460 	        )
2461 	
2462 	
2463 	class NodeNotFound(NameBuildTest):
2464 	    def test_build_messages(self):
2465 	        self.assert_message_from_report(
2466 	            "Node 'SOME_NODE' does not appear to exist in configuration",
2467 	            reports.NodeNotFound("SOME_NODE"),
2468 	        )
2469 	
2470 	    def test_build_messages_with_one_search_types(self):
2471 	        self.assert_message_from_report(
2472 	            "remote node 'SOME_NODE' does not appear to exist in configuration",
2473 	            reports.NodeNotFound("SOME_NODE", ["remote"]),
2474 	        )
2475 	
2476 	    def test_build_messages_with_multiple_search_types(self):
2477 	        self.assert_message_from_report(
2478 	            "nor remote node or guest node 'SOME_NODE' does not appear to exist"
2479 	            " in configuration",
2480 	            reports.NodeNotFound("SOME_NODE", ["remote", "guest"]),
2481 	        )
2482 	
2483 	
2484 	class NodeRenameNamesEqual(NameBuildTest):
2485 	    def test_message(self):
2486 	        self.assert_message_from_report(
2487 	            (
2488 	                "Unable to rename node 'node1': "
2489 	                "new name is the same as the current name"
2490 	            ),
2491 	            reports.NodeRenameNamesEqual("node1"),
2492 	        )
2493 	
2494 	
2495 	class NodeToClearIsStillInCluster(NameBuildTest):
2496 	    def test_build_messages(self):
2497 	        self.assert_message_from_report(
2498 	            "node 'node1' seems to be still in the cluster"
2499 	            "; this command should be used only with nodes that have been"
2500 	            " removed from the cluster",
2501 	            reports.NodeToClearIsStillInCluster("node1"),
2502 	        )
2503 	
2504 	
2505 	class NodeRemoveInPacemakerFailed(NameBuildTest):
2506 	    def test_minimal(self):
2507 	        self.assert_message_from_report(
2508 	            ("Unable to remove node(s) 'NODE1', 'NODE2' from pacemaker"),
2509 	            reports.NodeRemoveInPacemakerFailed(["NODE2", "NODE1"]),
2510 	        )
2511 	
2512 	    def test_without_node(self):
2513 	        self.assert_message_from_report(
2514 	            "Unable to remove node(s) 'NODE' from pacemaker: reason",
2515 	            reports.NodeRemoveInPacemakerFailed(["NODE"], reason="reason"),
2516 	        )
2517 	
2518 	    def test_with_node(self):
2519 	        self.assert_message_from_report(
2520 	            (
2521 	                "node-a: Unable to remove node(s) 'NODE1', 'NODE2' from "
2522 	                "pacemaker: reason"
2523 	            ),
2524 	            reports.NodeRemoveInPacemakerFailed(
2525 	                ["NODE1", "NODE2"], node="node-a", reason="reason"
2526 	            ),
2527 	        )
2528 	
2529 	
2530 	class NodeRemoveInPacemakerSkipped(NameBuildTest):
2531 	    def test_one_node(self):
2532 	        self.assert_message_from_report(
2533 	            (
2534 	                "Skipping removal of node 'NODE1' from pacemaker because the "
2535 	                "command does not run on a live cluster"
2536 	            ),
2537 	            reports.NodeRemoveInPacemakerSkipped(
2538 	                const.REASON_NOT_LIVE_CIB, ["NODE1"]
2539 	            ),
2540 	        )
2541 	
2542 	    def test_multiple_nodes(self):
2543 	        self.assert_message_from_report(
2544 	            (
2545 	                "Skipping removal of nodes 'NODE1', 'NODE2' from pacemaker "
2546 	                "because the command does not run on a live cluster"
2547 	            ),
2548 	            reports.NodeRemoveInPacemakerSkipped(
2549 	                const.REASON_NOT_LIVE_CIB, ["NODE2", "NODE1"]
2550 	            ),
2551 	        )
2552 	
2553 	    def test_with_node(self):
2554 	        self.assert_message_from_report(
2555 	            (
2556 	                "node-a: Unable to remove node(s) 'NODE1', 'NODE2' from "
2557 	                "pacemaker: reason"
2558 	            ),
2559 	            reports.NodeRemoveInPacemakerFailed(
2560 	                ["NODE1", "NODE2"], node="node-a", reason="reason"
2561 	            ),
2562 	        )
2563 	
2564 	
2565 	class MultipleResultsFound(NameBuildTest):
2566 	    def test_minimal(self):
2567 	        self.assert_message_from_report(
2568 	            "more than one resource found: 'ID1', 'ID2'",
2569 	            reports.MultipleResultsFound("resource", ["ID2", "ID1"]),
2570 	        )
2571 	
2572 	    def test_build_messages(self):
2573 	        self.assert_message_from_report(
2574 	            "more than one resource for 'NODE-NAME' found: 'ID1', 'ID2'",
2575 	            reports.MultipleResultsFound(
2576 	                "resource", ["ID2", "ID1"], "NODE-NAME"
2577 	            ),
2578 	        )
2579 	
2580 	
2581 	class PacemakerSimulationResult(NameBuildTest):
2582 	    def test_default(self):
2583 	        self.assert_message_from_report(
2584 	            "\nSimulation result:\ncrm_simulate output",
2585 	            reports.PacemakerSimulationResult("crm_simulate output"),
2586 	        )
2587 	
2588 	
2589 	class PacemakerLocalNodeNameNotFound(NameBuildTest):
2590 	    def test_all(self):
2591 	        self.assert_message_from_report(
2592 	            "unable to get local node name from pacemaker: reason",
2593 	            reports.PacemakerLocalNodeNameNotFound("reason"),
2594 	        )
2595 	
2596 	
2597 	class ServiceActionStarted(NameBuildTest):
2598 	    def test_start(self):
2599 	        self.assert_message_from_report(
2600 	            "Starting a_service...",
2601 	            reports.ServiceActionStarted(
2602 	                const.SERVICE_ACTION_START, "a_service"
2603 	            ),
2604 	        )
2605 	
2606 	    def test_start_instance(self):
2607 	        self.assert_message_from_report(
2608 	            "Starting a_service@an_instance...",
2609 	            reports.ServiceActionStarted(
2610 	                const.SERVICE_ACTION_START, "a_service", "an_instance"
2611 	            ),
2612 	        )
2613 	
2614 	    def test_stop(self):
2615 	        self.assert_message_from_report(
2616 	            "Stopping a_service...",
2617 	            reports.ServiceActionStarted(
2618 	                const.SERVICE_ACTION_STOP, "a_service"
2619 	            ),
2620 	        )
2621 	
2622 	    def test_stop_instance(self):
2623 	        self.assert_message_from_report(
2624 	            "Stopping a_service@an_instance...",
2625 	            reports.ServiceActionStarted(
2626 	                const.SERVICE_ACTION_STOP, "a_service", "an_instance"
2627 	            ),
2628 	        )
2629 	
2630 	    def test_enable(self):
2631 	        self.assert_message_from_report(
2632 	            "Enabling a_service...",
2633 	            reports.ServiceActionStarted(
2634 	                const.SERVICE_ACTION_ENABLE, "a_service"
2635 	            ),
2636 	        )
2637 	
2638 	    def test_enable_instance(self):
2639 	        self.assert_message_from_report(
2640 	            "Enabling a_service@an_instance...",
2641 	            reports.ServiceActionStarted(
2642 	                const.SERVICE_ACTION_ENABLE, "a_service", "an_instance"
2643 	            ),
2644 	        )
2645 	
2646 	    def test_disable(self):
2647 	        self.assert_message_from_report(
2648 	            "Disabling a_service...",
2649 	            reports.ServiceActionStarted(
2650 	                const.SERVICE_ACTION_DISABLE, "a_service"
2651 	            ),
2652 	        )
2653 	
2654 	    def test_disable_instance(self):
2655 	        self.assert_message_from_report(
2656 	            "Disabling a_service@an_instance...",
2657 	            reports.ServiceActionStarted(
2658 	                const.SERVICE_ACTION_DISABLE, "a_service", "an_instance"
2659 	            ),
2660 	        )
2661 	
2662 	    def test_kill(self):
2663 	        self.assert_message_from_report(
2664 	            "Killing a_service...",
2665 	            reports.ServiceActionStarted(
2666 	                const.SERVICE_ACTION_KILL, "a_service"
2667 	            ),
2668 	        )
2669 	
2670 	    def test_kill_instance(self):
2671 	        self.assert_message_from_report(
2672 	            "Killing a_service@an_instance...",
2673 	            reports.ServiceActionStarted(
2674 	                const.SERVICE_ACTION_KILL, "a_service", "an_instance"
2675 	            ),
2676 	        )
2677 	
2678 	
2679 	# TODO: add tests for node if needed
2680 	class ServiceActionFailed(NameBuildTest):
2681 	    def test_start(self):
2682 	        self.assert_message_from_report(
2683 	            "Unable to start a_service: a_reason",
2684 	            reports.ServiceActionFailed(
2685 	                const.SERVICE_ACTION_START, "a_service", "a_reason"
2686 	            ),
2687 	        )
2688 	
2689 	    def test_start_instance(self):
2690 	        self.assert_message_from_report(
2691 	            "Unable to start a_service@an_instance: a_reason",
2692 	            reports.ServiceActionFailed(
2693 	                const.SERVICE_ACTION_START,
2694 	                "a_service",
2695 	                "a_reason",
2696 	                instance="an_instance",
2697 	            ),
2698 	        )
2699 	
2700 	    def test_stop(self):
2701 	        self.assert_message_from_report(
2702 	            "Unable to stop a_service: a_reason",
2703 	            reports.ServiceActionFailed(
2704 	                const.SERVICE_ACTION_STOP, "a_service", "a_reason"
2705 	            ),
2706 	        )
2707 	
2708 	    def test_stop_instance(self):
2709 	        self.assert_message_from_report(
2710 	            "Unable to stop a_service@an_instance: a_reason",
2711 	            reports.ServiceActionFailed(
2712 	                const.SERVICE_ACTION_STOP,
2713 	                "a_service",
2714 	                "a_reason",
2715 	                instance="an_instance",
2716 	            ),
2717 	        )
2718 	
2719 	    def test_enable(self):
2720 	        self.assert_message_from_report(
2721 	            "Unable to enable a_service: a_reason",
2722 	            reports.ServiceActionFailed(
2723 	                const.SERVICE_ACTION_ENABLE, "a_service", "a_reason"
2724 	            ),
2725 	        )
2726 	
2727 	    def test_enable_instance(self):
2728 	        self.assert_message_from_report(
2729 	            "Unable to enable a_service@an_instance: a_reason",
2730 	            reports.ServiceActionFailed(
2731 	                const.SERVICE_ACTION_ENABLE,
2732 	                "a_service",
2733 	                "a_reason",
2734 	                instance="an_instance",
2735 	            ),
2736 	        )
2737 	
2738 	    def test_disable(self):
2739 	        self.assert_message_from_report(
2740 	            "Unable to disable a_service: a_reason",
2741 	            reports.ServiceActionFailed(
2742 	                const.SERVICE_ACTION_DISABLE, "a_service", "a_reason"
2743 	            ),
2744 	        )
2745 	
2746 	    def test_disable_instance(self):
2747 	        self.assert_message_from_report(
2748 	            "Unable to disable a_service@an_instance: a_reason",
2749 	            reports.ServiceActionFailed(
2750 	                const.SERVICE_ACTION_DISABLE,
2751 	                "a_service",
2752 	                "a_reason",
2753 	                instance="an_instance",
2754 	            ),
2755 	        )
2756 	
2757 	    def test_kill(self):
2758 	        self.assert_message_from_report(
2759 	            "Unable to kill a_service: a_reason",
2760 	            reports.ServiceActionFailed(
2761 	                const.SERVICE_ACTION_KILL, "a_service", "a_reason"
2762 	            ),
2763 	        )
2764 	
2765 	    def test_kill_instance(self):
2766 	        self.assert_message_from_report(
2767 	            "Unable to kill a_service@an_instance: a_reason",
2768 	            reports.ServiceActionFailed(
2769 	                const.SERVICE_ACTION_KILL,
2770 	                "a_service",
2771 	                "a_reason",
2772 	                instance="an_instance",
2773 	            ),
2774 	        )
2775 	
2776 	
2777 	# TODO: add tests for node if needed
2778 	class ServiceActionSucceeded(NameBuildTest):
2779 	    def test_start(self):
2780 	        self.assert_message_from_report(
2781 	            "a_service started",
2782 	            reports.ServiceActionSucceeded(
2783 	                const.SERVICE_ACTION_START, "a_service"
2784 	            ),
2785 	        )
2786 	
2787 	    def test_start_instance(self):
2788 	        self.assert_message_from_report(
2789 	            "a_service@an_instance started",
2790 	            reports.ServiceActionSucceeded(
2791 	                const.SERVICE_ACTION_START, "a_service", instance="an_instance"
2792 	            ),
2793 	        )
2794 	
2795 	    def test_stop(self):
2796 	        self.assert_message_from_report(
2797 	            "a_service stopped",
2798 	            reports.ServiceActionSucceeded(
2799 	                const.SERVICE_ACTION_STOP, "a_service"
2800 	            ),
2801 	        )
2802 	
2803 	    def test_stop_instance(self):
2804 	        self.assert_message_from_report(
2805 	            "a_service@an_instance stopped",
2806 	            reports.ServiceActionSucceeded(
2807 	                const.SERVICE_ACTION_STOP, "a_service", instance="an_instance"
2808 	            ),
2809 	        )
2810 	
2811 	    def test_enable(self):
2812 	        self.assert_message_from_report(
2813 	            "a_service enabled",
2814 	            reports.ServiceActionSucceeded(
2815 	                const.SERVICE_ACTION_ENABLE, "a_service"
2816 	            ),
2817 	        )
2818 	
2819 	    def test_enable_instance(self):
2820 	        self.assert_message_from_report(
2821 	            "a_service@an_instance enabled",
2822 	            reports.ServiceActionSucceeded(
2823 	                const.SERVICE_ACTION_ENABLE, "a_service", instance="an_instance"
2824 	            ),
2825 	        )
2826 	
2827 	    def test_disable(self):
2828 	        self.assert_message_from_report(
2829 	            "a_service disabled",
2830 	            reports.ServiceActionSucceeded(
2831 	                const.SERVICE_ACTION_DISABLE, "a_service"
2832 	            ),
2833 	        )
2834 	
2835 	    def test_disable_instance(self):
2836 	        self.assert_message_from_report(
2837 	            "a_service@an_instance disabled",
2838 	            reports.ServiceActionSucceeded(
2839 	                const.SERVICE_ACTION_DISABLE,
2840 	                "a_service",
2841 	                instance="an_instance",
2842 	            ),
2843 	        )
2844 	
2845 	    def test_kill(self):
2846 	        self.assert_message_from_report(
2847 	            "a_service killed",
2848 	            reports.ServiceActionSucceeded(
2849 	                const.SERVICE_ACTION_KILL, "a_service"
2850 	            ),
2851 	        )
2852 	
2853 	    def test_kill_instance(self):
2854 	        self.assert_message_from_report(
2855 	            "a_service@an_instance killed",
2856 	            reports.ServiceActionSucceeded(
2857 	                const.SERVICE_ACTION_KILL, "a_service", instance="an_instance"
2858 	            ),
2859 	        )
2860 	
2861 	
2862 	class ServiceActionSkipped(NameBuildTest):
2863 	    def test_start(self):
2864 	        self.assert_message_from_report(
2865 	            "not starting a_service: a_reason",
2866 	            reports.ServiceActionSkipped(
2867 	                const.SERVICE_ACTION_START, "a_service", "a_reason"
2868 	            ),
2869 	        )
2870 	
2871 	    def test_start_instance(self):
2872 	        self.assert_message_from_report(
2873 	            "not starting a_service@an_instance: a_reason",
2874 	            reports.ServiceActionSkipped(
2875 	                const.SERVICE_ACTION_START,
2876 	                "a_service",
2877 	                "a_reason",
2878 	                instance="an_instance",
2879 	            ),
2880 	        )
2881 	
2882 	    def test_stop(self):
2883 	        self.assert_message_from_report(
2884 	            "not stopping a_service: a_reason",
2885 	            reports.ServiceActionSkipped(
2886 	                const.SERVICE_ACTION_STOP, "a_service", "a_reason"
2887 	            ),
2888 	        )
2889 	
2890 	    def test_stop_instance(self):
2891 	        self.assert_message_from_report(
2892 	            "not stopping a_service@an_instance: a_reason",
2893 	            reports.ServiceActionSkipped(
2894 	                const.SERVICE_ACTION_STOP,
2895 	                "a_service",
2896 	                "a_reason",
2897 	                instance="an_instance",
2898 	            ),
2899 	        )
2900 	
2901 	    def test_enable(self):
2902 	        self.assert_message_from_report(
2903 	            "not enabling a_service: a_reason",
2904 	            reports.ServiceActionSkipped(
2905 	                const.SERVICE_ACTION_ENABLE, "a_service", "a_reason"
2906 	            ),
2907 	        )
2908 	
2909 	    def test_enable_instance(self):
2910 	        self.assert_message_from_report(
2911 	            "not enabling a_service@an_instance: a_reason",
2912 	            reports.ServiceActionSkipped(
2913 	                const.SERVICE_ACTION_ENABLE,
2914 	                "a_service",
2915 	                "a_reason",
2916 	                instance="an_instance",
2917 	            ),
2918 	        )
2919 	
2920 	    def test_disable(self):
2921 	        self.assert_message_from_report(
2922 	            "not disabling a_service: a_reason",
2923 	            reports.ServiceActionSkipped(
2924 	                const.SERVICE_ACTION_DISABLE, "a_service", "a_reason"
2925 	            ),
2926 	        )
2927 	
2928 	    def test_disable_instance(self):
2929 	        self.assert_message_from_report(
2930 	            "not disabling a_service@an_instance: a_reason",
2931 	            reports.ServiceActionSkipped(
2932 	                const.SERVICE_ACTION_DISABLE,
2933 	                "a_service",
2934 	                "a_reason",
2935 	                instance="an_instance",
2936 	            ),
2937 	        )
2938 	
2939 	    def test_kill(self):
2940 	        self.assert_message_from_report(
2941 	            "not killing a_service: a_reason",
2942 	            reports.ServiceActionSkipped(
2943 	                const.SERVICE_ACTION_KILL, "a_service", "a_reason"
2944 	            ),
2945 	        )
2946 	
2947 	    def test_kill_instance(self):
2948 	        self.assert_message_from_report(
2949 	            "not killing a_service@an_instance: a_reason",
2950 	            reports.ServiceActionSkipped(
2951 	                const.SERVICE_ACTION_KILL,
2952 	                "a_service",
2953 	                "a_reason",
2954 	                instance="an_instance",
2955 	            ),
2956 	        )
2957 	
2958 	
2959 	class ServiceUnableToDetectInitSystem(NameBuildTest):
2960 	    def test_success(self):
2961 	        self.assert_message_from_report(
2962 	            (
2963 	                "Unable to detect init system. All actions related to system "
2964 	                "services will be skipped."
2965 	            ),
2966 	            reports.ServiceUnableToDetectInitSystem(),
2967 	        )
2968 	
2969 	
2970 	class UnableToGetAgentMetadata(NameBuildTest):
2971 	    def test_all(self):
2972 	        self.assert_message_from_report(
2973 	            (
2974 	                "Agent 'agent-name' is not installed or does not provide valid "
2975 	                "metadata: reason"
2976 	            ),
2977 	            reports.UnableToGetAgentMetadata("agent-name", "reason"),
2978 	        )
2979 	
2980 	
2981 	class InvalidResourceAgentName(NameBuildTest):
2982 	    def test_build_message_with_data(self):
2983 	        self.assert_message_from_report(
2984 	            "Invalid resource agent name ':name'. Use standard:provider:type "
2985 	            "when standard is 'ocf' or standard:type otherwise.",
2986 	            reports.InvalidResourceAgentName(":name"),
2987 	        )
2988 	
2989 	
2990 	class InvalidStonithAgentName(NameBuildTest):
2991 	    def test_build_message_with_data(self):
2992 	        self.assert_message_from_report(
2993 	            "Invalid stonith agent name 'fence:name'. Agent name cannot contain "
2994 	            "the ':' character, do not use the 'stonith:' prefix.",
2995 	            reports.InvalidStonithAgentName("fence:name"),
2996 	        )
2997 	
2998 	
2999 	class AgentNameGuessed(NameBuildTest):
3000 	    def test_build_message_with_data(self):
3001 	        self.assert_message_from_report(
3002 	            "Assumed agent name 'ocf:heartbeat:Delay' (deduced from 'Delay')",
3003 	            reports.AgentNameGuessed("Delay", "ocf:heartbeat:Delay"),
3004 	        )
3005 	
3006 	
3007 	class AgentNameGuessFoundMoreThanOne(NameBuildTest):
3008 	    def test_all(self):
3009 	        self.assert_message_from_report(
3010 	            (
3011 	                "Multiple agents match 'agent', please specify full name: "
3012 	                "'agent1', 'agent2' or 'agent3'"
3013 	            ),
3014 	            reports.AgentNameGuessFoundMoreThanOne(
3015 	                "agent", ["agent2", "agent1", "agent3"]
3016 	            ),
3017 	        )
3018 	
3019 	
3020 	class AgentNameGuessFoundNone(NameBuildTest):
3021 	    def test_all(self):
3022 	        self.assert_message_from_report(
3023 	            "Unable to find agent 'agent-name', try specifying its full name",
3024 	            reports.AgentNameGuessFoundNone("agent-name"),
3025 	        )
3026 	
3027 	
3028 	class AgentImplementsUnsupportedOcfVersion(NameBuildTest):
3029 	    def test_singular(self):
3030 	        self.assert_message_from_report(
3031 	            "Unable to process agent 'agent-name' as it implements unsupported "
3032 	            "OCF version 'ocf-2.3', supported version is: 'v1'",
3033 	            reports.AgentImplementsUnsupportedOcfVersion(
3034 	                "agent-name", "ocf-2.3", ["v1"]
3035 	            ),
3036 	        )
3037 	
3038 	    def test_plural(self):
3039 	        self.assert_message_from_report(
3040 	            "Unable to process agent 'agent-name' as it implements unsupported "
3041 	            "OCF version 'ocf-2.3', supported versions are: 'v1', 'v2', 'v3'",
3042 	            reports.AgentImplementsUnsupportedOcfVersion(
3043 	                "agent-name", "ocf-2.3", ["v1", "v2", "v3"]
3044 	            ),
3045 	        )
3046 	
3047 	
3048 	class AgentGenericError(NameBuildTest):
3049 	    def test_success(self):
3050 	        self.assert_message_from_report(
3051 	            "Unable to load agent 'agent-name'",
3052 	            reports.AgentGenericError("agent-name"),
3053 	        )
3054 	
3055 	
3056 	class OmittingNode(NameBuildTest):
3057 	    def test_all(self):
3058 	        self.assert_message_from_report(
3059 	            "Omitting node 'node1'", reports.OmittingNode("node1")
3060 	        )
3061 	
3062 	
3063 	class SbdCheckStarted(NameBuildTest):
3064 	    def test_all(self):
3065 	        self.assert_message_from_report(
3066 	            "Running SBD pre-enabling checks...", reports.SbdCheckStarted()
3067 	        )
3068 	
3069 	
3070 	class SbdCheckSuccess(NameBuildTest):
3071 	    def test_all(self):
3072 	        self.assert_message_from_report(
3073 	            "node1: SBD pre-enabling checks done",
3074 	            reports.SbdCheckSuccess("node1"),
3075 	        )
3076 	
3077 	
3078 	class SbdConfigDistributionStarted(NameBuildTest):
3079 	    def test_all(self):
3080 	        self.assert_message_from_report(
3081 	            "Distributing SBD config...", reports.SbdConfigDistributionStarted()
3082 	        )
3083 	
3084 	
3085 	class SbdConfigAcceptedByNode(NameBuildTest):
3086 	    def test_all(self):
3087 	        self.assert_message_from_report(
3088 	            "node1: SBD config saved", reports.SbdConfigAcceptedByNode("node1")
3089 	        )
3090 	
3091 	
3092 	class UnableToGetSbdConfig(NameBuildTest):
3093 	    def test_no_reason(self):
3094 	        self.assert_message_from_report(
3095 	            "Unable to get SBD configuration from node 'node1'",
3096 	            reports.UnableToGetSbdConfig("node1", ""),
3097 	        )
3098 	
3099 	    def test_all(self):
3100 	        self.assert_message_from_report(
3101 	            "Unable to get SBD configuration from node 'node2': reason",
3102 	            reports.UnableToGetSbdConfig("node2", "reason"),
3103 	        )
3104 	
3105 	
3106 	class SbdDeviceInitializationStarted(NameBuildTest):
3107 	    def test_more_devices(self):
3108 	        self.assert_message_from_report(
3109 	            "Initializing devices '/dev1', '/dev2', '/dev3'...",
3110 	            reports.SbdDeviceInitializationStarted(["/dev3", "/dev2", "/dev1"]),
3111 	        )
3112 	
3113 	    def test_one_device(self):
3114 	        self.assert_message_from_report(
3115 	            "Initializing device '/dev1'...",
3116 	            reports.SbdDeviceInitializationStarted(["/dev1"]),
3117 	        )
3118 	
3119 	
3120 	class SbdDeviceInitializationSuccess(NameBuildTest):
3121 	    def test_more_devices(self):
3122 	        self.assert_message_from_report(
3123 	            "Devices initialized successfully",
3124 	            reports.SbdDeviceInitializationSuccess(["/dev2", "/dev1"]),
3125 	        )
3126 	
3127 	    def test_one_device(self):
3128 	        self.assert_message_from_report(
3129 	            "Device initialized successfully",
3130 	            reports.SbdDeviceInitializationSuccess(["/dev1"]),
3131 	        )
3132 	
3133 	
3134 	class SbdDeviceInitializationError(NameBuildTest):
3135 	    def test_more_devices(self):
3136 	        self.assert_message_from_report(
3137 	            "Initialization of devices '/dev1', '/dev2' failed: this is reason",
3138 	            reports.SbdDeviceInitializationError(
3139 	                ["/dev2", "/dev1"], "this is reason"
3140 	            ),
3141 	        )
3142 	
3143 	    def test_one_device(self):
3144 	        self.assert_message_from_report(
3145 	            "Initialization of device '/dev2' failed: this is reason",
3146 	            reports.SbdDeviceInitializationError(["/dev2"], "this is reason"),
3147 	        )
3148 	
3149 	
3150 	class SbdDeviceListError(NameBuildTest):
3151 	    def test_build_message(self):
3152 	        self.assert_message_from_report(
3153 	            "Unable to get list of messages from device '/dev': this is reason",
3154 	            reports.SbdDeviceListError("/dev", "this is reason"),
3155 	        )
3156 	
3157 	
3158 	class SbdDeviceMessageError(NameBuildTest):
3159 	    def test_build_message(self):
3160 	        self.assert_message_from_report(
3161 	            (
3162 	                "Unable to set message 'test' for node 'node1' on device "
3163 	                "'/dev1': this is reason"
3164 	            ),
3165 	            reports.SbdDeviceMessageError(
3166 	                "/dev1", "node1", "test", "this is reason"
3167 	            ),
3168 	        )
3169 	
3170 	
3171 	class SbdDeviceDumpError(NameBuildTest):
3172 	    def test_build_message(self):
3173 	        self.assert_message_from_report(
3174 	            "Unable to get SBD headers from device '/dev1': this is reason",
3175 	            reports.SbdDeviceDumpError("/dev1", "this is reason"),
3176 	        )
3177 	
3178 	
3179 	class FilesDistributionStarted(NameBuildTest):
3180 	    def test_build_messages(self):
3181 	        self.assert_message_from_report(
3182 	            "Sending 'first', 'second'",
3183 	            reports.FilesDistributionStarted(["first", "second"]),
3184 	        )
3185 	
3186 	    def test_build_messages_with_single_node(self):
3187 	        self.assert_message_from_report(
3188 	            "Sending 'first' to 'node1'",
3189 	            reports.FilesDistributionStarted(["first"], ["node1"]),
3190 	        )
3191 	
3192 	    def test_build_messages_with_nodes(self):
3193 	        self.assert_message_from_report(
3194 	            "Sending 'first', 'second' to 'node1', 'node2'",
3195 	            reports.FilesDistributionStarted(
3196 	                ["first", "second"], ["node1", "node2"]
3197 	            ),
3198 	        )
3199 	
3200 	
3201 	class FilesDistributionSkipped(NameBuildTest):
3202 	    def test_not_live(self):
3203 	        self.assert_message_from_report(
3204 	            "Distribution of 'file1' to 'nodeA', 'nodeB' was skipped because "
3205 	            "the command does not run on a live cluster. "
3206 	            "Please, distribute the file(s) manually.",
3207 	            reports.FilesDistributionSkipped(
3208 	                const.REASON_NOT_LIVE_CIB, ["file1"], ["nodeA", "nodeB"]
3209 	            ),
3210 	        )
3211 	
3212 	    def test_unreachable(self):
3213 	        self.assert_message_from_report(
3214 	            "Distribution of 'file1', 'file2' to 'nodeA' was skipped because "
3215 	            "pcs is unable to connect to the node(s). Please, distribute "
3216 	            "the file(s) manually.",
3217 	            reports.FilesDistributionSkipped(
3218 	                const.REASON_UNREACHABLE, ["file1", "file2"], ["nodeA"]
3219 	            ),
3220 	        )
3221 	
3222 	    def test_unknown_reason(self):
3223 	        self.assert_message_from_report(
3224 	            "Distribution of 'file1', 'file2' to 'nodeA', 'nodeB' was skipped "
3225 	            "because some undefined reason. Please, distribute the file(s) "
3226 	            "manually.",
3227 	            reports.FilesDistributionSkipped(
3228 	                "some undefined reason", ["file1", "file2"], ["nodeA", "nodeB"]
3229 	            ),
3230 	        )
3231 	
3232 	
3233 	class FileDistributionSuccess(NameBuildTest):
3234 	    def test_build_messages(self):
3235 	        self.assert_message_from_report(
3236 	            "node1: successful distribution of the file 'some authfile'",
3237 	            reports.FileDistributionSuccess("node1", "some authfile"),
3238 	        )
3239 	
3240 	
3241 	class FileDistributionError(NameBuildTest):
3242 	    def test_build_messages(self):
3243 	        self.assert_message_from_report(
3244 	            "node1: unable to distribute file 'file1': permission denied",
3245 	            reports.FileDistributionError(
3246 	                "node1", "file1", "permission denied"
3247 	            ),
3248 	        )
3249 	
3250 	
3251 	class FilesRemoveFromNodesStarted(NameBuildTest):
3252 	    def test_minimal(self):
3253 	        self.assert_message_from_report(
3254 	            "Requesting remove 'file'",
3255 	            reports.FilesRemoveFromNodesStarted(["file"]),
3256 	        )
3257 	
3258 	    def test_with_single_node(self):
3259 	        self.assert_message_from_report(
3260 	            "Requesting remove 'first' from 'node1'",
3261 	            reports.FilesRemoveFromNodesStarted(["first"], ["node1"]),
3262 	        )
3263 	
3264 	    def test_with_multiple_nodes(self):
3265 	        self.assert_message_from_report(
3266 	            "Requesting remove 'first', 'second' from 'node1', 'node2'",
3267 	            reports.FilesRemoveFromNodesStarted(
3268 	                ["first", "second"],
3269 	                ["node1", "node2"],
3270 	            ),
3271 	        )
3272 	
3273 	
3274 	class FilesRemoveFromNodesSkipped(NameBuildTest):
3275 	    def test_not_live(self):
3276 	        self.assert_message_from_report(
3277 	            "Removing 'file1' from 'nodeA', 'nodeB' was skipped because the "
3278 	            "command does not run on a live cluster. "
3279 	            "Please, remove the file(s) manually.",
3280 	            reports.FilesRemoveFromNodesSkipped(
3281 	                const.REASON_NOT_LIVE_CIB, ["file1"], ["nodeA", "nodeB"]
3282 	            ),
3283 	        )
3284 	
3285 	    def test_unreachable(self):
3286 	        self.assert_message_from_report(
3287 	            "Removing 'file1', 'file2' from 'nodeA' was skipped because pcs is "
3288 	            "unable to connect to the node(s). Please, remove the file(s) "
3289 	            "manually.",
3290 	            reports.FilesRemoveFromNodesSkipped(
3291 	                const.REASON_UNREACHABLE, ["file1", "file2"], ["nodeA"]
3292 	            ),
3293 	        )
3294 	
3295 	    def test_unknown_reason(self):
3296 	        self.assert_message_from_report(
3297 	            "Removing 'file1', 'file2' from 'nodeA', 'nodeB' was skipped "
3298 	            "because some undefined reason. Please, remove the file(s) "
3299 	            "manually.",
3300 	            reports.FilesRemoveFromNodesSkipped(
3301 	                "some undefined reason", ["file1", "file2"], ["nodeA", "nodeB"]
3302 	            ),
3303 	        )
3304 	
3305 	
3306 	class FileRemoveFromNodeSuccess(NameBuildTest):
3307 	    def test_build_messages(self):
3308 	        self.assert_message_from_report(
3309 	            "node1: successful removal of the file 'some authfile'",
3310 	            reports.FileRemoveFromNodeSuccess("node1", "some authfile"),
3311 	        )
3312 	
3313 	
3314 	class FileRemoveFromNodeError(NameBuildTest):
3315 	    def test_build_messages(self):
3316 	        self.assert_message_from_report(
3317 	            "node1: unable to remove file 'file1': permission denied",
3318 	            reports.FileRemoveFromNodeError(
3319 	                "node1", "file1", "permission denied"
3320 	            ),
3321 	        )
3322 	
3323 	
3324 	class ServiceCommandsOnNodesStarted(NameBuildTest):
3325 	    def test_build_messages(self):
3326 	        self.assert_message_from_report(
3327 	            "Requesting 'action1', 'action2'",
3328 	            reports.ServiceCommandsOnNodesStarted(["action1", "action2"]),
3329 	        )
3330 	
3331 	    def test_build_messages_with_single_node(self):
3332 	        self.assert_message_from_report(
3333 	            "Requesting 'action1' on 'node1'",
3334 	            reports.ServiceCommandsOnNodesStarted(
3335 	                ["action1"],
3336 	                ["node1"],
3337 	            ),
3338 	        )
3339 	
3340 	    def test_build_messages_with_nodes(self):
3341 	        self.assert_message_from_report(
3342 	            "Requesting 'action1', 'action2' on 'node1', 'node2'",
3343 	            reports.ServiceCommandsOnNodesStarted(
3344 	                ["action1", "action2"],
3345 	                ["node1", "node2"],
3346 	            ),
3347 	        )
3348 	
3349 	
3350 	class ServiceCommandsOnNodesSkipped(NameBuildTest):
3351 	    def test_not_live(self):
3352 	        self.assert_message_from_report(
3353 	            "Running action(s) 'pacemaker_remote enable', 'pacemaker_remote "
3354 	            "start' on 'nodeA', 'nodeB' was skipped because the command "
3355 	            "does not run on a live cluster. Please, "
3356 	            "run the action(s) manually.",
3357 	            reports.ServiceCommandsOnNodesSkipped(
3358 	                const.REASON_NOT_LIVE_CIB,
3359 	                ["pacemaker_remote enable", "pacemaker_remote start"],
3360 	                ["nodeA", "nodeB"],
3361 	            ),
3362 	        )
3363 	
3364 	    def test_unreachable(self):
3365 	        self.assert_message_from_report(
3366 	            "Running action(s) 'pacemaker_remote enable', 'pacemaker_remote "
3367 	            "start' on 'nodeA', 'nodeB' was skipped because pcs is unable "
3368 	            "to connect to the node(s). Please, run the action(s) manually.",
3369 	            reports.ServiceCommandsOnNodesSkipped(
3370 	                const.REASON_UNREACHABLE,
3371 	                ["pacemaker_remote enable", "pacemaker_remote start"],
3372 	                ["nodeA", "nodeB"],
3373 	            ),
3374 	        )
3375 	
3376 	    def test_unknown_reason(self):
3377 	        self.assert_message_from_report(
3378 	            "Running action(s) 'pacemaker_remote enable', 'pacemaker_remote "
3379 	            "start' on 'nodeA', 'nodeB' was skipped because some undefined "
3380 	            "reason. Please, run the action(s) manually.",
3381 	            reports.ServiceCommandsOnNodesSkipped(
3382 	                "some undefined reason",
3383 	                ["pacemaker_remote enable", "pacemaker_remote start"],
3384 	                ["nodeA", "nodeB"],
3385 	            ),
3386 	        )
3387 	
3388 	
3389 	class ServiceCommandOnNodeSuccess(NameBuildTest):
3390 	    def test_build_messages(self):
3391 	        self.assert_message_from_report(
3392 	            "node1: successful run of 'service enable'",
3393 	            reports.ServiceCommandOnNodeSuccess("node1", "service enable"),
3394 	        )
3395 	
3396 	
3397 	class ServiceCommandOnNodeError(NameBuildTest):
3398 	    def test_build_messages(self):
3399 	        self.assert_message_from_report(
3400 	            "node1: service command failed: service1 start: permission denied",
3401 	            reports.ServiceCommandOnNodeError(
3402 	                "node1", "service1 start", "permission denied"
3403 	            ),
3404 	        )
3405 	
3406 	
3407 	class InvalidResponseFormat(NameBuildTest):
3408 	    def test_all(self):
3409 	        self.assert_message_from_report(
3410 	            "node1: Invalid format of response",
3411 	            reports.InvalidResponseFormat("node1"),
3412 	        )
3413 	
3414 	
3415 	class SbdNotUsedCannotSetSbdOptions(NameBuildTest):
3416 	    def test_single_option(self):
3417 	        self.assert_message_from_report(
3418 	            "Cluster is not configured to use SBD, cannot specify SBD option(s)"
3419 	            " 'device' for node 'node1'",
3420 	            reports.SbdNotUsedCannotSetSbdOptions(["device"], "node1"),
3421 	        )
3422 	
3423 	    def test_multiple_options(self):
3424 	        self.assert_message_from_report(
3425 	            "Cluster is not configured to use SBD, cannot specify SBD option(s)"
3426 	            " 'device', 'watchdog' for node 'node1'",
3427 	            reports.SbdNotUsedCannotSetSbdOptions(
3428 	                ["device", "watchdog"], "node1"
3429 	            ),
3430 	        )
3431 	
3432 	
3433 	class SbdWithDevicesNotUsedCannotSetDevice(NameBuildTest):
3434 	    def test_success(self):
3435 	        self.assert_message_from_report(
3436 	            "Cluster is not configured to use SBD with shared storage, cannot "
3437 	            "specify SBD devices for node 'node1'",
3438 	            reports.SbdWithDevicesNotUsedCannotSetDevice("node1"),
3439 	        )
3440 	
3441 	
3442 	class SbdNoDeviceForNode(NameBuildTest):
3443 	    def test_not_enabled(self):
3444 	        self.assert_message_from_report(
3445 	            "No SBD device specified for node 'node1'",
3446 	            reports.SbdNoDeviceForNode("node1"),
3447 	        )
3448 	
3449 	    def test_enabled(self):
3450 	        self.assert_message_from_report(
3451 	            "Cluster uses SBD with shared storage so SBD devices must be "
3452 	            "specified for all nodes, no device specified for node 'node1'",
3453 	            reports.SbdNoDeviceForNode("node1", sbd_enabled_in_cluster=True),
3454 	        )
3455 	
3456 	
3457 	class SbdTooManyDevicesForNode(NameBuildTest):
3458 	    def test_build_messages(self):
3459 	        self.assert_message_from_report(
3460 	            "At most 3 SBD devices can be specified for a node, '/dev1', "
3461 	            "'/dev2', '/dev3' specified for node 'node1'",
3462 	            reports.SbdTooManyDevicesForNode(
3463 	                "node1", ["/dev1", "/dev3", "/dev2"], 3
3464 	            ),
3465 	        )
3466 	
3467 	
3468 	class SbdDevicePathNotAbsolute(NameBuildTest):
3469 	    def test_build_message(self):
3470 	        self.assert_message_from_report(
3471 	            "Device path '/dev' on node 'node1' is not absolute",
3472 	            reports.SbdDevicePathNotAbsolute("/dev", "node1"),
3473 	        )
3474 	
3475 	
3476 	class SbdDeviceDoesNotExist(NameBuildTest):
3477 	    def test_build_message(self):
3478 	        self.assert_message_from_report(
3479 	            "node1: device '/dev' not found",
3480 	            reports.SbdDeviceDoesNotExist("/dev", "node1"),
3481 	        )
3482 	
3483 	
3484 	class SbdDeviceIsNotBlockDevice(NameBuildTest):
3485 	    def test_build_message(self):
3486 	        self.assert_message_from_report(
3487 	            "node1: device '/dev' is not a block device",
3488 	            reports.SbdDeviceIsNotBlockDevice("/dev", "node1"),
3489 	        )
3490 	
3491 	
3492 	class StonithWatchdogTimeoutCannotBeSet(NameBuildTest):
3493 	    def test_sbd_not_enabled(self):
3494 	        self.assert_message_from_report(
3495 	            "fencing-watchdog-timeout / stonith-watchdog-timeout can only be "
3496 	            "unset or set to 0 while SBD is disabled",
3497 	            reports.StonithWatchdogTimeoutCannotBeSet(
3498 	                reports.const.SBD_NOT_SET_UP
3499 	            ),
3500 	        )
3501 	
3502 	    def test_sbd_with_devices(self):
3503 	        self.assert_message_from_report(
3504 	            "fencing-watchdog-timeout / stonith-watchdog-timeout can only be "
3505 	            "unset or set to 0 while SBD is enabled with devices",
3506 	            reports.StonithWatchdogTimeoutCannotBeSet(
3507 	                reports.const.SBD_SET_UP_WITH_DEVICES
3508 	            ),
3509 	        )
3510 	
3511 	
3512 	class StonithWatchdogTimeoutCannotBeUnset(NameBuildTest):
3513 	    def test_sbd_without_devices(self):
3514 	        self.assert_message_from_report(
3515 	            "fencing-watchdog-timeout / stonith-watchdog-timeout cannot be "
3516 	            "unset or set to 0 while SBD is enabled without devices",
3517 	            reports.StonithWatchdogTimeoutCannotBeUnset(
3518 	                reports.const.SBD_SET_UP_WITHOUT_DEVICES
3519 	            ),
3520 	        )
3521 	
3522 	
3523 	class StonithWatchdogTimeoutTooSmall(NameBuildTest):
3524 	    def test_all(self):
3525 	        self.assert_message_from_report(
3526 	            "The fencing-watchdog-timeout / stonith-watchdog-timeout must be "
3527 	            "greater than SBD watchdog timeout '5', entered '4'",
3528 	            reports.StonithWatchdogTimeoutTooSmall(5, "4"),
3529 	        )
3530 	
3531 	
3532 	class WatchdogNotFound(NameBuildTest):
3533 	    def test_all(self):
3534 	        self.assert_message_from_report(
3535 	            "Watchdog 'watchdog-name' does not exist on node 'node1'",
3536 	            reports.WatchdogNotFound("node1", "watchdog-name"),
3537 	        )
3538 	
3539 	
3540 	class WatchdogInvalid(NameBuildTest):
3541 	    def test_all(self):
3542 	        self.assert_message_from_report(
3543 	            "Watchdog path '/dev/wdog' is invalid.",
3544 	            reports.WatchdogInvalid("/dev/wdog"),
3545 	        )
3546 	
3547 	
3548 	class UnableToGetSbdStatus(NameBuildTest):
3549 	    def test_no_reason(self):
3550 	        self.assert_message_from_report(
3551 	            "Unable to get status of SBD from node 'node1'",
3552 	            reports.UnableToGetSbdStatus("node1", ""),
3553 	        )
3554 	
3555 	    def test_all(self):
3556 	        self.assert_message_from_report(
3557 	            "Unable to get status of SBD from node 'node2': reason",
3558 	            reports.UnableToGetSbdStatus("node2", "reason"),
3559 	        )
3560 	
3561 	
3562 	class ClusterRestartRequiredToApplyChanges(NameBuildTest):
3563 	    def test_all(self):
3564 	        self.assert_message_from_report(
3565 	            "Cluster restart is required in order to apply these changes.",
3566 	            reports.ClusterRestartRequiredToApplyChanges(),
3567 	        )
3568 	
3569 	
3570 	class CibAlertRecipientAlreadyExists(NameBuildTest):
3571 	    def test_all(self):
3572 	        self.assert_message_from_report(
3573 	            "Recipient 'recipient' in alert 'alert-id' already exists",
3574 	            reports.CibAlertRecipientAlreadyExists("alert-id", "recipient"),
3575 	        )
3576 	
3577 	
3578 	class CibAlertRecipientValueInvalid(NameBuildTest):
3579 	    def test_all(self):
3580 	        self.assert_message_from_report(
3581 	            "Recipient value 'recipient' is not valid.",
3582 	            reports.CibAlertRecipientValueInvalid("recipient"),
3583 	        )
3584 	
3585 	
3586 	class CibUpgradeSuccessful(NameBuildTest):
3587 	    def test_all(self):
3588 	        self.assert_message_from_report(
3589 	            "CIB has been upgraded to the latest schema version.",
3590 	            reports.CibUpgradeSuccessful(),
3591 	        )
3592 	
3593 	
3594 	class CibUpgradeFailed(NameBuildTest):
3595 	    def test_all(self):
3596 	        self.assert_message_from_report(
3597 	            "Upgrading of CIB to the latest schema failed: reason",
3598 	            reports.CibUpgradeFailed("reason"),
3599 	        )
3600 	
3601 	
3602 	class CibUpgradeFailedToMinimalRequiredVersion(NameBuildTest):
3603 	    def test_all(self):
3604 	        self.assert_message_from_report(
3605 	            (
3606 	                "Unable to upgrade CIB to required schema version"
3607 	                " 1.1 or higher. Current version is"
3608 	                " 0.8. Newer version of pacemaker is needed."
3609 	            ),
3610 	            reports.CibUpgradeFailedToMinimalRequiredVersion("0.8", "1.1"),
3611 	        )
3612 	
3613 	
3614 	class FileAlreadyExists(NameBuildTest):
3615 	    def test_minimal(self):
3616 	        self.assert_message_from_report(
3617 	            "Corosync authkey file '/corosync_conf/path' already exists",
3618 	            reports.FileAlreadyExists(
3619 	                "COROSYNC_AUTHKEY", "/corosync_conf/path"
3620 	            ),
3621 	        )
3622 	
3623 	    def test_with_node(self):
3624 	        self.assert_message_from_report(
3625 	            "node1: pcs configuration file '/pcs/conf/file' already exists",
3626 	            reports.FileAlreadyExists(
3627 	                "PCS_SETTINGS_CONF", "/pcs/conf/file", node="node1"
3628 	            ),
3629 	        )
3630 	
3631 	
3632 	class FileIoError(NameBuildTest):
3633 	    def test_minimal(self):
3634 	        self.assert_message_from_report(
3635 	            "Unable to read Booth configuration: ",
3636 	            reports.FileIoError(
3637 	                file_type_codes.BOOTH_CONFIG, RawFileError.ACTION_READ, ""
3638 	            ),
3639 	        )
3640 	
3641 	    def test_all(self):
3642 	        self.assert_message_from_report(
3643 	            "Unable to read pcsd SSL certificate '/ssl/cert/path': Failed",
3644 	            reports.FileIoError(
3645 	                file_type_codes.PCSD_SSL_CERT,
3646 	                RawFileError.ACTION_READ,
3647 	                "Failed",
3648 	                file_path="/ssl/cert/path",
3649 	            ),
3650 	        )
3651 	
3652 	    def test_role_translation_a(self):
3653 	        self.assert_message_from_report(
3654 	            "Unable to write pcsd SSL key '/ssl/key/path': Failed",
3655 	            reports.FileIoError(
3656 	                file_type_codes.PCSD_SSL_KEY,
3657 	                RawFileError.ACTION_WRITE,
3658 	                "Failed",
3659 	                file_path="/ssl/key/path",
3660 	            ),
3661 	        )
3662 	
3663 	    def test_role_translation_b(self):
3664 	        self.assert_message_from_report(
3665 	            (
3666 	                "Unable to change ownership of pcsd configuration "
3667 	                "'/pcsd/conf/path': Failed"
3668 	            ),
3669 	            reports.FileIoError(
3670 	                file_type_codes.PCSD_ENVIRONMENT_CONFIG,
3671 	                RawFileError.ACTION_CHOWN,
3672 	                "Failed",
3673 	                file_path="/pcsd/conf/path",
3674 	            ),
3675 	        )
3676 	
3677 	    def test_role_translation_c(self):
3678 	        self.assert_message_from_report(
3679 	            "Unable to change permissions of Corosync authkey: Failed",
3680 	            reports.FileIoError(
3681 	                file_type_codes.COROSYNC_AUTHKEY,
3682 	                RawFileError.ACTION_CHMOD,
3683 	                "Failed",
3684 	            ),
3685 	        )
3686 	
3687 	    def test_role_translation_d(self):
3688 	        self.assert_message_from_report(
3689 	            (
3690 	                "Unable to change ownership of pcs configuration: "
3691 	                "Permission denied"
3692 	            ),
3693 	            reports.FileIoError(
3694 	                file_type_codes.PCS_SETTINGS_CONF,
3695 	                RawFileError.ACTION_CHOWN,
3696 	                "Permission denied",
3697 	            ),
3698 	        )
3699 	
3700 	
3701 	class UnsupportedOperationOnNonSystemdSystems(NameBuildTest):
3702 	    def test_all(self):
3703 	        self.assert_message_from_report(
3704 	            "unsupported operation on non systemd systems",
3705 	            reports.UnsupportedOperationOnNonSystemdSystems(),
3706 	        )
3707 	
3708 	
3709 	class LiveEnvironmentRequired(NameBuildTest):
3710 	    def test_build_messages_transformable_codes(self):
3711 	        self.assert_message_from_report(
3712 	            "This command does not support passing '{}', '{}'".format(
3713 	                str(file_type_codes.CIB),
3714 	                str(file_type_codes.COROSYNC_CONF),
3715 	            ),
3716 	            reports.LiveEnvironmentRequired(
3717 	                [file_type_codes.COROSYNC_CONF, file_type_codes.CIB]
3718 	            ),
3719 	        )
3720 	
3721 	
3722 	class LiveEnvironmentRequiredForLocalNode(NameBuildTest):
3723 	    def test_all(self):
3724 	        self.assert_message_from_report(
3725 	            "Node(s) must be specified if mocked CIB is used",
3726 	            reports.LiveEnvironmentRequiredForLocalNode(),
3727 	        )
3728 	
3729 	
3730 	class LiveEnvironmentNotConsistent(NameBuildTest):
3731 	    def test_one_one(self):
3732 	        self.assert_message_from_report(
3733 	            "When '{}' is specified, '{}' must be specified as well".format(
3734 	                str(file_type_codes.BOOTH_CONFIG),
3735 	                str(file_type_codes.BOOTH_KEY),
3736 	            ),
3737 	            reports.LiveEnvironmentNotConsistent(
3738 	                [file_type_codes.BOOTH_CONFIG],
3739 	                [file_type_codes.BOOTH_KEY],
3740 	            ),
3741 	        )
3742 	
3743 	    def test_many_many(self):
3744 	        self.assert_message_from_report(
3745 	            (
3746 	                "When '{}', '{}' are specified, '{}', '{}' must be specified "
3747 	                "as well"
3748 	            ).format(
3749 	                str(file_type_codes.BOOTH_CONFIG),
3750 	                str(file_type_codes.CIB),
3751 	                str(file_type_codes.BOOTH_KEY),
3752 	                str(file_type_codes.COROSYNC_CONF),
3753 	            ),
3754 	            reports.LiveEnvironmentNotConsistent(
3755 	                [file_type_codes.CIB, file_type_codes.BOOTH_CONFIG],
3756 	                [file_type_codes.COROSYNC_CONF, file_type_codes.BOOTH_KEY],
3757 	            ),
3758 	        )
3759 	
3760 	
3761 	class CorosyncNodeConflictCheckSkipped(NameBuildTest):
3762 	    def test_success(self):
3763 	        self.assert_message_from_report(
3764 	            "Unable to check if there is a conflict with nodes set in corosync "
3765 	            "because the command does not run on a live cluster",
3766 	            reports.CorosyncNodeConflictCheckSkipped(const.REASON_NOT_LIVE_CIB),
3767 	        )
3768 	
3769 	
3770 	class CorosyncQuorumAtbCannotBeDisabledDueToSbd(NameBuildTest):
3771 	    def test_success(self):
3772 	        self.assert_message_from_report(
3773 	            (
3774 	                "Unable to disable auto_tie_breaker, SBD fencing would have no "
3775 	                "effect"
3776 	            ),
3777 	            reports.CorosyncQuorumAtbCannotBeDisabledDueToSbd(),
3778 	        )
3779 	
3780 	
3781 	class CorosyncQuorumAtbWillBeEnabledDueToSbd(NameBuildTest):
3782 	    def test_success(self):
3783 	        self.assert_message_from_report(
3784 	            (
3785 	                "SBD fencing is enabled in the cluster. To keep it effective, "
3786 	                "auto_tie_breaker quorum option will be enabled."
3787 	            ),
3788 	            reports.CorosyncQuorumAtbWillBeEnabledDueToSbd(),
3789 	        )
3790 	
3791 	
3792 	class CorosyncQuorumAtbWillBeEnabledDueToSbdClusterIsRunning(NameBuildTest):
3793 	    def test_success(self):
3794 	        self.assert_message_from_report(
3795 	            (
3796 	                "SBD fencing is enabled in the cluster. To keep it effective, "
3797 	                "auto_tie_breaker quorum option needs to be enabled. This can "
3798 	                "only be done when the cluster is stopped. To proceed, stop the "
3799 	                "cluster, enable auto_tie_breaker, and start the cluster. Then, "
3800 	                "repeat the requested action."
3801 	            ),
3802 	            reports.CorosyncQuorumAtbWillBeEnabledDueToSbdClusterIsRunning(),
3803 	        )
3804 	
3805 	
3806 	class CibAclRoleIsAlreadyAssignedToTarget(NameBuildTest):
3807 	    def test_all(self):
3808 	        self.assert_message_from_report(
3809 	            "Role 'role_id' is already assigned to 'target_id'",
3810 	            reports.CibAclRoleIsAlreadyAssignedToTarget("role_id", "target_id"),
3811 	        )
3812 	
3813 	
3814 	class CibAclRoleIsNotAssignedToTarget(NameBuildTest):
3815 	    def test_all(self):
3816 	        self.assert_message_from_report(
3817 	            "Role 'role_id' is not assigned to 'target_id'",
3818 	            reports.CibAclRoleIsNotAssignedToTarget("role_id", "target_id"),
3819 	        )
3820 	
3821 	
3822 	class CibAclTargetAlreadyExists(NameBuildTest):
3823 	    def test_all(self):
3824 	        self.assert_message_from_report(
3825 	            "'target_id' already exists",
3826 	            reports.CibAclTargetAlreadyExists("target_id"),
3827 	        )
3828 	
3829 	
3830 	class CibFencingLevelAlreadyExists(NameBuildTest):
3831 	    def test_target_node(self):
3832 	        self.assert_message_from_report(
3833 	            "Fencing level for 'nodeA' at level '1' with device(s) "
3834 	            "'device1', 'device2' already exists",
3835 	            reports.CibFencingLevelAlreadyExists(
3836 	                "1", TARGET_TYPE_NODE, "nodeA", ["device2", "device1"]
3837 	            ),
3838 	        )
3839 	
3840 	    def test_target_pattern(self):
3841 	        self.assert_message_from_report(
3842 	            "Fencing level for 'node-\\d+' at level '1' with device(s) "
3843 	            "'device1', 'device2' already exists",
3844 	            reports.CibFencingLevelAlreadyExists(
3845 	                "1", TARGET_TYPE_REGEXP, "node-\\d+", ["device1", "device2"]
3846 	            ),
3847 	        )
3848 	
3849 	    def test_target_attribute(self):
3850 	        self.assert_message_from_report(
3851 	            "Fencing level for 'name=value' at level '1' with device(s) "
3852 	            "'device2' already exists",
3853 	            reports.CibFencingLevelAlreadyExists(
3854 	                "1", TARGET_TYPE_ATTRIBUTE, ("name", "value"), ["device2"]
3855 	            ),
3856 	        )
3857 	
3858 	
3859 	class CibFencingLevelDoesNotExist(NameBuildTest):
3860 	    def test_full_info(self):
3861 	        self.assert_message_from_report(
3862 	            "Fencing level for 'nodeA' at level '1' with device(s) "
3863 	            "'device1', 'device2' does not exist",
3864 	            reports.CibFencingLevelDoesNotExist(
3865 	                "1", TARGET_TYPE_NODE, "nodeA", ["device2", "device1"]
3866 	            ),
3867 	        )
3868 	
3869 	    def test_only_level(self):
3870 	        self.assert_message_from_report(
3871 	            "Fencing level at level '1' does not exist",
3872 	            reports.CibFencingLevelDoesNotExist("1"),
3873 	        )
3874 	
3875 	    def test_only_target(self):
3876 	        self.assert_message_from_report(
3877 	            "Fencing level for 'name=value' does not exist",
3878 	            reports.CibFencingLevelDoesNotExist(
3879 	                target_type=TARGET_TYPE_ATTRIBUTE,
3880 	                target_value=("name", "value"),
3881 	            ),
3882 	        )
3883 	
3884 	    def test_only_devices(self):
3885 	        self.assert_message_from_report(
3886 	            "Fencing level with device(s) 'device1' does not exist",
3887 	            reports.CibFencingLevelDoesNotExist(devices=["device1"]),
3888 	        )
3889 	
3890 	    def test_no_info(self):
3891 	        self.assert_message_from_report(
3892 	            "Fencing level does not exist",
3893 	            reports.CibFencingLevelDoesNotExist(),
3894 	        )
3895 	
3896 	
3897 	class CibRemoveResources(NameBuildTest):
3898 	    def test_single_id(self):
3899 	        self.assert_message_from_report(
3900 	            "Removing resource: 'id1'", reports.CibRemoveResources(["id1"])
3901 	        )
3902 	
3903 	    def test_multiple_ids(self):
3904 	        self.assert_message_from_report(
3905 	            "Removing resources: 'id1', 'id2', 'id3'",
3906 	            reports.CibRemoveResources(["id1", "id2", "id3"]),
3907 	        )
3908 	
3909 	
3910 	class CibRemoveDependantElements(NameBuildTest):
3911 	    def test_single_element_type_with_single_id(self):
3912 	        self.assert_message_from_report(
3913 	            "Removing dependant element:\n  Location constraint: 'id1'",
3914 	            reports.CibRemoveDependantElements({"id1": "rsc_location"}),
3915 	        )
3916 	
3917 	    def test_single_element_type_with_multiple_ids(self):
3918 	        self.assert_message_from_report(
3919 	            (
3920 	                "Removing dependant elements:\n"
3921 	                "  Location constraints: 'id1', 'id2'"
3922 	            ),
3923 	            reports.CibRemoveDependantElements(
3924 	                {"id1": "rsc_location", "id2": "rsc_location"}
3925 	            ),
3926 	        )
3927 	
3928 	    def test_multiple_element_types_with_single_id(self):
3929 	        self.assert_message_from_report(
3930 	            (
3931 	                "Removing dependant elements:\n"
3932 	                "  Clone: 'id2'\n"
3933 	                "  Location constraint: 'id1'"
3934 	            ),
3935 	            reports.CibRemoveDependantElements(
3936 	                {"id1": "rsc_location", "id2": "clone"}
3937 	            ),
3938 	        )
3939 	
3940 	    def test_multiple_element_types_with_multiple_ids(self):
3941 	        self.assert_message_from_report(
3942 	            (
3943 	                "Removing dependant elements:\n"
3944 	                "  Another_elements: 'id5', 'id6'\n"
3945 	                "  Clones: 'id3', 'id4'\n"
3946 	                "  Location constraints: 'id1', 'id2'"
3947 	            ),
3948 	            reports.CibRemoveDependantElements(
3949 	                {
3950 	                    "id1": "rsc_location",
3951 	                    "id2": "rsc_location",
3952 	                    "id3": "clone",
3953 	                    "id4": "clone",
3954 	                    "id5": "another_element",
3955 	                    "id6": "another_element",
3956 	                }
3957 	            ),
3958 	        )
3959 	
3960 	
3961 	class CibRemoveReferences(NameBuildTest):
3962 	    def test_one_element_single_reference(self):
3963 	        self.assert_message_from_report(
3964 	            ("Removing references:\n  Resource 'id1' from:\n    Tag: 'id2'"),
3965 	            reports.CibRemoveReferences(
3966 	                {"id1": "primitive", "id2": "tag"}, {"id1": ["id2"]}
3967 	            ),
3968 	        )
3969 	
3970 	    def test_missing_tag_mapping(self):
3971 	        self.assert_message_from_report(
3972 	            ("Removing references:\n  Element 'id1' from:\n    Element: 'id2'"),
3973 	            reports.CibRemoveReferences({}, {"id1": ["id2"]}),
3974 	        )
3975 	
3976 	    def test_one_element_multiple_references_same_type(self):
3977 	        self.assert_message_from_report(
3978 	            (
3979 	                "Removing references:\n"
3980 	                "  Resource 'id1' from:\n"
3981 	                "    Tags: 'id2', 'id3'"
3982 	            ),
3983 	            reports.CibRemoveReferences(
3984 	                {"id1": "primitive", "id2": "tag", "id3": "tag"},
3985 	                {"id1": ["id2", "id3"]},
3986 	            ),
3987 	        )
3988 	
3989 	    def test_one_element_multiple_references_multiple_types(self):
3990 	        self.assert_message_from_report(
3991 	            (
3992 	                "Removing references:\n"
3993 	                "  Resource 'id1' from:\n"
3994 	                "    Group: 'id3'\n"
3995 	                "    Tag: 'id2'"
3996 	            ),
3997 	            reports.CibRemoveReferences(
3998 	                {"id1": "primitive", "id2": "tag", "id3": "group"},
3999 	                {"id1": ["id2", "id3"]},
4000 	            ),
4001 	        )
4002 	
4003 	    def test_multiple_elements_single_reference(self):
4004 	        self.assert_message_from_report(
4005 	            (
4006 	                "Removing references:\n"
4007 	                "  Resource 'id1' from:\n"
4008 	                "    Tag: 'id2'\n"
4009 	                "  Resource 'id3' from:\n"
4010 	                "    Tag: 'id4'"
4011 	            ),
4012 	            reports.CibRemoveReferences(
4013 	                {
4014 	                    "id1": "primitive",
4015 	                    "id2": "tag",
4016 	                    "id3": "primitive",
4017 	                    "id4": "tag",
4018 	                },
4019 	                {"id1": ["id2"], "id3": ["id4"]},
4020 	            ),
4021 	        )
4022 	
4023 	
4024 	class UseCommandNodeAddRemote(NameBuildTest):
4025 	    def test_build_messages(self):
4026 	        self.assert_message_from_report(
4027 	            "this command is not sufficient for creating a remote connection",
4028 	            reports.UseCommandNodeAddRemote(),
4029 	        )
4030 	
4031 	
4032 	class UseCommandNodeAddGuest(NameBuildTest):
4033 	    def test_build_messages(self):
4034 	        self.assert_message_from_report(
4035 	            "this command is not sufficient for creating a guest node",
4036 	            reports.UseCommandNodeAddGuest(),
4037 	        )
4038 	
4039 	
4040 	class UseCommandNodeRemoveRemote(NameBuildTest):
4041 	    def test_build_messages(self):
4042 	        self.assert_message_from_report(
4043 	            "this command is not sufficient for removing a remote node",
4044 	            reports.UseCommandNodeRemoveRemote(),
4045 	        )
4046 	
4047 	
4048 	class UseCommandNodeRemoveGuest(NameBuildTest):
4049 	    def test_build_messages(self):
4050 	        self.assert_message_from_report(
4051 	            "this command is not sufficient for removing a guest node",
4052 	            reports.UseCommandNodeRemoveGuest(),
4053 	        )
4054 	
4055 	
4056 	class UseCommandRemoveAndAddGuestNode(NameBuildTest):
4057 	    def test_message(self):
4058 	        self.assert_message_from_report(
4059 	            "Changing connection parameters of an existing guest node is not "
4060 	            "sufficient for connecting to a different guest node, remove the "
4061 	            "existing guest node and add a new one instead",
4062 	            reports.UseCommandRemoveAndAddGuestNode(),
4063 	        )
4064 	
4065 	
4066 	class GuestNodeNameAlreadyExists(NameBuildTest):
4067 	    def test_message(self):
4068 	        self.assert_message_from_report(
4069 	            "Cannot set name of the guest node to 'N' because that ID already "
4070 	            "exists in the cluster configuration.",
4071 	            reports.GuestNodeNameAlreadyExists("N"),
4072 	        )
4073 	
4074 	
4075 	class TmpFileWrite(NameBuildTest):
4076 	    def test_success(self):
4077 	        self.assert_message_from_report(
4078 	            (
4079 	                "Writing to a temporary file /tmp/pcs/test.tmp:\n"
4080 	                "--Debug Content Start--\n"
4081 	                "test file\ncontent\n\n"
4082 	                "--Debug Content End--\n"
4083 	            ),
4084 	            reports.TmpFileWrite("/tmp/pcs/test.tmp", "test file\ncontent\n"),
4085 	        )
4086 	
4087 	
4088 	class NodeAddressesUnresolvable(NameBuildTest):
4089 	    def test_one_address(self):
4090 	        self.assert_message_from_report(
4091 	            "Unable to resolve addresses: 'node1'",
4092 	            reports.NodeAddressesUnresolvable(["node1"]),
4093 	        )
4094 	
4095 	    def test_more_address(self):
4096 	        self.assert_message_from_report(
4097 	            "Unable to resolve addresses: 'node1', 'node2', 'node3'",
4098 	            reports.NodeAddressesUnresolvable(["node2", "node1", "node3"]),
4099 	        )
4100 	
4101 	
4102 	class UnableToPerformOperationOnAnyNode(NameBuildTest):
4103 	    def test_all(self):
4104 	        self.assert_message_from_report(
4105 	            (
4106 	                "Unable to perform operation on any available node/host, "
4107 	                "therefore it is not possible to continue"
4108 	            ),
4109 	            reports.UnableToPerformOperationOnAnyNode(),
4110 	        )
4111 	
4112 	
4113 	class HostNotFound(NameBuildTest):
4114 	    def test_single_host(self):
4115 	        self.assert_message_from_report(
4116 	            "Host 'unknown_host' is not known to pcs",
4117 	            reports.HostNotFound(["unknown_host"]),
4118 	        )
4119 	
4120 	    def test_multiple_hosts(self):
4121 	        self.assert_message_from_report(
4122 	            "Hosts 'another_one', 'unknown_host' are not known to pcs",
4123 	            reports.HostNotFound(["unknown_host", "another_one"]),
4124 	        )
4125 	
4126 	
4127 	class NoneHostFound(NameBuildTest):
4128 	    def test_all(self):
4129 	        self.assert_message_from_report(
4130 	            "None of hosts is known to pcs.", reports.NoneHostFound()
4131 	        )
4132 	
4133 	
4134 	class HostAlreadyAuthorized(NameBuildTest):
4135 	    def test_success(self):
4136 	        self.assert_message_from_report(
4137 	            "host: Already authorized", reports.HostAlreadyAuthorized("host")
4138 	        )
4139 	
4140 	
4141 	class AuthorizationSuccessful(NameBuildTest):
4142 	    def test_success(self):
4143 	        self.assert_message_from_report(
4144 	            "Authorized", reports.AuthorizationSuccessful()
4145 	        )
4146 	
4147 	
4148 	class IncorrectCredentials(NameBuildTest):
4149 	    def test_success(self):
4150 	        self.assert_message_from_report(
4151 	            "Username and/or password is incorrect",
4152 	            reports.IncorrectCredentials(),
4153 	        )
4154 	
4155 	
4156 	class NoHostSpecified(NameBuildTest):
4157 	    def test_success(self):
4158 	        self.assert_message_from_report(
4159 	            "No host specified", reports.NoHostSpecified()
4160 	        )
4161 	
4162 	
4163 	class ClusterDestroyStarted(NameBuildTest):
4164 	    def test_multiple_hosts(self):
4165 	        self.assert_message_from_report(
4166 	            "Destroying cluster on hosts: 'node1', 'node2', 'node3'...",
4167 	            reports.ClusterDestroyStarted(["node1", "node3", "node2"]),
4168 	        )
4169 	
4170 	    def test_single_host(self):
4171 	        self.assert_message_from_report(
4172 	            "Destroying cluster on hosts: 'node1'...",
4173 	            reports.ClusterDestroyStarted(["node1"]),
4174 	        )
4175 	
4176 	
4177 	class ClusterDestroySuccess(NameBuildTest):
4178 	    def test_success(self):
4179 	        self.assert_message_from_report(
4180 	            "node1: Successfully destroyed cluster",
4181 	            reports.ClusterDestroySuccess("node1"),
4182 	        )
4183 	
4184 	
4185 	class ClusterEnableStarted(NameBuildTest):
4186 	    def test_multiple_hosts(self):
4187 	        self.assert_message_from_report(
4188 	            "Enabling cluster on hosts: 'node1', 'node2', 'node3'...",
4189 	            reports.ClusterEnableStarted(["node1", "node3", "node2"]),
4190 	        )
4191 	
4192 	    def test_single_host(self):
4193 	        self.assert_message_from_report(
4194 	            "Enabling cluster on hosts: 'node1'...",
4195 	            reports.ClusterEnableStarted(["node1"]),
4196 	        )
4197 	
4198 	
4199 	class ClusterEnableSuccess(NameBuildTest):
4200 	    def test_success(self):
4201 	        self.assert_message_from_report(
4202 	            "node1: Cluster enabled", reports.ClusterEnableSuccess("node1")
4203 	        )
4204 	
4205 	
4206 	class ClusterStartStarted(NameBuildTest):
4207 	    def test_multiple_hosts(self):
4208 	        self.assert_message_from_report(
4209 	            "Starting cluster on hosts: 'node1', 'node2', 'node3'...",
4210 	            reports.ClusterStartStarted(["node1", "node3", "node2"]),
4211 	        )
4212 	
4213 	    def test_single_host(self):
4214 	        self.assert_message_from_report(
4215 	            "Starting cluster on hosts: 'node1'...",
4216 	            reports.ClusterStartStarted(["node1"]),
4217 	        )
4218 	
4219 	
4220 	class ClusterStartSuccess(NameBuildTest):
4221 	    def test_all(self):
4222 	        self.assert_message_from_report(
4223 	            "node1: Cluster started", reports.ClusterStartSuccess("node1")
4224 	        )
4225 	
4226 	
4227 	class ServiceNotInstalled(NameBuildTest):
4228 	    def test_multiple_services(self):
4229 	        self.assert_message_from_report(
4230 	            "node1: Required cluster services not installed: 'service1', "
4231 	            "'service2', 'service3'",
4232 	            reports.ServiceNotInstalled(
4233 	                "node1", ["service1", "service3", "service2"]
4234 	            ),
4235 	        )
4236 	
4237 	    def test_single_service(self):
4238 	        self.assert_message_from_report(
4239 	            "node1: Required cluster services not installed: 'service'",
4240 	            reports.ServiceNotInstalled("node1", ["service"]),
4241 	        )
4242 	
4243 	
4244 	class HostAlreadyInClusterConfig(NameBuildTest):
4245 	    def test_success(self):
4246 	        self.assert_message_from_report(
4247 	            "host: The host seems to be in a cluster already as cluster "
4248 	            "configuration files have been found on the host",
4249 	            reports.HostAlreadyInClusterConfig("host"),
4250 	        )
4251 	
4252 	
4253 	class HostAlreadyInClusterServices(NameBuildTest):
4254 	    def test_multiple_services(self):
4255 	        self.assert_message_from_report(
4256 	            "node1: The host seems to be in a cluster already as the following "
4257 	            "services are found to be running: 'service1', 'service2', "
4258 	            "'service3'. If the host is not part of a cluster, stop the "
4259 	            "services and retry",
4260 	            reports.HostAlreadyInClusterServices(
4261 	                "node1", ["service1", "service3", "service2"]
4262 	            ),
4263 	        )
4264 	
4265 	    def test_single_service(self):
4266 	        self.assert_message_from_report(
4267 	            "node1: The host seems to be in a cluster already as the following "
4268 	            "service is found to be running: 'service'. If the host is not "
4269 	            "part of a cluster, stop the service and retry",
4270 	            reports.HostAlreadyInClusterServices("node1", ["service"]),
4271 	        )
4272 	
4273 	
4274 	class ServiceVersionMismatch(NameBuildTest):
4275 	    def test_success(self):
4276 	        self.assert_message_from_report(
4277 	            "Hosts do not have the same version of 'service'; "
4278 	            "hosts 'host4', 'host5', 'host6' have version 2.0; "
4279 	            "hosts 'host1', 'host3' have version 1.0; "
4280 	            "host 'host2' has version 1.2",
4281 	            reports.ServiceVersionMismatch(
4282 	                "service",
4283 	                {
4284 	                    "host1": "1.0",
4285 	                    "host2": "1.2",
4286 	                    "host3": "1.0",
4287 	                    "host4": "2.0",
4288 	                    "host5": "2.0",
4289 	                    "host6": "2.0",
4290 	                },
4291 	            ),
4292 	        )
4293 	
4294 	
4295 	class WaitForNodeStartupStarted(NameBuildTest):
4296 	    def test_single_node(self):
4297 	        self.assert_message_from_report(
4298 	            "Waiting for node(s) to start: 'node1'...",
4299 	            reports.WaitForNodeStartupStarted(["node1"]),
4300 	        )
4301 	
4302 	    def test_multiple_nodes(self):
4303 	        self.assert_message_from_report(
4304 	            "Waiting for node(s) to start: 'node1', 'node2', 'node3'...",
4305 	            reports.WaitForNodeStartupStarted(["node3", "node2", "node1"]),
4306 	        )
4307 	
4308 	
4309 	class WaitForNodeStartupTimedOut(NameBuildTest):
4310 	    def test_all(self):
4311 	        self.assert_message_from_report(
4312 	            "Node(s) startup timed out", reports.WaitForNodeStartupTimedOut()
4313 	        )
4314 	
4315 	
4316 	class WaitForNodeStartupError(NameBuildTest):
4317 	    def test_all(self):
4318 	        self.assert_message_from_report(
4319 	            "Unable to verify all nodes have started",
4320 	            reports.WaitForNodeStartupError(),
4321 	        )
4322 	
4323 	
4324 	class WaitForNodeStartupWithoutStart(NameBuildTest):
4325 	    def test_all(self):
4326 	        self.assert_message_from_report(
4327 	            "Cannot specify 'wait' without specifying 'start'",
4328 	            reports.WaitForNodeStartupWithoutStart(),
4329 	        )
4330 	
4331 	
4332 	class PcsdVersionTooOld(NameBuildTest):
4333 	    def test_success(self):
4334 	        self.assert_message_from_report(
4335 	            (
4336 	                "node1: Old version of pcsd is running on the node, therefore "
4337 	                "it is unable to perform the action"
4338 	            ),
4339 	            reports.PcsdVersionTooOld("node1"),
4340 	        )
4341 	
4342 	
4343 	class PcsdSslCertAndKeyDistributionStarted(NameBuildTest):
4344 	    def test_multiple_nodes(self):
4345 	        self.assert_message_from_report(
4346 	            "Synchronizing pcsd SSL certificates on node(s) 'node1', 'node2', "
4347 	            "'node3'...",
4348 	            reports.PcsdSslCertAndKeyDistributionStarted(
4349 	                ["node1", "node3", "node2"]
4350 	            ),
4351 	        )
4352 	
4353 	    def test_single_node(self):
4354 	        self.assert_message_from_report(
4355 	            "Synchronizing pcsd SSL certificates on node(s) 'node3'...",
4356 	            reports.PcsdSslCertAndKeyDistributionStarted(["node3"]),
4357 	        )
4358 	
4359 	
4360 	class PcsdSslCertAndKeySetSuccess(NameBuildTest):
4361 	    def test_success(self):
4362 	        self.assert_message_from_report(
4363 	            "node1: Success", reports.PcsdSslCertAndKeySetSuccess("node1")
4364 	        )
4365 	
4366 	
4367 	class ClusterWillBeDestroyed(NameBuildTest):
4368 	    def test_all(self):
4369 	        self.assert_message_from_report(
4370 	            (
4371 	                "Some nodes are already in a cluster. Enforcing this will "
4372 	                "destroy existing cluster on those nodes. You should remove "
4373 	                "the nodes from their clusters instead to keep the clusters "
4374 	                "working properly"
4375 	            ),
4376 	            reports.ClusterWillBeDestroyed(),
4377 	        )
4378 	
4379 	
4380 	class ClusterSetupSuccess(NameBuildTest):
4381 	    def test_all(self):
4382 	        self.assert_message_from_report(
4383 	            "Cluster has been successfully set up.",
4384 	            reports.ClusterSetupSuccess(),
4385 	        )
4386 	
4387 	
4388 	class UsingDefaultAddressForHost(NameBuildTest):
4389 	    def test_success(self):
4390 	        self.assert_message_from_report(
4391 	            "No addresses specified for host 'node-name', using 'node-addr'",
4392 	            reports.UsingDefaultAddressForHost(
4393 	                "node-name",
4394 	                "node-addr",
4395 	                const.DEFAULT_ADDRESS_SOURCE_KNOWN_HOSTS,
4396 	            ),
4397 	        )
4398 	
4399 	
4400 	class ResourceInBundleNotAccessible(NameBuildTest):
4401 	    def test_success(self):
4402 	        self.assert_message_from_report(
4403 	            (
4404 	                "Resource 'resourceA' will not be accessible by the cluster "
4405 	                "inside bundle 'bundleA', at least one of bundle options "
4406 	                "'control-port' or 'ip-range-start' has to be specified"
4407 	            ),
4408 	            reports.ResourceInBundleNotAccessible("bundleA", "resourceA"),
4409 	        )
4410 	
4411 	
4412 	class UsingDefaultWatchdog(NameBuildTest):
4413 	    def test_success(self):
4414 	        self.assert_message_from_report(
4415 	            (
4416 	                "No watchdog has been specified for node 'node1'. Using "
4417 	                "default watchdog '/dev/watchdog'"
4418 	            ),
4419 	            reports.UsingDefaultWatchdog("/dev/watchdog", "node1"),
4420 	        )
4421 	
4422 	
4423 	class CannotRemoveAllClusterNodes(NameBuildTest):
4424 	    def test_success(self):
4425 	        self.assert_message_from_report(
4426 	            "No nodes would be left in the cluster",
4427 	            reports.CannotRemoveAllClusterNodes(),
4428 	        )
4429 	
4430 	
4431 	class UnableToConnectToAnyRemainingNode(NameBuildTest):
4432 	    def test_all(self):
4433 	        self.assert_message_from_report(
4434 	            "Unable to connect to any remaining cluster node",
4435 	            reports.UnableToConnectToAnyRemainingNode(),
4436 	        )
4437 	
4438 	
4439 	class UnableToConnectToAllRemainingNodes(NameBuildTest):
4440 	    def test_single_node(self):
4441 	        self.assert_message_from_report(
4442 	            ("Remaining cluster node 'node1' could not be reached"),
4443 	            reports.UnableToConnectToAllRemainingNodes(["node1"]),
4444 	        )
4445 	
4446 	    def test_multiple_nodes(self):
4447 	        self.assert_message_from_report(
4448 	            (
4449 	                "Remaining cluster nodes 'node0', 'node1', 'node2' could not "
4450 	                "be reached"
4451 	            ),
4452 	            reports.UnableToConnectToAllRemainingNodes(
4453 	                ["node1", "node0", "node2"]
4454 	            ),
4455 	        )
4456 	
4457 	
4458 	class NodesToRemoveUnreachable(NameBuildTest):
4459 	    def test_single_node(self):
4460 	        self.assert_message_from_report(
4461 	            (
4462 	                "Removed node 'node0' could not be reached and subsequently "
4463 	                "deconfigured"
4464 	            ),
4465 	            reports.NodesToRemoveUnreachable(["node0"]),
4466 	        )
4467 	
4468 	    def test_multiple_nodes(self):
4469 	        self.assert_message_from_report(
4470 	            (
4471 	                "Removed nodes 'node0', 'node1', 'node2' could not be reached "
4472 	                "and subsequently deconfigured"
4473 	            ),
4474 	            reports.NodesToRemoveUnreachable(["node1", "node0", "node2"]),
4475 	        )
4476 	
4477 	
4478 	class NodeUsedAsTieBreaker(NameBuildTest):
4479 	    def test_success(self):
4480 	        self.assert_message_from_report(
4481 	            (
4482 	                "Node 'node2' with id '2' is used as a tie breaker for a "
4483 	                "qdevice and therefore cannot be removed"
4484 	            ),
4485 	            reports.NodeUsedAsTieBreaker("node2", 2),
4486 	        )
4487 	
4488 	
4489 	class CorosyncQuorumWillBeLost(NameBuildTest):
4490 	    def test_all(self):
4491 	        self.assert_message_from_report(
4492 	            "This action will cause a loss of the quorum",
4493 	            reports.CorosyncQuorumWillBeLost(),
4494 	        )
4495 	
4496 	
4497 	class CorosyncQuorumLossUnableToCheck(NameBuildTest):
4498 	    def test_all(self):
4499 	        self.assert_message_from_report(
4500 	            (
4501 	                "Unable to determine whether this action will cause "
4502 	                "a loss of the quorum"
4503 	            ),
4504 	            reports.CorosyncQuorumLossUnableToCheck(),
4505 	        )
4506 	
4507 	
4508 	class SbdListWatchdogError(NameBuildTest):
4509 	    def test_success(self):
4510 	        self.assert_message_from_report(
4511 	            "Unable to query available watchdogs from sbd: this is a reason",
4512 	            reports.SbdListWatchdogError("this is a reason"),
4513 	        )
4514 	
4515 	
4516 	class SbdWatchdogNotSupported(NameBuildTest):
4517 	    def test_success(self):
4518 	        self.assert_message_from_report(
4519 	            (
4520 	                "node1: Watchdog '/dev/watchdog' is not supported (it may be a "
4521 	                "software watchdog)"
4522 	            ),
4523 	            reports.SbdWatchdogNotSupported("node1", "/dev/watchdog"),
4524 	        )
4525 	
4526 	
4527 	class SbdWatchdogValidationInactive(NameBuildTest):
4528 	    def test_all(self):
4529 	        self.assert_message_from_report(
4530 	            "Not validating the watchdog",
4531 	            reports.SbdWatchdogValidationInactive(),
4532 	        )
4533 	
4534 	
4535 	class SbdWatchdogTestError(NameBuildTest):
4536 	    def test_success(self):
4537 	        self.assert_message_from_report(
4538 	            "Unable to initialize test of the watchdog: some reason",
4539 	            reports.SbdWatchdogTestError("some reason"),
4540 	        )
4541 	
4542 	
4543 	class SbdWatchdogTestMultipleDevices(NameBuildTest):
4544 	    def test_all(self):
4545 	        self.assert_message_from_report(
4546 	            (
4547 	                "Multiple watchdog devices available, therefore, watchdog "
4548 	                "which should be tested has to be specified."
4549 	            ),
4550 	            reports.SbdWatchdogTestMultipleDevices(),
4551 	        )
4552 	
4553 	
4554 	class SbdWatchdogTestFailed(NameBuildTest):
4555 	    def test_all(self):
4556 	        self.assert_message_from_report(
4557 	            "System should have been reset already",
4558 	            reports.SbdWatchdogTestFailed(),
4559 	        )
4560 	
4561 	
4562 	class SystemWillReset(NameBuildTest):
4563 	    def test_all(self):
4564 	        self.assert_message_from_report(
4565 	            "System will reset shortly", reports.SystemWillReset()
4566 	        )
4567 	
4568 	
4569 	class ResourceBundleUnsupportedContainerType(NameBuildTest):
4570 	    def test_single_type(self):
4571 	        self.assert_message_from_report(
4572 	            (
4573 	                "Bundle 'bundle id' uses unsupported container type, therefore "
4574 	                "it is not possible to set its container options. Supported "
4575 	                "container types are: 'b'"
4576 	            ),
4577 	            reports.ResourceBundleUnsupportedContainerType("bundle id", ["b"]),
4578 	        )
4579 	
4580 	    def test_multiple_types(self):
4581 	        self.assert_message_from_report(
4582 	            (
4583 	                "Bundle 'bundle id' uses unsupported container type, therefore "
4584 	                "it is not possible to set its container options. Supported "
4585 	                "container types are: 'a', 'b', 'c'"
4586 	            ),
4587 	            reports.ResourceBundleUnsupportedContainerType(
4588 	                "bundle id", ["b", "a", "c"]
4589 	            ),
4590 	        )
4591 	
4592 	    def test_no_update(self):
4593 	        self.assert_message_from_report(
4594 	            (
4595 	                "Bundle 'bundle id' uses unsupported container type. Supported "
4596 	                "container types are: 'a', 'b', 'c'"
4597 	            ),
4598 	            reports.ResourceBundleUnsupportedContainerType(
4599 	                "bundle id", ["b", "a", "c"], updating_options=False
4600 	            ),
4601 	        )
4602 	
4603 	
4604 	class FenceHistoryCommandError(NameBuildTest):
4605 	    def test_success(self):
4606 	        self.assert_message_from_report(
4607 	            "Unable to show fence history: reason",
4608 	            reports.FenceHistoryCommandError(
4609 	                "reason", reports.const.FENCE_HISTORY_COMMAND_SHOW
4610 	            ),
4611 	        )
4612 	
4613 	
4614 	class FenceHistoryNotSupported(NameBuildTest):
4615 	    def test_success(self):
4616 	        self.assert_message_from_report(
4617 	            "Fence history is not supported, please upgrade pacemaker",
4618 	            reports.FenceHistoryNotSupported(),
4619 	        )
4620 	
4621 	
4622 	class ResourceInstanceAttrValueNotUnique(NameBuildTest):
4623 	    def test_one_resource(self):
4624 	        self.assert_message_from_report(
4625 	            (
4626 	                "Value 'val' of option 'attr' is not unique across 'agent' "
4627 	                "resources. Following resources are configured with the same "
4628 	                "value of the instance attribute: 'A'"
4629 	            ),
4630 	            reports.ResourceInstanceAttrValueNotUnique(
4631 	                "attr", "val", "agent", ["A"]
4632 	            ),
4633 	        )
4634 	
4635 	    def test_multiple_resources(self):
4636 	        self.assert_message_from_report(
4637 	            (
4638 	                "Value 'val' of option 'attr' is not unique across 'agent' "
4639 	                "resources. Following resources are configured with the same "
4640 	                "value of the instance attribute: 'A', 'B', 'C'"
4641 	            ),
4642 	            reports.ResourceInstanceAttrValueNotUnique(
4643 	                "attr", "val", "agent", ["B", "C", "A"]
4644 	            ),
4645 	        )
4646 	
4647 	
4648 	class ResourceInstanceAttrGroupValueNotUnique(NameBuildTest):
4649 	    def test_message(self):
4650 	        self.assert_message_from_report(
4651 	            (
4652 	                "Value '127.0.0.1', '12345' of options 'ip', 'port' (group "
4653 	                "'address') is not unique across 'agent' resources. Following "
4654 	                "resources are configured with the same values of the instance "
4655 	                "attributes: 'A', 'B'"
4656 	            ),
4657 	            reports.ResourceInstanceAttrGroupValueNotUnique(
4658 	                "address",
4659 	                {
4660 	                    "port": "12345",
4661 	                    "ip": "127.0.0.1",
4662 	                },
4663 	                "agent",
4664 	                ["B", "A"],
4665 	            ),
4666 	        )
4667 	
4668 	
4669 	class CannotLeaveGroupEmptyAfterMove(NameBuildTest):
4670 	    def test_single_resource(self):
4671 	        self.assert_message_from_report(
4672 	            "Unable to move resource 'R' as it would leave group 'gr1' empty.",
4673 	            reports.CannotLeaveGroupEmptyAfterMove("gr1", ["R"]),
4674 	        )
4675 	
4676 	    def test_multiple_resources(self):
4677 	        self.assert_message_from_report(
4678 	            "Unable to move resources 'R1', 'R2', 'R3' as it would leave "
4679 	            "group 'gr1' empty.",
4680 	            reports.CannotLeaveGroupEmptyAfterMove("gr1", ["R3", "R1", "R2"]),
4681 	        )
4682 	
4683 	
4684 	class CannotMoveResourceBundleInner(NameBuildTest):
4685 	    def test_success(self):
4686 	        self.assert_message_from_report(
4687 	            (
4688 	                "Resources cannot be moved out of their bundles. If you want "
4689 	                "to move a bundle, use the bundle id (B)"
4690 	            ),
4691 	            reports.CannotMoveResourceBundleInner("R", "B"),
4692 	        )
4693 	
4694 	
4695 	class CannotMoveResourceCloneInner(NameBuildTest):
4696 	    def test_success(self):
4697 	        self.assert_message_from_report(
4698 	            "to move clone resources you must use the clone id (C)",
4699 	            reports.CannotMoveResourceCloneInner("R", "C"),
4700 	        )
4701 	
4702 	
4703 	class CannotMoveResourceMultipleInstances(NameBuildTest):
4704 	    def test_success(self):
4705 	        self.assert_message_from_report(
4706 	            (
4707 	                "more than one instance of resource 'R' is running, "
4708 	                "thus the resource cannot be moved"
4709 	            ),
4710 	            reports.CannotMoveResourceMultipleInstances("R"),
4711 	        )
4712 	
4713 	
4714 	class CannotMoveResourceMultipleInstancesNoNodeSpecified(NameBuildTest):
4715 	    def test_success(self):
4716 	        self.assert_message_from_report(
4717 	            (
4718 	                "more than one instance of resource 'R' is running, "
4719 	                "thus the resource cannot be moved, "
4720 	                "unless a destination node is specified"
4721 	            ),
4722 	            reports.CannotMoveResourceMultipleInstancesNoNodeSpecified("R"),
4723 	        )
4724 	
4725 	
4726 	class CannotMoveResourcePromotableInner(NameBuildTest):
4727 	    def test_success(self):
4728 	        self.assert_message_from_report(
4729 	            (
4730 	                "to move promotable clone resources you must use "
4731 	                "the promotable clone id (P)"
4732 	            ),
4733 	            reports.CannotMoveResourcePromotableInner("R", "P"),
4734 	        )
4735 	
4736 	
4737 	class CannotMoveResourceMasterResourceNotPromotable(NameBuildTest):
4738 	    def test_without_promotable(self):
4739 	        self.assert_message_from_report(
4740 	            "when specifying promoted you must use the promotable clone id",
4741 	            reports.CannotMoveResourceMasterResourceNotPromotable("R"),
4742 	        )
4743 	
4744 	    def test_with_promotable(self):
4745 	        self.assert_message_from_report(
4746 	            "when specifying promoted you must use the promotable clone id (P)",
4747 	            reports.CannotMoveResourceMasterResourceNotPromotable(
4748 	                "R", promotable_id="P"
4749 	            ),
4750 	        )
4751 	
4752 	
4753 	class CannotMoveResourceNotRunning(NameBuildTest):
4754 	    def test_success(self):
4755 	        self.assert_message_from_report(
4756 	            (
4757 	                "It is not possible to move resource 'R' as it is not running "
4758 	                "at the moment"
4759 	            ),
4760 	            reports.CannotMoveResourceNotRunning("R"),
4761 	        )
4762 	
4763 	
4764 	class CannotMoveResourceStoppedNoNodeSpecified(NameBuildTest):
4765 	    def test_success(self):
4766 	        self.assert_message_from_report(
4767 	            "You must specify a node when moving/banning a stopped resource",
4768 	            reports.CannotMoveResourceStoppedNoNodeSpecified("R"),
4769 	        )
4770 	
4771 	
4772 	class ResourceMovePcmkError(NameBuildTest):
4773 	    def test_success(self):
4774 	        self.assert_message_from_report(
4775 	            "cannot move resource 'R'\nstdout1\n  stdout2\nstderr1\n  stderr2",
4776 	            reports.ResourceMovePcmkError(
4777 	                "R", "stdout1\n\n  stdout2\n", "stderr1\n\n  stderr2\n"
4778 	            ),
4779 	        )
4780 	
4781 	
4782 	class ResourceMovePcmkSuccess(NameBuildTest):
4783 	    def test_success(self):
4784 	        self.assert_message_from_report(
4785 	            "stdout1\n  stdout2\nstderr1\n  stderr2",
4786 	            reports.ResourceMovePcmkSuccess(
4787 	                "R", "stdout1\n\n  stdout2\n", "stderr1\n\n  stderr2\n"
4788 	            ),
4789 	        )
4790 	
4791 	    def test_translate(self):
4792 	        self.assert_message_from_report(
4793 	            (
4794 	                "Warning: Creating location constraint "
4795 	                "'cli-ban-dummy-on-node1' with a score of -INFINITY "
4796 	                "for resource dummy on node1.\n"
4797 	                "	This will prevent dummy from running on node1 until the "
4798 	                "constraint is removed\n"
4799 	                "	This will be the case even if node1 is the last node in "
4800 	                "the cluster"
4801 	            ),
4802 	            reports.ResourceMovePcmkSuccess(
4803 	                "dummy",
4804 	                "",
4805 	                (
4806 	                    "WARNING: Creating rsc_location constraint "
4807 	                    "'cli-ban-dummy-on-node1' with a score of -INFINITY "
4808 	                    "for resource dummy on node1.\n"
4809 	                    "	This will prevent dummy from running on node1 until "
4810 	                    "the constraint is removed using the clear option or "
4811 	                    "by editing the CIB with an appropriate tool\n"
4812 	                    "	This will be the case even if node1 is the last node "
4813 	                    "in the cluster\n"
4814 	                ),
4815 	            ),
4816 	        )
4817 	
4818 	
4819 	class CannotBanResourceBundleInner(NameBuildTest):
4820 	    def test_success(self):
4821 	        self.assert_message_from_report(
4822 	            (
4823 	                "Resource 'R' is in a bundle and cannot be banned. If you want "
4824 	                "to ban the bundle, use the bundle id (B)"
4825 	            ),
4826 	            reports.CannotBanResourceBundleInner("R", "B"),
4827 	        )
4828 	
4829 	
4830 	class CannotBanResourceMasterResourceNotPromotable(NameBuildTest):
4831 	    def test_without_promotable(self):
4832 	        self.assert_message_from_report(
4833 	            "when specifying promoted you must use the promotable clone id",
4834 	            reports.CannotBanResourceMasterResourceNotPromotable("R"),
4835 	        )
4836 	
4837 	    def test_with_promotable(self):
4838 	        self.assert_message_from_report(
4839 	            "when specifying promoted you must use the promotable clone id (P)",
4840 	            reports.CannotBanResourceMasterResourceNotPromotable(
4841 	                "R", promotable_id="P"
4842 	            ),
4843 	        )
4844 	
4845 	
4846 	class CannotBanResourceMultipleInstancesNoNodeSpecified(NameBuildTest):
4847 	    def test_success(self):
4848 	        self.assert_message_from_report(
4849 	            (
4850 	                "more than one instance of resource 'R' is running, "
4851 	                "thus the resource cannot be banned, "
4852 	                "unless a destination node is specified"
4853 	            ),
4854 	            reports.CannotBanResourceMultipleInstancesNoNodeSpecified("R"),
4855 	        )
4856 	
4857 	
4858 	class CannotBanResourceStoppedNoNodeSpecified(NameBuildTest):
4859 	    def test_success(self):
4860 	        self.assert_message_from_report(
4861 	            "You must specify a node when moving/banning a stopped resource",
4862 	            reports.CannotBanResourceStoppedNoNodeSpecified("R"),
4863 	        )
4864 	
4865 	
4866 	class ResourceBanPcmkError(NameBuildTest):
4867 	    def test_success(self):
4868 	        self.assert_message_from_report(
4869 	            "cannot ban resource 'R'\nstdout1\n  stdout2\nstderr1\n  stderr2",
4870 	            reports.ResourceBanPcmkError(
4871 	                "R", "stdout1\n\n  stdout2\n", "stderr1\n\n  stderr2\n"
4872 	            ),
4873 	        )
4874 	
4875 	
4876 	class ResourceBanPcmkSuccess(NameBuildTest):
4877 	    def test_success(self):
4878 	        self.assert_message_from_report(
4879 	            "stdout1\n  stdout2\nstderr1\n  stderr2",
4880 	            reports.ResourceBanPcmkSuccess(
4881 	                "R", "stdout1\n\n  stdout2\n", "stderr1\n\n  stderr2\n"
4882 	            ),
4883 	        )
4884 	
4885 	    def test_translate(self):
4886 	        self.assert_message_from_report(
4887 	            (
4888 	                "Warning: Creating location constraint "
4889 	                "'cli-ban-dummy-on-node1' with a score of -INFINITY "
4890 	                "for resource dummy on node1.\n"
4891 	                "	This will prevent dummy from running on node1 until the "
4892 	                "constraint is removed\n"
4893 	                "	This will be the case even if node1 is the last node in "
4894 	                "the cluster"
4895 	            ),
4896 	            reports.ResourceBanPcmkSuccess(
4897 	                "dummy",
4898 	                "",
4899 	                (
4900 	                    "WARNING: Creating rsc_location constraint "
4901 	                    "'cli-ban-dummy-on-node1' with a score of -INFINITY "
4902 	                    "for resource dummy on node1.\n"
4903 	                    "	This will prevent dummy from running on node1 until "
4904 	                    "the constraint is removed using the clear option or "
4905 	                    "by editing the CIB with an appropriate tool\n"
4906 	                    "	This will be the case even if node1 is the last node "
4907 	                    "in the cluster\n"
4908 	                ),
4909 	            ),
4910 	        )
4911 	
4912 	
4913 	class CannotUnmoveUnbanResourceMasterResourceNotPromotable(NameBuildTest):
4914 	    def test_without_promotable(self):
4915 	        self.assert_message_from_report(
4916 	            "when specifying promoted you must use the promotable clone id",
4917 	            reports.CannotUnmoveUnbanResourceMasterResourceNotPromotable("R"),
4918 	        )
4919 	
4920 	    def test_with_promotable(self):
4921 	        self.assert_message_from_report(
4922 	            "when specifying promoted you must use the promotable clone id (P)",
4923 	            reports.CannotUnmoveUnbanResourceMasterResourceNotPromotable(
4924 	                "R", promotable_id="P"
4925 	            ),
4926 	        )
4927 	
4928 	
4929 	class ResourceUnmoveUnbanPcmkExpiredNotSupported(NameBuildTest):
4930 	    def test_success(self):
4931 	        self.assert_message_from_report(
4932 	            "expired is not supported, please upgrade pacemaker",
4933 	            reports.ResourceUnmoveUnbanPcmkExpiredNotSupported(),
4934 	        )
4935 	
4936 	
4937 	class ResourceUnmoveUnbanPcmkError(NameBuildTest):
4938 	    def test_success(self):
4939 	        self.assert_message_from_report(
4940 	            "cannot clear resource 'R'\nstdout1\n  stdout2\nstderr1\n  stderr2",
4941 	            reports.ResourceUnmoveUnbanPcmkError(
4942 	                "R", "stdout1\n\n  stdout2\n", "stderr1\n\n  stderr2\n"
4943 	            ),
4944 	        )
4945 	
4946 	
4947 	class ResourceUnmoveUnbanPcmkSuccess(NameBuildTest):
4948 	    def test_success(self):
4949 	        self.assert_message_from_report(
4950 	            "stdout1\n  stdout2\nstderr1\n  stderr2",
4951 	            reports.ResourceUnmoveUnbanPcmkSuccess(
4952 	                "R", "stdout1\n\n  stdout2\n", "stderr1\n\n  stderr2\n"
4953 	            ),
4954 	        )
4955 	
4956 	
4957 	class ResourceMoveConstraintCreated(NameBuildTest):
4958 	    def test_success(self):
4959 	        self.assert_message_from_report(
4960 	            "Location constraint to move resource 'R1' has been created",
4961 	            reports.ResourceMoveConstraintCreated("R1"),
4962 	        )
4963 	
4964 	
4965 	class ResourceMoveConstraintRemoved(NameBuildTest):
4966 	    def test_success(self):
4967 	        self.assert_message_from_report(
4968 	            (
4969 	                "Location constraint created to move resource 'R1' has "
4970 	                "been removed"
4971 	            ),
4972 	            reports.ResourceMoveConstraintRemoved("R1"),
4973 	        )
4974 	
4975 	
4976 	class ResourceMoveNotAffectingResource(NameBuildTest):
4977 	    def test_success(self):
4978 	        self.assert_message_from_report(
4979 	            (
4980 	                "Unable to move resource 'R1' using a location constraint. "
4981 	                "Current location of the resource may be affected by some "
4982 	                "other constraint."
4983 	            ),
4984 	            reports.ResourceMoveNotAffectingResource("R1"),
4985 	        )
4986 	
4987 	
4988 	class ResourceMoveAffectsOtherResources(NameBuildTest):
4989 	    def test_multiple(self):
4990 	        self.assert_message_from_report(
4991 	            "Moving resource 'R1' affects resources: 'p0', 'p1', 'p2'",
4992 	            reports.ResourceMoveAffectsOtherResources("R1", ["p2", "p0", "p1"]),
4993 	        )
4994 	
4995 	    def test_single(self):
4996 	        self.assert_message_from_report(
4997 	            "Moving resource 'R1' affects resource: 'R2'",
4998 	            reports.ResourceMoveAffectsOtherResources("R1", ["R2"]),
4999 	        )
5000 	
5001 	
5002 	class ResourceMoveAutocleanSimulationFailure(NameBuildTest):
5003 	    def test_simulation(self):
5004 	        self.assert_message_from_report(
5005 	            (
5006 	                "Unable to ensure that moved resource 'R1' will stay on the "
5007 	                "same node after a constraint used for moving it is removed."
5008 	            ),
5009 	            reports.ResourceMoveAutocleanSimulationFailure(
5010 	                "R1", others_affected=False
5011 	            ),
5012 	        )
5013 	
5014 	    def test_simulation_others_affected(self):
5015 	        self.assert_message_from_report(
5016 	            (
5017 	                "Unable to ensure that moved resource 'R1' or other resources "
5018 	                "will stay on the same node after a constraint used for moving "
5019 	                "it is removed."
5020 	            ),
5021 	            reports.ResourceMoveAutocleanSimulationFailure(
5022 	                "R1", others_affected=True
5023 	            ),
5024 	        )
5025 	
5026 	    def test_live(self):
5027 	        self.assert_message_from_report(
5028 	            (
5029 	                "Unable to ensure that moved resource 'R1' will stay on the "
5030 	                "same node after a constraint used for moving it is removed."
5031 	                " The constraint to move the resource has not been removed "
5032 	                "from configuration. Consider removing it manually. Be aware "
5033 	                "that removing the constraint may cause resources to move to "
5034 	                "other nodes."
5035 	            ),
5036 	            reports.ResourceMoveAutocleanSimulationFailure(
5037 	                "R1", others_affected=False, move_constraint_left_in_cib=True
5038 	            ),
5039 	        )
5040 	
5041 	    def test_live_others_affected(self):
5042 	        self.assert_message_from_report(
5043 	            (
5044 	                "Unable to ensure that moved resource 'R1' or other resources "
5045 	                "will stay on the same node after a constraint used for moving "
5046 	                "it is removed."
5047 	                " The constraint to move the resource has not been removed "
5048 	                "from configuration. Consider removing it manually. Be aware "
5049 	                "that removing the constraint may cause resources to move to "
5050 	                "other nodes."
5051 	            ),
5052 	            reports.ResourceMoveAutocleanSimulationFailure(
5053 	                "R1", others_affected=True, move_constraint_left_in_cib=True
5054 	            ),
5055 	        )
5056 	
5057 	
5058 	class ResourceMayOrMayNotMove(NameBuildTest):
5059 	    def test_build_message(self):
5060 	        self.assert_message_from_report(
5061 	            (
5062 	                "A move constraint has been created and the resource 'id' may "
5063 	                "or may not move depending on other configuration"
5064 	            ),
5065 	            reports.ResourceMayOrMayNotMove("id"),
5066 	        )
5067 	
5068 	
5069 	class ParseErrorJsonFile(NameBuildTest):
5070 	    def test_success(self):
5071 	        self.assert_message_from_report(
5072 	            "Unable to parse known-hosts file '/tmp/known-hosts': "
5073 	            "some reason: line 15 column 5 (char 100)",
5074 	            reports.ParseErrorJsonFile(
5075 	                file_type_codes.PCS_KNOWN_HOSTS,
5076 	                15,
5077 	                5,
5078 	                100,
5079 	                "some reason",
5080 	                "some reason: line 15 column 5 (char 100)",
5081 	                file_path="/tmp/known-hosts",
5082 	            ),
5083 	        )
5084 	
5085 	
5086 	class ResourceDisableAffectsOtherResources(NameBuildTest):
5087 	    def test_multiple_disabled(self):
5088 	        self.assert_message_from_report(
5089 	            (
5090 	                "Disabling specified resource would have an effect on these "
5091 	                "resources: 'O1', 'O2'"
5092 	            ),
5093 	            reports.ResourceDisableAffectsOtherResources(
5094 	                ["D1"],
5095 	                ["O2", "O1"],
5096 	            ),
5097 	        )
5098 	
5099 	    def test_multiple_affected(self):
5100 	        self.assert_message_from_report(
5101 	            (
5102 	                "Disabling specified resources would have an effect on this "
5103 	                "resource: 'O1'"
5104 	            ),
5105 	            reports.ResourceDisableAffectsOtherResources(
5106 	                ["D2", "D1"],
5107 	                ["O1"],
5108 	            ),
5109 	        )
5110 	
5111 	
5112 	class DrConfigAlreadyExist(NameBuildTest):
5113 	    def test_success(self):
5114 	        self.assert_message_from_report(
5115 	            "Disaster-recovery already configured",
5116 	            reports.DrConfigAlreadyExist(),
5117 	        )
5118 	
5119 	
5120 	class DrConfigDoesNotExist(NameBuildTest):
5121 	    def test_success(self):
5122 	        self.assert_message_from_report(
5123 	            "Disaster-recovery is not configured",
5124 	            reports.DrConfigDoesNotExist(),
5125 	        )
5126 	
5127 	
5128 	class NodeInLocalCluster(NameBuildTest):
5129 	    def test_success(self):
5130 	        self.assert_message_from_report(
5131 	            "Node 'node-name' is part of local cluster",
5132 	            reports.NodeInLocalCluster("node-name"),
5133 	        )
5134 	
5135 	
5136 	class BoothLackOfSites(NameBuildTest):
5137 	    def test_no_site(self):
5138 	        self.assert_message_from_report(
5139 	            (
5140 	                "lack of sites for booth configuration (need 2 at least): "
5141 	                "sites missing"
5142 	            ),
5143 	            reports.BoothLackOfSites([]),
5144 	        )
5145 	
5146 	    def test_single_site(self):
5147 	        self.assert_message_from_report(
5148 	            (
5149 	                "lack of sites for booth configuration (need 2 at least): "
5150 	                "sites 'site1'"
5151 	            ),
5152 	            reports.BoothLackOfSites(["site1"]),
5153 	        )
5154 	
5155 	    def test_multiple_sites(self):
5156 	        self.assert_message_from_report(
5157 	            (
5158 	                "lack of sites for booth configuration (need 2 at least): "
5159 	                "sites 'site1', 'site2'"
5160 	            ),
5161 	            reports.BoothLackOfSites(["site1", "site2"]),
5162 	        )
5163 	
5164 	
5165 	class BoothEvenPeersNumber(NameBuildTest):
5166 	    def test_success(self):
5167 	        self.assert_message_from_report(
5168 	            "odd number of peers is required (entered 4 peers)",
5169 	            reports.BoothEvenPeersNumber(4),
5170 	        )
5171 	
5172 	
5173 	class BoothAddressDuplication(NameBuildTest):
5174 	    def test_single_address(self):
5175 	        self.assert_message_from_report(
5176 	            "duplicate address for booth configuration: 'addr1'",
5177 	            reports.BoothAddressDuplication(["addr1"]),
5178 	        )
5179 	
5180 	    def test_multiple_addresses(self):
5181 	        self.assert_message_from_report(
5182 	            (
5183 	                "duplicate address for booth configuration: 'addr1', 'addr2', "
5184 	                "'addr3'"
5185 	            ),
5186 	            reports.BoothAddressDuplication(
5187 	                sorted(["addr2", "addr1", "addr3"])
5188 	            ),
5189 	        )
5190 	
5191 	
5192 	class BoothConfigUnexpectedLines(NameBuildTest):
5193 	    def test_single_line(self):
5194 	        self.assert_message_from_report(
5195 	            "unexpected line in booth config:\nline",
5196 	            reports.BoothConfigUnexpectedLines(["line"]),
5197 	        )
5198 	
5199 	    def test_multiple_lines(self):
5200 	        self.assert_message_from_report(
5201 	            "unexpected lines in booth config:\nline\nline2",
5202 	            reports.BoothConfigUnexpectedLines(["line", "line2"]),
5203 	        )
5204 	
5205 	    def test_file_path(self):
5206 	        self.assert_message_from_report(
5207 	            "unexpected line in booth config 'PATH':\nline",
5208 	            reports.BoothConfigUnexpectedLines(["line"], file_path="PATH"),
5209 	        )
5210 	
5211 	
5212 	class BoothInvalidName(NameBuildTest):
5213 	    def test_success(self):
5214 	        self.assert_message_from_report(
5215 	            "booth name '/name' is not valid, it cannot contain /{} characters",
5216 	            reports.BoothInvalidName("/name", "/{}"),
5217 	        )
5218 	
5219 	
5220 	class BoothTicketNameInvalid(NameBuildTest):
5221 	    def test_success(self):
5222 	        self.assert_message_from_report(
5223 	            (
5224 	                "booth ticket name 'ticket&' is not valid, use up to 63 "
5225 	                "alphanumeric characters or dash"
5226 	            ),
5227 	            reports.BoothTicketNameInvalid("ticket&"),
5228 	        )
5229 	
5230 	
5231 	class BoothTicketDuplicate(NameBuildTest):
5232 	    def test_success(self):
5233 	        self.assert_message_from_report(
5234 	            "booth ticket name 'ticket_name' already exists in configuration",
5235 	            reports.BoothTicketDuplicate("ticket_name"),
5236 	        )
5237 	
5238 	
5239 	class BoothTicketDoesNotExist(NameBuildTest):
5240 	    def test_success(self):
5241 	        self.assert_message_from_report(
5242 	            "booth ticket name 'ticket_name' does not exist",
5243 	            reports.BoothTicketDoesNotExist("ticket_name"),
5244 	        )
5245 	
5246 	
5247 	class BoothTicketNotInCib(NameBuildTest):
5248 	    def test_success(self):
5249 	        self.assert_message_from_report(
5250 	            "Unable to find ticket 'name' in CIB",
5251 	            reports.BoothTicketNotInCib("name"),
5252 	        )
5253 	
5254 	
5255 	class BoothAlreadyInCib(NameBuildTest):
5256 	    def test_success(self):
5257 	        self.assert_message_from_report(
5258 	            "booth instance 'name' is already created as cluster resource",
5259 	            reports.BoothAlreadyInCib("name"),
5260 	        )
5261 	
5262 	
5263 	class BoothPathNotExists(NameBuildTest):
5264 	    def test_success(self):
5265 	        self.assert_message_from_report(
5266 	            (
5267 	                "Configuration directory for booth 'path' is missing. Is booth "
5268 	                "installed?"
5269 	            ),
5270 	            reports.BoothPathNotExists("path"),
5271 	        )
5272 	
5273 	
5274 	class BoothNotExistsInCib(NameBuildTest):
5275 	    def test_success(self):
5276 	        self.assert_message_from_report(
5277 	            "booth instance 'name' not found in cib",
5278 	            reports.BoothNotExistsInCib("name"),
5279 	        )
5280 	
5281 	
5282 	class BoothConfigIsUsed(NameBuildTest):
5283 	    def test_cluster(self):
5284 	        self.assert_message_from_report(
5285 	            "booth instance 'name' is used in a cluster resource",
5286 	            reports.BoothConfigIsUsed(
5287 	                "name", reports.const.BOOTH_CONFIG_USED_IN_CLUSTER_RESOURCE
5288 	            ),
5289 	        )
5290 	
5291 	    def test_cluster_resource(self):
5292 	        self.assert_message_from_report(
5293 	            "booth instance 'name' is used in cluster resource 'R'",
5294 	            reports.BoothConfigIsUsed(
5295 	                "name", reports.const.BOOTH_CONFIG_USED_IN_CLUSTER_RESOURCE, "R"
5296 	            ),
5297 	        )
5298 	
5299 	    def test_systemd_enabled(self):
5300 	        self.assert_message_from_report(
5301 	            "booth instance 'name' is used - it is enabled in systemd",
5302 	            reports.BoothConfigIsUsed(
5303 	                "name", reports.const.BOOTH_CONFIG_USED_ENABLED_IN_SYSTEMD
5304 	            ),
5305 	        )
5306 	
5307 	    def test_systemd_running(self):
5308 	        self.assert_message_from_report(
5309 	            "booth instance 'name' is used - it is running by systemd",
5310 	            reports.BoothConfigIsUsed(
5311 	                "name", reports.const.BOOTH_CONFIG_USED_RUNNING_IN_SYSTEMD
5312 	            ),
5313 	        )
5314 	
5315 	
5316 	class BoothMultipleTimesInCib(NameBuildTest):
5317 	    def test_success(self):
5318 	        self.assert_message_from_report(
5319 	            "found more than one booth instance 'name' in cib",
5320 	            reports.BoothMultipleTimesInCib("name"),
5321 	        )
5322 	
5323 	
5324 	class BoothConfigDistributionStarted(NameBuildTest):
5325 	    def test_success(self):
5326 	        self.assert_message_from_report(
5327 	            "Sending booth configuration to cluster nodes...",
5328 	            reports.BoothConfigDistributionStarted(),
5329 	        )
5330 	
5331 	
5332 	class BoothConfigAcceptedByNode(NameBuildTest):
5333 	    def test_defaults(self):
5334 	        self.assert_message_from_report(
5335 	            "Booth config saved",
5336 	            reports.BoothConfigAcceptedByNode(),
5337 	        )
5338 	
5339 	    def test_empty_name_list(self):
5340 	        self.assert_message_from_report(
5341 	            "Booth config saved",
5342 	            reports.BoothConfigAcceptedByNode(name_list=[]),
5343 	        )
5344 	
5345 	    def test_node_and_empty_name_list(self):
5346 	        self.assert_message_from_report(
5347 	            "node1: Booth config saved",
5348 	            reports.BoothConfigAcceptedByNode(node="node1", name_list=[]),
5349 	        )
5350 	
5351 	    def test_name_booth_only(self):
5352 	        self.assert_message_from_report(
5353 	            "Booth config saved",
5354 	            reports.BoothConfigAcceptedByNode(name_list=["booth"]),
5355 	        )
5356 	
5357 	    def test_name_booth_and_node(self):
5358 	        self.assert_message_from_report(
5359 	            "node1: Booth config saved",
5360 	            reports.BoothConfigAcceptedByNode(
5361 	                node="node1",
5362 	                name_list=["booth"],
5363 	            ),
5364 	        )
5365 	
5366 	    def test_single_name(self):
5367 	        self.assert_message_from_report(
5368 	            "Booth config 'some' saved",
5369 	            reports.BoothConfigAcceptedByNode(name_list=["some"]),
5370 	        )
5371 	
5372 	    def test_multiple_names(self):
5373 	        self.assert_message_from_report(
5374 	            "Booth configs 'another', 'some' saved",
5375 	            reports.BoothConfigAcceptedByNode(name_list=["another", "some"]),
5376 	        )
5377 	
5378 	    def test_node(self):
5379 	        self.assert_message_from_report(
5380 	            "node1: Booth configs 'another', 'some' saved",
5381 	            reports.BoothConfigAcceptedByNode(
5382 	                node="node1",
5383 	                name_list=["some", "another"],
5384 	            ),
5385 	        )
5386 	
5387 	
5388 	class BoothConfigDistributionNodeError(NameBuildTest):
5389 	    def test_empty_name(self):
5390 	        self.assert_message_from_report(
5391 	            "Unable to save booth config on node 'node1': reason1",
5392 	            reports.BoothConfigDistributionNodeError("node1", "reason1"),
5393 	        )
5394 	
5395 	    def test_booth_name(self):
5396 	        self.assert_message_from_report(
5397 	            "Unable to save booth config on node 'node1': reason1",
5398 	            reports.BoothConfigDistributionNodeError(
5399 	                "node1",
5400 	                "reason1",
5401 	                name="booth",
5402 	            ),
5403 	        )
5404 	
5405 	    def test_another_name(self):
5406 	        self.assert_message_from_report(
5407 	            "Unable to save booth config 'another' on node 'node1': reason1",
5408 	            reports.BoothConfigDistributionNodeError(
5409 	                "node1",
5410 	                "reason1",
5411 	                name="another",
5412 	            ),
5413 	        )
5414 	
5415 	
5416 	class BoothFetchingConfigFromNode(NameBuildTest):
5417 	    def test_empty_name(self):
5418 	        self.assert_message_from_report(
5419 	            "Fetching booth config from node 'node1'...",
5420 	            reports.BoothFetchingConfigFromNode("node1"),
5421 	        )
5422 	
5423 	    def test_booth_name(self):
5424 	        self.assert_message_from_report(
5425 	            "Fetching booth config from node 'node1'...",
5426 	            reports.BoothFetchingConfigFromNode("node1", config="booth"),
5427 	        )
5428 	
5429 	    def test_another_name(self):
5430 	        self.assert_message_from_report(
5431 	            "Fetching booth config 'another' from node 'node1'...",
5432 	            reports.BoothFetchingConfigFromNode("node1", config="another"),
5433 	        )
5434 	
5435 	
5436 	class BoothUnsupportedFileLocation(NameBuildTest):
5437 	    def test_success(self):
5438 	        self.assert_message_from_report(
5439 	            (
5440 	                "Booth configuration '/some/file' is outside of supported "
5441 	                "booth config directory '/booth/conf/dir/', ignoring the file"
5442 	            ),
5443 	            reports.BoothUnsupportedFileLocation(
5444 	                "/some/file",
5445 	                "/booth/conf/dir/",
5446 	                file_type_codes.BOOTH_CONFIG,
5447 	            ),
5448 	        )
5449 	
5450 	
5451 	class BoothDaemonStatusError(NameBuildTest):
5452 	    def test_success(self):
5453 	        self.assert_message_from_report(
5454 	            "unable to get status of booth daemon: some reason",
5455 	            reports.BoothDaemonStatusError("some reason"),
5456 	        )
5457 	
5458 	
5459 	class BoothTicketStatusError(NameBuildTest):
5460 	    def test_minimal(self):
5461 	        self.assert_message_from_report(
5462 	            "unable to get status of booth tickets",
5463 	            reports.BoothTicketStatusError(),
5464 	        )
5465 	
5466 	    def test_all(self):
5467 	        self.assert_message_from_report(
5468 	            "unable to get status of booth tickets: some reason",
5469 	            reports.BoothTicketStatusError(reason="some reason"),
5470 	        )
5471 	
5472 	
5473 	class BoothPeersStatusError(NameBuildTest):
5474 	    def test_minimal(self):
5475 	        self.assert_message_from_report(
5476 	            "unable to get status of booth peers",
5477 	            reports.BoothPeersStatusError(),
5478 	        )
5479 	
5480 	    def test_all(self):
5481 	        self.assert_message_from_report(
5482 	            "unable to get status of booth peers: some reason",
5483 	            reports.BoothPeersStatusError(reason="some reason"),
5484 	        )
5485 	
5486 	
5487 	class BoothCannotDetermineLocalSiteIp(NameBuildTest):
5488 	    def test_success(self):
5489 	        self.assert_message_from_report(
5490 	            "cannot determine local site ip, please specify site parameter",
5491 	            reports.BoothCannotDetermineLocalSiteIp(),
5492 	        )
5493 	
5494 	
5495 	class BoothTicketOperationFailed(NameBuildTest):
5496 	    def test_success(self):
5497 	        self.assert_message_from_report(
5498 	            (
5499 	                "unable to operation booth ticket 'ticket_name'"
5500 	                " for site 'site_ip', reason: reason"
5501 	            ),
5502 	            reports.BoothTicketOperationFailed(
5503 	                "operation", "reason", "site_ip", "ticket_name"
5504 	            ),
5505 	        )
5506 	
5507 	    def test_no_site_ip(self):
5508 	        self.assert_message_from_report(
5509 	            ("unable to operation booth ticket 'ticket_name', reason: reason"),
5510 	            reports.BoothTicketOperationFailed(
5511 	                "operation", "reason", None, "ticket_name"
5512 	            ),
5513 	        )
5514 	
5515 	
5516 	class BoothTicketChangingState(NameBuildTest):
5517 	    def test_success(self):
5518 	        self.assert_message_from_report(
5519 	            "Changing state of ticket 'name' to standby",
5520 	            reports.BoothTicketChangingState("name", "standby"),
5521 	        )
5522 	
5523 	
5524 	class BoothTicketCleanup(NameBuildTest):
5525 	    def test_success(self):
5526 	        self.assert_message_from_report(
5527 	            "Cleaning up ticket 'name' from CIB",
5528 	            reports.BoothTicketCleanup("name"),
5529 	        )
5530 	
5531 	
5532 	# TODO: remove, use ADD_REMOVE reports
5533 	class TagAddRemoveIdsDuplication(NameBuildTest):
5534 	    def test_message_add(self):
5535 	        self.assert_message_from_report(
5536 	            "Ids to add must be unique, duplicate ids: 'dup1', 'dup2'",
5537 	            reports.TagAddRemoveIdsDuplication(
5538 	                duplicate_ids_list=["dup2", "dup1"],
5539 	            ),
5540 	        )
5541 	
5542 	    def test_message_remove(self):
5543 	        self.assert_message_from_report(
5544 	            "Ids to remove must be unique, duplicate ids: 'dup1', 'dup2'",
5545 	            reports.TagAddRemoveIdsDuplication(
5546 	                duplicate_ids_list=["dup2", "dup1"],
5547 	                add_or_not_remove=False,
5548 	            ),
5549 	        )
5550 	
5551 	
5552 	# TODO: remove, use ADD_REMOVE reports
5553 	class TagAdjacentReferenceIdNotInTheTag(NameBuildTest):
5554 	    def test_message(self):
5555 	        self.assert_message_from_report(
5556 	            (
5557 	                "There is no reference id 'adj_id' in the tag 'tag_id', cannot "
5558 	                "put reference ids next to it in the tag"
5559 	            ),
5560 	            reports.TagAdjacentReferenceIdNotInTheTag("adj_id", "tag_id"),
5561 	        )
5562 	
5563 	
5564 	# TODO: remove, use ADD_REMOVE reports
5565 	class TagCannotAddAndRemoveIdsAtTheSameTime(NameBuildTest):
5566 	    def test_message_one_item(self):
5567 	        self.assert_message_from_report(
5568 	            "Ids cannot be added and removed at the same time: 'id1'",
5569 	            reports.TagCannotAddAndRemoveIdsAtTheSameTime(["id1"]),
5570 	        )
5571 	
5572 	    def test_message_more_items(self):
5573 	        self.assert_message_from_report(
5574 	            (
5575 	                "Ids cannot be added and removed at the same time: 'id1', "
5576 	                "'id2', 'id3'"
5577 	            ),
5578 	            reports.TagCannotAddAndRemoveIdsAtTheSameTime(
5579 	                ["id3", "id2", "id1"],
5580 	            ),
5581 	        )
5582 	
5583 	
5584 	# TODO: remove, use ADD_REMOVE reports
5585 	class TagCannotAddReferenceIdsAlreadyInTheTag(NameBuildTest):
5586 	    def test_message_singular(self):
5587 	        self.assert_message_from_report(
5588 	            "Cannot add reference id already in the tag 'tag_id': 'id1'",
5589 	            reports.TagCannotAddReferenceIdsAlreadyInTheTag(
5590 	                "tag_id",
5591 	                ["id1"],
5592 	            ),
5593 	        )
5594 	
5595 	    def test_message_plural(self):
5596 	        self.assert_message_from_report(
5597 	            "Cannot add reference ids already in the tag 'TAG': 'id1', 'id2'",
5598 	            reports.TagCannotAddReferenceIdsAlreadyInTheTag(
5599 	                "TAG",
5600 	                ["id2", "id1"],
5601 	            ),
5602 	        )
5603 	
5604 	
5605 	class TagCannotContainItself(NameBuildTest):
5606 	    def test_message(self):
5607 	        self.assert_message_from_report(
5608 	            "Tag cannot contain itself", reports.TagCannotContainItself()
5609 	        )
5610 	
5611 	
5612 	class TagCannotCreateEmptyTagNoIdsSpecified(NameBuildTest):
5613 	    def test_message(self):
5614 	        self.assert_message_from_report(
5615 	            "Cannot create empty tag, no resource ids specified",
5616 	            reports.TagCannotCreateEmptyTagNoIdsSpecified(),
5617 	        )
5618 	
5619 	
5620 	# TODO: remove, use ADD_REMOVE reports
5621 	class TagCannotPutIdNextToItself(NameBuildTest):
5622 	    def test_message(self):
5623 	        self.assert_message_from_report(
5624 	            "Cannot put id 'some_id' next to itself.",
5625 	            reports.TagCannotPutIdNextToItself("some_id"),
5626 	        )
5627 	
5628 	
5629 	# TODO: remove, use ADD_REMOVE reports
5630 	class TagCannotRemoveAdjacentId(NameBuildTest):
5631 	    def test_message(self):
5632 	        self.assert_message_from_report(
5633 	            "Cannot remove id 'some_id' next to which ids are being added",
5634 	            reports.TagCannotRemoveAdjacentId("some_id"),
5635 	        )
5636 	
5637 	
5638 	# TODO: remove, use ADD_REMOVE reports
5639 	class TagCannotRemoveReferencesWithoutRemovingTag(NameBuildTest):
5640 	    def test_message(self):
5641 	        self.assert_message_from_report(
5642 	            "There would be no references left in the tag 'tag-id'",
5643 	            reports.TagCannotRemoveReferencesWithoutRemovingTag("tag-id"),
5644 	        )
5645 	
5646 	
5647 	class TagCannotRemoveTagReferencedInConstraints(NameBuildTest):
5648 	    def test_message_singular(self):
5649 	        self.assert_message_from_report(
5650 	            "Tag 'tag1' cannot be removed because it is referenced in "
5651 	            "constraint 'constraint-id-1'",
5652 	            reports.TagCannotRemoveTagReferencedInConstraints(
5653 	                "tag1",
5654 	                ["constraint-id-1"],
5655 	            ),
5656 	        )
5657 	
5658 	    def test_message_plural(self):
5659 	        self.assert_message_from_report(
5660 	            "Tag 'tag2' cannot be removed because it is referenced in "
5661 	            "constraints 'constraint-id-1', 'constraint-id-2'",
5662 	            reports.TagCannotRemoveTagReferencedInConstraints(
5663 	                "tag2",
5664 	                ["constraint-id-2", "constraint-id-1"],
5665 	            ),
5666 	        )
5667 	
5668 	
5669 	class TagCannotRemoveTagsNoTagsSpecified(NameBuildTest):
5670 	    def test_message(self):
5671 	        self.assert_message_from_report(
5672 	            "Cannot remove tags, no tags to remove specified",
5673 	            reports.TagCannotRemoveTagsNoTagsSpecified(),
5674 	        )
5675 	
5676 	
5677 	# TODO: remove, use ADD_REMOVE reports
5678 	class TagCannotSpecifyAdjacentIdWithoutIdsToAdd(NameBuildTest):
5679 	    def test_message(self):
5680 	        self.assert_message_from_report(
5681 	            "Cannot specify adjacent id 'some-id' without ids to add",
5682 	            reports.TagCannotSpecifyAdjacentIdWithoutIdsToAdd("some-id"),
5683 	        )
5684 	
5685 	
5686 	# TODO: remove, use ADD_REMOVE reports
5687 	class TagCannotUpdateTagNoIdsSpecified(NameBuildTest):
5688 	    def test_message(self):
5689 	        self.assert_message_from_report(
5690 	            "Cannot update tag, no ids to be added or removed specified",
5691 	            reports.TagCannotUpdateTagNoIdsSpecified(),
5692 	        )
5693 	
5694 	
5695 	# TODO: remove, use ADD_REMOVE reports
5696 	class TagIdsNotInTheTag(NameBuildTest):
5697 	    def test_message_singular(self):
5698 	        self.assert_message_from_report(
5699 	            "Tag 'tag-id' does not contain id: 'a'",
5700 	            reports.TagIdsNotInTheTag("tag-id", ["a"]),
5701 	        )
5702 	
5703 	    def test_message_plural(self):
5704 	        self.assert_message_from_report(
5705 	            "Tag 'tag-id' does not contain ids: 'a', 'b'",
5706 	            reports.TagIdsNotInTheTag("tag-id", ["b", "a"]),
5707 	        )
5708 	
5709 	
5710 	class RuleInEffectStatusDetectionNotSupported(NameBuildTest):
5711 	    def test_success(self):
5712 	        self.assert_message_from_report(
5713 	            (
5714 	                "crm_rule is not available, therefore expired parts of "
5715 	                "configuration may not be detected. Consider upgrading pacemaker."
5716 	            ),
5717 	            reports.RuleInEffectStatusDetectionNotSupported(),
5718 	        )
5719 	
5720 	
5721 	class RuleExpressionOptionsDuplication(NameBuildTest):
5722 	    def test_success(self):
5723 	        self.assert_message_from_report(
5724 	            "Duplicate options in a single (sub)expression: 'key', 'name'",
5725 	            reports.RuleExpressionOptionsDuplication(["name", "key"]),
5726 	        )
5727 	
5728 	
5729 	class RuleExpressionSinceGreaterThanUntil(NameBuildTest):
5730 	    def test_success(self):
5731 	        self.assert_message_from_report(
5732 	            "Since '987' is not sooner than until '654'",
5733 	            reports.RuleExpressionSinceGreaterThanUntil("987", "654"),
5734 	        )
5735 	
5736 	
5737 	class RuleExpressionParseError(NameBuildTest):
5738 	    def test_success(self):
5739 	        self.assert_message_from_report(
5740 	            "'resource dummy op monitor' is not a valid rule expression, "
5741 	            "parse error near or after line 1 column 16",
5742 	            reports.RuleExpressionParseError(
5743 	                "resource dummy op monitor",
5744 	                "Expected end of text",
5745 	                "resource dummy op monitor",
5746 	                1,
5747 	                16,
5748 	                15,
5749 	            ),
5750 	        )
5751 	
5752 	
5753 	class RuleExpressionNotAllowed(NameBuildTest):
5754 	    def test_op(self):
5755 	        self.assert_message_from_report(
5756 	            "Keyword 'op' cannot be used in a rule in this command",
5757 	            reports.RuleExpressionNotAllowed(
5758 	                CibRuleExpressionType.OP_EXPRESSION
5759 	            ),
5760 	        )
5761 	
5762 	    def test_rsc(self):
5763 	        self.assert_message_from_report(
5764 	            "Keyword 'resource' cannot be used in a rule in this command",
5765 	            reports.RuleExpressionNotAllowed(
5766 	                CibRuleExpressionType.RSC_EXPRESSION
5767 	            ),
5768 	        )
5769 	
5770 	    def test_node_attr(self):
5771 	        self.assert_message_from_report(
5772 	            "Keywords 'defined', 'not_defined', 'eq', 'ne', 'gte', 'gt', "
5773 	            "'lte' and 'lt' cannot be used in a rule in this command",
5774 	            reports.RuleExpressionNotAllowed(CibRuleExpressionType.EXPRESSION),
5775 	        )
5776 	
5777 	
5778 	class RuleNoExpressionSpecified(NameBuildTest):
5779 	    def test_success(self):
5780 	        self.assert_message_from_report(
5781 	            "No rule expression was specified",
5782 	            reports.RuleNoExpressionSpecified(),
5783 	        )
5784 	
5785 	
5786 	class CibNvsetAmbiguousProvideNvsetId(NameBuildTest):
5787 	    def test_success(self):
5788 	        self.assert_message_from_report(
5789 	            "Several options sets exist, please specify an option set ID",
5790 	            reports.CibNvsetAmbiguousProvideNvsetId(
5791 	                const.PCS_COMMAND_RESOURCE_DEFAULTS_UPDATE
5792 	            ),
5793 	        )
5794 	
5795 	
5796 	class AddRemoveItemsNotSpecified(NameBuildTest):
5797 	    def test_message(self):
5798 	        self.assert_message_from_report(
5799 	            (
5800 	                "Cannot modify stonith resource 'container-id', no devices to "
5801 	                "add or remove specified"
5802 	            ),
5803 	            reports.AddRemoveItemsNotSpecified(
5804 	                const.ADD_REMOVE_CONTAINER_TYPE_STONITH_RESOURCE,
5805 	                const.ADD_REMOVE_ITEM_TYPE_DEVICE,
5806 	                "container-id",
5807 	            ),
5808 	        )
5809 	
5810 	
5811 	class AddRemoveItemsDuplication(NameBuildTest):
5812 	    def test_message(self):
5813 	        self.assert_message_from_report(
5814 	            (
5815 	                "Devices to add or remove must be unique, duplicate devices: "
5816 	                "'dup1', 'dup2'"
5817 	            ),
5818 	            reports.AddRemoveItemsDuplication(
5819 	                const.ADD_REMOVE_CONTAINER_TYPE_STONITH_RESOURCE,
5820 	                const.ADD_REMOVE_ITEM_TYPE_DEVICE,
5821 	                "container-id",
5822 	                ["dup2", "dup1"],
5823 	            ),
5824 	        )
5825 	
5826 	
5827 	class AddRemoveCannotAddItemsAlreadyInTheContainer(NameBuildTest):
5828 	    def test_message_plural(self):
5829 	        self.assert_message_from_report(
5830 	            "Cannot add devices 'i1', 'i2', they are already present in stonith"
5831 	            " resource 'container-id'",
5832 	            reports.AddRemoveCannotAddItemsAlreadyInTheContainer(
5833 	                const.ADD_REMOVE_CONTAINER_TYPE_STONITH_RESOURCE,
5834 	                const.ADD_REMOVE_ITEM_TYPE_DEVICE,
5835 	                "container-id",
5836 	                ["i2", "i1"],
5837 	            ),
5838 	        )
5839 	
5840 	    def test_message_singular(self):
5841 	        self.assert_message_from_report(
5842 	            "Cannot add device 'i1', it is already present in stonith resource "
5843 	            "'container-id'",
5844 	            reports.AddRemoveCannotAddItemsAlreadyInTheContainer(
5845 	                const.ADD_REMOVE_CONTAINER_TYPE_STONITH_RESOURCE,
5846 	                const.ADD_REMOVE_ITEM_TYPE_DEVICE,
5847 	                "container-id",
5848 	                ["i1"],
5849 	            ),
5850 	        )
5851 	
5852 	
5853 	class AddRemoveCannotRemoveItemsNotInTheContainer(NameBuildTest):
5854 	    def test_message_plural(self):
5855 	        self.assert_message_from_report(
5856 	            (
5857 	                "Cannot remove devices 'i1', 'i2', they are not present in "
5858 	                "stonith resource 'container-id'"
5859 	            ),
5860 	            reports.AddRemoveCannotRemoveItemsNotInTheContainer(
5861 	                const.ADD_REMOVE_CONTAINER_TYPE_STONITH_RESOURCE,
5862 	                const.ADD_REMOVE_ITEM_TYPE_DEVICE,
5863 	                "container-id",
5864 	                ["i2", "i1"],
5865 	            ),
5866 	        )
5867 	
5868 	    def test_message_singular(self):
5869 	        self.assert_message_from_report(
5870 	            (
5871 	                "Cannot remove device 'i1', it is not present in "
5872 	                "stonith resource 'container-id'"
5873 	            ),
5874 	            reports.AddRemoveCannotRemoveItemsNotInTheContainer(
5875 	                const.ADD_REMOVE_CONTAINER_TYPE_STONITH_RESOURCE,
5876 	                const.ADD_REMOVE_ITEM_TYPE_DEVICE,
5877 	                "container-id",
5878 	                ["i1"],
5879 	            ),
5880 	        )
5881 	
5882 	
5883 	class AddRemoveCannotAddAndRemoveItemsAtTheSameTime(NameBuildTest):
5884 	    def test_message_plural(self):
5885 	        self.assert_message_from_report(
5886 	            "Devices cannot be added and removed at the same time: 'i1', 'i2'",
5887 	            reports.AddRemoveCannotAddAndRemoveItemsAtTheSameTime(
5888 	                const.ADD_REMOVE_CONTAINER_TYPE_STONITH_RESOURCE,
5889 	                const.ADD_REMOVE_ITEM_TYPE_DEVICE,
5890 	                "container-id",
5891 	                ["i2", "i1"],
5892 	            ),
5893 	        )
5894 	
5895 	    def test_message_singular(self):
5896 	        self.assert_message_from_report(
5897 	            "Device cannot be added and removed at the same time: 'i1'",
5898 	            reports.AddRemoveCannotAddAndRemoveItemsAtTheSameTime(
5899 	                const.ADD_REMOVE_CONTAINER_TYPE_STONITH_RESOURCE,
5900 	                const.ADD_REMOVE_ITEM_TYPE_DEVICE,
5901 	                "container-id",
5902 	                ["i1"],
5903 	            ),
5904 	        )
5905 	
5906 	
5907 	class AddRemoveCannotRemoveAllItemsFromTheContainer(NameBuildTest):
5908 	    def test_message(self):
5909 	        self.assert_message_from_report(
5910 	            "Cannot remove all devices from stonith resource 'container-id'",
5911 	            reports.AddRemoveCannotRemoveAllItemsFromTheContainer(
5912 	                const.ADD_REMOVE_CONTAINER_TYPE_STONITH_RESOURCE,
5913 	                const.ADD_REMOVE_ITEM_TYPE_DEVICE,
5914 	                "container-id",
5915 	                ["i1", "i2"],
5916 	            ),
5917 	        )
5918 	
5919 	
5920 	class AddRemoveAdjacentItemNotInTheContainer(NameBuildTest):
5921 	    def test_message(self):
5922 	        self.assert_message_from_report(
5923 	            (
5924 	                "There is no device 'adjacent-item-id' in the stonith resource "
5925 	                "'container-id', cannot add devices next to it"
5926 	            ),
5927 	            reports.AddRemoveAdjacentItemNotInTheContainer(
5928 	                const.ADD_REMOVE_CONTAINER_TYPE_STONITH_RESOURCE,
5929 	                const.ADD_REMOVE_ITEM_TYPE_DEVICE,
5930 	                "container-id",
5931 	                "adjacent-item-id",
5932 	            ),
5933 	        )
5934 	
5935 	
5936 	class AddRemoveCannotPutItemNextToItself(NameBuildTest):
5937 	    def test_message(self):
5938 	        self.assert_message_from_report(
5939 	            "Cannot put device 'adjacent-item-id' next to itself",
5940 	            reports.AddRemoveCannotPutItemNextToItself(
5941 	                const.ADD_REMOVE_CONTAINER_TYPE_STONITH_RESOURCE,
5942 	                const.ADD_REMOVE_ITEM_TYPE_DEVICE,
5943 	                "container-id",
5944 	                "adjacent-item-id",
5945 	            ),
5946 	        )
5947 	
5948 	
5949 	class AddRemoveCannotSpecifyAdjacentItemWithoutItemsToAdd(NameBuildTest):
5950 	    def test_message(self):
5951 	        self.assert_message_from_report(
5952 	            (
5953 	                "Cannot specify adjacent device 'adjacent-item-id' without "
5954 	                "devices to add"
5955 	            ),
5956 	            reports.AddRemoveCannotSpecifyAdjacentItemWithoutItemsToAdd(
5957 	                const.ADD_REMOVE_CONTAINER_TYPE_STONITH_RESOURCE,
5958 	                const.ADD_REMOVE_ITEM_TYPE_DEVICE,
5959 	                "container-id",
5960 	                "adjacent-item-id",
5961 	            ),
5962 	        )
5963 	
5964 	
5965 	class CloningStonithResourcesHasNoEffect(NameBuildTest):
5966 	    def test_singular_without_group_id(self):
5967 	        self.assert_message_from_report(
5968 	            (
5969 	                "No need to clone stonith resource 'fence1', any node can use "
5970 	                "a stonith resource (unless specifically banned) regardless of "
5971 	                "whether the stonith resource is running on that node or not"
5972 	            ),
5973 	            reports.CloningStonithResourcesHasNoEffect(["fence1"]),
5974 	        )
5975 	
5976 	    def test_plural_with_group_id(self):
5977 	        self.assert_message_from_report(
5978 	            (
5979 	                "Group 'StonithGroup' contains stonith resources. No need to "
5980 	                "clone stonith resources 'fence1', 'fence2', any node can use "
5981 	                "a stonith resource (unless specifically banned) regardless of "
5982 	                "whether the stonith resource is running on that node or not"
5983 	            ),
5984 	            reports.CloningStonithResourcesHasNoEffect(
5985 	                ["fence1", "fence2"], "StonithGroup"
5986 	            ),
5987 	        )
5988 	
5989 	
5990 	class CommandInvalidPayload(NameBuildTest):
5991 	    def test_all(self):
5992 	        reason = "a reason"
5993 	        self.assert_message_from_report(
5994 	            f"Invalid command payload: {reason}",
5995 	            reports.CommandInvalidPayload(reason),
5996 	        )
5997 	
5998 	
5999 	class CommandUnknown(NameBuildTest):
6000 	    def test_all(self):
6001 	        cmd = "a cmd"
6002 	        self.assert_message_from_report(
6003 	            f"Unknown command '{cmd}'",
6004 	            reports.CommandUnknown(cmd),
6005 	        )
6006 	
6007 	
6008 	class NotAuthorized(NameBuildTest):
6009 	    def test_all(self):
6010 	        self.assert_message_from_report(
6011 	            "Current user is not authorized for this operation",
6012 	            reports.NotAuthorized(),
6013 	        )
6014 	
6015 	
6016 	class AgentSelfValidationResult(NameBuildTest):
6017 	    def test_message(self):
6018 	        lines = [f"line #{i}" for i in range(3)]
6019 	        self.assert_message_from_report(
6020 	            "Validation result from agent:\n  {}".format("\n  ".join(lines)),
6021 	            reports.AgentSelfValidationResult("\n".join(lines)),
6022 	        )
6023 	
6024 	
6025 	class AgentSelfValidationInvalidData(NameBuildTest):
6026 	    def test_message(self):
6027 	        reason = "not xml"
6028 	        self.assert_message_from_report(
6029 	            f"Invalid validation data from agent: {reason}",
6030 	            reports.AgentSelfValidationInvalidData(reason),
6031 	        )
6032 	
6033 	
6034 	class AgentSelfValidationSkippedUpdatedResourceMisconfigured(NameBuildTest):
6035 	    def test_message(self):
6036 	        lines = [f"line #{i}" for i in range(3)]
6037 	        self.assert_message_from_report(
6038 	            (
6039 	                "The resource was misconfigured before the update, therefore "
6040 	                "agent self-validation will not be run for the updated "
6041 	                "configuration. Validation output of the original "
6042 	                "configuration:\n  {}"
6043 	            ).format("\n  ".join(lines)),
6044 	            reports.AgentSelfValidationSkippedUpdatedResourceMisconfigured(
6045 	                "\n".join(lines)
6046 	            ),
6047 	        )
6048 	
6049 	
6050 	class AgentSelfValidationAutoOnWithWarnings(NameBuildTest):
6051 	    def test_message(self):
6052 	        self.assert_message_from_report(
6053 	            (
6054 	                "Validating resource options using the resource agent itself "
6055 	                "is enabled by default and produces warnings. In a future "
6056 	                "version, this might be changed to errors. Enable "
6057 	                "agent validation to switch to the future behavior."
6058 	            ),
6059 	            reports.AgentSelfValidationAutoOnWithWarnings(),
6060 	        )
6061 	
6062 	
6063 	class ResourceCloneIncompatibleMetaAttributes(NameBuildTest):
6064 	    def test_with_provider(self):
6065 	        attr = "attr_name"
6066 	        self.assert_message_from_report(
6067 	            f"Clone option '{attr}' is not compatible with 'standard:provider:type' resource agent",
6068 	            reports.ResourceCloneIncompatibleMetaAttributes(
6069 	                attr, ResourceAgentNameDto("standard", "provider", "type")
6070 	            ),
6071 	        )
6072 	
6073 	    def test_without_provider(self):
6074 	        attr = "attr_name"
6075 	        self.assert_message_from_report(
6076 	            f"Clone option '{attr}' is not compatible with 'standard:type' resource agent",
6077 	            reports.ResourceCloneIncompatibleMetaAttributes(
6078 	                attr, ResourceAgentNameDto("standard", None, "type")
6079 	            ),
6080 	        )
6081 	
6082 	    def test_resource_id(self):
6083 	        attr = "attr_name"
6084 	        res_id = "resource_id"
6085 	        self.assert_message_from_report(
6086 	            (
6087 	                f"Clone option '{attr}' is not compatible with 'standard:type' "
6088 	                f"resource agent of resource '{res_id}'"
6089 	            ),
6090 	            reports.ResourceCloneIncompatibleMetaAttributes(
6091 	                attr,
6092 	                ResourceAgentNameDto("standard", None, "type"),
6093 	                resource_id=res_id,
6094 	            ),
6095 	        )
6096 	
6097 	    def test_group_id(self):
6098 	        attr = "attr_name"
6099 	        res_id = "resource id"
6100 	        group_id = "group id"
6101 	        self.assert_message_from_report(
6102 	            (
6103 	                f"Clone option '{attr}' is not compatible with 'standard:type' "
6104 	                f"resource agent of resource '{res_id}' in group '{group_id}'"
6105 	            ),
6106 	            reports.ResourceCloneIncompatibleMetaAttributes(
6107 	                attr,
6108 	                ResourceAgentNameDto("standard", None, "type"),
6109 	                resource_id=res_id,
6110 	                group_id=group_id,
6111 	            ),
6112 	        )
6113 	
6114 	
6115 	class BoothAuthfileNotUsed(NameBuildTest):
6116 	    def test_message(self):
6117 	        self.assert_message_from_report(
6118 	            "Booth authfile is not enabled",
6119 	            reports.BoothAuthfileNotUsed("instance name"),
6120 	        )
6121 	
6122 	
6123 	class BoothUnsupportedOptionEnableAuthfile(NameBuildTest):
6124 	    def test_message(self):
6125 	        self.assert_message_from_report(
6126 	            "Unsupported option 'enable-authfile' is set in booth configuration",
6127 	            reports.BoothUnsupportedOptionEnableAuthfile("instance name"),
6128 	        )
6129 	
6130 	
6131 	class CannotCreateDefaultClusterPropertySet(NameBuildTest):
6132 	    def test_all(self):
6133 	        self.assert_message_from_report(
6134 	            (
6135 	                "Cannot create default cluster_property_set element, ID "
6136 	                "'cib-bootstrap-options' already exists. Find elements with the"
6137 	                " ID and remove them from cluster configuration."
6138 	            ),
6139 	            reports.CannotCreateDefaultClusterPropertySet(
6140 	                "cib-bootstrap-options"
6141 	            ),
6142 	        )
6143 	
6144 	
6145 	class ClusterStatusBundleMemberIdAsImplicit(NameBuildTest):
6146 	    def test_one(self):
6147 	        self.assert_message_from_report(
6148 	            (
6149 	                "Skipping bundle 'resource-bundle': resource 'resource' has "
6150 	                "the same id as some of the implicit bundle resources"
6151 	            ),
6152 	            reports.ClusterStatusBundleMemberIdAsImplicit(
6153 	                "resource-bundle", ["resource"]
6154 	            ),
6155 	        )
6156 	
6157 	    def test_multiple(self):
6158 	        self.assert_message_from_report(
6159 	            (
6160 	                "Skipping bundle 'resource-bundle': resources 'resource-0', "
6161 	                "'resource-1' have the same id as some of the implicit bundle "
6162 	                "resources"
6163 	            ),
6164 	            reports.ClusterStatusBundleMemberIdAsImplicit(
6165 	                "resource-bundle", ["resource-0", "resource-1"]
6166 	            ),
6167 	        )
6168 	
6169 	
6170 	class ResourceWaitDeprecated(NameBuildTest):
6171 	    def test_success(self):
6172 	        self.assert_message_from_report(
6173 	            (
6174 	                "Ability of this command to accept 'wait' argument is "
6175 	                "deprecated and will be removed in a future release."
6176 	            ),
6177 	            reports.ResourceWaitDeprecated(),
6178 	        )
6179 	
6180 	
6181 	class CommandArgumentTypeMismatch(NameBuildTest):
6182 	    def test_message(self) -> str:
6183 	        self.assert_message_from_report(
6184 	            "This command does not accept entity type.",
6185 	            reports.CommandArgumentTypeMismatch(
6186 	                "entity type", "pcs stonith create"
6187 	            ),
6188 	        )
6189 	
6190 	
6191 	class ResourceRestartError(NameBuildTest):
6192 	    def test_message(self) -> str:
6193 	        self.assert_message_from_report(
6194 	            "Unable to restart resource 'resourceId':\nerror description",
6195 	            reports.ResourceRestartError("error description", "resourceId"),
6196 	        )
6197 	
6198 	
6199 	class ResourceRestartNodeIsForMultiinstanceOnly(NameBuildTest):
6200 	    def test_message(self) -> str:
6201 	        self.assert_message_from_report(
6202 	            (
6203 	                "Can only restart on a specific node for a clone or bundle, "
6204 	                "'resourceId' is a resource"
6205 	            ),
6206 	            reports.ResourceRestartNodeIsForMultiinstanceOnly(
6207 	                "resourceId", "primitive", "node01"
6208 	            ),
6209 	        )
6210 	
6211 	
6212 	class ResourceRestartUsingParentRersource(NameBuildTest):
6213 	    def test_message(self) -> str:
6214 	        self.assert_message_from_report(
6215 	            (
6216 	                "Restarting 'parentId' instead...\n"
6217 	                "(If a resource is a clone or bundle, you must use the clone "
6218 	                "or bundle instead)"
6219 	            ),
6220 	            reports.ResourceRestartUsingParentRersource(
6221 	                "resourceId", "parentId"
6222 	            ),
6223 	        )
6224 	
6225 	
6226 	class ClusterOptionsMetadataNotSupported(NameBuildTest):
6227 	    def test_success(self):
6228 	        self.assert_message_from_report(
6229 	            (
6230 	                "Cluster options metadata are not supported, please upgrade "
6231 	                "pacemaker"
6232 	            ),
6233 	            reports.ClusterOptionsMetadataNotSupported(),
6234 	        )
6235 	
6236 	
6237 	class StoppingResources(NameBuildTest):
6238 	    def test_one_resource(self):
6239 	        self.assert_message_from_report(
6240 	            "Stopping resource 'resourceId'",
6241 	            reports.StoppingResources(["resourceId"]),
6242 	        )
6243 	
6244 	    def test_multiple_resources(self):
6245 	        self.assert_message_from_report(
6246 	            "Stopping resources 'resourceId1', 'resourceId2'",
6247 	            reports.StoppingResources(["resourceId1", "resourceId2"]),
6248 	        )
6249 	
6250 	
6251 	class StoppedResourcesBeforeDeleteCheckSkipped(NameBuildTest):
6252 	    def test_one_resource(self):
6253 	        self.assert_message_from_report(
6254 	            (
6255 	                "Not checking if resource 'A' is stopped before deletion. "
6256 	                "Deleting unstopped resources may result in orphaned resources "
6257 	                "being present in the cluster."
6258 	            ),
6259 	            reports.StoppedResourcesBeforeDeleteCheckSkipped(["A"]),
6260 	        )
6261 	
6262 	    def test_multiple_resources(self):
6263 	        self.assert_message_from_report(
6264 	            (
6265 	                "Not checking if resources 'A', 'B' are stopped before "
6266 	                "deletion. Deleting unstopped resources may result in orphaned "
6267 	                "resources being present in the cluster."
6268 	            ),
6269 	            reports.StoppedResourcesBeforeDeleteCheckSkipped(["A", "B"]),
6270 	        )
6271 	
6272 	    def test_with_reason(self):
6273 	        self.assert_message_from_report(
6274 	            (
6275 	                "Not checking if resource 'A' is stopped before deletion "
6276 	                "because the command does not run on a live cluster. Deleting "
6277 	                "unstopped resources may result in orphaned resources being "
6278 	                "present in the cluster."
6279 	            ),
6280 	            reports.StoppedResourcesBeforeDeleteCheckSkipped(
6281 	                ["A"], reports.const.REASON_NOT_LIVE_CIB
6282 	            ),
6283 	        )
6284 	
6285 	
6286 	class CannotRemoveResourcesNotStopped(NameBuildTest):
6287 	    def test_one_resource(self) -> str:
6288 	        self.assert_message_from_report(
6289 	            (
6290 	                "Resource 'resourceId' is not stopped, removing unstopped "
6291 	                "resources can lead to orphaned resources being present in the "
6292 	                "cluster."
6293 	            ),
6294 	            reports.CannotRemoveResourcesNotStopped(["resourceId"]),
6295 	        )
6296 	
6297 	    def test_multiple_resources(self) -> str:
6298 	        self.assert_message_from_report(
6299 	            (
6300 	                "Resources 'resourceId1', 'resourceId2' are not stopped, "
6301 	                "removing unstopped resources can lead to orphaned resources "
6302 	                "being present in the cluster."
6303 	            ),
6304 	            reports.CannotRemoveResourcesNotStopped(
6305 	                ["resourceId1", "resourceId2"]
6306 	            ),
6307 	        )
6308 	
6309 	
6310 	class DlmClusterRenameNeeded(NameBuildTest):
6311 	    def test_success(self):
6312 	        self.assert_message_from_report(
6313 	            (
6314 	                "The DLM cluster name in the shared volume groups metadata "
6315 	                "must be updated to reflect the name of the cluster so that "
6316 	                "the volume groups can start"
6317 	            ),
6318 	            reports.DlmClusterRenameNeeded(),
6319 	        )
6320 	
6321 	
6322 	class Gfs2LockTableRenameNeeded(NameBuildTest):
6323 	    def test_success(self):
6324 	        self.assert_message_from_report(
6325 	            (
6326 	                "The lock table name on each GFS2 filesystem must be updated "
6327 	                "to reflect the name of the cluster so that the filesystems "
6328 	                "can be mounted"
6329 	            ),
6330 	            reports.Gfs2LockTableRenameNeeded(),
6331 	        )
6332 	
6333 	
6334 	class CibClusterNameRemovalStarted(NameBuildTest):
6335 	    def test_success(self):
6336 	        self.assert_message_from_report(
6337 	            "Removing CIB cluster name property on nodes...",
6338 	            reports.CibClusterNameRemovalStarted(),
6339 	        )
6340 	
6341 	
6342 	class CibClusterNameRemoved(NameBuildTest):
6343 	    def test_success(self):
6344 	        self.assert_message_from_report(
6345 	            "node: Succeeded", reports.CibClusterNameRemoved("node")
6346 	        )
6347 	
6348 	
6349 	class CibClusterNameRemovalFailed(NameBuildTest):
6350 	    def test_success(self):
6351 	        self.assert_message_from_report(
6352 	            "CIB cluster name property removal failed: reason",
6353 	            reports.CibClusterNameRemovalFailed("reason"),
6354 	        )
6355 	
6356 	
6357 	class PacemakerRunning(NameBuildTest):
6358 	    def test_success(self):
6359 	        self.assert_message_from_report(
6360 	            "Pacemaker is running", reports.PacemakerRunning()
6361 	        )
6362 	
6363 	
6364 	class CibXmlMissing(NameBuildTest):
6365 	    def test_success(self):
6366 	        self.assert_message_from_report(
6367 	            "CIB XML file cannot be found", reports.CibXmlMissing()
6368 	        )
6369 	
6370 	
6371 	class CibNodeRenameElementUpdated(NameBuildTest):
6372 	    def test_location_constraint(self):
6373 	        self.assert_message_from_report(
6374 	            "Location constraint 'loc-1': node updated from 'node1' to 'node2'",
6375 	            reports.CibNodeRenameElementUpdated(
6376 	                "Location constraint", "loc-1", "node", "node1", "node2"
6377 	            ),
6378 	        )
6379 	
6380 	    def test_rule_expression(self):
6381 	        self.assert_message_from_report(
6382 	            "Rule 'rule-1': #uname expression updated from 'node1' to 'node2'",
6383 	            reports.CibNodeRenameElementUpdated(
6384 	                "Rule", "rule-1", "#uname expression", "node1", "node2"
6385 	            ),
6386 	        )
6387 	
6388 	    def test_fencing_level(self):
6389 	        self.assert_message_from_report(
6390 	            "Fencing level '1': target updated from 'node1' to 'node2'",
6391 	            reports.CibNodeRenameElementUpdated(
6392 	                "Fencing level", "1", "target", "node1", "node2"
6393 	            ),
6394 	        )
6395 	
6396 	    def test_fence_device(self):
6397 	        self.assert_message_from_report(
6398 	            "Fence device 'fence_xvm': attribute 'pcmk_host_list' "
6399 	            "updated from 'node1,node2' to 'node3,node2'",
6400 	            reports.CibNodeRenameElementUpdated(
6401 	                "Fence device",
6402 	                "fence_xvm",
6403 	                "attribute 'pcmk_host_list'",
6404 	                "node1,node2",
6405 	                "node3,node2",
6406 	            ),
6407 	        )
6408 	
6409 	
6410 	class CibNodeRenameFencingLevelPatternExists(NameBuildTest):
6411 	    def test_success(self):
6412 	        self.assert_message_from_report(
6413 	            "Fencing level '1' uses target-pattern 'node.*', "
6414 	            "which may match the renamed node, check the pattern and adjust the"
6415 	            " configuration if necessary",
6416 	            reports.CibNodeRenameFencingLevelPatternExists("1", "node.*"),
6417 	        )
6418 	
6419 	
6420 	class CibNodeRenameAclsExist(NameBuildTest):
6421 	    def test_success(self):
6422 	        self.assert_message_from_report(
6423 	            "ACL rules exist in CIB and may contain references to node "
6424 	            "names, check the ACL configuration and adjust it if necessary",
6425 	            reports.CibNodeRenameAclsExist(),
6426 	        )
6427 	
6428 	
6429 	class CibNodeRenameOldNodeInCorosync(NameBuildTest):
6430 	    def test_success(self):
6431 	        self.assert_message_from_report(
6432 	            "Node 'old_name' is still known to corosync, "
6433 	            "the node may not have been renamed in corosync.conf yet",
6434 	            reports.CibNodeRenameOldNodeInCorosync(
6435 	                old_name="old_name",
6436 	            ),
6437 	        )
6438 	
6439 	
6440 	class CibNodeRenameNewNodeNotInCorosync(NameBuildTest):
6441 	    def test_success(self):
6442 	        self.assert_message_from_report(
6443 	            "Node 'new_name' is not known to corosync, "
6444 	            "the node name may be incorrect",
6445 	            reports.CibNodeRenameNewNodeNotInCorosync(
6446 	                new_name="new_name",
6447 	            ),
6448 	        )
6449 	
6450 	
6451 	class CibNodeRenameNoChange(NameBuildTest):
6452 	    def test_success(self):
6453 	        self.assert_message_from_report(
6454 	            "No CIB configuration changes needed for node rename",
6455 	            reports.CibNodeRenameNoChange(),
6456 	        )
6457 	
6458 	
6459 	class ConfiguredResourceMissingInStatus(NameBuildTest):
6460 	    def test_only_resource_id(self):
6461 	        self.assert_message_from_report(
6462 	            (
6463 	                "Cannot check if the resource 'id' is in expected state, "
6464 	                "since the resource is missing in cluster status"
6465 	            ),
6466 	            reports.ConfiguredResourceMissingInStatus("id"),
6467 	        )
6468 	
6469 	    def test_with_expected_state(self):
6470 	        self.assert_message_from_report(
6471 	            (
6472 	                "Cannot check if the resource 'id' is in expected state "
6473 	                "(stopped), since the resource is missing in cluster status"
6474 	            ),
6475 	            reports.ConfiguredResourceMissingInStatus(
6476 	                "id", ResourceState.STOPPED
6477 	            ),
6478 	        )
6479 	
6480 	
6481 	class NoStonithMeansWouldBeLeft(NameBuildTest):
6482 	    def test_success(self):
6483 	        self.assert_message_from_report(
6484 	            (
6485 	                "Requested action leaves the cluster with no enabled means "
6486 	                "to fence nodes, resulting in the cluster not being able to "
6487 	                "recover from certain failure conditions"
6488 	            ),
6489 	            reports.NoStonithMeansWouldBeLeft(),
6490 	        )
6491 	
6492 	
6493 	class NoStonithMeansWouldBeLeftDueToProperties(NameBuildTest):
6494 	    def test_success(self):
6495 	        self.assert_message_from_report(
6496 	            (
6497 	                "Setting property stonith-enabled to false or fencing-enabled"
6498 	                " to 0 leaves the cluster with no enabled means to fence nodes,"
6499 	                " resulting in the cluster not being able to recover from"
6500 	                " certain failure conditions"
6501 	            ),
6502 	            reports.NoStonithMeansWouldBeLeftDueToProperties(
6503 	                {"stonith-enabled": "false", "fencing-enabled": "0"}
6504 	            ),
6505 	        )
6506 	
6507 	
6508 	class ParseErrorInvalidFileStructure(NameBuildTest):
6509 	    def test_no_path(self):
6510 	        self.assert_message_from_report(
6511 	            "Unable to parse known-hosts file: reason",
6512 	            reports.ParseErrorInvalidFileStructure(
6513 	                "reason", file_type_codes.PCS_KNOWN_HOSTS, None
6514 	            ),
6515 	        )
6516 	
6517 	    def test_path(self):
6518 	        self.assert_message_from_report(
6519 	            "Unable to parse known-hosts file '/foo/bar': reason",
6520 	            reports.ParseErrorInvalidFileStructure(
6521 	                "reason", file_type_codes.PCS_KNOWN_HOSTS, "/foo/bar"
6522 	            ),
6523 	        )
6524 	
6525 	
6526 	class NodeReportsUnexpectedClusterName(NameBuildTest):
6527 	    def test_success(self):
6528 	        self.assert_message_from_report(
6529 	            "The node is not in the cluster named 'name'",
6530 	            reports.NodeReportsUnexpectedClusterName("name"),
6531 	        )
6532 	
6533 	
6534 	class PcsCfgsyncSendingConfigsToNodes(NameBuildTest):
6535 	    def test_one_node(self):
6536 	        self.assert_message_from_report(
6537 	            "Sending file 'known-hosts' to node 'node1'",
6538 	            reports.PcsCfgsyncSendingConfigsToNodes(
6539 	                [file_type_codes.PCS_KNOWN_HOSTS], ["node1"]
6540 	            ),
6541 	        )
6542 	
6543 	    def test_multiple_nodes(self):
6544 	        self.assert_message_from_report(
6545 	            "Sending file 'known-hosts' to nodes 'node1', 'node2'",
6546 	            reports.PcsCfgsyncSendingConfigsToNodes(
6547 	                [file_type_codes.PCS_KNOWN_HOSTS], ["node1", "node2"]
6548 	            ),
6549 	        )
6550 	
6551 	    def test_multiple_files(self):
6552 	        self.assert_message_from_report(
6553 	            "Sending files 'known-hosts', 'pcs configuration' to node 'node1'",
6554 	            reports.PcsCfgsyncSendingConfigsToNodes(
6555 	                [
6556 	                    file_type_codes.PCS_KNOWN_HOSTS,
6557 	                    file_type_codes.PCS_SETTINGS_CONF,
6558 	                ],
6559 	                ["node1"],
6560 	            ),
6561 	        )
6562 	
6563 	
6564 	class PcsCfgsyncSendingConfigsToNodesFailed(NameBuildTest):
6565 	    def test_one_node(self):
6566 	        self.assert_message_from_report(
6567 	            "Unable to save file 'known-hosts' on node 'node1'",
6568 	            reports.PcsCfgsyncSendingConfigsToNodesFailed(
6569 	                [file_type_codes.PCS_KNOWN_HOSTS], ["node1"]
6570 	            ),
6571 	        )
6572 	
6573 	    def test_multiple_nodes(self):
6574 	        self.assert_message_from_report(
6575 	            "Unable to save file 'known-hosts' on nodes 'node1', 'node2'",
6576 	            reports.PcsCfgsyncSendingConfigsToNodesFailed(
6577 	                [file_type_codes.PCS_KNOWN_HOSTS], ["node1", "node2"]
6578 	            ),
6579 	        )
6580 	
6581 	    def test_multiple_files(self):
6582 	        self.assert_message_from_report(
6583 	            "Unable to save files 'known-hosts', 'pcs configuration' on node 'node1'",
6584 	            reports.PcsCfgsyncSendingConfigsToNodesFailed(
6585 	                [
6586 	                    file_type_codes.PCS_KNOWN_HOSTS,
6587 	                    file_type_codes.PCS_SETTINGS_CONF,
6588 	                ],
6589 	                ["node1"],
6590 	            ),
6591 	        )
6592 	
6593 	
6594 	class PcsCfgsyncConfigAccepted(NameBuildTest):
6595 	    def test_success(self):
6596 	        self.assert_message_from_report(
6597 	            "The known-hosts file saved successfully",
6598 	            reports.PcsCfgsyncConfigAccepted(file_type_codes.PCS_KNOWN_HOSTS),
6599 	        )
6600 	
6601 	
6602 	class PcsCfgsyncConfigRejected(NameBuildTest):
6603 	    def test_success(self):
6604 	        self.assert_message_from_report(
6605 	            (
6606 	                "The known-hosts file not saved, a newer version of the file "
6607 	                "exists on the node"
6608 	            ),
6609 	            reports.PcsCfgsyncConfigRejected(file_type_codes.PCS_KNOWN_HOSTS),
6610 	        )
6611 	
6612 	
6613 	class PcsCfgsyncConfigSaveError(NameBuildTest):
6614 	    def test_success(self):
6615 	        self.assert_message_from_report(
6616 	            "The known-hosts file not saved",
6617 	            reports.PcsCfgsyncConfigSaveError(file_type_codes.PCS_KNOWN_HOSTS),
6618 	        )
6619 	
6620 	
6621 	class PcsCfgsyncConfigUnsupported(NameBuildTest):
6622 	    def test_success(self):
6623 	        self.assert_message_from_report(
6624 	            (
6625 	                "The known-hosts file synchronization is not supported on this "
6626 	                "node"
6627 	            ),
6628 	            reports.PcsCfgsyncConfigUnsupported(
6629 	                file_type_codes.PCS_KNOWN_HOSTS
6630 	            ),
6631 	        )
6632 	
6633 	
6634 	class PcsCfgsyncFetchingNewestConfig(NameBuildTest):
6635 	    def test_one_node(self):
6636 	        self.assert_message_from_report(
6637 	            (
6638 	                "Fetching the newest version of file 'known-hosts' from node "
6639 	                "'node1'"
6640 	            ),
6641 	            reports.PcsCfgsyncFetchingNewestConfig(
6642 	                [file_type_codes.PCS_KNOWN_HOSTS], ["node1"]
6643 	            ),
6644 	        )
6645 	
6646 	    def test_multiple_nodes(self):
6647 	        self.assert_message_from_report(
6648 	            (
6649 	                "Fetching the newest version of file 'known-hosts' from nodes "
6650 	                "'node1', 'node2'"
6651 	            ),
6652 	            reports.PcsCfgsyncFetchingNewestConfig(
6653 	                [file_type_codes.PCS_KNOWN_HOSTS], ["node1", "node2"]
6654 	            ),
6655 	        )
6656 	
6657 	    def test_multiple_files(self):
6658 	        self.assert_message_from_report(
6659 	            (
6660 	                "Fetching the newest version of files 'known-hosts', "
6661 	                "'pcs configuration' from node 'node1'"
6662 	            ),
6663 	            reports.PcsCfgsyncFetchingNewestConfig(
6664 	                [
6665 	                    file_type_codes.PCS_KNOWN_HOSTS,
6666 	                    file_type_codes.PCS_SETTINGS_CONF,
6667 	                ],
6668 	                ["node1"],
6669 	            ),
6670 	        )
6671 	
6672 	
6673 	class PcsCfgsyncConflictRepeatAction(NameBuildTest):
6674 	    def test_success(self):
6675 	        self.assert_message_from_report(
6676 	            (
6677 	                "Configuration conflict detected. Some nodes had a newer "
6678 	                "configuration than the local node. Local node's configuration "
6679 	                "was updated. Please repeat the last action if appropriate."
6680 	            ),
6681 	            reports.PcsCfgsyncConflictRepeatAction(),
6682 	        )
6683 	
6684 	
6685 	class MetaAttrsUnknownToPcmk(NameBuildTest):
6686 	    def test_single_option(self):
6687 	        self.assert_message_from_report(
6688 	            (
6689 	                "Resource meta attribute 'unknown' has no effect on cluster "
6690 	                "resource handling, meta attribute with effect: 'known'"
6691 	            ),
6692 	            reports.MetaAttrsUnknownToPcmk(
6693 	                ["unknown"], ["known"], ["primitive-meta"]
6694 	            ),
6695 	        )
6696 	
6697 	    def test_multiple_options(self):
6698 	        self.assert_message_from_report(
6699 	            (
6700 	                "Resource / stonith meta attributes 'unknown1', 'unknown2' "
6701 	                "have no effect on cluster resource handling, meta attributes "
6702 	                "with effect: 'known1', 'known2'"
6703 	            ),
6704 	            reports.MetaAttrsUnknownToPcmk(
6705 	                ["unknown1", "unknown2"],
6706 	                ["known1", "known2"],
6707 	                ["primitive-meta", "stonith-meta"],
6708 	            ),
6709 	        )
6710 	
6711 	
6712 	class MetaAttrsNotValidatedUnsupportedType(NameBuildTest):
6713 	    def test_empty_options(self):
6714 	        self.assert_message_from_report(
6715 	            "Meta attributes are not validated",
6716 	            reports.MetaAttrsNotValidatedUnsupportedType([]),
6717 	        )
6718 	
6719 	    def test_single_option(self):
6720 	        self.assert_message_from_report(
6721 	            "Meta attributes of clone are not validated",
6722 	            reports.MetaAttrsNotValidatedUnsupportedType(["clone"]),
6723 	        )
6724 	
6725 	    def test_multiple_options(self):
6726 	        self.assert_message_from_report(
6727 	            (
6728 	                "Meta attributes of bundle / clone / group / resource are not "
6729 	                "validated"
6730 	            ),
6731 	            reports.MetaAttrsNotValidatedUnsupportedType(
6732 	                ["clone", "bundle", "group", "primitive"]
6733 	            ),
6734 	        )
6735 	
6736 	
6737 	class MetaAttrsNotValidatedLoadingError(NameBuildTest):
6738 	    def test_success(self):
6739 	        self.assert_message_from_report(
6740 	            (
6741 	                "Meta attribute validation is skipped due to an error loading "
6742 	                "meta attributes definition."
6743 	            ),
6744 	            reports.MetaAttrsNotValidatedLoadingError(),
6745 	        )
6746 	
6747 	
6748 	class NodeNotInCluster(NameBuildTest):
6749 	    def test_success(self):
6750 	        self.assert_message_from_report(
6751 	            "The node does not currently have a cluster configured",
6752 	            reports.NodeNotInCluster(),
6753 	        )
6754 	
6755 	
6756 	class ClusterNameAlreadyInUse(NameBuildTest):
6757 	    def test_success(self):
6758 	        self.assert_message_from_report(
6759 	            "The cluster name 'foo' is already used",
6760 	            reports.ClusterNameAlreadyInUse("foo"),
6761 	        )
6762 	
6763 	
6764 	class UnableToGetClusterInfoFromStatus(NameBuildTest):
6765 	    def test_success(self):
6766 	        self.assert_message_from_report(
6767 	            "Unable to retrieve cluster information from node status",
6768 	            reports.UnableToGetClusterInfoFromStatus(),
6769 	        )
6770 	
6771 	
6772 	class UnableToGetClusterKnownHosts(NameBuildTest):
6773 	    def test_success(self):
6774 	        self.assert_message_from_report(
6775 	            "Unable to get known hosts from cluster 'foo'",
6776 	            reports.UnableToGetClusterKnownHosts("foo"),
6777 	        )
6778 	
6779 	
6780 	class CibResourceSecretUnableToGet(NameBuildTest):
6781 	    def test_success(self):
6782 	        self.assert_message_from_report(
6783 	            "Unable to get secret 'secret_name' for resource 'resource_id'",
6784 	            reports.CibResourceSecretUnableToGet(
(1) Event Sigma main event: A secret, such as a password, cryptographic key, or token is stored in plaintext directly in the source code, in an application's properties, or configuration file. Users with access to the secret may then use the secret to access resources that they otherwise would not have access to. Secret type: Secret (generic).
(2) Event remediation: Avoid setting sensitive configuration values as string literals. Instead, these values should be set using variables with the sensitive data loaded from an encrypted file or a secret store.
6785 	                "resource_id", "secret_name", "reason"
6786 	            ),
6787 	        )
6788