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_list_custom_last_separator,
12   	    format_plural,
13   	)
14   	from pcs.constraint import LOCATION_NODE_VALIDATION_SKIP_MSG
15   	from pcs.lib.resource_agent import const as ra_const
16   	
17   	from pcs_test.tier1.cib_resource.common import (
18   	    ResourceTest,
19   	    fixture_meta_attributes_not_validated_warning,
20   	    fixture_meta_attributes_warning,
21   	    fixture_use_meta_command_instead_warning,
22   	)
23   	from pcs_test.tier1.legacy.common import FIXTURE_UTILIZATION_WARNING
24   	from pcs_test.tools.assertions import AssertPcsMixin, assert_pcs_status
25   	from pcs_test.tools.bin_mock import get_mock_settings
26   	from pcs_test.tools.cib import get_assert_pcs_effect_mixin
27   	from pcs_test.tools.fixture_cib import (
28   	    CachedCibFixture,
29   	    fixture_master_xml,
30   	    fixture_to_cib,
31   	    wrap_element_by_master,
32   	    wrap_element_by_master_file,
33   	)
34   	from pcs_test.tools.misc import get_test_resource as rc
35   	from pcs_test.tools.misc import (
36   	    get_tmp_file,
37   	    is_minimum_pacemaker_version,
38   	    is_pacemaker_21_without_20_compatibility,
39   	    outdent,
40   	    skip_unless_crm_rule,
41   	    skip_unless_pacemaker_supports_op_onfail_demote,
42   	    write_data_to_tmpfile,
43   	    write_file_to_tmpfile,
44   	)
45   	from pcs_test.tools.pcs_runner import PcsRunner
46   	from pcs_test.tools.xml import XmlManipulation, etree_to_str
47   	
48   	PCMK_2_0_3_PLUS = is_minimum_pacemaker_version(2, 0, 3)
49   	
50   	LOCATION_NODE_VALIDATION_SKIP_WARNING = (
51   	    f"Warning: {LOCATION_NODE_VALIDATION_SKIP_MSG}\n"
52   	)
53   	ERRORS_HAVE_OCCURRED = (
54   	    "Error: Errors have occurred, therefore pcs is unable to continue\n"
55   	)
56   	DEPRECATED_DASH_DASH_GROUP = (
57   	    "Deprecation Warning: Using '--group' is deprecated and will be replaced "
58   	    "with 'group' in a future release. Specify --future to switch to the future "
59   	    "behavior.\n"
60   	)
61   	
62   	
63   	def fixture_message_not_deleting_resources_not_live(resource_ids):
64   	    resources = format_plural(resource_ids, "resource")
65   	    are = format_plural(resource_ids, "is")
66   	    return (
67   	        "Warning: Resources are not going to be stopped before deletion "
68   	        "because the command does not run on a live cluster\n"
69   	        f"Warning: Not checking if {resources} {format_list(resource_ids)} "
70   	        f"{are} stopped before deletion because the command does not run on a "
71   	        "live cluster. Deleting unstopped resources may result in orphaned "
72   	        "resources being present in the cluster.\n"
73   	    )
74   	
75   	
76   	empty_cib = rc("cib-empty.xml")
77   	large_cib = rc("cib-large.xml")
78   	
79   	
80   	class ResourceDescribe(TestCase, AssertPcsMixin):
81   	    def setUp(self):
82   	        self.pcs_runner = PcsRunner(None)
83   	        self.pcs_runner.mock_settings = get_mock_settings()
84   	
85   	    @staticmethod
86   	    def fixture_description(advanced=False):
87   	        advanced_params = """
88   	              advanced (advanced use only)
89   	                Description: This parameter should not be set usually
90   	                Type: string"""
91   	        return dedent(
92   	            """\
93   	            ocf:pcsmock:params - Mock agent for pcs tests - agent with various parameters
94   	
95   	            This is a mock agent for pcs test - agent with parameters
96   	
97   	            Resource options:
98   	              mandatory (required)
99   	                Description: A generic mandatory string parameter
100  	                Type: string
101  	              optional
102  	                Description: A generic optional string parameter
103  	                Type: string
104  	                Default: if not specified
105  	              enum
106  	                Description: An optional enum parameter
107  	                Allowed values: 'value1', 'value2', 'value3'
108  	                Default: value1{0}
109  	              unique1 (unique group: group-A)
110  	                Description: First parameter in a unique group
111  	                Type: string
112  	              unique2 (unique group: group-A)
113  	                Description: Second parameter in a unique group
114  	                Type: string
115  	
116  	            Default operations:
117  	              start:
118  	                interval=0s timeout=20s
119  	              stop:
120  	                interval=0s timeout=20s
121  	              monitor:
122  	                interval=10s timeout=20s
123  	              reload:
124  	                interval=0s timeout=20s
125  	              reload-agent:
126  	                interval=0s timeout=20s
127  	              migrate_to:
128  	                interval=0s timeout=20s
129  	              migrate_from:
130  	                interval=0s timeout=20s
131  	            """.format(advanced_params if advanced else "")
132  	        )
133  	
134  	    def test_success(self):
135  	        self.assert_pcs_success(
136  	            "resource describe ocf:pcsmock:params".split(),
137  	            self.fixture_description(),
138  	        )
139  	
140  	    def test_full(self):
141  	        self.assert_pcs_success(
142  	            "resource describe ocf:pcsmock:params --full".split(),
143  	            self.fixture_description(True),
144  	        )
145  	
146  	    def test_success_guess_name(self):
147  	        self.assert_pcs_success(
148  	            "resource describe params".split(),
149  	            stdout_full=self.fixture_description(),
150  	            stderr_full=(
151  	                "Assumed agent name 'ocf:pcsmock:params' (deduced from 'params')\n"
152  	            ),
153  	        )
154  	
155  	    def test_nonextisting_agent(self):
156  	        self.assert_pcs_fail(
157  	            "resource describe ocf:pcsmock:nonexistent".split(),
158  	            (
159  	                "Error: Agent 'ocf:pcsmock:nonexistent' is not installed or does "
160  	                "not provide valid metadata: "
161  	                "pcs mock error message: unable to load agent metadata\n"
162  	                + ERRORS_HAVE_OCCURRED
163  	            ),
164  	        )
165  	
166  	    def test_nonextisting_agent_guess_name(self):
167  	        self.assert_pcs_fail(
168  	            "resource describe nonexistent".split(),
169  	            (
170  	                "Error: Unable to find agent 'nonexistent', try specifying"
171  	                " its full name\n"
172  	            ),
173  	        )
174  	
175  	    def test_more_agents_guess_name(self):
176  	        self.assert_pcs_fail(
177  	            "resource describe pcsmock".split(),
178  	            (
179  	                "Error: Multiple agents match 'pcsmock', please specify full"
180  	                " name: 'ocf:heartbeat:pcsMock' or 'ocf:pacemaker:pcsMock'\n"
181  	            ),
182  	        )
183  	
184  	    def test_not_enough_params(self):
185  	        self.assert_pcs_fail(
186  	            "resource describe".split(),
187  	            stderr_start="\nUsage: pcs resource describe...\n",
188  	        )
189  	
190  	    def test_too_many_params(self):
191  	        self.assert_pcs_fail(
192  	            "resource describe agent1 agent2".split(),
193  	            stderr_start="\nUsage: pcs resource describe...\n",
194  	        )
195  	
196  	
197  	class ResourceTestCibFixture(CachedCibFixture):
198  	    def _setup_cib(self):
199  	        self.assert_pcs_success_ignore_output(
200  	            (
201  	                "resource create --no-default-ops ClusterIP ocf:pcsmock:minimal"
202  	            ).split()
203  	        )
204  	        self.assert_pcs_success_ignore_output(
205  	            (
206  	                "resource create --no-default-ops ClusterIP2 ocf:pcsmock:minimal"
207  	            ).split()
208  	        )
209  	        self.assert_pcs_success_ignore_output(
210  	            (
211  	                "resource create --no-default-ops ClusterIP3 ocf:pcsmock:minimal"
212  	            ).split()
213  	        )
214  	        self.assert_pcs_success_ignore_output(
215  	            (
216  	                "resource create --no-default-ops ClusterIP4 ocf:pcsmock:minimal"
217  	            ).split()
218  	        )
219  	        self.assert_pcs_success_ignore_output(
220  	            (
221  	                "resource create --no-default-ops ClusterIP5 ocf:pcsmock:minimal"
222  	            ).split()
223  	        )
224  	        self.assert_pcs_success_ignore_output(
225  	            (
226  	                "resource create --no-default-ops ClusterIP6 ocf:pcsmock:minimal"
227  	            ).split()
228  	        )
229  	        self.assert_pcs_success(
230  	            "resource group add TestGroup1 ClusterIP".split()
231  	        )
232  	        self.assert_pcs_success(
233  	            "resource group add TestGroup2 ClusterIP2 ClusterIP3".split()
234  	        )
235  	        self.assert_pcs_success("resource clone ClusterIP4".split())
236  	        # pcs no longer allows turning resources into masters but supports
237  	        # existing ones. In order to test it, we need to put a master in the
238  	        # CIB without pcs.
239  	        wrap_element_by_master_file(
240  	            self.cache_path, "ClusterIP5", master_id="Master"
241  	        )
242  	
243  	
244  	CIB_FIXTURE = ResourceTestCibFixture("fixture_tier1_resource", empty_cib)
245  	
246  	
247  	class Resource(TestCase, AssertPcsMixin):
248  	    # pylint: disable=too-many-public-methods
249  	    def setUp(self):
250  	        self.temp_cib = get_tmp_file("tier1_resource")
251  	        self.temp_large_cib = get_tmp_file("tier1_resource_large")
252  	        write_file_to_tmpfile(empty_cib, self.temp_cib)
253  	        write_file_to_tmpfile(large_cib, self.temp_large_cib)
254  	        self.pcs_runner = PcsRunner(self.temp_cib.name)
255  	        self.pcs_runner.mock_settings = get_mock_settings()
256  	
257  	    def tearDown(self):
258  	        self.temp_cib.close()
259  	        self.temp_large_cib.close()
260  	
261  	    # Setups up a cluster with Resources, groups, master/slave resource & clones
262  	    def setup_cluster_a(self):
263  	        write_file_to_tmpfile(CIB_FIXTURE.cache_path, self.temp_cib)
264  	
265  	    def test_case_insensitive(self):
266  	        self.assert_pcs_fail(
267  	            "resource create --no-default-ops D0 pcsmock".split(),
268  	            (
269  	                "Error: Multiple agents match 'pcsmock', please specify full name: "
270  	                "'ocf:heartbeat:pcsMock' or 'ocf:pacemaker:pcsMock'\n"
271  	                + ERRORS_HAVE_OCCURRED
272  	            ),
273  	        )
274  	
275  	        self.assert_pcs_success(
276  	            "resource create --no-default-ops D1 camelcase".split(),
277  	            stderr_full=(
278  	                "Assumed agent name 'ocf:pcsmock:CamelCase'"
279  	                " (deduced from 'camelcase')\n"
280  	            ),
281  	        )
282  	
283  	        self.assert_pcs_success(
284  	            "resource create --no-default-ops D2 CAMELCASE".split(),
285  	            stderr_full=(
286  	                "Assumed agent name 'ocf:pcsmock:CamelCase'"
287  	                " (deduced from 'CAMELCASE')\n"
288  	            ),
289  	        )
290  	
291  	        self.assert_pcs_fail(
292  	            "resource create --no-default-ops D4 camel_case".split(),
293  	            (
294  	                "Error: Unable to find agent 'camel_case', try specifying its full name\n"
295  	                + ERRORS_HAVE_OCCURRED
296  	            ),
297  	        )
298  	
299  	    def test_empty(self):
300  	        self.assert_pcs_success(["resource"], "NO resources configured\n")
301  	
302  	    def test_add_resources_large_cib(self):
303  	        self.pcs_runner = PcsRunner(self.temp_large_cib.name)
304  	        self.pcs_runner.mock_settings = get_mock_settings()
305  	        self.assert_pcs_success(
306  	            "resource create dummy0 ocf:pcsmock:minimal --no-default-ops".split(),
307  	        )
308  	        self.assert_pcs_success(
309  	            "resource config dummy0".split(),
310  	            dedent(
311  	                """\
312  	                Resource: dummy0 (class=ocf provider=pcsmock type=minimal)
313  	                  Operations:
314  	                    monitor: dummy0-monitor-interval-10s
315  	                      interval=10s timeout=20s
316  	                """
317  	            ),
318  	        )
319  	
320  	    def test_resource_show(self):
321  	        self.assert_pcs_success(
322  	            (
323  	                "resource create --no-default-ops ClusterIP ocf:pcsmock:params"
324  	                " mandatory=mandat optional=opti op monitor interval=30s"
325  	            ).split()
326  	        )
327  	        self.assert_pcs_success(
328  	            "resource config ClusterIP".split(),
329  	            dedent(
330  	                """\
331  	                Resource: ClusterIP (class=ocf provider=pcsmock type=params)
332  	                  Attributes: ClusterIP-instance_attributes
333  	                    mandatory=mandat
334  	                    optional=opti
335  	                  Operations:
336  	                    monitor: ClusterIP-monitor-interval-30s
337  	                      interval=30s
338  	                """
339  	            ),
340  	        )
341  	
342  	    def test_add_operation(self):
343  	        # see also BundleMiscCommands
344  	        self.assert_pcs_success(
345  	            (
346  	                "resource create --no-default-ops ClusterIP ocf:pcsmock:minimal"
347  	                " op monitor interval=30s"
348  	            ).split()
349  	        )
350  	
351  	        self.assert_pcs_fail(
352  	            "resource op add".split(),
353  	            stderr_start="\nUsage: pcs resource op add...",
354  	        )
355  	        self.assert_pcs_fail(
356  	            "resource op remove".split(),
357  	            stderr_start="\nUsage: pcs resource op remove...",
358  	        )
359  	
360  	        self.assert_pcs_fail(
361  	            "resource op add ClusterIP monitor interval=31s".split(),
362  	            (
363  	                "Error: operation monitor already specified for ClusterIP, "
364  	                "use --force to override:\n"
365  	                "monitor interval=30s (ClusterIP-monitor-interval-30s)\n"
366  	            ),
367  	        )
368  	
369  	        self.assert_pcs_success(
370  	            "resource op add ClusterIP monitor interval=31s --force".split(),
371  	        )
372  	
373  	        self.assert_pcs_fail(
374  	            "resource op add ClusterIP monitor interval=31s".split(),
375  	            (
376  	                "Error: operation monitor with interval 31s already specified "
377  	                "for ClusterIP:\n"
378  	                "monitor interval=31s (ClusterIP-monitor-interval-31s)\n"
379  	            ),
380  	        )
381  	
382  	        self.assert_pcs_fail(
383  	            "resource op add ClusterIP monitor interval=31".split(),
384  	            (
385  	                "Error: operation monitor with interval 31s already specified "
386  	                "for ClusterIP:\n"
387  	                "monitor interval=31s (ClusterIP-monitor-interval-31s)\n"
388  	            ),
389  	        )
390  	
391  	        self.assert_pcs_fail(
392  	            "resource op add ClusterIP moni=tor interval=60".split(),
393  	            "Error: moni=tor does not appear to be a valid operation action\n",
394  	        )
395  	
396  	        self.assert_pcs_success(
397  	            "resource config ClusterIP".split(),
398  	            dedent(
399  	                """\
400  	                Resource: ClusterIP (class=ocf provider=pcsmock type=minimal)
401  	                  Operations:
402  	                    monitor: ClusterIP-monitor-interval-30s
403  	                      interval=30s
404  	                    monitor: ClusterIP-monitor-interval-31s
405  	                      interval=31s
406  	                """
407  	            ),
408  	        )
409  	
410  	        self.assert_pcs_success(
411  	            (
412  	                "resource create --no-default-ops OPTest ocf:pcsmock:minimal "
413  	                "op monitor interval=30s OCF_CHECK_LEVEL=1 "
414  	                "op monitor interval=25s OCF_CHECK_LEVEL=1 enabled=0"
415  	            ).split(),
416  	        )
417  	
418  	        self.assert_pcs_success(
419  	            "resource config OPTest".split(),
420  	            dedent(
421  	                """\
422  	                Resource: OPTest (class=ocf provider=pcsmock type=minimal)
423  	                  Operations:
424  	                    monitor: OPTest-monitor-interval-30s
425  	                      interval=30s OCF_CHECK_LEVEL=1
426  	                    monitor: OPTest-monitor-interval-25s
427  	                      interval=25s enabled=0 OCF_CHECK_LEVEL=1
428  	                """
429  	            ),
430  	        )
431  	
432  	        self.assert_pcs_success(
433  	            (
434  	                "resource create --no-default-ops OPTest2 ocf:pcsmock:minimal "
435  	                "op monitor interval=30s OCF_CHECK_LEVEL=1 "
436  	                "op monitor interval=25s OCF_CHECK_LEVEL=2 "
437  	                "op start timeout=30s"
438  	            ).split(),
439  	        )
440  	
441  	        self.assert_pcs_fail(
442  	            "resource op add OPTest2 start timeout=1800s".split(),
443  	            (
444  	                "Error: operation start with interval 0s already specified for OPTest2:\n"
445  	                "start interval=0s timeout=30s (OPTest2-start-interval-0s)\n"
446  	            ),
447  	        )
448  	
449  	        self.assert_pcs_fail(
450  	            "resource op add OPTest2 start interval=100".split(),
451  	            (
452  	                "Error: operation start already specified for OPTest2, use --force to override:\n"
453  	                "start interval=0s timeout=30s (OPTest2-start-interval-0s)\n"
454  	            ),
455  	        )
456  	
457  	        self.assert_pcs_success(
458  	            "resource op add OPTest2 monitor timeout=1800s".split(),
459  	        )
460  	
461  	        self.assert_pcs_success(
462  	            "resource config OPTest2".split(),
463  	            dedent(
464  	                """\
465  	                Resource: OPTest2 (class=ocf provider=pcsmock type=minimal)
466  	                  Operations:
467  	                    monitor: OPTest2-monitor-interval-30s
468  	                      interval=30s OCF_CHECK_LEVEL=1
469  	                    monitor: OPTest2-monitor-interval-25s
470  	                      interval=25s OCF_CHECK_LEVEL=2
471  	                    start: OPTest2-start-interval-0s
472  	                      interval=0s timeout=30s
473  	                    monitor: OPTest2-monitor-interval-60s
474  	                      interval=60s timeout=1800s
475  	                """
476  	            ),
477  	        )
478  	
479  	        self.assert_pcs_success(
480  	            (
481  	                "resource create --no-default-ops OPTest3 ocf:pcsmock:minimal "
482  	                "op monitor OCF_CHECK_LEVEL=1"
483  	            ).split(),
484  	        )
485  	
486  	        self.assert_pcs_success(
487  	            "resource config OPTest3".split(),
488  	            dedent(
489  	                """\
490  	                Resource: OPTest3 (class=ocf provider=pcsmock type=minimal)
491  	                  Operations:
492  	                    monitor: OPTest3-monitor-interval-60s
493  	                      interval=60s OCF_CHECK_LEVEL=1
494  	                """
495  	            ),
496  	        )
497  	
498  	        self.assert_pcs_success(
499  	            (
500  	                "resource create --no-default-ops OPTest4 ocf:pcsmock:minimal "
501  	                "op monitor interval=30s"
502  	            ).split(),
503  	        )
504  	
505  	        self.assert_pcs_success(
506  	            "resource update OPTest4 op monitor OCF_CHECK_LEVEL=1".split(),
507  	        )
508  	
509  	        self.assert_pcs_success(
510  	            "resource config OPTest4".split(),
511  	            dedent(
512  	                """\
513  	                Resource: OPTest4 (class=ocf provider=pcsmock type=minimal)
514  	                  Operations:
515  	                    monitor: OPTest4-monitor-interval-60s
516  	                      interval=60s OCF_CHECK_LEVEL=1
517  	                """
518  	            ),
519  	        )
520  	
521  	        self.assert_pcs_success(
522  	            "resource create --no-default-ops OPTest5 ocf:pcsmock:minimal".split(),
523  	        )
524  	
525  	        self.assert_pcs_success(
526  	            "resource update OPTest5 op monitor OCF_CHECK_LEVEL=1".split(),
527  	        )
528  	
529  	        self.assert_pcs_success(
530  	            "resource config OPTest5".split(),
531  	            dedent(
532  	                """\
533  	                Resource: OPTest5 (class=ocf provider=pcsmock type=minimal)
534  	                  Operations:
535  	                    monitor: OPTest5-monitor-interval-60s
536  	                      interval=60s OCF_CHECK_LEVEL=1
537  	                """
538  	            ),
539  	        )
540  	
541  	        self.assert_pcs_success(
542  	            "resource create --no-default-ops OPTest6 ocf:pcsmock:minimal".split(),
543  	        )
544  	
545  	        self.assert_pcs_success(
546  	            "resource op add OPTest6 monitor interval=30s OCF_CHECK_LEVEL=1".split(),
547  	        )
548  	
549  	        self.assert_pcs_success(
550  	            "resource config OPTest6".split(),
551  	            dedent(
552  	                """\
553  	                Resource: OPTest6 (class=ocf provider=pcsmock type=minimal)
554  	                  Operations:
555  	                    monitor: OPTest6-monitor-interval-10s
556  	                      interval=10s timeout=20s
557  	                    monitor: OPTest6-monitor-interval-30s
558  	                      interval=30s OCF_CHECK_LEVEL=1
559  	                """
560  	            ),
561  	        )
562  	
563  	        self.assert_pcs_success(
564  	            "resource create --no-default-ops OPTest7 ocf:pcsmock:minimal".split(),
565  	        )
566  	
567  	        self.assert_pcs_success(
568  	            "resource update OPTest7 op monitor interval=60s OCF_CHECK_LEVEL=1".split(),
569  	        )
570  	
571  	        self.assert_pcs_fail(
572  	            "resource op add OPTest7 monitor interval=61s OCF_CHECK_LEVEL=1".split(),
573  	            (
574  	                "Error: operation monitor already specified for OPTest7, use --force to override:\n"
575  	                "monitor interval=60s OCF_CHECK_LEVEL=1 (OPTest7-monitor-interval-60s)\n"
576  	            ),
577  	        )
578  	
579  	        self.assert_pcs_success(
580  	            "resource op add OPTest7 monitor interval=61s OCF_CHECK_LEVEL=1 --force".split(),
581  	        )
582  	
583  	        self.assert_pcs_success(
584  	            "resource config OPTest7".split(),
585  	            dedent(
586  	                """\
587  	                Resource: OPTest7 (class=ocf provider=pcsmock type=minimal)
588  	                  Operations:
589  	                    monitor: OPTest7-monitor-interval-60s
590  	                      interval=60s OCF_CHECK_LEVEL=1
591  	                    monitor: OPTest7-monitor-interval-61s
592  	                      interval=61s OCF_CHECK_LEVEL=1
593  	                """
594  	            ),
595  	        )
596  	
597  	        self.assert_pcs_fail(
598  	            "resource op add OPTest7 monitor interval=60s OCF_CHECK_LEVEL=1".split(),
599  	            (
600  	                "Error: operation monitor with interval 60s already specified for OPTest7:\n"
601  	                "monitor interval=60s OCF_CHECK_LEVEL=1 (OPTest7-monitor-interval-60s)\n"
602  	            ),
603  	        )
604  	
605  	        self.assert_pcs_success(
606  	            "resource create --no-default-ops OCFTest1 ocf:pcsmock:minimal".split(),
607  	        )
608  	
609  	        self.assert_pcs_fail(
610  	            "resource op add OCFTest1 monitor interval=31s".split(),
611  	            (
612  	                "Error: operation monitor already specified for OCFTest1, use --force to override:\n"
613  	                "monitor interval=10s timeout=20s (OCFTest1-monitor-interval-10s)\n"
614  	            ),
615  	        )
616  	
617  	        self.assert_pcs_success(
618  	            "resource op add OCFTest1 monitor interval=31s --force".split(),
619  	        )
620  	
621  	        self.assert_pcs_success(
622  	            "resource op add OCFTest1 monitor interval=30s OCF_CHECK_LEVEL=15".split(),
623  	        )
624  	
625  	        self.assert_pcs_success(
626  	            "resource config OCFTest1".split(),
627  	            dedent(
628  	                """\
629  	                Resource: OCFTest1 (class=ocf provider=pcsmock type=minimal)
630  	                  Operations:
631  	                    monitor: OCFTest1-monitor-interval-10s
632  	                      interval=10s timeout=20s
633  	                    monitor: OCFTest1-monitor-interval-31s
634  	                      interval=31s
635  	                    monitor: OCFTest1-monitor-interval-30s
636  	                      interval=30s OCF_CHECK_LEVEL=15
637  	                """
638  	            ),
639  	        )
640  	
641  	        self.assert_pcs_success(
642  	            "resource update OCFTest1 op monitor interval=61s OCF_CHECK_LEVEL=5".split(),
643  	        )
644  	
645  	        self.assert_pcs_success(
646  	            "resource config OCFTest1".split(),
647  	            dedent(
648  	                """\
649  	                Resource: OCFTest1 (class=ocf provider=pcsmock type=minimal)
650  	                  Operations:
651  	                    monitor: OCFTest1-monitor-interval-61s
652  	                      interval=61s OCF_CHECK_LEVEL=5
653  	                    monitor: OCFTest1-monitor-interval-31s
654  	                      interval=31s
655  	                    monitor: OCFTest1-monitor-interval-30s
656  	                      interval=30s OCF_CHECK_LEVEL=15
657  	                """
658  	            ),
659  	        )
660  	
661  	        self.assert_pcs_success(
662  	            "resource update OCFTest1 op monitor OCF_CHECK_LEVEL=4".split(),
663  	        )
664  	
665  	        self.assert_pcs_success(
666  	            "resource config OCFTest1".split(),
667  	            dedent(
668  	                """\
669  	                Resource: OCFTest1 (class=ocf provider=pcsmock type=minimal)
670  	                  Operations:
671  	                    monitor: OCFTest1-monitor-interval-60s
672  	                      interval=60s OCF_CHECK_LEVEL=4
673  	                    monitor: OCFTest1-monitor-interval-31s
674  	                      interval=31s
675  	                    monitor: OCFTest1-monitor-interval-30s
676  	                      interval=30s OCF_CHECK_LEVEL=15
677  	                """
678  	            ),
679  	        )
680  	
681  	        self.assert_pcs_success(
682  	            "resource update OCFTest1 op monitor OCF_CHECK_LEVEL=4 interval=35s".split(),
683  	        )
684  	
685  	        self.assert_pcs_success(
686  	            "resource config OCFTest1".split(),
687  	            dedent(
688  	                """\
689  	                Resource: OCFTest1 (class=ocf provider=pcsmock type=minimal)
690  	                  Operations:
691  	                    monitor: OCFTest1-monitor-interval-35s
692  	                      interval=35s OCF_CHECK_LEVEL=4
693  	                    monitor: OCFTest1-monitor-interval-31s
694  	                      interval=31s
695  	                    monitor: OCFTest1-monitor-interval-30s
696  	                      interval=30s OCF_CHECK_LEVEL=15
697  	                """
698  	            ),
699  	        )
700  	
701  	        self.assert_pcs_success(
702  	            "resource create --no-default-ops state ocf:pcsmock:stateful".split(),
703  	        )
704  	
705  	        self.assert_pcs_fail(
706  	            "resource op add state monitor interval=10".split(),
707  	            (
708  	                "Error: operation monitor with interval 10s already specified for state:\n"
709  	                "monitor interval=10s role=Master timeout=20s (state-monitor-interval-10s)\n"
710  	            ),
711  	        )
712  	
713  	        self.assert_pcs_fail(
714  	            "resource op add state monitor interval=10 role=Started".split(),
715  	            (
716  	                "Error: operation monitor with interval 10s already specified for state:\n"
717  	                "monitor interval=10s role=Master timeout=20s (state-monitor-interval-10s)\n"
718  	            ),
719  	        )
720  	
721  	        self.assert_pcs_success(
722  	            "resource op add state monitor interval=15 role=Master --force".split()
723  	        )
724  	
725  	        self.assert_pcs_success(
726  	            "resource config state".split(),
727  	            dedent(
728  	                f"""\
729  	                Resource: state (class=ocf provider=pcsmock type=stateful)
730  	                  Operations:
731  	                    monitor: state-monitor-interval-10s
732  	                      interval=10s timeout=20s role={const.PCMK_ROLE_PROMOTED}
733  	                    monitor: state-monitor-interval-11s
734  	                      interval=11s timeout=20s role={const.PCMK_ROLE_UNPROMOTED}
735  	                    monitor: state-monitor-interval-15
736  	                      interval=15 role={const.PCMK_ROLE_PROMOTED}
737  	                """
738  	            ),
739  	        )
740  	
741  	    @skip_unless_pacemaker_supports_op_onfail_demote()
742  	    def test_add_operation_onfail_demote_upgrade_cib(self):
743  	        write_file_to_tmpfile(rc("cib-empty-3.3.xml"), self.temp_cib)
744  	        self.assert_pcs_success(
745  	            "resource create --no-default-ops R ocf:pcsmock:minimal".split()
746  	        )
747  	        self.assert_pcs_success(
748  	            "resource op add R start on-fail=demote".split(),
749  	            stderr_full="Cluster CIB has been upgraded to latest version\n",
750  	        )
751  	
752  	    @skip_unless_pacemaker_supports_op_onfail_demote()
753  	    def test_update_add_operation_onfail_demote_upgrade_cib(self):
754  	        write_file_to_tmpfile(rc("cib-empty-3.3.xml"), self.temp_cib)
755  	        self.assert_pcs_success(
756  	            "resource create --no-default-ops R ocf:pcsmock:minimal".split()
757  	        )
758  	        self.assert_pcs_success(
759  	            "resource update R op start on-fail=demote".split(),
760  	            stderr_full="Cluster CIB has been upgraded to latest version\n",
761  	        )
762  	
763  	    def _test_delete_remove_operation(self, command):
764  	        assert command in {"delete", "remove"}
765  	
766  	        self.assert_pcs_success(
767  	            (
768  	                "resource create --no-default-ops ClusterIP ocf:pcsmock:minimal"
769  	                " op monitor interval=30s"
770  	            ).split()
771  	        )
772  	
773  	        self.assert_pcs_success(
774  	            "resource op add ClusterIP monitor interval=31s --force".split()
775  	        )
776  	
777  	        self.assert_pcs_success(
778  	            "resource op add ClusterIP monitor interval=32s --force".split()
779  	        )
780  	
781  	        self.assert_pcs_fail(
782  	            f"resource op {command} ClusterIP-monitor-interval-32s-xxxxx".split(),
783  	            (
784  	                "Error: unable to find operation id: "
785  	                "ClusterIP-monitor-interval-32s-xxxxx\n"
786  	            ),
787  	        )
788  	
789  	        self.assert_pcs_success(
790  	            f"resource op {command} ClusterIP-monitor-interval-32s".split()
791  	        )
792  	
793  	        self.assert_pcs_success(
794  	            f"resource op {command} ClusterIP monitor interval=30s".split()
795  	        )
796  	
797  	        self.assert_pcs_fail(
798  	            f"resource op {command} ClusterIP monitor interval=30s".split(),
799  	            "Error: Unable to find operation matching: monitor interval=30s\n",
800  	        )
801  	
802  	        self.assert_pcs_success(
803  	            "resource config ClusterIP".split(),
804  	            dedent(
805  	                """\
806  	                Resource: ClusterIP (class=ocf provider=pcsmock type=minimal)
807  	                  Operations:
808  	                    monitor: ClusterIP-monitor-interval-31s
809  	                      interval=31s
810  	                """
811  	            ),
812  	        )
813  	
814  	        self.assert_pcs_success(
815  	            f"resource op {command} ClusterIP monitor interval=31s".split()
816  	        )
817  	
818  	        self.assert_pcs_success(
819  	            "resource config ClusterIP".split(),
820  	            dedent(
821  	                """\
822  	                Resource: ClusterIP (class=ocf provider=pcsmock type=minimal)
823  	                """
824  	            ),
825  	        )
826  	
827  	        self.assert_pcs_success(
828  	            "resource op add ClusterIP monitor interval=31s".split()
829  	        )
830  	
831  	        self.assert_pcs_success(
832  	            "resource op add ClusterIP monitor interval=32s --force".split()
833  	        )
834  	
835  	        self.assert_pcs_success(
836  	            "resource op add ClusterIP stop timeout=34s".split()
837  	        )
838  	
839  	        self.assert_pcs_success(
840  	            "resource op add ClusterIP start timeout=33s".split()
841  	        )
842  	
843  	        self.assert_pcs_success(
844  	            f"resource op {command} ClusterIP monitor".split()
845  	        )
846  	
847  	        self.assert_pcs_success(
848  	            "resource config ClusterIP".split(),
849  	            dedent(
850  	                """\
851  	                Resource: ClusterIP (class=ocf provider=pcsmock type=minimal)
852  	                  Operations:
853  	                    stop: ClusterIP-stop-interval-0s
854  	                      interval=0s timeout=34s
855  	                    start: ClusterIP-start-interval-0s
856  	                      interval=0s timeout=33s
857  	                """
858  	            ),
859  	        )
860  	
861  	    def test_delete_operation(self):
862  	        # see also BundleMiscCommands
863  	        self.assert_pcs_fail(
864  	            "resource op delete".split(),
865  	            stderr_start="\nUsage: pcs resource op delete...",
866  	        )
867  	
868  	        self._test_delete_remove_operation("delete")
869  	
870  	    def test_remove_operation(self):
871  	        # see also BundleMiscCommands
872  	        self.assert_pcs_fail(
873  	            "resource op remove".split(),
874  	            stderr_start="\nUsage: pcs resource op remove...",
875  	        )
876  	
877  	        self._test_delete_remove_operation("remove")
878  	
879  	    def test_update_operation(self):
880  	        self.assert_pcs_success(
881  	            (
882  	                "resource create --no-default-ops ClusterIP ocf:pcsmock:params"
883  	                " mandatory=value op monitor interval=30s"
884  	            ).split()
885  	        )
886  	        self.assert_pcs_success(
887  	            "resource config ClusterIP".split(),
888  	            dedent(
889  	                """\
890  	                Resource: ClusterIP (class=ocf provider=pcsmock type=params)
891  	                  Attributes: ClusterIP-instance_attributes
892  	                    mandatory=value
893  	                  Operations:
894  	                    monitor: ClusterIP-monitor-interval-30s
895  	                      interval=30s
896  	                """
897  	            ),
898  	        )
899  	
900  	        self.assert_pcs_success(
901  	            "resource update ClusterIP op monitor interval=32s".split()
902  	        )
903  	        self.assert_pcs_success(
904  	            "resource config ClusterIP".split(),
905  	            dedent(
906  	                """\
907  	                Resource: ClusterIP (class=ocf provider=pcsmock type=params)
908  	                  Attributes: ClusterIP-instance_attributes
909  	                    mandatory=value
910  	                  Operations:
911  	                    monitor: ClusterIP-monitor-interval-32s
912  	                      interval=32s
913  	                """
914  	            ),
915  	        )
916  	
917  	        show_clusterip = dedent(
918  	            """\
919  	            Resource: ClusterIP (class=ocf provider=pcsmock type=params)
920  	              Attributes: ClusterIP-instance_attributes
921  	                mandatory=value
922  	              Operations:
923  	                monitor: ClusterIP-monitor-interval-33s
924  	                  interval=33s
925  	                start: ClusterIP-start-interval-30s
926  	                  interval=30s timeout=180s
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(
937  	            "resource update ClusterIP op monitor interval=33s start interval=30s timeout=180s".split()
938  	        )
939  	        self.assert_pcs_success(
940  	            "resource config ClusterIP".split(), show_clusterip
941  	        )
942  	
943  	        self.assert_pcs_success("resource update ClusterIP op".split())
944  	        self.assert_pcs_success(
945  	            "resource config ClusterIP".split(), show_clusterip
946  	        )
947  	
948  	        self.assert_pcs_success("resource update ClusterIP op monitor".split())
949  	        self.assert_pcs_success(
950  	            "resource config ClusterIP".split(), show_clusterip
951  	        )
952  	
953  	        # test invalid id
954  	        self.assert_pcs_fail_regardless_of_force(
955  	            "resource update ClusterIP op monitor interval=30 id=ab#cd".split(),
956  	            (
957  	                "Error: invalid operation id 'ab#cd', '#' is not a valid character"
958  	                " for a operation id\n"
959  	            ),
960  	        )
961  	        self.assert_pcs_success(
962  	            "resource config ClusterIP".split(), show_clusterip
963  	        )
964  	
965  	        # test existing id
966  	        self.assert_pcs_fail_regardless_of_force(
967  	            "resource update ClusterIP op monitor interval=30 id=ClusterIP".split(),
968  	            (
969  	                "Error: id 'ClusterIP' is already in use, please specify another"
970  	                " one\n"
971  	            ),
972  	        )
973  	        self.assert_pcs_success(
974  	            "resource config ClusterIP".split(), show_clusterip
975  	        )
976  	
977  	        # test id change
978  	        # there is a bug:
979  	        # - first an existing operation is removed
980  	        # - then a new operation is created at the same place
981  	        # - therefore options not specified for in the command are removed
982  	        #    instead of them being kept from the old operation
983  	        # This needs to be fixed. However it's not my task currently.
984  	        # Moreover it is documented behavior.
985  	        self.assert_pcs_success(
986  	            "resource update ClusterIP op monitor id=abcd".split()
987  	        )
988  	        self.assert_pcs_success(
989  	            "resource config ClusterIP".split(),
990  	            dedent(
991  	                """\
992  	                Resource: ClusterIP (class=ocf provider=pcsmock type=params)
993  	                  Attributes: ClusterIP-instance_attributes
994  	                    mandatory=value
995  	                  Operations:
996  	                    monitor: abcd
997  	                      interval=60s
998  	                    start: ClusterIP-start-interval-30s
999  	                      interval=30s timeout=180s
1000 	                """
1001 	            ),
1002 	        )
1003 	
1004 	        # test two monitor operations:
1005 	        # - the first one is updated
1006 	        # - operation duplicity detection test
1007 	        self.assert_pcs_success(
1008 	            "resource create A ocf:pcsmock:minimal op monitor interval=10 op monitor interval=20".split()
1009 	        )
1010 	        self.assert_pcs_success(
1011 	            "resource config A".split(),
1012 	            dedent(
1013 	                """\
1014 	                Resource: A (class=ocf provider=pcsmock type=minimal)
1015 	                  Operations:
1016 	                    migrate_from: A-migrate_from-interval-0s
1017 	                      interval=0s timeout=20s
1018 	                    migrate_to: A-migrate_to-interval-0s
1019 	                      interval=0s timeout=20s
1020 	                    monitor: A-monitor-interval-10
1021 	                      interval=10
1022 	                    monitor: A-monitor-interval-20
1023 	                      interval=20
1024 	                    reload: A-reload-interval-0s
1025 	                      interval=0s timeout=20s
1026 	                    reload-agent: A-reload-agent-interval-0s
1027 	                      interval=0s timeout=20s
1028 	                    start: A-start-interval-0s
1029 	                      interval=0s timeout=20s
1030 	                    stop: A-stop-interval-0s
1031 	                      interval=0s timeout=20s
1032 	                """
1033 	            ),
1034 	        )
1035 	
1036 	        self.assert_pcs_fail(
1037 	            "resource update A op monitor interval=20".split(),
1038 	            (
1039 	                "Error: operation monitor with interval 20s already specified for A:\n"
1040 	                "monitor interval=20 (A-monitor-interval-20)\n"
1041 	            ),
1042 	        )
1043 	
1044 	        self.assert_pcs_success(
1045 	            "resource update A op monitor interval=11".split(),
1046 	        )
1047 	
1048 	        self.assert_pcs_success(
1049 	            "resource config A".split(),
1050 	            dedent(
1051 	                """\
1052 	                Resource: A (class=ocf provider=pcsmock type=minimal)
1053 	                  Operations:
1054 	                    migrate_from: A-migrate_from-interval-0s
1055 	                      interval=0s timeout=20s
1056 	                    migrate_to: A-migrate_to-interval-0s
1057 	                      interval=0s timeout=20s
1058 	                    monitor: A-monitor-interval-11
1059 	                      interval=11
1060 	                    monitor: A-monitor-interval-20
1061 	                      interval=20
1062 	                    reload: A-reload-interval-0s
1063 	                      interval=0s timeout=20s
1064 	                    reload-agent: A-reload-agent-interval-0s
1065 	                      interval=0s timeout=20s
1066 	                    start: A-start-interval-0s
1067 	                      interval=0s timeout=20s
1068 	                    stop: A-stop-interval-0s
1069 	                      interval=0s timeout=20s
1070 	                """
1071 	            ),
1072 	        )
1073 	
1074 	        self.assert_pcs_success(
1075 	            "resource create B ocf:pcsmock:minimal --no-default-ops".split(),
1076 	        )
1077 	
1078 	        self.assert_pcs_success(
1079 	            "resource op remove B-monitor-interval-10s".split()
1080 	        )
1081 	
1082 	        self.assert_pcs_success(
1083 	            "resource config B".split(),
1084 	            "Resource: B (class=ocf provider=pcsmock type=minimal)\n",
1085 	        )
1086 	
1087 	        self.assert_pcs_success(
1088 	            "resource update B op monitor interval=60s".split(),
1089 	        )
1090 	
1091 	        self.assert_pcs_success(
1092 	            "resource config B".split(),
1093 	            dedent(
1094 	                """\
1095 	                Resource: B (class=ocf provider=pcsmock type=minimal)
1096 	                  Operations:
1097 	                    monitor: B-monitor-interval-60s
1098 	                      interval=60s
1099 	                """
1100 	            ),
1101 	        )
1102 	
1103 	        self.assert_pcs_success(
1104 	            "resource update B op monitor interval=30".split(),
1105 	        )
1106 	
1107 	        self.assert_pcs_success(
1108 	            "resource config B".split(),
1109 	            dedent(
1110 	                """\
1111 	                Resource: B (class=ocf provider=pcsmock type=minimal)
1112 	                  Operations:
1113 	                    monitor: B-monitor-interval-30
1114 	                      interval=30
1115 	                """
1116 	            ),
1117 	        )
1118 	
1119 	        self.assert_pcs_success(
1120 	            "resource update B op start interval=0 timeout=10".split(),
1121 	        )
1122 	
1123 	        self.assert_pcs_success(
1124 	            "resource config B".split(),
1125 	            dedent(
1126 	                """\
1127 	                Resource: B (class=ocf provider=pcsmock type=minimal)
1128 	                  Operations:
1129 	                    monitor: B-monitor-interval-30
1130 	                      interval=30
1131 	                    start: B-start-interval-0
1132 	                      interval=0 timeout=10
1133 	                """
1134 	            ),
1135 	        )
1136 	
1137 	        self.assert_pcs_success(
1138 	            "resource update B op start interval=0 timeout=20".split(),
1139 	        )
1140 	
1141 	        self.assert_pcs_success(
1142 	            "resource config B".split(),
1143 	            dedent(
1144 	                """\
1145 	                Resource: B (class=ocf provider=pcsmock type=minimal)
1146 	                  Operations:
1147 	                    monitor: B-monitor-interval-30
1148 	                      interval=30
1149 	                    start: B-start-interval-0
1150 	                      interval=0 timeout=20
1151 	                """
1152 	            ),
1153 	        )
1154 	
1155 	        self.assert_pcs_success(
1156 	            "resource update B op monitor interval=33".split(),
1157 	        )
1158 	
1159 	        self.assert_pcs_success(
1160 	            "resource config B".split(),
1161 	            dedent(
1162 	                """\
1163 	                Resource: B (class=ocf provider=pcsmock type=minimal)
1164 	                  Operations:
1165 	                    monitor: B-monitor-interval-33
1166 	                      interval=33
1167 	                    start: B-start-interval-0
1168 	                      interval=0 timeout=20
1169 	                """
1170 	            ),
1171 	        )
1172 	
1173 	        self.assert_pcs_fail(
1174 	            "resource update B op monitor interval=100 role=Master".split(),
1175 	            "Error: role must be: {} (use --force to override)\n".format(
1176 	                format_list_custom_last_separator(const.PCMK_ROLES, " or ")
1177 	            ),
1178 	        )
1179 	
1180 	        self.assert_pcs_success(
1181 	            "resource update B op monitor interval=100 role=Promoted".split(),
1182 	        )
1183 	
1184 	        self.assert_pcs_success(
1185 	            "resource config B".split(),
1186 	            dedent(
1187 	                f"""\
1188 	                Resource: B (class=ocf provider=pcsmock type=minimal)
1189 	                  Operations:
1190 	                    monitor: B-monitor-interval-33
1191 	                      interval=33
1192 	                    start: B-start-interval-0
1193 	                      interval=0 timeout=20
1194 	                    monitor: B-monitor-interval-100
1195 	                      interval=100 role={const.PCMK_ROLE_PROMOTED}
1196 	                """
1197 	            ),
1198 	        )
1199 	
1200 	        self.assert_pcs_success(
1201 	            "resource update B op start interval=0 timeout=22".split(),
1202 	        )
1203 	
1204 	        self.assert_pcs_success(
1205 	            "resource config B".split(),
1206 	            dedent(
1207 	                f"""\
1208 	                Resource: B (class=ocf provider=pcsmock type=minimal)
1209 	                  Operations:
1210 	                    monitor: B-monitor-interval-33
1211 	                      interval=33
1212 	                    start: B-start-interval-0
1213 	                      interval=0 timeout=22
1214 	                    monitor: B-monitor-interval-100
1215 	                      interval=100 role={const.PCMK_ROLE_PROMOTED}
1216 	                """
1217 	            ),
1218 	        )
1219 	
1220 	    @skip_unless_crm_rule()
1221 	    def test_group_ungroup(self):
1222 	        self.assert_pcs_success(
1223 	            "resource create --no-default-ops A1 ocf:pcsmock:minimal".split(),
1224 	        )
1225 	        self.assert_pcs_success(
1226 	            "resource create --no-default-ops A2 ocf:pcsmock:minimal".split(),
1227 	        )
1228 	        self.assert_pcs_success(
1229 	            "resource create --no-default-ops A3 ocf:pcsmock:minimal".split(),
1230 	        )
1231 	        self.assert_pcs_success(
1232 	            "resource create --no-default-ops A4 ocf:pcsmock:minimal".split(),
1233 	        )
1234 	        self.assert_pcs_success(
1235 	            "resource create --no-default-ops A5 ocf:pcsmock:minimal".split(),
1236 	        )
1237 	
1238 	        self.assert_pcs_success(
1239 	            "resource group add AGroup A1 A2 A3 A4 A5".split(),
1240 	        )
1241 	
1242 	        self.assert_pcs_success(
1243 	            "resource config AGroup".split(),
1244 	            dedent(
1245 	                """\
1246 	                Group: AGroup
1247 	                  Resource: A1 (class=ocf provider=pcsmock type=minimal)
1248 	                    Operations:
1249 	                      monitor: A1-monitor-interval-10s
1250 	                        interval=10s timeout=20s
1251 	                  Resource: A2 (class=ocf provider=pcsmock type=minimal)
1252 	                    Operations:
1253 	                      monitor: A2-monitor-interval-10s
1254 	                        interval=10s timeout=20s
1255 	                  Resource: A3 (class=ocf provider=pcsmock type=minimal)
1256 	                    Operations:
1257 	                      monitor: A3-monitor-interval-10s
1258 	                        interval=10s timeout=20s
1259 	                  Resource: A4 (class=ocf provider=pcsmock type=minimal)
1260 	                    Operations:
1261 	                      monitor: A4-monitor-interval-10s
1262 	                        interval=10s timeout=20s
1263 	                  Resource: A5 (class=ocf provider=pcsmock type=minimal)
1264 	                    Operations:
1265 	                      monitor: A5-monitor-interval-10s
1266 	                        interval=10s timeout=20s
1267 	                """
1268 	            ),
1269 	        )
1270 	
1271 	    def test_group_order(self):
1272 	        # This was considered for removing during 'resource group add' command
1273 	        # and tests overhaul. However, this is the only test where "resource
1274 	        # group list" is called. Due to that this test was not deleted.
1275 	        self.assert_pcs_success(
1276 	            "resource create --no-default-ops A ocf:pcsmock:minimal".split(),
1277 	        )
1278 	        self.assert_pcs_success(
1279 	            "resource create --no-default-ops B ocf:pcsmock:minimal".split(),
1280 	        )
1281 	        self.assert_pcs_success(
1282 	            "resource create --no-default-ops C ocf:pcsmock:minimal".split(),
1283 	        )
1284 	        self.assert_pcs_success(
1285 	            "resource create --no-default-ops D ocf:pcsmock:minimal".split(),
1286 	        )
1287 	        self.assert_pcs_success(
1288 	            "resource create --no-default-ops E ocf:pcsmock:minimal".split(),
1289 	        )
1290 	        self.assert_pcs_success(
1291 	            "resource create --no-default-ops F ocf:pcsmock:minimal".split(),
1292 	        )
1293 	        self.assert_pcs_success(
1294 	            "resource create --no-default-ops G ocf:pcsmock:minimal".split(),
1295 	        )
1296 	        self.assert_pcs_success(
1297 	            "resource create --no-default-ops H ocf:pcsmock:minimal".split(),
1298 	        )
1299 	        self.assert_pcs_success(
1300 	            "resource create --no-default-ops I ocf:pcsmock:minimal".split(),
1301 	        )
1302 	        self.assert_pcs_success(
1303 	            "resource create --no-default-ops J ocf:pcsmock:minimal".split(),
1304 	        )
1305 	        self.assert_pcs_success(
1306 	            "resource create --no-default-ops K ocf:pcsmock:minimal".split(),
1307 	        )
1308 	
1309 	        self.assert_pcs_success(
1310 	            "resource group add RGA A B C E D K J I".split()
1311 	        )
1312 	
1313 	        stdout, stderr, returncode = self.pcs_runner.run(["resource"])
1314 	        self.assertEqual(stderr, "")
1315 	        self.assertEqual(returncode, 0)
1316 	        if is_pacemaker_21_without_20_compatibility():
1317 	            self.assertEqual(
1318 	                stdout,
1319 	                outdent(
1320 	                    """\
1321 	                      * F\t(ocf:pcsmock:minimal):\t Stopped
1322 	                      * G\t(ocf:pcsmock:minimal):\t Stopped
1323 	                      * H\t(ocf:pcsmock:minimal):\t Stopped
1324 	                      * Resource Group: RGA:
1325 	                        * A\t(ocf:pcsmock:minimal):\t Stopped
1326 	                        * B\t(ocf:pcsmock:minimal):\t Stopped
1327 	                        * C\t(ocf:pcsmock:minimal):\t Stopped
1328 	                        * E\t(ocf:pcsmock:minimal):\t Stopped
1329 	                        * D\t(ocf:pcsmock:minimal):\t Stopped
1330 	                        * K\t(ocf:pcsmock:minimal):\t Stopped
1331 	                        * J\t(ocf:pcsmock:minimal):\t Stopped
1332 	                        * I\t(ocf:pcsmock:minimal):\t Stopped
1333 	                    """
1334 	                ),
1335 	            )
1336 	        elif PCMK_2_0_3_PLUS:
1337 	            assert_pcs_status(
1338 	                stdout,
1339 	                """\
1340 	  * F\t(ocf::pcsmock:minimal):\tStopped
1341 	  * G\t(ocf::pcsmock:minimal):\tStopped
1342 	  * H\t(ocf::pcsmock:minimal):\tStopped
1343 	  * Resource Group: RGA:
1344 	    * A\t(ocf::pcsmock:minimal):\tStopped
1345 	    * B\t(ocf::pcsmock:minimal):\tStopped
1346 	    * C\t(ocf::pcsmock:minimal):\tStopped
1347 	    * E\t(ocf::pcsmock:minimal):\tStopped
1348 	    * D\t(ocf::pcsmock:minimal):\tStopped
1349 	    * K\t(ocf::pcsmock:minimal):\tStopped
1350 	    * J\t(ocf::pcsmock:minimal):\tStopped
1351 	    * I\t(ocf::pcsmock:minimal):\tStopped
1352 	""",
1353 	            )
1354 	        else:
1355 	            self.assertEqual(
1356 	                stdout,
1357 	                """\
1358 	 F\t(ocf::pcsmock:minimal):\tStopped
1359 	 G\t(ocf::pcsmock:minimal):\tStopped
1360 	 H\t(ocf::pcsmock:minimal):\tStopped
1361 	 Resource Group: RGA
1362 	     A\t(ocf::pcsmock:minimal):\tStopped
1363 	     B\t(ocf::pcsmock:minimal):\tStopped
1364 	     C\t(ocf::pcsmock:minimal):\tStopped
1365 	     E\t(ocf::pcsmock:minimal):\tStopped
1366 	     D\t(ocf::pcsmock:minimal):\tStopped
1367 	     K\t(ocf::pcsmock:minimal):\tStopped
1368 	     J\t(ocf::pcsmock:minimal):\tStopped
1369 	     I\t(ocf::pcsmock:minimal):\tStopped
1370 	""",
1371 	            )
1372 	
1373 	        self.assert_pcs_success(
1374 	            "resource group list".split(), "RGA: A B C E D K J I\n"
1375 	        )
1376 	
1377 	    @skip_unless_crm_rule()
1378 	    def test_cluster_config(self):
1379 	        self.setup_cluster_a()
1380 	
1381 	        self.pcs_runner.mock_settings = {
1382 	            "corosync_conf_file": rc("corosync.conf"),
1383 	        }
1384 	        self.assert_pcs_success(
1385 	            ["config"],
1386 	            dedent(
1387 	                """\
1388 	                Cluster Name: test99
1389 	                Corosync Nodes:
1390 	                 rh7-1 rh7-2
1391 	                Pacemaker Nodes:
1392 	
1393 	                Resources:
1394 	                  Resource: ClusterIP6 (class=ocf provider=pcsmock type=minimal)
1395 	                    Operations:
1396 	                      monitor: ClusterIP6-monitor-interval-10s
1397 	                        interval=10s timeout=20s
1398 	                  Group: TestGroup1
1399 	                    Resource: ClusterIP (class=ocf provider=pcsmock type=minimal)
1400 	                      Operations:
1401 	                        monitor: ClusterIP-monitor-interval-10s
1402 	                          interval=10s timeout=20s
1403 	                  Group: TestGroup2
1404 	                    Resource: ClusterIP2 (class=ocf provider=pcsmock type=minimal)
1405 	                      Operations:
1406 	                        monitor: ClusterIP2-monitor-interval-10s
1407 	                          interval=10s timeout=20s
1408 	                    Resource: ClusterIP3 (class=ocf provider=pcsmock type=minimal)
1409 	                      Operations:
1410 	                        monitor: ClusterIP3-monitor-interval-10s
1411 	                          interval=10s timeout=20s
1412 	                  Clone: ClusterIP4-clone
1413 	                    Resource: ClusterIP4 (class=ocf provider=pcsmock type=minimal)
1414 	                      Operations:
1415 	                        monitor: ClusterIP4-monitor-interval-10s
1416 	                          interval=10s timeout=20s
1417 	                  Clone: Master
1418 	                    Meta Attributes:
1419 	                      promotable=true
1420 	                    Resource: ClusterIP5 (class=ocf provider=pcsmock type=minimal)
1421 	                      Operations:
1422 	                        monitor: ClusterIP5-monitor-interval-10s
1423 	                          interval=10s timeout=20s
1424 	                """
1425 	            ),
1426 	        )
1427 	
1428 	    def test_ms_group(self):
1429 	        self.assert_pcs_success(
1430 	            "resource create --no-default-ops D0 ocf:pcsmock:minimal".split(),
1431 	        )
1432 	        self.assert_pcs_success(
1433 	            "resource create --no-default-ops D1 ocf:pcsmock:minimal".split(),
1434 	        )
1435 	        self.assert_pcs_success("resource group add Group D0 D1".split())
1436 	
1437 	        # pcs no longer allows turning resources into masters but supports
1438 	        # existing ones. In order to test it, we need to put a master in the
1439 	        # CIB without pcs.
1440 	        wrap_element_by_master(self.temp_cib, "Group", master_id="GroupMaster")
1441 	
1442 	        self.assert_pcs_success(
1443 	            "resource config".split(),
1444 	            dedent(
1445 	                """\
1446 	                Clone: GroupMaster
1447 	                  Meta Attributes:
1448 	                    promotable=true
1449 	                  Group: Group
1450 	                    Resource: D0 (class=ocf provider=pcsmock type=minimal)
1451 	                      Operations:
1452 	                        monitor: D0-monitor-interval-10s
1453 	                          interval=10s timeout=20s
1454 	                    Resource: D1 (class=ocf provider=pcsmock type=minimal)
1455 	                      Operations:
1456 	                        monitor: D1-monitor-interval-10s
1457 	                          interval=10s timeout=20s
1458 	                """
1459 	            ),
1460 	        )
1461 	
1462 	    def test_unclone(self):
1463 	        # see also BundleClone
1464 	        self.assert_pcs_success(
1465 	            "resource create --no-default-ops dummy1 ocf:pcsmock:minimal".split()
1466 	        )
1467 	        self.assert_pcs_success(
1468 	            "resource create --no-default-ops dummy2 ocf:pcsmock:minimal".split(),
1469 	        )
1470 	        self.assert_pcs_success("resource group add gr dummy1".split())
1471 	
1472 	        self.assert_pcs_fail(
1473 	            "resource unclone gr".split(),
1474 	            "Error: 'gr' is not a clone resource\n",
1475 	        )
1476 	
1477 	        # unclone with a clone itself specified
1478 	        self.assert_pcs_success("resource group add gr dummy2".split())
1479 	        self.assert_pcs_success("resource clone gr".split())
1480 	        self.assert_pcs_success(
1481 	            "resource config".split(),
1482 	            dedent(
1483 	                """\
1484 	                Clone: gr-clone
1485 	                  Group: gr
1486 	                    Resource: dummy1 (class=ocf provider=pcsmock type=minimal)
1487 	                      Operations:
1488 	                        monitor: dummy1-monitor-interval-10s
1489 	                          interval=10s timeout=20s
1490 	                    Resource: dummy2 (class=ocf provider=pcsmock type=minimal)
1491 	                      Operations:
1492 	                        monitor: dummy2-monitor-interval-10s
1493 	                          interval=10s timeout=20s
1494 	                """
1495 	            ),
1496 	        )
1497 	
1498 	        self.assert_pcs_success("resource unclone gr-clone".split())
1499 	        self.assert_pcs_success(
1500 	            "resource config".split(),
1501 	            dedent(
1502 	                """\
1503 	                Group: gr
1504 	                  Resource: dummy1 (class=ocf provider=pcsmock type=minimal)
1505 	                    Operations:
1506 	                      monitor: dummy1-monitor-interval-10s
1507 	                        interval=10s timeout=20s
1508 	                  Resource: dummy2 (class=ocf provider=pcsmock type=minimal)
1509 	                    Operations:
1510 	                      monitor: dummy2-monitor-interval-10s
1511 	                        interval=10s timeout=20s
1512 	                """
1513 	            ),
1514 	        )
1515 	
1516 	        # unclone with a cloned group specified
1517 	        self.assert_pcs_success("resource clone gr".split())
1518 	        self.assert_pcs_success(
1519 	            "resource config".split(),
1520 	            dedent(
1521 	                """\
1522 	                Clone: gr-clone
1523 	                  Group: gr
1524 	                    Resource: dummy1 (class=ocf provider=pcsmock type=minimal)
1525 	                      Operations:
1526 	                        monitor: dummy1-monitor-interval-10s
1527 	                          interval=10s timeout=20s
1528 	                    Resource: dummy2 (class=ocf provider=pcsmock type=minimal)
1529 	                      Operations:
1530 	                        monitor: dummy2-monitor-interval-10s
1531 	                          interval=10s timeout=20s
1532 	                """
1533 	            ),
1534 	        )
1535 	
1536 	        self.assert_pcs_success("resource unclone gr".split())
1537 	        self.assert_pcs_success(
1538 	            "resource config".split(),
1539 	            dedent(
1540 	                """\
1541 	                Group: gr
1542 	                  Resource: dummy1 (class=ocf provider=pcsmock type=minimal)
1543 	                    Operations:
1544 	                      monitor: dummy1-monitor-interval-10s
1545 	                        interval=10s timeout=20s
1546 	                  Resource: dummy2 (class=ocf provider=pcsmock type=minimal)
1547 	                    Operations:
1548 	                      monitor: dummy2-monitor-interval-10s
1549 	                        interval=10s timeout=20s
1550 	                """
1551 	            ),
1552 	        )
1553 	
1554 	        # unclone with a cloned grouped resource specified
1555 	        self.assert_pcs_success("resource clone gr".split())
1556 	        self.assert_pcs_success(
1557 	            "resource config".split(),
1558 	            dedent(
1559 	                """\
1560 	                Clone: gr-clone
1561 	                  Group: gr
1562 	                    Resource: dummy1 (class=ocf provider=pcsmock type=minimal)
1563 	                      Operations:
1564 	                        monitor: dummy1-monitor-interval-10s
1565 	                          interval=10s timeout=20s
1566 	                    Resource: dummy2 (class=ocf provider=pcsmock type=minimal)
1567 	                      Operations:
1568 	                        monitor: dummy2-monitor-interval-10s
1569 	                          interval=10s timeout=20s
1570 	                """
1571 	            ),
1572 	        )
1573 	
1574 	        self.assert_pcs_success("resource unclone dummy1".split())
1575 	        self.assert_pcs_success(
1576 	            "resource config".split(),
1577 	            dedent(
1578 	                """\
1579 	                Resource: dummy1 (class=ocf provider=pcsmock type=minimal)
1580 	                  Operations:
1581 	                    monitor: dummy1-monitor-interval-10s
1582 	                      interval=10s timeout=20s
1583 	                Clone: gr-clone
1584 	                  Group: gr
1585 	                    Resource: dummy2 (class=ocf provider=pcsmock type=minimal)
1586 	                      Operations:
1587 	                        monitor: dummy2-monitor-interval-10s
1588 	                          interval=10s timeout=20s
1589 	                """
1590 	            ),
1591 	        )
1592 	
1593 	        self.assert_pcs_success("resource unclone dummy2".split())
1594 	        self.assert_pcs_success(
1595 	            "resource config".split(),
1596 	            dedent(
1597 	                """\
1598 	                Resource: dummy1 (class=ocf provider=pcsmock type=minimal)
1599 	                  Operations:
1600 	                    monitor: dummy1-monitor-interval-10s
1601 	                      interval=10s timeout=20s
1602 	                Resource: dummy2 (class=ocf provider=pcsmock type=minimal)
1603 	                  Operations:
1604 	                    monitor: dummy2-monitor-interval-10s
1605 	                      interval=10s timeout=20s
1606 	                """
1607 	            ),
1608 	        )
1609 	
1610 	    def test_unclone_master(self):
1611 	        # see also BundleClone
1612 	        self.assert_pcs_success(
1613 	            "resource create --no-default-ops dummy1 ocf:pcsmock:stateful".split(),
1614 	        )
1615 	        self.assert_pcs_success(
1616 	            "resource create --no-default-ops dummy2 ocf:pcsmock:stateful".split(),
1617 	        )
1618 	
1619 	        # try to unclone a non-cloned resource
1620 	        self.assert_pcs_fail(
1621 	            "resource unclone dummy1".split(),
1622 	            "Error: 'dummy1' is not a clone resource\n",
1623 	        )
1624 	
1625 	        self.assert_pcs_success("resource group add gr dummy1".split())
1626 	
1627 	        self.assert_pcs_fail(
1628 	            "resource unclone gr".split(),
1629 	            "Error: 'gr' is not a clone resource\n",
1630 	        )
1631 	
1632 	        # unclone with a cloned primitive specified
1633 	        # pcs no longer allows turning resources into masters but supports
1634 	        # existing ones. In order to test it, we need to put a master in the
1635 	        # CIB without pcs.
1636 	        wrap_element_by_master(self.temp_cib, "dummy2")
1637 	        self.assert_pcs_success(
1638 	            "resource config".split(),
1639 	            dedent(
1640 	                f"""\
1641 	                Group: gr
1642 	                  Resource: dummy1 (class=ocf provider=pcsmock type=stateful)
1643 	                    Operations:
1644 	                      monitor: dummy1-monitor-interval-10s
1645 	                        interval=10s timeout=20s role={const.PCMK_ROLE_PROMOTED}
1646 	                      monitor: dummy1-monitor-interval-11s
1647 	                        interval=11s timeout=20s role={const.PCMK_ROLE_UNPROMOTED}
1648 	                Clone: dummy2-master
1649 	                  Meta Attributes:
1650 	                    promotable=true
1651 	                  Resource: dummy2 (class=ocf provider=pcsmock type=stateful)
1652 	                    Operations:
1653 	                      monitor: dummy2-monitor-interval-10s
1654 	                        interval=10s timeout=20s role={const.PCMK_ROLE_PROMOTED}
1655 	                      monitor: dummy2-monitor-interval-11s
1656 	                        interval=11s timeout=20s role={const.PCMK_ROLE_UNPROMOTED}
1657 	                """
1658 	            ),
1659 	        )
1660 	
1661 	        self.assert_pcs_success("resource unclone dummy2".split())
1662 	        self.assert_pcs_success(
1663 	            "resource config".split(),
1664 	            dedent(
1665 	                f"""\
1666 	                Resource: dummy2 (class=ocf provider=pcsmock type=stateful)
1667 	                  Operations:
1668 	                    monitor: dummy2-monitor-interval-10s
1669 	                      interval=10s timeout=20s role={const.PCMK_ROLE_PROMOTED}
1670 	                    monitor: dummy2-monitor-interval-11s
1671 	                      interval=11s timeout=20s role={const.PCMK_ROLE_UNPROMOTED}
1672 	                Group: gr
1673 	                  Resource: dummy1 (class=ocf provider=pcsmock type=stateful)
1674 	                    Operations:
1675 	                      monitor: dummy1-monitor-interval-10s
1676 	                        interval=10s timeout=20s role={const.PCMK_ROLE_PROMOTED}
1677 	                      monitor: dummy1-monitor-interval-11s
1678 	                        interval=11s timeout=20s role={const.PCMK_ROLE_UNPROMOTED}
1679 	                """
1680 	            ),
1681 	        )
1682 	
1683 	        # unclone with a clone itself specified
1684 	        self.assert_pcs_success("resource group add gr dummy2".split())
1685 	        # pcs no longer allows turning resources into masters but supports
1686 	        # existing ones. In order to test it, we need to put a master in the
1687 	        # CIB without pcs.
1688 	        wrap_element_by_master(self.temp_cib, "gr")
1689 	        self.assert_pcs_success(
1690 	            "resource config".split(),
1691 	            dedent(
1692 	                f"""\
1693 	                Clone: gr-master
1694 	                  Meta Attributes:
1695 	                    promotable=true
1696 	                  Group: gr
1697 	                    Resource: dummy1 (class=ocf provider=pcsmock type=stateful)
1698 	                      Operations:
1699 	                        monitor: dummy1-monitor-interval-10s
1700 	                          interval=10s timeout=20s role={const.PCMK_ROLE_PROMOTED}
1701 	                        monitor: dummy1-monitor-interval-11s
1702 	                          interval=11s timeout=20s role={const.PCMK_ROLE_UNPROMOTED}
1703 	                    Resource: dummy2 (class=ocf provider=pcsmock type=stateful)
1704 	                      Operations:
1705 	                        monitor: dummy2-monitor-interval-10s
1706 	                          interval=10s timeout=20s role={const.PCMK_ROLE_PROMOTED}
1707 	                        monitor: dummy2-monitor-interval-11s
1708 	                          interval=11s timeout=20s role={const.PCMK_ROLE_UNPROMOTED}
1709 	                """
1710 	            ),
1711 	        )
1712 	
1713 	        self.assert_pcs_success("resource unclone gr-master".split())
1714 	        self.assert_pcs_success(
1715 	            "resource config".split(),
1716 	            dedent(
1717 	                f"""\
1718 	                Group: gr
1719 	                  Resource: dummy1 (class=ocf provider=pcsmock type=stateful)
1720 	                    Operations:
1721 	                      monitor: dummy1-monitor-interval-10s
1722 	                        interval=10s timeout=20s role={const.PCMK_ROLE_PROMOTED}
1723 	                      monitor: dummy1-monitor-interval-11s
1724 	                        interval=11s timeout=20s role={const.PCMK_ROLE_UNPROMOTED}
1725 	                  Resource: dummy2 (class=ocf provider=pcsmock type=stateful)
1726 	                    Operations:
1727 	                      monitor: dummy2-monitor-interval-10s
1728 	                        interval=10s timeout=20s role={const.PCMK_ROLE_PROMOTED}
1729 	                      monitor: dummy2-monitor-interval-11s
1730 	                        interval=11s timeout=20s role={const.PCMK_ROLE_UNPROMOTED}
1731 	                """
1732 	            ),
1733 	        )
1734 	
1735 	        # unclone with a cloned group specified
1736 	        # pcs no longer allows turning resources into masters but supports
1737 	        # existing ones. In order to test it, we need to put a master in the
1738 	        # CIB without pcs.
1739 	        wrap_element_by_master(self.temp_cib, "gr")
1740 	        self.assert_pcs_success(
1741 	            "resource config".split(),
1742 	            dedent(
1743 	                f"""\
1744 	                Clone: gr-master
1745 	                  Meta Attributes:
1746 	                    promotable=true
1747 	                  Group: gr
1748 	                    Resource: dummy1 (class=ocf provider=pcsmock type=stateful)
1749 	                      Operations:
1750 	                        monitor: dummy1-monitor-interval-10s
1751 	                          interval=10s timeout=20s role={const.PCMK_ROLE_PROMOTED}
1752 	                        monitor: dummy1-monitor-interval-11s
1753 	                          interval=11s timeout=20s role={const.PCMK_ROLE_UNPROMOTED}
1754 	                    Resource: dummy2 (class=ocf provider=pcsmock type=stateful)
1755 	                      Operations:
1756 	                        monitor: dummy2-monitor-interval-10s
1757 	                          interval=10s timeout=20s role={const.PCMK_ROLE_PROMOTED}
1758 	                        monitor: dummy2-monitor-interval-11s
1759 	                          interval=11s timeout=20s role={const.PCMK_ROLE_UNPROMOTED}
1760 	                """
1761 	            ),
1762 	        )
1763 	
1764 	        self.assert_pcs_success("resource unclone gr".split())
1765 	        self.assert_pcs_success(
1766 	            "resource config".split(),
1767 	            dedent(
1768 	                f"""\
1769 	                Group: gr
1770 	                  Resource: dummy1 (class=ocf provider=pcsmock type=stateful)
1771 	                    Operations:
1772 	                      monitor: dummy1-monitor-interval-10s
1773 	                        interval=10s timeout=20s role={const.PCMK_ROLE_PROMOTED}
1774 	                      monitor: dummy1-monitor-interval-11s
1775 	                        interval=11s timeout=20s role={const.PCMK_ROLE_UNPROMOTED}
1776 	                  Resource: dummy2 (class=ocf provider=pcsmock type=stateful)
1777 	                    Operations:
1778 	                      monitor: dummy2-monitor-interval-10s
1779 	                        interval=10s timeout=20s role={const.PCMK_ROLE_PROMOTED}
1780 	                      monitor: dummy2-monitor-interval-11s
1781 	                        interval=11s timeout=20s role={const.PCMK_ROLE_UNPROMOTED}
1782 	                """
1783 	            ),
1784 	        )
1785 	
1786 	        # unclone with a cloned grouped resource specified
1787 	        self.assert_pcs_success("resource ungroup gr dummy2".split())
1788 	        # pcs no longer allows turning resources into masters but supports
1789 	        # existing ones. In order to test it, we need to put a master in the
1790 	        # CIB without pcs.
1791 	        wrap_element_by_master(self.temp_cib, "gr")
1792 	        self.assert_pcs_success(
1793 	            "resource config".split(),
1794 	            dedent(
1795 	                f"""\
1796 	                Resource: dummy2 (class=ocf provider=pcsmock type=stateful)
1797 	                  Operations:
1798 	                    monitor: dummy2-monitor-interval-10s
1799 	                      interval=10s timeout=20s role={const.PCMK_ROLE_PROMOTED}
1800 	                    monitor: dummy2-monitor-interval-11s
1801 	                      interval=11s timeout=20s role={const.PCMK_ROLE_UNPROMOTED}
1802 	                Clone: gr-master
1803 	                  Meta Attributes:
1804 	                    promotable=true
1805 	                  Group: gr
1806 	                    Resource: dummy1 (class=ocf provider=pcsmock type=stateful)
1807 	                      Operations:
1808 	                        monitor: dummy1-monitor-interval-10s
1809 	                          interval=10s timeout=20s role={const.PCMK_ROLE_PROMOTED}
1810 	                        monitor: dummy1-monitor-interval-11s
1811 	                          interval=11s timeout=20s role={const.PCMK_ROLE_UNPROMOTED}
1812 	                """
1813 	            ),
1814 	        )
1815 	
1816 	        self.assert_pcs_success("resource unclone dummy1".split())
1817 	        self.assert_pcs_success(
1818 	            "resource config".split(),
1819 	            dedent(
1820 	                f"""\
1821 	                Resource: dummy2 (class=ocf provider=pcsmock type=stateful)
1822 	                  Operations:
1823 	                    monitor: dummy2-monitor-interval-10s
1824 	                      interval=10s timeout=20s role={const.PCMK_ROLE_PROMOTED}
1825 	                    monitor: dummy2-monitor-interval-11s
1826 	                      interval=11s timeout=20s role={const.PCMK_ROLE_UNPROMOTED}
1827 	                Resource: dummy1 (class=ocf provider=pcsmock type=stateful)
1828 	                  Operations:
1829 	                    monitor: dummy1-monitor-interval-10s
1830 	                      interval=10s timeout=20s role={const.PCMK_ROLE_PROMOTED}
1831 	                    monitor: dummy1-monitor-interval-11s
1832 	                      interval=11s timeout=20s role={const.PCMK_ROLE_UNPROMOTED}
1833 	                """
1834 	            ),
1835 	        )
1836 	
1837 	        self.assert_pcs_success("resource group add gr dummy1 dummy2".split())
1838 	
1839 	        # pcs no longer allows turning resources into masters but supports
1840 	        # existing ones. In order to test it, we need to put a master in the
1841 	        # CIB without pcs.
1842 	        wrap_element_by_master(self.temp_cib, "gr")
1843 	        self.assert_pcs_success(
1844 	            "resource config".split(),
1845 	            dedent(
1846 	                f"""\
1847 	                Clone: gr-master
1848 	                  Meta Attributes:
1849 	                    promotable=true
1850 	                  Group: gr
1851 	                    Resource: dummy1 (class=ocf provider=pcsmock type=stateful)
1852 	                      Operations:
1853 	                        monitor: dummy1-monitor-interval-10s
1854 	                          interval=10s timeout=20s role={const.PCMK_ROLE_PROMOTED}
1855 	                        monitor: dummy1-monitor-interval-11s
1856 	                          interval=11s timeout=20s role={const.PCMK_ROLE_UNPROMOTED}
1857 	                    Resource: dummy2 (class=ocf provider=pcsmock type=stateful)
1858 	                      Operations:
1859 	                        monitor: dummy2-monitor-interval-10s
1860 	                          interval=10s timeout=20s role={const.PCMK_ROLE_PROMOTED}
1861 	                        monitor: dummy2-monitor-interval-11s
1862 	                          interval=11s timeout=20s role={const.PCMK_ROLE_UNPROMOTED}
1863 	                """
1864 	            ),
1865 	        )
1866 	
1867 	        self.assert_pcs_success("resource unclone dummy2".split())
1868 	
1869 	        self.assert_pcs_success(
1870 	            "resource config".split(),
1871 	            dedent(
1872 	                f"""\
1873 	                Resource: dummy2 (class=ocf provider=pcsmock type=stateful)
1874 	                  Operations:
1875 	                    monitor: dummy2-monitor-interval-10s
1876 	                      interval=10s timeout=20s role={const.PCMK_ROLE_PROMOTED}
1877 	                    monitor: dummy2-monitor-interval-11s
1878 	                      interval=11s timeout=20s role={const.PCMK_ROLE_UNPROMOTED}
1879 	                Clone: gr-master
1880 	                  Meta Attributes:
1881 	                    promotable=true
1882 	                  Group: gr
1883 	                    Resource: dummy1 (class=ocf provider=pcsmock type=stateful)
1884 	                      Operations:
1885 	                        monitor: dummy1-monitor-interval-10s
1886 	                          interval=10s timeout=20s role={const.PCMK_ROLE_PROMOTED}
1887 	                        monitor: dummy1-monitor-interval-11s
1888 	                          interval=11s timeout=20s role={const.PCMK_ROLE_UNPROMOTED}
1889 	                """
1890 	            ),
1891 	        )
1892 	
1893 	    def test_clone_group_member(self):
1894 	        self.assert_pcs_success(
1895 	            "resource create --no-default-ops D0 ocf:pcsmock:minimal --group AG".split(),
1896 	            stderr_full=DEPRECATED_DASH_DASH_GROUP,
1897 	        )
1898 	        self.assert_pcs_success(
1899 	            "resource create --no-default-ops D1 ocf:pcsmock:minimal --group AG".split(),
1900 	            stderr_full=DEPRECATED_DASH_DASH_GROUP,
1901 	        )
1902 	
1903 	        self.assert_pcs_success("resource clone D0".split())
1904 	        self.assert_pcs_success(
1905 	            ["resource", "config"],
1906 	            dedent(
1907 	                """\
1908 	                Group: AG
1909 	                  Resource: D1 (class=ocf provider=pcsmock type=minimal)
1910 	                    Operations:
1911 	                      monitor: D1-monitor-interval-10s
1912 	                        interval=10s timeout=20s
1913 	                Clone: D0-clone
1914 	                  Resource: D0 (class=ocf provider=pcsmock type=minimal)
1915 	                    Operations:
1916 	                      monitor: D0-monitor-interval-10s
1917 	                        interval=10s timeout=20s
1918 	                """
1919 	            ),
1920 	        )
1921 	
1922 	        self.assert_pcs_success("resource clone D1".split())
1923 	        self.assert_pcs_success(
1924 	            ["resource", "config"],
1925 	            dedent(
1926 	                """\
1927 	                Clone: D0-clone
1928 	                  Resource: D0 (class=ocf provider=pcsmock type=minimal)
1929 	                    Operations:
1930 	                      monitor: D0-monitor-interval-10s
1931 	                        interval=10s timeout=20s
1932 	                Clone: D1-clone
1933 	                  Resource: D1 (class=ocf provider=pcsmock type=minimal)
1934 	                    Operations:
1935 	                      monitor: D1-monitor-interval-10s
1936 	                        interval=10s timeout=20s
1937 	                """
1938 	            ),
1939 	        )
1940 	
1941 	    def test_promotable_group_member(self):
1942 	        self.assert_pcs_success(
1943 	            "resource create --no-default-ops D0 ocf:pcsmock:stateful --group AG".split(),
1944 	            stderr_full=DEPRECATED_DASH_DASH_GROUP,
1945 	        )
1946 	        self.assert_pcs_success(
1947 	            "resource create --no-default-ops D1 ocf:pcsmock:stateful --group AG".split(),
1948 	            stderr_full=DEPRECATED_DASH_DASH_GROUP,
1949 	        )
1950 	
1951 	        self.assert_pcs_success("resource promotable D0".split())
1952 	        self.assert_pcs_success(
1953 	            ["resource", "config"],
1954 	            dedent(
1955 	                """\
1956 	                Group: AG
1957 	                  Resource: D1 (class=ocf provider=pcsmock type=stateful)
1958 	                    Operations:
1959 	                      monitor: D1-monitor-interval-10s
1960 	                        interval=10s timeout=20s role=Promoted
1961 	                      monitor: D1-monitor-interval-11s
1962 	                        interval=11s timeout=20s role=Unpromoted
1963 	                Clone: D0-clone
1964 	                  Meta Attributes: D0-clone-meta_attributes
1965 	                    promotable=true
1966 	                  Resource: D0 (class=ocf provider=pcsmock type=stateful)
1967 	                    Operations:
1968 	                      monitor: D0-monitor-interval-10s
1969 	                        interval=10s timeout=20s role=Promoted
1970 	                      monitor: D0-monitor-interval-11s
1971 	                        interval=11s timeout=20s role=Unpromoted
1972 	                """
1973 	            ),
1974 	        )
1975 	
1976 	        self.assert_pcs_success("resource promotable D1".split())
1977 	        self.assert_pcs_success(
1978 	            ["resource", "config"],
1979 	            dedent(
1980 	                """\
1981 	                Clone: D0-clone
1982 	                  Meta Attributes: D0-clone-meta_attributes
1983 	                    promotable=true
1984 	                  Resource: D0 (class=ocf provider=pcsmock type=stateful)
1985 	                    Operations:
1986 	                      monitor: D0-monitor-interval-10s
1987 	                        interval=10s timeout=20s role=Promoted
1988 	                      monitor: D0-monitor-interval-11s
1989 	                        interval=11s timeout=20s role=Unpromoted
1990 	                Clone: D1-clone
1991 	                  Meta Attributes: D1-clone-meta_attributes
1992 	                    promotable=true
1993 	                  Resource: D1 (class=ocf provider=pcsmock type=stateful)
1994 	                    Operations:
1995 	                      monitor: D1-monitor-interval-10s
1996 	                        interval=10s timeout=20s role=Promoted
1997 	                      monitor: D1-monitor-interval-11s
1998 	                        interval=11s timeout=20s role=Unpromoted
1999 	                """
2000 	            ),
2001 	        )
2002 	
2003 	    def test_clone_master(self):
2004 	        # see also BundleClone
2005 	        self.assert_pcs_success(
2006 	            "resource create --no-default-ops D0 ocf:pcsmock:stateful".split(),
2007 	        )
2008 	        self.assert_pcs_success(
2009 	            "resource create --no-default-ops D1 ocf:pcsmock:stateful".split(),
2010 	        )
2011 	        self.assert_pcs_success(
2012 	            "resource create --no-default-ops D2 ocf:pcsmock:stateful".split(),
2013 	        )
2014 	        self.assert_pcs_success(
2015 	            "resource create --no-default-ops D3 ocf:pcsmock:stateful".split(),
2016 	        )
2017 	        self.assert_pcs_success("resource clone D0".split())
2018 	
2019 	        self.assert_pcs_fail(
2020 	            "resource promotable D3 meta promotable=false".split(),
2021 	            "Error: you cannot specify both promotable option and promotable keyword\n",
2022 	        )
2023 	
2024 	        self.assert_pcs_success("resource promotable D3".split())
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(
2030 	            self.temp_cib, "D1", master_id="D1-master-custom"
2031 	        )
2032 	
2033 	        # pcs no longer allows turning resources into masters but supports
2034 	        # existing ones. In order to test it, we need to put a master in the
2035 	        # CIB without pcs.
2036 	        wrap_element_by_master(self.temp_cib, "D2")
2037 	
2038 	        self.assert_pcs_success(
2039 	            "resource config".split(),
2040 	            dedent(
2041 	                """\
2042 	                Clone: D0-clone
2043 	                  Resource: D0 (class=ocf provider=pcsmock type=stateful)
2044 	                    Operations:
2045 	                      monitor: D0-monitor-interval-10s
2046 	                        interval=10s timeout=20s role=Promoted
2047 	                      monitor: D0-monitor-interval-11s
2048 	                        interval=11s timeout=20s role=Unpromoted
2049 	                Clone: D3-clone
2050 	                  Meta Attributes: D3-clone-meta_attributes
2051 	                    promotable=true
2052 	                  Resource: D3 (class=ocf provider=pcsmock type=stateful)
2053 	                    Operations:
2054 	                      monitor: D3-monitor-interval-10s
2055 	                        interval=10s timeout=20s role=Promoted
2056 	                      monitor: D3-monitor-interval-11s
2057 	                        interval=11s timeout=20s role=Unpromoted
2058 	                Clone: D1-master-custom
2059 	                  Meta Attributes:
2060 	                    promotable=true
2061 	                  Resource: D1 (class=ocf provider=pcsmock type=stateful)
2062 	                    Operations:
2063 	                      monitor: D1-monitor-interval-10s
2064 	                        interval=10s timeout=20s role=Promoted
2065 	                      monitor: D1-monitor-interval-11s
2066 	                        interval=11s timeout=20s role=Unpromoted
2067 	                Clone: D2-master
2068 	                  Meta Attributes:
2069 	                    promotable=true
2070 	                  Resource: D2 (class=ocf provider=pcsmock type=stateful)
2071 	                    Operations:
2072 	                      monitor: D2-monitor-interval-10s
2073 	                        interval=10s timeout=20s role=Promoted
2074 	                      monitor: D2-monitor-interval-11s
2075 	                        interval=11s timeout=20s role=Unpromoted
2076 	                """
2077 	            ),
2078 	        )
2079 	
2080 	    def test_lsb_resource(self):
2081 	        self.assert_pcs_fail(
2082 	            "resource create --no-default-ops D2 lsb:pcsmock foo=bar".split(),
2083 	            (
2084 	                "Error: invalid resource option 'foo', there are no options"
2085 	                " allowed, use --force to override\n" + ERRORS_HAVE_OCCURRED
2086 	            ),
2087 	        )
2088 	        self.assert_pcs_success(
2089 	            "resource create --no-default-ops D2 lsb:pcsmock foo=bar --force".split(),
2090 	            stderr_full=(
2091 	                "Warning: invalid resource option 'foo', there are no options"
2092 	                " allowed\n"
2093 	            ),
2094 	        )
2095 	        self.assert_pcs_success(
2096 	            "resource config".split(),
2097 	            dedent(
2098 	                """\
2099 	                Resource: D2 (class=lsb type=pcsmock)
2100 	                  Attributes: D2-instance_attributes
2101 	                    foo=bar
2102 	                  Operations:
2103 	                    monitor: D2-monitor-interval-15
2104 	                      interval=15 timeout=15
2105 	                """
2106 	            ),
2107 	        )
2108 	
2109 	        self.assert_pcs_fail(
2110 	            "resource update D2 bar=baz".split(),
2111 	            (
2112 	                "Error: invalid resource option 'bar', there are no options"
2113 	                " allowed, use --force to override\n"
2114 	            ),
2115 	        )
2116 	        self.assert_pcs_success(
2117 	            "resource update D2 bar=baz --force".split(),
2118 	            stderr_full=(
2119 	                "Warning: invalid resource option 'bar', there are no options"
2120 	                " allowed\n"
2121 	            ),
2122 	        )
2123 	        self.assert_pcs_success(
2124 	            "resource config".split(),
2125 	            dedent(
2126 	                """\
2127 	                Resource: D2 (class=lsb type=pcsmock)
2128 	                  Attributes: D2-instance_attributes
2129 	                    bar=baz
2130 	                    foo=bar
2131 	                  Operations:
2132 	                    monitor: D2-monitor-interval-15
2133 	                      interval=15 timeout=15
2134 	                """
2135 	            ),
2136 	        )
2137 	
2138 	    @skip(
2139 	        "test of 'pcs resource debug-*' to be moved to pcs.lib with the "
2140 	        "command itself"
2141 	    )
2142 	    def test_debug_start_clone_group(self):
2143 	        self.assert_pcs_success(
2144 	            "resource create D0 ocf:pcsmock:minimal --group DGroup".split(),
2145 	            stderr_full=DEPRECATED_DASH_DASH_GROUP,
2146 	        )
2147 	        self.assert_pcs_success(
2148 	            "resource create D1 ocf:pcsmock:minimal --group DGroup".split(),
2149 	            stderr_full=DEPRECATED_DASH_DASH_GROUP,
2150 	        )
2151 	        self.assert_pcs_success(
2152 	            "resource create D2 ocf:pcsmock:minimal clone".split(),
2153 	        )
2154 	
2155 	        # pcs no longer allows creating masters but supports existing ones. In
2156 	        # order to test it, we need to put a master in the CIB without pcs.
2157 	        fixture_to_cib(self.temp_cib.name, fixture_master_xml("D3"))
2158 	
2159 	        self.assert_pcs_fail(
2160 	            "resource debug-start DGroup".split(),
2161 	            "Error: unable to debug-start a group, try one of the group's resource(s) (D0,D1)\n",
2162 	        )
2163 	        self.assert_pcs_fail(
2164 	            "resource debug-start D2-clone".split(),
2165 	            "Error: unable to debug-start a clone, try the clone's resource: D2\n",
2166 	        )
2167 	        self.assert_pcs_fail(
2168 	            "resource debug-start D3-master".split(),
2169 	            "Error: unable to debug-start a master, try the master's resource: D3\n",
2170 	        )
2171 	
2172 	    def test_group_clone_creation(self):
2173 	        self.assert_pcs_success(
2174 	            "resource create --no-default-ops D1 ocf:pcsmock:minimal --group DGroup".split(),
2175 	            stderr_full=DEPRECATED_DASH_DASH_GROUP,
2176 	        )
2177 	
2178 	        self.assert_pcs_fail(
2179 	            "resource clone DGroup1".split(),
2180 	            "Error: unable to find group or resource: DGroup1\n",
2181 	        )
2182 	
2183 	        self.assert_pcs_success("resource clone DGroup".split())
2184 	        self.assert_pcs_success(
2185 	            "resource config".split(),
2186 	            dedent(
2187 	                """\
2188 	                Clone: DGroup-clone
2189 	                  Group: DGroup
2190 	                    Resource: D1 (class=ocf provider=pcsmock type=minimal)
2191 	                      Operations:
2192 	                        monitor: D1-monitor-interval-10s
2193 	                          interval=10s timeout=20s
2194 	                """
2195 	            ),
2196 	        )
2197 	
2198 	        self.assert_pcs_fail(
2199 	            "resource clone DGroup".split(),
2200 	            "Error: cannot clone a group that has already been cloned\n",
2201 	        )
2202 	
2203 	    def test_group_promotable_creation(self):
2204 	        self.assert_pcs_success(
2205 	            "resource create --no-default-ops D1 ocf:pcsmock:stateful --group DGroup".split(),
2206 	            stderr_full=DEPRECATED_DASH_DASH_GROUP,
2207 	        )
2208 	
2209 	        self.assert_pcs_fail(
2210 	            "resource promotable DGroup1".split(),
2211 	            "Error: unable to find group or resource: DGroup1\n",
2212 	        )
2213 	
2214 	        self.assert_pcs_success("resource promotable DGroup".split())
2215 	        self.assert_pcs_success(
2216 	            "resource config".split(),
2217 	            dedent(
2218 	                """\
2219 	                Clone: DGroup-clone
2220 	                  Meta Attributes: DGroup-clone-meta_attributes
2221 	                    promotable=true
2222 	                  Group: DGroup
2223 	                    Resource: D1 (class=ocf provider=pcsmock type=stateful)
2224 	                      Operations:
2225 	                        monitor: D1-monitor-interval-10s
2226 	                          interval=10s timeout=20s role=Promoted
2227 	                        monitor: D1-monitor-interval-11s
2228 	                          interval=11s timeout=20s role=Unpromoted
2229 	                """
2230 	            ),
2231 	        )
2232 	
2233 	        self.assert_pcs_fail(
2234 	            "resource promotable DGroup".split(),
2235 	            "Error: cannot clone a group that has already been cloned\n",
2236 	        )
2237 	
2238 	    def test_resource_clone_creation(self):
2239 	        self.pcs_runner = PcsRunner(self.temp_large_cib.name)
2240 	        self.pcs_runner.mock_settings = get_mock_settings()
2241 	        # resource "dummy1" is already in "temp_large_cib
2242 	        self.assert_pcs_success("resource clone dummy1".split())
2243 	
2244 	    def test_resource_clone_id_clone_command(self):
2245 	        self.assert_pcs_success(
2246 	            "resource create --no-default-ops dummy-clone ocf:pcsmock:minimal".split(),
2247 	        )
2248 	        self.assert_pcs_success(
2249 	            "resource create --no-default-ops dummy ocf:pcsmock:minimal".split(),
2250 	        )
2251 	        self.assert_pcs_success("resource clone dummy".split())
2252 	        self.assert_pcs_success(
2253 	            "resource config".split(),
2254 	            dedent(
2255 	                """\
2256 	                Resource: dummy-clone (class=ocf provider=pcsmock type=minimal)
2257 	                  Operations:
2258 	                    monitor: dummy-clone-monitor-interval-10s
2259 	                      interval=10s timeout=20s
2260 	                Clone: dummy-clone-1
2261 	                  Resource: dummy (class=ocf provider=pcsmock type=minimal)
2262 	                    Operations:
2263 	                      monitor: dummy-monitor-interval-10s
2264 	                        interval=10s timeout=20s
2265 	                """
2266 	            ),
2267 	        )
2268 	
2269 	    def test_resource_clone_id_create_command(self):
2270 	        self.assert_pcs_success(
2271 	            "resource create --no-default-ops dummy-clone ocf:pcsmock:minimal".split(),
2272 	        )
2273 	        self.assert_pcs_success(
2274 	            "resource create --no-default-ops dummy ocf:pcsmock:minimal clone".split(),
2275 	        )
2276 	        self.assert_pcs_success(
2277 	            "resource config".split(),
2278 	            dedent(
2279 	                """\
2280 	                Resource: dummy-clone (class=ocf provider=pcsmock type=minimal)
2281 	                  Operations:
2282 	                    monitor: dummy-clone-monitor-interval-10s
2283 	                      interval=10s timeout=20s
2284 	                Clone: dummy-clone-1
2285 	                  Resource: dummy (class=ocf provider=pcsmock type=minimal)
2286 	                    Operations:
2287 	                      monitor: dummy-monitor-interval-10s
2288 	                        interval=10s timeout=20s
2289 	                """
2290 	            ),
2291 	        )
2292 	
2293 	    def test_resource_promotable_id_promotable_command(self):
2294 	        self.assert_pcs_success(
2295 	            "resource create --no-default-ops dummy-clone ocf:pcsmock:stateful".split(),
2296 	        )
2297 	        self.assert_pcs_success(
2298 	            "resource create --no-default-ops dummy ocf:pcsmock:stateful".split(),
2299 	        )
2300 	        self.assert_pcs_success("resource promotable dummy".split())
2301 	        self.assert_pcs_success(
2302 	            "resource config".split(),
2303 	            dedent(
2304 	                """\
2305 	                Resource: dummy-clone (class=ocf provider=pcsmock type=stateful)
2306 	                  Operations:
2307 	                    monitor: dummy-clone-monitor-interval-10s
2308 	                      interval=10s timeout=20s role=Promoted
2309 	                    monitor: dummy-clone-monitor-interval-11s
2310 	                      interval=11s timeout=20s role=Unpromoted
2311 	                Clone: dummy-clone-1
2312 	                  Meta Attributes: dummy-clone-1-meta_attributes
2313 	                    promotable=true
2314 	                  Resource: dummy (class=ocf provider=pcsmock type=stateful)
2315 	                    Operations:
2316 	                      monitor: dummy-monitor-interval-10s
2317 	                        interval=10s timeout=20s role=Promoted
2318 	                      monitor: dummy-monitor-interval-11s
2319 	                        interval=11s timeout=20s role=Unpromoted
2320 	                """
2321 	            ),
2322 	        )
2323 	
2324 	    def test_resource_promotable_id_create_command(self):
2325 	        self.assert_pcs_success(
2326 	            "resource create --no-default-ops dummy-clone ocf:pcsmock:stateful".split(),
2327 	        )
2328 	        self.assert_pcs_success(
2329 	            "resource create --no-default-ops dummy ocf:pcsmock:stateful promotable".split(),
2330 	            stderr_full=fixture_meta_attributes_not_validated_warning(
2331 	                ["clone"]
2332 	            ),
2333 	        )
2334 	        self.assert_pcs_success(
2335 	            "resource config".split(),
2336 	            dedent(
2337 	                """\
2338 	                Resource: dummy-clone (class=ocf provider=pcsmock type=stateful)
2339 	                  Operations:
2340 	                    monitor: dummy-clone-monitor-interval-10s
2341 	                      interval=10s timeout=20s role=Promoted
2342 	                    monitor: dummy-clone-monitor-interval-11s
2343 	                      interval=11s timeout=20s role=Unpromoted
2344 	                Clone: dummy-clone-1
2345 	                  Meta Attributes: dummy-clone-1-meta_attributes
2346 	                    promotable=true
2347 	                  Resource: dummy (class=ocf provider=pcsmock type=stateful)
2348 	                    Operations:
2349 	                      monitor: dummy-monitor-interval-10s
2350 	                        interval=10s timeout=20s role=Promoted
2351 	                      monitor: dummy-monitor-interval-11s
2352 	                        interval=11s timeout=20s role=Unpromoted
2353 	                """
2354 	            ),
2355 	        )
2356 	
2357 	    def test_resource_clone_update(self):
2358 	        self.assert_pcs_success(
2359 	            "resource create --no-default-ops D1 ocf:pcsmock:minimal clone".split(),
2360 	        )
2361 	        self.assert_pcs_success(
2362 	            "resource config".split(),
2363 	            dedent(
2364 	                """\
2365 	                Clone: D1-clone
2366 	                  Resource: D1 (class=ocf provider=pcsmock type=minimal)
2367 	                    Operations:
2368 	                      monitor: D1-monitor-interval-10s
2369 	                        interval=10s timeout=20s
2370 	                """
2371 	            ),
2372 	        )
2373 	
2374 	        self.assert_pcs_success(
2375 	            "resource update D1-clone foo=bar".split(),
2376 	            stderr_full=fixture_meta_attributes_not_validated_warning(
2377 	                ["clone"]
2378 	            ),
2379 	        )
2380 	        self.assert_pcs_success(
2381 	            "resource config".split(),
2382 	            dedent(
2383 	                """\
2384 	                Clone: D1-clone
2385 	                  Meta Attributes: D1-clone-meta_attributes
2386 	                    foo=bar
2387 	                  Resource: D1 (class=ocf provider=pcsmock type=minimal)
2388 	                    Operations:
2389 	                      monitor: D1-monitor-interval-10s
2390 	                        interval=10s timeout=20s
2391 	                """
2392 	            ),
2393 	        )
2394 	
2395 	        self.assert_pcs_success(
2396 	            "resource update D1-clone bar=baz".split(),
2397 	            stderr_full=fixture_meta_attributes_not_validated_warning(
2398 	                ["clone"]
2399 	            ),
2400 	        )
2401 	        self.assert_pcs_success(
2402 	            "resource config".split(),
2403 	            dedent(
2404 	                """\
2405 	                Clone: D1-clone
2406 	                  Meta Attributes: D1-clone-meta_attributes
2407 	                    bar=baz
2408 	                    foo=bar
2409 	                  Resource: D1 (class=ocf provider=pcsmock type=minimal)
2410 	                    Operations:
2411 	                      monitor: D1-monitor-interval-10s
2412 	                        interval=10s timeout=20s
2413 	                """
2414 	            ),
2415 	        )
2416 	
2417 	        self.assert_pcs_success("resource update D1-clone foo=".split())
2418 	        self.assert_pcs_success(
2419 	            "resource config".split(),
2420 	            dedent(
2421 	                """\
2422 	                Clone: D1-clone
2423 	                  Meta Attributes: D1-clone-meta_attributes
2424 	                    bar=baz
2425 	                  Resource: D1 (class=ocf provider=pcsmock type=minimal)
2426 	                    Operations:
2427 	                      monitor: D1-monitor-interval-10s
2428 	                        interval=10s timeout=20s
2429 	                """
2430 	            ),
2431 	        )
2432 	
2433 	    def test_mastered_group(self):
2434 	        self.assert_pcs_success(
2435 	            "resource create --no-default-ops A ocf:pcsmock:minimal --group AG".split(),
2436 	            stderr_full=DEPRECATED_DASH_DASH_GROUP,
2437 	        )
2438 	        self.assert_pcs_success(
2439 	            "resource create --no-default-ops B ocf:pcsmock:minimal --group AG".split(),
2440 	            stderr_full=DEPRECATED_DASH_DASH_GROUP,
2441 	        )
2442 	        self.assert_pcs_success(
2443 	            "resource create --no-default-ops C ocf:pcsmock:minimal --group AG".split(),
2444 	            stderr_full=DEPRECATED_DASH_DASH_GROUP,
2445 	        )
2446 	        # pcs no longer allows turning resources into masters but supports
2447 	        # existing ones. In order to test it, we need to put a master in the
2448 	        # CIB without pcs.
2449 	        wrap_element_by_master(self.temp_cib, "AG", master_id="AGMaster")
2450 	
2451 	        self.assert_pcs_fail(
2452 	            "resource create --no-default-ops A ocf:pcsmock:minimal".split(),
2453 	            "Error: 'A' already exists\n",
2454 	        )
2455 	        self.assert_pcs_fail(
2456 	            "resource create --no-default-ops AG ocf:pcsmock:minimal".split(),
2457 	            "Error: 'AG' already exists\n",
2458 	        )
2459 	        self.assert_pcs_fail(
2460 	            "resource create --no-default-ops AGMaster ocf:pcsmock:minimal".split(),
2461 	            "Error: 'AGMaster' already exists\n",
2462 	        )
2463 	
2464 	        self.assert_pcs_fail(
2465 	            "resource ungroup AG".split(),
2466 	            "Error: Cannot remove all resources from a cloned group\n",
2467 	        )
2468 	
2469 	        self.assert_pcs_success(
2470 	            "resource delete B".split(),
2471 	            stderr_full=fixture_message_not_deleting_resources_not_live(["B"])
2472 	            + dedent(
2473 	                """\
2474 	                Removing references:
2475 	                  Resource 'B' from:
2476 	                    Group: 'AG'
2477 	                """
2478 	            ),
2479 	        )
2480 	        self.assert_pcs_success(
2481 	            "resource delete C".split(),
2482 	            stderr_full=fixture_message_not_deleting_resources_not_live(["C"])
2483 	            + dedent(
2484 	                """\
2485 	                Removing references:
2486 	                  Resource 'C' from:
2487 	                    Group: 'AG'
2488 	                """
2489 	            ),
2490 	        )
2491 	        self.assert_pcs_success("resource ungroup AG".split())
2492 	        self.assert_pcs_success(
2493 	            "resource config".split(),
2494 	            dedent(
2495 	                """\
2496 	                Clone: AGMaster
2497 	                  Meta Attributes:
2498 	                    promotable=true
2499 	                  Resource: A (class=ocf provider=pcsmock type=minimal)
2500 	                    Operations:
2501 	                      monitor: A-monitor-interval-10s
2502 	                        interval=10s timeout=20s
2503 	                """
2504 	            ),
2505 	        )
2506 	
2507 	    def test_cloned_group(self):
2508 	        self.assert_pcs_success(
2509 	            "resource create --no-default-ops D1 ocf:pcsmock:minimal --group DG".split(),
2510 	            stderr_full=DEPRECATED_DASH_DASH_GROUP,
2511 	        )
2512 	        self.assert_pcs_success(
2513 	            "resource create --no-default-ops D2 ocf:pcsmock:minimal --group DG".split(),
2514 	            stderr_full=DEPRECATED_DASH_DASH_GROUP,
2515 	        )
2516 	        self.assert_pcs_success("resource clone DG".split())
2517 	        self.assert_pcs_success(
2518 	            "resource config".split(),
2519 	            dedent(
2520 	                """\
2521 	                Clone: DG-clone
2522 	                  Group: DG
2523 	                    Resource: D1 (class=ocf provider=pcsmock type=minimal)
2524 	                      Operations:
2525 	                        monitor: D1-monitor-interval-10s
2526 	                          interval=10s timeout=20s
2527 	                    Resource: D2 (class=ocf provider=pcsmock type=minimal)
2528 	                      Operations:
2529 	                        monitor: D2-monitor-interval-10s
2530 	                          interval=10s timeout=20s
2531 	                """
2532 	            ),
2533 	        )
2534 	
2535 	        self.assert_pcs_fail(
2536 	            "resource create --no-default-ops D1 ocf:pcsmock:minimal".split(),
2537 	            "Error: 'D1' already exists\n",
2538 	        )
2539 	        self.assert_pcs_fail(
2540 	            "resource create --no-default-ops DG ocf:pcsmock:minimal".split(),
2541 	            "Error: 'DG' already exists\n",
2542 	        )
2543 	        self.assert_pcs_fail(
2544 	            "resource create --no-default-ops DG-clone ocf:pcsmock:minimal".split(),
2545 	            "Error: 'DG-clone' already exists\n",
2546 	        )
2547 	
2548 	    def test_op_option(self):
2549 	        self.assert_pcs_success(
2550 	            "resource create --no-default-ops B ocf:pcsmock:minimal".split(),
2551 	        )
2552 	
2553 	        self.assert_pcs_fail(
2554 	            "resource update B ocf:pcsmock:minimal op monitor interval=30s blah=blah".split(),
2555 	            "Error: blah is not a valid op option (use --force to override)\n",
2556 	        )
2557 	
2558 	        self.assert_pcs_success(
2559 	            "resource create --no-default-ops C ocf:pcsmock:minimal".split(),
2560 	        )
2561 	
2562 	        self.assert_pcs_fail(
2563 	            "resource op add C monitor interval=30s blah=blah".split(),
2564 	            "Error: blah is not a valid op option (use --force to override)\n",
2565 	        )
2566 	
2567 	        self.assert_pcs_fail(
2568 	            "resource op add C monitor interval=60 role=role".split(),
2569 	            "Error: role must be: {} (use --force to override)\n".format(
2570 	                format_list_custom_last_separator(const.PCMK_ROLES, " or ")
2571 	            ),
2572 	        )
2573 	
2574 	        self.assert_pcs_success(
2575 	            "resource config".split(),
2576 	            dedent(
2577 	                """\
2578 	                Resource: B (class=ocf provider=pcsmock type=minimal)
2579 	                  Operations:
2580 	                    monitor: B-monitor-interval-10s
2581 	                      interval=10s timeout=20s
2582 	                Resource: C (class=ocf provider=pcsmock type=minimal)
2583 	                  Operations:
2584 	                    monitor: C-monitor-interval-10s
2585 	                      interval=10s timeout=20s
2586 	                """
2587 	            ),
2588 	        )
2589 	
2590 	        self.assert_pcs_fail(
2591 	            "resource update B op monitor interval=30s monitor interval=31s role=Master".split(),
2592 	            "Error: role must be: {} (use --force to override)\n".format(
2593 	                format_list_custom_last_separator(const.PCMK_ROLES, " or ")
2594 	            ),
2595 	        )
2596 	
2597 	        self.assert_pcs_fail(
2598 	            "resource update B op monitor interval=30s monitor interval=31s role=promoted".split(),
2599 	            "Error: role must be: {} (use --force to override)\n".format(
2600 	                format_list_custom_last_separator(const.PCMK_ROLES, " or ")
2601 	            ),
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(
(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