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