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