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