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