1    	from textwrap import dedent
2    	from unittest import TestCase, skip
3    	
4    	from lxml import etree
5    	
6    	from pcs import resource, utils
7    	from pcs.common import const
8    	from pcs.common.str_tools import format_list, format_plural
9    	from pcs.constraint import LOCATION_NODE_VALIDATION_SKIP_MSG
10   	from pcs.lib.resource_agent import const as ra_const
11   	
12   	from pcs_test.tier1.cib_resource.common import (
13   	    ResourceTest,
14   	    fixture_meta_attributes_not_validated_warning,
15   	    fixture_meta_attributes_warning,
16   	    fixture_use_meta_command_instead_warning,
17   	)
18   	from pcs_test.tier1.legacy.common import FIXTURE_UTILIZATION_WARNING
19   	from pcs_test.tools.assertions import AssertPcsMixin, assert_pcs_status
20   	from pcs_test.tools.bin_mock import get_mock_settings
21   	from pcs_test.tools.cib import get_assert_pcs_effect_mixin
22   	from pcs_test.tools.fixture_cib import (
23   	    CachedCibFixture,
24   	    fixture_master_xml,
25   	    fixture_to_cib,
26   	    wrap_element_by_master,
27   	    wrap_element_by_master_file,
28   	)
29   	from pcs_test.tools.misc import get_test_resource as rc
30   	from pcs_test.tools.misc import (
31   	    get_tmp_file,
32   	    is_minimum_pacemaker_version,
33   	    is_pacemaker_21_without_20_compatibility,
34   	    outdent,
35   	    skip_unless_crm_rule,
36   	    skip_unless_pacemaker_supports_op_onfail_demote,
37   	    write_data_to_tmpfile,
38   	    write_file_to_tmpfile,
39   	)
40   	from pcs_test.tools.pcs_runner import PcsRunner
41   	from pcs_test.tools.xml import XmlManipulation, etree_to_str
42   	
43   	PCMK_2_0_3_PLUS = is_minimum_pacemaker_version(2, 0, 3)
44   	
45   	LOCATION_NODE_VALIDATION_SKIP_WARNING = (
46   	    f"Warning: {LOCATION_NODE_VALIDATION_SKIP_MSG}\n"
47   	)
48   	ERRORS_HAVE_OCCURRED = (
49   	    "Error: Errors have occurred, therefore pcs is unable to continue\n"
50   	)
51   	
52   	
53   	def fixture_message_not_deleting_resources_not_live(resource_ids):
54   	    resources = format_plural(resource_ids, "resource")
55   	    are = format_plural(resource_ids, "is")
56   	    return (
57   	        "Warning: Resources are not going to be stopped before deletion "
58   	        "because the command does not run on a live cluster\n"
59   	        f"Warning: Not checking if {resources} {format_list(resource_ids)} "
60   	        f"{are} stopped before deletion because the command does not run on a "
61   	        "live cluster. Deleting unstopped resources may result in orphaned "
62   	        "resources being present in the cluster.\n"
63   	    )
64   	
65   	
66   	empty_cib = rc("cib-empty.xml")
67   	large_cib = rc("cib-large.xml")
68   	
69   	
70   	class ResourceDescribe(TestCase, AssertPcsMixin):
71   	    def setUp(self):
72   	        self.pcs_runner = PcsRunner(None)
73   	        self.pcs_runner.mock_settings = get_mock_settings()
74   	
75   	    @staticmethod
76   	    def fixture_description(advanced=False):
77   	        advanced_params = """
78   	              advanced (advanced use only)
79   	                Description: This parameter should not be set usually
80   	                Type: string"""
81   	        return dedent(
82   	            """\
83   	            ocf:pcsmock:params - Mock agent for pcs tests - agent with various parameters
84   	
85   	            This is a mock agent for pcs test - agent with parameters
86   	
87   	            Resource options:
88   	              mandatory (required)
89   	                Description: A generic mandatory string parameter
90   	                Type: string
91   	              optional
92   	                Description: A generic optional string parameter
93   	                Type: string
94   	                Default: if not specified
95   	              enum
96   	                Description: An optional enum parameter
97   	                Allowed values: 'value1', 'value2', 'value3'
98   	                Default: value1{0}
99   	              unique1 (unique group: group-A)
100  	                Description: First parameter in a unique group
101  	                Type: string
102  	              unique2 (unique group: group-A)
103  	                Description: Second parameter in a unique group
104  	                Type: string
105  	
106  	            Default operations:
107  	              start:
108  	                interval=0s timeout=20s
109  	              stop:
110  	                interval=0s timeout=20s
111  	              monitor:
112  	                interval=10s timeout=20s
113  	              reload:
114  	                interval=0s timeout=20s
115  	              reload-agent:
116  	                interval=0s timeout=20s
117  	              migrate_to:
118  	                interval=0s timeout=20s
119  	              migrate_from:
120  	                interval=0s timeout=20s
121  	            """.format(advanced_params if advanced else "")
122  	        )
123  	
124  	    def test_success(self):
125  	        self.assert_pcs_success(
126  	            "resource describe ocf:pcsmock:params".split(),
127  	            self.fixture_description(),
128  	        )
129  	
130  	    def test_full(self):
131  	        self.assert_pcs_success(
132  	            "resource describe ocf:pcsmock:params --full".split(),
133  	            self.fixture_description(True),
134  	        )
135  	
136  	    def test_success_guess_name(self):
137  	        self.assert_pcs_success(
138  	            "resource describe params".split(),
139  	            stdout_full=self.fixture_description(),
140  	            stderr_full=(
141  	                "Assumed agent name 'ocf:pcsmock:params' (deduced from 'params')\n"
142  	            ),
143  	        )
144  	
145  	    def test_nonextisting_agent(self):
146  	        self.assert_pcs_fail(
147  	            "resource describe ocf:pcsmock:nonexistent".split(),
148  	            (
149  	                "Error: Agent 'ocf:pcsmock:nonexistent' is not installed or does "
150  	                "not provide valid metadata: "
151  	                "pcs mock error message: unable to load agent metadata\n"
152  	                + ERRORS_HAVE_OCCURRED
153  	            ),
154  	        )
155  	
156  	    def test_nonextisting_agent_guess_name(self):
157  	        self.assert_pcs_fail(
158  	            "resource describe nonexistent".split(),
159  	            (
160  	                "Error: Unable to find agent 'nonexistent', try specifying"
161  	                " its full name\n"
162  	            ),
163  	        )
164  	
165  	    def test_more_agents_guess_name(self):
166  	        self.assert_pcs_fail(
167  	            "resource describe pcsmock".split(),
168  	            (
169  	                "Error: Multiple agents match 'pcsmock', please specify full"
170  	                " name: 'ocf:heartbeat:pcsMock' or 'ocf:pacemaker:pcsMock'\n"
171  	            ),
172  	        )
173  	
174  	    def test_not_enough_params(self):
175  	        self.assert_pcs_fail(
176  	            "resource describe".split(),
177  	            stderr_start="\nUsage: pcs resource describe...\n",
178  	        )
179  	
180  	    def test_too_many_params(self):
181  	        self.assert_pcs_fail(
182  	            "resource describe agent1 agent2".split(),
183  	            stderr_start="\nUsage: pcs resource describe...\n",
184  	        )
185  	
186  	
187  	class ResourceTestCibFixture(CachedCibFixture):
188  	    def _setup_cib(self):
189  	        self.assert_pcs_success_ignore_output(
190  	            (
191  	                "resource create --no-default-ops ClusterIP ocf:pcsmock:minimal"
192  	            ).split()
193  	        )
194  	        self.assert_pcs_success_ignore_output(
195  	            (
196  	                "resource create --no-default-ops ClusterIP2 ocf:pcsmock:minimal"
197  	            ).split()
198  	        )
199  	        self.assert_pcs_success_ignore_output(
200  	            (
201  	                "resource create --no-default-ops ClusterIP3 ocf:pcsmock:minimal"
202  	            ).split()
203  	        )
204  	        self.assert_pcs_success_ignore_output(
205  	            (
206  	                "resource create --no-default-ops ClusterIP4 ocf:pcsmock:minimal"
207  	            ).split()
208  	        )
209  	        self.assert_pcs_success_ignore_output(
210  	            (
211  	                "resource create --no-default-ops ClusterIP5 ocf:pcsmock:minimal"
212  	            ).split()
213  	        )
214  	        self.assert_pcs_success_ignore_output(
215  	            (
216  	                "resource create --no-default-ops ClusterIP6 ocf:pcsmock:minimal"
217  	            ).split()
218  	        )
219  	        self.assert_pcs_success(
220  	            "resource group add TestGroup1 ClusterIP".split()
221  	        )
222  	        self.assert_pcs_success(
223  	            "resource group add TestGroup2 ClusterIP2 ClusterIP3".split()
224  	        )
225  	        self.assert_pcs_success("resource clone ClusterIP4".split())
226  	        # pcs no longer allows turning resources into masters but supports
227  	        # existing ones. In order to test it, we need to put a master in the
228  	        # CIB without pcs.
229  	        wrap_element_by_master_file(
230  	            self.cache_path, "ClusterIP5", master_id="Master"
231  	        )
232  	
233  	
234  	CIB_FIXTURE = ResourceTestCibFixture("fixture_tier1_resource", empty_cib)
235  	
236  	
237  	class Resource(TestCase, AssertPcsMixin):
238  	    def setUp(self):
239  	        self.temp_cib = get_tmp_file("tier1_resource")
240  	        self.temp_large_cib = get_tmp_file("tier1_resource_large")
241  	        write_file_to_tmpfile(empty_cib, self.temp_cib)
242  	        write_file_to_tmpfile(large_cib, self.temp_large_cib)
243  	        self.pcs_runner = PcsRunner(self.temp_cib.name)
244  	        self.pcs_runner.mock_settings = get_mock_settings()
245  	
246  	    def tearDown(self):
247  	        self.temp_cib.close()
248  	        self.temp_large_cib.close()
249  	
250  	    # Setups up a cluster with Resources, groups, master/slave resource & clones
251  	    def setup_cluster_a(self):
252  	        write_file_to_tmpfile(CIB_FIXTURE.cache_path, self.temp_cib)
253  	
254  	    def test_case_insensitive(self):
255  	        self.assert_pcs_fail(
256  	            "resource create --no-default-ops D0 pcsmock".split(),
257  	            (
258  	                "Error: Multiple agents match 'pcsmock', please specify full name: "
259  	                "'ocf:heartbeat:pcsMock' or 'ocf:pacemaker:pcsMock'\n"
260  	                + ERRORS_HAVE_OCCURRED
261  	            ),
262  	        )
263  	
264  	        self.assert_pcs_success(
265  	            "resource create --no-default-ops D1 camelcase".split(),
266  	            stderr_full=(
267  	                "Assumed agent name 'ocf:pcsmock:CamelCase'"
268  	                " (deduced from 'camelcase')\n"
269  	            ),
270  	        )
271  	
272  	        self.assert_pcs_success(
273  	            "resource create --no-default-ops D2 CAMELCASE".split(),
274  	            stderr_full=(
275  	                "Assumed agent name 'ocf:pcsmock:CamelCase'"
276  	                " (deduced from 'CAMELCASE')\n"
277  	            ),
278  	        )
279  	
280  	        self.assert_pcs_fail(
281  	            "resource create --no-default-ops D4 camel_case".split(),
282  	            (
283  	                "Error: Unable to find agent 'camel_case', try specifying its full name\n"
284  	                + ERRORS_HAVE_OCCURRED
285  	            ),
286  	        )
287  	
288  	    def test_empty(self):
289  	        self.assert_pcs_success(["resource"], "NO resources configured\n")
290  	
291  	    def test_add_resources_large_cib(self):
292  	        self.pcs_runner = PcsRunner(self.temp_large_cib.name)
293  	        self.pcs_runner.mock_settings = get_mock_settings()
294  	        self.assert_pcs_success(
295  	            "resource create dummy0 ocf:pcsmock:minimal --no-default-ops".split(),
296  	        )
297  	        self.assert_pcs_success(
298  	            "resource config dummy0".split(),
299  	            dedent(
300  	                """\
301  	                Resource: dummy0 (class=ocf provider=pcsmock type=minimal)
302  	                  Operations:
303  	                    monitor: dummy0-monitor-interval-10s
304  	                      interval=10s timeout=20s
305  	                """
306  	            ),
307  	        )
308  	
309  	    def test_resource_show(self):
310  	        self.assert_pcs_success(
311  	            (
312  	                "resource create --no-default-ops ClusterIP ocf:pcsmock:params"
313  	                " mandatory=mandat optional=opti op monitor interval=30s"
314  	            ).split()
315  	        )
316  	        self.assert_pcs_success(
317  	            "resource config ClusterIP".split(),
318  	            dedent(
319  	                """\
320  	                Resource: ClusterIP (class=ocf provider=pcsmock type=params)
321  	                  Attributes: ClusterIP-instance_attributes
322  	                    mandatory=mandat
323  	                    optional=opti
324  	                  Operations:
325  	                    monitor: ClusterIP-monitor-interval-30s
326  	                      interval=30s
327  	                """
328  	            ),
329  	        )
330  	
331  	    def test_add_operation(self):
332  	        # see also BundleMiscCommands
333  	        self.assert_pcs_success(
334  	            (
335  	                "resource create --no-default-ops ClusterIP ocf:pcsmock:minimal"
336  	                " op monitor interval=30s"
337  	            ).split()
338  	        )
339  	
340  	        self.assert_pcs_fail(
341  	            "resource op add".split(),
342  	            stderr_start="\nUsage: pcs resource op add...",
343  	        )
344  	        self.assert_pcs_fail(
345  	            "resource op remove".split(),
346  	            stderr_start="\nUsage: pcs resource op remove...",
347  	        )
348  	
349  	        self.assert_pcs_fail(
350  	            "resource op add ClusterIP monitor interval=31s".split(),
351  	            (
352  	                "Error: operation monitor already specified for ClusterIP, "
353  	                "use --force to override:\n"
354  	                "monitor interval=30s (ClusterIP-monitor-interval-30s)\n"
355  	            ),
356  	        )
357  	
358  	        self.assert_pcs_success(
359  	            "resource op add ClusterIP monitor interval=31s --force".split(),
360  	        )
361  	
362  	        self.assert_pcs_fail(
363  	            "resource op add ClusterIP monitor interval=31s".split(),
364  	            (
365  	                "Error: operation monitor with interval 31s already specified "
366  	                "for ClusterIP:\n"
367  	                "monitor interval=31s (ClusterIP-monitor-interval-31s)\n"
368  	            ),
369  	        )
370  	
371  	        self.assert_pcs_fail(
372  	            "resource op add ClusterIP monitor interval=31".split(),
373  	            (
374  	                "Error: operation monitor with interval 31s already specified "
375  	                "for ClusterIP:\n"
376  	                "monitor interval=31s (ClusterIP-monitor-interval-31s)\n"
377  	            ),
378  	        )
379  	
380  	        self.assert_pcs_fail(
381  	            "resource op add ClusterIP moni=tor interval=60".split(),
382  	            "Error: moni=tor does not appear to be a valid operation action\n",
383  	        )
384  	
385  	        self.assert_pcs_success(
386  	            "resource config ClusterIP".split(),
387  	            dedent(
388  	                """\
389  	                Resource: ClusterIP (class=ocf provider=pcsmock type=minimal)
390  	                  Operations:
391  	                    monitor: ClusterIP-monitor-interval-30s
392  	                      interval=30s
393  	                    monitor: ClusterIP-monitor-interval-31s
394  	                      interval=31s
395  	                """
396  	            ),
397  	        )
398  	
399  	        self.assert_pcs_success(
400  	            (
401  	                "resource create --no-default-ops OPTest ocf:pcsmock:minimal "
402  	                "op monitor interval=30s OCF_CHECK_LEVEL=1 "
403  	                "op monitor interval=25s OCF_CHECK_LEVEL=1 enabled=0"
404  	            ).split(),
405  	        )
406  	
407  	        self.assert_pcs_success(
408  	            "resource config OPTest".split(),
409  	            dedent(
410  	                """\
411  	                Resource: OPTest (class=ocf provider=pcsmock type=minimal)
412  	                  Operations:
413  	                    monitor: OPTest-monitor-interval-30s
414  	                      interval=30s OCF_CHECK_LEVEL=1
415  	                    monitor: OPTest-monitor-interval-25s
416  	                      interval=25s enabled=0 OCF_CHECK_LEVEL=1
417  	                """
418  	            ),
419  	        )
420  	
421  	        self.assert_pcs_success(
422  	            (
423  	                "resource create --no-default-ops OPTest2 ocf:pcsmock:minimal "
424  	                "op monitor interval=30s OCF_CHECK_LEVEL=1 "
425  	                "op monitor interval=25s OCF_CHECK_LEVEL=2 "
426  	                "op start timeout=30s"
427  	            ).split(),
428  	        )
429  	
430  	        self.assert_pcs_fail(
431  	            "resource op add OPTest2 start timeout=1800s".split(),
432  	            (
433  	                "Error: operation start with interval 0s already specified for OPTest2:\n"
434  	                "start interval=0s timeout=30s (OPTest2-start-interval-0s)\n"
435  	            ),
436  	        )
437  	
438  	        self.assert_pcs_fail(
439  	            "resource op add OPTest2 start interval=100".split(),
440  	            (
441  	                "Error: operation start already specified for OPTest2, use --force to override:\n"
442  	                "start interval=0s timeout=30s (OPTest2-start-interval-0s)\n"
443  	            ),
444  	        )
445  	
446  	        self.assert_pcs_success(
447  	            "resource op add OPTest2 monitor timeout=1800s".split(),
448  	        )
449  	
450  	        self.assert_pcs_success(
451  	            "resource config OPTest2".split(),
452  	            dedent(
453  	                """\
454  	                Resource: OPTest2 (class=ocf provider=pcsmock type=minimal)
455  	                  Operations:
456  	                    monitor: OPTest2-monitor-interval-30s
457  	                      interval=30s OCF_CHECK_LEVEL=1
458  	                    monitor: OPTest2-monitor-interval-25s
459  	                      interval=25s OCF_CHECK_LEVEL=2
460  	                    start: OPTest2-start-interval-0s
461  	                      interval=0s timeout=30s
462  	                    monitor: OPTest2-monitor-interval-60s
463  	                      interval=60s timeout=1800s
464  	                """
465  	            ),
466  	        )
467  	
468  	        self.assert_pcs_success(
469  	            (
470  	                "resource create --no-default-ops OPTest3 ocf:pcsmock:minimal "
471  	                "op monitor OCF_CHECK_LEVEL=1"
472  	            ).split(),
473  	        )
474  	
475  	        self.assert_pcs_success(
476  	            "resource config OPTest3".split(),
477  	            dedent(
478  	                """\
479  	                Resource: OPTest3 (class=ocf provider=pcsmock type=minimal)
480  	                  Operations:
481  	                    monitor: OPTest3-monitor-interval-60s
482  	                      interval=60s OCF_CHECK_LEVEL=1
483  	                """
484  	            ),
485  	        )
486  	
487  	        self.assert_pcs_success(
488  	            (
489  	                "resource create --no-default-ops OPTest4 ocf:pcsmock:minimal "
490  	                "op monitor interval=30s"
491  	            ).split(),
492  	        )
493  	
494  	        self.assert_pcs_success(
495  	            "resource update OPTest4 op monitor OCF_CHECK_LEVEL=1".split(),
496  	        )
497  	
498  	        self.assert_pcs_success(
499  	            "resource config OPTest4".split(),
500  	            dedent(
501  	                """\
502  	                Resource: OPTest4 (class=ocf provider=pcsmock type=minimal)
503  	                  Operations:
504  	                    monitor: OPTest4-monitor-interval-60s
505  	                      interval=60s OCF_CHECK_LEVEL=1
506  	                """
507  	            ),
508  	        )
509  	
510  	        self.assert_pcs_success(
511  	            "resource create --no-default-ops OPTest5 ocf:pcsmock:minimal".split(),
512  	        )
513  	
514  	        self.assert_pcs_success(
515  	            "resource update OPTest5 op monitor OCF_CHECK_LEVEL=1".split(),
516  	        )
517  	
518  	        self.assert_pcs_success(
519  	            "resource config OPTest5".split(),
520  	            dedent(
521  	                """\
522  	                Resource: OPTest5 (class=ocf provider=pcsmock type=minimal)
523  	                  Operations:
524  	                    monitor: OPTest5-monitor-interval-60s
525  	                      interval=60s OCF_CHECK_LEVEL=1
526  	                """
527  	            ),
528  	        )
529  	
530  	        self.assert_pcs_success(
531  	            "resource create --no-default-ops OPTest6 ocf:pcsmock:minimal".split(),
532  	        )
533  	
534  	        self.assert_pcs_success(
535  	            "resource op add OPTest6 monitor interval=30s OCF_CHECK_LEVEL=1".split(),
536  	        )
537  	
538  	        self.assert_pcs_success(
539  	            "resource config OPTest6".split(),
540  	            dedent(
541  	                """\
542  	                Resource: OPTest6 (class=ocf provider=pcsmock type=minimal)
543  	                  Operations:
544  	                    monitor: OPTest6-monitor-interval-10s
545  	                      interval=10s timeout=20s
546  	                    monitor: OPTest6-monitor-interval-30s
547  	                      interval=30s OCF_CHECK_LEVEL=1
548  	                """
549  	            ),
550  	        )
551  	
552  	        self.assert_pcs_success(
553  	            "resource create --no-default-ops OPTest7 ocf:pcsmock:minimal".split(),
554  	        )
555  	
556  	        self.assert_pcs_success(
557  	            "resource update OPTest7 op monitor interval=60s OCF_CHECK_LEVEL=1".split(),
558  	        )
559  	
560  	        self.assert_pcs_fail(
561  	            "resource op add OPTest7 monitor interval=61s OCF_CHECK_LEVEL=1".split(),
562  	            (
563  	                "Error: operation monitor already specified for OPTest7, use --force to override:\n"
564  	                "monitor interval=60s OCF_CHECK_LEVEL=1 (OPTest7-monitor-interval-60s)\n"
565  	            ),
566  	        )
567  	
568  	        self.assert_pcs_success(
569  	            "resource op add OPTest7 monitor interval=61s OCF_CHECK_LEVEL=1 --force".split(),
570  	        )
571  	
572  	        self.assert_pcs_success(
573  	            "resource config OPTest7".split(),
574  	            dedent(
575  	                """\
576  	                Resource: OPTest7 (class=ocf provider=pcsmock type=minimal)
577  	                  Operations:
578  	                    monitor: OPTest7-monitor-interval-60s
579  	                      interval=60s OCF_CHECK_LEVEL=1
580  	                    monitor: OPTest7-monitor-interval-61s
581  	                      interval=61s OCF_CHECK_LEVEL=1
582  	                """
583  	            ),
584  	        )
585  	
586  	        self.assert_pcs_fail(
587  	            "resource op add OPTest7 monitor interval=60s OCF_CHECK_LEVEL=1".split(),
588  	            (
589  	                "Error: operation monitor with interval 60s already specified for OPTest7:\n"
590  	                "monitor interval=60s OCF_CHECK_LEVEL=1 (OPTest7-monitor-interval-60s)\n"
591  	            ),
592  	        )
593  	
594  	        self.assert_pcs_success(
595  	            "resource create --no-default-ops OCFTest1 ocf:pcsmock:minimal".split(),
596  	        )
597  	
598  	        self.assert_pcs_fail(
599  	            "resource op add OCFTest1 monitor interval=31s".split(),
600  	            (
601  	                "Error: operation monitor already specified for OCFTest1, use --force to override:\n"
602  	                "monitor interval=10s timeout=20s (OCFTest1-monitor-interval-10s)\n"
603  	            ),
604  	        )
605  	
606  	        self.assert_pcs_success(
607  	            "resource op add OCFTest1 monitor interval=31s --force".split(),
608  	        )
609  	
610  	        self.assert_pcs_success(
611  	            "resource op add OCFTest1 monitor interval=30s OCF_CHECK_LEVEL=15".split(),
612  	        )
613  	
614  	        self.assert_pcs_success(
615  	            "resource config OCFTest1".split(),
616  	            dedent(
617  	                """\
618  	                Resource: OCFTest1 (class=ocf provider=pcsmock type=minimal)
619  	                  Operations:
620  	                    monitor: OCFTest1-monitor-interval-10s
621  	                      interval=10s timeout=20s
622  	                    monitor: OCFTest1-monitor-interval-31s
623  	                      interval=31s
624  	                    monitor: OCFTest1-monitor-interval-30s
625  	                      interval=30s OCF_CHECK_LEVEL=15
626  	                """
627  	            ),
628  	        )
629  	
630  	        self.assert_pcs_success(
631  	            "resource update OCFTest1 op monitor interval=61s OCF_CHECK_LEVEL=5".split(),
632  	        )
633  	
634  	        self.assert_pcs_success(
635  	            "resource config OCFTest1".split(),
636  	            dedent(
637  	                """\
638  	                Resource: OCFTest1 (class=ocf provider=pcsmock type=minimal)
639  	                  Operations:
640  	                    monitor: OCFTest1-monitor-interval-61s
641  	                      interval=61s OCF_CHECK_LEVEL=5
642  	                    monitor: OCFTest1-monitor-interval-31s
643  	                      interval=31s
644  	                    monitor: OCFTest1-monitor-interval-30s
645  	                      interval=30s OCF_CHECK_LEVEL=15
646  	                """
647  	            ),
648  	        )
649  	
650  	        self.assert_pcs_success(
651  	            "resource update OCFTest1 op monitor OCF_CHECK_LEVEL=4".split(),
652  	        )
653  	
654  	        self.assert_pcs_success(
655  	            "resource config OCFTest1".split(),
656  	            dedent(
657  	                """\
658  	                Resource: OCFTest1 (class=ocf provider=pcsmock type=minimal)
659  	                  Operations:
660  	                    monitor: OCFTest1-monitor-interval-60s
661  	                      interval=60s OCF_CHECK_LEVEL=4
662  	                    monitor: OCFTest1-monitor-interval-31s
663  	                      interval=31s
664  	                    monitor: OCFTest1-monitor-interval-30s
665  	                      interval=30s OCF_CHECK_LEVEL=15
666  	                """
667  	            ),
668  	        )
669  	
670  	        self.assert_pcs_success(
671  	            "resource update OCFTest1 op monitor OCF_CHECK_LEVEL=4 interval=35s".split(),
672  	        )
673  	
674  	        self.assert_pcs_success(
675  	            "resource config OCFTest1".split(),
676  	            dedent(
677  	                """\
678  	                Resource: OCFTest1 (class=ocf provider=pcsmock type=minimal)
679  	                  Operations:
680  	                    monitor: OCFTest1-monitor-interval-35s
681  	                      interval=35s OCF_CHECK_LEVEL=4
682  	                    monitor: OCFTest1-monitor-interval-31s
683  	                      interval=31s
684  	                    monitor: OCFTest1-monitor-interval-30s
685  	                      interval=30s OCF_CHECK_LEVEL=15
686  	                """
687  	            ),
688  	        )
689  	
690  	        self.assert_pcs_success(
691  	            "resource create --no-default-ops state ocf:pcsmock:stateful".split(),
692  	        )
693  	
694  	        self.assert_pcs_fail(
695  	            "resource op add state monitor interval=10".split(),
696  	            (
697  	                "Error: operation monitor with interval 10s already specified for state:\n"
698  	                "monitor interval=10s role=Master timeout=20s (state-monitor-interval-10s)\n"
699  	            ),
700  	        )
701  	
702  	        self.assert_pcs_fail(
703  	            "resource op add state monitor interval=10 role=Started".split(),
704  	            (
705  	                "Error: operation monitor with interval 10s already specified for state:\n"
706  	                "monitor interval=10s role=Master timeout=20s (state-monitor-interval-10s)\n"
707  	            ),
708  	        )
709  	
710  	        self.assert_pcs_success(
711  	            "resource config state".split(),
712  	            dedent(
713  	                f"""\
714  	                Resource: state (class=ocf provider=pcsmock type=stateful)
715  	                  Operations:
716  	                    monitor: state-monitor-interval-10s
717  	                      interval=10s timeout=20s role={const.PCMK_ROLE_PROMOTED}
718  	                    monitor: state-monitor-interval-11s
719  	                      interval=11s timeout=20s role={const.PCMK_ROLE_UNPROMOTED}
720  	                """
721  	            ),
722  	        )
723  	
724  	    @skip_unless_pacemaker_supports_op_onfail_demote()
725  	    def test_add_operation_onfail_demote_upgrade_cib(self):
726  	        write_file_to_tmpfile(rc("cib-empty-3.3.xml"), self.temp_cib)
727  	        self.assert_pcs_success(
728  	            "resource create --no-default-ops R ocf:pcsmock:minimal".split()
729  	        )
730  	        self.assert_pcs_success(
731  	            "resource op add R start on-fail=demote".split(),
732  	            stderr_full="Cluster CIB has been upgraded to latest version\n",
733  	        )
734  	
735  	    @skip_unless_pacemaker_supports_op_onfail_demote()
736  	    def test_update_add_operation_onfail_demote_upgrade_cib(self):
737  	        write_file_to_tmpfile(rc("cib-empty-3.3.xml"), self.temp_cib)
738  	        self.assert_pcs_success(
739  	            "resource create --no-default-ops R ocf:pcsmock:minimal".split()
740  	        )
741  	        self.assert_pcs_success(
742  	            "resource update R op start on-fail=demote".split(),
743  	            stderr_full="Cluster CIB has been upgraded to latest version\n",
744  	        )
745  	
746  	    def _test_delete_remove_operation(self, command):
747  	        assert command in {"delete", "remove"}
748  	
749  	        self.assert_pcs_success(
750  	            (
751  	                "resource create --no-default-ops ClusterIP ocf:pcsmock:minimal"
752  	                " op monitor interval=30s"
753  	            ).split()
754  	        )
755  	
756  	        self.assert_pcs_success(
757  	            "resource op add ClusterIP monitor interval=31s --force".split()
758  	        )
759  	
760  	        self.assert_pcs_success(
761  	            "resource op add ClusterIP monitor interval=32s --force".split()
762  	        )
763  	
764  	        self.assert_pcs_fail(
765  	            f"resource op {command} ClusterIP-monitor-interval-32s-xxxxx".split(),
766  	            (
767  	                "Error: unable to find operation id: "
768  	                "ClusterIP-monitor-interval-32s-xxxxx\n"
769  	            ),
770  	        )
771  	
772  	        self.assert_pcs_success(
773  	            f"resource op {command} ClusterIP-monitor-interval-32s".split()
774  	        )
775  	
776  	        self.assert_pcs_success(
777  	            f"resource op {command} ClusterIP monitor interval=30s".split()
778  	        )
779  	
780  	        self.assert_pcs_fail(
781  	            f"resource op {command} ClusterIP monitor interval=30s".split(),
782  	            "Error: Unable to find operation matching: monitor interval=30s\n",
783  	        )
784  	
785  	        self.assert_pcs_success(
786  	            "resource config ClusterIP".split(),
787  	            dedent(
788  	                """\
789  	                Resource: ClusterIP (class=ocf provider=pcsmock type=minimal)
790  	                  Operations:
791  	                    monitor: ClusterIP-monitor-interval-31s
792  	                      interval=31s
793  	                """
794  	            ),
795  	        )
796  	
797  	        self.assert_pcs_success(
798  	            f"resource op {command} ClusterIP monitor interval=31s".split()
799  	        )
800  	
801  	        self.assert_pcs_success(
802  	            "resource config ClusterIP".split(),
803  	            dedent(
804  	                """\
805  	                Resource: ClusterIP (class=ocf provider=pcsmock type=minimal)
806  	                """
807  	            ),
808  	        )
809  	
810  	        self.assert_pcs_success(
811  	            "resource op add ClusterIP monitor interval=31s".split()
812  	        )
813  	
814  	        self.assert_pcs_success(
815  	            "resource op add ClusterIP monitor interval=32s --force".split()
816  	        )
817  	
818  	        self.assert_pcs_success(
819  	            "resource op add ClusterIP stop timeout=34s".split()
820  	        )
821  	
822  	        self.assert_pcs_success(
823  	            "resource op add ClusterIP start timeout=33s".split()
824  	        )
825  	
826  	        self.assert_pcs_success(
827  	            f"resource op {command} ClusterIP monitor".split()
828  	        )
829  	
830  	        self.assert_pcs_success(
831  	            "resource config ClusterIP".split(),
832  	            dedent(
833  	                """\
834  	                Resource: ClusterIP (class=ocf provider=pcsmock type=minimal)
835  	                  Operations:
836  	                    stop: ClusterIP-stop-interval-0s
837  	                      interval=0s timeout=34s
838  	                    start: ClusterIP-start-interval-0s
839  	                      interval=0s timeout=33s
840  	                """
841  	            ),
842  	        )
843  	
844  	    def test_delete_operation(self):
845  	        # see also BundleMiscCommands
846  	        self.assert_pcs_fail(
847  	            "resource op delete".split(),
848  	            stderr_start="\nUsage: pcs resource op delete...",
849  	        )
850  	
851  	        self._test_delete_remove_operation("delete")
852  	
853  	    def test_remove_operation(self):
854  	        # see also BundleMiscCommands
855  	        self.assert_pcs_fail(
856  	            "resource op remove".split(),
857  	            stderr_start="\nUsage: pcs resource op remove...",
858  	        )
859  	
860  	        self._test_delete_remove_operation("remove")
861  	
862  	    def test_update_operation(self):
863  	        self.assert_pcs_success(
864  	            (
865  	                "resource create --no-default-ops ClusterIP ocf:pcsmock:params"
866  	                " mandatory=value op monitor interval=30s"
867  	            ).split()
868  	        )
869  	        self.assert_pcs_success(
870  	            "resource config ClusterIP".split(),
871  	            dedent(
872  	                """\
873  	                Resource: ClusterIP (class=ocf provider=pcsmock type=params)
874  	                  Attributes: ClusterIP-instance_attributes
875  	                    mandatory=value
876  	                  Operations:
877  	                    monitor: ClusterIP-monitor-interval-30s
878  	                      interval=30s
879  	                """
880  	            ),
881  	        )
882  	
883  	        self.assert_pcs_success(
884  	            "resource update ClusterIP op monitor interval=32s".split()
885  	        )
886  	        self.assert_pcs_success(
887  	            "resource config ClusterIP".split(),
888  	            dedent(
889  	                """\
890  	                Resource: ClusterIP (class=ocf provider=pcsmock type=params)
891  	                  Attributes: ClusterIP-instance_attributes
892  	                    mandatory=value
893  	                  Operations:
894  	                    monitor: ClusterIP-monitor-interval-32s
895  	                      interval=32s
896  	                """
897  	            ),
898  	        )
899  	
900  	        show_clusterip = dedent(
901  	            """\
902  	            Resource: ClusterIP (class=ocf provider=pcsmock type=params)
903  	              Attributes: ClusterIP-instance_attributes
904  	                mandatory=value
905  	              Operations:
906  	                monitor: ClusterIP-monitor-interval-33s
907  	                  interval=33s
908  	                start: ClusterIP-start-interval-30s
909  	                  interval=30s timeout=180s
910  	            """
911  	        )
912  	        self.assert_pcs_success(
913  	            "resource update ClusterIP op monitor interval=33s start interval=30s timeout=180s".split()
914  	        )
915  	        self.assert_pcs_success(
916  	            "resource config ClusterIP".split(), show_clusterip
917  	        )
918  	
919  	        self.assert_pcs_success(
920  	            "resource update ClusterIP op monitor interval=33s start interval=30s timeout=180s".split()
921  	        )
922  	        self.assert_pcs_success(
923  	            "resource config ClusterIP".split(), show_clusterip
924  	        )
925  	
926  	        self.assert_pcs_success("resource update ClusterIP op".split())
927  	        self.assert_pcs_success(
928  	            "resource config ClusterIP".split(), show_clusterip
929  	        )
930  	
931  	        self.assert_pcs_success("resource update ClusterIP op monitor".split())
932  	        self.assert_pcs_success(
933  	            "resource config ClusterIP".split(), show_clusterip
934  	        )
935  	
936  	        # test invalid id
937  	        self.assert_pcs_fail_regardless_of_force(
938  	            "resource update ClusterIP op monitor interval=30 id=ab#cd".split(),
939  	            (
940  	                "Error: invalid operation id 'ab#cd', '#' is not a valid character"
941  	                " for a operation id\n"
942  	            ),
943  	        )
944  	        self.assert_pcs_success(
945  	            "resource config ClusterIP".split(), show_clusterip
946  	        )
947  	
948  	        # test existing id
949  	        self.assert_pcs_fail_regardless_of_force(
950  	            "resource update ClusterIP op monitor interval=30 id=ClusterIP".split(),
951  	            (
952  	                "Error: id 'ClusterIP' is already in use, please specify another"
953  	                " one\n"
954  	            ),
955  	        )
956  	        self.assert_pcs_success(
957  	            "resource config ClusterIP".split(), show_clusterip
958  	        )
959  	
960  	        # test id change
961  	        # there is a bug:
962  	        # - first an existing operation is removed
963  	        # - then a new operation is created at the same place
964  	        # - therefore options not specified for in the command are removed
965  	        #    instead of them being kept from the old operation
966  	        # This needs to be fixed. However it's not my task currently.
967  	        # Moreover it is documented behavior.
968  	        self.assert_pcs_success(
969  	            "resource update ClusterIP op monitor id=abcd".split()
970  	        )
971  	        self.assert_pcs_success(
972  	            "resource config ClusterIP".split(),
973  	            dedent(
974  	                """\
975  	                Resource: ClusterIP (class=ocf provider=pcsmock type=params)
976  	                  Attributes: ClusterIP-instance_attributes
977  	                    mandatory=value
978  	                  Operations:
979  	                    monitor: abcd
980  	                      interval=60s
981  	                    start: ClusterIP-start-interval-30s
982  	                      interval=30s timeout=180s
983  	                """
984  	            ),
985  	        )
986  	
987  	        # test two monitor operations:
988  	        # - the first one is updated
989  	        # - operation duplicity detection test
990  	        self.assert_pcs_success(
991  	            "resource create A ocf:pcsmock:minimal op monitor interval=10 op monitor interval=20".split()
992  	        )
993  	        self.assert_pcs_success(
994  	            "resource config A".split(),
995  	            dedent(
996  	                """\
997  	                Resource: A (class=ocf provider=pcsmock type=minimal)
998  	                  Operations:
999  	                    migrate_from: A-migrate_from-interval-0s
1000 	                      interval=0s timeout=20s
1001 	                    migrate_to: A-migrate_to-interval-0s
1002 	                      interval=0s timeout=20s
1003 	                    monitor: A-monitor-interval-10
1004 	                      interval=10
1005 	                    monitor: A-monitor-interval-20
1006 	                      interval=20
1007 	                    reload: A-reload-interval-0s
1008 	                      interval=0s timeout=20s
1009 	                    reload-agent: A-reload-agent-interval-0s
1010 	                      interval=0s timeout=20s
1011 	                    start: A-start-interval-0s
1012 	                      interval=0s timeout=20s
1013 	                    stop: A-stop-interval-0s
1014 	                      interval=0s timeout=20s
1015 	                """
1016 	            ),
1017 	        )
1018 	
1019 	        self.assert_pcs_fail(
1020 	            "resource update A op monitor interval=20".split(),
1021 	            (
1022 	                "Error: operation monitor with interval 20s already specified for A:\n"
1023 	                "monitor interval=20 (A-monitor-interval-20)\n"
1024 	            ),
1025 	        )
1026 	
1027 	        self.assert_pcs_success(
1028 	            "resource update A op monitor interval=11".split(),
1029 	        )
1030 	
1031 	        self.assert_pcs_success(
1032 	            "resource config A".split(),
1033 	            dedent(
1034 	                """\
1035 	                Resource: A (class=ocf provider=pcsmock type=minimal)
1036 	                  Operations:
1037 	                    migrate_from: A-migrate_from-interval-0s
1038 	                      interval=0s timeout=20s
1039 	                    migrate_to: A-migrate_to-interval-0s
1040 	                      interval=0s timeout=20s
1041 	                    monitor: A-monitor-interval-11
1042 	                      interval=11
1043 	                    monitor: A-monitor-interval-20
1044 	                      interval=20
1045 	                    reload: A-reload-interval-0s
1046 	                      interval=0s timeout=20s
1047 	                    reload-agent: A-reload-agent-interval-0s
1048 	                      interval=0s timeout=20s
1049 	                    start: A-start-interval-0s
1050 	                      interval=0s timeout=20s
1051 	                    stop: A-stop-interval-0s
1052 	                      interval=0s timeout=20s
1053 	                """
1054 	            ),
1055 	        )
1056 	
1057 	        self.assert_pcs_success(
1058 	            "resource create B ocf:pcsmock:minimal --no-default-ops".split(),
1059 	        )
1060 	
1061 	        self.assert_pcs_success(
1062 	            "resource op remove B-monitor-interval-10s".split()
1063 	        )
1064 	
1065 	        self.assert_pcs_success(
1066 	            "resource config B".split(),
1067 	            "Resource: B (class=ocf provider=pcsmock type=minimal)\n",
1068 	        )
1069 	
1070 	        self.assert_pcs_success(
1071 	            "resource update B op monitor interval=60s".split(),
1072 	        )
1073 	
1074 	        self.assert_pcs_success(
1075 	            "resource config B".split(),
1076 	            dedent(
1077 	                """\
1078 	                Resource: B (class=ocf provider=pcsmock type=minimal)
1079 	                  Operations:
1080 	                    monitor: B-monitor-interval-60s
1081 	                      interval=60s
1082 	                """
1083 	            ),
1084 	        )
1085 	
1086 	        self.assert_pcs_success(
1087 	            "resource update B op monitor interval=30".split(),
1088 	        )
1089 	
1090 	        self.assert_pcs_success(
1091 	            "resource config B".split(),
1092 	            dedent(
1093 	                """\
1094 	                Resource: B (class=ocf provider=pcsmock type=minimal)
1095 	                  Operations:
1096 	                    monitor: B-monitor-interval-30
1097 	                      interval=30
1098 	                """
1099 	            ),
1100 	        )
1101 	
1102 	        self.assert_pcs_success(
1103 	            "resource update B op start interval=0 timeout=10".split(),
1104 	        )
1105 	
1106 	        self.assert_pcs_success(
1107 	            "resource config B".split(),
1108 	            dedent(
1109 	                """\
1110 	                Resource: B (class=ocf provider=pcsmock type=minimal)
1111 	                  Operations:
1112 	                    monitor: B-monitor-interval-30
1113 	                      interval=30
1114 	                    start: B-start-interval-0
1115 	                      interval=0 timeout=10
1116 	                """
1117 	            ),
1118 	        )
1119 	
1120 	        self.assert_pcs_success(
1121 	            "resource update B op start interval=0 timeout=20".split(),
1122 	        )
1123 	
1124 	        self.assert_pcs_success(
1125 	            "resource config B".split(),
1126 	            dedent(
1127 	                """\
1128 	                Resource: B (class=ocf provider=pcsmock type=minimal)
1129 	                  Operations:
1130 	                    monitor: B-monitor-interval-30
1131 	                      interval=30
1132 	                    start: B-start-interval-0
1133 	                      interval=0 timeout=20
1134 	                """
1135 	            ),
1136 	        )
1137 	
1138 	        self.assert_pcs_success(
1139 	            "resource update B op monitor interval=33".split(),
1140 	        )
1141 	
1142 	        self.assert_pcs_success(
1143 	            "resource config B".split(),
1144 	            dedent(
1145 	                """\
1146 	                Resource: B (class=ocf provider=pcsmock type=minimal)
1147 	                  Operations:
1148 	                    monitor: B-monitor-interval-33
1149 	                      interval=33
1150 	                    start: B-start-interval-0
1151 	                      interval=0 timeout=20
1152 	                """
1153 	            ),
1154 	        )
1155 	
1156 	        self.assert_pcs_fail(
1157 	            "resource update B op monitor interval=100 role=Master".split(),
1158 	            "Error: 'Master' is not a valid role value, use {}\n".format(
1159 	                format_list(const.PCMK_ROLES)
1160 	            ),
1161 	        )
1162 	
1163 	        self.assert_pcs_success(
1164 	            "resource update B op monitor interval=100 role=Promoted".split(),
1165 	        )
1166 	
1167 	        self.assert_pcs_success(
1168 	            "resource config B".split(),
1169 	            dedent(
1170 	                f"""\
1171 	                Resource: B (class=ocf provider=pcsmock type=minimal)
1172 	                  Operations:
1173 	                    monitor: B-monitor-interval-33
1174 	                      interval=33
1175 	                    start: B-start-interval-0
1176 	                      interval=0 timeout=20
1177 	                    monitor: B-monitor-interval-100
1178 	                      interval=100 role={const.PCMK_ROLE_PROMOTED}
1179 	                """
1180 	            ),
1181 	        )
1182 	
1183 	        self.assert_pcs_success(
1184 	            "resource update B op start interval=0 timeout=22".split(),
1185 	        )
1186 	
1187 	        self.assert_pcs_success(
1188 	            "resource config B".split(),
1189 	            dedent(
1190 	                f"""\
1191 	                Resource: B (class=ocf provider=pcsmock type=minimal)
1192 	                  Operations:
1193 	                    monitor: B-monitor-interval-33
1194 	                      interval=33
1195 	                    start: B-start-interval-0
1196 	                      interval=0 timeout=22
1197 	                    monitor: B-monitor-interval-100
1198 	                      interval=100 role={const.PCMK_ROLE_PROMOTED}
1199 	                """
1200 	            ),
1201 	        )
1202 	
1203 	    @skip_unless_crm_rule()
1204 	    def test_group_ungroup(self):
1205 	        self.assert_pcs_success(
1206 	            "resource create --no-default-ops A1 ocf:pcsmock:minimal".split(),
1207 	        )
1208 	        self.assert_pcs_success(
1209 	            "resource create --no-default-ops A2 ocf:pcsmock:minimal".split(),
1210 	        )
1211 	        self.assert_pcs_success(
1212 	            "resource create --no-default-ops A3 ocf:pcsmock:minimal".split(),
1213 	        )
1214 	        self.assert_pcs_success(
1215 	            "resource create --no-default-ops A4 ocf:pcsmock:minimal".split(),
1216 	        )
1217 	        self.assert_pcs_success(
1218 	            "resource create --no-default-ops A5 ocf:pcsmock:minimal".split(),
1219 	        )
1220 	
1221 	        self.assert_pcs_success(
1222 	            "resource group add AGroup A1 A2 A3 A4 A5".split(),
1223 	        )
1224 	
1225 	        self.assert_pcs_success(
1226 	            "resource config AGroup".split(),
1227 	            dedent(
1228 	                """\
1229 	                Group: AGroup
1230 	                  Resource: A1 (class=ocf provider=pcsmock type=minimal)
1231 	                    Operations:
1232 	                      monitor: A1-monitor-interval-10s
1233 	                        interval=10s timeout=20s
1234 	                  Resource: A2 (class=ocf provider=pcsmock type=minimal)
1235 	                    Operations:
1236 	                      monitor: A2-monitor-interval-10s
1237 	                        interval=10s timeout=20s
1238 	                  Resource: A3 (class=ocf provider=pcsmock type=minimal)
1239 	                    Operations:
1240 	                      monitor: A3-monitor-interval-10s
1241 	                        interval=10s timeout=20s
1242 	                  Resource: A4 (class=ocf provider=pcsmock type=minimal)
1243 	                    Operations:
1244 	                      monitor: A4-monitor-interval-10s
1245 	                        interval=10s timeout=20s
1246 	                  Resource: A5 (class=ocf provider=pcsmock type=minimal)
1247 	                    Operations:
1248 	                      monitor: A5-monitor-interval-10s
1249 	                        interval=10s timeout=20s
1250 	                """
1251 	            ),
1252 	        )
1253 	
1254 	    def test_group_order(self):
1255 	        # This was considered for removing during 'resource group add' command
1256 	        # and tests overhaul. However, this is the only test where "resource
1257 	        # group list" is called. Due to that this test was not deleted.
1258 	        self.assert_pcs_success(
1259 	            "resource create --no-default-ops A ocf:pcsmock:minimal".split(),
1260 	        )
1261 	        self.assert_pcs_success(
1262 	            "resource create --no-default-ops B ocf:pcsmock:minimal".split(),
1263 	        )
1264 	        self.assert_pcs_success(
1265 	            "resource create --no-default-ops C ocf:pcsmock:minimal".split(),
1266 	        )
1267 	        self.assert_pcs_success(
1268 	            "resource create --no-default-ops D ocf:pcsmock:minimal".split(),
1269 	        )
1270 	        self.assert_pcs_success(
1271 	            "resource create --no-default-ops E ocf:pcsmock:minimal".split(),
1272 	        )
1273 	        self.assert_pcs_success(
1274 	            "resource create --no-default-ops F ocf:pcsmock:minimal".split(),
1275 	        )
1276 	        self.assert_pcs_success(
1277 	            "resource create --no-default-ops G ocf:pcsmock:minimal".split(),
1278 	        )
1279 	        self.assert_pcs_success(
1280 	            "resource create --no-default-ops H ocf:pcsmock:minimal".split(),
1281 	        )
1282 	        self.assert_pcs_success(
1283 	            "resource create --no-default-ops I ocf:pcsmock:minimal".split(),
1284 	        )
1285 	        self.assert_pcs_success(
1286 	            "resource create --no-default-ops J ocf:pcsmock:minimal".split(),
1287 	        )
1288 	        self.assert_pcs_success(
1289 	            "resource create --no-default-ops K ocf:pcsmock:minimal".split(),
1290 	        )
1291 	
1292 	        self.assert_pcs_success(
1293 	            "resource group add RGA A B C E D K J I".split()
1294 	        )
1295 	
1296 	        stdout, stderr, returncode = self.pcs_runner.run(["resource"])
1297 	        self.assertEqual(stderr, "")
1298 	        self.assertEqual(returncode, 0)
1299 	        if is_pacemaker_21_without_20_compatibility():
1300 	            self.assertEqual(
1301 	                stdout,
1302 	                outdent(
1303 	                    """\
1304 	                      * F\t(ocf:pcsmock:minimal):\t Stopped
1305 	                      * G\t(ocf:pcsmock:minimal):\t Stopped
1306 	                      * H\t(ocf:pcsmock:minimal):\t Stopped
1307 	                      * Resource Group: RGA:
1308 	                        * A\t(ocf:pcsmock:minimal):\t Stopped
1309 	                        * B\t(ocf:pcsmock:minimal):\t Stopped
1310 	                        * C\t(ocf:pcsmock:minimal):\t Stopped
1311 	                        * E\t(ocf:pcsmock:minimal):\t Stopped
1312 	                        * D\t(ocf:pcsmock:minimal):\t Stopped
1313 	                        * K\t(ocf:pcsmock:minimal):\t Stopped
1314 	                        * J\t(ocf:pcsmock:minimal):\t Stopped
1315 	                        * I\t(ocf:pcsmock:minimal):\t Stopped
1316 	                    """
1317 	                ),
1318 	            )
1319 	        elif PCMK_2_0_3_PLUS:
1320 	            assert_pcs_status(
1321 	                stdout,
1322 	                """\
1323 	  * F\t(ocf::pcsmock:minimal):\tStopped
1324 	  * G\t(ocf::pcsmock:minimal):\tStopped
1325 	  * H\t(ocf::pcsmock:minimal):\tStopped
1326 	  * Resource Group: RGA:
1327 	    * A\t(ocf::pcsmock:minimal):\tStopped
1328 	    * B\t(ocf::pcsmock:minimal):\tStopped
1329 	    * C\t(ocf::pcsmock:minimal):\tStopped
1330 	    * E\t(ocf::pcsmock:minimal):\tStopped
1331 	    * D\t(ocf::pcsmock:minimal):\tStopped
1332 	    * K\t(ocf::pcsmock:minimal):\tStopped
1333 	    * J\t(ocf::pcsmock:minimal):\tStopped
1334 	    * I\t(ocf::pcsmock:minimal):\tStopped
1335 	""",
1336 	            )
1337 	        else:
1338 	            self.assertEqual(
1339 	                stdout,
1340 	                """\
1341 	 F\t(ocf::pcsmock:minimal):\tStopped
1342 	 G\t(ocf::pcsmock:minimal):\tStopped
1343 	 H\t(ocf::pcsmock:minimal):\tStopped
1344 	 Resource Group: RGA
1345 	     A\t(ocf::pcsmock:minimal):\tStopped
1346 	     B\t(ocf::pcsmock:minimal):\tStopped
1347 	     C\t(ocf::pcsmock:minimal):\tStopped
1348 	     E\t(ocf::pcsmock:minimal):\tStopped
1349 	     D\t(ocf::pcsmock:minimal):\tStopped
1350 	     K\t(ocf::pcsmock:minimal):\tStopped
1351 	     J\t(ocf::pcsmock:minimal):\tStopped
1352 	     I\t(ocf::pcsmock:minimal):\tStopped
1353 	""",
1354 	            )
1355 	
1356 	        self.assert_pcs_success(
1357 	            "resource group list".split(), "RGA: A B C E D K J I\n"
1358 	        )
1359 	
1360 	    @skip_unless_crm_rule()
1361 	    def test_cluster_config(self):
1362 	        self.setup_cluster_a()
1363 	
1364 	        self.pcs_runner.mock_settings = {
1365 	            "corosync_conf_file": rc("corosync.conf"),
1366 	        }
1367 	        self.assert_pcs_success(
1368 	            ["config"],
1369 	            dedent(
1370 	                """\
1371 	                Cluster Name: test99
1372 	                Corosync Nodes:
1373 	                 rh7-1 rh7-2
1374 	                Pacemaker Nodes:
1375 	
1376 	                Resources:
1377 	                  Resource: ClusterIP6 (class=ocf provider=pcsmock type=minimal)
1378 	                    Operations:
1379 	                      monitor: ClusterIP6-monitor-interval-10s
1380 	                        interval=10s timeout=20s
1381 	                  Group: TestGroup1
1382 	                    Resource: ClusterIP (class=ocf provider=pcsmock type=minimal)
1383 	                      Operations:
1384 	                        monitor: ClusterIP-monitor-interval-10s
1385 	                          interval=10s timeout=20s
1386 	                  Group: TestGroup2
1387 	                    Resource: ClusterIP2 (class=ocf provider=pcsmock type=minimal)
1388 	                      Operations:
1389 	                        monitor: ClusterIP2-monitor-interval-10s
1390 	                          interval=10s timeout=20s
1391 	                    Resource: ClusterIP3 (class=ocf provider=pcsmock type=minimal)
1392 	                      Operations:
1393 	                        monitor: ClusterIP3-monitor-interval-10s
1394 	                          interval=10s timeout=20s
1395 	                  Clone: ClusterIP4-clone
1396 	                    Resource: ClusterIP4 (class=ocf provider=pcsmock type=minimal)
1397 	                      Operations:
1398 	                        monitor: ClusterIP4-monitor-interval-10s
1399 	                          interval=10s timeout=20s
1400 	                  Clone: Master
1401 	                    Meta Attributes:
1402 	                      promotable=true
1403 	                    Resource: ClusterIP5 (class=ocf provider=pcsmock type=minimal)
1404 	                      Operations:
1405 	                        monitor: ClusterIP5-monitor-interval-10s
1406 	                          interval=10s timeout=20s
1407 	                """
1408 	            ),
1409 	        )
1410 	
1411 	    def test_ms_group(self):
1412 	        self.assert_pcs_success(
1413 	            "resource create --no-default-ops D0 ocf:pcsmock:minimal".split(),
1414 	        )
1415 	        self.assert_pcs_success(
1416 	            "resource create --no-default-ops D1 ocf:pcsmock:minimal".split(),
1417 	        )
1418 	        self.assert_pcs_success("resource group add Group D0 D1".split())
1419 	
1420 	        # pcs no longer allows turning resources into masters but supports
1421 	        # existing ones. In order to test it, we need to put a master in the
1422 	        # CIB without pcs.
1423 	        wrap_element_by_master(self.temp_cib, "Group", master_id="GroupMaster")
1424 	
1425 	        self.assert_pcs_success(
1426 	            "resource config".split(),
1427 	            dedent(
1428 	                """\
1429 	                Clone: GroupMaster
1430 	                  Meta Attributes:
1431 	                    promotable=true
1432 	                  Group: Group
1433 	                    Resource: D0 (class=ocf provider=pcsmock type=minimal)
1434 	                      Operations:
1435 	                        monitor: D0-monitor-interval-10s
1436 	                          interval=10s timeout=20s
1437 	                    Resource: D1 (class=ocf provider=pcsmock type=minimal)
1438 	                      Operations:
1439 	                        monitor: D1-monitor-interval-10s
1440 	                          interval=10s timeout=20s
1441 	                """
1442 	            ),
1443 	        )
1444 	
1445 	    def test_unclone(self):
1446 	        # see also BundleClone
1447 	        self.assert_pcs_success(
1448 	            "resource create --no-default-ops dummy1 ocf:pcsmock:minimal".split()
1449 	        )
1450 	        self.assert_pcs_success(
1451 	            "resource create --no-default-ops dummy2 ocf:pcsmock:minimal".split(),
1452 	        )
1453 	        self.assert_pcs_success("resource group add gr dummy1".split())
1454 	
1455 	        self.assert_pcs_fail(
1456 	            "resource unclone gr".split(),
1457 	            "Error: 'gr' is not a clone resource\n",
1458 	        )
1459 	
1460 	        # unclone with a clone itself specified
1461 	        self.assert_pcs_success("resource group add gr dummy2".split())
1462 	        self.assert_pcs_success("resource clone gr".split())
1463 	        self.assert_pcs_success(
1464 	            "resource config".split(),
1465 	            dedent(
1466 	                """\
1467 	                Clone: gr-clone
1468 	                  Group: gr
1469 	                    Resource: dummy1 (class=ocf provider=pcsmock type=minimal)
1470 	                      Operations:
1471 	                        monitor: dummy1-monitor-interval-10s
1472 	                          interval=10s timeout=20s
1473 	                    Resource: dummy2 (class=ocf provider=pcsmock type=minimal)
1474 	                      Operations:
1475 	                        monitor: dummy2-monitor-interval-10s
1476 	                          interval=10s timeout=20s
1477 	                """
1478 	            ),
1479 	        )
1480 	
1481 	        self.assert_pcs_success("resource unclone gr-clone".split())
1482 	        self.assert_pcs_success(
1483 	            "resource config".split(),
1484 	            dedent(
1485 	                """\
1486 	                Group: gr
1487 	                  Resource: dummy1 (class=ocf provider=pcsmock type=minimal)
1488 	                    Operations:
1489 	                      monitor: dummy1-monitor-interval-10s
1490 	                        interval=10s timeout=20s
1491 	                  Resource: dummy2 (class=ocf provider=pcsmock type=minimal)
1492 	                    Operations:
1493 	                      monitor: dummy2-monitor-interval-10s
1494 	                        interval=10s timeout=20s
1495 	                """
1496 	            ),
1497 	        )
1498 	
1499 	        # unclone with a cloned group specified
1500 	        self.assert_pcs_success("resource clone gr".split())
1501 	        self.assert_pcs_success(
1502 	            "resource config".split(),
1503 	            dedent(
1504 	                """\
1505 	                Clone: gr-clone
1506 	                  Group: gr
1507 	                    Resource: dummy1 (class=ocf provider=pcsmock type=minimal)
1508 	                      Operations:
1509 	                        monitor: dummy1-monitor-interval-10s
1510 	                          interval=10s timeout=20s
1511 	                    Resource: dummy2 (class=ocf provider=pcsmock type=minimal)
1512 	                      Operations:
1513 	                        monitor: dummy2-monitor-interval-10s
1514 	                          interval=10s timeout=20s
1515 	                """
1516 	            ),
1517 	        )
1518 	
1519 	        self.assert_pcs_success("resource unclone gr".split())
1520 	        self.assert_pcs_success(
1521 	            "resource config".split(),
1522 	            dedent(
1523 	                """\
1524 	                Group: gr
1525 	                  Resource: dummy1 (class=ocf provider=pcsmock type=minimal)
1526 	                    Operations:
1527 	                      monitor: dummy1-monitor-interval-10s
1528 	                        interval=10s timeout=20s
1529 	                  Resource: dummy2 (class=ocf provider=pcsmock type=minimal)
1530 	                    Operations:
1531 	                      monitor: dummy2-monitor-interval-10s
1532 	                        interval=10s timeout=20s
1533 	                """
1534 	            ),
1535 	        )
1536 	
1537 	        # unclone with a cloned grouped resource specified
1538 	        self.assert_pcs_success("resource clone gr".split())
1539 	        self.assert_pcs_success(
1540 	            "resource config".split(),
1541 	            dedent(
1542 	                """\
1543 	                Clone: gr-clone
1544 	                  Group: gr
1545 	                    Resource: dummy1 (class=ocf provider=pcsmock type=minimal)
1546 	                      Operations:
1547 	                        monitor: dummy1-monitor-interval-10s
1548 	                          interval=10s timeout=20s
1549 	                    Resource: dummy2 (class=ocf provider=pcsmock type=minimal)
1550 	                      Operations:
1551 	                        monitor: dummy2-monitor-interval-10s
1552 	                          interval=10s timeout=20s
1553 	                """
1554 	            ),
1555 	        )
1556 	
1557 	        self.assert_pcs_success("resource unclone dummy1".split())
1558 	        self.assert_pcs_success(
1559 	            "resource config".split(),
1560 	            dedent(
1561 	                """\
1562 	                Resource: dummy1 (class=ocf provider=pcsmock type=minimal)
1563 	                  Operations:
1564 	                    monitor: dummy1-monitor-interval-10s
1565 	                      interval=10s timeout=20s
1566 	                Clone: gr-clone
1567 	                  Group: gr
1568 	                    Resource: dummy2 (class=ocf provider=pcsmock type=minimal)
1569 	                      Operations:
1570 	                        monitor: dummy2-monitor-interval-10s
1571 	                          interval=10s timeout=20s
1572 	                """
1573 	            ),
1574 	        )
1575 	
1576 	        self.assert_pcs_success("resource unclone dummy2".split())
1577 	        self.assert_pcs_success(
1578 	            "resource config".split(),
1579 	            dedent(
1580 	                """\
1581 	                Resource: dummy1 (class=ocf provider=pcsmock type=minimal)
1582 	                  Operations:
1583 	                    monitor: dummy1-monitor-interval-10s
1584 	                      interval=10s timeout=20s
1585 	                Resource: dummy2 (class=ocf provider=pcsmock type=minimal)
1586 	                  Operations:
1587 	                    monitor: dummy2-monitor-interval-10s
1588 	                      interval=10s timeout=20s
1589 	                """
1590 	            ),
1591 	        )
1592 	
1593 	    def test_unclone_master(self):
1594 	        # see also BundleClone
1595 	        self.assert_pcs_success(
1596 	            "resource create --no-default-ops dummy1 ocf:pcsmock:stateful".split(),
1597 	        )
1598 	        self.assert_pcs_success(
1599 	            "resource create --no-default-ops dummy2 ocf:pcsmock:stateful".split(),
1600 	        )
1601 	
1602 	        # try to unclone a non-cloned resource
1603 	        self.assert_pcs_fail(
1604 	            "resource unclone dummy1".split(),
1605 	            "Error: 'dummy1' is not a clone resource\n",
1606 	        )
1607 	
1608 	        self.assert_pcs_success("resource group add gr dummy1".split())
1609 	
1610 	        self.assert_pcs_fail(
1611 	            "resource unclone gr".split(),
1612 	            "Error: 'gr' is not a clone resource\n",
1613 	        )
1614 	
1615 	        # unclone with a cloned primitive specified
1616 	        # pcs no longer allows turning resources into masters but supports
1617 	        # existing ones. In order to test it, we need to put a master in the
1618 	        # CIB without pcs.
1619 	        wrap_element_by_master(self.temp_cib, "dummy2")
1620 	        self.assert_pcs_success(
1621 	            "resource config".split(),
1622 	            dedent(
1623 	                f"""\
1624 	                Group: gr
1625 	                  Resource: dummy1 (class=ocf provider=pcsmock type=stateful)
1626 	                    Operations:
1627 	                      monitor: dummy1-monitor-interval-10s
1628 	                        interval=10s timeout=20s role={const.PCMK_ROLE_PROMOTED}
1629 	                      monitor: dummy1-monitor-interval-11s
1630 	                        interval=11s timeout=20s role={const.PCMK_ROLE_UNPROMOTED}
1631 	                Clone: dummy2-master
1632 	                  Meta Attributes:
1633 	                    promotable=true
1634 	                  Resource: dummy2 (class=ocf provider=pcsmock type=stateful)
1635 	                    Operations:
1636 	                      monitor: dummy2-monitor-interval-10s
1637 	                        interval=10s timeout=20s role={const.PCMK_ROLE_PROMOTED}
1638 	                      monitor: dummy2-monitor-interval-11s
1639 	                        interval=11s timeout=20s role={const.PCMK_ROLE_UNPROMOTED}
1640 	                """
1641 	            ),
1642 	        )
1643 	
1644 	        self.assert_pcs_success("resource unclone dummy2".split())
1645 	        self.assert_pcs_success(
1646 	            "resource config".split(),
1647 	            dedent(
1648 	                f"""\
1649 	                Resource: dummy2 (class=ocf provider=pcsmock type=stateful)
1650 	                  Operations:
1651 	                    monitor: dummy2-monitor-interval-10s
1652 	                      interval=10s timeout=20s role={const.PCMK_ROLE_PROMOTED}
1653 	                    monitor: dummy2-monitor-interval-11s
1654 	                      interval=11s timeout=20s role={const.PCMK_ROLE_UNPROMOTED}
1655 	                Group: gr
1656 	                  Resource: dummy1 (class=ocf provider=pcsmock type=stateful)
1657 	                    Operations:
1658 	                      monitor: dummy1-monitor-interval-10s
1659 	                        interval=10s timeout=20s role={const.PCMK_ROLE_PROMOTED}
1660 	                      monitor: dummy1-monitor-interval-11s
1661 	                        interval=11s timeout=20s role={const.PCMK_ROLE_UNPROMOTED}
1662 	                """
1663 	            ),
1664 	        )
1665 	
1666 	        # unclone with a clone itself specified
1667 	        self.assert_pcs_success("resource group add gr dummy2".split())
1668 	        # pcs no longer allows turning resources into masters but supports
1669 	        # existing ones. In order to test it, we need to put a master in the
1670 	        # CIB without pcs.
1671 	        wrap_element_by_master(self.temp_cib, "gr")
1672 	        self.assert_pcs_success(
1673 	            "resource config".split(),
1674 	            dedent(
1675 	                f"""\
1676 	                Clone: gr-master
1677 	                  Meta Attributes:
1678 	                    promotable=true
1679 	                  Group: gr
1680 	                    Resource: dummy1 (class=ocf provider=pcsmock type=stateful)
1681 	                      Operations:
1682 	                        monitor: dummy1-monitor-interval-10s
1683 	                          interval=10s timeout=20s role={const.PCMK_ROLE_PROMOTED}
1684 	                        monitor: dummy1-monitor-interval-11s
1685 	                          interval=11s timeout=20s role={const.PCMK_ROLE_UNPROMOTED}
1686 	                    Resource: dummy2 (class=ocf provider=pcsmock type=stateful)
1687 	                      Operations:
1688 	                        monitor: dummy2-monitor-interval-10s
1689 	                          interval=10s timeout=20s role={const.PCMK_ROLE_PROMOTED}
1690 	                        monitor: dummy2-monitor-interval-11s
1691 	                          interval=11s timeout=20s role={const.PCMK_ROLE_UNPROMOTED}
1692 	                """
1693 	            ),
1694 	        )
1695 	
1696 	        self.assert_pcs_success("resource unclone gr-master".split())
1697 	        self.assert_pcs_success(
1698 	            "resource config".split(),
1699 	            dedent(
1700 	                f"""\
1701 	                Group: gr
1702 	                  Resource: dummy1 (class=ocf provider=pcsmock type=stateful)
1703 	                    Operations:
1704 	                      monitor: dummy1-monitor-interval-10s
1705 	                        interval=10s timeout=20s role={const.PCMK_ROLE_PROMOTED}
1706 	                      monitor: dummy1-monitor-interval-11s
1707 	                        interval=11s timeout=20s role={const.PCMK_ROLE_UNPROMOTED}
1708 	                  Resource: dummy2 (class=ocf provider=pcsmock type=stateful)
1709 	                    Operations:
1710 	                      monitor: dummy2-monitor-interval-10s
1711 	                        interval=10s timeout=20s role={const.PCMK_ROLE_PROMOTED}
1712 	                      monitor: dummy2-monitor-interval-11s
1713 	                        interval=11s timeout=20s role={const.PCMK_ROLE_UNPROMOTED}
1714 	                """
1715 	            ),
1716 	        )
1717 	
1718 	        # unclone with a cloned group specified
1719 	        # pcs no longer allows turning resources into masters but supports
1720 	        # existing ones. In order to test it, we need to put a master in the
1721 	        # CIB without pcs.
1722 	        wrap_element_by_master(self.temp_cib, "gr")
1723 	        self.assert_pcs_success(
1724 	            "resource config".split(),
1725 	            dedent(
1726 	                f"""\
1727 	                Clone: gr-master
1728 	                  Meta Attributes:
1729 	                    promotable=true
1730 	                  Group: gr
1731 	                    Resource: dummy1 (class=ocf provider=pcsmock type=stateful)
1732 	                      Operations:
1733 	                        monitor: dummy1-monitor-interval-10s
1734 	                          interval=10s timeout=20s role={const.PCMK_ROLE_PROMOTED}
1735 	                        monitor: dummy1-monitor-interval-11s
1736 	                          interval=11s timeout=20s role={const.PCMK_ROLE_UNPROMOTED}
1737 	                    Resource: dummy2 (class=ocf provider=pcsmock type=stateful)
1738 	                      Operations:
1739 	                        monitor: dummy2-monitor-interval-10s
1740 	                          interval=10s timeout=20s role={const.PCMK_ROLE_PROMOTED}
1741 	                        monitor: dummy2-monitor-interval-11s
1742 	                          interval=11s timeout=20s role={const.PCMK_ROLE_UNPROMOTED}
1743 	                """
1744 	            ),
1745 	        )
1746 	
1747 	        self.assert_pcs_success("resource unclone gr".split())
1748 	        self.assert_pcs_success(
1749 	            "resource config".split(),
1750 	            dedent(
1751 	                f"""\
1752 	                Group: gr
1753 	                  Resource: dummy1 (class=ocf provider=pcsmock type=stateful)
1754 	                    Operations:
1755 	                      monitor: dummy1-monitor-interval-10s
1756 	                        interval=10s timeout=20s role={const.PCMK_ROLE_PROMOTED}
1757 	                      monitor: dummy1-monitor-interval-11s
1758 	                        interval=11s timeout=20s role={const.PCMK_ROLE_UNPROMOTED}
1759 	                  Resource: dummy2 (class=ocf provider=pcsmock type=stateful)
1760 	                    Operations:
1761 	                      monitor: dummy2-monitor-interval-10s
1762 	                        interval=10s timeout=20s role={const.PCMK_ROLE_PROMOTED}
1763 	                      monitor: dummy2-monitor-interval-11s
1764 	                        interval=11s timeout=20s role={const.PCMK_ROLE_UNPROMOTED}
1765 	                """
1766 	            ),
1767 	        )
1768 	
1769 	        # unclone with a cloned grouped resource specified
1770 	        self.assert_pcs_success("resource ungroup gr dummy2".split())
1771 	        # pcs no longer allows turning resources into masters but supports
1772 	        # existing ones. In order to test it, we need to put a master in the
1773 	        # CIB without pcs.
1774 	        wrap_element_by_master(self.temp_cib, "gr")
1775 	        self.assert_pcs_success(
1776 	            "resource config".split(),
1777 	            dedent(
1778 	                f"""\
1779 	                Resource: dummy2 (class=ocf provider=pcsmock type=stateful)
1780 	                  Operations:
1781 	                    monitor: dummy2-monitor-interval-10s
1782 	                      interval=10s timeout=20s role={const.PCMK_ROLE_PROMOTED}
1783 	                    monitor: dummy2-monitor-interval-11s
1784 	                      interval=11s timeout=20s role={const.PCMK_ROLE_UNPROMOTED}
1785 	                Clone: gr-master
1786 	                  Meta Attributes:
1787 	                    promotable=true
1788 	                  Group: gr
1789 	                    Resource: dummy1 (class=ocf provider=pcsmock type=stateful)
1790 	                      Operations:
1791 	                        monitor: dummy1-monitor-interval-10s
1792 	                          interval=10s timeout=20s role={const.PCMK_ROLE_PROMOTED}
1793 	                        monitor: dummy1-monitor-interval-11s
1794 	                          interval=11s timeout=20s role={const.PCMK_ROLE_UNPROMOTED}
1795 	                """
1796 	            ),
1797 	        )
1798 	
1799 	        self.assert_pcs_success("resource unclone dummy1".split())
1800 	        self.assert_pcs_success(
1801 	            "resource config".split(),
1802 	            dedent(
1803 	                f"""\
1804 	                Resource: dummy2 (class=ocf provider=pcsmock type=stateful)
1805 	                  Operations:
1806 	                    monitor: dummy2-monitor-interval-10s
1807 	                      interval=10s timeout=20s role={const.PCMK_ROLE_PROMOTED}
1808 	                    monitor: dummy2-monitor-interval-11s
1809 	                      interval=11s timeout=20s role={const.PCMK_ROLE_UNPROMOTED}
1810 	                Resource: dummy1 (class=ocf provider=pcsmock type=stateful)
1811 	                  Operations:
1812 	                    monitor: dummy1-monitor-interval-10s
1813 	                      interval=10s timeout=20s role={const.PCMK_ROLE_PROMOTED}
1814 	                    monitor: dummy1-monitor-interval-11s
1815 	                      interval=11s timeout=20s role={const.PCMK_ROLE_UNPROMOTED}
1816 	                """
1817 	            ),
1818 	        )
1819 	
1820 	        self.assert_pcs_success("resource group add gr dummy1 dummy2".split())
1821 	
1822 	        # pcs no longer allows turning resources into masters but supports
1823 	        # existing ones. In order to test it, we need to put a master in the
1824 	        # CIB without pcs.
1825 	        wrap_element_by_master(self.temp_cib, "gr")
1826 	        self.assert_pcs_success(
1827 	            "resource config".split(),
1828 	            dedent(
1829 	                f"""\
1830 	                Clone: gr-master
1831 	                  Meta Attributes:
1832 	                    promotable=true
1833 	                  Group: gr
1834 	                    Resource: dummy1 (class=ocf provider=pcsmock type=stateful)
1835 	                      Operations:
1836 	                        monitor: dummy1-monitor-interval-10s
1837 	                          interval=10s timeout=20s role={const.PCMK_ROLE_PROMOTED}
1838 	                        monitor: dummy1-monitor-interval-11s
1839 	                          interval=11s timeout=20s role={const.PCMK_ROLE_UNPROMOTED}
1840 	                    Resource: dummy2 (class=ocf provider=pcsmock type=stateful)
1841 	                      Operations:
1842 	                        monitor: dummy2-monitor-interval-10s
1843 	                          interval=10s timeout=20s role={const.PCMK_ROLE_PROMOTED}
1844 	                        monitor: dummy2-monitor-interval-11s
1845 	                          interval=11s timeout=20s role={const.PCMK_ROLE_UNPROMOTED}
1846 	                """
1847 	            ),
1848 	        )
1849 	
1850 	        self.assert_pcs_success("resource unclone dummy2".split())
1851 	
1852 	        self.assert_pcs_success(
1853 	            "resource config".split(),
1854 	            dedent(
1855 	                f"""\
1856 	                Resource: dummy2 (class=ocf provider=pcsmock type=stateful)
1857 	                  Operations:
1858 	                    monitor: dummy2-monitor-interval-10s
1859 	                      interval=10s timeout=20s role={const.PCMK_ROLE_PROMOTED}
1860 	                    monitor: dummy2-monitor-interval-11s
1861 	                      interval=11s timeout=20s role={const.PCMK_ROLE_UNPROMOTED}
1862 	                Clone: gr-master
1863 	                  Meta Attributes:
1864 	                    promotable=true
1865 	                  Group: gr
1866 	                    Resource: dummy1 (class=ocf provider=pcsmock type=stateful)
1867 	                      Operations:
1868 	                        monitor: dummy1-monitor-interval-10s
1869 	                          interval=10s timeout=20s role={const.PCMK_ROLE_PROMOTED}
1870 	                        monitor: dummy1-monitor-interval-11s
1871 	                          interval=11s timeout=20s role={const.PCMK_ROLE_UNPROMOTED}
1872 	                """
1873 	            ),
1874 	        )
1875 	
1876 	    def test_clone_group_member(self):
1877 	        self.assert_pcs_success(
1878 	            "resource create --no-default-ops D0 ocf:pcsmock:minimal group AG".split(),
1879 	        )
1880 	        self.assert_pcs_success(
1881 	            "resource create --no-default-ops D1 ocf:pcsmock:minimal group AG".split(),
1882 	        )
1883 	
1884 	        self.assert_pcs_success("resource clone D0".split())
1885 	        self.assert_pcs_success(
1886 	            ["resource", "config"],
1887 	            dedent(
1888 	                """\
1889 	                Group: AG
1890 	                  Resource: D1 (class=ocf provider=pcsmock type=minimal)
1891 	                    Operations:
1892 	                      monitor: D1-monitor-interval-10s
1893 	                        interval=10s timeout=20s
1894 	                Clone: D0-clone
1895 	                  Resource: D0 (class=ocf provider=pcsmock type=minimal)
1896 	                    Operations:
1897 	                      monitor: D0-monitor-interval-10s
1898 	                        interval=10s timeout=20s
1899 	                """
1900 	            ),
1901 	        )
1902 	
1903 	        self.assert_pcs_success("resource clone D1".split())
1904 	        self.assert_pcs_success(
1905 	            ["resource", "config"],
1906 	            dedent(
1907 	                """\
1908 	                Clone: D0-clone
1909 	                  Resource: D0 (class=ocf provider=pcsmock type=minimal)
1910 	                    Operations:
1911 	                      monitor: D0-monitor-interval-10s
1912 	                        interval=10s timeout=20s
1913 	                Clone: D1-clone
1914 	                  Resource: D1 (class=ocf provider=pcsmock type=minimal)
1915 	                    Operations:
1916 	                      monitor: D1-monitor-interval-10s
1917 	                        interval=10s timeout=20s
1918 	                """
1919 	            ),
1920 	        )
1921 	
1922 	    def test_promotable_group_member(self):
1923 	        self.assert_pcs_success(
1924 	            "resource create --no-default-ops D0 ocf:pcsmock:stateful group AG".split(),
1925 	        )
1926 	        self.assert_pcs_success(
1927 	            "resource create --no-default-ops D1 ocf:pcsmock:stateful group AG".split(),
1928 	        )
1929 	
1930 	        self.assert_pcs_success("resource promotable D0".split())
1931 	        self.assert_pcs_success(
1932 	            ["resource", "config"],
1933 	            dedent(
1934 	                """\
1935 	                Group: AG
1936 	                  Resource: D1 (class=ocf provider=pcsmock type=stateful)
1937 	                    Operations:
1938 	                      monitor: D1-monitor-interval-10s
1939 	                        interval=10s timeout=20s role=Promoted
1940 	                      monitor: D1-monitor-interval-11s
1941 	                        interval=11s timeout=20s role=Unpromoted
1942 	                Clone: D0-clone
1943 	                  Meta Attributes: D0-clone-meta_attributes
1944 	                    promotable=true
1945 	                  Resource: D0 (class=ocf provider=pcsmock type=stateful)
1946 	                    Operations:
1947 	                      monitor: D0-monitor-interval-10s
1948 	                        interval=10s timeout=20s role=Promoted
1949 	                      monitor: D0-monitor-interval-11s
1950 	                        interval=11s timeout=20s role=Unpromoted
1951 	                """
1952 	            ),
1953 	        )
1954 	
1955 	        self.assert_pcs_success("resource promotable D1".split())
1956 	        self.assert_pcs_success(
1957 	            ["resource", "config"],
1958 	            dedent(
1959 	                """\
1960 	                Clone: D0-clone
1961 	                  Meta Attributes: D0-clone-meta_attributes
1962 	                    promotable=true
1963 	                  Resource: D0 (class=ocf provider=pcsmock type=stateful)
1964 	                    Operations:
1965 	                      monitor: D0-monitor-interval-10s
1966 	                        interval=10s timeout=20s role=Promoted
1967 	                      monitor: D0-monitor-interval-11s
1968 	                        interval=11s timeout=20s role=Unpromoted
1969 	                Clone: D1-clone
1970 	                  Meta Attributes: D1-clone-meta_attributes
1971 	                    promotable=true
1972 	                  Resource: D1 (class=ocf provider=pcsmock type=stateful)
1973 	                    Operations:
1974 	                      monitor: D1-monitor-interval-10s
1975 	                        interval=10s timeout=20s role=Promoted
1976 	                      monitor: D1-monitor-interval-11s
1977 	                        interval=11s timeout=20s role=Unpromoted
1978 	                """
1979 	            ),
1980 	        )
1981 	
1982 	    def test_clone_master(self):
1983 	        # see also BundleClone
1984 	        self.assert_pcs_success(
1985 	            "resource create --no-default-ops D0 ocf:pcsmock:stateful".split(),
1986 	        )
1987 	        self.assert_pcs_success(
1988 	            "resource create --no-default-ops D1 ocf:pcsmock:stateful".split(),
1989 	        )
1990 	        self.assert_pcs_success(
1991 	            "resource create --no-default-ops D2 ocf:pcsmock:stateful".split(),
1992 	        )
1993 	        self.assert_pcs_success(
1994 	            "resource create --no-default-ops D3 ocf:pcsmock:stateful".split(),
1995 	        )
1996 	        self.assert_pcs_success("resource clone D0".split())
1997 	
1998 	        self.assert_pcs_fail(
1999 	            "resource promotable D3 meta promotable=false".split(),
2000 	            "Error: you cannot specify both promotable option and promotable keyword\n",
2001 	        )
2002 	
2003 	        self.assert_pcs_success("resource promotable D3".split())
2004 	
2005 	        # pcs no longer allows turning resources into masters but supports
2006 	        # existing ones. In order to test it, we need to put a master in the
2007 	        # CIB without pcs.
2008 	        wrap_element_by_master(
2009 	            self.temp_cib, "D1", master_id="D1-master-custom"
2010 	        )
2011 	
2012 	        # pcs no longer allows turning resources into masters but supports
2013 	        # existing ones. In order to test it, we need to put a master in the
2014 	        # CIB without pcs.
2015 	        wrap_element_by_master(self.temp_cib, "D2")
2016 	
2017 	        self.assert_pcs_success(
2018 	            "resource config".split(),
2019 	            dedent(
2020 	                """\
2021 	                Clone: D0-clone
2022 	                  Resource: D0 (class=ocf provider=pcsmock type=stateful)
2023 	                    Operations:
2024 	                      monitor: D0-monitor-interval-10s
2025 	                        interval=10s timeout=20s role=Promoted
2026 	                      monitor: D0-monitor-interval-11s
2027 	                        interval=11s timeout=20s role=Unpromoted
2028 	                Clone: D3-clone
2029 	                  Meta Attributes: D3-clone-meta_attributes
2030 	                    promotable=true
2031 	                  Resource: D3 (class=ocf provider=pcsmock type=stateful)
2032 	                    Operations:
2033 	                      monitor: D3-monitor-interval-10s
2034 	                        interval=10s timeout=20s role=Promoted
2035 	                      monitor: D3-monitor-interval-11s
2036 	                        interval=11s timeout=20s role=Unpromoted
2037 	                Clone: D1-master-custom
2038 	                  Meta Attributes:
2039 	                    promotable=true
2040 	                  Resource: D1 (class=ocf provider=pcsmock type=stateful)
2041 	                    Operations:
2042 	                      monitor: D1-monitor-interval-10s
2043 	                        interval=10s timeout=20s role=Promoted
2044 	                      monitor: D1-monitor-interval-11s
2045 	                        interval=11s timeout=20s role=Unpromoted
2046 	                Clone: D2-master
2047 	                  Meta Attributes:
2048 	                    promotable=true
2049 	                  Resource: D2 (class=ocf provider=pcsmock type=stateful)
2050 	                    Operations:
2051 	                      monitor: D2-monitor-interval-10s
2052 	                        interval=10s timeout=20s role=Promoted
2053 	                      monitor: D2-monitor-interval-11s
2054 	                        interval=11s timeout=20s role=Unpromoted
2055 	                """
2056 	            ),
2057 	        )
2058 	
2059 	    def test_lsb_resource(self):
2060 	        self.assert_pcs_fail(
2061 	            "resource create --no-default-ops D2 lsb:pcsmock foo=bar".split(),
2062 	            (
2063 	                "Error: invalid resource option 'foo', there are no options"
2064 	                " allowed, use --force to override\n" + ERRORS_HAVE_OCCURRED
2065 	            ),
2066 	        )
2067 	        self.assert_pcs_success(
2068 	            "resource create --no-default-ops D2 lsb:pcsmock foo=bar --force".split(),
2069 	            stderr_full=(
2070 	                "Warning: invalid resource option 'foo', there are no options"
2071 	                " allowed\n"
2072 	            ),
2073 	        )
2074 	        self.assert_pcs_success(
2075 	            "resource config".split(),
2076 	            dedent(
2077 	                """\
2078 	                Resource: D2 (class=lsb type=pcsmock)
2079 	                  Attributes: D2-instance_attributes
2080 	                    foo=bar
2081 	                  Operations:
2082 	                    monitor: D2-monitor-interval-15
2083 	                      interval=15 timeout=15
2084 	                """
2085 	            ),
2086 	        )
2087 	
2088 	        self.assert_pcs_fail(
2089 	            "resource update D2 bar=baz".split(),
2090 	            (
2091 	                "Error: invalid resource option 'bar', there are no options"
2092 	                " allowed, use --force to override\n"
2093 	            ),
2094 	        )
2095 	        self.assert_pcs_success(
2096 	            "resource update D2 bar=baz --force".split(),
2097 	            stderr_full=(
2098 	                "Warning: invalid resource option 'bar', there are no options"
2099 	                " allowed\n"
2100 	            ),
2101 	        )
2102 	        self.assert_pcs_success(
2103 	            "resource config".split(),
2104 	            dedent(
2105 	                """\
2106 	                Resource: D2 (class=lsb type=pcsmock)
2107 	                  Attributes: D2-instance_attributes
2108 	                    bar=baz
2109 	                    foo=bar
2110 	                  Operations:
2111 	                    monitor: D2-monitor-interval-15
2112 	                      interval=15 timeout=15
2113 	                """
2114 	            ),
2115 	        )
2116 	
2117 	    @skip(
2118 	        "test of 'pcs resource debug-*' to be moved to pcs.lib with the "
2119 	        "command itself"
2120 	    )
2121 	    def test_debug_start_clone_group(self):
2122 	        self.assert_pcs_success(
2123 	            "resource create D0 ocf:pcsmock:minimal group DGroup".split(),
2124 	        )
2125 	        self.assert_pcs_success(
2126 	            "resource create D1 ocf:pcsmock:minimal group DGroup".split(),
2127 	        )
2128 	        self.assert_pcs_success(
2129 	            "resource create D2 ocf:pcsmock:minimal clone".split(),
2130 	        )
2131 	
2132 	        # pcs no longer allows creating masters but supports existing ones. In
2133 	        # order to test it, we need to put a master in the CIB without pcs.
2134 	        fixture_to_cib(self.temp_cib.name, fixture_master_xml("D3"))
2135 	
2136 	        self.assert_pcs_fail(
2137 	            "resource debug-start DGroup".split(),
2138 	            "Error: unable to debug-start a group, try one of the group's resource(s) (D0,D1)\n",
2139 	        )
2140 	        self.assert_pcs_fail(
2141 	            "resource debug-start D2-clone".split(),
2142 	            "Error: unable to debug-start a clone, try the clone's resource: D2\n",
2143 	        )
2144 	        self.assert_pcs_fail(
2145 	            "resource debug-start D3-master".split(),
2146 	            "Error: unable to debug-start a master, try the master's resource: D3\n",
2147 	        )
2148 	
2149 	    def test_group_clone_creation(self):
2150 	        self.assert_pcs_success(
2151 	            "resource create --no-default-ops D1 ocf:pcsmock:minimal group DGroup".split(),
2152 	        )
2153 	
2154 	        self.assert_pcs_fail(
2155 	            "resource clone DGroup1".split(),
2156 	            "Error: unable to find group or resource: DGroup1\n",
2157 	        )
2158 	
2159 	        self.assert_pcs_success("resource clone DGroup".split())
2160 	        self.assert_pcs_success(
2161 	            "resource config".split(),
2162 	            dedent(
2163 	                """\
2164 	                Clone: DGroup-clone
2165 	                  Group: DGroup
2166 	                    Resource: D1 (class=ocf provider=pcsmock type=minimal)
2167 	                      Operations:
2168 	                        monitor: D1-monitor-interval-10s
2169 	                          interval=10s timeout=20s
2170 	                """
2171 	            ),
2172 	        )
2173 	
2174 	        self.assert_pcs_fail(
2175 	            "resource clone DGroup".split(),
2176 	            "Error: cannot clone a group that has already been cloned\n",
2177 	        )
2178 	
2179 	    def test_group_promotable_creation(self):
2180 	        self.assert_pcs_success(
2181 	            "resource create --no-default-ops D1 ocf:pcsmock:stateful group DGroup".split(),
2182 	        )
2183 	
2184 	        self.assert_pcs_fail(
2185 	            "resource promotable DGroup1".split(),
2186 	            "Error: unable to find group or resource: DGroup1\n",
2187 	        )
2188 	
2189 	        self.assert_pcs_success("resource promotable DGroup".split())
2190 	        self.assert_pcs_success(
2191 	            "resource config".split(),
2192 	            dedent(
2193 	                """\
2194 	                Clone: DGroup-clone
2195 	                  Meta Attributes: DGroup-clone-meta_attributes
2196 	                    promotable=true
2197 	                  Group: DGroup
2198 	                    Resource: D1 (class=ocf provider=pcsmock type=stateful)
2199 	                      Operations:
2200 	                        monitor: D1-monitor-interval-10s
2201 	                          interval=10s timeout=20s role=Promoted
2202 	                        monitor: D1-monitor-interval-11s
2203 	                          interval=11s timeout=20s role=Unpromoted
2204 	                """
2205 	            ),
2206 	        )
2207 	
2208 	        self.assert_pcs_fail(
2209 	            "resource promotable DGroup".split(),
2210 	            "Error: cannot clone a group that has already been cloned\n",
2211 	        )
2212 	
2213 	    def test_resource_clone_creation(self):
2214 	        self.pcs_runner = PcsRunner(self.temp_large_cib.name)
2215 	        self.pcs_runner.mock_settings = get_mock_settings()
2216 	        # resource "dummy1" is already in "temp_large_cib
2217 	        self.assert_pcs_success("resource clone dummy1".split())
2218 	
2219 	    def test_resource_clone_id_clone_command(self):
2220 	        self.assert_pcs_success(
2221 	            "resource create --no-default-ops dummy-clone ocf:pcsmock:minimal".split(),
2222 	        )
2223 	        self.assert_pcs_success(
2224 	            "resource create --no-default-ops dummy ocf:pcsmock:minimal".split(),
2225 	        )
2226 	        self.assert_pcs_success("resource clone dummy".split())
2227 	        self.assert_pcs_success(
2228 	            "resource config".split(),
2229 	            dedent(
2230 	                """\
2231 	                Resource: dummy-clone (class=ocf provider=pcsmock type=minimal)
2232 	                  Operations:
2233 	                    monitor: dummy-clone-monitor-interval-10s
2234 	                      interval=10s timeout=20s
2235 	                Clone: dummy-clone-1
2236 	                  Resource: dummy (class=ocf provider=pcsmock type=minimal)
2237 	                    Operations:
2238 	                      monitor: dummy-monitor-interval-10s
2239 	                        interval=10s timeout=20s
2240 	                """
2241 	            ),
2242 	        )
2243 	
2244 	    def test_resource_clone_id_create_command(self):
2245 	        self.assert_pcs_success(
2246 	            "resource create --no-default-ops dummy-clone ocf:pcsmock:minimal".split(),
2247 	        )
2248 	        self.assert_pcs_success(
2249 	            "resource create --no-default-ops dummy ocf:pcsmock:minimal clone".split(),
2250 	        )
2251 	        self.assert_pcs_success(
2252 	            "resource config".split(),
2253 	            dedent(
2254 	                """\
2255 	                Resource: dummy-clone (class=ocf provider=pcsmock type=minimal)
2256 	                  Operations:
2257 	                    monitor: dummy-clone-monitor-interval-10s
2258 	                      interval=10s timeout=20s
2259 	                Clone: dummy-clone-1
2260 	                  Resource: dummy (class=ocf provider=pcsmock type=minimal)
2261 	                    Operations:
2262 	                      monitor: dummy-monitor-interval-10s
2263 	                        interval=10s timeout=20s
2264 	                """
2265 	            ),
2266 	        )
2267 	
2268 	    def test_resource_promotable_id_promotable_command(self):
2269 	        self.assert_pcs_success(
2270 	            "resource create --no-default-ops dummy-clone ocf:pcsmock:stateful".split(),
2271 	        )
2272 	        self.assert_pcs_success(
2273 	            "resource create --no-default-ops dummy ocf:pcsmock:stateful".split(),
2274 	        )
2275 	        self.assert_pcs_success("resource promotable dummy".split())
2276 	        self.assert_pcs_success(
2277 	            "resource config".split(),
2278 	            dedent(
2279 	                """\
2280 	                Resource: dummy-clone (class=ocf provider=pcsmock type=stateful)
2281 	                  Operations:
2282 	                    monitor: dummy-clone-monitor-interval-10s
2283 	                      interval=10s timeout=20s role=Promoted
2284 	                    monitor: dummy-clone-monitor-interval-11s
2285 	                      interval=11s timeout=20s role=Unpromoted
2286 	                Clone: dummy-clone-1
2287 	                  Meta Attributes: dummy-clone-1-meta_attributes
2288 	                    promotable=true
2289 	                  Resource: dummy (class=ocf provider=pcsmock type=stateful)
2290 	                    Operations:
2291 	                      monitor: dummy-monitor-interval-10s
2292 	                        interval=10s timeout=20s role=Promoted
2293 	                      monitor: dummy-monitor-interval-11s
2294 	                        interval=11s timeout=20s role=Unpromoted
2295 	                """
2296 	            ),
2297 	        )
2298 	
2299 	    def test_resource_promotable_id_create_command(self):
2300 	        self.assert_pcs_success(
2301 	            "resource create --no-default-ops dummy-clone ocf:pcsmock:stateful".split(),
2302 	        )
2303 	        self.assert_pcs_success(
2304 	            "resource create --no-default-ops dummy ocf:pcsmock:stateful promotable".split(),
2305 	            stderr_full=fixture_meta_attributes_not_validated_warning(
2306 	                ["clone"]
2307 	            ),
2308 	        )
2309 	        self.assert_pcs_success(
2310 	            "resource config".split(),
2311 	            dedent(
2312 	                """\
2313 	                Resource: dummy-clone (class=ocf provider=pcsmock type=stateful)
2314 	                  Operations:
2315 	                    monitor: dummy-clone-monitor-interval-10s
2316 	                      interval=10s timeout=20s role=Promoted
2317 	                    monitor: dummy-clone-monitor-interval-11s
2318 	                      interval=11s timeout=20s role=Unpromoted
2319 	                Clone: dummy-clone-1
2320 	                  Meta Attributes: dummy-clone-1-meta_attributes
2321 	                    promotable=true
2322 	                  Resource: dummy (class=ocf provider=pcsmock type=stateful)
2323 	                    Operations:
2324 	                      monitor: dummy-monitor-interval-10s
2325 	                        interval=10s timeout=20s role=Promoted
2326 	                      monitor: dummy-monitor-interval-11s
2327 	                        interval=11s timeout=20s role=Unpromoted
2328 	                """
2329 	            ),
2330 	        )
2331 	
2332 	    def test_resource_clone_update(self):
2333 	        self.assert_pcs_success(
2334 	            "resource create --no-default-ops D1 ocf:pcsmock:minimal clone".split(),
2335 	        )
2336 	        self.assert_pcs_success(
2337 	            "resource config".split(),
2338 	            dedent(
2339 	                """\
2340 	                Clone: D1-clone
2341 	                  Resource: D1 (class=ocf provider=pcsmock type=minimal)
2342 	                    Operations:
2343 	                      monitor: D1-monitor-interval-10s
2344 	                        interval=10s timeout=20s
2345 	                """
2346 	            ),
2347 	        )
2348 	
2349 	        self.assert_pcs_success(
2350 	            "resource update D1-clone foo=bar".split(),
2351 	            stderr_full=fixture_meta_attributes_not_validated_warning(
2352 	                ["clone"]
2353 	            ),
2354 	        )
2355 	        self.assert_pcs_success(
2356 	            "resource config".split(),
2357 	            dedent(
2358 	                """\
2359 	                Clone: D1-clone
2360 	                  Meta Attributes: D1-clone-meta_attributes
2361 	                    foo=bar
2362 	                  Resource: D1 (class=ocf provider=pcsmock type=minimal)
2363 	                    Operations:
2364 	                      monitor: D1-monitor-interval-10s
2365 	                        interval=10s timeout=20s
2366 	                """
2367 	            ),
2368 	        )
2369 	
2370 	        self.assert_pcs_success(
2371 	            "resource update D1-clone bar=baz".split(),
2372 	            stderr_full=fixture_meta_attributes_not_validated_warning(
2373 	                ["clone"]
2374 	            ),
2375 	        )
2376 	        self.assert_pcs_success(
2377 	            "resource config".split(),
2378 	            dedent(
2379 	                """\
2380 	                Clone: D1-clone
2381 	                  Meta Attributes: D1-clone-meta_attributes
2382 	                    bar=baz
2383 	                    foo=bar
2384 	                  Resource: D1 (class=ocf provider=pcsmock type=minimal)
2385 	                    Operations:
2386 	                      monitor: D1-monitor-interval-10s
2387 	                        interval=10s timeout=20s
2388 	                """
2389 	            ),
2390 	        )
2391 	
2392 	        self.assert_pcs_success("resource update D1-clone foo=".split())
2393 	        self.assert_pcs_success(
2394 	            "resource config".split(),
2395 	            dedent(
2396 	                """\
2397 	                Clone: D1-clone
2398 	                  Meta Attributes: D1-clone-meta_attributes
2399 	                    bar=baz
2400 	                  Resource: D1 (class=ocf provider=pcsmock type=minimal)
2401 	                    Operations:
2402 	                      monitor: D1-monitor-interval-10s
2403 	                        interval=10s timeout=20s
2404 	                """
2405 	            ),
2406 	        )
2407 	
2408 	    def test_mastered_group(self):
2409 	        self.assert_pcs_success(
2410 	            "resource create --no-default-ops A ocf:pcsmock:minimal group AG".split(),
2411 	        )
2412 	        self.assert_pcs_success(
2413 	            "resource create --no-default-ops B ocf:pcsmock:minimal group AG".split(),
2414 	        )
2415 	        self.assert_pcs_success(
2416 	            "resource create --no-default-ops C ocf:pcsmock:minimal group AG".split(),
2417 	        )
2418 	        # pcs no longer allows turning resources into masters but supports
2419 	        # existing ones. In order to test it, we need to put a master in the
2420 	        # CIB without pcs.
2421 	        wrap_element_by_master(self.temp_cib, "AG", master_id="AGMaster")
2422 	
2423 	        self.assert_pcs_fail(
2424 	            "resource create --no-default-ops A ocf:pcsmock:minimal".split(),
2425 	            "Error: 'A' already exists\n",
2426 	        )
2427 	        self.assert_pcs_fail(
2428 	            "resource create --no-default-ops AG ocf:pcsmock:minimal".split(),
2429 	            "Error: 'AG' already exists\n",
2430 	        )
2431 	        self.assert_pcs_fail(
2432 	            "resource create --no-default-ops AGMaster ocf:pcsmock:minimal".split(),
2433 	            "Error: 'AGMaster' already exists\n",
2434 	        )
2435 	
2436 	        self.assert_pcs_fail(
2437 	            "resource ungroup AG".split(),
2438 	            "Error: Cannot remove all resources from a cloned group\n",
2439 	        )
2440 	
2441 	        self.assert_pcs_success(
2442 	            "resource delete B".split(),
2443 	            stderr_full=fixture_message_not_deleting_resources_not_live(["B"])
2444 	            + dedent(
2445 	                """\
2446 	                Removing references:
2447 	                  Resource 'B' from:
2448 	                    Group: 'AG'
2449 	                """
2450 	            ),
2451 	        )
2452 	        self.assert_pcs_success(
2453 	            "resource delete C".split(),
2454 	            stderr_full=fixture_message_not_deleting_resources_not_live(["C"])
2455 	            + dedent(
2456 	                """\
2457 	                Removing references:
2458 	                  Resource 'C' from:
2459 	                    Group: 'AG'
2460 	                """
2461 	            ),
2462 	        )
2463 	        self.assert_pcs_success("resource ungroup AG".split())
2464 	        self.assert_pcs_success(
2465 	            "resource config".split(),
2466 	            dedent(
2467 	                """\
2468 	                Clone: AGMaster
2469 	                  Meta Attributes:
2470 	                    promotable=true
2471 	                  Resource: A (class=ocf provider=pcsmock type=minimal)
2472 	                    Operations:
2473 	                      monitor: A-monitor-interval-10s
2474 	                        interval=10s timeout=20s
2475 	                """
2476 	            ),
2477 	        )
2478 	
2479 	    def test_cloned_group(self):
2480 	        self.assert_pcs_success(
2481 	            "resource create --no-default-ops D1 ocf:pcsmock:minimal group DG".split(),
2482 	        )
2483 	        self.assert_pcs_success(
2484 	            "resource create --no-default-ops D2 ocf:pcsmock:minimal group DG".split(),
2485 	        )
2486 	        self.assert_pcs_success("resource clone DG".split())
2487 	        self.assert_pcs_success(
2488 	            "resource config".split(),
2489 	            dedent(
2490 	                """\
2491 	                Clone: DG-clone
2492 	                  Group: DG
2493 	                    Resource: D1 (class=ocf provider=pcsmock type=minimal)
2494 	                      Operations:
2495 	                        monitor: D1-monitor-interval-10s
2496 	                          interval=10s timeout=20s
2497 	                    Resource: D2 (class=ocf provider=pcsmock type=minimal)
2498 	                      Operations:
2499 	                        monitor: D2-monitor-interval-10s
2500 	                          interval=10s timeout=20s
2501 	                """
2502 	            ),
2503 	        )
2504 	
2505 	        self.assert_pcs_fail(
2506 	            "resource create --no-default-ops D1 ocf:pcsmock:minimal".split(),
2507 	            "Error: 'D1' already exists\n",
2508 	        )
2509 	        self.assert_pcs_fail(
2510 	            "resource create --no-default-ops DG ocf:pcsmock:minimal".split(),
2511 	            "Error: 'DG' already exists\n",
2512 	        )
2513 	        self.assert_pcs_fail(
2514 	            "resource create --no-default-ops DG-clone ocf:pcsmock:minimal".split(),
2515 	            "Error: 'DG-clone' already exists\n",
2516 	        )
2517 	
2518 	    def test_op_option(self):
2519 	        self.assert_pcs_success(
2520 	            "resource create --no-default-ops B ocf:pcsmock:minimal".split(),
2521 	        )
2522 	
2523 	        self.assert_pcs_fail(
2524 	            "resource update B ocf:pcsmock:minimal op monitor interval=30s blah=blah".split(),
2525 	            (
2526 	                "Error: invalid resource operation option 'blah', allowed "
2527 	                "options are: 'OCF_CHECK_LEVEL', 'description', 'enabled', "
2528 	                "'id', 'interval', 'interval-origin', 'name', 'on-fail', "
2529 	                "'record-pending', 'role', 'start-delay', 'timeout'\n"
2530 	            ),
2531 	        )
2532 	
2533 	        self.assert_pcs_success(
2534 	            "resource create --no-default-ops C ocf:pcsmock:minimal".split(),
2535 	        )
2536 	
2537 	        self.assert_pcs_fail(
2538 	            "resource op add C monitor interval=30s blah=blah".split(),
2539 	            (
2540 	                "Error: invalid resource operation option 'blah', allowed "
2541 	                "options are: 'OCF_CHECK_LEVEL', 'description', 'enabled', "
2542 	                "'id', 'interval', 'interval-origin', 'name', 'on-fail', "
2543 	                "'record-pending', 'role', 'start-delay', 'timeout'\n"
2544 	            ),
2545 	        )
2546 	
2547 	        self.assert_pcs_fail(
2548 	            "resource op add C monitor interval=60 role=role".split(),
2549 	            "Error: 'role' is not a valid role value, use {}\n".format(
2550 	                format_list(const.PCMK_ROLES)
2551 	            ),
2552 	        )
2553 	
2554 	        self.assert_pcs_success(
2555 	            "resource config".split(),
2556 	            dedent(
2557 	                """\
2558 	                Resource: B (class=ocf provider=pcsmock type=minimal)
2559 	                  Operations:
2560 	                    monitor: B-monitor-interval-10s
2561 	                      interval=10s timeout=20s
2562 	                Resource: C (class=ocf provider=pcsmock type=minimal)
2563 	                  Operations:
2564 	                    monitor: C-monitor-interval-10s
2565 	                      interval=10s timeout=20s
2566 	                """
2567 	            ),
2568 	        )
2569 	
2570 	        self.assert_pcs_fail(
2571 	            "resource update B op monitor interval=30s monitor interval=31s role=Master".split(),
2572 	            "Error: 'Master' is not a valid role value, use {}\n".format(
2573 	                format_list(const.PCMK_ROLES)
2574 	            ),
2575 	        )
2576 	
2577 	        self.assert_pcs_success(
2578 	            "resource update B op monitor interval=30s monitor interval=31s role=promoted".split(),
2579 	        )
2580 	
2581 	        self.assert_pcs_success(
2582 	            "resource update B op monitor interval=30s monitor interval=31s role=Promoted".split(),
2583 	        )
2584 	
2585 	        self.assert_pcs_success(
2586 	            "resource config".split(),
2587 	            dedent(
2588 	                f"""\
2589 	                Resource: B (class=ocf provider=pcsmock type=minimal)
2590 	                  Operations:
2591 	                    monitor: B-monitor-interval-30s
2592 	                      interval=30s
2593 	                    monitor: B-monitor-interval-31s
2594 	                      interval=31s role={const.PCMK_ROLE_PROMOTED}
2595 	                Resource: C (class=ocf provider=pcsmock type=minimal)
2596 	                  Operations:
2597 	                    monitor: C-monitor-interval-10s
2598 	                      interval=10s timeout=20s
2599 	                """
2600 	            ),
2601 	        )
2602 	
2603 	        self.assert_pcs_fail(
2604 	            "resource update B op interval=5s".split(),
2605 	            "Error: interval=5s does not appear to be a valid operation action\n",
2606 	        )
2607 	
2608 	    def test_clone_bad_resources(self):
2609 	        self.setup_cluster_a()
2610 	        self.assert_pcs_fail(
2611 	            "resource clone ClusterIP4".split(),
2612 	            "Error: ClusterIP4 is already a clone resource\n",
2613 	        )
2614 	        self.assert_pcs_fail(
2615 	            "resource clone ClusterIP5".split(),
2616 	            "Error: ClusterIP5 is already a clone resource\n",
2617 	        )
2618 	        self.assert_pcs_fail(
2619 	            "resource promotable ClusterIP4".split(),
2620 	            "Error: ClusterIP4 is already a clone resource\n",
2621 	        )
2622 	        self.assert_pcs_fail(
2623 	            "resource promotable ClusterIP5".split(),
2624 	            "Error: ClusterIP5 is already a clone resource\n",
2625 	        )
2626 	
2627 	    def test_group_ms_and_clone(self):
2628 	        self.assert_pcs_fail(
2629 	            "resource create --no-default-ops D3 ocf:pcsmock:minimal promotable group xxx clone".split(),
2630 	            "Error: you can specify only one of clone, promotable, bundle or group\n",
2631 	        )
2632 	        self.assert_pcs_fail(
2633 	            "resource create --no-default-ops D4 ocf:pcsmock:minimal promotable group xxx".split(),
2634 	            "Error: you can specify only one of clone, promotable, bundle or group\n",
2635 	        )
2636 	
2637 	    def test_resource_missing_values(self):
2638 	        self.assert_pcs_success(
2639 	            "resource create --no-default-ops myip params --force".split(),
2640 	            stderr_full=(
2641 	                "Assumed agent name 'ocf:pcsmock:params' (deduced from 'params')\n"
2642 	                "Warning: required resource option 'mandatory' is missing\n"
2643 	            ),
2644 	        )
2645 	        self.assert_pcs_success(
2646 	            "resource create --no-default-ops myip2 params mandatory=value".split(),
2647 	            stderr_full=(
2648 	                "Assumed agent name 'ocf:pcsmock:params' (deduced from 'params')\n"
2649 	            ),
2650 	        )
2651 	        self.assert_pcs_success(
2652 	            "resource config".split(),
2653 	            dedent(
2654 	                """\
2655 	                Resource: myip (class=ocf provider=pcsmock type=params)
2656 	                  Operations:
2657 	                    monitor: myip-monitor-interval-10s
2658 	                      interval=10s timeout=20s
2659 	                Resource: myip2 (class=ocf provider=pcsmock type=params)
2660 	                  Attributes: myip2-instance_attributes
2661 	                    mandatory=value
2662 	                  Operations:
2663 	                    monitor: myip2-monitor-interval-10s
2664 	                      interval=10s timeout=20s
2665 	                """
2666 	            ),
2667 	        )
2668 	
2669 	    def test_cloned_mastered_group(self):
2670 	        self.assert_pcs_success(
2671 	            "resource create dummy1 ocf:pcsmock:minimal --no-default-ops group dummies".split(),
2672 	        )
2673 	        self.assert_pcs_success(
2674 	            "resource create dummy2 ocf:pcsmock:minimal --no-default-ops group dummies".split(),
2675 	        )
2676 	        self.assert_pcs_success(
2677 	            "resource create dummy3 ocf:pcsmock:minimal --no-default-ops group dummies".split(),
2678 	        )
2679 	        self.assert_pcs_success("resource clone dummies".split())
2680 	        self.assert_pcs_success(
2681 	            "resource config dummies-clone".split(),
2682 	            dedent(
2683 	                """\
2684 	                Clone: dummies-clone
2685 	                  Group: dummies
2686 	                    Resource: dummy1 (class=ocf provider=pcsmock type=minimal)
2687 	                      Operations:
2688 	                        monitor: dummy1-monitor-interval-10s
2689 	                          interval=10s timeout=20s
2690 	                    Resource: dummy2 (class=ocf provider=pcsmock type=minimal)
2691 	                      Operations:
2692 	                        monitor: dummy2-monitor-interval-10s
2693 	                          interval=10s timeout=20s
2694 	                    Resource: dummy3 (class=ocf provider=pcsmock type=minimal)
2695 	                      Operations:
2696 	                        monitor: dummy3-monitor-interval-10s
2697 	                          interval=10s timeout=20s
2698 	                """
2699 	            ),
2700 	        )
2701 	
2702 	        self.assert_pcs_success("resource unclone dummies-clone".split())
2703 	        stdout, stderr, returncode = self.pcs_runner.run(
2704 	            "resource status".split()
2705 	        )
2706 	        self.assertEqual(stderr, "")
2707 	        self.assertEqual(returncode, 0)
2708 	        if is_pacemaker_21_without_20_compatibility():
2709 	            self.assertEqual(
2710 	                stdout,
2711 	                outdent(
2712 	                    """\
2713 	                      * Resource Group: dummies:
2714 	                        * dummy1\t(ocf:pcsmock:minimal):\t Stopped
2715 	                        * dummy2\t(ocf:pcsmock:minimal):\t Stopped
2716 	                        * dummy3\t(ocf:pcsmock:minimal):\t Stopped
2717 	                    """
2718 	                ),
2719 	            )
2720 	        elif PCMK_2_0_3_PLUS:
2721 	            assert_pcs_status(
2722 	                stdout,
2723 	                outdent(
2724 	                    """\
2725 	                  * Resource Group: dummies:
2726 	                    * dummy1\t(ocf::pcsmock:minimal):\tStopped
2727 	                    * dummy2\t(ocf::pcsmock:minimal):\tStopped
2728 	                    * dummy3\t(ocf::pcsmock:minimal):\tStopped
2729 	                """
2730 	                ),
2731 	            )
2732 	        else:
2733 	            self.assertEqual(
2734 	                stdout,
2735 	                outdent(
2736 	                    """\
2737 	                 Resource Group: dummies
2738 	                     dummy1\t(ocf::pcsmock:minimal):\tStopped
2739 	                     dummy2\t(ocf::pcsmock:minimal):\tStopped
2740 	                     dummy3\t(ocf::pcsmock:minimal):\tStopped
2741 	                """
2742 	                ),
2743 	            )
2744 	
2745 	        self.assert_pcs_success("resource clone dummies".split())
2746 	        self.assert_pcs_success(
2747 	            "resource config dummies-clone".split(),
2748 	            dedent(
2749 	                """\
2750 	                Clone: dummies-clone
2751 	                  Group: dummies
2752 	                    Resource: dummy1 (class=ocf provider=pcsmock type=minimal)
2753 	                      Operations:
2754 	                        monitor: dummy1-monitor-interval-10s
2755 	                          interval=10s timeout=20s
2756 	                    Resource: dummy2 (class=ocf provider=pcsmock type=minimal)
2757 	                      Operations:
2758 	                        monitor: dummy2-monitor-interval-10s
2759 	                          interval=10s timeout=20s
2760 	                    Resource: dummy3 (class=ocf provider=pcsmock type=minimal)
2761 	                      Operations:
2762 	                        monitor: dummy3-monitor-interval-10s
2763 	                          interval=10s timeout=20s
2764 	                """
2765 	            ),
2766 	        )
2767 	
2768 	        self.assert_pcs_success(
2769 	            "resource delete dummies-clone".split(),
2770 	            stderr_full=fixture_message_not_deleting_resources_not_live(
2771 	                ["dummy1", "dummy2", "dummy3", "dummies", "dummies-clone"]
2772 	            )
2773 	            + dedent(
2774 	                """\
2775 	                Removing dependant elements:
2776 	                  Group: 'dummies'
2777 	                  Resources: 'dummy1', 'dummy2', 'dummy3'
2778 	                """
2779 	            ),
2780 	        )
2781 	        self.assert_pcs_success(
2782 	            "resource status".split(), "NO resources configured\n"
2783 	        )
2784 	
2785 	        self.assert_pcs_success(
2786 	            "resource create dummy1 ocf:pcsmock:minimal --no-default-ops group dummies".split(),
2787 	        )
2788 	        self.assert_pcs_success(
2789 	            "resource create dummy2 ocf:pcsmock:minimal --no-default-ops group dummies".split(),
2790 	        )
2791 	        self.assert_pcs_success(
2792 	            "resource create dummy3 ocf:pcsmock:minimal --no-default-ops group dummies".split(),
2793 	        )
2794 	        # pcs no longer allows turning resources into masters but supports
2795 	        # existing ones. In order to test it, we need to put a master in the
2796 	        # CIB without pcs.
2797 	        wrap_element_by_master(self.temp_cib, "dummies")
2798 	
2799 	        self.assert_pcs_success(
2800 	            "resource config dummies-master".split(),
2801 	            dedent(
2802 	                """\
2803 	                Clone: dummies-master
2804 	                  Meta Attributes:
2805 	                    promotable=true
2806 	                  Group: dummies
2807 	                    Resource: dummy1 (class=ocf provider=pcsmock type=minimal)
2808 	                      Operations:
2809 	                        monitor: dummy1-monitor-interval-10s
2810 	                          interval=10s timeout=20s
2811 	                    Resource: dummy2 (class=ocf provider=pcsmock type=minimal)
2812 	                      Operations:
2813 	                        monitor: dummy2-monitor-interval-10s
2814 	                          interval=10s timeout=20s
2815 	                    Resource: dummy3 (class=ocf provider=pcsmock type=minimal)
2816 	                      Operations:
2817 	                        monitor: dummy3-monitor-interval-10s
2818 	                          interval=10s timeout=20s
2819 	                """
2820 	            ),
2821 	        )
2822 	
2823 	        self.assert_pcs_success("resource unclone dummies-master".split())
2824 	        stdout, stderr, returncode = self.pcs_runner.run(
2825 	            "resource status".split()
2826 	        )
2827 	        self.assertEqual(stderr, "")
2828 	        self.assertEqual(returncode, 0)
2829 	        if is_pacemaker_21_without_20_compatibility():
2830 	            self.assertEqual(
2831 	                stdout,
2832 	                outdent(
2833 	                    """\
2834 	                      * Resource Group: dummies:
2835 	                        * dummy1\t(ocf:pcsmock:minimal):\t Stopped
2836 	                        * dummy2\t(ocf:pcsmock:minimal):\t Stopped
2837 	                        * dummy3\t(ocf:pcsmock:minimal):\t Stopped
2838 	                    """
2839 	                ),
2840 	            )
2841 	        elif PCMK_2_0_3_PLUS:
2842 	            assert_pcs_status(
2843 	                stdout,
2844 	                outdent(
2845 	                    """\
2846 	                  * Resource Group: dummies:
2847 	                    * dummy1\t(ocf::pcsmock:minimal):\tStopped
2848 	                    * dummy2\t(ocf::pcsmock:minimal):\tStopped
2849 	                    * dummy3\t(ocf::pcsmock:minimal):\tStopped
2850 	                """
2851 	                ),
2852 	            )
2853 	        else:
2854 	            self.assertEqual(
2855 	                stdout,
2856 	                outdent(
2857 	                    """\
2858 	                 Resource Group: dummies
2859 	                     dummy1\t(ocf::pcsmock:minimal):\tStopped
2860 	                     dummy2\t(ocf::pcsmock:minimal):\tStopped
2861 	                     dummy3\t(ocf::pcsmock:minimal):\tStopped
2862 	                """
2863 	                ),
2864 	            )
2865 	
2866 	        # pcs no longer allows turning resources into masters but supports
2867 	        # existing ones. In order to test it, we need to put a master in the
2868 	        # CIB without pcs.
2869 	        wrap_element_by_master(self.temp_cib, "dummies")
2870 	        self.assert_pcs_success(
2871 	            "resource config dummies-master".split(),
2872 	            dedent(
2873 	                """\
2874 	                Clone: dummies-master
2875 	                  Meta Attributes:
2876 	                    promotable=true
2877 	                  Group: dummies
2878 	                    Resource: dummy1 (class=ocf provider=pcsmock type=minimal)
2879 	                      Operations:
2880 	                        monitor: dummy1-monitor-interval-10s
2881 	                          interval=10s timeout=20s
2882 	                    Resource: dummy2 (class=ocf provider=pcsmock type=minimal)
2883 	                      Operations:
2884 	                        monitor: dummy2-monitor-interval-10s
2885 	                          interval=10s timeout=20s
2886 	                    Resource: dummy3 (class=ocf provider=pcsmock type=minimal)
2887 	                      Operations:
2888 	                        monitor: dummy3-monitor-interval-10s
2889 	                          interval=10s timeout=20s
2890 	                """
2891 	            ),
2892 	        )
2893 	
2894 	        self.assert_pcs_success(
2895 	            "resource delete dummies-master".split(),
2896 	            stderr_full=fixture_message_not_deleting_resources_not_live(
2897 	                ["dummy1", "dummy2", "dummy3", "dummies", "dummies-master"]
2898 	            )
2899 	            + dedent(
2900 	                """\
2901 	                Removing dependant elements:
2902 	                  Group: 'dummies'
2903 	                  Resources: 'dummy1', 'dummy2', 'dummy3'
2904 	                """
2905 	            ),
2906 	        )
2907 	        self.assert_pcs_success(
2908 	            "resource status".split(), "NO resources configured\n"
2909 	        )
2910 	
2911 	    def test_relocate_stickiness(self):
2912 	        self.assert_pcs_success(
2913 	            "resource create D1 ocf:pcsmock:minimal --no-default-ops".split()
2914 	        )
2915 	        self.assert_pcs_success(
2916 	            "resource create DG1 ocf:pcsmock:minimal --no-default-ops group GR".split(),
2917 	        )
2918 	        self.assert_pcs_success(
2919 	            "resource create DG2 ocf:pcsmock:minimal --no-default-ops group GR".split(),
2920 	        )
2921 	        self.assert_pcs_success(
2922 	            "resource create DC ocf:pcsmock:minimal --no-default-ops clone".split()
2923 	        )
2924 	        self.assert_pcs_success(
2925 	            "resource create DGC1 ocf:pcsmock:minimal --no-default-ops group GRC".split(),
2926 	        )
2927 	        self.assert_pcs_success(
2928 	            "resource create DGC2 ocf:pcsmock:minimal --no-default-ops group GRC".split(),
2929 	        )
2930 	        self.assert_pcs_success("resource clone GRC".split())
2931 	
2932 	        status = dedent(
2933 	            """\
2934 	            Resource: D1 (class=ocf provider=pcsmock type=minimal)
2935 	              Operations:
2936 	                monitor: D1-monitor-interval-10s
2937 	                  interval=10s timeout=20s
2938 	            Group: GR
2939 	              Resource: DG1 (class=ocf provider=pcsmock type=minimal)
2940 	                Operations:
2941 	                  monitor: DG1-monitor-interval-10s
2942 	                    interval=10s timeout=20s
2943 	              Resource: DG2 (class=ocf provider=pcsmock type=minimal)
2944 	                Operations:
2945 	                  monitor: DG2-monitor-interval-10s
2946 	                    interval=10s timeout=20s
2947 	            Clone: DC-clone
2948 	              Resource: DC (class=ocf provider=pcsmock type=minimal)
2949 	                Operations:
2950 	                  monitor: DC-monitor-interval-10s
2951 	                    interval=10s timeout=20s
2952 	            Clone: GRC-clone
2953 	              Group: GRC
2954 	                Resource: DGC1 (class=ocf provider=pcsmock type=minimal)
2955 	                  Operations:
2956 	                    monitor: DGC1-monitor-interval-10s
2957 	                      interval=10s timeout=20s
2958 	                Resource: DGC2 (class=ocf provider=pcsmock type=minimal)
2959 	                  Operations:
2960 	                    monitor: DGC2-monitor-interval-10s
2961 	                      interval=10s timeout=20s
2962 	            """
2963 	        )
2964 	
2965 	        cib_original, stderr, returncode = self.pcs_runner.run(
2966 	            "cluster cib".split()
2967 	        )
2968 	        self.assertEqual(stderr, "")
2969 	        self.assertEqual(returncode, 0)
2970 	
2971 	        resources = {
2972 	            "D1",
2973 	            "DG1",
2974 	            "DG2",
2975 	            "GR",
2976 	            "DC",
2977 	            "DC-clone",
2978 	            "DGC1",
2979 	            "DGC2",
2980 	            "GRC",
2981 	            "GRC-clone",
2982 	        }
2983 	        self.assert_pcs_success("resource config".split(), status)
2984 	        cib_in = utils.parseString(cib_original)
2985 	        cib_out, updated_resources = resource.resource_relocate_set_stickiness(
2986 	            cib_in
2987 	        )
2988 	        self.assertFalse(cib_in is cib_out)
2989 	        self.assertEqual(resources, updated_resources)
2990 	        self.assert_pcs_success("resource config".split(), status)
2991 	        write_data_to_tmpfile(cib_out.toxml(), self.temp_cib)
2992 	
2993 	        self.assert_pcs_success(
2994 	            "resource config".split(),
2995 	            dedent(
2996 	                """\
2997 	                Resource: D1 (class=ocf provider=pcsmock type=minimal)
2998 	                  Meta Attributes: D1-meta_attributes
2999 	                    resource-stickiness=0
3000 	                  Operations:
3001 	                    monitor: D1-monitor-interval-10s
3002 	                      interval=10s timeout=20s
3003 	                Group: GR
3004 	                  Meta Attributes: GR-meta_attributes
3005 	                    resource-stickiness=0
3006 	                  Resource: DG1 (class=ocf provider=pcsmock type=minimal)
3007 	                    Meta Attributes: DG1-meta_attributes
3008 	                      resource-stickiness=0
3009 	                    Operations:
3010 	                      monitor: DG1-monitor-interval-10s
3011 	                        interval=10s timeout=20s
3012 	                  Resource: DG2 (class=ocf provider=pcsmock type=minimal)
3013 	                    Meta Attributes: DG2-meta_attributes
3014 	                      resource-stickiness=0
3015 	                    Operations:
3016 	                      monitor: DG2-monitor-interval-10s
3017 	                        interval=10s timeout=20s
3018 	                Clone: DC-clone
3019 	                  Meta Attributes: DC-clone-meta_attributes
3020 	                    resource-stickiness=0
3021 	                  Resource: DC (class=ocf provider=pcsmock type=minimal)
3022 	                    Meta Attributes: DC-meta_attributes
3023 	                      resource-stickiness=0
3024 	                    Operations:
3025 	                      monitor: DC-monitor-interval-10s
3026 	                        interval=10s timeout=20s
3027 	                Clone: GRC-clone
3028 	                  Meta Attributes: GRC-clone-meta_attributes
3029 	                    resource-stickiness=0
3030 	                  Group: GRC
3031 	                    Meta Attributes: GRC-meta_attributes
3032 	                      resource-stickiness=0
3033 	                    Resource: DGC1 (class=ocf provider=pcsmock type=minimal)
3034 	                      Meta Attributes: DGC1-meta_attributes
3035 	                        resource-stickiness=0
3036 	                      Operations:
3037 	                        monitor: DGC1-monitor-interval-10s
3038 	                          interval=10s timeout=20s
3039 	                    Resource: DGC2 (class=ocf provider=pcsmock type=minimal)
3040 	                      Meta Attributes: DGC2-meta_attributes
3041 	                        resource-stickiness=0
3042 	                      Operations:
3043 	                        monitor: DGC2-monitor-interval-10s
3044 	                          interval=10s timeout=20s
3045 	                """
3046 	            ),
3047 	        )
3048 	
3049 	        resources = {"D1", "DG1", "DC", "DGC1"}
3050 	        write_data_to_tmpfile(cib_original, self.temp_cib)
3051 	        self.assert_pcs_success("resource config".split(), status)
3052 	        cib_in = utils.parseString(cib_original)
3053 	        cib_out, updated_resources = resource.resource_relocate_set_stickiness(
3054 	            cib_in, resources
3055 	        )
3056 	        self.assertFalse(cib_in is cib_out)
3057 	        self.assertEqual(resources, updated_resources)
3058 	        self.assert_pcs_success("resource config".split(), status)
3059 	        write_data_to_tmpfile(cib_out.toxml(), self.temp_cib)
3060 	        self.assert_pcs_success(
3061 	            "resource config".split(),
3062 	            dedent(
3063 	                """\
3064 	                Resource: D1 (class=ocf provider=pcsmock type=minimal)
3065 	                  Meta Attributes: D1-meta_attributes
3066 	                    resource-stickiness=0
3067 	                  Operations:
3068 	                    monitor: D1-monitor-interval-10s
3069 	                      interval=10s timeout=20s
3070 	                Group: GR
3071 	                  Resource: DG1 (class=ocf provider=pcsmock type=minimal)
3072 	                    Meta Attributes: DG1-meta_attributes
3073 	                      resource-stickiness=0
3074 	                    Operations:
3075 	                      monitor: DG1-monitor-interval-10s
3076 	                        interval=10s timeout=20s
3077 	                  Resource: DG2 (class=ocf provider=pcsmock type=minimal)
3078 	                    Operations:
3079 	                      monitor: DG2-monitor-interval-10s
3080 	                        interval=10s timeout=20s
3081 	                Clone: DC-clone
3082 	                  Resource: DC (class=ocf provider=pcsmock type=minimal)
3083 	                    Meta Attributes: DC-meta_attributes
3084 	                      resource-stickiness=0
3085 	                    Operations:
3086 	                      monitor: DC-monitor-interval-10s
3087 	                        interval=10s timeout=20s
3088 	                Clone: GRC-clone
3089 	                  Group: GRC
3090 	                    Resource: DGC1 (class=ocf provider=pcsmock type=minimal)
3091 	                      Meta Attributes: DGC1-meta_attributes
3092 	                        resource-stickiness=0
3093 	                      Operations:
3094 	                        monitor: DGC1-monitor-interval-10s
3095 	                          interval=10s timeout=20s
3096 	                    Resource: DGC2 (class=ocf provider=pcsmock type=minimal)
3097 	                      Operations:
3098 	                        monitor: DGC2-monitor-interval-10s
3099 	                          interval=10s timeout=20s
3100 	                """
3101 	            ),
3102 	        )
3103 	
3104 	        resources = {"GRC-clone", "GRC", "DGC1", "DGC2"}
3105 	        write_data_to_tmpfile(cib_original, self.temp_cib)
3106 	        self.assert_pcs_success("resource config".split(), status)
3107 	        cib_in = utils.parseString(cib_original)
3108 	        cib_out, updated_resources = resource.resource_relocate_set_stickiness(
3109 	            cib_in, ["GRC-clone"]
3110 	        )
3111 	        self.assertFalse(cib_in is cib_out)
3112 	        self.assertEqual(resources, updated_resources)
3113 	        self.assert_pcs_success("resource config".split(), status)
3114 	        write_data_to_tmpfile(cib_out.toxml(), self.temp_cib)
3115 	        self.assert_pcs_success(
3116 	            "resource config".split(),
3117 	            dedent(
3118 	                """\
3119 	                Resource: D1 (class=ocf provider=pcsmock type=minimal)
3120 	                  Operations:
3121 	                    monitor: D1-monitor-interval-10s
3122 	                      interval=10s timeout=20s
3123 	                Group: GR
3124 	                  Resource: DG1 (class=ocf provider=pcsmock type=minimal)
3125 	                    Operations:
3126 	                      monitor: DG1-monitor-interval-10s
3127 	                        interval=10s timeout=20s
3128 	                  Resource: DG2 (class=ocf provider=pcsmock type=minimal)
3129 	                    Operations:
3130 	                      monitor: DG2-monitor-interval-10s
3131 	                        interval=10s timeout=20s
3132 	                Clone: DC-clone
3133 	                  Resource: DC (class=ocf provider=pcsmock type=minimal)
3134 	                    Operations:
3135 	                      monitor: DC-monitor-interval-10s
3136 	                        interval=10s timeout=20s
3137 	                Clone: GRC-clone
3138 	                  Meta Attributes: GRC-clone-meta_attributes
3139 	                    resource-stickiness=0
3140 	                  Group: GRC
3141 	                    Meta Attributes: GRC-meta_attributes
3142 	                      resource-stickiness=0
3143 	                    Resource: DGC1 (class=ocf provider=pcsmock type=minimal)
3144 	                      Meta Attributes: DGC1-meta_attributes
3145 	                        resource-stickiness=0
3146 	                      Operations:
3147 	                        monitor: DGC1-monitor-interval-10s
3148 	                          interval=10s timeout=20s
3149 	                    Resource: DGC2 (class=ocf provider=pcsmock type=minimal)
3150 	                      Meta Attributes: DGC2-meta_attributes
3151 	                        resource-stickiness=0
3152 	                      Operations:
3153 	                        monitor: DGC2-monitor-interval-10s
3154 	                          interval=10s timeout=20s
3155 	                """
3156 	            ),
3157 	        )
3158 	
3159 	        resources = {"GR", "DG1", "DG2", "DC-clone", "DC"}
3160 	        write_data_to_tmpfile(cib_original, self.temp_cib)
3161 	        self.assert_pcs_success("resource config".split(), status)
3162 	        cib_in = utils.parseString(cib_original)
3163 	        cib_out, updated_resources = resource.resource_relocate_set_stickiness(
3164 	            cib_in, ["GR", "DC-clone"]
3165 	        )
3166 	        self.assertFalse(cib_in is cib_out)
3167 	        self.assertEqual(resources, updated_resources)
3168 	        self.assert_pcs_success("resource config".split(), status)
3169 	        write_data_to_tmpfile(cib_out.toxml(), self.temp_cib)
3170 	        self.assert_pcs_success(
3171 	            "resource config".split(),
3172 	            dedent(
3173 	                """\
3174 	                Resource: D1 (class=ocf provider=pcsmock type=minimal)
3175 	                  Operations:
3176 	                    monitor: D1-monitor-interval-10s
3177 	                      interval=10s timeout=20s
3178 	                Group: GR
3179 	                  Meta Attributes: GR-meta_attributes
3180 	                    resource-stickiness=0
3181 	                  Resource: DG1 (class=ocf provider=pcsmock type=minimal)
3182 	                    Meta Attributes: DG1-meta_attributes
3183 	                      resource-stickiness=0
3184 	                    Operations:
3185 	                      monitor: DG1-monitor-interval-10s
3186 	                        interval=10s timeout=20s
3187 	                  Resource: DG2 (class=ocf provider=pcsmock type=minimal)
3188 	                    Meta Attributes: DG2-meta_attributes
3189 	                      resource-stickiness=0
3190 	                    Operations:
3191 	                      monitor: DG2-monitor-interval-10s
3192 	                        interval=10s timeout=20s
3193 	                Clone: DC-clone
3194 	                  Meta Attributes: DC-clone-meta_attributes
3195 	                    resource-stickiness=0
3196 	                  Resource: DC (class=ocf provider=pcsmock type=minimal)
3197 	                    Meta Attributes: DC-meta_attributes
3198 	                      resource-stickiness=0
3199 	                    Operations:
3200 	                      monitor: DC-monitor-interval-10s
3201 	                        interval=10s timeout=20s
3202 	                Clone: GRC-clone
3203 	                  Group: GRC
3204 	                    Resource: DGC1 (class=ocf provider=pcsmock type=minimal)
3205 	                      Operations:
3206 	                        monitor: DGC1-monitor-interval-10s
3207 	                          interval=10s timeout=20s
3208 	                    Resource: DGC2 (class=ocf provider=pcsmock type=minimal)
3209 	                      Operations:
3210 	                        monitor: DGC2-monitor-interval-10s
3211 	                          interval=10s timeout=20s
3212 	                """
3213 	            ),
3214 	        )
3215 	
3216 	
3217 	class OperationDeleteRemoveMixin(
3218 	    get_assert_pcs_effect_mixin(
3219 	        lambda cib: etree.tostring(etree.parse(cib).findall(".//resources")[0])
3220 	    )
3221 	):
3222 	    # see also BundleMiscCommands
3223 	
3224 	    def setUp(self):
3225 	        self.empty_cib = empty_cib
3226 	        self.temp_cib = get_tmp_file("tier1_resource_operation_delete")
3227 	        self.temp_large_cib = get_tmp_file(
3228 	            "tier1_resource_large_operation_delete"
3229 	        )
3230 	        write_file_to_tmpfile(empty_cib, self.temp_cib)
3231 	        write_file_to_tmpfile(large_cib, self.temp_large_cib)
3232 	        self.pcs_runner = PcsRunner(self.temp_cib.name)
3233 	        self.pcs_runner.mock_settings = get_mock_settings()
3234 	        self.command = "to-be-overridden"
3235 	
3236 	    def tearDown(self):
3237 	        self.temp_cib.close()
3238 	        self.temp_large_cib.close()
3239 	
3240 	    fixture_xml_1_monitor = """
3241 	        <resources>
3242 	            <primitive class="ocf" id="R" provider="pcsmock" type="minimal">
3243 	                <operations>
3244 	                    <op id="R-monitor-interval-10s" interval="10s"
3245 	                        name="monitor" timeout="20s"
3246 	                    />
3247 	                </operations>
3248 	            </primitive>
3249 	        </resources>
3250 	    """
3251 	
3252 	    fixture_xml_empty_operations = """
3253 	        <resources>
3254 	            <primitive class="ocf" id="R" provider="pcsmock" type="minimal">
3255 	                <operations>
3256 	                </operations>
3257 	            </primitive>
3258 	        </resources>
3259 	    """
3260 	
3261 	    def fixture_resource(self):
3262 	        self.assert_effect(
3263 	            "resource create --no-default-ops R ocf:pcsmock:minimal".split(),
3264 	            self.fixture_xml_1_monitor,
3265 	        )
3266 	
3267 	    def fixture_monitor_20(self):
3268 	        self.assert_effect(
3269 	            "resource op add R monitor interval=20s timeout=20s --force".split(),
3270 	            """
3271 	                <resources>
3272 	                    <primitive class="ocf" id="R" provider="pcsmock"
3273 	                        type="minimal"
3274 	                    >
3275 	                        <operations>
3276 	                            <op id="R-monitor-interval-10s" interval="10s"
3277 	                                name="monitor" timeout="20s"
3278 	                            />
3279 	                            <op id="R-monitor-interval-20s" interval="20s"
3280 	                                name="monitor" timeout="20s"
3281 	                            />
3282 	                        </operations>
3283 	                    </primitive>
3284 	                </resources>
3285 	            """,
3286 	        )
3287 	
3288 	    def fixture_start(self):
3289 	        self.assert_effect(
3290 	            "resource op add R start timeout=20s".split(),
3291 	            """
3292 	                <resources>
3293 	                    <primitive class="ocf" id="R" provider="pcsmock"
3294 	                        type="minimal"
3295 	                    >
3296 	                        <operations>
3297 	                            <op id="R-monitor-interval-10s" interval="10s"
3298 	                                name="monitor" timeout="20s"
3299 	                            />
3300 	                            <op id="R-monitor-interval-20s" interval="20s"
3301 	                                name="monitor" timeout="20s"
3302 	                            />
3303 	                            <op id="R-start-interval-0s" interval="0s"
3304 	                                name="start" timeout="20s"
3305 	                            />
3306 	                        </operations>
3307 	                    </primitive>
3308 	                </resources>
3309 	            """,
3310 	        )
3311 	
3312 	    def test_remove_missing_op(self):
3313 	        assert self.command in {"delete", "remove"}
3314 	        self.fixture_resource()
3315 	        self.assert_pcs_fail(
3316 	            f"resource op {self.command} R-monitor-interval-30s".split(),
3317 	            "Error: unable to find operation id: R-monitor-interval-30s\n",
3318 	        )
3319 	
3320 	    def test_keep_empty_operations(self):
3321 	        assert self.command in {"delete", "remove"}
3322 	        self.fixture_resource()
3323 	        self.assert_effect(
3324 	            f"resource op {self.command} R-monitor-interval-10s".split(),
3325 	            self.fixture_xml_empty_operations,
3326 	        )
3327 	
3328 	    def test_remove_by_id_success(self):
3329 	        assert self.command in {"delete", "remove"}
3330 	        self.fixture_resource()
3331 	        self.fixture_monitor_20()
3332 	        self.assert_effect(
3333 	            f"resource op {self.command} R-monitor-interval-20s".split(),
3334 	            self.fixture_xml_1_monitor,
3335 	        )
3336 	
3337 	    def test_remove_all_monitors(self):
3338 	        assert self.command in {"delete", "remove"}
3339 	        self.fixture_resource()
3340 	        self.fixture_monitor_20()
3341 	        self.fixture_start()
3342 	        self.assert_effect(
3343 	            f"resource op {self.command} R monitor".split(),
3344 	            """
3345 	                <resources>
3346 	                    <primitive class="ocf" id="R" provider="pcsmock"
3347 	                        type="minimal"
3348 	                    >
3349 	                        <operations>
3350 	                            <op id="R-start-interval-0s" interval="0s"
3351 	                                name="start" timeout="20s"
3352 	                            />
3353 	                        </operations>
3354 	                    </primitive>
3355 	                </resources>
3356 	            """,
3357 	        )
3358 	
3359 	
3360 	class OperationDelete(OperationDeleteRemoveMixin, TestCase):
3361 	    def setUp(self):
3362 	        super().setUp()
3363 	        self.command = "delete"
3364 	
3365 	    def test_usage(self):
3366 	        self.assert_pcs_fail(
3367 	            "resource op delete".split(),
3368 	            stderr_start="\nUsage: pcs resource op delete...",
3369 	        )
3370 	
3371 	
3372 	class OperationRemove(OperationDeleteRemoveMixin, TestCase):
3373 	    def setUp(self):
3374 	        super().setUp()
3375 	        self.command = "remove"
3376 	
3377 	    def test_usage(self):
3378 	        self.assert_pcs_fail(
3379 	            "resource op remove".split(),
3380 	            stderr_start="\nUsage: pcs resource op remove...",
3381 	        )
3382 	
3383 	
3384 	class Utilization(
3385 	    TestCase,
3386 	    get_assert_pcs_effect_mixin(
3387 	        lambda cib: etree.tostring(etree.parse(cib).findall(".//resources")[0])
3388 	    ),
3389 	):
3390 	    def setUp(self):
3391 	        self.empty_cib = empty_cib
3392 	        self.temp_cib = get_tmp_file("tier1_resource_utilization")
3393 	        self.temp_large_cib = get_tmp_file("tier1_resource_utilization_large")
3394 	        write_file_to_tmpfile(empty_cib, self.temp_cib)
3395 	        write_file_to_tmpfile(large_cib, self.temp_large_cib)
3396 	        self.pcs_runner = PcsRunner(self.temp_cib.name)
3397 	        self.pcs_runner.mock_settings = get_mock_settings()
3398 	
3399 	    def tearDown(self):
3400 	        self.temp_cib.close()
3401 	        self.temp_large_cib.close()
3402 	
3403 	    @staticmethod
3404 	    def fixture_xml_resource_no_utilization():
3405 	        return """
3406 	            <resources>
3407 	                <primitive class="ocf" id="R" provider="pcsmock" type="minimal">
3408 	                    <operations>
3409 	                        <op id="R-monitor-interval-10s" interval="10s"
3410 	                            name="monitor" timeout="20s"
3411 	                        />
3412 	                    </operations>
3413 	                </primitive>
3414 	            </resources>
3415 	            """
3416 	
3417 	    @staticmethod
3418 	    def fixture_xml_resource_empty_utilization():
3419 	        return """
3420 	            <resources>
3421 	                <primitive class="ocf" id="R" provider="pcsmock" type="minimal">
3422 	                    <operations>
3423 	                        <op id="R-monitor-interval-10s" interval="10s"
3424 	                            name="monitor" timeout="20s"
3425 	                        />
3426 	                    </operations>
3427 	                    <utilization id="R-utilization" />
3428 	                </primitive>
3429 	            </resources>
3430 	            """
3431 	
3432 	    @staticmethod
3433 	    def fixture_xml_resource_with_utilization():
3434 	        return """
3435 	            <resources>
3436 	                <primitive class="ocf" id="R" provider="pcsmock" type="minimal">
3437 	                    <operations>
3438 	                        <op id="R-monitor-interval-10s" interval="10s"
3439 	                            name="monitor" timeout="20s"
3440 	                        />
3441 	                    </operations>
3442 	                    <utilization id="R-utilization">
3443 	                        <nvpair id="R-utilization-test" name="test"
3444 	                            value="100"
3445 	                        />
3446 	                    </utilization>
3447 	                </primitive>
3448 	            </resources>
3449 	            """
3450 	
3451 	    def fixture_resource(self):
3452 	        self.assert_effect(
3453 	            "resource create --no-default-ops R ocf:pcsmock:minimal".split(),
3454 	            self.fixture_xml_resource_no_utilization(),
3455 	        )
3456 	
3457 	    def fixture_resource_utilization(self):
3458 	        self.fixture_resource()
3459 	        self.assert_effect(
3460 	            "resource utilization R test=100".split(),
3461 	            self.fixture_xml_resource_with_utilization(),
3462 	            stderr_full=FIXTURE_UTILIZATION_WARNING,
3463 	        )
3464 	
3465 	    def test_resource_utilization_set(self):
3466 	        # see also BundleMiscCommands
3467 	        self.pcs_runner = PcsRunner(self.temp_large_cib.name)
3468 	        self.pcs_runner.mock_settings = get_mock_settings()
3469 	
3470 	        self.assert_pcs_success(
3471 	            "resource utilization dummy test1=10".split(),
3472 	            stderr_full=FIXTURE_UTILIZATION_WARNING,
3473 	        )
3474 	        self.assert_pcs_success(
3475 	            "resource utilization dummy1".split(),
3476 	            dedent(
3477 	                """\
3478 	                Resource Utilization:
3479 	                 dummy1: 
3480 	                """
3481 	            ),
3482 	            stderr_full=FIXTURE_UTILIZATION_WARNING,
3483 	        )
3484 	        self.assert_pcs_success(
3485 	            "resource utilization dummy".split(),
3486 	            dedent(
3487 	                """\
3488 	                Resource Utilization:
3489 	                 dummy: test1=10
3490 	                """
3491 	            ),
3492 	            stderr_full=FIXTURE_UTILIZATION_WARNING,
3493 	        )
3494 	        self.assert_pcs_success(
3495 	            "resource utilization dummy test1=-10 test4=1234".split(),
3496 	            stderr_full=FIXTURE_UTILIZATION_WARNING,
3497 	        )
3498 	        self.assert_pcs_success(
3499 	            "resource utilization dummy".split(),
3500 	            dedent(
3501 	                """\
3502 	                Resource Utilization:
3503 	                 dummy: test1=-10 test4=1234
3504 	                """
3505 	            ),
3506 	            stderr_full=FIXTURE_UTILIZATION_WARNING,
3507 	        )
3508 	        self.assert_pcs_success(
3509 	            "resource utilization dummy1 test2=321 empty=".split(),
3510 	            stderr_full=FIXTURE_UTILIZATION_WARNING,
3511 	        )
3512 	        self.assert_pcs_success(
3513 	            "resource utilization dummy1".split(),
3514 	            dedent(
3515 	                """\
3516 	                Resource Utilization:
3517 	                 dummy1: test2=321
3518 	                """
3519 	            ),
3520 	            stderr_full=FIXTURE_UTILIZATION_WARNING,
3521 	        )
3522 	        self.assert_pcs_success(
3523 	            "resource utilization".split(),
3524 	            dedent(
3525 	                """\
3526 	                Resource Utilization:
3527 	                 dummy: test1=-10 test4=1234
3528 	                 dummy1: test2=321
3529 	                """
3530 	            ),
3531 	            stderr_full=FIXTURE_UTILIZATION_WARNING,
3532 	        )
3533 	
3534 	    def test_no_warning_printed_placement_strategy_is_set(self):
3535 	        self.fixture_resource()
3536 	        self.assert_effect(
3537 	            "property set placement-strategy=minimal".split(),
3538 	            self.fixture_xml_resource_no_utilization(),
3539 	        )
3540 	        self.assert_resources_xml_in_cib(
3541 	            """
3542 	            <crm_config>
3543 	                <cluster_property_set id="cib-bootstrap-options">
3544 	                    <nvpair id="cib-bootstrap-options-placement-strategy"
3545 	                        name="placement-strategy" value="minimal"
3546 	                    />
3547 	                </cluster_property_set>
3548 	            </crm_config>
3549 	            """,
3550 	            get_cib_part_func=lambda cib: etree.tostring(
3551 	                etree.parse(cib).findall(".//crm_config")[0],
3552 	            ),
3553 	        )
3554 	        self.assert_effect(
3555 	            "resource utilization R test=100".split(),
3556 	            self.fixture_xml_resource_with_utilization(),
3557 	        )
3558 	        self.assert_pcs_success(
3559 	            "resource utilization".split(),
3560 	            dedent(
3561 	                """\
3562 	                Resource Utilization:
3563 	                 R: test=100
3564 	                """
3565 	            ),
3566 	        )
3567 	
3568 	    def test_resource_utilization_set_invalid(self):
3569 	        self.pcs_runner = PcsRunner(self.temp_large_cib.name)
3570 	        self.pcs_runner.mock_settings = get_mock_settings()
3571 	        self.assert_pcs_fail(
3572 	            "resource utilization dummy test".split(),
3573 	            (
3574 	                f"{FIXTURE_UTILIZATION_WARNING}"
3575 	                "Error: missing value of 'test' option\n"
3576 	            ),
3577 	        )
3578 	        self.assert_pcs_fail(
3579 	            "resource utilization dummy =10".split(),
3580 	            f"{FIXTURE_UTILIZATION_WARNING}Error: missing key in '=10' option\n",
3581 	        )
3582 	        self.assert_pcs_fail(
3583 	            "resource utilization dummy0".split(),
3584 	            (
3585 	                f"{FIXTURE_UTILIZATION_WARNING}"
3586 	                "Error: Unable to find a resource: dummy0\n"
3587 	            ),
3588 	        )
3589 	        self.assert_pcs_fail(
3590 	            "resource utilization dummy0 test=10".split(),
3591 	            (
3592 	                f"{FIXTURE_UTILIZATION_WARNING}"
3593 	                "Error: Unable to find a resource: dummy0\n"
3594 	            ),
3595 	        )
3596 	        self.assert_pcs_fail(
3597 	            "resource utilization dummy1 test1=10 test=int".split(),
3598 	            (
3599 	                f"{FIXTURE_UTILIZATION_WARNING}"
3600 	                "Error: Value of utilization attribute must be integer: "
3601 	                "'test=int'\n"
3602 	            ),
3603 	        )
3604 	
3605 	    def test_keep_empty_nvset(self):
3606 	        self.fixture_resource_utilization()
3607 	        self.assert_effect(
3608 	            "resource utilization R test=".split(),
3609 	            self.fixture_xml_resource_empty_utilization(),
3610 	            stderr_full=FIXTURE_UTILIZATION_WARNING,
3611 	        )
3612 	
3613 	    def test_dont_create_nvset_on_removal(self):
3614 	        self.fixture_resource()
3615 	        self.assert_effect(
3616 	            "resource utilization R test=".split(),
3617 	            self.fixture_xml_resource_no_utilization(),
3618 	            stderr_full=FIXTURE_UTILIZATION_WARNING,
3619 	        )
3620 	
3621 	
3622 	class MetaAttrs(
3623 	    TestCase,
3624 	    get_assert_pcs_effect_mixin(
3625 	        lambda cib: etree.tostring(etree.parse(cib).findall(".//resources")[0])
3626 	    ),
3627 	):
3628 	    def setUp(self):
3629 	        self.empty_cib = empty_cib
3630 	        self.temp_cib = get_tmp_file("tier1_resource_meta")
3631 	        write_file_to_tmpfile(empty_cib, self.temp_cib)
3632 	        self.pcs_runner = PcsRunner(self.temp_cib.name)
3633 	        self.pcs_runner.mock_settings = get_mock_settings()
3634 	
3635 	    def tearDown(self):
3636 	        self.temp_cib.close()
3637 	
3638 	    def set_cib_file(self, *xml_string_list):
3639 	        xml_manip = XmlManipulation.from_file(self.empty_cib)
3640 	        xml_manip.append_to_first_tag_name("resources", *xml_string_list)
3641 	        write_data_to_tmpfile(str(xml_manip), self.temp_cib)
3642 	
3643 	    @staticmethod
3644 	    def _fixture_xml_resource_no_meta():
3645 	        return """
3646 	        <primitive class="ocf" id="R" provider="pcsmock" type="minimal">
3647 	            <operations>
3648 	                <op id="R-monitor-interval-10s" interval="10s"
3649 	                    name="monitor" timeout="20s"
3650 	                />
3651 	            </operations>
3652 	        </primitive>
3653 	        """
3654 	
3655 	    @staticmethod
3656 	    def fixture_xml_resource_no_meta():
3657 	        return f"""
3658 	            <resources>
3659 	            {MetaAttrs._fixture_xml_resource_no_meta()}
3660 	            </resources>
3661 	            """
3662 	
3663 	    @staticmethod
3664 	    def fixture_xml_resource_empty_meta():
3665 	        return """
3666 	            <resources>
3667 	                <primitive class="ocf" id="R" provider="pcsmock" type="minimal">
3668 	                    <meta_attributes id="R-meta_attributes" />
3669 	                    <operations>
3670 	                        <op id="R-monitor-interval-10s" interval="10s"
3671 	                            name="monitor" timeout="20s"
3672 	                        />
3673 	                    </operations>
3674 	                </primitive>
3675 	            </resources>
3676 	            """
3677 	
3678 	    @staticmethod
3679 	    def fixture_xml_resource_with_meta():
3680 	        return """
3681 	            <resources>
3682 	                <primitive class="ocf" id="R" provider="pcsmock" type="minimal">
3683 	                    <meta_attributes id="R-meta_attributes">
3684 	                        <nvpair id="R-meta_attributes-a" name="a" value="b"/>
3685 	                    </meta_attributes>
3686 	                    <operations>
3687 	                        <op id="R-monitor-interval-10s" interval="10s"
3688 	                            name="monitor" timeout="20s"
3689 	                        />
3690 	                    </operations>
3691 	                </primitive>
3692 	            </resources>
3693 	            """
3694 	
3695 	    def fixture_resource(self):
3696 	        self.assert_effect(
3697 	            "resource create --no-default-ops R ocf:pcsmock:minimal".split(),
3698 	            self.fixture_xml_resource_no_meta(),
3699 	        )
3700 	
3701 	    def fixture_resource_meta(self):
3702 	        self.assert_effect(
3703 	            "resource create --no-default-ops R ocf:pcsmock:minimal meta a=b".split(),
3704 	            self.fixture_xml_resource_with_meta(),
3705 	            stderr_full=fixture_meta_attributes_warning(
3706 	                ["a"], ra_const.PRIMITIVE_META
3707 	            ),
3708 	        )
3709 	
3710 	    def test_meta_attrs(self):
3711 	        # see also BundleMiscCommands
3712 	        self.assert_pcs_success(
3713 	            (
3714 	                "resource create --no-default-ops D0 ocf:pcsmock:params"
3715 	                " mandatory=test1a optional=test2a op monitor interval=30 meta"
3716 	                " test5=test5a test6=test6a"
3717 	            ).split(),
3718 	            stderr_full=fixture_meta_attributes_warning(
3719 	                ["test5", "test6"], ra_const.PRIMITIVE_META
3720 	            ),
3721 	        )
3722 	        self.assert_pcs_success(
3723 	            (
3724 	                "resource create --no-default-ops D1 ocf:pcsmock:params"
3725 	                " mandatory=test1a optional=test2a op monitor interval=30"
3726 	            ).split(),
3727 	        )
3728 	        self.assert_pcs_success(
3729 	            (
3730 	                "resource update D0 mandatory=test1b optional=test2a op monitor "
3731 	                "interval=35 meta test7=test7a test6="
3732 	            ).split(),
3733 	            stderr_full=fixture_use_meta_command_instead_warning(),
3734 	        )
3735 	        self.assert_pcs_success(
3736 	            "resource meta D1 d1meta=superd1meta".split(),
3737 	            stderr_full=fixture_meta_attributes_warning(
3738 	                ["d1meta"], ra_const.PRIMITIVE_META
3739 	            ),
3740 	        )
3741 	        self.assert_pcs_success("resource group add TestRG D1".split())
3742 	        self.assert_pcs_success(
3743 	            "resource meta TestRG testrgmeta=mymeta testrgmeta2=mymeta2".split(),
3744 	            stderr_full=fixture_meta_attributes_warning(
3745 	                ["testrgmeta", "testrgmeta2"], ra_const.PRIMITIVE_META
3746 	            ),
3747 	        )
3748 	        self.assert_pcs_success(
3749 	            "resource config".split(),
3750 	            dedent(
3751 	                """\
3752 	                Resource: D0 (class=ocf provider=pcsmock type=params)
3753 	                  Attributes: D0-instance_attributes
3754 	                    mandatory=test1b
3755 	                    optional=test2a
3756 	                  Meta Attributes: D0-meta_attributes
3757 	                    test5=test5a
3758 	                    test7=test7a
3759 	                  Operations:
3760 	                    monitor: D0-monitor-interval-35
3761 	                      interval=35
3762 	                Group: TestRG
3763 	                  Meta Attributes: TestRG-meta_attributes
3764 	                    testrgmeta=mymeta
3765 	                    testrgmeta2=mymeta2
3766 	                  Resource: D1 (class=ocf provider=pcsmock type=params)
3767 	                    Attributes: D1-instance_attributes
3768 	                      mandatory=test1a
3769 	                      optional=test2a
3770 	                    Meta Attributes: D1-meta_attributes
3771 	                      d1meta=superd1meta
3772 	                    Operations:
3773 	                      monitor: D1-monitor-interval-30
3774 	                        interval=30
3775 	                """
3776 	            ),
3777 	        )
3778 	
3779 	    def test_resource_update_keep_empty_meta(self):
3780 	        self.fixture_resource_meta()
3781 	        self.assert_effect(
3782 	            "resource update R meta a=".split(),
3783 	            self.fixture_xml_resource_empty_meta(),
3784 	        )
3785 	
3786 	    def test_resource_update_dont_create_meta_on_removal(self):
3787 	        self.fixture_resource()
3788 	        self.assert_effect(
3789 	            "resource update R meta a=".split(),
3790 	            self.fixture_xml_resource_no_meta(),
3791 	        )
3792 	
3793 	    @staticmethod
3794 	    def fixture_not_ocf_clone():
3795 	        return """
3796 	            <clone id="clone-R">
3797 	                <primitive class="systemd" id="R" type="pcsmock">
3798 	                    <instance_attributes id="R-instance_attributes" />
3799 	                    <operations>
3800 	                        <op id="R-monitor-interval-10s" interval="10s"
3801 	                            name="monitor" timeout="20s"
3802 	                        />
3803 	                    </operations>
3804 	                </primitive>
3805 	            </clone>
3806 	            """
3807 	
3808 	
3809 	class UpdateInstanceAttrs(
3810 	    TestCase,
3811 	    get_assert_pcs_effect_mixin(
3812 	        lambda cib: etree.tostring(etree.parse(cib).findall(".//resources")[0])
3813 	    ),
3814 	):
3815 	    # The idempotency with remote-node is tested in
3816 	    # pcs_test/tier1/legacy/test_cluster_pcmk_remote.py in
3817 	    # NodeAddGuest.test_success_when_guest_node_matches_with_existing_guest
3818 	
3819 	    # see also BundleMiscCommands
3820 	
3821 	    def setUp(self):
3822 	        self.empty_cib = empty_cib
3823 	        self.temp_cib = get_tmp_file("tier1_resource_update_instance_attrs")
3824 	        write_file_to_tmpfile(empty_cib, self.temp_cib)
3825 	        self.pcs_runner = PcsRunner(self.temp_cib.name)
3826 	        self.pcs_runner.mock_settings = get_mock_settings()
3827 	
3828 	    def tearDown(self):
3829 	        self.temp_cib.close()
3830 	
3831 	    def set_cib_file(self, *xml_string_list):
3832 	        xml_manip = XmlManipulation.from_file(self.empty_cib)
3833 	        xml_manip.append_to_first_tag_name("resources", *xml_string_list)
3834 	        write_data_to_tmpfile(str(xml_manip), self.temp_cib)
3835 	
3836 	    @staticmethod
3837 	    def fixture_xml_resource_no_attrs():
3838 	        return """
3839 	            <resources>
3840 	                <primitive class="ocf" id="R" provider="pcsmock" type="params">
3841 	                    <operations>
3842 	                        <op id="R-monitor-interval-10s" interval="10s"
3843 	                            name="monitor" timeout="20s"
3844 	                        />
3845 	                    </operations>
3846 	                </primitive>
3847 	            </resources>
3848 	            """
3849 	
3850 	    @staticmethod
3851 	    def _fixture_xml_resource_empty_attrs():
3852 	        return """
3853 	            <primitive class="ocf" id="R" provider="pcsmock" type="params">
3854 	                <instance_attributes id="R-instance_attributes" />
3855 	                <operations>
3856 	                    <op id="R-monitor-interval-10s" interval="10s"
3857 	                        name="monitor" timeout="20s"
3858 	                    />
3859 	                </operations>
3860 	            </primitive>
3861 	        """
3862 	
3863 	    @staticmethod
3864 	    def fixture_xml_resource_empty_attrs():
3865 	        return f"""
3866 	            <resources>
3867 	            {UpdateInstanceAttrs._fixture_xml_resource_empty_attrs()}
3868 	            </resources>
3869 	            """
3870 	
3871 	    @staticmethod
3872 	    def fixture_xml_resource_with_attrs():
3873 	        return """
3874 	            <resources>
3875 	                <primitive class="ocf" id="R" provider="pcsmock" type="params">
3876 	                    <instance_attributes id="R-instance_attributes">
3877 	                        <nvpair id="R-instance_attributes-mandatory"
3878 	                            name="mandatory" value="F"
3879 	                        />
3880 	                    </instance_attributes>
3881 	                    <operations>
3882 	                        <op id="R-monitor-interval-10s" interval="10s"
3883 	                            name="monitor" timeout="20s"
3884 	                        />
3885 	                    </operations>
3886 	                </primitive>
3887 	            </resources>
3888 	            """
3889 	
3890 	    def fixture_resource(self):
3891 	        self.assert_effect(
3892 	            "resource create --no-default-ops R ocf:pcsmock:params --force".split(),
3893 	            self.fixture_xml_resource_no_attrs(),
3894 	            stderr_full="Warning: required resource option 'mandatory' is missing\n",
3895 	        )
3896 	
3897 	    def fixture_resource_attrs(self):
3898 	        self.assert_effect(
3899 	            "resource create --no-default-ops R ocf:pcsmock:params mandatory=F".split(),
3900 	            self.fixture_xml_resource_with_attrs(),
3901 	        )
3902 	
3903 	    def test_usage(self):
3904 	        self.assert_pcs_fail(
3905 	            "resource update".split(),
3906 	            stderr_start="\nUsage: pcs resource update...\n",
3907 	        )
3908 	
3909 	    def test_bad_instance_variables(self):
3910 	        self.assert_pcs_fail(
3911 	            (
3912 	                "resource create --no-default-ops D0 ocf:pcsmock:params"
3913 	                " test=testC test2=test2a test4=test4A op monitor interval=35"
3914 	                " meta test7=test7a test6="
3915 	            ).split(),
3916 	            (
3917 	                "Error: invalid resource options: 'test', 'test2', 'test4', "
3918 	                "allowed options are: 'advanced', 'enum', 'mandatory', "
3919 	                "'optional', 'unique1', 'unique2', use --force to override\n"
3920 	                "Error: required resource option 'mandatory' is missing, "
3921 	                "use --force to override\n"
3922 	                + fixture_meta_attributes_warning(
3923 	                    ["test7"], ra_const.PRIMITIVE_META
3924 	                )
3925 	                + ERRORS_HAVE_OCCURRED
3926 	            ),
3927 	        )
3928 	
3929 	        self.assert_pcs_success(
3930 	            (
3931 	                "resource create --no-default-ops --force D0 ocf:pcsmock:params"
3932 	                " test=testC test2=test2a test4=test4A op monitor interval=35"
3933 	                " meta test7=test7a test6="
3934 	            ).split(),
3935 	            stderr_full=(
3936 	                "Warning: invalid resource options: 'test', 'test2', 'test4',"
3937 	                " allowed options are: 'advanced', 'enum', 'mandatory', "
3938 	                "'optional', 'unique1', 'unique2'\n"
3939 	                "Warning: required resource option 'mandatory' is missing\n"
3940 	                + fixture_meta_attributes_warning(
3941 	                    ["test7"], ra_const.PRIMITIVE_META
3942 	                )
3943 	            ),
3944 	        )
3945 	
3946 	        self.assert_pcs_fail(
3947 	            "resource update D0 test=testA test2=testB test3=testD".split(),
3948 	            (
3949 	                "Error: invalid resource option 'test3', allowed options"
3950 	                " are: 'advanced', 'enum', 'mandatory', 'optional', 'unique1', "
3951 	                "'unique2', use --force to override\n"
3952 	            ),
3953 	        )
3954 	
3955 	        self.assert_pcs_success(
3956 	            "resource update D0 test=testB test2=testC test3=testD --force".split(),
3957 	            stderr_full=(
3958 	                "Warning: invalid resource option 'test3',"
3959 	                " allowed options are: 'advanced', 'enum', 'mandatory', "
3960 	                "'optional', 'unique1', 'unique2'\n"
3961 	            ),
3962 	        )
3963 	
3964 	        self.assert_pcs_success(
3965 	            "resource config D0".split(),
3966 	            dedent(
3967 	                """\
3968 	                Resource: D0 (class=ocf provider=pcsmock type=params)
3969 	                  Attributes: D0-instance_attributes
3970 	                    test=testB
3971 	                    test2=testC
3972 	                    test3=testD
3973 	                    test4=test4A
3974 	                  Meta Attributes: D0-meta_attributes
3975 	                    test6=
3976 	                    test7=test7a
3977 	                  Operations:
3978 	                    monitor: D0-monitor-interval-35
3979 	                      interval=35
3980 	                """
3981 	            ),
3982 	        )
3983 	
3984 	    def test_nonexisting_agent(self):
3985 	        agent = "ocf:pcsmock:nonexistent"
3986 	        message = (
3987 	            f"Agent '{agent}' is not installed or does "
3988 	            "not provide valid metadata: "
3989 	            "pcs mock error message: unable to load agent metadata"
3990 	        )
3991 	        self.assert_pcs_success(
3992 	            f"resource create --force D0 {agent}".split(),
3993 	            stderr_full=f"Warning: {message}\n",
3994 	        )
3995 	
3996 	        self.assert_pcs_fail(
3997 	            "resource update D0 test=testA".split(),
3998 	            f"Error: {message}, use --force to override\n",
3999 	        )
4000 	        self.assert_pcs_success(
4001 	            "resource update --force D0 test=testA".split(),
4002 	            stderr_full=f"Warning: {message}\n",
4003 	        )
4004 	
4005 	    def test_update_existing(self):
4006 	        xml = """
4007 	            <resources>
4008 	                <primitive class="ocf" id="Dummy" provider="pcsmock"
4009 	                    type="params"
4010 	                >
4011 	                    <instance_attributes id="Dummy-instance_attributes">
4012 	                        <nvpair id="Dummy-instance_attributes-mandatory"
4013 	                            name="mandatory" value="manda"
4014 	                        />
4015 	                        <nvpair id="Dummy-instance_attributes-optional"
4016 	                            name="optional" value="{optional}"
4017 	                        />
4018 	                    </instance_attributes>
4019 	                    <operations>
4020 	                        <op id="Dummy-monitor-interval-30s" interval="30s"
4021 	                            name="monitor"
4022 	                        />
4023 	                    </operations>
4024 	                </primitive>
4025 	            </resources>
4026 	        """
4027 	        self.assert_effect(
4028 	            (
4029 	                "resource create --no-default-ops Dummy ocf:pcsmock:params"
4030 	                " mandatory=manda optional=opti1 op monitor interval=30s"
4031 	            ).split(),
4032 	            xml.format(optional="opti1"),
4033 	        )
4034 	
4035 	        self.assert_effect(
4036 	            "resource update Dummy optional=opti2".split(),
4037 	            xml.format(optional="opti2"),
4038 	        )
4039 	
4040 	    def test_keep_empty_nvset(self):
4041 	        self.fixture_resource_attrs()
4042 	        self.assert_effect(
4043 	            "resource update R mandatory= --force".split(),
4044 	            self.fixture_xml_resource_empty_attrs(),
4045 	            stderr_full="Warning: required resource option 'mandatory' is missing\n",
4046 	        )
4047 	
4048 	    def test_dont_create_nvset_on_removal(self):
4049 	        self.fixture_resource()
4050 	        self.assert_effect(
4051 	            "resource update R mandatory= --force".split(),
4052 	            self.fixture_xml_resource_no_attrs(),
4053 	            stderr_full="Warning: required resource option 'mandatory' is missing\n",
4054 	        )
4055 	
4056 	    def test_agent_self_validation_failure(self):
4057 	        self.fixture_resource()
4058 	        self.assert_pcs_fail(
4059 	            [
4060 	                "resource",
4061 	                "update",
4062 	                "R",
4063 	                "mandatory=is_invalid=True",
4064 	                "--agent-validation",
4065 	            ],
4066 	            stderr_full=(
4067 	                "Error: Validation result from agent (use --force to override):\n"
4068 	                "  pcsmock validation failure\n"
4069 	            ),
4070 	        )
4071 	
4072 	    @staticmethod
4073 	    def fixture_not_ocf_clone():
4074 	        return """
4075 	            <clone id="clone-R">
4076 	                <primitive class="systemd" id="R" type="pcsmock">
4077 	                    <instance_attributes id="R-instance_attributes" />
4078 	                    <operations>
4079 	                        <op id="R-monitor-interval-10s" interval="10s"
4080 	                            name="monitor" timeout="20s"
4081 	                        />
4082 	                    </operations>
4083 	                </primitive>
4084 	            </clone>
4085 	            """
4086 	
4087 	    def test_clone_promotable_not_ocf(self):
4088 	        self.set_cib_file(self.fixture_not_ocf_clone())
4089 	        self.assert_pcs_fail_regardless_of_force(
4090 	            "resource update clone-R promotable=1".split(),
4091 	            (
4092 	                "Error: Clone option 'promotable' is not compatible with "
4093 	                "'systemd:pcsmock' resource agent of resource 'R'\n"
4094 	            ),
4095 	        )
4096 	
4097 	    def test_clone_globally_unique_not_ocf(self):
4098 	        self.set_cib_file(self.fixture_not_ocf_clone())
4099 	        self.assert_pcs_fail_regardless_of_force(
4100 	            "resource update clone-R globally-unique=1".split(),
4101 	            (
4102 	                "Error: Clone option 'globally-unique' is not compatible with "
4103 	                "'systemd:pcsmock' resource agent of resource 'R'\n"
4104 	            ),
4105 	        )
4106 	
4107 	    def test_clone_promotable_unsupported(self):
4108 	        self.set_cib_file(
4109 	            f"""
4110 	            <clone id="clone-R">
4111 	                {self._fixture_xml_resource_empty_attrs()}
4112 	            </clone>
4113 	            """
4114 	        )
4115 	        self.assert_pcs_fail(
4116 	            "resource update clone-R promotable=1".split(),
4117 	            (
4118 	                "Error: Clone option 'promotable' is not compatible with "
4119 	                "'ocf:pcsmock:params' resource agent of resource 'R', "
4120 	                "use --force to override\n"
4121 	            ),
4122 	        )
4123 	
4124 	
4125 	class CloneMasterUpdate(TestCase, AssertPcsMixin):
4126 	    def setUp(self):
4127 	        self.temp_cib = get_tmp_file("tier1_resource_clone_master_update")
4128 	        write_file_to_tmpfile(empty_cib, self.temp_cib)
4129 	        self.pcs_runner = PcsRunner(self.temp_cib.name)
4130 	        self.pcs_runner.mock_settings = get_mock_settings()
4131 	
4132 	    def tearDown(self):
4133 	        self.temp_cib.close()
4134 	
4135 	    def test_no_op_allowed_in_clone_update(self):
4136 	        self.assert_pcs_success(
4137 	            "resource create dummy ocf:pcsmock:minimal clone".split()
4138 	        )
4139 	        self.assert_pcs_success(
4140 	            "resource config dummy-clone".split(),
4141 	            dedent(
4142 	                """\
4143 	                Clone: dummy-clone
4144 	                  Resource: dummy (class=ocf provider=pcsmock type=minimal)
4145 	                    Operations:
4146 	                      migrate_from: dummy-migrate_from-interval-0s
4147 	                        interval=0s timeout=20s
4148 	                      migrate_to: dummy-migrate_to-interval-0s
4149 	                        interval=0s timeout=20s
4150 	                      monitor: dummy-monitor-interval-10s
4151 	                        interval=10s timeout=20s
4152 	                      reload: dummy-reload-interval-0s
4153 	                        interval=0s timeout=20s
4154 	                      reload-agent: dummy-reload-agent-interval-0s
4155 	                        interval=0s timeout=20s
4156 	                      start: dummy-start-interval-0s
4157 	                        interval=0s timeout=20s
4158 	                      stop: dummy-stop-interval-0s
4159 	                        interval=0s timeout=20s
4160 	                """
4161 	            ),
4162 	        )
4163 	        self.assert_pcs_fail(
4164 	            "resource update dummy-clone op stop timeout=300".split(),
4165 	            "Error: op settings must be changed on base resource, not the clone\n",
4166 	        )
4167 	        self.assert_pcs_fail(
4168 	            "resource update dummy-clone foo=bar op stop timeout=300".split(),
4169 	            "Error: op settings must be changed on base resource, not the clone\n",
4170 	        )
4171 	        self.assert_pcs_success(
4172 	            "resource config dummy-clone".split(),
4173 	            dedent(
4174 	                """\
4175 	                Clone: dummy-clone
4176 	                  Resource: dummy (class=ocf provider=pcsmock type=minimal)
4177 	                    Operations:
4178 	                      migrate_from: dummy-migrate_from-interval-0s
4179 	                        interval=0s timeout=20s
4180 	                      migrate_to: dummy-migrate_to-interval-0s
4181 	                        interval=0s timeout=20s
4182 	                      monitor: dummy-monitor-interval-10s
4183 	                        interval=10s timeout=20s
4184 	                      reload: dummy-reload-interval-0s
4185 	                        interval=0s timeout=20s
4186 	                      reload-agent: dummy-reload-agent-interval-0s
4187 	                        interval=0s timeout=20s
4188 	                      start: dummy-start-interval-0s
4189 	                        interval=0s timeout=20s
4190 	                      stop: dummy-stop-interval-0s
4191 	                        interval=0s timeout=20s
4192 	                """
4193 	            ),
4194 	        )
4195 	
4196 	    def test_no_op_allowed_in_master_update(self):
4197 	        # pcs no longer allows creating masters but supports existing ones. In
4198 	        # order to test it, we need to put a master in the CIB without pcs.
4199 	        fixture_to_cib(self.temp_cib.name, fixture_master_xml("dummy"))
4200 	        show = dedent(
4201 	            f"""\
4202 	            Clone: dummy-master
4203 	              Meta Attributes:
4204 	                promotable=true
4205 	              Resource: dummy (class=ocf provider=pcsmock type=stateful)
4206 	                Operations:
4207 	                  monitor: dummy-monitor-interval-10
4208 	                    interval=10 timeout=20 role={const.PCMK_ROLE_PROMOTED}
4209 	                  monitor: dummy-monitor-interval-11
4210 	                    interval=11 timeout=20 role={const.PCMK_ROLE_UNPROMOTED}
4211 	                  notify: dummy-notify-interval-0s
4212 	                    interval=0s timeout=5
4213 	                  start: dummy-start-interval-0s
4214 	                    interval=0s timeout=20
4215 	                  stop: dummy-stop-interval-0s
4216 	                    interval=0s timeout=20
4217 	            """
4218 	        )
4219 	        self.assert_pcs_success("resource config dummy-master".split(), show)
4220 	        self.assert_pcs_fail(
4221 	            "resource update dummy-master op stop timeout=300".split(),
4222 	            "Error: op settings must be changed on base resource, not the clone\n",
4223 	        )
4224 	        self.assert_pcs_fail(
4225 	            "resource update dummy-master foo=bar op stop timeout=300".split(),
4226 	            "Error: op settings must be changed on base resource, not the clone\n",
4227 	        )
4228 	        self.assert_pcs_success("resource config dummy-master".split(), show)
4229 	
4230 	
4231 	class TransformMasterToClone(ResourceTest):
4232 	    def setUp(self):
4233 	        super().setUp()
4234 	        self.pcs_runner.mock_settings = get_mock_settings()
4235 	
4236 	    def test_transform_master_without_meta_on_update(self):
4237 	        # pcs no longer allows creating masters but supports existing ones. In
4238 	        # order to test it, we need to put a master in the CIB without pcs.
4239 	        fixture_to_cib(self.temp_cib.name, fixture_master_xml("dummy"))
4240 	        self.assert_effect(
4241 	            "resource update dummy-master meta a=b".split(),
4242 	            """<resources>
4243 	                <clone id="dummy-master">
4244 	                    <primitive class="ocf" id="dummy" provider="pcsmock"
4245 	                        type="stateful"
4246 	                    >
4247 	                        <operations>
4248 	                            <op id="dummy-monitor-interval-10" interval="10"
4249 	                                name="monitor" role="Master" timeout="20"
4250 	                            />
4251 	                            <op id="dummy-monitor-interval-11" interval="11"
4252 	                                name="monitor" role="Slave" timeout="20"
4253 	                            />
4254 	                            <op id="dummy-notify-interval-0s" interval="0s"
4255 	                                name="notify" timeout="5"
4256 	                            />
4257 	                            <op id="dummy-start-interval-0s" interval="0s"
4258 	                                name="start" timeout="20"
4259 	                            />
4260 	                            <op id="dummy-stop-interval-0s" interval="0s"
4261 	                                name="stop" timeout="20"
4262 	                            />
4263 	                        </operations>
4264 	                    </primitive>
4265 	                    <meta_attributes id="dummy-master-meta_attributes">
4266 	                        <nvpair id="dummy-master-meta_attributes-promotable"
4267 	                              name="promotable" value="true"
4268 	                        />
4269 	                        <nvpair id="dummy-master-meta_attributes-a" name="a"
4270 	                            value="b"
4271 	                        />
4272 	                    </meta_attributes>
4273 	                </clone>
4274 	            </resources>""",
4275 	            stderr_full=fixture_meta_attributes_not_validated_warning(
4276 	                ["clone"]
4277 	            ),
4278 	        )
4279 	
4280 	    def test_transform_master_with_meta_on_update(self):
4281 	        # pcs no longer allows creating masters but supports existing ones. In
4282 	        # order to test it, we need to put a master in the CIB without pcs.
4283 	        fixture_to_cib(
4284 	            self.temp_cib.name,
4285 	            fixture_master_xml("dummy", meta_dict=dict(a="A", b="B", c="C")),
4286 	        )
4287 	        self.assert_effect(
4288 	            "resource update dummy-master meta a=AA b= d=D promotable=".split(),
4289 	            """<resources>
4290 	                <clone id="dummy-master">
4291 	                    <primitive class="ocf" id="dummy" provider="pcsmock"
4292 	                        type="stateful"
4293 	                    >
4294 	                        <operations>
4295 	                            <op id="dummy-monitor-interval-10" interval="10"
4296 	                                name="monitor" role="Master" timeout="20"
4297 	                            />
4298 	                            <op id="dummy-monitor-interval-11" interval="11"
4299 	                                name="monitor" role="Slave" timeout="20"
4300 	                            />
4301 	                            <op id="dummy-notify-interval-0s" interval="0s"
4302 	                                name="notify" timeout="5"
4303 	                            />
4304 	                            <op id="dummy-start-interval-0s" interval="0s"
4305 	                                name="start" timeout="20"
4306 	                            />
4307 	                            <op id="dummy-stop-interval-0s" interval="0s"
4308 	                                name="stop" timeout="20"
4309 	                            />
4310 	                        </operations>
4311 	                    </primitive>
4312 	                    <meta_attributes id="dummy-master-meta_attributes">
4313 	                        <nvpair id="dummy-master-meta_attributes-a" name="a"
4314 	                            value="AA"
4315 	                        />
4316 	                        <nvpair id="dummy-master-meta_attributes-c" name="c"
4317 	                            value="C"
4318 	                        />
4319 	                        <nvpair id="dummy-master-meta_attributes-d" name="d"
4320 	                            value="D"
4321 	                        />
4322 	                    </meta_attributes>
4323 	                </clone>
4324 	            </resources>""",
4325 	            stderr_full=fixture_meta_attributes_not_validated_warning(
4326 	                ["clone"]
4327 	            ),
4328 	        )
4329 	
4330 	
4331 	class BundleCommon(
4332 	    TestCase,
4333 	    get_assert_pcs_effect_mixin(
4334 	        lambda cib: etree.tostring(etree.parse(cib).findall(".//resources")[0])
4335 	    ),
4336 	):
4337 	    def setUp(self):
4338 	        self.temp_cib = get_tmp_file("tier1_resource_bundle")
4339 	        write_file_to_tmpfile(empty_cib, self.temp_cib)
4340 	        self.pcs_runner = PcsRunner(self.temp_cib.name)
4341 	        self.pcs_runner.mock_settings = get_mock_settings()
4342 	
4343 	    def tearDown(self):
4344 	        self.temp_cib.close()
4345 	
4346 	    def fixture_primitive(self, name, bundle):
4347 	        self.assert_pcs_success(
4348 	            [
4349 	                "resource",
4350 	                "create",
4351 	                name,
4352 	                "ocf:pcsmock:minimal",
4353 	                "bundle",
4354 	                bundle,
4355 	            ]
4356 	        )
4357 	
4358 	    def fixture_bundle(self, name, container="docker"):
4359 	        self.assert_pcs_success(
4360 	            [
4361 	                "resource",
4362 	                "bundle",
4363 	                "create",
4364 	                name,
4365 	                "container",
4366 	                container,
4367 	                "image=pcs:test",
4368 	                "network",
4369 	                "control-port=1234",
4370 	            ],
4371 	        )
4372 	
4373 	
4374 	class BundleShow(BundleCommon):
4375 	    empty_cib = rc("cib-empty.xml")
4376 	
4377 	    def test_docker(self):
4378 	        self.fixture_bundle("B1", "docker")
4379 	        self.assert_pcs_success(
4380 	            "resource config B1".split(),
4381 	            dedent(
4382 	                """\
4383 	                Bundle: B1
4384 	                  Docker: image=pcs:test
4385 	                  Network: control-port=1234
4386 	                """
4387 	            ),
4388 	        )
4389 	
4390 	    def test_podman(self):
4391 	        write_file_to_tmpfile(rc("cib-empty-3.2.xml"), self.temp_cib)
4392 	        self.fixture_bundle("B1", "podman")
4393 	        self.assert_pcs_success(
4394 	            "resource config B1".split(),
4395 	            dedent(
4396 	                """\
4397 	                Bundle: B1
4398 	                  Podman: image=pcs:test
4399 	                  Network: control-port=1234
4400 	                """
4401 	            ),
4402 	        )
4403 	
4404 	    def test_unknown_container(self):
4405 	        write_file_to_tmpfile(rc("cib-empty-3.7.xml"), self.temp_cib)
4406 	        self.fixture_bundle("B1")
4407 	
4408 	        # pcs no longer allows creating rkt bundles
4409 	        cib_tree = etree.parse(
CID (unavailable; MK=4240aff4e9e57313345f81041f87585b) (#1 of 1): XML entity processing enabled (SIGMA.xml_entity_enabled):
(1) Event Sigma main event: The Python application enables entity expansion by setting the `lxml.etree.XMLParser` value `resolve_entities` to `true` or `internal` (the default value). If untrusted XML is parsed with entity expansion enabled, a malicious attacker could submit a document that contains very deeply nested entity definitions (known as a Billion Laughs Attack), causing the parser to use large amounts of memory and processing power resulting in a denial of service (DoS) condition.
(2) Event remediation: Explicitly set `resolve_entities` argument to `False`.
4410 	            self.temp_cib.name, etree.XMLParser(huge_tree=True)
4411 	        ).getroot()
4412 	        element = cib_tree.find('.//*[@id="B1"]/docker')
4413 	        element.tag = "rkt"
4414 	        write_data_to_tmpfile(etree_to_str(cib_tree), self.temp_cib)
4415 	
4416 	        self.assert_pcs_success(
4417 	            "resource config B1".split(),
4418 	            dedent(
4419 	                """\
4420 	                Bundle: B1
4421 	                  Network: control-port=1234
4422 	                """
4423 	            ),
4424 	            stderr_full=(
4425 	                "Warning: Bundle 'B1' uses unsupported container type. "
4426 	                "Supported container types are: 'docker', 'podman'\n"
4427 	            ),
4428 	        )
4429 	
4430 	
4431 	class BundleGroup(BundleCommon):
4432 	    def test_group_delete_primitive(self):
4433 	        self.fixture_bundle("B")
4434 	        self.fixture_primitive("R", "B")
4435 	        self.assert_pcs_fail(
4436 	            "resource group delete B R".split(),
4437 	            "Error: Group 'B' does not exist\n",
4438 	        )
4439 	
4440 	    def test_group_remove_primitive(self):
4441 	        self.fixture_bundle("B")
4442 	        self.fixture_primitive("R", "B")
4443 	        self.assert_pcs_fail(
4444 	            "resource group remove B R".split(),
4445 	            "Error: Group 'B' does not exist\n",
4446 	        )
4447 	
4448 	    def test_ungroup_bundle(self):
4449 	        self.fixture_bundle("B")
4450 	        self.assert_pcs_fail(
4451 	            "resource ungroup B".split(), "Error: Group 'B' does not exist\n"
4452 	        )
4453 	
4454 	
4455 	class BundleClone(BundleCommon):
4456 	    def test_clone_bundle(self):
4457 	        self.fixture_bundle("B")
4458 	        self.assert_pcs_fail(
4459 	            "resource clone B".split(),
4460 	            "Error: unable to find group or resource: B\n",
4461 	        )
4462 	
4463 	    def test_clone_primitive(self):
4464 	        self.fixture_bundle("B")
4465 	        self.fixture_primitive("R", "B")
4466 	        self.assert_pcs_fail(
4467 	            "resource clone R".split(), "Error: cannot clone bundle resource\n"
4468 	        )
4469 	
4470 	    def test_unclone_bundle(self):
4471 	        self.fixture_bundle("B")
4472 	        self.assert_pcs_fail(
4473 	            "resource unclone B".split(), "Error: could not find resource: B\n"
4474 	        )
4475 	
4476 	
4477 	class BundleMiscCommands(BundleCommon):
4478 	    def test_resource_enable_bundle(self):
4479 	        self.fixture_bundle("B")
4480 	        self.assert_pcs_success("resource enable B".split())
4481 	
4482 	    def test_resource_disable_bundle(self):
4483 	        self.fixture_bundle("B")
4484 	        self.assert_pcs_success("resource disable B".split())
4485 	
4486 	    def test_resource_manage_bundle(self):
4487 	        self.fixture_bundle("B")
4488 	        self.assert_pcs_success("resource manage B".split())
4489 	
4490 	    def test_resource_unmanage_bundle(self):
4491 	        self.fixture_bundle("B")
4492 	        self.assert_pcs_success("resource unmanage B".split())
4493 	
4494 	    def test_op_add(self):
4495 	        self.fixture_bundle("B")
4496 	        self.assert_pcs_fail_regardless_of_force(
4497 	            "resource op add B monitor interval=30".split(),
4498 	            "Error: Unable to find resource: B\n",
4499 	        )
4500 	
4501 	    def test_op_remove(self):
4502 	        self.fixture_bundle("B")
4503 	        self.assert_pcs_fail(
4504 	            "resource op remove B monitor interval=30".split(),
4505 	            "Error: Unable to find resource: B\n",
4506 	        )
4507 	
4508 	    def test_update(self):
4509 	        self.fixture_bundle("B")
4510 	        self.assert_pcs_fail_regardless_of_force(
4511 	            "resource update B meta aaa=bbb".split(),
4512 	            "Error: Unable to find resource: B\n",
4513 	        )
4514 	
4515 	    def test_utilization(self):
4516 	        self.fixture_bundle("B")
4517 	        self.assert_pcs_fail(
4518 	            "resource utilization B aaa=10".split(),
4519 	            (
4520 	                f"{FIXTURE_UTILIZATION_WARNING}"
4521 	                "Error: Unable to find a resource: B\n"
4522 	            ),
4523 	        )
4524 	
4525 	    @skip(
4526 	        "test of 'pcs resource debug-*' to be moved to pcs.lib with the "
4527 	        "command itself"
4528 	    )
4529 	    def test_debug_start_bundle(self):
4530 	        self.fixture_bundle("B")
4531 	        self.assert_pcs_fail_regardless_of_force(
4532 	            "resource debug-start B".split(),
4533 	            "Error: unable to debug-start a bundle\n",
4534 	        )
4535 	
4536 	    @skip(
4537 	        "test of 'pcs resource debug-*' to be moved to pcs.lib with the "
4538 	        "command itself"
4539 	    )
4540 	    def test_debug_start_with_resource(self):
4541 	        self.fixture_bundle("B")
4542 	        self.fixture_primitive("R", "B")
4543 	        self.assert_pcs_fail_regardless_of_force(
4544 	            "resource debug-start B".split(),
4545 	            "Error: unable to debug-start a bundle, try the bundle's resource: R\n",
4546 	        )
4547 	
4548 	    @skip(
4549 	        "test of 'pcs resource debug-*' to be moved to pcs.lib with the "
4550 	        "command itself"
4551 	    )
4552 	    def test_debug_stop_bundle(self):
4553 	        self.fixture_bundle("B")
4554 	        self.assert_pcs_fail_regardless_of_force(
4555 	            "resource debug-stop B".split(),
4556 	            "Error: unable to debug-stop a bundle\n",
4557 	        )
4558 	
4559 	    @skip(
4560 	        "test of 'pcs resource debug-*' to be moved to pcs.lib with the "
4561 	        "command itself"
4562 	    )
4563 	    def test_debug_stop_with_resource(self):
4564 	        self.fixture_bundle("B")
4565 	        self.fixture_primitive("R", "B")
4566 	        self.assert_pcs_fail_regardless_of_force(
4567 	            "resource debug-stop B".split(),
4568 	            "Error: unable to debug-stop a bundle, try the bundle's resource: R\n",
4569 	        )
4570 	
4571 	    @skip(
4572 	        "test of 'pcs resource debug-*' to be moved to pcs.lib with the "
4573 	        "command itself"
4574 	    )
4575 	    def test_debug_monitor_bundle(self):
4576 	        self.fixture_bundle("B")
4577 	        self.assert_pcs_fail_regardless_of_force(
4578 	            "resource debug-monitor B".split(),
4579 	            "Error: unable to debug-monitor a bundle\n",
4580 	        )
4581 	
4582 	    @skip(
4583 	        "test of 'pcs resource debug-*' to be moved to pcs.lib with the "
4584 	        "command itself"
4585 	    )
4586 	    def test_debug_monitor_with_resource(self):
4587 	        self.fixture_bundle("B")
4588 	        self.fixture_primitive("R", "B")
4589 	        self.assert_pcs_fail_regardless_of_force(
4590 	            "resource debug-monitor B".split(),
4591 	            "Error: unable to debug-monitor a bundle, try the bundle's resource: R\n",
4592 	        )
4593 	
4594 	    @skip(
4595 	        "test of 'pcs resource debug-*' to be moved to pcs.lib with the "
4596 	        "command itself"
4597 	    )
4598 	    def test_debug_promote_bundle(self):
4599 	        self.fixture_bundle("B")
4600 	        self.assert_pcs_fail_regardless_of_force(
4601 	            "resource debug-promote B".split(),
4602 	            "Error: unable to debug-promote a bundle\n",
4603 	        )
4604 	
4605 	    @skip(
4606 	        "test of 'pcs resource debug-*' to be moved to pcs.lib with the "
4607 	        "command itself"
4608 	    )
4609 	    def test_debug_promote_with_resource(self):
4610 	        self.fixture_bundle("B")
4611 	        self.fixture_primitive("R", "B")
4612 	        self.assert_pcs_fail_regardless_of_force(
4613 	            "resource debug-promote B".split(),
4614 	            "Error: unable to debug-promote a bundle, try the bundle's resource: R\n",
4615 	        )
4616 	
4617 	    @skip(
4618 	        "test of 'pcs resource debug-*' to be moved to pcs.lib with the "
4619 	        "command itself"
4620 	    )
4621 	    def test_debug_demote_bundle(self):
4622 	        self.fixture_bundle("B")
4623 	        self.assert_pcs_fail_regardless_of_force(
4624 	            "resource debug-demote B".split(),
4625 	            "Error: unable to debug-demote a bundle\n",
4626 	        )
4627 	
4628 	    @skip(
4629 	        "test of 'pcs resource debug-*' to be moved to pcs.lib with the "
4630 	        "command itself"
4631 	    )
4632 	    def test_debug_demote_with_resource(self):
4633 	        self.fixture_bundle("B")
4634 	        self.fixture_primitive("R", "B")
4635 	        self.assert_pcs_fail_regardless_of_force(
4636 	            "resource debug-demote B".split(),
4637 	            "Error: unable to debug-demote a bundle, try the bundle's resource: R\n",
4638 	        )
4639 	
4640 	
4641 	class ResourceUpdateRemoteAndGuestChecks(TestCase, AssertPcsMixin):
4642 	    def setUp(self):
4643 	        self.temp_cib = get_tmp_file("tier1_resource_update_remote_guest")
4644 	        write_file_to_tmpfile(empty_cib, self.temp_cib)
4645 	        self.pcs_runner = PcsRunner(self.temp_cib.name)
4646 	        self.pcs_runner.mock_settings = get_mock_settings()
4647 	
4648 	    def tearDown(self):
4649 	        self.temp_cib.close()
4650 	
4651 	    def test_update_fail_on_pacemaker_guest_attempt(self):
4652 	        self.assert_pcs_success("resource create R ocf:pcsmock:minimal".split())
4653 	        self.assert_pcs_fail(
4654 	            "resource update R meta remote-node=HOST".split(),
4655 	            (
4656 	                "Error: this command is not sufficient for creating a guest "
4657 	                "node, use 'pcs cluster node add-guest', use --force to "
4658 	                "override\n" + ERRORS_HAVE_OCCURRED
4659 	            ),
4660 	        )
4661 	
4662 	    def test_update_warn_on_pacemaker_guest_attempt(self):
4663 	        self.assert_pcs_success("resource create R ocf:pcsmock:minimal".split())
4664 	        self.assert_pcs_success(
4665 	            "resource update R meta remote-node=HOST --force".split(),
4666 	            stderr_full=(
4667 	                "Warning: this command is not sufficient for creating a guest node,"
4668 	                " use 'pcs cluster node add-guest'\n"
4669 	                + fixture_use_meta_command_instead_warning()
4670 	            ),
4671 	        )
4672 	
4673 	    def test_update_fail_on_pacemaker_guest_attempt_remove(self):
4674 	        self.assert_pcs_success(
4675 	            (
4676 	                "resource create R ocf:pcsmock:minimal meta remote-node=HOST"
4677 	                " --force"
4678 	            ).split(),
4679 	            stderr_full=(
4680 	                "Warning: this command is not sufficient for creating a guest node,"
4681 	                " use 'pcs cluster node add-guest'\n"
4682 	            ),
4683 	        )
4684 	        self.assert_pcs_fail(
4685 	            "resource update R meta remote-node=".split(),
4686 	            (
4687 	                "Error: this command is not sufficient for removing a guest "
4688 	                "node, use 'pcs cluster node remove-guest', use --force "
4689 	                "to override\n" + ERRORS_HAVE_OCCURRED
4690 	            ),
4691 	        )
4692 	
4693 	    def test_update_warn_on_pacemaker_guest_attempt_remove(self):
4694 	        self.assert_pcs_success(
4695 	            (
4696 	                "resource create R ocf:pcsmock:minimal meta remote-node=HOST"
4697 	                " --force"
4698 	            ).split(),
4699 	            stderr_full=(
4700 	                "Warning: this command is not sufficient for creating a guest node,"
4701 	                " use 'pcs cluster node add-guest'\n"
4702 	            ),
4703 	        )
4704 	        self.assert_pcs_success(
4705 	            "resource update R meta remote-node= --force".split(),
4706 	            stderr_full=(
4707 	                "Warning: this command is not sufficient for removing a guest node,"
4708 	                " use 'pcs cluster node remove-guest'\n"
4709 	            ),
4710 	        )
4711 	
4712 	
4713 	class ResourceUpdateUniqueAttrChecks(TestCase, AssertPcsMixin):
4714 	    def setUp(self):
4715 	        self.temp_cib = get_tmp_file("tier1_resource_update_unique_attr")
4716 	        write_file_to_tmpfile(empty_cib, self.temp_cib)
4717 	        self.pcs_runner = PcsRunner(self.temp_cib.name)
4718 	        self.pcs_runner.mock_settings = get_mock_settings()
4719 	
4720 	    def tearDown(self):
4721 	        self.temp_cib.close()
4722 	
4723 	    def test_unique_err(self):
4724 	        self.assert_pcs_success(
4725 	            "resource create R1 ocf:pcsmock:unique state=1".split()
4726 	        )
4727 	        self.assert_pcs_success("resource create R2 ocf:pcsmock:unique".split())
4728 	        self.assert_pcs_fail(
4729 	            "resource update R2 state=1".split(),
4730 	            (
4731 	                "Error: Value '1' of option 'state' is not unique across "
4732 	                "'ocf:pcsmock:unique' resources. Following resources are "
4733 	                "configured with the same value of the instance attribute: 'R1', "
4734 	                "use --force to override\n"
4735 	            ),
4736 	        )
4737 	
4738 	    def test_unique_setting_same_value(self):
4739 	        self.assert_pcs_success(
4740 	            "resource create R1 ocf:pcsmock:unique state=1 --no-default-ops".split()
4741 	        )
4742 	        self.assert_pcs_success(
4743 	            "resource create R2 ocf:pcsmock:unique --no-default-ops".split()
4744 	        )
4745 	        self.assert_pcs_success(
4746 	            "resource update R2 state=1 --force".split(),
4747 	            stderr_full=(
4748 	                "Warning: Value '1' of option 'state' is not unique across "
4749 	                "'ocf:pcsmock:unique' resources. Following resources are "
4750 	                "configured with the same value of the instance attribute: 'R1'\n"
4751 	            ),
4752 	        )
4753 	        res_config = dedent(
4754 	            """\
4755 	            Resource: R1 (class=ocf provider=pcsmock type=unique)
4756 	              Attributes: R1-instance_attributes
4757 	                state=1
4758 	              Operations:
4759 	                monitor: R1-monitor-interval-10s
4760 	                  interval=10s timeout=20s
4761 	            Resource: R2 (class=ocf provider=pcsmock type=unique)
4762 	              Attributes: R2-instance_attributes
4763 	                state=1
4764 	              Operations:
4765 	                monitor: R2-monitor-interval-10s
4766 	                  interval=10s timeout=20s
4767 	            """
4768 	        )
4769 	        self.assert_pcs_success("resource config".split(), res_config)
4770 	        # make sure that it doesn't check against resource itself
4771 	        self.assert_pcs_success(
4772 	            "resource update R2 state=1 --force".split(),
4773 	            stderr_full=(
4774 	                "Warning: Value '1' of option 'state' is not unique across "
4775 	                "'ocf:pcsmock:unique' resources. Following resources are "
4776 	                "configured with the same value of the instance attribute: 'R1'\n"
4777 	            ),
4778 	        )
4779 	        self.assert_pcs_success("resource config".split(), res_config)
4780 	        res_config = dedent(
4781 	            """\
4782 	            Resource: R1 (class=ocf provider=pcsmock type=unique)
4783 	              Attributes: R1-instance_attributes
4784 	                state=1
4785 	              Operations:
4786 	                monitor: R1-monitor-interval-10s
4787 	                  interval=10s timeout=20s
4788 	            Resource: R2 (class=ocf provider=pcsmock type=unique)
4789 	              Attributes: R2-instance_attributes
4790 	                state=2
4791 	              Operations:
4792 	                monitor: R2-monitor-interval-10s
4793 	                  interval=10s timeout=20s
4794 	            """
4795 	        )
4796 	        self.assert_pcs_success("resource update R2 state=2".split())
4797 	        self.assert_pcs_success("resource config".split(), res_config)
4798 	        self.assert_pcs_success("resource update R2 state=2".split())
4799 	        self.assert_pcs_success("resource config".split(), res_config)
4800 	
4801 	    def test_unique_warn(self):
4802 	        self.assert_pcs_success(
4803 	            "resource create R1 ocf:pcsmock:unique state=1 --no-default-ops".split()
4804 	        )
4805 	        self.assert_pcs_success(
4806 	            "resource create R2 ocf:pcsmock:unique --no-default-ops".split()
4807 	        )
4808 	        self.assert_pcs_success(
4809 	            "resource create R3 ocf:pcsmock:unique --no-default-ops".split()
4810 	        )
4811 	        self.assert_pcs_success(
4812 	            "resource update R2 state=1 --force".split(),
4813 	            stderr_full=(
4814 	                "Warning: Value '1' of option 'state' is not unique across "
4815 	                "'ocf:pcsmock:unique' resources. Following resources are "
4816 	                "configured with the same value of the instance attribute: 'R1'\n"
4817 	            ),
4818 	        )
4819 	        self.assert_pcs_success(
4820 	            "resource config".split(),
4821 	            dedent(
4822 	                """\
4823 	                Resource: R1 (class=ocf provider=pcsmock type=unique)
4824 	                  Attributes: R1-instance_attributes
4825 	                    state=1
4826 	                  Operations:
4827 	                    monitor: R1-monitor-interval-10s
4828 	                      interval=10s timeout=20s
4829 	                Resource: R2 (class=ocf provider=pcsmock type=unique)
4830 	                  Attributes: R2-instance_attributes
4831 	                    state=1
4832 	                  Operations:
4833 	                    monitor: R2-monitor-interval-10s
4834 	                      interval=10s timeout=20s
4835 	                Resource: R3 (class=ocf provider=pcsmock type=unique)
4836 	                  Operations:
4837 	                    monitor: R3-monitor-interval-10s
4838 	                      interval=10s timeout=20s
4839 	                """
4840 	            ),
4841 	        )
4842 	        self.assert_pcs_success(
4843 	            "resource update R3 state=1 --force".split(),
4844 	            stderr_full=(
4845 	                "Warning: Value '1' of option 'state' is not unique across "
4846 	                "'ocf:pcsmock:unique' resources. Following resources are "
4847 	                "configured with the same value of the instance attribute: 'R1', "
4848 	                "'R2'\n"
4849 	            ),
4850 	        )
4851 	        self.assert_pcs_success(
4852 	            "resource config".split(),
4853 	            dedent(
4854 	                """\
4855 	                Resource: R1 (class=ocf provider=pcsmock type=unique)
4856 	                  Attributes: R1-instance_attributes
4857 	                    state=1
4858 	                  Operations:
4859 	                    monitor: R1-monitor-interval-10s
4860 	                      interval=10s timeout=20s
4861 	                Resource: R2 (class=ocf provider=pcsmock type=unique)
4862 	                  Attributes: R2-instance_attributes
4863 	                    state=1
4864 	                  Operations:
4865 	                    monitor: R2-monitor-interval-10s
4866 	                      interval=10s timeout=20s
4867 	                Resource: R3 (class=ocf provider=pcsmock type=unique)
4868 	                  Attributes: R3-instance_attributes
4869 	                    state=1
4870 	                  Operations:
4871 	                    monitor: R3-monitor-interval-10s
4872 	                      interval=10s timeout=20s
4873 	                """
4874 	            ),
4875 	        )
4876