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