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