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