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