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