diff --git a/cts/cts-scheduler.in b/cts/cts-scheduler.in index 2fa657ccc7..066c39b237 100644 --- a/cts/cts-scheduler.in +++ b/cts/cts-scheduler.in @@ -1,1716 +1,1740 @@ #!@PYTHON@ """ Regression tests for Pacemaker's scheduler """ __copyright__ = "Copyright 2004-2023 the Pacemaker project contributors" __license__ = "GNU General Public License version 2 or later (GPLv2+) WITHOUT ANY WARRANTY" import io import os import re import sys import stat import shlex import shutil import argparse import subprocess import platform import tempfile # These imports allow running from a source checkout after running `make`. # Note that while this doesn't necessarily mean it will successfully run tests, # but being able to see --help output can be useful. if os.path.exists("@abs_top_srcdir@/python"): sys.path.insert(0, "@abs_top_srcdir@/python") if os.path.exists("@abs_top_builddir@/python") and "@abs_top_builddir@" != "@abs_top_srcdir@": sys.path.insert(0, "@abs_top_builddir@/python") from pacemaker.buildoptions import BuildOptions from pacemaker.exitstatus import ExitStatus DESC = """Regression tests for Pacemaker's scheduler""" # Each entry in TESTS is a group of tests, where each test consists of a # test base name, test description, and additional test arguments. # Test groups will be separated by newlines in output. TESTS = [ [ [ "simple1", "Offline" ], [ "simple2", "Start" ], [ "simple3", "Start 2" ], [ "simple4", "Start Failed" ], [ "simple6", "Stop Start" ], [ "simple7", "Shutdown" ], #[ "simple8", "Stonith" ], #[ "simple9", "Lower version" ], #[ "simple10", "Higher version" ], [ "simple11", "Priority (ne)" ], [ "simple12", "Priority (eq)" ], [ "simple8", "Stickiness" ], ], [ [ "group1", "Group" ], [ "group2", "Group + Native" ], [ "group3", "Group + Group" ], [ "group4", "Group + Native (nothing)" ], [ "group5", "Group + Native (move)" ], [ "group6", "Group + Group (move)" ], [ "group7", "Group colocation" ], [ "group13", "Group colocation (cant run)" ], [ "group8", "Group anti-colocation" ], [ "group9", "Group recovery" ], [ "group10", "Group partial recovery" ], [ "group11", "Group target_role" ], [ "group14", "Group stop (graph terminated)" ], [ "group15", "Negative group colocation" ], [ "bug-1573", "Partial stop of a group with two children" ], [ "bug-1718", "Mandatory group ordering - Stop group_FUN" ], [ "failed-sticky-group", "Move group on last member failure despite infinite stickiness" ], [ "failed-sticky-anticolocated-group", "Move group on last member failure despite infinite stickiness and optional anti-colocation" ], [ "bug-lf-2619", "Move group on clone failure" ], [ "group-fail", "Ensure stop order is preserved for partially active groups" ], [ "group-unmanaged", "No need to restart r115 because r114 is unmanaged" ], [ "group-unmanaged-stopped", "Make sure r115 is stopped when r114 fails" ], [ "partial-unmanaged-group", "New member in partially unmanaged group" ], [ "group-dependents", "Account for the location preferences of things colocated with a group" ], [ "group-stop-ordering", "Ensure blocked group member stop does not force other member stops" ], [ "colocate-unmanaged-group", "Respect mandatory colocations even if earlier group member is unmanaged" ], [ "coloc-with-inner-group-member", "Consider explicit colocations with inner group members" ], [ "banned-group-inner-constraints", "Group banned from current node, inner member constrained" ], ], [ [ "rsc_dep1", "Must not" ], [ "rsc_dep3", "Must" ], [ "rsc_dep5", "Must not 3" ], [ "rsc_dep7", "Must 3" ], [ "rsc_dep10", "Must (but cant)" ], [ "rsc_dep2", "Must (running)" ], [ "rsc_dep8", "Must (running : alt)" ], [ "rsc_dep4", "Must (running + move)" ], [ "asymmetric", "Asymmetric - require explicit location constraints" ], ], [ [ "orphan-0", "Orphan ignore" ], [ "orphan-1", "Orphan stop" ], [ "orphan-2", "Orphan stop, remove failcount" ], ], [ [ "params-0", "Params: No change" ], [ "params-1", "Params: Changed" ], [ "params-2", "Params: Resource definition" ], [ "params-3", "Params: Restart instead of reload if start pending" ], [ "params-4", "Params: Reload" ], [ "params-5", "Params: Restart based on probe digest" ], [ "novell-251689", "Resource definition change + target_role=stopped" ], [ "bug-lf-2106", "Restart all anonymous clone instances after config change" ], [ "params-6", "Params: Detect reload in previously migrated resource" ], [ "nvpair-id-ref", "Support id-ref in nvpair with optional name" ], [ "not-reschedule-unneeded-monitor", "Do not reschedule unneeded monitors while resource definitions have changed" ], [ "reload-becomes-restart", "Cancel reload if restart becomes required" ], [ "restart-with-extra-op-params", "Restart if with extra operation parameters upon changes of any" ], ], [ [ "target-0", "Target Role : baseline" ], [ "target-1", "Target Role : promoted" ], [ "target-2", "Target Role : invalid" ], ], [ [ "base-score", "Set a node's default score for all nodes" ], ], [ [ "date-1", "Dates", [ "-t", "2005-020" ] ], [ "date-2", "Date Spec - Pass", [ "-t", "2005-020T12:30" ] ], [ "date-3", "Date Spec - Fail", [ "-t", "2005-020T11:30" ] ], [ "origin", "Timing of recurring operations", [ "-t", "2014-05-07 00:28:00" ] ], [ "probe-0", "Probe (anon clone)" ], [ "probe-1", "Pending Probe" ], [ "probe-2", "Correctly re-probe cloned groups" ], [ "probe-3", "Probe (pending node)" ], [ "probe-4", "Probe (pending node + stopped resource)" ], [ "probe-pending-node", "Probe (pending node + unmanaged resource)" ], [ "failed-probe-primitive", "Maskable vs. unmaskable probe failures on primitive resources" ], [ "failed-probe-clone", "Maskable vs. unmaskable probe failures on cloned resources" ], [ "expired-failed-probe-primitive", "Maskable, expired probe failure on primitive resources" ], [ "standby", "Standby" ], [ "comments", "Comments" ], ], [ [ "one-or-more-0", "Everything starts" ], [ "one-or-more-1", "Nothing starts because of A" ], [ "one-or-more-2", "D can start because of C" ], [ "one-or-more-3", "D cannot start because of B and C" ], [ "one-or-more-4", "D cannot start because of target-role" ], [ "one-or-more-5", "Start A and F even though C and D are stopped" ], [ "one-or-more-6", "Leave A running even though B is stopped" ], [ "one-or-more-7", "Leave A running even though C is stopped" ], [ "bug-5140-require-all-false", "Allow basegrp:0 to stop" ], [ "clone-require-all-1", "clone B starts node 3 and 4" ], [ "clone-require-all-2", "clone B remains stopped everywhere" ], [ "clone-require-all-3", "clone B stops everywhere because A stops everywhere" ], [ "clone-require-all-4", "clone B remains on node 3 and 4 with only one instance of A remaining" ], [ "clone-require-all-5", "clone B starts on node 1 3 and 4" ], [ "clone-require-all-6", "clone B remains active after shutting down instances of A" ], [ "clone-require-all-7", "clone A and B both start at the same time. all instances of A start before B" ], [ "clone-require-all-no-interleave-1", "C starts everywhere after A and B" ], [ "clone-require-all-no-interleave-2", "C starts on nodes 1, 2, and 4 with only one active instance of B" ], [ "clone-require-all-no-interleave-3", "C remains active when instance of B is stopped on one node and started on another" ], [ "one-or-more-unrunnable-instances", "Avoid dependencies on instances that won't ever be started" ], ], [ [ "location-date-rules-1", "Use location constraints with ineffective date-based rules" ], [ "location-date-rules-2", "Use location constraints with effective date-based rules" ], [ "nvpair-date-rules-1", "Use nvpair blocks with a variety of date-based rules" ], [ "value-source", "Use location constraints with node attribute expressions using value-source" ], [ "rule-dbl-as-auto-number-match", "Floating-point rule values default to number comparison: match" ], [ "rule-dbl-as-auto-number-no-match", "Floating-point rule values default to number comparison: no " "match" ], [ "rule-dbl-as-integer-match", "Floating-point rule values set to integer comparison: match" ], [ "rule-dbl-as-integer-no-match", "Floating-point rule values set to integer comparison: no match" ], [ "rule-dbl-as-number-match", "Floating-point rule values set to number comparison: match" ], [ "rule-dbl-as-number-no-match", "Floating-point rule values set to number comparison: no match" ], [ "rule-dbl-parse-fail-default-str-match", "Floating-point rule values fail to parse, default to string " "comparison: match" ], [ "rule-dbl-parse-fail-default-str-no-match", "Floating-point rule values fail to parse, default to string " "comparison: no match" ], [ "rule-int-as-auto-integer-match", "Integer rule values default to integer comparison: match" ], [ "rule-int-as-auto-integer-no-match", "Integer rule values default to integer comparison: no match" ], [ "rule-int-as-integer-match", "Integer rule values set to integer comparison: match" ], [ "rule-int-as-integer-no-match", "Integer rule values set to integer comparison: no match" ], [ "rule-int-as-number-match", "Integer rule values set to number comparison: match" ], [ "rule-int-as-number-no-match", "Integer rule values set to number comparison: no match" ], [ "rule-int-parse-fail-default-str-match", "Integer rule values fail to parse, default to string " "comparison: match" ], [ "rule-int-parse-fail-default-str-no-match", "Integer rule values fail to parse, default to string " "comparison: no match" ], [ "timeout-by-node", "Start timeout varies by node" ], ], [ [ "order1", "Order start 1" ], [ "order2", "Order start 2" ], [ "order3", "Order stop" ], [ "order4", "Order (multiple)" ], [ "order5", "Order (move)" ], [ "order6", "Order (move w/ restart)" ], [ "order7", "Order (mandatory)" ], [ "order-optional", "Order (score=0)" ], [ "order-required", "Order (score=INFINITY)" ], [ "bug-lf-2171", "Prevent group start when clone is stopped" ], [ "order-clone", "Clone ordering should be able to prevent startup of dependent clones" ], [ "order-sets", "Ordering for resource sets" ], [ "order-serialize", "Serialize resources without inhibiting migration" ], [ "order-serialize-set", "Serialize a set of resources without inhibiting migration" ], [ "clone-order-primitive", "Order clone start after a primitive" ], [ "clone-order-16instances", "Verify ordering of 16 cloned resources" ], [ "order-optional-keyword", "Order (optional keyword)" ], [ "order-mandatory", "Order (mandatory keyword)" ], [ "bug-lf-2493", "Don't imply colocation requirements when applying ordering constraints with clones" ], [ "ordered-set-basic-startup", "Constraint set with default order settings" ], [ "ordered-set-natural", "Allow natural set ordering" ], [ "order-wrong-kind", "Order (error)" ], ], [ [ "coloc-loop", "Colocation - loop" ], [ "coloc-many-one", "Colocation - many-to-one" ], [ "coloc-list", "Colocation - many-to-one with list" ], [ "coloc-group", "Colocation - groups" ], [ "coloc-unpromoted-anti", "Anti-colocation with unpromoted shouldn't prevent promoted colocation" ], [ "coloc-attr", "Colocation based on node attributes" ], [ "coloc-negative-group", "Negative colocation with a group" ], [ "coloc-intra-set", "Intra-set colocation" ], [ "bug-lf-2435", "Colocation sets with a negative score" ], [ "coloc-clone-stays-active", "Ensure clones don't get stopped/demoted because a dependent must stop" ], [ "coloc_fp_logic", "Verify floating point calculations in colocation are working" ], [ "colo_promoted_w_native", "cl#5070 - Verify promotion order is affected when colocating promoted with primitive" ], [ "colo_unpromoted_w_native", "cl#5070 - Verify promotion order is affected when colocating unpromoted with primitive" ], [ "anti-colocation-order", "cl#5187 - Prevent resources in an anti-colocation from even temporarily running on a same node" ], [ "anti-colocation-promoted", "Organize order of actions for promoted resources in anti-colocations" ], [ "anti-colocation-unpromoted", "Organize order of actions for unpromoted resources in anti-colocations" ], [ "group-anticolocation", "Group with failed last member anti-colocated with another group" ], [ "group-anticolocation-2", "Group with failed last member anti-colocated with another sticky group" ], [ "group-anticolocation-3", "Group with failed last member mandatorily anti-colocated with another group" ], [ "group-anticolocation-4", "Group with failed last member anti-colocated without influence with another group" ], [ "group-anticolocation-5", "Group with failed last member anti-colocated with another group (third node allowed)" ], [ "group-colocation-failure", "Group with sole member failed, colocated with another group" ], [ "enforce-colo1", "Always enforce B with A INFINITY" ], [ "complex_enforce_colo", "Always enforce B with A INFINITY. (make sure heat-engine stops)" ], [ "coloc-dependee-should-stay", "Stickiness outweighs group colocation" ], [ "coloc-dependee-should-move", "Group colocation outweighs stickiness" ], [ "colocation-influence", "Respect colocation influence" ], [ "colocation-priority-group", "Apply group colocations in order of primary priority" ], [ "colocation-vs-stickiness", "Group stickiness outweighs anti-colocation score" ], [ "promoted-with-blocked", "Promoted role colocated with a resource with blocked start" ], [ "primitive-with-group-with-clone", "Consider group dependent when colocating with clone" ], [ "primitive-with-group-with-promoted", "Consider group dependent when colocating with promoted role" ], [ "primitive-with-unrunnable-group", "Block primitive colocated with group that can't start", ], + [ + "coloc-cloned-group-promoted-dependent1", + "Cloned group promoted role with primitive (mandatory)" + ], + [ + "coloc-cloned-group-promoted-dependent2", + "Cloned group promoted role with primitive (optional)" + ], + [ + "coloc-optional-promoted-dependent-moves-1", + "Colocation score less than promotion score difference: move" + ], + [ + "coloc-optional-promoted-dependent-moves-2", + "Colocation score greater than promotion score difference: move" + ], + [ + "coloc-optional-promoted-dependent-stays-1", + "Colocation score greater than promotion score difference: stay" + ], + [ + "coloc-optional-promoted-dependent-stays-2", + "Colocation score less than promotion score difference: stay" + ], ], [ [ "rsc-sets-seq-true", "Resource Sets - sequential=false" ], [ "rsc-sets-seq-false", "Resource Sets - sequential=true" ], [ "rsc-sets-clone", "Resource Sets - Clone" ], [ "rsc-sets-promoted", "Resource Sets - Promoted" ], [ "rsc-sets-clone-1", "Resource Sets - Clone (lf#2404)" ], ], [ [ "attrs1", "string: eq (and)" ], [ "attrs2", "string: lt / gt (and)" ], [ "attrs3", "string: ne (or)" ], [ "attrs4", "string: exists" ], [ "attrs5", "string: not_exists" ], [ "attrs6", "is_dc: true" ], [ "attrs7", "is_dc: false" ], [ "attrs8", "score_attribute" ], [ "per-node-attrs", "Per node resource parameters" ], ], [ [ "mon-rsc-1", "Schedule Monitor - start" ], [ "mon-rsc-2", "Schedule Monitor - move" ], [ "mon-rsc-3", "Schedule Monitor - pending start" ], [ "mon-rsc-4", "Schedule Monitor - move/pending start" ], ], [ [ "rec-rsc-0", "Resource Recover - no start" ], [ "rec-rsc-1", "Resource Recover - start" ], [ "rec-rsc-2", "Resource Recover - monitor" ], [ "rec-rsc-3", "Resource Recover - stop - ignore" ], [ "rec-rsc-4", "Resource Recover - stop - block" ], [ "rec-rsc-5", "Resource Recover - stop - fence" ], [ "rec-rsc-6", "Resource Recover - multiple - restart" ], [ "rec-rsc-7", "Resource Recover - multiple - stop" ], [ "rec-rsc-8", "Resource Recover - multiple - block" ], [ "rec-rsc-9", "Resource Recover - group/group" ], [ "stop-unexpected", "Recover multiply active group with stop_unexpected" ], [ "stop-unexpected-2", "Resource multiply active primitve with stop_unexpected" ], [ "monitor-recovery", "on-fail=block + resource recovery detected by recurring monitor" ], [ "stop-failure-no-quorum", "Stop failure without quorum" ], [ "stop-failure-no-fencing", "Stop failure without fencing available" ], [ "stop-failure-with-fencing", "Stop failure with fencing available" ], [ "multiple-active-block-group", "Support of multiple-active=block for resource groups" ], [ "multiple-monitor-one-failed", "Consider resource failed if any of the configured monitor operations failed" ], ], [ [ "quorum-1", "No quorum - ignore" ], [ "quorum-2", "No quorum - freeze" ], [ "quorum-3", "No quorum - stop" ], [ "quorum-4", "No quorum - start anyway" ], [ "quorum-5", "No quorum - start anyway (group)" ], [ "quorum-6", "No quorum - start anyway (clone)" ], [ "bug-cl-5212", "No promotion with no-quorum-policy=freeze" ], [ "suicide-needed-inquorate", "no-quorum-policy=suicide: suicide necessary" ], [ "suicide-not-needed-initial-quorum", "no-quorum-policy=suicide: suicide not necessary at initial quorum" ], [ "suicide-not-needed-never-quorate", "no-quorum-policy=suicide: suicide not necessary if never quorate" ], [ "suicide-not-needed-quorate", "no-quorum-policy=suicide: suicide necessary if quorate" ], ], [ [ "rec-node-1", "Node Recover - Startup - no fence" ], [ "rec-node-2", "Node Recover - Startup - fence" ], [ "rec-node-3", "Node Recover - HA down - no fence" ], [ "rec-node-4", "Node Recover - HA down - fence" ], [ "rec-node-5", "Node Recover - CRM down - no fence" ], [ "rec-node-6", "Node Recover - CRM down - fence" ], [ "rec-node-7", "Node Recover - no quorum - ignore" ], [ "rec-node-8", "Node Recover - no quorum - freeze" ], [ "rec-node-9", "Node Recover - no quorum - stop" ], [ "rec-node-10", "Node Recover - no quorum - stop w/fence" ], [ "rec-node-11", "Node Recover - CRM down w/ group - fence" ], [ "rec-node-12", "Node Recover - nothing active - fence" ], [ "rec-node-13", "Node Recover - failed resource + shutdown - fence" ], [ "rec-node-15", "Node Recover - unknown lrm section" ], [ "rec-node-14", "Serialize all stonith's" ], ], [ [ "multi1", "Multiple Active (stop/start)" ], ], [ [ "migrate-begin", "Normal migration" ], [ "migrate-success", "Completed migration" ], [ "migrate-partial-1", "Completed migration, missing stop on source" ], [ "migrate-partial-2", "Successful migrate_to only" ], [ "migrate-partial-3", "Successful migrate_to only, target down" ], [ "migrate-partial-4", "Migrate from the correct host after migrate_to+migrate_from" ], [ "bug-5186-partial-migrate", "Handle partial migration when src node loses membership" ], [ "migrate-fail-2", "Failed migrate_from" ], [ "migrate-fail-3", "Failed migrate_from + stop on source" ], [ "migrate-fail-4", "Failed migrate_from + stop on target - ideally we wouldn't need to re-stop on target" ], [ "migrate-fail-5", "Failed migrate_from + stop on source and target" ], [ "migrate-fail-6", "Failed migrate_to" ], [ "migrate-fail-7", "Failed migrate_to + stop on source" ], [ "migrate-fail-8", "Failed migrate_to + stop on target - ideally we wouldn't need to re-stop on target" ], [ "migrate-fail-9", "Failed migrate_to + stop on source and target" ], [ "migration-ping-pong", "Old migrate_to failure + successful migrate_from on same node" ], [ "migrate-stop", "Migration in a stopping stack" ], [ "migrate-start", "Migration in a starting stack" ], [ "migrate-stop_start", "Migration in a restarting stack" ], [ "migrate-stop-complex", "Migration in a complex stopping stack" ], [ "migrate-start-complex", "Migration in a complex starting stack" ], [ "migrate-stop-start-complex", "Migration in a complex moving stack" ], [ "migrate-shutdown", "Order the post-migration 'stop' before node shutdown" ], [ "migrate-1", "Migrate (migrate)" ], [ "migrate-2", "Migrate (stable)" ], [ "migrate-3", "Migrate (failed migrate_to)" ], [ "migrate-4", "Migrate (failed migrate_from)" ], [ "novell-252693", "Migration in a stopping stack" ], [ "novell-252693-2", "Migration in a starting stack" ], [ "novell-252693-3", "Non-Migration in a starting and stopping stack" ], [ "bug-1820", "Migration in a group" ], [ "bug-1820-1", "Non-migration in a group" ], [ "migrate-5", "Primitive migration with a clone" ], [ "migrate-fencing", "Migration after Fencing" ], [ "migrate-both-vms", "Migrate two VMs that have no colocation" ], [ "migration-behind-migrating-remote", "Migrate resource behind migrating remote connection" ], [ "1-a-then-bm-move-b", "Advanced migrate logic. A then B. migrate B" ], [ "2-am-then-b-move-a", "Advanced migrate logic, A then B, migrate A without stopping B" ], [ "3-am-then-bm-both-migrate", "Advanced migrate logic. A then B. migrate both" ], [ "4-am-then-bm-b-not-migratable", "Advanced migrate logic, A then B, B not migratable" ], [ "5-am-then-bm-a-not-migratable", "Advanced migrate logic. A then B. move both, a not migratable" ], [ "6-migrate-group", "Advanced migrate logic, migrate a group" ], [ "7-migrate-group-one-unmigratable", "Advanced migrate logic, migrate group mixed with allow-migrate true/false" ], [ "8-am-then-bm-a-migrating-b-stopping", "Advanced migrate logic, A then B, A migrating, B stopping" ], [ "9-am-then-bm-b-migrating-a-stopping", "Advanced migrate logic, A then B, B migrate, A stopping" ], [ "10-a-then-bm-b-move-a-clone", "Advanced migrate logic, A clone then B, migrate B while stopping A" ], [ "11-a-then-bm-b-move-a-clone-starting", "Advanced migrate logic, A clone then B, B moving while A is start/stopping" ], [ "a-promote-then-b-migrate", "A promote then B start. migrate B" ], [ "a-demote-then-b-migrate", "A demote then B stop. migrate B" ], [ "probe-target-of-failed-migrate_to-1", "Failed migrate_to, target rejoins" ], [ "probe-target-of-failed-migrate_to-2", "Failed migrate_to, target rejoined and probed" ], [ "partial-live-migration-multiple-active", "Prevent running on multiple nodes due to partial live migration" ], [ "migration-intermediary-cleaned", "Probe live-migration intermediary with no history" ], [ "bug-lf-2422", "Dependency on partially active group - stop ocfs:*" ], ], [ [ "clone-anon-probe-1", "Probe the correct (anonymous) clone instance for each node" ], [ "clone-anon-probe-2", "Avoid needless re-probing of anonymous clones" ], [ "clone-anon-failcount", "Merge failcounts for anonymous clones" ], [ "force-anon-clone-max", "Update clone-max properly when forcing a clone to be anonymous" ], [ "anon-instance-pending", "Assign anonymous clone instance numbers properly when action pending" ], [ "inc0", "Incarnation start" ], [ "inc1", "Incarnation start order" ], [ "inc2", "Incarnation silent restart, stop, move" ], [ "inc3", "Inter-incarnation ordering, silent restart, stop, move" ], [ "inc4", "Inter-incarnation ordering, silent restart, stop, move (ordered)" ], [ "inc5", "Inter-incarnation ordering, silent restart, stop, move (restart 1)" ], [ "inc6", "Inter-incarnation ordering, silent restart, stop, move (restart 2)" ], [ "inc7", "Clone colocation" ], [ "inc8", "Clone anti-colocation" ], [ "inc9", "Non-unique clone" ], [ "inc10", "Non-unique clone (stop)" ], [ "inc11", "Primitive colocation with clones" ], [ "inc12", "Clone shutdown" ], [ "cloned-group", "Make sure only the correct number of cloned groups are started" ], [ "cloned-group-stop", "Ensure stopping qpidd also stops glance and cinder" ], [ "clone-no-shuffle", "Don't prioritize allocation of instances that must be moved" ], [ "clone-recover-no-shuffle-1", "Don't shuffle instances when starting a new primitive instance" ], [ "clone-recover-no-shuffle-2", "Don't shuffle instances when starting a new group instance" ], [ "clone-recover-no-shuffle-3", "Don't shuffle instances when starting a new bundle instance" ], [ "clone-recover-no-shuffle-4", "Don't shuffle instances when starting a new primitive instance with " "location preference "], [ "clone-recover-no-shuffle-5", "Don't shuffle instances when starting a new group instance with " "location preference" ], [ "clone-recover-no-shuffle-6", "Don't shuffle instances when starting a new bundle instance with " "location preference" ], [ "clone-recover-no-shuffle-7", "Don't shuffle instances when starting a new primitive instance that " "will be promoted" ], [ "clone-recover-no-shuffle-8", "Don't shuffle instances when starting a new group instance that " "will be promoted " ], [ "clone-recover-no-shuffle-9", "Don't shuffle instances when starting a new bundle instance that " "will be promoted " ], [ "clone-recover-no-shuffle-10", "Don't shuffle instances when starting a new primitive instance that " "won't be promoted" ], [ "clone-recover-no-shuffle-11", "Don't shuffle instances when starting a new group instance that " "won't be promoted " ], [ "clone-recover-no-shuffle-12", "Don't shuffle instances when starting a new bundle instance that " "won't be promoted " ], [ "clone-max-zero", "Orphan processing with clone-max=0" ], [ "clone-anon-dup", "Bug LF#2087 - Correctly parse the state of anonymous clones that are active more than once per node" ], [ "bug-lf-2160", "Don't shuffle clones due to colocation" ], [ "bug-lf-2213", "clone-node-max enforcement for cloned groups" ], [ "bug-lf-2153", "Clone ordering constraints" ], [ "bug-lf-2361", "Ensure clones observe mandatory ordering constraints if the LHS is unrunnable" ], [ "bug-lf-2317", "Avoid needless restart of primitive depending on a clone" ], [ "bug-lf-2453", "Enforce mandatory clone ordering without colocation" ], [ "bug-lf-2508", "Correctly reconstruct the status of anonymous cloned groups" ], [ "bug-lf-2544", "Balanced clone placement" ], [ "bug-lf-2445", "Redistribute clones with node-max > 1 and stickiness = 0" ], [ "bug-lf-2574", "Avoid clone shuffle" ], [ "bug-lf-2581", "Avoid group restart due to unrelated clone (re)start" ], [ "bug-cl-5168", "Don't shuffle clones" ], [ "bug-cl-5170", "Prevent clone from starting with on-fail=block" ], [ "clone-fail-block-colocation", "Move colocated group when failed clone has on-fail=block" ], [ "clone-interleave-1", "Clone-3 cannot start on pcmk-1 due to interleaved ordering (no colocation)" ], [ "clone-interleave-2", "Clone-3 must stop on pcmk-1 due to interleaved ordering (no colocation)" ], [ "clone-interleave-3", "Clone-3 must be recovered on pcmk-1 due to interleaved ordering (no colocation)" ], [ "rebalance-unique-clones", "Rebalance unique clone instances with no stickiness" ], [ "clone-requires-quorum-recovery", "Clone with requires=quorum on failed node needing recovery" ], [ "clone-requires-quorum", "Clone with requires=quorum with presumed-inactive instance on failed node" ], ], [ [ "cloned_start_one", "order first clone then clone... first clone_min=2" ], [ "cloned_start_two", "order first clone then clone... first clone_min=2" ], [ "cloned_stop_one", "order first clone then clone... first clone_min=2" ], [ "cloned_stop_two", "order first clone then clone... first clone_min=2" ], [ "clone_min_interleave_start_one", "order first clone then clone... first clone_min=2 and then has interleave=true" ], [ "clone_min_interleave_start_two", "order first clone then clone... first clone_min=2 and then has interleave=true" ], [ "clone_min_interleave_stop_one", "order first clone then clone... first clone_min=2 and then has interleave=true" ], [ "clone_min_interleave_stop_two", "order first clone then clone... first clone_min=2 and then has interleave=true" ], [ "clone_min_start_one", "order first clone then primitive... first clone_min=2" ], [ "clone_min_start_two", "order first clone then primitive... first clone_min=2" ], [ "clone_min_stop_all", "order first clone then primitive... first clone_min=2" ], [ "clone_min_stop_one", "order first clone then primitive... first clone_min=2" ], [ "clone_min_stop_two", "order first clone then primitive... first clone_min=2" ], ], [ [ "unfence-startup", "Clean unfencing" ], [ "unfence-definition", "Unfencing when the agent changes" ], [ "unfence-parameters", "Unfencing when the agent parameters changes" ], [ "unfence-device", "Unfencing when a cluster has only fence devices" ], ], [ [ "promoted-0", "Stopped -> Unpromoted" ], [ "promoted-1", "Stopped -> Promote" ], [ "promoted-2", "Stopped -> Promote : notify" ], [ "promoted-3", "Stopped -> Promote : promoted location" ], [ "promoted-4", "Started -> Promote : promoted location" ], [ "promoted-5", "Promoted -> Promoted" ], [ "promoted-6", "Promoted -> Promoted (2)" ], [ "promoted-7", "Promoted -> Fenced" ], [ "promoted-8", "Promoted -> Fenced -> Moved" ], [ "promoted-9", "Stopped + Promotable + No quorum" ], [ "promoted-10", "Stopped -> Promotable : notify with monitor" ], [ "promoted-11", "Stopped -> Promote : colocation" ], [ "novell-239082", "Demote/Promote ordering" ], [ "novell-239087", "Stable promoted placement" ], [ "promoted-12", "Promotion based solely on rsc_location constraints" ], [ "promoted-13", "Include preferences of colocated resources when placing promoted" ], [ "promoted-demote", "Ordering when actions depends on demoting an unpromoted resource" ], [ "promoted-ordering", "Prevent resources from starting that need a promoted" ], [ "bug-1765", "Verify promoted-with-promoted colocation does not stop unpromoted instances" ], [ "promoted-group", "Promotion of cloned groups" ], [ "bug-lf-1852", "Don't shuffle promotable instances unnecessarily" ], [ "promoted-failed-demote", "Don't retry failed demote actions" ], [ "promoted-failed-demote-2", "Don't retry failed demote actions (notify=false)" ], [ "promoted-depend", "Ensure resources that depend on promoted instance don't get allocated until that does" ], [ "promoted-reattach", "Re-attach to a running promoted" ], [ "promoted-allow-start", "Don't include promoted score if it would prevent allocation" ], [ "promoted-colocation", "Allow promoted instances placemaker to be influenced by colocation constraints" ], [ "promoted-pseudo", "Make sure promote/demote pseudo actions are created correctly" ], [ "promoted-role", "Prevent target-role from promoting more than promoted-max instances" ], [ "bug-lf-2358", "Anti-colocation of promoted instances" ], [ "promoted-promotion-constraint", "Mandatory promoted colocation constraints" ], [ "unmanaged-promoted", "Ensure role is preserved for unmanaged resources" ], [ "promoted-unmanaged-monitor", "Start correct monitor for unmanaged promoted instances" ], [ "promoted-demote-2", "Demote does not clear past failure" ], [ "promoted-move", "Move promoted based on failure of colocated group" ], [ "promoted-probed-score", "Observe the promotion score of probed resources" ], [ "colocation_constraint_stops_promoted", "cl#5054 - Ensure promoted is demoted when stopped by colocation constraint" ], [ "colocation_constraint_stops_unpromoted", "cl#5054 - Ensure unpromoted is not demoted when stopped by colocation constraint" ], [ "order_constraint_stops_promoted", "cl#5054 - Ensure promoted is demoted when stopped by order constraint" ], [ "order_constraint_stops_unpromoted", "cl#5054 - Ensure unpromoted is not demoted when stopped by order constraint" ], [ "promoted_monitor_restart", "cl#5072 - Ensure promoted monitor operation will start after promotion" ], [ "bug-rh-880249", "Handle replacement of an m/s resource with a primitive" ], [ "bug-5143-ms-shuffle", "Prevent promoted instance shuffling due to promotion score" ], [ "promoted-demote-block", "Block promotion if demote fails with on-fail=block" ], [ "promoted-dependent-ban", "Don't stop instances from being active because a dependent is banned from that host" ], [ "promoted-stop", "Stop instances due to location constraint with role=Started" ], [ "promoted-partially-demoted-group", "Allow partially demoted group to finish demoting" ], [ "bug-cl-5213", "Ensure role colocation with -INFINITY is enforced" ], [ "bug-cl-5219", "Allow unrelated resources with a common colocation target to remain promoted" ], [ "promoted-asymmetrical-order", "Fix the behaviors of multi-state resources with asymmetrical ordering" ], [ "promoted-notify", "Promotion with notifications" ], [ "promoted-score-startup", "Use permanent promoted scores without LRM history" ], [ "failed-demote-recovery", "Recover resource in unpromoted role after demote fails" ], [ "failed-demote-recovery-promoted", "Recover resource in promoted role after demote fails" ], [ "on_fail_demote1", "Recovery with on-fail=\"demote\" on healthy cluster, remote, guest, and bundle nodes" ], [ "on_fail_demote2", "Recovery with on-fail=\"demote\" with promotion on different node" ], [ "on_fail_demote3", "Recovery with on-fail=\"demote\" with no promotion" ], [ "on_fail_demote4", "Recovery with on-fail=\"demote\" on failed cluster, remote, guest, and bundle nodes" ], [ "no_quorum_demote", "Promotable demotion and primitive stop with no-quorum-policy=\"demote\"" ], [ "no-promote-on-unrunnable-guest", "Don't select bundle instance for promotion when container can't run" ], [ "leftover-pending-monitor", "Prevent a leftover pending monitor from causing unexpected stop of other instances" ], ], [ [ "history-1", "Correctly parse stateful-1 resource state" ], ], [ [ "managed-0", "Managed (reference)" ], [ "managed-1", "Not managed - down" ], [ "managed-2", "Not managed - up" ], [ "bug-5028", "Shutdown should block if anything depends on an unmanaged resource" ], [ "bug-5028-detach", "Ensure detach still works" ], [ "bug-5028-bottom", "Ensure shutdown still blocks if the blocked resource is at the bottom of the stack" ], [ "unmanaged-stop-1", "cl#5155 - Block the stop of resources if any depending resource is unmanaged" ], [ "unmanaged-stop-2", "cl#5155 - Block the stop of resources if the first resource in a mandatory stop order is unmanaged" ], [ "unmanaged-stop-3", "cl#5155 - Block the stop of resources if any depending resource in a group is unmanaged" ], [ "unmanaged-stop-4", "cl#5155 - Block the stop of resources if any depending resource in the middle of a group is unmanaged" ], [ "unmanaged-block-restart", "Block restart of resources if any dependent resource in a group is unmanaged" ], ], [ [ "interleave-0", "Interleave (reference)" ], [ "interleave-1", "coloc - not interleaved" ], [ "interleave-2", "coloc - interleaved" ], [ "interleave-3", "coloc - interleaved (2)" ], [ "interleave-pseudo-stop", "Interleaved clone during stonith" ], [ "interleave-stop", "Interleaved clone during stop" ], [ "interleave-restart", "Interleaved clone during dependency restart" ], ], [ [ "notify-0", "Notify reference" ], [ "notify-1", "Notify simple" ], [ "notify-2", "Notify simple, confirm" ], [ "notify-3", "Notify move, confirm" ], [ "novell-239079", "Notification priority" ], #[ "notify-2", "Notify - 764" ], [ "notifs-for-unrunnable", "Don't schedule notifications for an unrunnable action" ], [ "route-remote-notify", "Route remote notify actions through correct cluster node" ], [ "notify-behind-stopping-remote", "Don't schedule notifications behind stopped remote" ], ], [ [ "594", "OSDL #594 - Unrunnable actions scheduled in transition" ], [ "662", "OSDL #662 - Two resources start on one node when incarnation_node_max = 1" ], [ "696", "OSDL #696 - CRM starts stonith RA without monitor" ], [ "726", "OSDL #726 - Attempting to schedule rsc_posic041_monitor_5000 _after_ a stop" ], [ "735", "OSDL #735 - Correctly detect that rsc_hadev1 is stopped on hadev3" ], [ "764", "OSDL #764 - Missing monitor op for DoFencing:child_DoFencing:1" ], [ "797", "OSDL #797 - Assert triggered: task_id_i > max_call_id" ], [ "829", "OSDL #829" ], [ "994", "OSDL #994 - Stopping the last resource in a resource group causes the entire group to be restarted" ], [ "994-2", "OSDL #994 - with a dependent resource" ], [ "1360", "OSDL #1360 - Clone stickiness" ], [ "1484", "OSDL #1484 - on_fail=stop" ], [ "1494", "OSDL #1494 - Clone stability" ], [ "unrunnable-1", "Unrunnable" ], [ "unrunnable-2", "Unrunnable 2" ], [ "stonith-0", "Stonith loop - 1" ], [ "stonith-1", "Stonith loop - 2" ], [ "stonith-2", "Stonith loop - 3" ], [ "stonith-3", "Stonith startup" ], [ "stonith-4", "Stonith node state" ], [ "dc-fence-ordering", "DC needs fencing while other nodes are shutting down" ], [ "bug-1572-1", "Recovery of groups depending on promotable role" ], [ "bug-1572-2", "Recovery of groups depending on promotable role when promoted is not re-promoted" ], [ "bug-1685", "Depends-on-promoted ordering" ], [ "bug-1822", "Don't promote partially active groups" ], [ "bug-pm-11", "New resource added to a m/s group" ], [ "bug-pm-12", "Recover only the failed portion of a cloned group" ], [ "bug-n-387749", "Don't shuffle clone instances" ], [ "bug-n-385265", "Don't ignore the failure stickiness of group children - resource_idvscommon should stay stopped" ], [ "bug-n-385265-2", "Ensure groups are migrated instead of remaining partially active on the current node" ], [ "bug-lf-1920", "Correctly handle probes that find active resources" ], [ "bnc-515172", "Location constraint with multiple expressions" ], [ "colocate-primitive-with-clone", "Optional colocation with a clone" ], [ "use-after-free-merge", "Use-after-free in native_merge_weights" ], [ "bug-lf-2551", "STONITH ordering for stop" ], [ "bug-lf-2606", "Stonith implies demote" ], [ "bug-lf-2474", "Ensure resource op timeout takes precedence over op_defaults" ], [ "bug-suse-707150", "Prevent vm-01 from starting due to colocation/ordering" ], [ "bug-5014-A-start-B-start", "Verify when A starts B starts using symmetrical=false" ], [ "bug-5014-A-stop-B-started", "Verify when A stops B does not stop if it has already started using symmetric=false" ], [ "bug-5014-A-stopped-B-stopped", "Verify when A is stopped and B has not started, B does not start before A using symmetric=false" ], [ "bug-5014-CthenAthenB-C-stopped", "Verify when C then A is symmetrical=true, A then B is symmetric=false, and C is stopped that nothing starts" ], [ "bug-5014-CLONE-A-start-B-start", "Verify when A starts B starts using clone resources with symmetric=false" ], [ "bug-5014-CLONE-A-stop-B-started", "Verify when A stops B does not stop if it has already started using clone resources with symmetric=false" ], [ "bug-5014-GROUP-A-start-B-start", "Verify when A starts B starts when using group resources with symmetric=false" ], [ "bug-5014-GROUP-A-stopped-B-started", "Verify when A stops B does not stop if it has already started using group resources with symmetric=false" ], [ "bug-5014-GROUP-A-stopped-B-stopped", "Verify when A is stopped and B has not started, B does not start before A using group resources with symmetric=false" ], [ "bug-5014-ordered-set-symmetrical-false", "Verify ordered sets work with symmetrical=false" ], [ "bug-5014-ordered-set-symmetrical-true", "Verify ordered sets work with symmetrical=true" ], [ "clbz5007-promotable-colocation", "Verify use of colocation scores other than INFINITY and -INFINITY work on multi-state resources" ], [ "bug-5038", "Prevent restart of anonymous clones when clone-max decreases" ], [ "bug-5025-1", "Automatically clean up failcount after resource config change with reload" ], [ "bug-5025-2", "Make sure clear failcount action isn't set when config does not change" ], [ "bug-5025-3", "Automatically clean up failcount after resource config change with restart" ], [ "bug-5025-4", "Clear failcount when last failure is a start op and rsc attributes changed" ], [ "failcount", "Ensure failcounts are correctly expired" ], [ "failcount-block", "Ensure failcounts are not expired when on-fail=block is present" ], [ "per-op-failcount", "Ensure per-operation failcount is handled and not passed to fence agent" ], [ "on-fail-ignore", "Ensure on-fail=ignore works even beyond migration-threshold" ], [ "monitor-onfail-restart", "bug-5058 - Monitor failure with on-fail set to restart" ], [ "monitor-onfail-stop", "bug-5058 - Monitor failure wiht on-fail set to stop" ], [ "bug-5059", "No need to restart p_stateful1:*" ], [ "bug-5069-op-enabled", "Test on-fail=ignore with failure when monitor is enabled" ], [ "bug-5069-op-disabled", "Test on-fail-ignore with failure when monitor is disabled" ], [ "obsolete-lrm-resource", "cl#5115 - Do not use obsolete lrm_resource sections" ], [ "expire-non-blocked-failure", "Ignore failure-timeout only if the failed operation has on-fail=block" ], [ "asymmetrical-order-move", "Respect asymmetrical ordering when trying to move resources" ], [ "asymmetrical-order-restart", "Respect asymmetrical ordering when restarting dependent resource" ], [ "start-then-stop-with-unfence", "Avoid graph loop with start-then-stop constraint plus unfencing" ], [ "order-expired-failure", "Order failcount cleanup after remote fencing" ], [ "expired-stop-1", "Expired stop failure should not block resource" ], [ "ignore_stonith_rsc_order1", "cl#5056- Ignore order constraint between stonith and non-stonith rsc" ], [ "ignore_stonith_rsc_order2", "cl#5056- Ignore order constraint with group rsc containing mixed stonith and non-stonith" ], [ "ignore_stonith_rsc_order3", "cl#5056- Ignore order constraint, stonith clone and mixed group" ], [ "ignore_stonith_rsc_order4", "cl#5056- Ignore order constraint, stonith clone and clone with nested mixed group" ], [ "honor_stonith_rsc_order1", "cl#5056- Honor order constraint, stonith clone and pure stonith group(single rsc)" ], [ "honor_stonith_rsc_order2", "cl#5056- Honor order constraint, stonith clone and pure stonith group(multiple rsc)" ], [ "honor_stonith_rsc_order3", "cl#5056- Honor order constraint, stonith clones with nested pure stonith group" ], [ "honor_stonith_rsc_order4", "cl#5056- Honor order constraint, between two native stonith rscs" ], [ "multiply-active-stonith", "Multiply active stonith" ], [ "probe-timeout", "cl#5099 - Default probe timeout" ], [ "order-first-probes", "cl#5301 - respect order constraints when relevant resources are being probed" ], [ "concurrent-fencing", "Allow performing fencing operations in parallel" ], [ "priority-fencing-delay", "Delay fencing targeting the more significant node" ], [ "pending-node-no-uname", "Do not fence a pending node that doesn't have an uname in node state yet" ], [ "node-pending-timeout", "Fence a pending node that has reached `node-pending-timeout`" ], ], [ [ "systemhealth1", "System Health () #1" ], [ "systemhealth2", "System Health () #2" ], [ "systemhealth3", "System Health () #3" ], [ "systemhealthn1", "System Health (None) #1" ], [ "systemhealthn2", "System Health (None) #2" ], [ "systemhealthn3", "System Health (None) #3" ], [ "systemhealthm1", "System Health (Migrate On Red) #1" ], [ "systemhealthm2", "System Health (Migrate On Red) #2" ], [ "systemhealthm3", "System Health (Migrate On Red) #3" ], [ "systemhealtho1", "System Health (Only Green) #1" ], [ "systemhealtho2", "System Health (Only Green) #2" ], [ "systemhealtho3", "System Health (Only Green) #3" ], [ "systemhealthp1", "System Health (Progessive) #1" ], [ "systemhealthp2", "System Health (Progessive) #2" ], [ "systemhealthp3", "System Health (Progessive) #3" ], [ "allow-unhealthy-nodes", "System Health (migrate-on-red + allow-unhealth-nodes)" ], ], [ [ "utilization", "Placement Strategy - utilization" ], [ "minimal", "Placement Strategy - minimal" ], [ "balanced", "Placement Strategy - balanced" ], ], [ [ "placement-stickiness", "Optimized Placement Strategy - stickiness" ], [ "placement-priority", "Optimized Placement Strategy - priority" ], [ "placement-location", "Optimized Placement Strategy - location" ], [ "placement-capacity", "Optimized Placement Strategy - capacity" ], ], [ [ "utilization-order1", "Utilization Order - Simple" ], [ "utilization-order2", "Utilization Order - Complex" ], [ "utilization-order3", "Utilization Order - Migrate" ], [ "utilization-order4", "Utilization Order - Live Migration (bnc#695440)" ], [ "utilization-complex", "Utilization with complex relationships" ], [ "utilization-shuffle", "Don't displace prmExPostgreSQLDB2 on act2, Start prmExPostgreSQLDB1 on act3" ], [ "load-stopped-loop", "Avoid transition loop due to load_stopped (cl#5044)" ], [ "load-stopped-loop-2", "cl#5235 - Prevent graph loops that can be introduced by load_stopped -> migrate_to ordering" ], ], [ [ "colocated-utilization-primitive-1", "Colocated Utilization - Primitive" ], [ "colocated-utilization-primitive-2", "Colocated Utilization - Choose the most capable node" ], [ "colocated-utilization-group", "Colocated Utilization - Group" ], [ "colocated-utilization-clone", "Colocated Utilization - Clone" ], [ "utilization-check-allowed-nodes", "Only check the capacities of the nodes that can run the resource" ], ], [ [ "reprobe-target_rc", "Ensure correct target_rc for reprobe of inactive resources" ], [ "node-maintenance-1", "cl#5128 - Node maintenance" ], [ "node-maintenance-2", "cl#5128 - Node maintenance (coming out of maintenance mode)" ], [ "shutdown-maintenance-node", "Do not fence a maintenance node if it shuts down cleanly" ], [ "rsc-maintenance", "Per-resource maintenance" ], ], [ [ "not-installed-agent", "The resource agent is missing" ], [ "not-installed-tools", "Something the resource agent needs is missing" ], ], [ [ "stopped-monitor-00", "Stopped Monitor - initial start" ], [ "stopped-monitor-01", "Stopped Monitor - failed started" ], [ "stopped-monitor-02", "Stopped Monitor - started multi-up" ], [ "stopped-monitor-03", "Stopped Monitor - stop started" ], [ "stopped-monitor-04", "Stopped Monitor - failed stop" ], [ "stopped-monitor-05", "Stopped Monitor - start unmanaged" ], [ "stopped-monitor-06", "Stopped Monitor - unmanaged multi-up" ], [ "stopped-monitor-07", "Stopped Monitor - start unmanaged multi-up" ], [ "stopped-monitor-08", "Stopped Monitor - migrate" ], [ "stopped-monitor-09", "Stopped Monitor - unmanage started" ], [ "stopped-monitor-10", "Stopped Monitor - unmanaged started multi-up" ], [ "stopped-monitor-11", "Stopped Monitor - stop unmanaged started" ], [ "stopped-monitor-12", "Stopped Monitor - unmanaged started multi-up (target-role=Stopped)" ], [ "stopped-monitor-20", "Stopped Monitor - initial stop" ], [ "stopped-monitor-21", "Stopped Monitor - stopped single-up" ], [ "stopped-monitor-22", "Stopped Monitor - stopped multi-up" ], [ "stopped-monitor-23", "Stopped Monitor - start stopped" ], [ "stopped-monitor-24", "Stopped Monitor - unmanage stopped" ], [ "stopped-monitor-25", "Stopped Monitor - unmanaged stopped multi-up" ], [ "stopped-monitor-26", "Stopped Monitor - start unmanaged stopped" ], [ "stopped-monitor-27", "Stopped Monitor - unmanaged stopped multi-up (target-role=Started)" ], [ "stopped-monitor-30", "Stopped Monitor - new node started" ], [ "stopped-monitor-31", "Stopped Monitor - new node stopped" ], ], [ # This is a combo test to check: # - probe timeout defaults to the minimum-interval monitor's # - duplicate recurring operations are ignored # - if timeout spec is bad, the default timeout is used # - failure is blocked with on-fail=block even if ISO8601 interval is specified # - started/stopped role monitors are started/stopped on right nodes [ "intervals", "Recurring monitor interval handling" ], ], [ [ "ticket-primitive-1", "Ticket - Primitive (loss-policy=stop, initial)" ], [ "ticket-primitive-2", "Ticket - Primitive (loss-policy=stop, granted)" ], [ "ticket-primitive-3", "Ticket - Primitive (loss-policy-stop, revoked)" ], [ "ticket-primitive-4", "Ticket - Primitive (loss-policy=demote, initial)" ], [ "ticket-primitive-5", "Ticket - Primitive (loss-policy=demote, granted)" ], [ "ticket-primitive-6", "Ticket - Primitive (loss-policy=demote, revoked)" ], [ "ticket-primitive-7", "Ticket - Primitive (loss-policy=fence, initial)" ], [ "ticket-primitive-8", "Ticket - Primitive (loss-policy=fence, granted)" ], [ "ticket-primitive-9", "Ticket - Primitive (loss-policy=fence, revoked)" ], [ "ticket-primitive-10", "Ticket - Primitive (loss-policy=freeze, initial)" ], [ "ticket-primitive-11", "Ticket - Primitive (loss-policy=freeze, granted)" ], [ "ticket-primitive-12", "Ticket - Primitive (loss-policy=freeze, revoked)" ], [ "ticket-primitive-13", "Ticket - Primitive (loss-policy=stop, standby, granted)" ], [ "ticket-primitive-14", "Ticket - Primitive (loss-policy=stop, granted, standby)" ], [ "ticket-primitive-15", "Ticket - Primitive (loss-policy=stop, standby, revoked)" ], [ "ticket-primitive-16", "Ticket - Primitive (loss-policy=demote, standby, granted)" ], [ "ticket-primitive-17", "Ticket - Primitive (loss-policy=demote, granted, standby)" ], [ "ticket-primitive-18", "Ticket - Primitive (loss-policy=demote, standby, revoked)" ], [ "ticket-primitive-19", "Ticket - Primitive (loss-policy=fence, standby, granted)" ], [ "ticket-primitive-20", "Ticket - Primitive (loss-policy=fence, granted, standby)" ], [ "ticket-primitive-21", "Ticket - Primitive (loss-policy=fence, standby, revoked)" ], [ "ticket-primitive-22", "Ticket - Primitive (loss-policy=freeze, standby, granted)" ], [ "ticket-primitive-23", "Ticket - Primitive (loss-policy=freeze, granted, standby)" ], [ "ticket-primitive-24", "Ticket - Primitive (loss-policy=freeze, standby, revoked)" ], ], [ [ "ticket-group-1", "Ticket - Group (loss-policy=stop, initial)" ], [ "ticket-group-2", "Ticket - Group (loss-policy=stop, granted)" ], [ "ticket-group-3", "Ticket - Group (loss-policy-stop, revoked)" ], [ "ticket-group-4", "Ticket - Group (loss-policy=demote, initial)" ], [ "ticket-group-5", "Ticket - Group (loss-policy=demote, granted)" ], [ "ticket-group-6", "Ticket - Group (loss-policy=demote, revoked)" ], [ "ticket-group-7", "Ticket - Group (loss-policy=fence, initial)" ], [ "ticket-group-8", "Ticket - Group (loss-policy=fence, granted)" ], [ "ticket-group-9", "Ticket - Group (loss-policy=fence, revoked)" ], [ "ticket-group-10", "Ticket - Group (loss-policy=freeze, initial)" ], [ "ticket-group-11", "Ticket - Group (loss-policy=freeze, granted)" ], [ "ticket-group-12", "Ticket - Group (loss-policy=freeze, revoked)" ], [ "ticket-group-13", "Ticket - Group (loss-policy=stop, standby, granted)" ], [ "ticket-group-14", "Ticket - Group (loss-policy=stop, granted, standby)" ], [ "ticket-group-15", "Ticket - Group (loss-policy=stop, standby, revoked)" ], [ "ticket-group-16", "Ticket - Group (loss-policy=demote, standby, granted)" ], [ "ticket-group-17", "Ticket - Group (loss-policy=demote, granted, standby)" ], [ "ticket-group-18", "Ticket - Group (loss-policy=demote, standby, revoked)" ], [ "ticket-group-19", "Ticket - Group (loss-policy=fence, standby, granted)" ], [ "ticket-group-20", "Ticket - Group (loss-policy=fence, granted, standby)" ], [ "ticket-group-21", "Ticket - Group (loss-policy=fence, standby, revoked)" ], [ "ticket-group-22", "Ticket - Group (loss-policy=freeze, standby, granted)" ], [ "ticket-group-23", "Ticket - Group (loss-policy=freeze, granted, standby)" ], [ "ticket-group-24", "Ticket - Group (loss-policy=freeze, standby, revoked)" ], ], [ [ "ticket-clone-1", "Ticket - Clone (loss-policy=stop, initial)" ], [ "ticket-clone-2", "Ticket - Clone (loss-policy=stop, granted)" ], [ "ticket-clone-3", "Ticket - Clone (loss-policy-stop, revoked)" ], [ "ticket-clone-4", "Ticket - Clone (loss-policy=demote, initial)" ], [ "ticket-clone-5", "Ticket - Clone (loss-policy=demote, granted)" ], [ "ticket-clone-6", "Ticket - Clone (loss-policy=demote, revoked)" ], [ "ticket-clone-7", "Ticket - Clone (loss-policy=fence, initial)" ], [ "ticket-clone-8", "Ticket - Clone (loss-policy=fence, granted)" ], [ "ticket-clone-9", "Ticket - Clone (loss-policy=fence, revoked)" ], [ "ticket-clone-10", "Ticket - Clone (loss-policy=freeze, initial)" ], [ "ticket-clone-11", "Ticket - Clone (loss-policy=freeze, granted)" ], [ "ticket-clone-12", "Ticket - Clone (loss-policy=freeze, revoked)" ], [ "ticket-clone-13", "Ticket - Clone (loss-policy=stop, standby, granted)" ], [ "ticket-clone-14", "Ticket - Clone (loss-policy=stop, granted, standby)" ], [ "ticket-clone-15", "Ticket - Clone (loss-policy=stop, standby, revoked)" ], [ "ticket-clone-16", "Ticket - Clone (loss-policy=demote, standby, granted)" ], [ "ticket-clone-17", "Ticket - Clone (loss-policy=demote, granted, standby)" ], [ "ticket-clone-18", "Ticket - Clone (loss-policy=demote, standby, revoked)" ], [ "ticket-clone-19", "Ticket - Clone (loss-policy=fence, standby, granted)" ], [ "ticket-clone-20", "Ticket - Clone (loss-policy=fence, granted, standby)" ], [ "ticket-clone-21", "Ticket - Clone (loss-policy=fence, standby, revoked)" ], [ "ticket-clone-22", "Ticket - Clone (loss-policy=freeze, standby, granted)" ], [ "ticket-clone-23", "Ticket - Clone (loss-policy=freeze, granted, standby)" ], [ "ticket-clone-24", "Ticket - Clone (loss-policy=freeze, standby, revoked)" ], ], [ [ "ticket-promoted-1", "Ticket - Promoted (loss-policy=stop, initial)" ], [ "ticket-promoted-2", "Ticket - Promoted (loss-policy=stop, granted)" ], [ "ticket-promoted-3", "Ticket - Promoted (loss-policy-stop, revoked)" ], [ "ticket-promoted-4", "Ticket - Promoted (loss-policy=demote, initial)" ], [ "ticket-promoted-5", "Ticket - Promoted (loss-policy=demote, granted)" ], [ "ticket-promoted-6", "Ticket - Promoted (loss-policy=demote, revoked)" ], [ "ticket-promoted-7", "Ticket - Promoted (loss-policy=fence, initial)" ], [ "ticket-promoted-8", "Ticket - Promoted (loss-policy=fence, granted)" ], [ "ticket-promoted-9", "Ticket - Promoted (loss-policy=fence, revoked)" ], [ "ticket-promoted-10", "Ticket - Promoted (loss-policy=freeze, initial)" ], [ "ticket-promoted-11", "Ticket - Promoted (loss-policy=freeze, granted)" ], [ "ticket-promoted-12", "Ticket - Promoted (loss-policy=freeze, revoked)" ], [ "ticket-promoted-13", "Ticket - Promoted (loss-policy=stop, standby, granted)" ], [ "ticket-promoted-14", "Ticket - Promoted (loss-policy=stop, granted, standby)" ], [ "ticket-promoted-15", "Ticket - Promoted (loss-policy=stop, standby, revoked)" ], [ "ticket-promoted-16", "Ticket - Promoted (loss-policy=demote, standby, granted)" ], [ "ticket-promoted-17", "Ticket - Promoted (loss-policy=demote, granted, standby)" ], [ "ticket-promoted-18", "Ticket - Promoted (loss-policy=demote, standby, revoked)" ], [ "ticket-promoted-19", "Ticket - Promoted (loss-policy=fence, standby, granted)" ], [ "ticket-promoted-20", "Ticket - Promoted (loss-policy=fence, granted, standby)" ], [ "ticket-promoted-21", "Ticket - Promoted (loss-policy=fence, standby, revoked)" ], [ "ticket-promoted-22", "Ticket - Promoted (loss-policy=freeze, standby, granted)" ], [ "ticket-promoted-23", "Ticket - Promoted (loss-policy=freeze, granted, standby)" ], [ "ticket-promoted-24", "Ticket - Promoted (loss-policy=freeze, standby, revoked)" ], ], [ [ "ticket-rsc-sets-1", "Ticket - Resource sets (1 ticket, initial)" ], [ "ticket-rsc-sets-2", "Ticket - Resource sets (1 ticket, granted)" ], [ "ticket-rsc-sets-3", "Ticket - Resource sets (1 ticket, revoked)" ], [ "ticket-rsc-sets-4", "Ticket - Resource sets (2 tickets, initial)" ], [ "ticket-rsc-sets-5", "Ticket - Resource sets (2 tickets, granted)" ], [ "ticket-rsc-sets-6", "Ticket - Resource sets (2 tickets, granted)" ], [ "ticket-rsc-sets-7", "Ticket - Resource sets (2 tickets, revoked)" ], [ "ticket-rsc-sets-8", "Ticket - Resource sets (1 ticket, standby, granted)" ], [ "ticket-rsc-sets-9", "Ticket - Resource sets (1 ticket, granted, standby)" ], [ "ticket-rsc-sets-10", "Ticket - Resource sets (1 ticket, standby, revoked)" ], [ "ticket-rsc-sets-11", "Ticket - Resource sets (2 tickets, standby, granted)" ], [ "ticket-rsc-sets-12", "Ticket - Resource sets (2 tickets, standby, granted)" ], [ "ticket-rsc-sets-13", "Ticket - Resource sets (2 tickets, granted, standby)" ], [ "ticket-rsc-sets-14", "Ticket - Resource sets (2 tickets, standby, revoked)" ], [ "cluster-specific-params", "Cluster-specific instance attributes based on rules" ], [ "site-specific-params", "Site-specific instance attributes based on rules" ], ], [ [ "template-1", "Template - 1" ], [ "template-2", "Template - 2" ], [ "template-3", "Template - 3 (merge operations)" ], [ "template-coloc-1", "Template - Colocation 1" ], [ "template-coloc-2", "Template - Colocation 2" ], [ "template-coloc-3", "Template - Colocation 3" ], [ "template-order-1", "Template - Order 1" ], [ "template-order-2", "Template - Order 2" ], [ "template-order-3", "Template - Order 3" ], [ "template-ticket", "Template - Ticket" ], [ "template-rsc-sets-1", "Template - Resource Sets 1" ], [ "template-rsc-sets-2", "Template - Resource Sets 2" ], [ "template-rsc-sets-3", "Template - Resource Sets 3" ], [ "template-rsc-sets-4", "Template - Resource Sets 4" ], [ "template-clone-primitive", "Cloned primitive from template" ], [ "template-clone-group", "Cloned group from template" ], [ "location-sets-templates", "Resource sets and templates - Location" ], [ "tags-coloc-order-1", "Tags - Colocation and Order (Simple)" ], [ "tags-coloc-order-2", "Tags - Colocation and Order (Resource Sets with Templates)" ], [ "tags-location", "Tags - Location" ], [ "tags-ticket", "Tags - Ticket" ], ], [ [ "container-1", "Container - initial" ], [ "container-2", "Container - monitor failed" ], [ "container-3", "Container - stop failed" ], [ "container-4", "Container - reached migration-threshold" ], [ "container-group-1", "Container in group - initial" ], [ "container-group-2", "Container in group - monitor failed" ], [ "container-group-3", "Container in group - stop failed" ], [ "container-group-4", "Container in group - reached migration-threshold" ], [ "container-is-remote-node", "Place resource within container when container is remote-node" ], [ "bug-rh-1097457", "Kill user defined container/contents ordering" ], [ "bug-cl-5247", "Graph loop when recovering m/s resource in a container" ], [ "bundle-order-startup", "Bundle startup ordering" ], [ "bundle-order-partial-start", "Bundle startup ordering when some dependencies are already running" ], [ "bundle-order-partial-start-2", "Bundle startup ordering when some dependencies and the container are already running" ], [ "bundle-order-stop", "Bundle stop ordering" ], [ "bundle-order-partial-stop", "Bundle startup ordering when some dependencies are already stopped" ], [ "bundle-order-stop-on-remote", "Stop nested resource after bringing up the connection" ], [ "bundle-order-startup-clone", "Prevent startup because bundle isn't promoted" ], [ "bundle-order-startup-clone-2", "Bundle startup with clones" ], [ "bundle-order-stop-clone", "Stop bundle because clone is stopping" ], [ "bundle-interleave-start", "Interleave bundle starts" ], [ "bundle-interleave-promote", "Interleave bundle promotes" ], [ "bundle-nested-colocation", "Colocation of nested connection resources" ], [ "bundle-order-fencing", "Order pseudo bundle fencing after parent node fencing if both are happening" ], [ "bundle-probe-order-1", "order 1" ], [ "bundle-probe-order-2", "order 2" ], [ "bundle-probe-order-3", "order 3" ], [ "bundle-probe-remotes", "Ensure remotes get probed too" ], [ "bundle-replicas-change", "Change bundle from 1 replica to multiple" ], [ "bundle-connection-with-container", "Don't move a container due to connection preferences" ], [ "nested-remote-recovery", "Recover bundle's container hosted on remote node" ], [ "bundle-promoted-location-1", "Promotable bundle, positive location" ], [ "bundle-promoted-location-2", "Promotable bundle, negative location" ], [ "bundle-promoted-location-3", "Promotable bundle, positive location for promoted role" ], [ "bundle-promoted-location-4", "Promotable bundle, negative location for promoted role" ], [ "bundle-promoted-location-5", "Promotable bundle, positive location for unpromoted role" ], [ "bundle-promoted-location-6", "Promotable bundle, negative location for unpromoted role" ], [ "bundle-promoted-colocation-1", "Primary promoted bundle, dependent primitive (mandatory coloc)" ], [ "bundle-promoted-colocation-2", "Primary promoted bundle, dependent primitive (optional coloc)" ], [ "bundle-promoted-colocation-3", "Dependent promoted bundle, primary primitive (mandatory coloc)" ], [ "bundle-promoted-colocation-4", "Dependent promoted bundle, primary primitive (optional coloc)" ], [ "bundle-promoted-colocation-5", "Primary and dependent promoted bundle instances (mandatory coloc)" ], [ "bundle-promoted-colocation-6", "Primary and dependent promoted bundle instances (optional coloc)" ], [ "bundle-promoted-anticolocation-1", "Primary promoted bundle, dependent primitive (mandatory anti)" ], [ "bundle-promoted-anticolocation-2", "Primary promoted bundle, dependent primitive (optional anti)" ], [ "bundle-promoted-anticolocation-3", "Dependent promoted bundle, primary primitive (mandatory anti)" ], [ "bundle-promoted-anticolocation-4", "Dependent promoted bundle, primary primitive (optional anti)" ], [ "bundle-promoted-anticolocation-5", "Primary and dependent promoted bundle instances (mandatory anti)" ], [ "bundle-promoted-anticolocation-6", "Primary and dependent promoted bundle instances (optional anti)" ], ], [ [ "whitebox-fail1", "Fail whitebox container rsc" ], [ "whitebox-fail2", "Fail cluster connection to guest node" ], [ "whitebox-fail3", "Failed containers should not run nested on remote nodes" ], [ "whitebox-start", "Start whitebox container with resources assigned to it" ], [ "whitebox-stop", "Stop whitebox container with resources assigned to it" ], [ "whitebox-move", "Move whitebox container with resources assigned to it" ], [ "whitebox-asymmetric", "Verify connection rsc opts-in based on container resource" ], [ "whitebox-ms-ordering", "Verify promote/demote can not occur before connection is established" ], [ "whitebox-ms-ordering-move", "Stop/Start cycle within a moving container" ], [ "whitebox-orphaned", "Properly shutdown orphaned whitebox container" ], [ "whitebox-orphan-ms", "Properly tear down orphan ms resources on remote-nodes" ], [ "whitebox-unexpectedly-running", "Recover container nodes the cluster did not start" ], [ "whitebox-migrate1", "Migrate both container and connection resource" ], [ "whitebox-imply-stop-on-fence", "imply stop action on container node rsc when host node is fenced" ], [ "whitebox-nested-group", "Verify guest remote-node works nested in a group" ], [ "guest-node-host-dies", "Verify guest node is recovered if host goes away" ], [ "guest-node-cleanup", "Order guest node connection recovery after container probe" ], [ "guest-host-not-fenceable", "Actions on guest node are unrunnable if host is unclean and cannot be fenced" ], ], [ [ "remote-startup-probes", "Baremetal remote-node startup probes" ], [ "remote-startup", "Startup a newly discovered remote-nodes with no status" ], [ "remote-fence-unclean", "Fence unclean baremetal remote-node" ], [ "remote-fence-unclean2", "Fence baremetal remote-node after cluster node fails and connection can not be recovered" ], [ "remote-fence-unclean-3", "Probe failed remote nodes (triggers fencing)" ], [ "remote-move", "Move remote-node connection resource" ], [ "remote-disable", "Disable a baremetal remote-node" ], [ "remote-probe-disable", "Probe then stop a baremetal remote-node" ], [ "remote-orphaned", "Properly shutdown orphaned connection resource" ], [ "remote-orphaned2", "verify we can handle orphaned remote connections with active resources on the remote" ], [ "remote-recover", "Recover connection resource after cluster-node fails" ], [ "remote-stale-node-entry", "Make sure we properly handle leftover remote-node entries in the node section" ], [ "remote-partial-migrate", "Make sure partial migrations are handled before ops on the remote node" ], [ "remote-partial-migrate2", "Make sure partial migration target is prefered for remote connection" ], [ "remote-recover-fail", "Make sure start failure causes fencing if rsc are active on remote" ], [ "remote-start-fail", "Make sure a start failure does not result in fencing if no active resources are on remote" ], [ "remote-unclean2", "Make monitor failure always results in fencing, even if no rsc are active on remote" ], [ "remote-fence-before-reconnect", "Fence before clearing recurring monitor failure" ], [ "remote-recovery", "Recover remote connections before attempting demotion" ], [ "remote-recover-connection", "Optimistically recovery of only the connection" ], [ "remote-recover-all", "Fencing when the connection has no home" ], [ "remote-recover-no-resources", "Fencing when the connection has no home and no active resources" ], [ "remote-recover-unknown", "Fencing when the connection has no home and the remote has no operation history" ], [ "remote-reconnect-delay", "Waiting for remote reconnect interval to expire" ], [ "remote-connection-unrecoverable", "Remote connection host must be fenced, with connection unrecoverable" ], [ "remote-connection-shutdown", "Remote connection shutdown" ], [ "cancel-behind-moving-remote", "Route recurring monitor cancellations through original node of a moving remote connection" ], ], [ [ "resource-discovery", "Exercises resource-discovery location constraint option" ], [ "rsc-discovery-per-node", "Disable resource discovery per node" ], [ "shutdown-lock", "Ensure shutdown lock works properly" ], [ "shutdown-lock-expiration", "Ensure shutdown lock expiration works properly" ], ], [ [ "op-defaults", "Test op_defaults conditional expressions" ], [ "op-defaults-2", "Test op_defaults AND'ed conditional expressions" ], [ "op-defaults-3", "Test op_defaults precedence" ], [ "rsc-defaults", "Test rsc_defaults conditional expressions" ], [ "rsc-defaults-2", "Test rsc_defaults conditional expressions without type" ], ], [ [ "stop-all-resources", "Test stop-all-resources=true "], ], [ [ "ocf_degraded-remap-ocf_ok", "Test degraded remapped to OK" ], [ "ocf_degraded_promoted-remap-ocf_ok", "Test degraded promoted remapped to OK"], ], ] TESTS_64BIT = [ [ [ "year-2038", "Check handling of timestamps beyond 2038-01-19 03:14:08 UTC" ], ], ] def is_executable(path): """ Check whether a file at a given path is executable. """ try: return os.stat(path)[stat.ST_MODE] & stat.S_IXUSR except OSError: return False def diff(file1, file2, **kwargs): """ Call diff on two files """ return subprocess.call([ "diff", "-u", "-N", "--ignore-all-space", "--ignore-blank-lines", file1, file2 ], **kwargs) def sort_file(filename): """ Sort a file alphabetically """ with io.open(filename, "rt") as f: lines = sorted(f) with io.open(filename, "wt") as f: f.writelines(lines) def remove_files(filenames): """ Remove a list of files """ for filename in filenames: try: os.remove(filename) except OSError: pass def normalize(filename): """ Remove text from a file that isn't important for comparison """ if not hasattr(normalize, "patterns"): normalize.patterns = [ re.compile(r'crm_feature_set="[^"]*"'), re.compile(r'batch-limit="[0-9]*"') ] if os.path.isfile(filename): with io.open(filename, "rt") as f: lines = f.readlines() with io.open(filename, "wt") as f: for line in lines: for pattern in normalize.patterns: line = pattern.sub("", line) f.write(line) def cat(filename, dest=sys.stdout): """ Copy a file to a destination file descriptor """ with io.open(filename, "rt") as f: shutil.copyfileobj(f, dest) class CtsScheduler(object): """ Regression tests for Pacemaker's scheduler """ def _parse_args(self, argv): """ Parse command-line arguments """ parser = argparse.ArgumentParser(description=DESC) parser.add_argument('-V', '--verbose', action='count', help='Display any differences from expected output') parser.add_argument('--run', metavar='TEST', help=('Run only single specified test (any further ' 'arguments will be passed to crm_simulate)')) parser.add_argument('--update', action='store_true', help='Update expected results with actual results') parser.add_argument('-b', '--binary', metavar='PATH', help='Specify path to crm_simulate') parser.add_argument('-i', '--io-dir', metavar='PATH', help='Specify path to regression test data directory') parser.add_argument('-o', '--out-dir', metavar='PATH', help='Specify where intermediate and output files should go') parser.add_argument('-v', '--valgrind', action='store_true', help='Run all commands under valgrind') parser.add_argument('--valgrind-dhat', action='store_true', help='Run all commands under valgrind with heap analyzer') parser.add_argument('--valgrind-skip-output', action='store_true', help='If running under valgrind, do not display output') parser.add_argument('--testcmd-options', metavar='OPTIONS', default='', help='Additional options for command under test') # argparse can't handle "everything after --run TEST", so grab that self.single_test_args = [] narg = 0 for arg in argv: narg = narg + 1 if arg == '--run': (argv, self.single_test_args) = (argv[:narg+1], argv[narg+1:]) break self.args = parser.parse_args(argv[1:]) def _error(self, s): print(" * ERROR: %s" % s) def _failed(self, s): print(" * FAILED: %s" % s) def _get_valgrind_cmd(self): """ Return command arguments needed (or not) to run valgrind """ if self.args.valgrind: os.environ['G_SLICE'] = "always-malloc" return [ "valgrind", "-q", "--gen-suppressions=all", "--time-stamp=yes", "--trace-children=no", "--show-reachable=no", "--leak-check=full", "--num-callers=20", "--suppressions=%s/valgrind-pcmk.suppressions" % (self.test_home) ] if self.args.valgrind_dhat: os.environ['G_SLICE'] = "always-malloc" return [ "valgrind", "--tool=exp-dhat", "--time-stamp=yes", "--trace-children=no", "--show-top-n=100", "--num-callers=4" ] return [] def _get_simulator_cmd(self): """ Locate the simulation binary """ if self.args.binary is None: self.args.binary = BuildOptions._BUILD_DIR + "/tools/crm_simulate" if not is_executable(self.args.binary): self.args.binary = BuildOptions.SBIN_DIR + "/crm_simulate" if not is_executable(self.args.binary): # @TODO it would be more pythonic to raise an exception self._error("Test binary " + self.args.binary + " not found") sys.exit(ExitStatus.NOT_INSTALLED) return [ self.args.binary ] + shlex.split(self.args.testcmd_options) def set_schema_env(self): """ Ensure schema directory environment variable is set, if possible """ try: return os.environ['PCMK_schema_directory'] except KeyError: for d in [ os.path.join(BuildOptions._BUILD_DIR, "xml"), BuildOptions.SCHEMA_DIR ]: if os.path.isdir(d): os.environ['PCMK_schema_directory'] = d return d return None def __init__(self, argv=sys.argv): # Ensure all command output is in portable locale for comparison os.environ['LC_ALL'] = "C" self._parse_args(argv) # Where this executable lives self.test_home = os.path.dirname(os.path.realpath(argv[0])) # Where test data resides if self.args.io_dir is None: self.args.io_dir = os.path.join(self.test_home, "scheduler") self.xml_input_dir = os.path.join(self.args.io_dir, "xml") self.expected_dir = os.path.join(self.args.io_dir, "exp") self.dot_expected_dir = os.path.join(self.args.io_dir, "dot") self.scores_dir = os.path.join(self.args.io_dir, "scores") self.summary_dir = os.path.join(self.args.io_dir, "summary") self.stderr_expected_dir = os.path.join(self.args.io_dir, "stderr") # Create a temporary directory to store diff file self.failed_dir = tempfile.mkdtemp(prefix='cts-scheduler_') # Where to store generated files if self.args.out_dir is None: self.args.out_dir = self.args.io_dir self.failed_filename = os.path.join(self.failed_dir, "test-output.diff") else: self.failed_filename = os.path.join(self.args.out_dir, "test-output.diff") os.environ['CIB_shadow_dir'] = self.args.out_dir self.failed_file = None self.outfile_out_dir = os.path.join(self.args.out_dir, "out") self.dot_out_dir = os.path.join(self.args.out_dir, "dot") self.scores_out_dir = os.path.join(self.args.out_dir, "scores") self.summary_out_dir = os.path.join(self.args.out_dir, "summary") self.stderr_out_dir = os.path.join(self.args.out_dir, "stderr") self.valgrind_out_dir = os.path.join(self.args.out_dir, "valgrind") # Single test mode (if requested) try: # User can give test base name or file name of a test input self.args.run = os.path.splitext(os.path.basename(self.args.run))[0] except (AttributeError, TypeError): pass # --run was not specified self.set_schema_env() # Arguments needed (or not) to run commands self.valgrind_args = self._get_valgrind_cmd() self.simulate_args = self._get_simulator_cmd() # Test counters self.num_failed = 0 self.num_tests = 0 # Ensure that the main output directory exists # We don't want to create it with os.makedirs below if not os.path.isdir(self.args.out_dir): self._error("Output directory missing; can't create output files") sys.exit(ExitStatus.CANTCREAT) # Create output subdirectories if they don't exist try: os.makedirs(self.outfile_out_dir, 0o755, True) os.makedirs(self.dot_out_dir, 0o755, True) os.makedirs(self.scores_out_dir, 0o755, True) os.makedirs(self.summary_out_dir, 0o755, True) os.makedirs(self.stderr_out_dir, 0o755, True) if self.valgrind_args: os.makedirs(self.valgrind_out_dir, 0o755, True) except OSError as ex: self._error("Unable to create output subdirectory: %s" % ex) remove_files([ self.outfile_out_dir, self.dot_out_dir, self.scores_out_dir, self.summary_out_dir, self.stderr_out_dir, ]) sys.exit(ExitStatus.CANTCREAT) def _compare_files(self, filename1, filename2): """ Add any file differences to failed results """ if diff(filename1, filename2, stdout=subprocess.DEVNULL) != 0: diff(filename1, filename2, stdout=self.failed_file, stderr=subprocess.DEVNULL) self.failed_file.write("\n") return True return False def run_one(self, test_name, test_desc, test_args=[]): """ Run one scheduler test """ print(" Test %-25s %s" % ((test_name + ":"), test_desc)) did_fail = False self.num_tests = self.num_tests + 1 # Test inputs input_filename = os.path.join( self.xml_input_dir, "%s.xml" % test_name) expected_filename = os.path.join( self.expected_dir, "%s.exp" % test_name) dot_expected_filename = os.path.join( self.dot_expected_dir, "%s.dot" % test_name) scores_filename = os.path.join( self.scores_dir, "%s.scores" % test_name) summary_filename = os.path.join( self.summary_dir, "%s.summary" % test_name) stderr_expected_filename = os.path.join( self.stderr_expected_dir, "%s.stderr" % test_name) # (Intermediate) test outputs output_filename = os.path.join( self.outfile_out_dir, "%s.out" % test_name) dot_output_filename = os.path.join( self.dot_out_dir, "%s.dot.pe" % test_name) score_output_filename = os.path.join( self.scores_out_dir, "%s.scores.pe" % test_name) summary_output_filename = os.path.join( self.summary_out_dir, "%s.summary.pe" % test_name) stderr_output_filename = os.path.join( self.stderr_out_dir, "%s.stderr.pe" % test_name) valgrind_output_filename = os.path.join( self.valgrind_out_dir, "%s.valgrind" % test_name) # Common arguments for running test test_cmd = [] if self.valgrind_args: test_cmd = self.valgrind_args + [ "--log-file=%s" % valgrind_output_filename ] test_cmd = test_cmd + self.simulate_args # @TODO It would be more pythonic to raise exceptions for errors, # then perhaps it would be nice to make a single-test class # Ensure necessary test inputs exist if not os.path.isfile(input_filename): self._error("No input") self.num_failed = self.num_failed + 1 return ExitStatus.NOINPUT if not self.args.update and not os.path.isfile(expected_filename): self._error("no stored output") return ExitStatus.NOINPUT # Run simulation to generate summary output if self.args.run: # Single test mode test_cmd_full = test_cmd + [ '-x', input_filename, '-S' ] + test_args print(" ".join(test_cmd_full)) else: # @TODO Why isn't test_args added here? test_cmd_full = test_cmd + [ '-x', input_filename, '-S' ] with io.open(summary_output_filename, "wt") as f: simulation = subprocess.Popen(test_cmd_full, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=os.environ) # This makes diff happy regardless of --enable-compat-2.0. # Use sed -E to make Linux and BSD special characters more compatible. sed = subprocess.Popen(["sed", "-E", "-e", "s/ocf::/ocf:/g", "-e", r"s/Masters:/Promoted:/", "-e", r"s/Slaves:/Unpromoted:/", "-e", r"s/ Master( |\[|$)/ Promoted\1/", "-e", r"s/ Slave / Unpromoted /", ], stdin=simulation.stdout, stdout=f, stderr=subprocess.STDOUT) simulation.stdout.close() sed.communicate() if self.args.run: cat(summary_output_filename) # Re-run simulation to generate dot, graph, and scores test_cmd_full = test_cmd + [ '-x', input_filename, '-D', dot_output_filename, '-G', output_filename, '-sSQ' ] + test_args with io.open(stderr_output_filename, "wt") as f_stderr, \ io.open(score_output_filename, "wt") as f_score: rc = subprocess.call(test_cmd_full, stdout=f_score, stderr=f_stderr, env=os.environ) # Check for test command failure if rc != ExitStatus.OK: self._failed("Test returned: %d" % rc) did_fail = True print(" ".join(test_cmd_full)) # Check for valgrind errors if self.valgrind_args and not self.args.valgrind_skip_output: if os.stat(valgrind_output_filename).st_size > 0: self._failed("Valgrind reported errors") did_fail = True cat(valgrind_output_filename) remove_files([ valgrind_output_filename ]) # Check for core dump if os.path.isfile("core"): self._failed("Core-file detected: core." + test_name) did_fail = True os.rename("core", "%s/core.%s" % (self.test_home, test_name)) # Check any stderr output if os.path.isfile(stderr_expected_filename): if self._compare_files(stderr_expected_filename, stderr_output_filename): self._failed("stderr changed") did_fail = True elif os.stat(stderr_output_filename).st_size > 0: self._failed("Output was written to stderr") did_fail = True cat(stderr_output_filename) remove_files([ stderr_output_filename ]) # Check whether output graph exists, and normalize it if (not os.path.isfile(output_filename) or os.stat(output_filename).st_size == 0): self._error("No graph produced") did_fail = True self.num_failed = self.num_failed + 1 remove_files([ output_filename ]) return ExitStatus.ERROR normalize(output_filename) # Check whether dot output exists, and sort it if (not os.path.isfile(dot_output_filename) or os.stat(dot_output_filename).st_size == 0): self._error("No dot-file summary produced") did_fail = True self.num_failed = self.num_failed + 1 remove_files([ dot_output_filename, output_filename ]) return ExitStatus.ERROR with io.open(dot_output_filename, "rt") as f: first_line = f.readline() # "digraph" line with opening brace lines = f.readlines() last_line = lines[-1] # closing brace del lines[-1] lines = sorted(set(lines)) # unique sort with io.open(dot_output_filename, "wt") as f: f.write(first_line) f.writelines(lines) f.write(last_line) # Check whether score output exists, and sort it if (not os.path.isfile(score_output_filename) or os.stat(score_output_filename).st_size == 0): self._error("No allocation scores produced") did_fail = True self.num_failed = self.num_failed + 1 remove_files([ score_output_filename, output_filename ]) return ExitStatus.ERROR else: sort_file(score_output_filename) if self.args.update: shutil.copyfile(output_filename, expected_filename) shutil.copyfile(dot_output_filename, dot_expected_filename) shutil.copyfile(score_output_filename, scores_filename) shutil.copyfile(summary_output_filename, summary_filename) print(" Updated expected outputs") if self._compare_files(summary_filename, summary_output_filename): self._failed("summary changed") did_fail = True if self._compare_files(dot_expected_filename, dot_output_filename): self._failed("dot-file summary changed") did_fail = True else: remove_files([ dot_output_filename ]) if self._compare_files(expected_filename, output_filename): self._failed("xml-file changed") did_fail = True if self._compare_files(scores_filename, score_output_filename): self._failed("scores-file changed") did_fail = True remove_files([ output_filename, dot_output_filename, score_output_filename, summary_output_filename]) if did_fail: self.num_failed = self.num_failed + 1 return ExitStatus.ERROR return ExitStatus.OK def run_all(self): """ Run all defined tests """ if platform.architecture()[0] == "64bit": TESTS.extend(TESTS_64BIT) for group in TESTS: for test in group: try: args = test[2] except IndexError: args = [] self.run_one(test[0], test[1], args) print() def _print_summary(self): """ Print a summary of parameters for this test run """ print("Test home is:\t" + self.test_home) print("Test binary is:\t" + self.args.binary) if 'PCMK_schema_directory' in os.environ: print("Schema home is:\t" + os.environ['PCMK_schema_directory']) if self.valgrind_args != []: print("Activating memory testing with valgrind") print() def _test_results(self): if self.num_failed == 0: shutil.rmtree(self.failed_dir) return ExitStatus.OK if os.path.isfile(self.failed_filename) and os.stat(self.failed_filename).st_size != 0: if self.args.verbose: self._error("Results of %d failed tests (out of %d):" % (self.num_failed, self.num_tests)) cat(self.failed_filename) else: self._error("Results of %d failed tests (out of %d) are in %s" % (self.num_failed, self.num_tests, self.failed_filename)) self._error("Use -V to display them after running the tests") else: self._error("%d (of %d) tests failed (no diff results)" % (self.num_failed, self.num_tests)) if os.path.isfile(self.failed_filename): shutil.rmtree(self.failed_dir) return ExitStatus.ERROR def run(self): """ Run test(s) as specified """ # Check for pre-existing core so we don't think it's from us if os.path.exists("core"): self._failed("Can't run with core already present in " + self.test_home) return ExitStatus.OSFILE self._print_summary() # Zero out the error log self.failed_file = io.open(self.failed_filename, "wt") if self.args.run is None: print("Performing the following tests from " + self.args.io_dir) print() self.run_all() print() self.failed_file.close() rc = self._test_results() else: rc = self.run_one(self.args.run, "Single shot", self.single_test_args) self.failed_file.close() if self.num_failed > 0: print("\nFailures:\nThese have also been written to: " + self.failed_filename + "\n") cat(self.failed_filename) shutil.rmtree(self.failed_dir) return rc if __name__ == "__main__": sys.exit(CtsScheduler().run()) # vim: set filetype=python expandtab tabstop=4 softtabstop=4 shiftwidth=4 textwidth=120: diff --git a/cts/scheduler/dot/coloc-cloned-group-promoted-dependent1.dot b/cts/scheduler/dot/coloc-cloned-group-promoted-dependent1.dot new file mode 100644 index 0000000000..d8f1c9f22b --- /dev/null +++ b/cts/scheduler/dot/coloc-cloned-group-promoted-dependent1.dot @@ -0,0 +1,2 @@ + digraph "g" { +} diff --git a/cts/scheduler/dot/coloc-cloned-group-promoted-dependent2.dot b/cts/scheduler/dot/coloc-cloned-group-promoted-dependent2.dot new file mode 100644 index 0000000000..d8f1c9f22b --- /dev/null +++ b/cts/scheduler/dot/coloc-cloned-group-promoted-dependent2.dot @@ -0,0 +1,2 @@ + digraph "g" { +} diff --git a/cts/scheduler/dot/coloc-optional-promoted-dependent-moves-1.dot b/cts/scheduler/dot/coloc-optional-promoted-dependent-moves-1.dot new file mode 100644 index 0000000000..002a4388ca --- /dev/null +++ b/cts/scheduler/dot/coloc-optional-promoted-dependent-moves-1.dot @@ -0,0 +1,27 @@ + digraph "g" { +"Cancel coloc_dependent_monitor_10000 fastvm-fedora39-23" -> "coloc_dependent_demote_0 fastvm-fedora39-23" [ style = bold] +"Cancel coloc_dependent_monitor_10000 fastvm-fedora39-23" [ style=bold color="green" fontcolor="black"] +"Cancel coloc_dependent_monitor_11000 fastvm-fedora39-22" -> "coloc_dependent_promote_0 fastvm-fedora39-22" [ style = bold] +"Cancel coloc_dependent_monitor_11000 fastvm-fedora39-22" [ style=bold color="green" fontcolor="black"] +"coloc_dependent-clone_demote_0" -> "coloc_dependent-clone_demoted_0" [ style = bold] +"coloc_dependent-clone_demote_0" -> "coloc_dependent_demote_0 fastvm-fedora39-23" [ style = bold] +"coloc_dependent-clone_demote_0" [ style=bold color="green" fontcolor="orange"] +"coloc_dependent-clone_demoted_0" -> "coloc_dependent-clone_promote_0" [ style = bold] +"coloc_dependent-clone_demoted_0" [ style=bold color="green" fontcolor="orange"] +"coloc_dependent-clone_promote_0" -> "coloc_dependent_promote_0 fastvm-fedora39-22" [ style = bold] +"coloc_dependent-clone_promote_0" [ style=bold color="green" fontcolor="orange"] +"coloc_dependent-clone_promoted_0" [ style=bold color="green" fontcolor="orange"] +"coloc_dependent_demote_0 fastvm-fedora39-23" -> "coloc_dependent-clone_demoted_0" [ style = bold] +"coloc_dependent_demote_0 fastvm-fedora39-23" -> "coloc_dependent_monitor_11000 fastvm-fedora39-23" [ style = bold] +"coloc_dependent_demote_0 fastvm-fedora39-23" [ style=bold color="green" fontcolor="black"] +"coloc_dependent_monitor_10000 fastvm-fedora39-22" [ style=bold color="green" fontcolor="black"] +"coloc_dependent_monitor_11000 fastvm-fedora39-23" [ style=bold color="green" fontcolor="black"] +"coloc_dependent_promote_0 fastvm-fedora39-22" -> "coloc_dependent-clone_promoted_0" [ style = bold] +"coloc_dependent_promote_0 fastvm-fedora39-22" -> "coloc_dependent_monitor_10000 fastvm-fedora39-22" [ style = bold] +"coloc_dependent_promote_0 fastvm-fedora39-22" [ style=bold color="green" fontcolor="black"] +"coloc_primary_monitor_10000 fastvm-fedora39-23" [ style=bold color="green" fontcolor="black"] +"coloc_primary_start_0 fastvm-fedora39-23" -> "coloc_primary_monitor_10000 fastvm-fedora39-23" [ style = bold] +"coloc_primary_start_0 fastvm-fedora39-23" [ style=bold color="green" fontcolor="black"] +"coloc_primary_stop_0 fastvm-fedora39-22" -> "coloc_primary_start_0 fastvm-fedora39-23" [ style = bold] +"coloc_primary_stop_0 fastvm-fedora39-22" [ style=bold color="green" fontcolor="black"] +} diff --git a/cts/scheduler/dot/coloc-optional-promoted-dependent-moves-2.dot b/cts/scheduler/dot/coloc-optional-promoted-dependent-moves-2.dot new file mode 100644 index 0000000000..0bc1cfc6f9 --- /dev/null +++ b/cts/scheduler/dot/coloc-optional-promoted-dependent-moves-2.dot @@ -0,0 +1,27 @@ + digraph "g" { +"Cancel coloc_dependent_monitor_10000 fastvm-fedora39-22" -> "coloc_dependent_demote_0 fastvm-fedora39-22" [ style = bold] +"Cancel coloc_dependent_monitor_10000 fastvm-fedora39-22" [ style=bold color="green" fontcolor="black"] +"Cancel coloc_dependent_monitor_11000 fastvm-fedora39-23" -> "coloc_dependent_promote_0 fastvm-fedora39-23" [ style = bold] +"Cancel coloc_dependent_monitor_11000 fastvm-fedora39-23" [ style=bold color="green" fontcolor="black"] +"coloc_dependent-clone_demote_0" -> "coloc_dependent-clone_demoted_0" [ style = bold] +"coloc_dependent-clone_demote_0" -> "coloc_dependent_demote_0 fastvm-fedora39-22" [ style = bold] +"coloc_dependent-clone_demote_0" [ style=bold color="green" fontcolor="orange"] +"coloc_dependent-clone_demoted_0" -> "coloc_dependent-clone_promote_0" [ style = bold] +"coloc_dependent-clone_demoted_0" [ style=bold color="green" fontcolor="orange"] +"coloc_dependent-clone_promote_0" -> "coloc_dependent_promote_0 fastvm-fedora39-23" [ style = bold] +"coloc_dependent-clone_promote_0" [ style=bold color="green" fontcolor="orange"] +"coloc_dependent-clone_promoted_0" [ style=bold color="green" fontcolor="orange"] +"coloc_dependent_demote_0 fastvm-fedora39-22" -> "coloc_dependent-clone_demoted_0" [ style = bold] +"coloc_dependent_demote_0 fastvm-fedora39-22" -> "coloc_dependent_monitor_11000 fastvm-fedora39-22" [ style = bold] +"coloc_dependent_demote_0 fastvm-fedora39-22" [ style=bold color="green" fontcolor="black"] +"coloc_dependent_monitor_10000 fastvm-fedora39-23" [ style=bold color="green" fontcolor="black"] +"coloc_dependent_monitor_11000 fastvm-fedora39-22" [ style=bold color="green" fontcolor="black"] +"coloc_dependent_promote_0 fastvm-fedora39-23" -> "coloc_dependent-clone_promoted_0" [ style = bold] +"coloc_dependent_promote_0 fastvm-fedora39-23" -> "coloc_dependent_monitor_10000 fastvm-fedora39-23" [ style = bold] +"coloc_dependent_promote_0 fastvm-fedora39-23" [ style=bold color="green" fontcolor="black"] +"coloc_primary_monitor_10000 fastvm-fedora39-23" [ style=bold color="green" fontcolor="black"] +"coloc_primary_start_0 fastvm-fedora39-23" -> "coloc_primary_monitor_10000 fastvm-fedora39-23" [ style = bold] +"coloc_primary_start_0 fastvm-fedora39-23" [ style=bold color="green" fontcolor="black"] +"coloc_primary_stop_0 fastvm-fedora39-22" -> "coloc_primary_start_0 fastvm-fedora39-23" [ style = bold] +"coloc_primary_stop_0 fastvm-fedora39-22" [ style=bold color="green" fontcolor="black"] +} diff --git a/cts/scheduler/dot/coloc-optional-promoted-dependent-stays-1.dot b/cts/scheduler/dot/coloc-optional-promoted-dependent-stays-1.dot new file mode 100644 index 0000000000..c5b568f6d5 --- /dev/null +++ b/cts/scheduler/dot/coloc-optional-promoted-dependent-stays-1.dot @@ -0,0 +1,7 @@ + digraph "g" { +"coloc_primary_monitor_10000 fastvm-fedora39-23" [ style=bold color="green" fontcolor="black"] +"coloc_primary_start_0 fastvm-fedora39-23" -> "coloc_primary_monitor_10000 fastvm-fedora39-23" [ style = bold] +"coloc_primary_start_0 fastvm-fedora39-23" [ style=bold color="green" fontcolor="black"] +"coloc_primary_stop_0 fastvm-fedora39-22" -> "coloc_primary_start_0 fastvm-fedora39-23" [ style = bold] +"coloc_primary_stop_0 fastvm-fedora39-22" [ style=bold color="green" fontcolor="black"] +} diff --git a/cts/scheduler/dot/coloc-optional-promoted-dependent-stays-2.dot b/cts/scheduler/dot/coloc-optional-promoted-dependent-stays-2.dot new file mode 100644 index 0000000000..c5b568f6d5 --- /dev/null +++ b/cts/scheduler/dot/coloc-optional-promoted-dependent-stays-2.dot @@ -0,0 +1,7 @@ + digraph "g" { +"coloc_primary_monitor_10000 fastvm-fedora39-23" [ style=bold color="green" fontcolor="black"] +"coloc_primary_start_0 fastvm-fedora39-23" -> "coloc_primary_monitor_10000 fastvm-fedora39-23" [ style = bold] +"coloc_primary_start_0 fastvm-fedora39-23" [ style=bold color="green" fontcolor="black"] +"coloc_primary_stop_0 fastvm-fedora39-22" -> "coloc_primary_start_0 fastvm-fedora39-23" [ style = bold] +"coloc_primary_stop_0 fastvm-fedora39-22" [ style=bold color="green" fontcolor="black"] +} diff --git a/cts/scheduler/exp/coloc-cloned-group-promoted-dependent1.exp b/cts/scheduler/exp/coloc-cloned-group-promoted-dependent1.exp new file mode 100644 index 0000000000..56e315ff01 --- /dev/null +++ b/cts/scheduler/exp/coloc-cloned-group-promoted-dependent1.exp @@ -0,0 +1 @@ + diff --git a/cts/scheduler/exp/coloc-cloned-group-promoted-dependent2.exp b/cts/scheduler/exp/coloc-cloned-group-promoted-dependent2.exp new file mode 100644 index 0000000000..56e315ff01 --- /dev/null +++ b/cts/scheduler/exp/coloc-cloned-group-promoted-dependent2.exp @@ -0,0 +1 @@ + diff --git a/cts/scheduler/exp/coloc-optional-promoted-dependent-moves-1.exp b/cts/scheduler/exp/coloc-optional-promoted-dependent-moves-1.exp new file mode 100644 index 0000000000..eb03489eb3 --- /dev/null +++ b/cts/scheduler/exp/coloc-optional-promoted-dependent-moves-1.exp @@ -0,0 +1,160 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cts/scheduler/exp/coloc-optional-promoted-dependent-moves-2.exp b/cts/scheduler/exp/coloc-optional-promoted-dependent-moves-2.exp new file mode 100644 index 0000000000..e02a466611 --- /dev/null +++ b/cts/scheduler/exp/coloc-optional-promoted-dependent-moves-2.exp @@ -0,0 +1,160 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cts/scheduler/exp/coloc-optional-promoted-dependent-stays-1.exp b/cts/scheduler/exp/coloc-optional-promoted-dependent-stays-1.exp new file mode 100644 index 0000000000..48451cfb4a --- /dev/null +++ b/cts/scheduler/exp/coloc-optional-promoted-dependent-stays-1.exp @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cts/scheduler/exp/coloc-optional-promoted-dependent-stays-2.exp b/cts/scheduler/exp/coloc-optional-promoted-dependent-stays-2.exp new file mode 100644 index 0000000000..48451cfb4a --- /dev/null +++ b/cts/scheduler/exp/coloc-optional-promoted-dependent-stays-2.exp @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cts/scheduler/scores/bug-lf-2358.scores b/cts/scheduler/scores/bug-lf-2358.scores index 422d9d8ea1..3d36027701 100644 --- a/cts/scheduler/scores/bug-lf-2358.scores +++ b/cts/scheduler/scores/bug-lf-2358.scores @@ -1,79 +1,79 @@ pcmk__clone_assign: ms_drbd_mysql1 allocation score on alice.demo: 0 pcmk__clone_assign: ms_drbd_mysql1 allocation score on bob.demo: 0 pcmk__clone_assign: ms_drbd_mysql2 allocation score on alice.demo: 0 pcmk__clone_assign: ms_drbd_mysql2 allocation score on bob.demo: 0 pcmk__clone_assign: ms_drbd_nfsexport allocation score on alice.demo: 0 pcmk__clone_assign: ms_drbd_nfsexport allocation score on bob.demo: 0 pcmk__clone_assign: res_drbd_mysql1:0 allocation score on alice.demo: 0 pcmk__clone_assign: res_drbd_mysql1:0 allocation score on bob.demo: 10001 pcmk__clone_assign: res_drbd_mysql1:1 allocation score on alice.demo: 0 pcmk__clone_assign: res_drbd_mysql1:1 allocation score on bob.demo: 0 pcmk__clone_assign: res_drbd_mysql2:0 allocation score on alice.demo: 0 pcmk__clone_assign: res_drbd_mysql2:0 allocation score on bob.demo: 10001 pcmk__clone_assign: res_drbd_mysql2:1 allocation score on alice.demo: 10001 pcmk__clone_assign: res_drbd_mysql2:1 allocation score on bob.demo: 0 pcmk__clone_assign: res_drbd_nfsexport:0 allocation score on alice.demo: 0 pcmk__clone_assign: res_drbd_nfsexport:0 allocation score on bob.demo: 0 pcmk__clone_assign: res_drbd_nfsexport:1 allocation score on alice.demo: 0 pcmk__clone_assign: res_drbd_nfsexport:1 allocation score on bob.demo: 0 pcmk__group_assign: res_fs_mysql1 allocation score on alice.demo: 0 pcmk__group_assign: res_fs_mysql1 allocation score on bob.demo: 0 pcmk__group_assign: res_fs_mysql2 allocation score on alice.demo: 0 pcmk__group_assign: res_fs_mysql2 allocation score on bob.demo: 0 pcmk__group_assign: res_fs_nfsexport allocation score on alice.demo: 0 pcmk__group_assign: res_fs_nfsexport allocation score on bob.demo: 0 pcmk__group_assign: res_ip_mysql1 allocation score on alice.demo: 0 pcmk__group_assign: res_ip_mysql1 allocation score on bob.demo: 0 pcmk__group_assign: res_ip_mysql2 allocation score on alice.demo: 0 pcmk__group_assign: res_ip_mysql2 allocation score on bob.demo: 0 pcmk__group_assign: res_ip_nfs allocation score on alice.demo: 0 pcmk__group_assign: res_ip_nfs allocation score on bob.demo: 0 pcmk__group_assign: res_mysql1 allocation score on alice.demo: 0 pcmk__group_assign: res_mysql1 allocation score on bob.demo: 0 pcmk__group_assign: res_mysql2 allocation score on alice.demo: 0 pcmk__group_assign: res_mysql2 allocation score on bob.demo: 0 pcmk__group_assign: res_nfs allocation score on alice.demo: 0 pcmk__group_assign: res_nfs allocation score on bob.demo: 0 pcmk__group_assign: rg_mysql1 allocation score on alice.demo: 0 pcmk__group_assign: rg_mysql1 allocation score on bob.demo: 0 pcmk__group_assign: rg_mysql2 allocation score on alice.demo: 0 pcmk__group_assign: rg_mysql2 allocation score on bob.demo: 0 pcmk__group_assign: rg_nfs allocation score on alice.demo: 0 pcmk__group_assign: rg_nfs allocation score on bob.demo: 0 pcmk__primitive_assign: res_drbd_mysql1:0 allocation score on alice.demo: -INFINITY pcmk__primitive_assign: res_drbd_mysql1:0 allocation score on bob.demo: 10001 pcmk__primitive_assign: res_drbd_mysql1:1 allocation score on alice.demo: 0 pcmk__primitive_assign: res_drbd_mysql1:1 allocation score on bob.demo: -INFINITY pcmk__primitive_assign: res_drbd_mysql2:0 allocation score on alice.demo: 0 pcmk__primitive_assign: res_drbd_mysql2:0 allocation score on bob.demo: 10001 pcmk__primitive_assign: res_drbd_mysql2:1 allocation score on alice.demo: 10001 pcmk__primitive_assign: res_drbd_mysql2:1 allocation score on bob.demo: -INFINITY pcmk__primitive_assign: res_drbd_nfsexport:0 allocation score on alice.demo: -INFINITY pcmk__primitive_assign: res_drbd_nfsexport:0 allocation score on bob.demo: -INFINITY pcmk__primitive_assign: res_drbd_nfsexport:1 allocation score on alice.demo: -INFINITY pcmk__primitive_assign: res_drbd_nfsexport:1 allocation score on bob.demo: -INFINITY pcmk__primitive_assign: res_fs_mysql1 allocation score on alice.demo: -INFINITY pcmk__primitive_assign: res_fs_mysql1 allocation score on bob.demo: 10001 pcmk__primitive_assign: res_fs_mysql2 allocation score on alice.demo: 10001 pcmk__primitive_assign: res_fs_mysql2 allocation score on bob.demo: -INFINITY pcmk__primitive_assign: res_fs_nfsexport allocation score on alice.demo: -INFINITY pcmk__primitive_assign: res_fs_nfsexport allocation score on bob.demo: -INFINITY pcmk__primitive_assign: res_ip_mysql1 allocation score on alice.demo: -INFINITY pcmk__primitive_assign: res_ip_mysql1 allocation score on bob.demo: 0 pcmk__primitive_assign: res_ip_mysql2 allocation score on alice.demo: 0 pcmk__primitive_assign: res_ip_mysql2 allocation score on bob.demo: -INFINITY pcmk__primitive_assign: res_ip_nfs allocation score on alice.demo: -INFINITY pcmk__primitive_assign: res_ip_nfs allocation score on bob.demo: -INFINITY pcmk__primitive_assign: res_mysql1 allocation score on alice.demo: -INFINITY pcmk__primitive_assign: res_mysql1 allocation score on bob.demo: 0 pcmk__primitive_assign: res_mysql2 allocation score on alice.demo: 0 pcmk__primitive_assign: res_mysql2 allocation score on bob.demo: -INFINITY pcmk__primitive_assign: res_nfs allocation score on alice.demo: -INFINITY pcmk__primitive_assign: res_nfs allocation score on bob.demo: -INFINITY -res_drbd_mysql1:0 promotion score on bob.demo: 0 +res_drbd_mysql1:0 promotion score on bob.demo: 10000 res_drbd_mysql1:1 promotion score on alice.demo: -INFINITY res_drbd_mysql2:0 promotion score on bob.demo: 10000 res_drbd_mysql2:1 promotion score on alice.demo: 10000 res_drbd_nfsexport:0 promotion score (inactive): -INFINITY res_drbd_nfsexport:1 promotion score (inactive): -INFINITY diff --git a/cts/scheduler/scores/coloc-clone-stays-active.scores b/cts/scheduler/scores/coloc-clone-stays-active.scores index 52c6bc59ef..6995176853 100644 --- a/cts/scheduler/scores/coloc-clone-stays-active.scores +++ b/cts/scheduler/scores/coloc-clone-stays-active.scores @@ -1,505 +1,505 @@ drbd-pool-0:0 promotion score on s01-1: 10000 drbd-pool-0:1 promotion score on s01-0: 12000 drbd-pool-1:0 promotion score on s01-1: 12000 drbd-pool-1:1 promotion score on s01-0: 10000 drbd-s01-logs:0 promotion score on s01-1: 10000 drbd-s01-logs:1 promotion score on s01-0: 10000 drbd-s01-service:0 promotion score on s01-1: 10000 drbd-s01-service:1 promotion score on s01-0: 10000 drbd-s01-vm-data:0 promotion score on s01-1: 10000 drbd-s01-vm-data:1 promotion score on s01-0: 1 drbd-vds-dom0-stateless-0:0 promotion score on s01-1: 10000 drbd-vds-dom0-stateless-0:1 promotion score on s01-0: 10000 drbd-vds-http:0 promotion score on s01-1: 10000 drbd-vds-http:1 promotion score on s01-0: 10000 drbd-vds-tftpboot:0 promotion score on s01-1: 10000 drbd-vds-tftpboot:1 promotion score on s01-0: 10000 iscsi-pool-0-vips-fw:0 promotion score on s01-1: -INFINITY -iscsi-pool-0-vips-fw:1 promotion score on s01-0: 2000 -iscsi-pool-1-vips-fw:0 promotion score on s01-1: 2000 +iscsi-pool-0-vips-fw:1 promotion score on s01-0: INFINITY +iscsi-pool-1-vips-fw:0 promotion score on s01-1: INFINITY iscsi-pool-1-vips-fw:1 promotion score on s01-0: -INFINITY iscsi-vds-dom0-stateless-0-vips-fw:0 promotion score on s01-1: -INFINITY iscsi-vds-dom0-stateless-0-vips-fw:1 promotion score on s01-0: -INFINITY pcmk__clone_assign: cl-clvmd allocation score on s01-0: 0 pcmk__clone_assign: cl-clvmd allocation score on s01-1: 0 pcmk__clone_assign: cl-dhcpd allocation score on s01-0: 0 pcmk__clone_assign: cl-dhcpd allocation score on s01-1: 0 pcmk__clone_assign: cl-dlm allocation score on s01-0: 0 pcmk__clone_assign: cl-dlm allocation score on s01-1: 0 pcmk__clone_assign: cl-drbdlinks-s01-service allocation score on s01-0: 0 pcmk__clone_assign: cl-drbdlinks-s01-service allocation score on s01-1: 0 pcmk__clone_assign: cl-gfs2 allocation score on s01-0: 0 pcmk__clone_assign: cl-gfs2 allocation score on s01-1: 0 pcmk__clone_assign: cl-ietd allocation score on s01-0: 12001 pcmk__clone_assign: cl-ietd allocation score on s01-1: 1000 pcmk__clone_assign: cl-libvirtd allocation score on s01-0: 0 pcmk__clone_assign: cl-libvirtd allocation score on s01-1: 0 pcmk__clone_assign: cl-o2cb allocation score on s01-0: 0 pcmk__clone_assign: cl-o2cb allocation score on s01-1: 0 pcmk__clone_assign: cl-ospf-routing allocation score on s01-0: 0 pcmk__clone_assign: cl-ospf-routing allocation score on s01-1: 0 pcmk__clone_assign: cl-s01-logs-fs allocation score on s01-0: 0 pcmk__clone_assign: cl-s01-logs-fs allocation score on s01-1: 0 pcmk__clone_assign: cl-s01-service-fs allocation score on s01-0: 0 pcmk__clone_assign: cl-s01-service-fs allocation score on s01-1: 0 pcmk__clone_assign: cl-s01-vm-data-metadata-fs allocation score on s01-0: 0 pcmk__clone_assign: cl-s01-vm-data-metadata-fs allocation score on s01-1: 0 pcmk__clone_assign: cl-s01-vm-data-storage-pool allocation score on s01-0: 0 pcmk__clone_assign: cl-s01-vm-data-storage-pool allocation score on s01-1: 0 pcmk__clone_assign: cl-vds-http-fs allocation score on s01-0: 0 pcmk__clone_assign: cl-vds-http-fs allocation score on s01-1: 0 pcmk__clone_assign: cl-vds-tftpboot-fs allocation score on s01-0: 0 pcmk__clone_assign: cl-vds-tftpboot-fs allocation score on s01-1: 0 pcmk__clone_assign: cl-vg-s01-vm-data allocation score on s01-0: 0 pcmk__clone_assign: cl-vg-s01-vm-data allocation score on s01-1: 0 pcmk__clone_assign: cl-xinetd allocation score on s01-0: 0 pcmk__clone_assign: cl-xinetd allocation score on s01-1: 0 pcmk__clone_assign: clvmd:0 allocation score on s01-0: 0 pcmk__clone_assign: clvmd:0 allocation score on s01-1: 1 pcmk__clone_assign: clvmd:1 allocation score on s01-0: 1 pcmk__clone_assign: clvmd:1 allocation score on s01-1: 0 pcmk__clone_assign: connected-outer allocation score on s01-0: 0 pcmk__clone_assign: connected-outer allocation score on s01-1: 0 pcmk__clone_assign: dhcpd:0 allocation score on s01-0: 0 pcmk__clone_assign: dhcpd:0 allocation score on s01-1: 0 pcmk__clone_assign: dhcpd:1 allocation score on s01-0: 0 pcmk__clone_assign: dhcpd:1 allocation score on s01-1: 0 pcmk__clone_assign: dlm:0 allocation score on s01-0: 0 pcmk__clone_assign: dlm:0 allocation score on s01-1: 1 pcmk__clone_assign: dlm:1 allocation score on s01-0: 1 pcmk__clone_assign: dlm:1 allocation score on s01-1: 0 pcmk__clone_assign: drbd-pool-0:0 allocation score on s01-0: 0 pcmk__clone_assign: drbd-pool-0:0 allocation score on s01-1: 10001 pcmk__clone_assign: drbd-pool-0:1 allocation score on s01-0: 10001 pcmk__clone_assign: drbd-pool-0:1 allocation score on s01-1: 0 pcmk__clone_assign: drbd-pool-1:0 allocation score on s01-0: 0 pcmk__clone_assign: drbd-pool-1:0 allocation score on s01-1: 10001 pcmk__clone_assign: drbd-pool-1:1 allocation score on s01-0: 10001 pcmk__clone_assign: drbd-pool-1:1 allocation score on s01-1: 0 pcmk__clone_assign: drbd-s01-logs:0 allocation score on s01-0: 0 pcmk__clone_assign: drbd-s01-logs:0 allocation score on s01-1: 10001 pcmk__clone_assign: drbd-s01-logs:1 allocation score on s01-0: 10001 pcmk__clone_assign: drbd-s01-logs:1 allocation score on s01-1: 0 pcmk__clone_assign: drbd-s01-service:0 allocation score on s01-0: 0 pcmk__clone_assign: drbd-s01-service:0 allocation score on s01-1: 10001 pcmk__clone_assign: drbd-s01-service:1 allocation score on s01-0: 10001 pcmk__clone_assign: drbd-s01-service:1 allocation score on s01-1: 0 pcmk__clone_assign: drbd-s01-vm-data:0 allocation score on s01-0: 0 pcmk__clone_assign: drbd-s01-vm-data:0 allocation score on s01-1: 10001 pcmk__clone_assign: drbd-s01-vm-data:1 allocation score on s01-0: 10001 pcmk__clone_assign: drbd-s01-vm-data:1 allocation score on s01-1: 0 pcmk__clone_assign: drbd-vds-dom0-stateless-0:0 allocation score on s01-0: 0 pcmk__clone_assign: drbd-vds-dom0-stateless-0:0 allocation score on s01-1: 10001 pcmk__clone_assign: drbd-vds-dom0-stateless-0:1 allocation score on s01-0: 10001 pcmk__clone_assign: drbd-vds-dom0-stateless-0:1 allocation score on s01-1: 0 pcmk__clone_assign: drbd-vds-http:0 allocation score on s01-0: 0 pcmk__clone_assign: drbd-vds-http:0 allocation score on s01-1: 10001 pcmk__clone_assign: drbd-vds-http:1 allocation score on s01-0: 10001 pcmk__clone_assign: drbd-vds-http:1 allocation score on s01-1: 0 pcmk__clone_assign: drbd-vds-tftpboot:0 allocation score on s01-0: 0 pcmk__clone_assign: drbd-vds-tftpboot:0 allocation score on s01-1: 10001 pcmk__clone_assign: drbd-vds-tftpboot:1 allocation score on s01-0: 10001 pcmk__clone_assign: drbd-vds-tftpboot:1 allocation score on s01-1: 0 pcmk__clone_assign: drbdlinks-s01-service:0 allocation score on s01-0: 0 pcmk__clone_assign: drbdlinks-s01-service:0 allocation score on s01-1: 1 pcmk__clone_assign: drbdlinks-s01-service:1 allocation score on s01-0: 1 pcmk__clone_assign: drbdlinks-s01-service:1 allocation score on s01-1: 0 pcmk__clone_assign: gfs2:0 allocation score on s01-0: 0 pcmk__clone_assign: gfs2:0 allocation score on s01-1: 1 pcmk__clone_assign: gfs2:1 allocation score on s01-0: 1 pcmk__clone_assign: gfs2:1 allocation score on s01-1: 0 pcmk__clone_assign: ietd:0 allocation score on s01-0: 0 pcmk__clone_assign: ietd:0 allocation score on s01-1: 1 pcmk__clone_assign: ietd:1 allocation score on s01-0: 1 pcmk__clone_assign: ietd:1 allocation score on s01-1: 0 pcmk__clone_assign: iscsi-pool-0-vips-fw:0 allocation score on s01-0: 0 pcmk__clone_assign: iscsi-pool-0-vips-fw:0 allocation score on s01-1: 2000 pcmk__clone_assign: iscsi-pool-0-vips-fw:1 allocation score on s01-0: 2000 pcmk__clone_assign: iscsi-pool-0-vips-fw:1 allocation score on s01-1: 0 pcmk__clone_assign: iscsi-pool-1-vips-fw:0 allocation score on s01-0: 0 pcmk__clone_assign: iscsi-pool-1-vips-fw:0 allocation score on s01-1: 2000 pcmk__clone_assign: iscsi-pool-1-vips-fw:1 allocation score on s01-0: 2000 pcmk__clone_assign: iscsi-pool-1-vips-fw:1 allocation score on s01-1: 0 pcmk__clone_assign: iscsi-vds-dom0-stateless-0-vips-fw:0 allocation score on s01-0: 0 pcmk__clone_assign: iscsi-vds-dom0-stateless-0-vips-fw:0 allocation score on s01-1: 2000 pcmk__clone_assign: iscsi-vds-dom0-stateless-0-vips-fw:1 allocation score on s01-0: 2000 pcmk__clone_assign: iscsi-vds-dom0-stateless-0-vips-fw:1 allocation score on s01-1: 0 pcmk__clone_assign: libvirtd:0 allocation score on s01-0: 0 pcmk__clone_assign: libvirtd:0 allocation score on s01-1: 1 pcmk__clone_assign: libvirtd:1 allocation score on s01-0: 1 pcmk__clone_assign: libvirtd:1 allocation score on s01-1: 0 pcmk__clone_assign: ms-drbd-pool-0 allocation score on s01-0: 1000 pcmk__clone_assign: ms-drbd-pool-0 allocation score on s01-1: 0 pcmk__clone_assign: ms-drbd-pool-1 allocation score on s01-0: 0 pcmk__clone_assign: ms-drbd-pool-1 allocation score on s01-1: 1000 pcmk__clone_assign: ms-drbd-s01-logs allocation score on s01-0: 0 pcmk__clone_assign: ms-drbd-s01-logs allocation score on s01-1: 0 pcmk__clone_assign: ms-drbd-s01-service allocation score on s01-0: 0 pcmk__clone_assign: ms-drbd-s01-service allocation score on s01-1: 0 pcmk__clone_assign: ms-drbd-s01-vm-data allocation score on s01-0: 0 pcmk__clone_assign: ms-drbd-s01-vm-data allocation score on s01-1: 0 pcmk__clone_assign: ms-drbd-vds-dom0-stateless-0 allocation score on s01-0: 0 pcmk__clone_assign: ms-drbd-vds-dom0-stateless-0 allocation score on s01-1: 0 pcmk__clone_assign: ms-drbd-vds-http allocation score on s01-0: 0 pcmk__clone_assign: ms-drbd-vds-http allocation score on s01-1: 0 pcmk__clone_assign: ms-drbd-vds-tftpboot allocation score on s01-0: 0 pcmk__clone_assign: ms-drbd-vds-tftpboot allocation score on s01-1: 0 pcmk__clone_assign: ms-iscsi-pool-0-vips-fw allocation score on s01-0: 0 pcmk__clone_assign: ms-iscsi-pool-0-vips-fw allocation score on s01-1: 0 pcmk__clone_assign: ms-iscsi-pool-1-vips-fw allocation score on s01-0: 0 pcmk__clone_assign: ms-iscsi-pool-1-vips-fw allocation score on s01-1: 0 pcmk__clone_assign: ms-iscsi-vds-dom0-stateless-0-vips-fw allocation score on s01-0: 0 pcmk__clone_assign: ms-iscsi-vds-dom0-stateless-0-vips-fw allocation score on s01-1: 0 pcmk__clone_assign: o2cb:0 allocation score on s01-0: 0 pcmk__clone_assign: o2cb:0 allocation score on s01-1: 0 pcmk__clone_assign: o2cb:1 allocation score on s01-0: 0 pcmk__clone_assign: o2cb:1 allocation score on s01-1: 0 pcmk__clone_assign: ospf-routing:0 allocation score on s01-0: 0 pcmk__clone_assign: ospf-routing:0 allocation score on s01-1: 0 pcmk__clone_assign: ospf-routing:1 allocation score on s01-0: 0 pcmk__clone_assign: ospf-routing:1 allocation score on s01-1: 0 pcmk__clone_assign: ospfd:0 allocation score on s01-0: 0 pcmk__clone_assign: ospfd:0 allocation score on s01-1: 1 pcmk__clone_assign: ospfd:1 allocation score on s01-0: 1 pcmk__clone_assign: ospfd:1 allocation score on s01-1: 0 pcmk__clone_assign: ping-bmc-and-switch:0 allocation score on s01-0: 0 pcmk__clone_assign: ping-bmc-and-switch:0 allocation score on s01-1: 1 pcmk__clone_assign: ping-bmc-and-switch:1 allocation score on s01-0: 1 pcmk__clone_assign: ping-bmc-and-switch:1 allocation score on s01-1: 0 pcmk__clone_assign: s01-logs-fs:0 allocation score on s01-0: 0 pcmk__clone_assign: s01-logs-fs:0 allocation score on s01-1: 1 pcmk__clone_assign: s01-logs-fs:1 allocation score on s01-0: 1 pcmk__clone_assign: s01-logs-fs:1 allocation score on s01-1: 0 pcmk__clone_assign: s01-service-fs:0 allocation score on s01-0: 0 pcmk__clone_assign: s01-service-fs:0 allocation score on s01-1: 1 pcmk__clone_assign: s01-service-fs:1 allocation score on s01-0: 1 pcmk__clone_assign: s01-service-fs:1 allocation score on s01-1: 0 pcmk__clone_assign: s01-vm-data-metadata-fs:0 allocation score on s01-0: 0 pcmk__clone_assign: s01-vm-data-metadata-fs:0 allocation score on s01-1: 1 pcmk__clone_assign: s01-vm-data-metadata-fs:1 allocation score on s01-0: 1 pcmk__clone_assign: s01-vm-data-metadata-fs:1 allocation score on s01-1: 0 pcmk__clone_assign: s01-vm-data-storage-pool:0 allocation score on s01-0: 0 pcmk__clone_assign: s01-vm-data-storage-pool:0 allocation score on s01-1: 1 pcmk__clone_assign: s01-vm-data-storage-pool:1 allocation score on s01-0: 1 pcmk__clone_assign: s01-vm-data-storage-pool:1 allocation score on s01-1: 0 pcmk__clone_assign: vds-http-fs:0 allocation score on s01-0: 0 pcmk__clone_assign: vds-http-fs:0 allocation score on s01-1: 1 pcmk__clone_assign: vds-http-fs:1 allocation score on s01-0: 1 pcmk__clone_assign: vds-http-fs:1 allocation score on s01-1: 0 pcmk__clone_assign: vds-tftpboot-fs:0 allocation score on s01-0: 0 pcmk__clone_assign: vds-tftpboot-fs:0 allocation score on s01-1: 0 pcmk__clone_assign: vds-tftpboot-fs:1 allocation score on s01-0: 0 pcmk__clone_assign: vds-tftpboot-fs:1 allocation score on s01-1: 0 pcmk__clone_assign: vg-s01-vm-data:0 allocation score on s01-0: 0 pcmk__clone_assign: vg-s01-vm-data:0 allocation score on s01-1: 1 pcmk__clone_assign: vg-s01-vm-data:1 allocation score on s01-0: 1 pcmk__clone_assign: vg-s01-vm-data:1 allocation score on s01-1: 0 pcmk__clone_assign: vip-227-fw:0 allocation score on s01-0: 0 pcmk__clone_assign: vip-227-fw:0 allocation score on s01-1: 1 pcmk__clone_assign: vip-227-fw:1 allocation score on s01-0: 1 pcmk__clone_assign: vip-227-fw:1 allocation score on s01-1: 0 pcmk__clone_assign: vip-228-fw:0 allocation score on s01-0: 0 pcmk__clone_assign: vip-228-fw:0 allocation score on s01-1: 1 pcmk__clone_assign: vip-228-fw:1 allocation score on s01-0: 1 pcmk__clone_assign: vip-228-fw:1 allocation score on s01-1: 0 pcmk__clone_assign: vip-235-fw:0 allocation score on s01-0: 0 pcmk__clone_assign: vip-235-fw:0 allocation score on s01-1: 1 pcmk__clone_assign: vip-235-fw:1 allocation score on s01-0: 1 pcmk__clone_assign: vip-235-fw:1 allocation score on s01-1: 0 pcmk__clone_assign: vip-236-fw:0 allocation score on s01-0: 0 pcmk__clone_assign: vip-236-fw:0 allocation score on s01-1: 1 pcmk__clone_assign: vip-236-fw:1 allocation score on s01-0: 1 pcmk__clone_assign: vip-236-fw:1 allocation score on s01-1: 0 pcmk__clone_assign: vip-237-fw:0 allocation score on s01-0: 0 pcmk__clone_assign: vip-237-fw:0 allocation score on s01-1: 1 pcmk__clone_assign: vip-237-fw:1 allocation score on s01-0: 1 pcmk__clone_assign: vip-237-fw:1 allocation score on s01-1: 0 pcmk__clone_assign: vip-238-fw:0 allocation score on s01-0: 0 pcmk__clone_assign: vip-238-fw:0 allocation score on s01-1: 1 pcmk__clone_assign: vip-238-fw:1 allocation score on s01-0: 1 pcmk__clone_assign: vip-238-fw:1 allocation score on s01-1: 0 pcmk__clone_assign: xinetd:0 allocation score on s01-0: 0 pcmk__clone_assign: xinetd:0 allocation score on s01-1: 1 pcmk__clone_assign: xinetd:1 allocation score on s01-0: 1 pcmk__clone_assign: xinetd:1 allocation score on s01-1: 0 pcmk__clone_assign: zebra:0 allocation score on s01-0: 0 pcmk__clone_assign: zebra:0 allocation score on s01-1: 1 pcmk__clone_assign: zebra:1 allocation score on s01-0: 1 pcmk__clone_assign: zebra:1 allocation score on s01-1: 0 pcmk__group_assign: http-server allocation score on s01-0: 0 pcmk__group_assign: http-server allocation score on s01-1: 0 pcmk__group_assign: iscsi-pool-0-lun-1 allocation score on s01-0: 0 pcmk__group_assign: iscsi-pool-0-lun-1 allocation score on s01-1: 0 pcmk__group_assign: iscsi-pool-0-target allocation score on s01-0: 1000 pcmk__group_assign: iscsi-pool-0-target allocation score on s01-1: 0 pcmk__group_assign: iscsi-pool-0-target-all allocation score on s01-0: 1000 pcmk__group_assign: iscsi-pool-0-target-all allocation score on s01-1: 0 pcmk__group_assign: iscsi-pool-0-vips allocation score on s01-0: 0 pcmk__group_assign: iscsi-pool-0-vips allocation score on s01-1: 0 pcmk__group_assign: iscsi-pool-0-vips-fw:0 allocation score on s01-0: -INFINITY pcmk__group_assign: iscsi-pool-0-vips-fw:0 allocation score on s01-1: 2000 pcmk__group_assign: iscsi-pool-0-vips-fw:1 allocation score on s01-0: 2000 pcmk__group_assign: iscsi-pool-0-vips-fw:1 allocation score on s01-1: 0 pcmk__group_assign: iscsi-pool-1-lun-1 allocation score on s01-0: 0 pcmk__group_assign: iscsi-pool-1-lun-1 allocation score on s01-1: 0 pcmk__group_assign: iscsi-pool-1-target allocation score on s01-0: 0 pcmk__group_assign: iscsi-pool-1-target allocation score on s01-1: 1000 pcmk__group_assign: iscsi-pool-1-target-all allocation score on s01-0: 0 pcmk__group_assign: iscsi-pool-1-target-all allocation score on s01-1: 1000 pcmk__group_assign: iscsi-pool-1-vips allocation score on s01-0: 0 pcmk__group_assign: iscsi-pool-1-vips allocation score on s01-1: 0 pcmk__group_assign: iscsi-pool-1-vips-fw:0 allocation score on s01-0: 0 pcmk__group_assign: iscsi-pool-1-vips-fw:0 allocation score on s01-1: 2000 pcmk__group_assign: iscsi-pool-1-vips-fw:1 allocation score on s01-0: 2000 pcmk__group_assign: iscsi-pool-1-vips-fw:1 allocation score on s01-1: -INFINITY pcmk__group_assign: iscsi-vds-dom0-stateless-0-lun-1 allocation score on s01-0: 0 pcmk__group_assign: iscsi-vds-dom0-stateless-0-lun-1 allocation score on s01-1: 0 pcmk__group_assign: iscsi-vds-dom0-stateless-0-target allocation score on s01-0: 0 pcmk__group_assign: iscsi-vds-dom0-stateless-0-target allocation score on s01-1: 0 pcmk__group_assign: iscsi-vds-dom0-stateless-0-target-all allocation score on s01-0: 0 pcmk__group_assign: iscsi-vds-dom0-stateless-0-target-all allocation score on s01-1: 0 pcmk__group_assign: iscsi-vds-dom0-stateless-0-vips allocation score on s01-0: 0 pcmk__group_assign: iscsi-vds-dom0-stateless-0-vips allocation score on s01-1: 0 pcmk__group_assign: iscsi-vds-dom0-stateless-0-vips-fw:0 allocation score on s01-0: 0 pcmk__group_assign: iscsi-vds-dom0-stateless-0-vips-fw:0 allocation score on s01-1: 2000 pcmk__group_assign: iscsi-vds-dom0-stateless-0-vips-fw:1 allocation score on s01-0: 2000 pcmk__group_assign: iscsi-vds-dom0-stateless-0-vips-fw:1 allocation score on s01-1: -INFINITY pcmk__group_assign: nginx allocation score on s01-0: 0 pcmk__group_assign: nginx allocation score on s01-1: 0 pcmk__group_assign: ospf-routing:0 allocation score on s01-0: 0 pcmk__group_assign: ospf-routing:0 allocation score on s01-1: 0 pcmk__group_assign: ospf-routing:1 allocation score on s01-0: 0 pcmk__group_assign: ospf-routing:1 allocation score on s01-1: -INFINITY pcmk__group_assign: ospfd:0 allocation score on s01-0: 0 pcmk__group_assign: ospfd:0 allocation score on s01-1: 1 pcmk__group_assign: ospfd:1 allocation score on s01-0: 1 pcmk__group_assign: ospfd:1 allocation score on s01-1: -INFINITY pcmk__group_assign: syslog-ng allocation score on s01-0: 0 pcmk__group_assign: syslog-ng allocation score on s01-1: 0 pcmk__group_assign: syslog-server allocation score on s01-0: 0 pcmk__group_assign: syslog-server allocation score on s01-1: 0 pcmk__group_assign: tftp-server allocation score on s01-0: 0 pcmk__group_assign: tftp-server allocation score on s01-1: 0 pcmk__group_assign: tftpd allocation score on s01-0: 0 pcmk__group_assign: tftpd allocation score on s01-1: 0 pcmk__group_assign: vip-227 allocation score on s01-0: 0 pcmk__group_assign: vip-227 allocation score on s01-1: 0 pcmk__group_assign: vip-227-fw:0 allocation score on s01-0: 0 pcmk__group_assign: vip-227-fw:0 allocation score on s01-1: 1 pcmk__group_assign: vip-227-fw:1 allocation score on s01-0: 1 pcmk__group_assign: vip-227-fw:1 allocation score on s01-1: -INFINITY pcmk__group_assign: vip-228 allocation score on s01-0: 0 pcmk__group_assign: vip-228 allocation score on s01-1: 0 pcmk__group_assign: vip-228-fw:0 allocation score on s01-0: 0 pcmk__group_assign: vip-228-fw:0 allocation score on s01-1: 1 pcmk__group_assign: vip-228-fw:1 allocation score on s01-0: 1 pcmk__group_assign: vip-228-fw:1 allocation score on s01-1: -INFINITY pcmk__group_assign: vip-232 allocation score on s01-0: 0 pcmk__group_assign: vip-232 allocation score on s01-1: 0 pcmk__group_assign: vip-233 allocation score on s01-0: 0 pcmk__group_assign: vip-233 allocation score on s01-1: 0 pcmk__group_assign: vip-234 allocation score on s01-0: 0 pcmk__group_assign: vip-234 allocation score on s01-1: 0 pcmk__group_assign: vip-235 allocation score on s01-0: 0 pcmk__group_assign: vip-235 allocation score on s01-1: 0 pcmk__group_assign: vip-235-fw:0 allocation score on s01-0: -INFINITY pcmk__group_assign: vip-235-fw:0 allocation score on s01-1: 1 pcmk__group_assign: vip-235-fw:1 allocation score on s01-0: 1 pcmk__group_assign: vip-235-fw:1 allocation score on s01-1: 0 pcmk__group_assign: vip-236 allocation score on s01-0: 0 pcmk__group_assign: vip-236 allocation score on s01-1: 0 pcmk__group_assign: vip-236-fw:0 allocation score on s01-0: -INFINITY pcmk__group_assign: vip-236-fw:0 allocation score on s01-1: 1 pcmk__group_assign: vip-236-fw:1 allocation score on s01-0: 1 pcmk__group_assign: vip-236-fw:1 allocation score on s01-1: 0 pcmk__group_assign: vip-237 allocation score on s01-0: 0 pcmk__group_assign: vip-237 allocation score on s01-1: 0 pcmk__group_assign: vip-237-fw:0 allocation score on s01-0: 0 pcmk__group_assign: vip-237-fw:0 allocation score on s01-1: 1 pcmk__group_assign: vip-237-fw:1 allocation score on s01-0: 1 pcmk__group_assign: vip-237-fw:1 allocation score on s01-1: -INFINITY pcmk__group_assign: vip-238 allocation score on s01-0: 0 pcmk__group_assign: vip-238 allocation score on s01-1: 0 pcmk__group_assign: vip-238-fw:0 allocation score on s01-0: 0 pcmk__group_assign: vip-238-fw:0 allocation score on s01-1: 1 pcmk__group_assign: vip-238-fw:1 allocation score on s01-0: 1 pcmk__group_assign: vip-238-fw:1 allocation score on s01-1: -INFINITY pcmk__group_assign: zebra:0 allocation score on s01-0: 0 pcmk__group_assign: zebra:0 allocation score on s01-1: 1 pcmk__group_assign: zebra:1 allocation score on s01-0: 1 pcmk__group_assign: zebra:1 allocation score on s01-1: -INFINITY pcmk__primitive_assign: clvmd:0 allocation score on s01-0: -INFINITY pcmk__primitive_assign: clvmd:0 allocation score on s01-1: 1 pcmk__primitive_assign: clvmd:1 allocation score on s01-0: 1 pcmk__primitive_assign: clvmd:1 allocation score on s01-1: -INFINITY pcmk__primitive_assign: dhcpd:0 allocation score on s01-0: -INFINITY pcmk__primitive_assign: dhcpd:0 allocation score on s01-1: -INFINITY pcmk__primitive_assign: dhcpd:1 allocation score on s01-0: -INFINITY pcmk__primitive_assign: dhcpd:1 allocation score on s01-1: -INFINITY pcmk__primitive_assign: dlm:0 allocation score on s01-0: -INFINITY pcmk__primitive_assign: dlm:0 allocation score on s01-1: 1 pcmk__primitive_assign: dlm:1 allocation score on s01-0: 1 pcmk__primitive_assign: dlm:1 allocation score on s01-1: -INFINITY pcmk__primitive_assign: drbd-pool-0:0 allocation score on s01-0: -INFINITY pcmk__primitive_assign: drbd-pool-0:0 allocation score on s01-1: 10001 pcmk__primitive_assign: drbd-pool-0:1 allocation score on s01-0: 11001 pcmk__primitive_assign: drbd-pool-0:1 allocation score on s01-1: 0 pcmk__primitive_assign: drbd-pool-1:0 allocation score on s01-0: 0 pcmk__primitive_assign: drbd-pool-1:0 allocation score on s01-1: 11001 pcmk__primitive_assign: drbd-pool-1:1 allocation score on s01-0: 10001 pcmk__primitive_assign: drbd-pool-1:1 allocation score on s01-1: -INFINITY pcmk__primitive_assign: drbd-s01-logs:0 allocation score on s01-0: 0 pcmk__primitive_assign: drbd-s01-logs:0 allocation score on s01-1: 10001 pcmk__primitive_assign: drbd-s01-logs:1 allocation score on s01-0: 10001 pcmk__primitive_assign: drbd-s01-logs:1 allocation score on s01-1: -INFINITY pcmk__primitive_assign: drbd-s01-service:0 allocation score on s01-0: 0 pcmk__primitive_assign: drbd-s01-service:0 allocation score on s01-1: 10001 pcmk__primitive_assign: drbd-s01-service:1 allocation score on s01-0: 10001 pcmk__primitive_assign: drbd-s01-service:1 allocation score on s01-1: -INFINITY pcmk__primitive_assign: drbd-s01-vm-data:0 allocation score on s01-0: -INFINITY pcmk__primitive_assign: drbd-s01-vm-data:0 allocation score on s01-1: 10001 pcmk__primitive_assign: drbd-s01-vm-data:1 allocation score on s01-0: 10001 pcmk__primitive_assign: drbd-s01-vm-data:1 allocation score on s01-1: -INFINITY pcmk__primitive_assign: drbd-vds-dom0-stateless-0:0 allocation score on s01-0: 0 pcmk__primitive_assign: drbd-vds-dom0-stateless-0:0 allocation score on s01-1: 10001 pcmk__primitive_assign: drbd-vds-dom0-stateless-0:1 allocation score on s01-0: 10001 pcmk__primitive_assign: drbd-vds-dom0-stateless-0:1 allocation score on s01-1: -INFINITY pcmk__primitive_assign: drbd-vds-http:0 allocation score on s01-0: 0 pcmk__primitive_assign: drbd-vds-http:0 allocation score on s01-1: 10001 pcmk__primitive_assign: drbd-vds-http:1 allocation score on s01-0: 10001 pcmk__primitive_assign: drbd-vds-http:1 allocation score on s01-1: -INFINITY pcmk__primitive_assign: drbd-vds-tftpboot:0 allocation score on s01-0: 0 pcmk__primitive_assign: drbd-vds-tftpboot:0 allocation score on s01-1: 10001 pcmk__primitive_assign: drbd-vds-tftpboot:1 allocation score on s01-0: 10001 pcmk__primitive_assign: drbd-vds-tftpboot:1 allocation score on s01-1: -INFINITY pcmk__primitive_assign: drbdlinks-s01-service:0 allocation score on s01-0: -INFINITY pcmk__primitive_assign: drbdlinks-s01-service:0 allocation score on s01-1: 1 pcmk__primitive_assign: drbdlinks-s01-service:1 allocation score on s01-0: 1 pcmk__primitive_assign: drbdlinks-s01-service:1 allocation score on s01-1: -INFINITY pcmk__primitive_assign: gfs2:0 allocation score on s01-0: -INFINITY pcmk__primitive_assign: gfs2:0 allocation score on s01-1: 1 pcmk__primitive_assign: gfs2:1 allocation score on s01-0: 1 pcmk__primitive_assign: gfs2:1 allocation score on s01-1: -INFINITY pcmk__primitive_assign: ietd:0 allocation score on s01-0: -INFINITY pcmk__primitive_assign: ietd:0 allocation score on s01-1: 1001 pcmk__primitive_assign: ietd:1 allocation score on s01-0: 12002 pcmk__primitive_assign: ietd:1 allocation score on s01-1: -INFINITY pcmk__primitive_assign: iscsi-pool-0-lun-1 allocation score on s01-0: 0 pcmk__primitive_assign: iscsi-pool-0-lun-1 allocation score on s01-1: -INFINITY pcmk__primitive_assign: iscsi-pool-0-target allocation score on s01-0: 12001 pcmk__primitive_assign: iscsi-pool-0-target allocation score on s01-1: -INFINITY pcmk__primitive_assign: iscsi-pool-1-lun-1 allocation score on s01-0: -INFINITY pcmk__primitive_assign: iscsi-pool-1-lun-1 allocation score on s01-1: 0 pcmk__primitive_assign: iscsi-pool-1-target allocation score on s01-0: -INFINITY pcmk__primitive_assign: iscsi-pool-1-target allocation score on s01-1: 12001 pcmk__primitive_assign: iscsi-vds-dom0-stateless-0-lun-1 allocation score on s01-0: -INFINITY pcmk__primitive_assign: iscsi-vds-dom0-stateless-0-lun-1 allocation score on s01-1: -INFINITY pcmk__primitive_assign: iscsi-vds-dom0-stateless-0-target allocation score on s01-0: -INFINITY pcmk__primitive_assign: iscsi-vds-dom0-stateless-0-target allocation score on s01-1: -INFINITY pcmk__primitive_assign: libvirtd:0 allocation score on s01-0: -INFINITY pcmk__primitive_assign: libvirtd:0 allocation score on s01-1: 1 pcmk__primitive_assign: libvirtd:1 allocation score on s01-0: 1 pcmk__primitive_assign: libvirtd:1 allocation score on s01-1: -INFINITY pcmk__primitive_assign: mgmt-vm allocation score on s01-0: -INFINITY pcmk__primitive_assign: mgmt-vm allocation score on s01-1: 0 pcmk__primitive_assign: nginx allocation score on s01-0: -INFINITY pcmk__primitive_assign: nginx allocation score on s01-1: -INFINITY pcmk__primitive_assign: o2cb:0 allocation score on s01-0: -INFINITY pcmk__primitive_assign: o2cb:0 allocation score on s01-1: -INFINITY pcmk__primitive_assign: o2cb:1 allocation score on s01-0: -INFINITY pcmk__primitive_assign: o2cb:1 allocation score on s01-1: -INFINITY pcmk__primitive_assign: ospfd:0 allocation score on s01-0: -INFINITY pcmk__primitive_assign: ospfd:0 allocation score on s01-1: 1 pcmk__primitive_assign: ospfd:1 allocation score on s01-0: 1 pcmk__primitive_assign: ospfd:1 allocation score on s01-1: -INFINITY pcmk__primitive_assign: ping-bmc-and-switch:0 allocation score on s01-0: 0 pcmk__primitive_assign: ping-bmc-and-switch:0 allocation score on s01-1: 1 pcmk__primitive_assign: ping-bmc-and-switch:1 allocation score on s01-0: 1 pcmk__primitive_assign: ping-bmc-and-switch:1 allocation score on s01-1: -INFINITY pcmk__primitive_assign: s01-logs-fs:0 allocation score on s01-0: -INFINITY pcmk__primitive_assign: s01-logs-fs:0 allocation score on s01-1: 10002 pcmk__primitive_assign: s01-logs-fs:1 allocation score on s01-0: 10002 pcmk__primitive_assign: s01-logs-fs:1 allocation score on s01-1: -INFINITY pcmk__primitive_assign: s01-service-fs:0 allocation score on s01-0: -INFINITY pcmk__primitive_assign: s01-service-fs:0 allocation score on s01-1: 10002 pcmk__primitive_assign: s01-service-fs:1 allocation score on s01-0: 10002 pcmk__primitive_assign: s01-service-fs:1 allocation score on s01-1: -INFINITY pcmk__primitive_assign: s01-vm-data-metadata-fs:0 allocation score on s01-0: -INFINITY pcmk__primitive_assign: s01-vm-data-metadata-fs:0 allocation score on s01-1: 1 pcmk__primitive_assign: s01-vm-data-metadata-fs:1 allocation score on s01-0: 1 pcmk__primitive_assign: s01-vm-data-metadata-fs:1 allocation score on s01-1: -INFINITY pcmk__primitive_assign: s01-vm-data-storage-pool:0 allocation score on s01-0: -INFINITY pcmk__primitive_assign: s01-vm-data-storage-pool:0 allocation score on s01-1: 1 pcmk__primitive_assign: s01-vm-data-storage-pool:1 allocation score on s01-0: 1 pcmk__primitive_assign: s01-vm-data-storage-pool:1 allocation score on s01-1: -INFINITY pcmk__primitive_assign: stonith-s01-0 allocation score on s01-0: -INFINITY pcmk__primitive_assign: stonith-s01-0 allocation score on s01-1: 0 pcmk__primitive_assign: stonith-s01-1 allocation score on s01-0: 0 pcmk__primitive_assign: stonith-s01-1 allocation score on s01-1: -INFINITY pcmk__primitive_assign: syslog-ng allocation score on s01-0: -INFINITY pcmk__primitive_assign: syslog-ng allocation score on s01-1: 0 pcmk__primitive_assign: tftpd allocation score on s01-0: -INFINITY pcmk__primitive_assign: tftpd allocation score on s01-1: -INFINITY pcmk__primitive_assign: vds-http-fs:0 allocation score on s01-0: -INFINITY pcmk__primitive_assign: vds-http-fs:0 allocation score on s01-1: 10002 pcmk__primitive_assign: vds-http-fs:1 allocation score on s01-0: 10002 pcmk__primitive_assign: vds-http-fs:1 allocation score on s01-1: -INFINITY pcmk__primitive_assign: vds-tftpboot-fs:0 allocation score on s01-0: -INFINITY pcmk__primitive_assign: vds-tftpboot-fs:0 allocation score on s01-1: -INFINITY pcmk__primitive_assign: vds-tftpboot-fs:1 allocation score on s01-0: -INFINITY pcmk__primitive_assign: vds-tftpboot-fs:1 allocation score on s01-1: -INFINITY pcmk__primitive_assign: vg-s01-vm-data:0 allocation score on s01-0: -INFINITY pcmk__primitive_assign: vg-s01-vm-data:0 allocation score on s01-1: 10002 pcmk__primitive_assign: vg-s01-vm-data:1 allocation score on s01-0: 10002 pcmk__primitive_assign: vg-s01-vm-data:1 allocation score on s01-1: -INFINITY pcmk__primitive_assign: vip-227 allocation score on s01-0: -INFINITY pcmk__primitive_assign: vip-227 allocation score on s01-1: -INFINITY pcmk__primitive_assign: vip-227-fw:0 allocation score on s01-0: 0 pcmk__primitive_assign: vip-227-fw:0 allocation score on s01-1: 2 pcmk__primitive_assign: vip-227-fw:1 allocation score on s01-0: 2 pcmk__primitive_assign: vip-227-fw:1 allocation score on s01-1: -INFINITY pcmk__primitive_assign: vip-228 allocation score on s01-0: -INFINITY pcmk__primitive_assign: vip-228 allocation score on s01-1: -INFINITY pcmk__primitive_assign: vip-228-fw:0 allocation score on s01-0: -INFINITY pcmk__primitive_assign: vip-228-fw:0 allocation score on s01-1: 1 pcmk__primitive_assign: vip-228-fw:1 allocation score on s01-0: 1 pcmk__primitive_assign: vip-228-fw:1 allocation score on s01-1: -INFINITY pcmk__primitive_assign: vip-232 allocation score on s01-0: -INFINITY pcmk__primitive_assign: vip-232 allocation score on s01-1: -INFINITY pcmk__primitive_assign: vip-233 allocation score on s01-0: 0 pcmk__primitive_assign: vip-233 allocation score on s01-1: 0 pcmk__primitive_assign: vip-234 allocation score on s01-0: 0 pcmk__primitive_assign: vip-234 allocation score on s01-1: 0 pcmk__primitive_assign: vip-235 allocation score on s01-0: 0 pcmk__primitive_assign: vip-235 allocation score on s01-1: -INFINITY pcmk__primitive_assign: vip-235-fw:0 allocation score on s01-0: -INFINITY pcmk__primitive_assign: vip-235-fw:0 allocation score on s01-1: 2 pcmk__primitive_assign: vip-235-fw:1 allocation score on s01-0: 2 pcmk__primitive_assign: vip-235-fw:1 allocation score on s01-1: 0 pcmk__primitive_assign: vip-236 allocation score on s01-0: 0 pcmk__primitive_assign: vip-236 allocation score on s01-1: -INFINITY pcmk__primitive_assign: vip-236-fw:0 allocation score on s01-0: -INFINITY pcmk__primitive_assign: vip-236-fw:0 allocation score on s01-1: 1 pcmk__primitive_assign: vip-236-fw:1 allocation score on s01-0: 1 pcmk__primitive_assign: vip-236-fw:1 allocation score on s01-1: -INFINITY pcmk__primitive_assign: vip-237 allocation score on s01-0: -INFINITY pcmk__primitive_assign: vip-237 allocation score on s01-1: 0 pcmk__primitive_assign: vip-237-fw:0 allocation score on s01-0: 0 pcmk__primitive_assign: vip-237-fw:0 allocation score on s01-1: 2 pcmk__primitive_assign: vip-237-fw:1 allocation score on s01-0: 2 pcmk__primitive_assign: vip-237-fw:1 allocation score on s01-1: -INFINITY pcmk__primitive_assign: vip-238 allocation score on s01-0: -INFINITY pcmk__primitive_assign: vip-238 allocation score on s01-1: 0 pcmk__primitive_assign: vip-238-fw:0 allocation score on s01-0: -INFINITY pcmk__primitive_assign: vip-238-fw:0 allocation score on s01-1: 1 pcmk__primitive_assign: vip-238-fw:1 allocation score on s01-0: 1 pcmk__primitive_assign: vip-238-fw:1 allocation score on s01-1: -INFINITY pcmk__primitive_assign: xinetd:0 allocation score on s01-0: 0 pcmk__primitive_assign: xinetd:0 allocation score on s01-1: 1 pcmk__primitive_assign: xinetd:1 allocation score on s01-0: 1 pcmk__primitive_assign: xinetd:1 allocation score on s01-1: -INFINITY pcmk__primitive_assign: zebra:0 allocation score on s01-0: 0 pcmk__primitive_assign: zebra:0 allocation score on s01-1: 2 pcmk__primitive_assign: zebra:1 allocation score on s01-0: 2 pcmk__primitive_assign: zebra:1 allocation score on s01-1: -INFINITY diff --git a/cts/scheduler/scores/coloc-cloned-group-promoted-dependent1.scores b/cts/scheduler/scores/coloc-cloned-group-promoted-dependent1.scores new file mode 100644 index 0000000000..a900815c34 --- /dev/null +++ b/cts/scheduler/scores/coloc-cloned-group-promoted-dependent1.scores @@ -0,0 +1,27 @@ + +grp1:0 promotion score on node2: -INFINITY +grp1:1 promotion score on node1: INFINITY +pcmk__clone_assign: grp1-clone allocation score on node1: 0 +pcmk__clone_assign: grp1-clone allocation score on node2: 0 +pcmk__clone_assign: grp1:0 allocation score on node1: 0 +pcmk__clone_assign: grp1:0 allocation score on node2: 50 +pcmk__clone_assign: grp1:1 allocation score on node1: 10 +pcmk__clone_assign: grp1:1 allocation score on node2: 0 +pcmk__clone_assign: rsc1:0 allocation score on node1: 0 +pcmk__clone_assign: rsc1:0 allocation score on node2: 1 +pcmk__clone_assign: rsc1:1 allocation score on node1: 1 +pcmk__clone_assign: rsc1:1 allocation score on node2: 0 +pcmk__group_assign: grp1:0 allocation score on node1: 0 +pcmk__group_assign: grp1:0 allocation score on node2: 50 +pcmk__group_assign: grp1:1 allocation score on node1: 10 +pcmk__group_assign: grp1:1 allocation score on node2: -INFINITY +pcmk__group_assign: rsc1:0 allocation score on node1: 0 +pcmk__group_assign: rsc1:0 allocation score on node2: 1 +pcmk__group_assign: rsc1:1 allocation score on node1: 1 +pcmk__group_assign: rsc1:1 allocation score on node2: -INFINITY +pcmk__primitive_assign: primary allocation score on node1: 1 +pcmk__primitive_assign: primary allocation score on node2: 0 +pcmk__primitive_assign: rsc1:0 allocation score on node1: 0 +pcmk__primitive_assign: rsc1:0 allocation score on node2: 1 +pcmk__primitive_assign: rsc1:1 allocation score on node1: 1 +pcmk__primitive_assign: rsc1:1 allocation score on node2: -INFINITY diff --git a/cts/scheduler/scores/coloc-cloned-group-promoted-dependent2.scores b/cts/scheduler/scores/coloc-cloned-group-promoted-dependent2.scores new file mode 100644 index 0000000000..ac0a7f2d54 --- /dev/null +++ b/cts/scheduler/scores/coloc-cloned-group-promoted-dependent2.scores @@ -0,0 +1,27 @@ + +grp1:0 promotion score on node2: 50 +grp1:1 promotion score on node1: 2010 +pcmk__clone_assign: grp1-clone allocation score on node1: 0 +pcmk__clone_assign: grp1-clone allocation score on node2: 0 +pcmk__clone_assign: grp1:0 allocation score on node1: 0 +pcmk__clone_assign: grp1:0 allocation score on node2: 50 +pcmk__clone_assign: grp1:1 allocation score on node1: 10 +pcmk__clone_assign: grp1:1 allocation score on node2: 0 +pcmk__clone_assign: rsc1:0 allocation score on node1: 0 +pcmk__clone_assign: rsc1:0 allocation score on node2: 1 +pcmk__clone_assign: rsc1:1 allocation score on node1: 1 +pcmk__clone_assign: rsc1:1 allocation score on node2: 0 +pcmk__group_assign: grp1:0 allocation score on node1: 0 +pcmk__group_assign: grp1:0 allocation score on node2: 50 +pcmk__group_assign: grp1:1 allocation score on node1: 10 +pcmk__group_assign: grp1:1 allocation score on node2: -INFINITY +pcmk__group_assign: rsc1:0 allocation score on node1: 0 +pcmk__group_assign: rsc1:0 allocation score on node2: 1 +pcmk__group_assign: rsc1:1 allocation score on node1: 1 +pcmk__group_assign: rsc1:1 allocation score on node2: -INFINITY +pcmk__primitive_assign: primary allocation score on node1: 1 +pcmk__primitive_assign: primary allocation score on node2: 0 +pcmk__primitive_assign: rsc1:0 allocation score on node1: 0 +pcmk__primitive_assign: rsc1:0 allocation score on node2: 1 +pcmk__primitive_assign: rsc1:1 allocation score on node1: 1 +pcmk__primitive_assign: rsc1:1 allocation score on node2: -INFINITY diff --git a/cts/scheduler/scores/coloc-optional-promoted-dependent-moves-1.scores b/cts/scheduler/scores/coloc-optional-promoted-dependent-moves-1.scores new file mode 100644 index 0000000000..cf497120d1 --- /dev/null +++ b/cts/scheduler/scores/coloc-optional-promoted-dependent-moves-1.scores @@ -0,0 +1,15 @@ + +coloc_dependent:0 promotion score on fastvm-fedora39-22: 150 +coloc_dependent:1 promotion score on fastvm-fedora39-23: 130 +pcmk__clone_assign: coloc_dependent-clone allocation score on fastvm-fedora39-22: 0 +pcmk__clone_assign: coloc_dependent-clone allocation score on fastvm-fedora39-23: 0 +pcmk__clone_assign: coloc_dependent:0 allocation score on fastvm-fedora39-22: 151 +pcmk__clone_assign: coloc_dependent:0 allocation score on fastvm-fedora39-23: 0 +pcmk__clone_assign: coloc_dependent:1 allocation score on fastvm-fedora39-22: 0 +pcmk__clone_assign: coloc_dependent:1 allocation score on fastvm-fedora39-23: 101 +pcmk__primitive_assign: coloc_dependent:0 allocation score on fastvm-fedora39-22: 151 +pcmk__primitive_assign: coloc_dependent:0 allocation score on fastvm-fedora39-23: 0 +pcmk__primitive_assign: coloc_dependent:1 allocation score on fastvm-fedora39-22: -INFINITY +pcmk__primitive_assign: coloc_dependent:1 allocation score on fastvm-fedora39-23: 101 +pcmk__primitive_assign: coloc_primary allocation score on fastvm-fedora39-22: 1 +pcmk__primitive_assign: coloc_primary allocation score on fastvm-fedora39-23: INFINITY diff --git a/cts/scheduler/scores/coloc-optional-promoted-dependent-moves-2.scores b/cts/scheduler/scores/coloc-optional-promoted-dependent-moves-2.scores new file mode 100644 index 0000000000..b02db784d5 --- /dev/null +++ b/cts/scheduler/scores/coloc-optional-promoted-dependent-moves-2.scores @@ -0,0 +1,15 @@ + +coloc_dependent:0 promotion score on fastvm-fedora39-22: 150 +coloc_dependent:1 promotion score on fastvm-fedora39-23: 160 +pcmk__clone_assign: coloc_dependent-clone allocation score on fastvm-fedora39-22: 0 +pcmk__clone_assign: coloc_dependent-clone allocation score on fastvm-fedora39-23: 0 +pcmk__clone_assign: coloc_dependent:0 allocation score on fastvm-fedora39-22: 151 +pcmk__clone_assign: coloc_dependent:0 allocation score on fastvm-fedora39-23: 0 +pcmk__clone_assign: coloc_dependent:1 allocation score on fastvm-fedora39-22: 0 +pcmk__clone_assign: coloc_dependent:1 allocation score on fastvm-fedora39-23: 101 +pcmk__primitive_assign: coloc_dependent:0 allocation score on fastvm-fedora39-22: 151 +pcmk__primitive_assign: coloc_dependent:0 allocation score on fastvm-fedora39-23: 0 +pcmk__primitive_assign: coloc_dependent:1 allocation score on fastvm-fedora39-22: -INFINITY +pcmk__primitive_assign: coloc_dependent:1 allocation score on fastvm-fedora39-23: 101 +pcmk__primitive_assign: coloc_primary allocation score on fastvm-fedora39-22: 1 +pcmk__primitive_assign: coloc_primary allocation score on fastvm-fedora39-23: INFINITY diff --git a/cts/scheduler/scores/coloc-optional-promoted-dependent-stays-1.scores b/cts/scheduler/scores/coloc-optional-promoted-dependent-stays-1.scores new file mode 100644 index 0000000000..830e01b0ff --- /dev/null +++ b/cts/scheduler/scores/coloc-optional-promoted-dependent-stays-1.scores @@ -0,0 +1,15 @@ + +coloc_dependent:0 promotion score on fastvm-fedora39-22: 100 +coloc_dependent:1 promotion score on fastvm-fedora39-23: 210 +pcmk__clone_assign: coloc_dependent-clone allocation score on fastvm-fedora39-22: 0 +pcmk__clone_assign: coloc_dependent-clone allocation score on fastvm-fedora39-23: 0 +pcmk__clone_assign: coloc_dependent:0 allocation score on fastvm-fedora39-22: 101 +pcmk__clone_assign: coloc_dependent:0 allocation score on fastvm-fedora39-23: 0 +pcmk__clone_assign: coloc_dependent:1 allocation score on fastvm-fedora39-22: 0 +pcmk__clone_assign: coloc_dependent:1 allocation score on fastvm-fedora39-23: 151 +pcmk__primitive_assign: coloc_dependent:0 allocation score on fastvm-fedora39-22: 101 +pcmk__primitive_assign: coloc_dependent:0 allocation score on fastvm-fedora39-23: -INFINITY +pcmk__primitive_assign: coloc_dependent:1 allocation score on fastvm-fedora39-22: 0 +pcmk__primitive_assign: coloc_dependent:1 allocation score on fastvm-fedora39-23: 151 +pcmk__primitive_assign: coloc_primary allocation score on fastvm-fedora39-22: 1 +pcmk__primitive_assign: coloc_primary allocation score on fastvm-fedora39-23: INFINITY diff --git a/cts/scheduler/scores/coloc-optional-promoted-dependent-stays-2.scores b/cts/scheduler/scores/coloc-optional-promoted-dependent-stays-2.scores new file mode 100644 index 0000000000..cf497120d1 --- /dev/null +++ b/cts/scheduler/scores/coloc-optional-promoted-dependent-stays-2.scores @@ -0,0 +1,15 @@ + +coloc_dependent:0 promotion score on fastvm-fedora39-22: 150 +coloc_dependent:1 promotion score on fastvm-fedora39-23: 130 +pcmk__clone_assign: coloc_dependent-clone allocation score on fastvm-fedora39-22: 0 +pcmk__clone_assign: coloc_dependent-clone allocation score on fastvm-fedora39-23: 0 +pcmk__clone_assign: coloc_dependent:0 allocation score on fastvm-fedora39-22: 151 +pcmk__clone_assign: coloc_dependent:0 allocation score on fastvm-fedora39-23: 0 +pcmk__clone_assign: coloc_dependent:1 allocation score on fastvm-fedora39-22: 0 +pcmk__clone_assign: coloc_dependent:1 allocation score on fastvm-fedora39-23: 101 +pcmk__primitive_assign: coloc_dependent:0 allocation score on fastvm-fedora39-22: 151 +pcmk__primitive_assign: coloc_dependent:0 allocation score on fastvm-fedora39-23: 0 +pcmk__primitive_assign: coloc_dependent:1 allocation score on fastvm-fedora39-22: -INFINITY +pcmk__primitive_assign: coloc_dependent:1 allocation score on fastvm-fedora39-23: 101 +pcmk__primitive_assign: coloc_primary allocation score on fastvm-fedora39-22: 1 +pcmk__primitive_assign: coloc_primary allocation score on fastvm-fedora39-23: INFINITY diff --git a/cts/scheduler/scores/promoted-partially-demoted-group.scores b/cts/scheduler/scores/promoted-partially-demoted-group.scores index f266c64753..d1504ac08c 100644 --- a/cts/scheduler/scores/promoted-partially-demoted-group.scores +++ b/cts/scheduler/scores/promoted-partially-demoted-group.scores @@ -1,93 +1,93 @@ cdev-pool-0-drbd:0 promotion score on sd01-1: 10800 cdev-pool-0-drbd:1 promotion score on sd01-0: INFINITY cdev-pool-0-iscsi-vips-fw:0 promotion score on sd01-1: -INFINITY -cdev-pool-0-iscsi-vips-fw:1 promotion score on sd01-0: 2000 +cdev-pool-0-iscsi-vips-fw:1 promotion score on sd01-0: INFINITY pcmk__clone_assign: cdev-pool-0-drbd:0 allocation score on sd01-0: 0 pcmk__clone_assign: cdev-pool-0-drbd:0 allocation score on sd01-1: 10100 pcmk__clone_assign: cdev-pool-0-drbd:1 allocation score on sd01-0: 10100 pcmk__clone_assign: cdev-pool-0-drbd:1 allocation score on sd01-1: 0 pcmk__clone_assign: cdev-pool-0-iscsi-vips-fw:0 allocation score on sd01-0: 0 pcmk__clone_assign: cdev-pool-0-iscsi-vips-fw:0 allocation score on sd01-1: 2000 pcmk__clone_assign: cdev-pool-0-iscsi-vips-fw:1 allocation score on sd01-0: 2000 pcmk__clone_assign: cdev-pool-0-iscsi-vips-fw:1 allocation score on sd01-1: 0 pcmk__clone_assign: cl-ietd allocation score on sd01-0: INFINITY pcmk__clone_assign: cl-ietd allocation score on sd01-1: 300 pcmk__clone_assign: cl-vlan1-net allocation score on sd01-0: 0 pcmk__clone_assign: cl-vlan1-net allocation score on sd01-1: 0 pcmk__clone_assign: ietd:0 allocation score on sd01-0: 0 pcmk__clone_assign: ietd:0 allocation score on sd01-1: 100 pcmk__clone_assign: ietd:1 allocation score on sd01-0: 100 pcmk__clone_assign: ietd:1 allocation score on sd01-1: 0 pcmk__clone_assign: ms-cdev-pool-0-drbd allocation score on sd01-0: INFINITY pcmk__clone_assign: ms-cdev-pool-0-drbd allocation score on sd01-1: 400 pcmk__clone_assign: ms-cdev-pool-0-iscsi-vips-fw allocation score on sd01-0: 0 pcmk__clone_assign: ms-cdev-pool-0-iscsi-vips-fw allocation score on sd01-1: 0 pcmk__clone_assign: vip-164-fw:0 allocation score on sd01-0: 0 pcmk__clone_assign: vip-164-fw:0 allocation score on sd01-1: 100 pcmk__clone_assign: vip-164-fw:1 allocation score on sd01-0: 100 pcmk__clone_assign: vip-164-fw:1 allocation score on sd01-1: 0 pcmk__clone_assign: vip-165-fw:0 allocation score on sd01-0: 0 pcmk__clone_assign: vip-165-fw:0 allocation score on sd01-1: 100 pcmk__clone_assign: vip-165-fw:1 allocation score on sd01-0: 100 pcmk__clone_assign: vip-165-fw:1 allocation score on sd01-1: 0 pcmk__clone_assign: vlan1-net:0 allocation score on sd01-0: 0 pcmk__clone_assign: vlan1-net:0 allocation score on sd01-1: 100 pcmk__clone_assign: vlan1-net:1 allocation score on sd01-0: 100 pcmk__clone_assign: vlan1-net:1 allocation score on sd01-1: 0 pcmk__group_assign: cdev-pool-0-iscsi-export allocation score on sd01-0: INFINITY pcmk__group_assign: cdev-pool-0-iscsi-export allocation score on sd01-1: 0 pcmk__group_assign: cdev-pool-0-iscsi-lun-1 allocation score on sd01-0: 0 pcmk__group_assign: cdev-pool-0-iscsi-lun-1 allocation score on sd01-1: 100 pcmk__group_assign: cdev-pool-0-iscsi-target allocation score on sd01-0: INFINITY pcmk__group_assign: cdev-pool-0-iscsi-target allocation score on sd01-1: 100 pcmk__group_assign: cdev-pool-0-iscsi-vips allocation score on sd01-0: 0 pcmk__group_assign: cdev-pool-0-iscsi-vips allocation score on sd01-1: 0 pcmk__group_assign: cdev-pool-0-iscsi-vips-fw:0 allocation score on sd01-0: -INFINITY pcmk__group_assign: cdev-pool-0-iscsi-vips-fw:0 allocation score on sd01-1: 2000 pcmk__group_assign: cdev-pool-0-iscsi-vips-fw:1 allocation score on sd01-0: 2000 pcmk__group_assign: cdev-pool-0-iscsi-vips-fw:1 allocation score on sd01-1: 0 pcmk__group_assign: vip-164 allocation score on sd01-0: 0 pcmk__group_assign: vip-164 allocation score on sd01-1: 100 pcmk__group_assign: vip-164-fw:0 allocation score on sd01-0: -INFINITY pcmk__group_assign: vip-164-fw:0 allocation score on sd01-1: 100 pcmk__group_assign: vip-164-fw:1 allocation score on sd01-0: 100 pcmk__group_assign: vip-164-fw:1 allocation score on sd01-1: 0 pcmk__group_assign: vip-165 allocation score on sd01-0: 0 pcmk__group_assign: vip-165 allocation score on sd01-1: 100 pcmk__group_assign: vip-165-fw:0 allocation score on sd01-0: -INFINITY pcmk__group_assign: vip-165-fw:0 allocation score on sd01-1: 100 pcmk__group_assign: vip-165-fw:1 allocation score on sd01-0: 100 pcmk__group_assign: vip-165-fw:1 allocation score on sd01-1: 0 pcmk__primitive_assign: cdev-pool-0-drbd:0 allocation score on sd01-0: -INFINITY pcmk__primitive_assign: cdev-pool-0-drbd:0 allocation score on sd01-1: 10500 pcmk__primitive_assign: cdev-pool-0-drbd:1 allocation score on sd01-0: INFINITY pcmk__primitive_assign: cdev-pool-0-drbd:1 allocation score on sd01-1: 400 pcmk__primitive_assign: cdev-pool-0-iscsi-lun-1 allocation score on sd01-0: 0 pcmk__primitive_assign: cdev-pool-0-iscsi-lun-1 allocation score on sd01-1: -INFINITY pcmk__primitive_assign: cdev-pool-0-iscsi-target allocation score on sd01-0: INFINITY pcmk__primitive_assign: cdev-pool-0-iscsi-target allocation score on sd01-1: -INFINITY pcmk__primitive_assign: ietd:0 allocation score on sd01-0: -INFINITY pcmk__primitive_assign: ietd:0 allocation score on sd01-1: 100 pcmk__primitive_assign: ietd:1 allocation score on sd01-0: INFINITY pcmk__primitive_assign: ietd:1 allocation score on sd01-1: -INFINITY pcmk__primitive_assign: stonith-xvm-sd01-0 allocation score on sd01-0: -INFINITY pcmk__primitive_assign: stonith-xvm-sd01-0 allocation score on sd01-1: 100 pcmk__primitive_assign: stonith-xvm-sd01-1 allocation score on sd01-0: 100 pcmk__primitive_assign: stonith-xvm-sd01-1 allocation score on sd01-1: -INFINITY pcmk__primitive_assign: vip-164 allocation score on sd01-0: 0 pcmk__primitive_assign: vip-164 allocation score on sd01-1: -INFINITY pcmk__primitive_assign: vip-164-fw:0 allocation score on sd01-0: -INFINITY pcmk__primitive_assign: vip-164-fw:0 allocation score on sd01-1: 200 pcmk__primitive_assign: vip-164-fw:1 allocation score on sd01-0: 200 pcmk__primitive_assign: vip-164-fw:1 allocation score on sd01-1: 0 pcmk__primitive_assign: vip-165 allocation score on sd01-0: 0 pcmk__primitive_assign: vip-165 allocation score on sd01-1: -INFINITY pcmk__primitive_assign: vip-165-fw:0 allocation score on sd01-0: -INFINITY pcmk__primitive_assign: vip-165-fw:0 allocation score on sd01-1: 100 pcmk__primitive_assign: vip-165-fw:1 allocation score on sd01-0: 100 pcmk__primitive_assign: vip-165-fw:1 allocation score on sd01-1: -INFINITY pcmk__primitive_assign: vlan1-net:0 allocation score on sd01-0: 0 pcmk__primitive_assign: vlan1-net:0 allocation score on sd01-1: 100 pcmk__primitive_assign: vlan1-net:1 allocation score on sd01-0: 100 pcmk__primitive_assign: vlan1-net:1 allocation score on sd01-1: -INFINITY diff --git a/cts/scheduler/scores/use-after-free-merge.scores b/cts/scheduler/scores/use-after-free-merge.scores index ff428e9031..04efe04107 100644 --- a/cts/scheduler/scores/use-after-free-merge.scores +++ b/cts/scheduler/scores/use-after-free-merge.scores @@ -1,25 +1,25 @@ pcmk__clone_assign: ms0 allocation score on hex-13: 0 pcmk__clone_assign: ms0 allocation score on hex-14: 0 pcmk__clone_assign: s0:0 allocation score on hex-13: 0 pcmk__clone_assign: s0:0 allocation score on hex-14: 0 pcmk__clone_assign: s0:1 allocation score on hex-13: 0 pcmk__clone_assign: s0:1 allocation score on hex-14: 0 pcmk__group_assign: d0 allocation score on hex-13: 0 pcmk__group_assign: d0 allocation score on hex-14: 0 pcmk__group_assign: d1 allocation score on hex-13: 0 pcmk__group_assign: d1 allocation score on hex-14: 0 pcmk__group_assign: g0 allocation score on hex-13: 0 pcmk__group_assign: g0 allocation score on hex-14: 0 pcmk__primitive_assign: d0 allocation score on hex-13: -INFINITY pcmk__primitive_assign: d0 allocation score on hex-14: -INFINITY pcmk__primitive_assign: d1 allocation score on hex-13: -INFINITY pcmk__primitive_assign: d1 allocation score on hex-14: -INFINITY pcmk__primitive_assign: fencing-sbd allocation score on hex-13: -INFINITY pcmk__primitive_assign: fencing-sbd allocation score on hex-14: 0 pcmk__primitive_assign: s0:0 allocation score on hex-13: 0 pcmk__primitive_assign: s0:0 allocation score on hex-14: 0 pcmk__primitive_assign: s0:1 allocation score on hex-13: -INFINITY pcmk__primitive_assign: s0:1 allocation score on hex-14: 0 -s0:0 promotion score on hex-13: -1 -s0:1 promotion score on hex-14: -1 +s0:0 promotion score on hex-13: -INFINITY +s0:1 promotion score on hex-14: -INFINITY diff --git a/cts/scheduler/summary/coloc-cloned-group-promoted-dependent1.summary b/cts/scheduler/summary/coloc-cloned-group-promoted-dependent1.summary new file mode 100644 index 0000000000..6e8518093e --- /dev/null +++ b/cts/scheduler/summary/coloc-cloned-group-promoted-dependent1.summary @@ -0,0 +1,23 @@ +Current cluster status: + * Node List: + * Online: [ node1 node2 ] + + * Full List of Resources: + * Clone Set: grp1-clone [grp1] (promotable): + * Promoted: [ node1 ] + * Unpromoted: [ node2 ] + * primary (ocf:pacemaker:Dummy): Started node1 + +Transition Summary: + +Executing Cluster Transition: + +Revised Cluster Status: + * Node List: + * Online: [ node1 node2 ] + + * Full List of Resources: + * Clone Set: grp1-clone [grp1] (promotable): + * Promoted: [ node1 ] + * Unpromoted: [ node2 ] + * primary (ocf:pacemaker:Dummy): Started node1 diff --git a/cts/scheduler/summary/coloc-cloned-group-promoted-dependent2.summary b/cts/scheduler/summary/coloc-cloned-group-promoted-dependent2.summary new file mode 100644 index 0000000000..6e8518093e --- /dev/null +++ b/cts/scheduler/summary/coloc-cloned-group-promoted-dependent2.summary @@ -0,0 +1,23 @@ +Current cluster status: + * Node List: + * Online: [ node1 node2 ] + + * Full List of Resources: + * Clone Set: grp1-clone [grp1] (promotable): + * Promoted: [ node1 ] + * Unpromoted: [ node2 ] + * primary (ocf:pacemaker:Dummy): Started node1 + +Transition Summary: + +Executing Cluster Transition: + +Revised Cluster Status: + * Node List: + * Online: [ node1 node2 ] + + * Full List of Resources: + * Clone Set: grp1-clone [grp1] (promotable): + * Promoted: [ node1 ] + * Unpromoted: [ node2 ] + * primary (ocf:pacemaker:Dummy): Started node1 diff --git a/cts/scheduler/summary/coloc-optional-promoted-dependent-moves-1.summary b/cts/scheduler/summary/coloc-optional-promoted-dependent-moves-1.summary new file mode 100644 index 0000000000..0da352d9e8 --- /dev/null +++ b/cts/scheduler/summary/coloc-optional-promoted-dependent-moves-1.summary @@ -0,0 +1,39 @@ +Current cluster status: + * Node List: + * Online: [ fastvm-fedora39-22 fastvm-fedora39-23 ] + + * Full List of Resources: + * coloc_primary (ocf:pacemaker:Dummy): Started fastvm-fedora39-22 + * Clone Set: coloc_dependent-clone [coloc_dependent] (promotable): + * Promoted: [ fastvm-fedora39-23 ] + * Unpromoted: [ fastvm-fedora39-22 ] + +Transition Summary: + * Move coloc_primary ( fastvm-fedora39-22 -> fastvm-fedora39-23 ) + * Promote coloc_dependent:0 ( Unpromoted -> Promoted fastvm-fedora39-22 ) + * Demote coloc_dependent:1 ( Promoted -> Unpromoted fastvm-fedora39-23 ) + +Executing Cluster Transition: + * Resource action: coloc_primary stop on fastvm-fedora39-22 + * Resource action: coloc_dependent cancel=11000 on fastvm-fedora39-22 + * Resource action: coloc_dependent cancel=10000 on fastvm-fedora39-23 + * Pseudo action: coloc_dependent-clone_demote_0 + * Resource action: coloc_primary start on fastvm-fedora39-23 + * Resource action: coloc_dependent demote on fastvm-fedora39-23 + * Pseudo action: coloc_dependent-clone_demoted_0 + * Pseudo action: coloc_dependent-clone_promote_0 + * Resource action: coloc_primary monitor=10000 on fastvm-fedora39-23 + * Resource action: coloc_dependent promote on fastvm-fedora39-22 + * Resource action: coloc_dependent monitor=11000 on fastvm-fedora39-23 + * Pseudo action: coloc_dependent-clone_promoted_0 + * Resource action: coloc_dependent monitor=10000 on fastvm-fedora39-22 + +Revised Cluster Status: + * Node List: + * Online: [ fastvm-fedora39-22 fastvm-fedora39-23 ] + + * Full List of Resources: + * coloc_primary (ocf:pacemaker:Dummy): Started fastvm-fedora39-23 + * Clone Set: coloc_dependent-clone [coloc_dependent] (promotable): + * Promoted: [ fastvm-fedora39-22 ] + * Unpromoted: [ fastvm-fedora39-23 ] diff --git a/cts/scheduler/summary/coloc-optional-promoted-dependent-moves-2.summary b/cts/scheduler/summary/coloc-optional-promoted-dependent-moves-2.summary new file mode 100644 index 0000000000..d7142d54fa --- /dev/null +++ b/cts/scheduler/summary/coloc-optional-promoted-dependent-moves-2.summary @@ -0,0 +1,39 @@ +Current cluster status: + * Node List: + * Online: [ fastvm-fedora39-22 fastvm-fedora39-23 ] + + * Full List of Resources: + * coloc_primary (ocf:pacemaker:Dummy): Started fastvm-fedora39-22 + * Clone Set: coloc_dependent-clone [coloc_dependent] (promotable): + * Promoted: [ fastvm-fedora39-22 ] + * Unpromoted: [ fastvm-fedora39-23 ] + +Transition Summary: + * Move coloc_primary ( fastvm-fedora39-22 -> fastvm-fedora39-23 ) + * Demote coloc_dependent:0 ( Promoted -> Unpromoted fastvm-fedora39-22 ) + * Promote coloc_dependent:1 ( Unpromoted -> Promoted fastvm-fedora39-23 ) + +Executing Cluster Transition: + * Resource action: coloc_primary stop on fastvm-fedora39-22 + * Resource action: coloc_dependent cancel=10000 on fastvm-fedora39-22 + * Resource action: coloc_dependent cancel=11000 on fastvm-fedora39-23 + * Pseudo action: coloc_dependent-clone_demote_0 + * Resource action: coloc_primary start on fastvm-fedora39-23 + * Resource action: coloc_dependent demote on fastvm-fedora39-22 + * Pseudo action: coloc_dependent-clone_demoted_0 + * Pseudo action: coloc_dependent-clone_promote_0 + * Resource action: coloc_primary monitor=10000 on fastvm-fedora39-23 + * Resource action: coloc_dependent monitor=11000 on fastvm-fedora39-22 + * Resource action: coloc_dependent promote on fastvm-fedora39-23 + * Pseudo action: coloc_dependent-clone_promoted_0 + * Resource action: coloc_dependent monitor=10000 on fastvm-fedora39-23 + +Revised Cluster Status: + * Node List: + * Online: [ fastvm-fedora39-22 fastvm-fedora39-23 ] + + * Full List of Resources: + * coloc_primary (ocf:pacemaker:Dummy): Started fastvm-fedora39-23 + * Clone Set: coloc_dependent-clone [coloc_dependent] (promotable): + * Promoted: [ fastvm-fedora39-23 ] + * Unpromoted: [ fastvm-fedora39-22 ] diff --git a/cts/scheduler/summary/coloc-optional-promoted-dependent-stays-1.summary b/cts/scheduler/summary/coloc-optional-promoted-dependent-stays-1.summary new file mode 100644 index 0000000000..6c7fc42d17 --- /dev/null +++ b/cts/scheduler/summary/coloc-optional-promoted-dependent-stays-1.summary @@ -0,0 +1,27 @@ +Current cluster status: + * Node List: + * Online: [ fastvm-fedora39-22 fastvm-fedora39-23 ] + + * Full List of Resources: + * coloc_primary (ocf:pacemaker:Dummy): Started fastvm-fedora39-22 + * Clone Set: coloc_dependent-clone [coloc_dependent] (promotable): + * Promoted: [ fastvm-fedora39-23 ] + * Unpromoted: [ fastvm-fedora39-22 ] + +Transition Summary: + * Move coloc_primary ( fastvm-fedora39-22 -> fastvm-fedora39-23 ) + +Executing Cluster Transition: + * Resource action: coloc_primary stop on fastvm-fedora39-22 + * Resource action: coloc_primary start on fastvm-fedora39-23 + * Resource action: coloc_primary monitor=10000 on fastvm-fedora39-23 + +Revised Cluster Status: + * Node List: + * Online: [ fastvm-fedora39-22 fastvm-fedora39-23 ] + + * Full List of Resources: + * coloc_primary (ocf:pacemaker:Dummy): Started fastvm-fedora39-23 + * Clone Set: coloc_dependent-clone [coloc_dependent] (promotable): + * Promoted: [ fastvm-fedora39-23 ] + * Unpromoted: [ fastvm-fedora39-22 ] diff --git a/cts/scheduler/summary/coloc-optional-promoted-dependent-stays-2.summary b/cts/scheduler/summary/coloc-optional-promoted-dependent-stays-2.summary new file mode 100644 index 0000000000..1f1f2081e8 --- /dev/null +++ b/cts/scheduler/summary/coloc-optional-promoted-dependent-stays-2.summary @@ -0,0 +1,27 @@ +Current cluster status: + * Node List: + * Online: [ fastvm-fedora39-22 fastvm-fedora39-23 ] + + * Full List of Resources: + * coloc_primary (ocf:pacemaker:Dummy): Started fastvm-fedora39-22 + * Clone Set: coloc_dependent-clone [coloc_dependent] (promotable): + * Promoted: [ fastvm-fedora39-22 ] + * Unpromoted: [ fastvm-fedora39-23 ] + +Transition Summary: + * Move coloc_primary ( fastvm-fedora39-22 -> fastvm-fedora39-23 ) + +Executing Cluster Transition: + * Resource action: coloc_primary stop on fastvm-fedora39-22 + * Resource action: coloc_primary start on fastvm-fedora39-23 + * Resource action: coloc_primary monitor=10000 on fastvm-fedora39-23 + +Revised Cluster Status: + * Node List: + * Online: [ fastvm-fedora39-22 fastvm-fedora39-23 ] + + * Full List of Resources: + * coloc_primary (ocf:pacemaker:Dummy): Started fastvm-fedora39-23 + * Clone Set: coloc_dependent-clone [coloc_dependent] (promotable): + * Promoted: [ fastvm-fedora39-22 ] + * Unpromoted: [ fastvm-fedora39-23 ] diff --git a/cts/scheduler/xml/coloc-cloned-group-promoted-dependent1.xml b/cts/scheduler/xml/coloc-cloned-group-promoted-dependent1.xml new file mode 100644 index 0000000000..58df0980f9 --- /dev/null +++ b/cts/scheduler/xml/coloc-cloned-group-promoted-dependent1.xml @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cts/scheduler/xml/coloc-cloned-group-promoted-dependent2.xml b/cts/scheduler/xml/coloc-cloned-group-promoted-dependent2.xml new file mode 100644 index 0000000000..1fbfcbb903 --- /dev/null +++ b/cts/scheduler/xml/coloc-cloned-group-promoted-dependent2.xml @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cts/scheduler/xml/coloc-optional-promoted-dependent-moves-1.xml b/cts/scheduler/xml/coloc-optional-promoted-dependent-moves-1.xml new file mode 100644 index 0000000000..276b402657 --- /dev/null +++ b/cts/scheduler/xml/coloc-optional-promoted-dependent-moves-1.xml @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cts/scheduler/xml/coloc-optional-promoted-dependent-moves-2.xml b/cts/scheduler/xml/coloc-optional-promoted-dependent-moves-2.xml new file mode 100644 index 0000000000..c1e99e7bfa --- /dev/null +++ b/cts/scheduler/xml/coloc-optional-promoted-dependent-moves-2.xml @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cts/scheduler/xml/coloc-optional-promoted-dependent-stays-1.xml b/cts/scheduler/xml/coloc-optional-promoted-dependent-stays-1.xml new file mode 100644 index 0000000000..5c6ac3cd86 --- /dev/null +++ b/cts/scheduler/xml/coloc-optional-promoted-dependent-stays-1.xml @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cts/scheduler/xml/coloc-optional-promoted-dependent-stays-2.xml b/cts/scheduler/xml/coloc-optional-promoted-dependent-stays-2.xml new file mode 100644 index 0000000000..c3f0d1f6ab --- /dev/null +++ b/cts/scheduler/xml/coloc-optional-promoted-dependent-stays-2.xml @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/pacemaker/libpacemaker_private.h b/lib/pacemaker/libpacemaker_private.h index 4738e47609..3f8870b304 100644 --- a/lib/pacemaker/libpacemaker_private.h +++ b/lib/pacemaker/libpacemaker_private.h @@ -1,1181 +1,1183 @@ /* * Copyright 2021-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU Lesser General Public License * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. */ #ifndef PCMK__LIBPACEMAKER_PRIVATE__H # define PCMK__LIBPACEMAKER_PRIVATE__H /* This header is for the sole use of libpacemaker, so that functions can be * declared with G_GNUC_INTERNAL for efficiency. */ #include // lrmd_event_data_t #include // pcmk_action_t, pcmk_node_t, etc. #include // pcmk__location_t // Colocation flags enum pcmk__coloc_flags { pcmk__coloc_none = 0U, // Primary is affected even if already active pcmk__coloc_influence = (1U << 0), // Colocation was explicitly configured in CIB pcmk__coloc_explicit = (1U << 1), }; // Flags to modify the behavior of add_colocated_node_scores() enum pcmk__coloc_select { // With no other flags, apply all "with this" colocations pcmk__coloc_select_default = 0, // Apply "this with" colocations instead of "with this" colocations pcmk__coloc_select_this_with = (1 << 0), // Apply only colocations with non-negative scores pcmk__coloc_select_nonnegative = (1 << 1), // Apply only colocations with at least one matching node pcmk__coloc_select_active = (1 << 2), }; // Flags the update_ordered_actions() method can return enum pcmk__updated { pcmk__updated_none = 0, // Nothing changed pcmk__updated_first = (1 << 0), // First action was updated pcmk__updated_then = (1 << 1), // Then action was updated }; #define pcmk__set_updated_flags(au_flags, action, flags_to_set) do { \ au_flags = pcmk__set_flags_as(__func__, __LINE__, \ LOG_TRACE, "Action update", \ (action)->uuid, au_flags, \ (flags_to_set), #flags_to_set); \ } while (0) #define pcmk__clear_updated_flags(au_flags, action, flags_to_clear) do { \ au_flags = pcmk__clear_flags_as(__func__, __LINE__, \ LOG_TRACE, "Action update", \ (action)->uuid, au_flags, \ (flags_to_clear), #flags_to_clear); \ } while (0) // Resource assignment methods struct resource_alloc_functions_s { /*! * \internal * \brief Assign a resource to a node * * \param[in,out] rsc Resource to assign to a node * \param[in] prefer Node to prefer, if all else is equal * \param[in] stop_if_fail If \c true and \p rsc can't be assigned to a * node, set next role to stopped and update * existing actions (if \p rsc is not a * primitive, this applies to its primitive * descendants instead) * * \return Node that \p rsc is assigned to, if assigned entirely to one node * * \note If \p stop_if_fail is \c false, then \c pcmk__unassign_resource() * can completely undo the assignment. A successful assignment can be * either undone or left alone as final. A failed assignment has the * same effect as calling pcmk__unassign_resource(); there are no side * effects on roles or actions. */ pcmk_node_t *(*assign)(pcmk_resource_t *rsc, const pcmk_node_t *prefer, bool stop_if_fail); /*! * \internal * \brief Create all actions needed for a given resource * * \param[in,out] rsc Resource to create actions for */ void (*create_actions)(pcmk_resource_t *rsc); /*! * \internal * \brief Schedule any probes needed for a resource on a node * * \param[in,out] rsc Resource to create probe for * \param[in,out] node Node to create probe on * * \return true if any probe was created, otherwise false */ bool (*create_probe)(pcmk_resource_t *rsc, pcmk_node_t *node); /*! * \internal * \brief Create implicit constraints needed for a resource * * \param[in,out] rsc Resource to create implicit constraints for */ void (*internal_constraints)(pcmk_resource_t *rsc); /*! * \internal * \brief Apply a colocation's score to node scores or resource priority * * Given a colocation constraint, apply its score to the dependent's * allowed node scores (if we are still placing resources) or priority (if * we are choosing promotable clone instance roles). * * \param[in,out] dependent Dependent resource in colocation * \param[in] primary Primary resource in colocation * \param[in] colocation Colocation constraint to apply * \param[in] for_dependent true if called on behalf of dependent + * + * \return The score added to the dependent's priority */ - void (*apply_coloc_score)(pcmk_resource_t *dependent, - const pcmk_resource_t *primary, - const pcmk__colocation_t *colocation, - bool for_dependent); + int (*apply_coloc_score)(pcmk_resource_t *dependent, + const pcmk_resource_t *primary, + const pcmk__colocation_t *colocation, + bool for_dependent); /*! * \internal * \brief Create list of all resources in colocations with a given resource * * Given a resource, create a list of all resources involved in mandatory * colocations with it, whether directly or via chained colocations. * * \param[in] rsc Resource to add to colocated list * \param[in] orig_rsc Resource originally requested * \param[in,out] colocated_rscs Existing list * * \return List of given resource and all resources involved in colocations * * \note This function is recursive; top-level callers should pass NULL as * \p colocated_rscs and \p orig_rsc, and the desired resource as * \p rsc. The recursive calls will use other values. */ GList *(*colocated_resources)(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList *colocated_rscs); /*! * \internal * \brief Add colocations affecting a resource as primary to a list * * Given a resource being assigned (\p orig_rsc) and a resource somewhere in * its chain of ancestors (\p rsc, which may be \p orig_rsc), get * colocations that affect the ancestor as primary and should affect the * resource, and add them to a given list. * * \param[in] rsc Resource whose colocations should be added * \param[in] orig_rsc Affected resource (\p rsc or a descendant) * \param[in,out] list List of colocations to add to * * \note All arguments should be non-NULL. * \note The pcmk__with_this_colocations() wrapper should usually be used * instead of using this method directly. */ void (*with_this_colocations)(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList **list); /*! * \internal * \brief Add colocations affecting a resource as dependent to a list * * Given a resource being assigned (\p orig_rsc) and a resource somewhere in * its chain of ancestors (\p rsc, which may be \p orig_rsc), get * colocations that affect the ancestor as dependent and should affect the * resource, and add them to a given list. * * * \param[in] rsc Resource whose colocations should be added * \param[in] orig_rsc Affected resource (\p rsc or a descendant) * \param[in,out] list List of colocations to add to * * \note All arguments should be non-NULL. * \note The pcmk__this_with_colocations() wrapper should usually be used * instead of using this method directly. */ void (*this_with_colocations)(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList **list); /*! * \internal * \brief Update nodes with scores of colocated resources' nodes * * Given a table of nodes and a resource, update the nodes' scores with the * scores of the best nodes matching the attribute used for each of the * resource's relevant colocations. * * \param[in,out] source_rsc Resource whose node scores to add * \param[in] target_rsc Resource on whose behalf to update \p *nodes * \param[in] log_id Resource ID for logs (if \c NULL, use * \p source_rsc ID) * \param[in,out] nodes Nodes to update (set initial contents to * \c NULL to copy allowed nodes from * \p source_rsc) * \param[in] colocation Original colocation constraint (used to get * configured primary resource's stickiness, and * to get colocation node attribute; if \c NULL, * source_rsc's own matching node scores * will not be added, and \p *nodes must be * \c NULL as well) * \param[in] factor Incorporate scores multiplied by this factor * \param[in] flags Bitmask of enum pcmk__coloc_select values * * \note \c NULL \p target_rsc, \c NULL \p *nodes, \c NULL \p colocation, * and the \c pcmk__coloc_select_this_with flag are used together (and * only by \c cmp_resources()). * \note The caller remains responsible for freeing \p *nodes. */ void (*add_colocated_node_scores)(pcmk_resource_t *source_rsc, const pcmk_resource_t *target_rsc, const char *log_id, GHashTable **nodes, const pcmk__colocation_t *colocation, float factor, uint32_t flags); /*! * \internal * \brief Apply a location constraint to a resource's allowed node scores * * \param[in,out] rsc Resource to apply constraint to * \param[in,out] location Location constraint to apply */ void (*apply_location)(pcmk_resource_t *rsc, pcmk__location_t *location); /*! * \internal * \brief Return action flags for a given resource action * * \param[in,out] action Action to get flags for * \param[in] node If not NULL, limit effects to this node * * \return Flags appropriate to \p action on \p node * \note For primitives, this will be the same as action->flags regardless * of node. For collective resources, the flags can differ due to * multiple instances possibly being involved. */ uint32_t (*action_flags)(pcmk_action_t *action, const pcmk_node_t *node); /*! * \internal * \brief Update two actions according to an ordering between them * * Given information about an ordering of two actions, update the actions' * flags (and runnable_before members if appropriate) as appropriate for the * ordering. Effects may cascade to other orderings involving the actions as * well. * * \param[in,out] first 'First' action in an ordering * \param[in,out] then 'Then' action in an ordering * \param[in] node If not NULL, limit scope of ordering to this * node (only used when interleaving instances) * \param[in] flags Action flags for \p first for ordering purposes * \param[in] filter Action flags to limit scope of certain updates * (may include pcmk_action_optional to affect * only mandatory actions and pcmk_action_runnable * to affect only runnable actions) * \param[in] type Group of enum pcmk__action_relation_flags * \param[in,out] scheduler Scheduler data * * \return Group of enum pcmk__updated flags indicating what was updated */ uint32_t (*update_ordered_actions)(pcmk_action_t *first, pcmk_action_t *then, const pcmk_node_t *node, uint32_t flags, uint32_t filter, uint32_t type, pcmk_scheduler_t *scheduler); /*! * \internal * \brief Output a summary of scheduled actions for a resource * * \param[in,out] rsc Resource to output actions for */ void (*output_actions)(pcmk_resource_t *rsc); /*! * \internal * \brief Add a resource's actions to the transition graph * * \param[in,out] rsc Resource whose actions should be added */ void (*add_actions_to_graph)(pcmk_resource_t *rsc); /*! * \internal * \brief Add meta-attributes relevant to transition graph actions to XML * * If a given resource supports variant-specific meta-attributes that are * needed for transition graph actions, add them to a given XML element. * * \param[in] rsc Resource whose meta-attributes should be added * \param[in,out] xml Transition graph action attributes XML to add to */ void (*add_graph_meta)(const pcmk_resource_t *rsc, xmlNode *xml); /*! * \internal * \brief Add a resource's utilization to a table of utilization values * * This function is used when summing the utilization of a resource and all * resources colocated with it, to determine whether a node has sufficient * capacity. Given a resource and a table of utilization values, it will add * the resource's utilization to the existing values, if the resource has * not yet been assigned to a node. * * \param[in] rsc Resource with utilization to add * \param[in] orig_rsc Resource being assigned (for logging only) * \param[in] all_rscs List of all resources that will be summed * \param[in,out] utilization Table of utilization values to add to */ void (*add_utilization)(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList *all_rscs, GHashTable *utilization); /*! * \internal * \brief Apply a shutdown lock for a resource, if appropriate * * \param[in,out] rsc Resource to check for shutdown lock */ void (*shutdown_lock)(pcmk_resource_t *rsc); }; // Actions (pcmk_sched_actions.c) G_GNUC_INTERNAL void pcmk__update_action_for_orderings(pcmk_action_t *action, pcmk_scheduler_t *scheduler); G_GNUC_INTERNAL uint32_t pcmk__update_ordered_actions(pcmk_action_t *first, pcmk_action_t *then, const pcmk_node_t *node, uint32_t flags, uint32_t filter, uint32_t type, pcmk_scheduler_t *scheduler); G_GNUC_INTERNAL void pcmk__log_action(const char *pre_text, const pcmk_action_t *action, bool details); G_GNUC_INTERNAL pcmk_action_t *pcmk__new_cancel_action(pcmk_resource_t *rsc, const char *name, guint interval_ms, const pcmk_node_t *node); G_GNUC_INTERNAL pcmk_action_t *pcmk__new_shutdown_action(pcmk_node_t *node); G_GNUC_INTERNAL bool pcmk__action_locks_rsc_to_node(const pcmk_action_t *action); G_GNUC_INTERNAL void pcmk__deduplicate_action_inputs(pcmk_action_t *action); G_GNUC_INTERNAL void pcmk__output_actions(pcmk_scheduler_t *scheduler); G_GNUC_INTERNAL bool pcmk__check_action_config(pcmk_resource_t *rsc, pcmk_node_t *node, const xmlNode *xml_op); G_GNUC_INTERNAL void pcmk__handle_rsc_config_changes(pcmk_scheduler_t *scheduler); // Recurring actions (pcmk_sched_recurring.c) G_GNUC_INTERNAL void pcmk__create_recurring_actions(pcmk_resource_t *rsc); G_GNUC_INTERNAL void pcmk__schedule_cancel(pcmk_resource_t *rsc, const char *call_id, const char *task, guint interval_ms, const pcmk_node_t *node, const char *reason); G_GNUC_INTERNAL void pcmk__reschedule_recurring(pcmk_resource_t *rsc, const char *task, guint interval_ms, pcmk_node_t *node); G_GNUC_INTERNAL bool pcmk__action_is_recurring(const pcmk_action_t *action); // Producing transition graphs (pcmk_graph_producer.c) G_GNUC_INTERNAL bool pcmk__graph_has_loop(const pcmk_action_t *init_action, const pcmk_action_t *action, pcmk__related_action_t *input); G_GNUC_INTERNAL void pcmk__add_rsc_actions_to_graph(pcmk_resource_t *rsc); G_GNUC_INTERNAL void pcmk__create_graph(pcmk_scheduler_t *scheduler); // Fencing (pcmk_sched_fencing.c) G_GNUC_INTERNAL void pcmk__order_vs_fence(pcmk_action_t *stonith_op, pcmk_scheduler_t *scheduler); G_GNUC_INTERNAL void pcmk__order_vs_unfence(const pcmk_resource_t *rsc, pcmk_node_t *node, pcmk_action_t *action, enum pcmk__action_relation_flags order); G_GNUC_INTERNAL void pcmk__fence_guest(pcmk_node_t *node); G_GNUC_INTERNAL bool pcmk__node_unfenced(const pcmk_node_t *node); G_GNUC_INTERNAL void pcmk__order_restart_vs_unfence(gpointer data, gpointer user_data); // Injected scheduler inputs (pcmk_sched_injections.c) void pcmk__inject_scheduler_input(pcmk_scheduler_t *scheduler, cib_t *cib, const pcmk_injections_t *injections); // Constraints of any type (pcmk_sched_constraints.c) G_GNUC_INTERNAL pcmk_resource_t *pcmk__find_constraint_resource(GList *rsc_list, const char *id); G_GNUC_INTERNAL xmlNode *pcmk__expand_tags_in_sets(xmlNode *xml_obj, const pcmk_scheduler_t *scheduler); G_GNUC_INTERNAL bool pcmk__valid_resource_or_tag(const pcmk_scheduler_t *scheduler, const char *id, pcmk_resource_t **rsc, pcmk_tag_t **tag); G_GNUC_INTERNAL bool pcmk__tag_to_set(xmlNode *xml_obj, xmlNode **rsc_set, const char *attr, bool convert_rsc, const pcmk_scheduler_t *scheduler); G_GNUC_INTERNAL void pcmk__create_internal_constraints(pcmk_scheduler_t *scheduler); // Location constraints G_GNUC_INTERNAL void pcmk__unpack_location(xmlNode *xml_obj, pcmk_scheduler_t *scheduler); G_GNUC_INTERNAL pcmk__location_t *pcmk__new_location(const char *id, pcmk_resource_t *rsc, int node_score, const char *discover_mode, pcmk_node_t *foo_node); G_GNUC_INTERNAL void pcmk__apply_locations(pcmk_scheduler_t *scheduler); G_GNUC_INTERNAL void pcmk__apply_location(pcmk_resource_t *rsc, pcmk__location_t *constraint); // Colocation constraints (pcmk_sched_colocation.c) enum pcmk__coloc_affects { pcmk__coloc_affects_nothing = 0, pcmk__coloc_affects_location, pcmk__coloc_affects_role, }; /*! * \internal * \brief Get the value of a colocation's node attribute * * \param[in] node Node on which to look up the attribute * \param[in] attr Name of attribute to look up * \param[in] rsc Resource on whose behalf to look up the attribute * * \return Value of \p attr on \p node or on the host of \p node, as appropriate */ static inline const char * pcmk__colocation_node_attr(const pcmk_node_t *node, const char *attr, const pcmk_resource_t *rsc) { const char *target = NULL; /* A resource colocated with a bundle or its primitive can't run on the * bundle node itself (where only the primitive, if any, can run). Instead, * we treat it as a colocation with the bundle's containers, so always look * up colocation node attributes on the container host. */ if (pcmk__is_bundle_node(node) && pcmk__is_bundled(rsc) && (pe__const_top_resource(rsc, false) == pe__bundled_resource(rsc))) { target = PCMK_VALUE_HOST; } else if (rsc != NULL) { target = g_hash_table_lookup(rsc->meta, PCMK_META_CONTAINER_ATTRIBUTE_TARGET); } return pcmk__node_attr(node, attr, target, pcmk__rsc_node_assigned); } G_GNUC_INTERNAL enum pcmk__coloc_affects pcmk__colocation_affects(const pcmk_resource_t *dependent, const pcmk_resource_t *primary, const pcmk__colocation_t *colocation, bool preview); G_GNUC_INTERNAL void pcmk__apply_coloc_to_scores(pcmk_resource_t *dependent, const pcmk_resource_t *primary, const pcmk__colocation_t *colocation); G_GNUC_INTERNAL -void pcmk__apply_coloc_to_priority(pcmk_resource_t *dependent, - const pcmk_resource_t *primary, - const pcmk__colocation_t *colocation); +int pcmk__apply_coloc_to_priority(pcmk_resource_t *dependent, + const pcmk_resource_t *primary, + const pcmk__colocation_t *colocation); G_GNUC_INTERNAL void pcmk__add_colocated_node_scores(pcmk_resource_t *source_rsc, const pcmk_resource_t *target_rsc, const char *log_id, GHashTable **nodes, const pcmk__colocation_t *colocation, float factor, uint32_t flags); G_GNUC_INTERNAL void pcmk__add_dependent_scores(gpointer data, gpointer user_data); G_GNUC_INTERNAL void pcmk__colocation_intersect_nodes(pcmk_resource_t *dependent, const pcmk_resource_t *primary, const pcmk__colocation_t *colocation, const GList *primary_nodes, bool merge_scores); G_GNUC_INTERNAL void pcmk__unpack_colocation(xmlNode *xml_obj, pcmk_scheduler_t *scheduler); G_GNUC_INTERNAL void pcmk__add_this_with(GList **list, const pcmk__colocation_t *colocation, const pcmk_resource_t *rsc); G_GNUC_INTERNAL void pcmk__add_this_with_list(GList **list, GList *addition, const pcmk_resource_t *rsc); G_GNUC_INTERNAL void pcmk__add_with_this(GList **list, const pcmk__colocation_t *colocation, const pcmk_resource_t *rsc); G_GNUC_INTERNAL void pcmk__add_with_this_list(GList **list, GList *addition, const pcmk_resource_t *rsc); G_GNUC_INTERNAL GList *pcmk__with_this_colocations(const pcmk_resource_t *rsc); G_GNUC_INTERNAL GList *pcmk__this_with_colocations(const pcmk_resource_t *rsc); G_GNUC_INTERNAL void pcmk__new_colocation(const char *id, const char *node_attr, int score, pcmk_resource_t *dependent, pcmk_resource_t *primary, const char *dependent_role, const char *primary_role, uint32_t flags); G_GNUC_INTERNAL void pcmk__block_colocation_dependents(pcmk_action_t *action); /*! * \internal * \brief Check whether colocation's dependent preferences should be considered * * \param[in] colocation Colocation constraint * \param[in] rsc Primary instance (normally this will be * colocation->primary, which NULL will be treated as, * but for clones or bundles with multiple instances * this can be a particular instance) * * \return true if colocation influence should be effective, otherwise false */ static inline bool pcmk__colocation_has_influence(const pcmk__colocation_t *colocation, const pcmk_resource_t *rsc) { if (rsc == NULL) { rsc = colocation->primary; } /* A bundle replica colocates its remote connection with its container, * using a finite score so that the container can run on Pacemaker Remote * nodes. * * Moving a connection is lightweight and does not interrupt the service, * while moving a container is heavyweight and does interrupt the service, * so don't move a clean, active container based solely on the preferences * of its connection. * * This also avoids problematic scenarios where two containers want to * perpetually swap places. */ if (pcmk_is_set(colocation->dependent->flags, pcmk_rsc_remote_nesting_allowed) && !pcmk_is_set(rsc->flags, pcmk_rsc_failed) && pcmk__list_of_1(rsc->running_on)) { return false; } /* The dependent in a colocation influences the primary's location * if the PCMK_XA_INFLUENCE option is true or the primary is not yet active. */ return pcmk_is_set(colocation->flags, pcmk__coloc_influence) || (rsc->running_on == NULL); } // Ordering constraints (pcmk_sched_ordering.c) G_GNUC_INTERNAL void pcmk__new_ordering(pcmk_resource_t *first_rsc, char *first_task, pcmk_action_t *first_action, pcmk_resource_t *then_rsc, char *then_task, pcmk_action_t *then_action, uint32_t flags, pcmk_scheduler_t *sched); G_GNUC_INTERNAL void pcmk__unpack_ordering(xmlNode *xml_obj, pcmk_scheduler_t *scheduler); G_GNUC_INTERNAL void pcmk__disable_invalid_orderings(pcmk_scheduler_t *scheduler); G_GNUC_INTERNAL void pcmk__order_stops_before_shutdown(pcmk_node_t *node, pcmk_action_t *shutdown_op); G_GNUC_INTERNAL void pcmk__apply_orderings(pcmk_scheduler_t *sched); G_GNUC_INTERNAL void pcmk__order_after_each(pcmk_action_t *after, GList *list); /*! * \internal * \brief Create a new ordering between two resource actions * * \param[in,out] first_rsc Resource for 'first' action * \param[in,out] first_task Action key for 'first' action * \param[in] then_rsc Resource for 'then' action * \param[in,out] then_task Action key for 'then' action * \param[in] flags Group of enum pcmk__action_relation_flags */ #define pcmk__order_resource_actions(first_rsc, first_task, \ then_rsc, then_task, flags) \ pcmk__new_ordering((first_rsc), \ pcmk__op_key((first_rsc)->id, (first_task), 0), \ NULL, \ (then_rsc), \ pcmk__op_key((then_rsc)->id, (then_task), 0), \ NULL, (flags), (first_rsc)->cluster) #define pcmk__order_starts(rsc1, rsc2, flags) \ pcmk__order_resource_actions((rsc1), PCMK_ACTION_START, \ (rsc2), PCMK_ACTION_START, (flags)) #define pcmk__order_stops(rsc1, rsc2, flags) \ pcmk__order_resource_actions((rsc1), PCMK_ACTION_STOP, \ (rsc2), PCMK_ACTION_STOP, (flags)) // Ticket constraints (pcmk_sched_tickets.c) G_GNUC_INTERNAL void pcmk__unpack_rsc_ticket(xmlNode *xml_obj, pcmk_scheduler_t *scheduler); // Promotable clone resources (pcmk_sched_promotable.c) G_GNUC_INTERNAL void pcmk__add_promotion_scores(pcmk_resource_t *rsc); G_GNUC_INTERNAL void pcmk__require_promotion_tickets(pcmk_resource_t *rsc); G_GNUC_INTERNAL void pcmk__set_instance_roles(pcmk_resource_t *rsc); G_GNUC_INTERNAL void pcmk__create_promotable_actions(pcmk_resource_t *clone); G_GNUC_INTERNAL void pcmk__promotable_restart_ordering(pcmk_resource_t *rsc); G_GNUC_INTERNAL void pcmk__order_promotable_instances(pcmk_resource_t *clone); G_GNUC_INTERNAL void pcmk__update_dependent_with_promotable(const pcmk_resource_t *primary, pcmk_resource_t *dependent, const pcmk__colocation_t *colocation); G_GNUC_INTERNAL -void pcmk__update_promotable_dependent_priority(const pcmk_resource_t *primary, - pcmk_resource_t *dependent, - const pcmk__colocation_t - *colocation); +int pcmk__update_promotable_dependent_priority(const pcmk_resource_t *primary, + pcmk_resource_t *dependent, + const pcmk__colocation_t + *colocation); // Pacemaker Remote nodes (pcmk_sched_remote.c) G_GNUC_INTERNAL bool pcmk__is_failed_remote_node(const pcmk_node_t *node); G_GNUC_INTERNAL void pcmk__order_remote_connection_actions(pcmk_scheduler_t *scheduler); G_GNUC_INTERNAL bool pcmk__rsc_corresponds_to_guest(const pcmk_resource_t *rsc, const pcmk_node_t *node); G_GNUC_INTERNAL pcmk_node_t *pcmk__connection_host_for_action(const pcmk_action_t *action); G_GNUC_INTERNAL void pcmk__substitute_remote_addr(pcmk_resource_t *rsc, GHashTable *params); G_GNUC_INTERNAL void pcmk__add_guest_meta_to_xml(xmlNode *args_xml, const pcmk_action_t *action); // Primitives (pcmk_sched_primitive.c) G_GNUC_INTERNAL pcmk_node_t *pcmk__primitive_assign(pcmk_resource_t *rsc, const pcmk_node_t *prefer, bool stop_if_fail); G_GNUC_INTERNAL void pcmk__primitive_create_actions(pcmk_resource_t *rsc); G_GNUC_INTERNAL void pcmk__primitive_internal_constraints(pcmk_resource_t *rsc); G_GNUC_INTERNAL uint32_t pcmk__primitive_action_flags(pcmk_action_t *action, const pcmk_node_t *node); G_GNUC_INTERNAL -void pcmk__primitive_apply_coloc_score(pcmk_resource_t *dependent, - const pcmk_resource_t *primary, - const pcmk__colocation_t *colocation, - bool for_dependent); +int pcmk__primitive_apply_coloc_score(pcmk_resource_t *dependent, + const pcmk_resource_t *primary, + const pcmk__colocation_t *colocation, + bool for_dependent); G_GNUC_INTERNAL void pcmk__with_primitive_colocations(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList **list); G_GNUC_INTERNAL void pcmk__primitive_with_colocations(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList **list); G_GNUC_INTERNAL void pcmk__schedule_cleanup(pcmk_resource_t *rsc, const pcmk_node_t *node, bool optional); G_GNUC_INTERNAL void pcmk__primitive_add_graph_meta(const pcmk_resource_t *rsc, xmlNode *xml); G_GNUC_INTERNAL void pcmk__primitive_add_utilization(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList *all_rscs, GHashTable *utilization); G_GNUC_INTERNAL void pcmk__primitive_shutdown_lock(pcmk_resource_t *rsc); // Groups (pcmk_sched_group.c) G_GNUC_INTERNAL pcmk_node_t *pcmk__group_assign(pcmk_resource_t *rsc, const pcmk_node_t *prefer, bool stop_if_fail); G_GNUC_INTERNAL void pcmk__group_create_actions(pcmk_resource_t *rsc); G_GNUC_INTERNAL void pcmk__group_internal_constraints(pcmk_resource_t *rsc); G_GNUC_INTERNAL -void pcmk__group_apply_coloc_score(pcmk_resource_t *dependent, - const pcmk_resource_t *primary, - const pcmk__colocation_t *colocation, - bool for_dependent); +int pcmk__group_apply_coloc_score(pcmk_resource_t *dependent, + const pcmk_resource_t *primary, + const pcmk__colocation_t *colocation, + bool for_dependent); G_GNUC_INTERNAL void pcmk__with_group_colocations(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList **list); G_GNUC_INTERNAL void pcmk__group_with_colocations(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList **list); G_GNUC_INTERNAL void pcmk__group_add_colocated_node_scores(pcmk_resource_t *source_rsc, const pcmk_resource_t *target_rsc, const char *log_id, GHashTable **nodes, const pcmk__colocation_t *colocation, float factor, uint32_t flags); G_GNUC_INTERNAL void pcmk__group_apply_location(pcmk_resource_t *rsc, pcmk__location_t *location); G_GNUC_INTERNAL uint32_t pcmk__group_action_flags(pcmk_action_t *action, const pcmk_node_t *node); G_GNUC_INTERNAL uint32_t pcmk__group_update_ordered_actions(pcmk_action_t *first, pcmk_action_t *then, const pcmk_node_t *node, uint32_t flags, uint32_t filter, uint32_t type, pcmk_scheduler_t *scheduler); G_GNUC_INTERNAL GList *pcmk__group_colocated_resources(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList *colocated_rscs); G_GNUC_INTERNAL void pcmk__group_add_utilization(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList *all_rscs, GHashTable *utilization); G_GNUC_INTERNAL void pcmk__group_shutdown_lock(pcmk_resource_t *rsc); // Clones (pcmk_sched_clone.c) G_GNUC_INTERNAL pcmk_node_t *pcmk__clone_assign(pcmk_resource_t *rsc, const pcmk_node_t *prefer, bool stop_if_fail); G_GNUC_INTERNAL void pcmk__clone_create_actions(pcmk_resource_t *rsc); G_GNUC_INTERNAL bool pcmk__clone_create_probe(pcmk_resource_t *rsc, pcmk_node_t *node); G_GNUC_INTERNAL void pcmk__clone_internal_constraints(pcmk_resource_t *rsc); G_GNUC_INTERNAL -void pcmk__clone_apply_coloc_score(pcmk_resource_t *dependent, - const pcmk_resource_t *primary, - const pcmk__colocation_t *colocation, - bool for_dependent); +int pcmk__clone_apply_coloc_score(pcmk_resource_t *dependent, + const pcmk_resource_t *primary, + const pcmk__colocation_t *colocation, + bool for_dependent); G_GNUC_INTERNAL void pcmk__with_clone_colocations(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList **list); G_GNUC_INTERNAL void pcmk__clone_with_colocations(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList **list); G_GNUC_INTERNAL void pcmk__clone_apply_location(pcmk_resource_t *rsc, pcmk__location_t *constraint); G_GNUC_INTERNAL uint32_t pcmk__clone_action_flags(pcmk_action_t *action, const pcmk_node_t *node); G_GNUC_INTERNAL void pcmk__clone_add_actions_to_graph(pcmk_resource_t *rsc); G_GNUC_INTERNAL void pcmk__clone_add_graph_meta(const pcmk_resource_t *rsc, xmlNode *xml); G_GNUC_INTERNAL void pcmk__clone_add_utilization(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList *all_rscs, GHashTable *utilization); G_GNUC_INTERNAL void pcmk__clone_shutdown_lock(pcmk_resource_t *rsc); // Bundles (pcmk_sched_bundle.c) G_GNUC_INTERNAL pcmk_node_t *pcmk__bundle_assign(pcmk_resource_t *rsc, const pcmk_node_t *prefer, bool stop_if_fail); G_GNUC_INTERNAL void pcmk__bundle_create_actions(pcmk_resource_t *rsc); G_GNUC_INTERNAL bool pcmk__bundle_create_probe(pcmk_resource_t *rsc, pcmk_node_t *node); G_GNUC_INTERNAL void pcmk__bundle_internal_constraints(pcmk_resource_t *rsc); G_GNUC_INTERNAL -void pcmk__bundle_apply_coloc_score(pcmk_resource_t *dependent, - const pcmk_resource_t *primary, - const pcmk__colocation_t *colocation, - bool for_dependent); +int pcmk__bundle_apply_coloc_score(pcmk_resource_t *dependent, + const pcmk_resource_t *primary, + const pcmk__colocation_t *colocation, + bool for_dependent); G_GNUC_INTERNAL void pcmk__with_bundle_colocations(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList **list); G_GNUC_INTERNAL void pcmk__bundle_with_colocations(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList **list); G_GNUC_INTERNAL void pcmk__bundle_apply_location(pcmk_resource_t *rsc, pcmk__location_t *constraint); G_GNUC_INTERNAL uint32_t pcmk__bundle_action_flags(pcmk_action_t *action, const pcmk_node_t *node); G_GNUC_INTERNAL void pcmk__output_bundle_actions(pcmk_resource_t *rsc); G_GNUC_INTERNAL void pcmk__bundle_add_actions_to_graph(pcmk_resource_t *rsc); G_GNUC_INTERNAL void pcmk__bundle_add_utilization(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList *all_rscs, GHashTable *utilization); G_GNUC_INTERNAL void pcmk__bundle_shutdown_lock(pcmk_resource_t *rsc); // Clone instances or bundle replica containers (pcmk_sched_instances.c) G_GNUC_INTERNAL void pcmk__assign_instances(pcmk_resource_t *collective, GList *instances, int max_total, int max_per_node); G_GNUC_INTERNAL void pcmk__create_instance_actions(pcmk_resource_t *rsc, GList *instances); G_GNUC_INTERNAL bool pcmk__instance_matches(const pcmk_resource_t *instance, const pcmk_node_t *node, enum rsc_role_e role, bool current); G_GNUC_INTERNAL pcmk_resource_t *pcmk__find_compatible_instance(const pcmk_resource_t *match_rsc, const pcmk_resource_t *rsc, enum rsc_role_e role, bool current); G_GNUC_INTERNAL uint32_t pcmk__instance_update_ordered_actions(pcmk_action_t *first, pcmk_action_t *then, const pcmk_node_t *node, uint32_t flags, uint32_t filter, uint32_t type, pcmk_scheduler_t *scheduler); G_GNUC_INTERNAL uint32_t pcmk__collective_action_flags(pcmk_action_t *action, const GList *instances, const pcmk_node_t *node); // Injections (pcmk_injections.c) G_GNUC_INTERNAL xmlNode *pcmk__inject_node(cib_t *cib_conn, const char *node, const char *uuid); G_GNUC_INTERNAL xmlNode *pcmk__inject_node_state_change(cib_t *cib_conn, const char *node, bool up); G_GNUC_INTERNAL xmlNode *pcmk__inject_resource_history(pcmk__output_t *out, xmlNode *cib_node, const char *resource, const char *lrm_name, const char *rclass, const char *rtype, const char *rprovider); G_GNUC_INTERNAL void pcmk__inject_failcount(pcmk__output_t *out, cib_t *cib_conn, xmlNode *cib_node, const char *resource, const char *task, guint interval_ms, int rc); G_GNUC_INTERNAL xmlNode *pcmk__inject_action_result(xmlNode *cib_resource, lrmd_event_data_t *op, int target_rc); // Nodes (pcmk_sched_nodes.c) G_GNUC_INTERNAL bool pcmk__node_available(const pcmk_node_t *node, bool consider_score, bool consider_guest); G_GNUC_INTERNAL bool pcmk__any_node_available(GHashTable *nodes); G_GNUC_INTERNAL GHashTable *pcmk__copy_node_table(GHashTable *nodes); G_GNUC_INTERNAL void pcmk__copy_node_tables(const pcmk_resource_t *rsc, GHashTable **copy); G_GNUC_INTERNAL void pcmk__restore_node_tables(pcmk_resource_t *rsc, GHashTable *backup); G_GNUC_INTERNAL GList *pcmk__sort_nodes(GList *nodes, pcmk_node_t *active_node); G_GNUC_INTERNAL void pcmk__apply_node_health(pcmk_scheduler_t *scheduler); G_GNUC_INTERNAL pcmk_node_t *pcmk__top_allowed_node(const pcmk_resource_t *rsc, const pcmk_node_t *node); // Functions applying to more than one variant (pcmk_sched_resource.c) G_GNUC_INTERNAL void pcmk__set_assignment_methods(pcmk_scheduler_t *scheduler); G_GNUC_INTERNAL bool pcmk__rsc_agent_changed(pcmk_resource_t *rsc, pcmk_node_t *node, const xmlNode *rsc_entry, bool active_on_node); G_GNUC_INTERNAL GList *pcmk__rscs_matching_id(const char *id, const pcmk_scheduler_t *scheduler); G_GNUC_INTERNAL GList *pcmk__colocated_resources(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList *colocated_rscs); G_GNUC_INTERNAL void pcmk__noop_add_graph_meta(const pcmk_resource_t *rsc, xmlNode *xml); G_GNUC_INTERNAL void pcmk__output_resource_actions(pcmk_resource_t *rsc); G_GNUC_INTERNAL bool pcmk__assign_resource(pcmk_resource_t *rsc, pcmk_node_t *node, bool force, bool stop_if_fail); G_GNUC_INTERNAL void pcmk__unassign_resource(pcmk_resource_t *rsc); G_GNUC_INTERNAL bool pcmk__threshold_reached(pcmk_resource_t *rsc, const pcmk_node_t *node, pcmk_resource_t **failed); G_GNUC_INTERNAL void pcmk__sort_resources(pcmk_scheduler_t *scheduler); G_GNUC_INTERNAL gint pcmk__cmp_instance(gconstpointer a, gconstpointer b); G_GNUC_INTERNAL gint pcmk__cmp_instance_number(gconstpointer a, gconstpointer b); // Functions related to probes (pcmk_sched_probes.c) G_GNUC_INTERNAL bool pcmk__probe_rsc_on_node(pcmk_resource_t *rsc, pcmk_node_t *node); G_GNUC_INTERNAL void pcmk__order_probes(pcmk_scheduler_t *scheduler); G_GNUC_INTERNAL bool pcmk__probe_resource_list(GList *rscs, pcmk_node_t *node); G_GNUC_INTERNAL void pcmk__schedule_probes(pcmk_scheduler_t *scheduler); // Functions related to live migration (pcmk_sched_migration.c) void pcmk__create_migration_actions(pcmk_resource_t *rsc, const pcmk_node_t *current); void pcmk__abort_dangling_migration(void *data, void *user_data); bool pcmk__rsc_can_migrate(const pcmk_resource_t *rsc, const pcmk_node_t *current); void pcmk__order_migration_equivalents(pcmk__action_relation_t *order); // Functions related to node utilization (pcmk_sched_utilization.c) G_GNUC_INTERNAL int pcmk__compare_node_capacities(const pcmk_node_t *node1, const pcmk_node_t *node2); G_GNUC_INTERNAL void pcmk__consume_node_capacity(GHashTable *current_utilization, const pcmk_resource_t *rsc); G_GNUC_INTERNAL void pcmk__release_node_capacity(GHashTable *current_utilization, const pcmk_resource_t *rsc); G_GNUC_INTERNAL const pcmk_node_t *pcmk__ban_insufficient_capacity(pcmk_resource_t *rsc); G_GNUC_INTERNAL void pcmk__create_utilization_constraints(pcmk_resource_t *rsc, const GList *allowed_nodes); G_GNUC_INTERNAL void pcmk__show_node_capacities(const char *desc, pcmk_scheduler_t *scheduler); // Functions related to the scheduler (pcmk_scheduler.c) G_GNUC_INTERNAL int pcmk__init_scheduler(pcmk__output_t *out, xmlNodePtr input, const crm_time_t *date, pcmk_scheduler_t **scheduler); // General setup functions (pcmk_setup.c) G_GNUC_INTERNAL int pcmk__setup_output_cib_sched(pcmk__output_t **out, cib_t **cib, pcmk_scheduler_t **scheduler, xmlNode **xml); #endif // PCMK__LIBPACEMAKER_PRIVATE__H diff --git a/lib/pacemaker/pcmk_sched_bundle.c b/lib/pacemaker/pcmk_sched_bundle.c index 167f519bd3..48a7eb63e1 100644 --- a/lib/pacemaker/pcmk_sched_bundle.c +++ b/lib/pacemaker/pcmk_sched_bundle.c @@ -1,1051 +1,1062 @@ /* * Copyright 2004-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU General Public License version 2 * or later (GPLv2+) WITHOUT ANY WARRANTY. */ #include #include #include #include #include "libpacemaker_private.h" struct assign_data { const pcmk_node_t *prefer; bool stop_if_fail; }; /*! * \internal * \brief Assign a single bundle replica's resources (other than container) * * \param[in,out] replica Replica to assign * \param[in] user_data Preferred node, if any * * \return true (to indicate that any further replicas should be processed) */ static bool assign_replica(pcmk__bundle_replica_t *replica, void *user_data) { pcmk_node_t *container_host = NULL; struct assign_data *assign_data = user_data; const pcmk_node_t *prefer = assign_data->prefer; bool stop_if_fail = assign_data->stop_if_fail; const pcmk_resource_t *bundle = pe__const_top_resource(replica->container, true); if (replica->ip != NULL) { pcmk__rsc_trace(bundle, "Assigning bundle %s IP %s", bundle->id, replica->ip->id); replica->ip->cmds->assign(replica->ip, prefer, stop_if_fail); } container_host = replica->container->allocated_to; if (replica->remote != NULL) { if (pcmk__is_pacemaker_remote_node(container_host)) { /* REMOTE_CONTAINER_HACK: "Nested" connection resources must be on * the same host because Pacemaker Remote only supports a single * active connection. */ pcmk__new_colocation("#replica-remote-with-host-remote", NULL, PCMK_SCORE_INFINITY, replica->remote, container_host->details->remote_rsc, NULL, NULL, pcmk__coloc_influence); } pcmk__rsc_trace(bundle, "Assigning bundle %s connection %s", bundle->id, replica->remote->id); replica->remote->cmds->assign(replica->remote, prefer, stop_if_fail); } if (replica->child != NULL) { pcmk_node_t *node = NULL; GHashTableIter iter; g_hash_table_iter_init(&iter, replica->child->allowed_nodes); while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &node)) { if (!pcmk__same_node(node, replica->node)) { node->weight = -PCMK_SCORE_INFINITY; } else if (!pcmk__threshold_reached(replica->child, node, NULL)) { node->weight = PCMK_SCORE_INFINITY; } } pcmk__set_rsc_flags(replica->child->parent, pcmk_rsc_assigning); pcmk__rsc_trace(bundle, "Assigning bundle %s replica child %s", bundle->id, replica->child->id); replica->child->cmds->assign(replica->child, replica->node, stop_if_fail); pcmk__clear_rsc_flags(replica->child->parent, pcmk_rsc_assigning); } return true; } /*! * \internal * \brief Assign a bundle resource to a node * * \param[in,out] rsc Resource to assign to a node * \param[in] prefer Node to prefer, if all else is equal * \param[in] stop_if_fail If \c true and a primitive descendant of \p rsc * can't be assigned to a node, set the * descendant's next role to stopped and update * existing actions * * \return Node that \p rsc is assigned to, if assigned entirely to one node * * \note If \p stop_if_fail is \c false, then \c pcmk__unassign_resource() can * completely undo the assignment. A successful assignment can be either * undone or left alone as final. A failed assignment has the same effect * as calling pcmk__unassign_resource(); there are no side effects on * roles or actions. */ pcmk_node_t * pcmk__bundle_assign(pcmk_resource_t *rsc, const pcmk_node_t *prefer, bool stop_if_fail) { GList *containers = NULL; pcmk_resource_t *bundled_resource = NULL; struct assign_data assign_data = { prefer, stop_if_fail }; CRM_ASSERT(pcmk__is_bundle(rsc)); pcmk__rsc_trace(rsc, "Assigning bundle %s", rsc->id); pcmk__set_rsc_flags(rsc, pcmk_rsc_assigning); pe__show_node_scores(!pcmk_is_set(rsc->cluster->flags, pcmk_sched_output_scores), rsc, __func__, rsc->allowed_nodes, rsc->cluster); // Assign all containers first, so we know what nodes the bundle will be on containers = g_list_sort(pe__bundle_containers(rsc), pcmk__cmp_instance); pcmk__assign_instances(rsc, containers, pe__bundle_max(rsc), rsc->fns->max_per_node(rsc)); g_list_free(containers); // Then assign remaining replica resources pe__foreach_bundle_replica(rsc, assign_replica, (void *) &assign_data); // Finally, assign the bundled resources to each bundle node bundled_resource = pe__bundled_resource(rsc); if (bundled_resource != NULL) { pcmk_node_t *node = NULL; GHashTableIter iter; g_hash_table_iter_init(&iter, bundled_resource->allowed_nodes); while (g_hash_table_iter_next(&iter, NULL, (gpointer *) & node)) { if (pe__node_is_bundle_instance(rsc, node)) { node->weight = 0; } else { node->weight = -PCMK_SCORE_INFINITY; } } bundled_resource->cmds->assign(bundled_resource, prefer, stop_if_fail); } pcmk__clear_rsc_flags(rsc, pcmk_rsc_assigning|pcmk_rsc_unassigned); return NULL; } /*! * \internal * \brief Create actions for a bundle replica's resources (other than child) * * \param[in,out] replica Replica to create actions for * \param[in] user_data Unused * * \return true (to indicate that any further replicas should be processed) */ static bool create_replica_actions(pcmk__bundle_replica_t *replica, void *user_data) { if (replica->ip != NULL) { replica->ip->cmds->create_actions(replica->ip); } if (replica->container != NULL) { replica->container->cmds->create_actions(replica->container); } if (replica->remote != NULL) { replica->remote->cmds->create_actions(replica->remote); } return true; } /*! * \internal * \brief Create all actions needed for a given bundle resource * * \param[in,out] rsc Bundle resource to create actions for */ void pcmk__bundle_create_actions(pcmk_resource_t *rsc) { pcmk_action_t *action = NULL; GList *containers = NULL; pcmk_resource_t *bundled_resource = NULL; CRM_ASSERT(pcmk__is_bundle(rsc)); pe__foreach_bundle_replica(rsc, create_replica_actions, NULL); containers = pe__bundle_containers(rsc); pcmk__create_instance_actions(rsc, containers); g_list_free(containers); bundled_resource = pe__bundled_resource(rsc); if (bundled_resource != NULL) { bundled_resource->cmds->create_actions(bundled_resource); if (pcmk_is_set(bundled_resource->flags, pcmk_rsc_promotable)) { pe__new_rsc_pseudo_action(rsc, PCMK_ACTION_PROMOTE, true, true); action = pe__new_rsc_pseudo_action(rsc, PCMK_ACTION_PROMOTED, true, true); action->priority = PCMK_SCORE_INFINITY; pe__new_rsc_pseudo_action(rsc, PCMK_ACTION_DEMOTE, true, true); action = pe__new_rsc_pseudo_action(rsc, PCMK_ACTION_DEMOTED, true, true); action->priority = PCMK_SCORE_INFINITY; } } } /*! * \internal * \brief Create internal constraints for a bundle replica's resources * * \param[in,out] replica Replica to create internal constraints for * \param[in,out] user_data Replica's parent bundle * * \return true (to indicate that any further replicas should be processed) */ static bool replica_internal_constraints(pcmk__bundle_replica_t *replica, void *user_data) { pcmk_resource_t *bundle = user_data; replica->container->cmds->internal_constraints(replica->container); // Start bundle -> start replica container pcmk__order_starts(bundle, replica->container, pcmk__ar_unrunnable_first_blocks |pcmk__ar_then_implies_first_graphed); // Stop bundle -> stop replica child and container if (replica->child != NULL) { pcmk__order_stops(bundle, replica->child, pcmk__ar_then_implies_first_graphed); } pcmk__order_stops(bundle, replica->container, pcmk__ar_then_implies_first_graphed); // Start replica container -> bundle is started pcmk__order_resource_actions(replica->container, PCMK_ACTION_START, bundle, PCMK_ACTION_RUNNING, pcmk__ar_first_implies_then_graphed); // Stop replica container -> bundle is stopped pcmk__order_resource_actions(replica->container, PCMK_ACTION_STOP, bundle, PCMK_ACTION_STOPPED, pcmk__ar_first_implies_then_graphed); if (replica->ip != NULL) { replica->ip->cmds->internal_constraints(replica->ip); // Replica IP address -> replica container (symmetric) pcmk__order_starts(replica->ip, replica->container, pcmk__ar_unrunnable_first_blocks |pcmk__ar_guest_allowed); pcmk__order_stops(replica->container, replica->ip, pcmk__ar_then_implies_first|pcmk__ar_guest_allowed); pcmk__new_colocation("#ip-with-container", NULL, PCMK_SCORE_INFINITY, replica->ip, replica->container, NULL, NULL, pcmk__coloc_influence); } if (replica->remote != NULL) { /* This handles ordering and colocating remote relative to container * (via "#resource-with-container"). Since IP is also ordered and * colocated relative to the container, we don't need to do anything * explicit here with IP. */ replica->remote->cmds->internal_constraints(replica->remote); } if (replica->child != NULL) { CRM_ASSERT(replica->remote != NULL); // "Start remote then child" is implicit in scheduler's remote logic } return true; } /*! * \internal * \brief Create implicit constraints needed for a bundle resource * * \param[in,out] rsc Bundle resource to create implicit constraints for */ void pcmk__bundle_internal_constraints(pcmk_resource_t *rsc) { pcmk_resource_t *bundled_resource = NULL; CRM_ASSERT(pcmk__is_bundle(rsc)); pe__foreach_bundle_replica(rsc, replica_internal_constraints, rsc); bundled_resource = pe__bundled_resource(rsc); if (bundled_resource == NULL) { return; } // Start bundle -> start bundled clone pcmk__order_resource_actions(rsc, PCMK_ACTION_START, bundled_resource, PCMK_ACTION_START, pcmk__ar_then_implies_first_graphed); // Bundled clone is started -> bundle is started pcmk__order_resource_actions(bundled_resource, PCMK_ACTION_RUNNING, rsc, PCMK_ACTION_RUNNING, pcmk__ar_first_implies_then_graphed); // Stop bundle -> stop bundled clone pcmk__order_resource_actions(rsc, PCMK_ACTION_STOP, bundled_resource, PCMK_ACTION_STOP, pcmk__ar_then_implies_first_graphed); // Bundled clone is stopped -> bundle is stopped pcmk__order_resource_actions(bundled_resource, PCMK_ACTION_STOPPED, rsc, PCMK_ACTION_STOPPED, pcmk__ar_first_implies_then_graphed); bundled_resource->cmds->internal_constraints(bundled_resource); if (!pcmk_is_set(bundled_resource->flags, pcmk_rsc_promotable)) { return; } pcmk__promotable_restart_ordering(rsc); // Demote bundle -> demote bundled clone pcmk__order_resource_actions(rsc, PCMK_ACTION_DEMOTE, bundled_resource, PCMK_ACTION_DEMOTE, pcmk__ar_then_implies_first_graphed); // Bundled clone is demoted -> bundle is demoted pcmk__order_resource_actions(bundled_resource, PCMK_ACTION_DEMOTED, rsc, PCMK_ACTION_DEMOTED, pcmk__ar_first_implies_then_graphed); // Promote bundle -> promote bundled clone pcmk__order_resource_actions(rsc, PCMK_ACTION_PROMOTE, bundled_resource, PCMK_ACTION_PROMOTE, pcmk__ar_then_implies_first_graphed); // Bundled clone is promoted -> bundle is promoted pcmk__order_resource_actions(bundled_resource, PCMK_ACTION_PROMOTED, rsc, PCMK_ACTION_PROMOTED, pcmk__ar_first_implies_then_graphed); } struct match_data { const pcmk_node_t *node; // Node to compare against replica pcmk_resource_t *container; // Replica container corresponding to node }; /*! * \internal * \brief Check whether a replica container is assigned to a given node * * \param[in] replica Replica to check * \param[in,out] user_data struct match_data with node to compare against * * \return true if the replica does not match (to indicate further replicas * should be processed), otherwise false */ static bool match_replica_container(const pcmk__bundle_replica_t *replica, void *user_data) { struct match_data *match_data = user_data; if (pcmk__instance_matches(replica->container, match_data->node, pcmk_role_unknown, false)) { match_data->container = replica->container; return false; // Match found, don't bother searching further replicas } return true; // No match, keep searching } /*! * \internal * \brief Get the host to which a bundle node is assigned * * \param[in] node Possible bundle node to check * * \return Node to which the container for \p node is assigned if \p node is a * bundle node, otherwise \p node itself */ static const pcmk_node_t * get_bundle_node_host(const pcmk_node_t *node) { if (pcmk__is_bundle_node(node)) { const pcmk_resource_t *container = node->details->remote_rsc->container; return container->fns->location(container, NULL, 0); } return node; } /*! * \internal * \brief Find a bundle container compatible with a dependent resource * * \param[in] dependent Dependent resource in colocation with bundle * \param[in] bundle Bundle that \p dependent is colocated with * * \return A container from \p bundle assigned to the same node as \p dependent * if assigned, otherwise assigned to any of dependent's allowed nodes, * otherwise NULL. */ static pcmk_resource_t * compatible_container(const pcmk_resource_t *dependent, const pcmk_resource_t *bundle) { GList *scratch = NULL; struct match_data match_data = { NULL, NULL }; // If dependent is assigned, only check there match_data.node = dependent->fns->location(dependent, NULL, 0); match_data.node = get_bundle_node_host(match_data.node); if (match_data.node != NULL) { pe__foreach_const_bundle_replica(bundle, match_replica_container, &match_data); return match_data.container; } // Otherwise, check for any of the dependent's allowed nodes scratch = g_hash_table_get_values(dependent->allowed_nodes); scratch = pcmk__sort_nodes(scratch, NULL); for (const GList *iter = scratch; iter != NULL; iter = iter->next) { match_data.node = iter->data; match_data.node = get_bundle_node_host(match_data.node); if (match_data.node == NULL) { continue; } pe__foreach_const_bundle_replica(bundle, match_replica_container, &match_data); if (match_data.container != NULL) { break; } } g_list_free(scratch); return match_data.container; } struct coloc_data { const pcmk__colocation_t *colocation; pcmk_resource_t *dependent; GList *container_hosts; + int priority_delta; }; /*! * \internal * \brief Apply a colocation score to replica node scores or resource priority * * \param[in] replica Replica of primary bundle resource in colocation * \param[in,out] user_data struct coloc_data for colocation being applied * * \return true (to indicate that any further replicas should be processed) */ static bool replica_apply_coloc_score(const pcmk__bundle_replica_t *replica, void *user_data) { struct coloc_data *coloc_data = user_data; pcmk_node_t *chosen = NULL; if (coloc_data->colocation->score < PCMK_SCORE_INFINITY) { - replica->container->cmds->apply_coloc_score(coloc_data->dependent, - replica->container, - coloc_data->colocation, - false); + int priority_delta = + replica->container->cmds->apply_coloc_score(coloc_data->dependent, + replica->container, + coloc_data->colocation, + false); + + coloc_data->priority_delta = + pcmk__add_scores(coloc_data->priority_delta, priority_delta); return true; } chosen = replica->container->fns->location(replica->container, NULL, 0); if ((chosen == NULL) || is_set_recursive(replica->container, pcmk_rsc_blocked, true)) { return true; } if ((coloc_data->colocation->primary_role >= pcmk_role_promoted) && ((replica->child == NULL) || (replica->child->next_role < pcmk_role_promoted))) { return true; } pcmk__rsc_trace(pe__const_top_resource(replica->container, true), "Allowing mandatory colocation %s using %s @%d", coloc_data->colocation->id, pcmk__node_name(chosen), chosen->weight); coloc_data->container_hosts = g_list_prepend(coloc_data->container_hosts, chosen); return true; } /*! * \internal * \brief Apply a colocation's score to node scores or resource priority * * Given a colocation constraint, apply its score to the dependent's * allowed node scores (if we are still placing resources) or priority (if * we are choosing promotable clone instance roles). * * \param[in,out] dependent Dependent resource in colocation * \param[in] primary Primary resource in colocation * \param[in] colocation Colocation constraint to apply * \param[in] for_dependent true if called on behalf of dependent + * + * \return The score added to the dependent's priority */ -void +int pcmk__bundle_apply_coloc_score(pcmk_resource_t *dependent, const pcmk_resource_t *primary, const pcmk__colocation_t *colocation, bool for_dependent) { - struct coloc_data coloc_data = { colocation, dependent, NULL }; + struct coloc_data coloc_data = { colocation, dependent, NULL, 0 }; /* This should never be called for the bundle itself as a dependent. * Instead, we add its colocation constraints to its containers and bundled * primitive and call the apply_coloc_score() method for them as dependents. */ CRM_ASSERT(pcmk__is_bundle(primary) && pcmk__is_primitive(dependent) && (colocation != NULL) && !for_dependent); if (pcmk_is_set(primary->flags, pcmk_rsc_unassigned)) { pcmk__rsc_trace(primary, "Skipping applying colocation %s " "because %s is still provisional", colocation->id, primary->id); - return; + return 0; } pcmk__rsc_trace(primary, "Applying colocation %s (%s with %s at %s)", colocation->id, dependent->id, primary->id, pcmk_readable_score(colocation->score)); /* If the constraint dependent is a clone or bundle, "dependent" here is one * of its instances. Look for a compatible instance of this bundle. */ if (colocation->dependent->variant > pcmk_rsc_variant_group) { const pcmk_resource_t *primary_container = NULL; primary_container = compatible_container(dependent, primary); if (primary_container != NULL) { // Success, we found one pcmk__rsc_debug(primary, "Pairing %s with %s", dependent->id, primary_container->id); - dependent->cmds->apply_coloc_score(dependent, primary_container, - colocation, true); - } else if (colocation->score >= PCMK_SCORE_INFINITY) { + return dependent->cmds->apply_coloc_score(dependent, + primary_container, + colocation, true); + } + + if (colocation->score >= PCMK_SCORE_INFINITY) { // Failure, and it's fatal crm_notice("%s cannot run because there is no compatible " "instance of %s to colocate with", dependent->id, primary->id); pcmk__assign_resource(dependent, NULL, true, true); } else { // Failure, but we can ignore it pcmk__rsc_debug(primary, "%s cannot be colocated with any instance of %s", dependent->id, primary->id); } - return; + return 0; } pe__foreach_const_bundle_replica(primary, replica_apply_coloc_score, &coloc_data); if (colocation->score >= PCMK_SCORE_INFINITY) { pcmk__colocation_intersect_nodes(dependent, primary, colocation, coloc_data.container_hosts, false); } g_list_free(coloc_data.container_hosts); + return coloc_data.priority_delta; } // Bundle implementation of pcmk_assignment_methods_t:with_this_colocations() void pcmk__with_bundle_colocations(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList **list) { const pcmk_resource_t *bundled_rsc = NULL; CRM_ASSERT(pcmk__is_bundle(rsc) && (orig_rsc != NULL) && (list != NULL)); // The bundle itself and its containers always get its colocations if ((orig_rsc == rsc) || pcmk_is_set(orig_rsc->flags, pcmk_rsc_replica_container)) { pcmk__add_with_this_list(list, rsc->rsc_cons_lhs, orig_rsc); return; } /* The bundled resource gets the colocations if it's promotable and we've * begun choosing roles */ bundled_rsc = pe__bundled_resource(rsc); if ((bundled_rsc == NULL) || !pcmk_is_set(bundled_rsc->flags, pcmk_rsc_promotable) || (pe__const_top_resource(orig_rsc, false) != bundled_rsc)) { return; } if (orig_rsc == bundled_rsc) { if (pe__clone_flag_is_set(orig_rsc, pcmk__clone_promotion_constrained)) { /* orig_rsc is the clone and we're setting roles (or have already * done so) */ pcmk__add_with_this_list(list, rsc->rsc_cons_lhs, orig_rsc); } } else if (!pcmk_is_set(orig_rsc->flags, pcmk_rsc_unassigned)) { /* orig_rsc is an instance and is already assigned. If something * requests colocations for orig_rsc now, it's for setting roles. */ pcmk__add_with_this_list(list, rsc->rsc_cons_lhs, orig_rsc); } } // Bundle implementation of pcmk_assignment_methods_t:this_with_colocations() void pcmk__bundle_with_colocations(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList **list) { const pcmk_resource_t *bundled_rsc = NULL; CRM_ASSERT(pcmk__is_bundle(rsc) && (orig_rsc != NULL) && (list != NULL)); // The bundle itself and its containers always get its colocations if ((orig_rsc == rsc) || pcmk_is_set(orig_rsc->flags, pcmk_rsc_replica_container)) { pcmk__add_this_with_list(list, rsc->rsc_cons, orig_rsc); return; } /* The bundled resource gets the colocations if it's promotable and we've * begun choosing roles */ bundled_rsc = pe__bundled_resource(rsc); if ((bundled_rsc == NULL) || !pcmk_is_set(bundled_rsc->flags, pcmk_rsc_promotable) || (pe__const_top_resource(orig_rsc, false) != bundled_rsc)) { return; } if (orig_rsc == bundled_rsc) { if (pe__clone_flag_is_set(orig_rsc, pcmk__clone_promotion_constrained)) { /* orig_rsc is the clone and we're setting roles (or have already * done so) */ pcmk__add_this_with_list(list, rsc->rsc_cons, orig_rsc); } } else if (!pcmk_is_set(orig_rsc->flags, pcmk_rsc_unassigned)) { /* orig_rsc is an instance and is already assigned. If something * requests colocations for orig_rsc now, it's for setting roles. */ pcmk__add_this_with_list(list, rsc->rsc_cons, orig_rsc); } } /*! * \internal * \brief Return action flags for a given bundle resource action * * \param[in,out] action Bundle resource action to get flags for * \param[in] node If not NULL, limit effects to this node * * \return Flags appropriate to \p action on \p node */ uint32_t pcmk__bundle_action_flags(pcmk_action_t *action, const pcmk_node_t *node) { GList *containers = NULL; uint32_t flags = 0; pcmk_resource_t *bundled_resource = NULL; CRM_ASSERT((action != NULL) && pcmk__is_bundle(action->rsc)); bundled_resource = pe__bundled_resource(action->rsc); if (bundled_resource != NULL) { // Clone actions are done on the bundled clone resource, not container switch (get_complex_task(bundled_resource, action->task)) { case pcmk_action_unspecified: case pcmk_action_notify: case pcmk_action_notified: case pcmk_action_promote: case pcmk_action_promoted: case pcmk_action_demote: case pcmk_action_demoted: return pcmk__collective_action_flags(action, bundled_resource->children, node); default: break; } } containers = pe__bundle_containers(action->rsc); flags = pcmk__collective_action_flags(action, containers, node); g_list_free(containers); return flags; } /*! * \internal * \brief Apply a location constraint to a bundle replica * * \param[in,out] replica Replica to apply constraint to * \param[in,out] user_data Location constraint to apply * * \return true (to indicate that any further replicas should be processed) */ static bool apply_location_to_replica(pcmk__bundle_replica_t *replica, void *user_data) { pcmk__location_t *location = user_data; if (replica->container != NULL) { replica->container->cmds->apply_location(replica->container, location); } if (replica->ip != NULL) { replica->ip->cmds->apply_location(replica->ip, location); } return true; } /*! * \internal * \brief Apply a location constraint to a bundle resource's allowed node scores * * \param[in,out] rsc Bundle resource to apply constraint to * \param[in,out] location Location constraint to apply */ void pcmk__bundle_apply_location(pcmk_resource_t *rsc, pcmk__location_t *location) { pcmk_resource_t *bundled_resource = NULL; CRM_ASSERT((location != NULL) && pcmk__is_bundle(rsc)); pcmk__apply_location(rsc, location); pe__foreach_bundle_replica(rsc, apply_location_to_replica, location); bundled_resource = pe__bundled_resource(rsc); if ((bundled_resource != NULL) && ((location->role_filter == pcmk_role_unpromoted) || (location->role_filter == pcmk_role_promoted))) { bundled_resource->cmds->apply_location(bundled_resource, location); bundled_resource->rsc_location = g_list_prepend( bundled_resource->rsc_location, location); } } #define XPATH_REMOTE "//nvpair[@name='" PCMK_REMOTE_RA_ADDR "']" /*! * \internal * \brief Add a bundle replica's actions to transition graph * * \param[in,out] replica Replica to add to graph * \param[in] user_data Bundle that replica belongs to (for logging only) * * \return true (to indicate that any further replicas should be processed) */ static bool add_replica_actions_to_graph(pcmk__bundle_replica_t *replica, void *user_data) { if ((replica->remote != NULL) && (replica->container != NULL) && pe__bundle_needs_remote_name(replica->remote)) { /* REMOTE_CONTAINER_HACK: Allow remote nodes to run containers that * run pacemaker-remoted inside, without needing a separate IP for * the container. This is done by configuring the inner remote's * connection host as the magic string "#uname", then * replacing it with the underlying host when needed. */ xmlNode *nvpair = get_xpath_object(XPATH_REMOTE, replica->remote->xml, LOG_ERR); const char *calculated_addr = NULL; // Replace the value in replica->remote->xml (if appropriate) calculated_addr = pe__add_bundle_remote_name(replica->remote, nvpair, PCMK_XA_VALUE); if (calculated_addr != NULL) { /* Since this is for the bundle as a resource, and not any * particular action, replace the value in the default * parameters (not evaluated for node). create_graph_action() * will grab it from there to replace it in node-evaluated * parameters. */ GHashTable *params = pe_rsc_params(replica->remote, NULL, replica->remote->cluster); pcmk__insert_dup(params, PCMK_REMOTE_RA_ADDR, calculated_addr); } else { pcmk_resource_t *bundle = user_data; /* The only way to get here is if the remote connection is * neither currently running nor scheduled to run. That means we * won't be doing any operations that require addr (only start * requires it; we additionally use it to compare digests when * unpacking status, promote, and migrate_from history, but * that's already happened by this point). */ pcmk__rsc_info(bundle, "Unable to determine address for bundle %s " "remote connection", bundle->id); } } if (replica->ip != NULL) { replica->ip->cmds->add_actions_to_graph(replica->ip); } if (replica->container != NULL) { replica->container->cmds->add_actions_to_graph(replica->container); } if (replica->remote != NULL) { replica->remote->cmds->add_actions_to_graph(replica->remote); } return true; } /*! * \internal * \brief Add a bundle resource's actions to the transition graph * * \param[in,out] rsc Bundle resource whose actions should be added */ void pcmk__bundle_add_actions_to_graph(pcmk_resource_t *rsc) { pcmk_resource_t *bundled_resource = NULL; CRM_ASSERT(pcmk__is_bundle(rsc)); bundled_resource = pe__bundled_resource(rsc); if (bundled_resource != NULL) { bundled_resource->cmds->add_actions_to_graph(bundled_resource); } pe__foreach_bundle_replica(rsc, add_replica_actions_to_graph, rsc); } struct probe_data { pcmk_resource_t *bundle; // Bundle being probed pcmk_node_t *node; // Node to create probes on bool any_created; // Whether any probes have been created }; /*! * \internal * \brief Order a bundle replica's start after another replica's probe * * \param[in,out] replica Replica to order start for * \param[in,out] user_data Replica with probe to order after * * \return true (to indicate that any further replicas should be processed) */ static bool order_replica_start_after(pcmk__bundle_replica_t *replica, void *user_data) { pcmk__bundle_replica_t *probed_replica = user_data; if ((replica == probed_replica) || (replica->container == NULL)) { return true; } pcmk__new_ordering(probed_replica->container, pcmk__op_key(probed_replica->container->id, PCMK_ACTION_MONITOR, 0), NULL, replica->container, pcmk__op_key(replica->container->id, PCMK_ACTION_START, 0), NULL, pcmk__ar_ordered|pcmk__ar_if_on_same_node, replica->container->cluster); return true; } /*! * \internal * \brief Create probes for a bundle replica's resources * * \param[in,out] replica Replica to create probes for * \param[in,out] user_data struct probe_data * * \return true (to indicate that any further replicas should be processed) */ static bool create_replica_probes(pcmk__bundle_replica_t *replica, void *user_data) { struct probe_data *probe_data = user_data; if ((replica->ip != NULL) && replica->ip->cmds->create_probe(replica->ip, probe_data->node)) { probe_data->any_created = true; } if ((replica->child != NULL) && pcmk__same_node(probe_data->node, replica->node) && replica->child->cmds->create_probe(replica->child, probe_data->node)) { probe_data->any_created = true; } if ((replica->container != NULL) && replica->container->cmds->create_probe(replica->container, probe_data->node)) { probe_data->any_created = true; /* If we're limited to one replica per host (due to * the lack of an IP range probably), then we don't * want any of our peer containers starting until * we've established that no other copies are already * running. * * Partly this is to ensure that the maximum replicas per host is * observed, but also to ensure that the containers * don't fail to start because the necessary port * mappings (which won't include an IP for uniqueness) * are already taken */ if (probe_data->bundle->fns->max_per_node(probe_data->bundle) == 1) { pe__foreach_bundle_replica(probe_data->bundle, order_replica_start_after, replica); } } if ((replica->container != NULL) && (replica->remote != NULL) && replica->remote->cmds->create_probe(replica->remote, probe_data->node)) { /* Do not probe the remote resource until we know where the container is * running. This is required for REMOTE_CONTAINER_HACK to correctly * probe remote resources. */ char *probe_uuid = pcmk__op_key(replica->remote->id, PCMK_ACTION_MONITOR, 0); pcmk_action_t *probe = find_first_action(replica->remote->actions, probe_uuid, NULL, probe_data->node); free(probe_uuid); if (probe != NULL) { probe_data->any_created = true; pcmk__rsc_trace(probe_data->bundle, "Ordering %s probe on %s", replica->remote->id, pcmk__node_name(probe_data->node)); pcmk__new_ordering(replica->container, pcmk__op_key(replica->container->id, PCMK_ACTION_START, 0), NULL, replica->remote, NULL, probe, pcmk__ar_nested_remote_probe, probe_data->bundle->cluster); } } return true; } /*! * \internal * * \brief Schedule any probes needed for a bundle resource on a node * * \param[in,out] rsc Bundle resource to create probes for * \param[in,out] node Node to create probe on * * \return true if any probe was created, otherwise false */ bool pcmk__bundle_create_probe(pcmk_resource_t *rsc, pcmk_node_t *node) { struct probe_data probe_data = { rsc, node, false }; CRM_ASSERT(pcmk__is_bundle(rsc)); pe__foreach_bundle_replica(rsc, create_replica_probes, &probe_data); return probe_data.any_created; } /*! * \internal * \brief Output actions for one bundle replica * * \param[in,out] replica Replica to output actions for * \param[in] user_data Unused * * \return true (to indicate that any further replicas should be processed) */ static bool output_replica_actions(pcmk__bundle_replica_t *replica, void *user_data) { if (replica->ip != NULL) { replica->ip->cmds->output_actions(replica->ip); } if (replica->container != NULL) { replica->container->cmds->output_actions(replica->container); } if (replica->remote != NULL) { replica->remote->cmds->output_actions(replica->remote); } if (replica->child != NULL) { replica->child->cmds->output_actions(replica->child); } return true; } /*! * \internal * \brief Output a summary of scheduled actions for a bundle resource * * \param[in,out] rsc Bundle resource to output actions for */ void pcmk__output_bundle_actions(pcmk_resource_t *rsc) { CRM_ASSERT(pcmk__is_bundle(rsc)); pe__foreach_bundle_replica(rsc, output_replica_actions, NULL); } // Bundle implementation of pcmk_assignment_methods_t:add_utilization() void pcmk__bundle_add_utilization(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList *all_rscs, GHashTable *utilization) { pcmk_resource_t *container = NULL; CRM_ASSERT(pcmk__is_bundle(rsc)); if (!pcmk_is_set(rsc->flags, pcmk_rsc_unassigned)) { return; } /* All bundle replicas are identical, so using the utilization of the first * is sufficient for any. Only the implicit container resource can have * utilization values. */ container = pe__first_container(rsc); if (container != NULL) { container->cmds->add_utilization(container, orig_rsc, all_rscs, utilization); } } // Bundle implementation of pcmk_assignment_methods_t:shutdown_lock() void pcmk__bundle_shutdown_lock(pcmk_resource_t *rsc) { CRM_ASSERT(pcmk__is_bundle(rsc)); // Bundles currently don't support shutdown locks } diff --git a/lib/pacemaker/pcmk_sched_clone.c b/lib/pacemaker/pcmk_sched_clone.c index dacee7f5ec..23b72fce4f 100644 --- a/lib/pacemaker/pcmk_sched_clone.c +++ b/lib/pacemaker/pcmk_sched_clone.c @@ -1,708 +1,718 @@ /* * Copyright 2004-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU General Public License version 2 * or later (GPLv2+) WITHOUT ANY WARRANTY. */ #include #include #include #include "libpacemaker_private.h" /*! * \internal * \brief Assign a clone resource's instances to nodes * * \param[in,out] rsc Clone resource to assign * \param[in] prefer Node to prefer, if all else is equal * \param[in] stop_if_fail If \c true and a primitive descendant of \p rsc * can't be assigned to a node, set the * descendant's next role to stopped and update * existing actions * * \return NULL (clones are not assigned to a single node) * * \note If \p stop_if_fail is \c false, then \c pcmk__unassign_resource() can * completely undo the assignment. A successful assignment can be either * undone or left alone as final. A failed assignment has the same effect * as calling pcmk__unassign_resource(); there are no side effects on * roles or actions. */ pcmk_node_t * pcmk__clone_assign(pcmk_resource_t *rsc, const pcmk_node_t *prefer, bool stop_if_fail) { GList *colocations = NULL; CRM_ASSERT(pcmk__is_clone(rsc)); if (!pcmk_is_set(rsc->flags, pcmk_rsc_unassigned)) { return NULL; // Assignment has already been done } // Detect assignment loops if (pcmk_is_set(rsc->flags, pcmk_rsc_assigning)) { pcmk__rsc_debug(rsc, "Breaking assignment loop involving %s", rsc->id); return NULL; } pcmk__set_rsc_flags(rsc, pcmk_rsc_assigning); // If this clone is promotable, consider nodes' promotion scores if (pcmk_is_set(rsc->flags, pcmk_rsc_promotable)) { pcmk__add_promotion_scores(rsc); } // If this clone is colocated with any other resources, assign those first colocations = pcmk__this_with_colocations(rsc); for (GList *iter = colocations; iter != NULL; iter = iter->next) { pcmk__colocation_t *constraint = (pcmk__colocation_t *) iter->data; pcmk__rsc_trace(rsc, "%s: Assigning colocation %s primary %s first", rsc->id, constraint->id, constraint->primary->id); constraint->primary->cmds->assign(constraint->primary, prefer, stop_if_fail); } g_list_free(colocations); // If any resources are colocated with this one, consider their preferences colocations = pcmk__with_this_colocations(rsc); g_list_foreach(colocations, pcmk__add_dependent_scores, rsc); g_list_free(colocations); pe__show_node_scores(!pcmk_is_set(rsc->cluster->flags, pcmk_sched_output_scores), rsc, __func__, rsc->allowed_nodes, rsc->cluster); rsc->children = g_list_sort(rsc->children, pcmk__cmp_instance); pcmk__assign_instances(rsc, rsc->children, pe__clone_max(rsc), pe__clone_node_max(rsc)); if (pcmk_is_set(rsc->flags, pcmk_rsc_promotable)) { pcmk__set_instance_roles(rsc); } pcmk__clear_rsc_flags(rsc, pcmk_rsc_unassigned|pcmk_rsc_assigning); pcmk__rsc_trace(rsc, "Assigned clone %s", rsc->id); return NULL; } /*! * \internal * \brief Create all actions needed for a given clone resource * * \param[in,out] rsc Clone resource to create actions for */ void pcmk__clone_create_actions(pcmk_resource_t *rsc) { CRM_ASSERT(pcmk__is_clone(rsc)); pcmk__rsc_trace(rsc, "Creating actions for clone %s", rsc->id); pcmk__create_instance_actions(rsc, rsc->children); if (pcmk_is_set(rsc->flags, pcmk_rsc_promotable)) { pcmk__create_promotable_actions(rsc); } } /*! * \internal * \brief Create implicit constraints needed for a clone resource * * \param[in,out] rsc Clone resource to create implicit constraints for */ void pcmk__clone_internal_constraints(pcmk_resource_t *rsc) { bool ordered = false; CRM_ASSERT(pcmk__is_clone(rsc)); pcmk__rsc_trace(rsc, "Creating internal constraints for clone %s", rsc->id); // Restart ordering: Stop -> stopped -> start -> started pcmk__order_resource_actions(rsc, PCMK_ACTION_STOPPED, rsc, PCMK_ACTION_START, pcmk__ar_ordered); pcmk__order_resource_actions(rsc, PCMK_ACTION_START, rsc, PCMK_ACTION_RUNNING, pcmk__ar_unrunnable_first_blocks); pcmk__order_resource_actions(rsc, PCMK_ACTION_STOP, rsc, PCMK_ACTION_STOPPED, pcmk__ar_unrunnable_first_blocks); // Demoted -> stop and started -> promote if (pcmk_is_set(rsc->flags, pcmk_rsc_promotable)) { pcmk__order_resource_actions(rsc, PCMK_ACTION_DEMOTED, rsc, PCMK_ACTION_STOP, pcmk__ar_ordered); pcmk__order_resource_actions(rsc, PCMK_ACTION_RUNNING, rsc, PCMK_ACTION_PROMOTE, pcmk__ar_unrunnable_first_blocks); } ordered = pe__clone_is_ordered(rsc); if (ordered) { /* Ordered clone instances must start and stop by instance number. The * instances might have been previously shuffled for assignment or * promotion purposes, so re-sort them. */ rsc->children = g_list_sort(rsc->children, pcmk__cmp_instance_number); } for (GList *iter = rsc->children; iter != NULL; iter = iter->next) { pcmk_resource_t *instance = (pcmk_resource_t *) iter->data; instance->cmds->internal_constraints(instance); // Start clone -> start instance -> clone started pcmk__order_starts(rsc, instance, pcmk__ar_unrunnable_first_blocks |pcmk__ar_then_implies_first_graphed); pcmk__order_resource_actions(instance, PCMK_ACTION_START, rsc, PCMK_ACTION_RUNNING, pcmk__ar_first_implies_then_graphed); // Stop clone -> stop instance -> clone stopped pcmk__order_stops(rsc, instance, pcmk__ar_then_implies_first_graphed); pcmk__order_resource_actions(instance, PCMK_ACTION_STOP, rsc, PCMK_ACTION_STOPPED, pcmk__ar_first_implies_then_graphed); /* Instances of ordered clones must be started and stopped by instance * number. Since only some instances may be starting or stopping, order * each instance relative to every later instance. */ if (ordered) { for (GList *later = iter->next; later != NULL; later = later->next) { pcmk__order_starts(instance, (pcmk_resource_t *) later->data, pcmk__ar_ordered); pcmk__order_stops((pcmk_resource_t *) later->data, instance, pcmk__ar_ordered); } } } if (pcmk_is_set(rsc->flags, pcmk_rsc_promotable)) { pcmk__order_promotable_instances(rsc); } } /*! * \internal * \brief Check whether colocated resources can be interleaved * * \param[in] colocation Colocation constraint with clone as primary * * \return true if colocated resources can be interleaved, otherwise false */ static bool can_interleave(const pcmk__colocation_t *colocation) { const pcmk_resource_t *dependent = colocation->dependent; // Only colocations between clone or bundle resources use interleaving if (dependent->variant <= pcmk_rsc_variant_group) { return false; } // Only the dependent needs to be marked for interleaving if (!crm_is_true(g_hash_table_lookup(dependent->meta, PCMK_META_INTERLEAVE))) { return false; } /* @TODO Do we actually care about multiple primary instances sharing a * dependent instance? */ if (dependent->fns->max_per_node(dependent) != colocation->primary->fns->max_per_node(colocation->primary)) { pcmk__config_err("Cannot interleave %s and %s because they do not " "support the same number of instances per node", dependent->id, colocation->primary->id); return false; } return true; } /*! * \internal * \brief Apply a colocation's score to node scores or resource priority * * Given a colocation constraint, apply its score to the dependent's * allowed node scores (if we are still placing resources) or priority (if * we are choosing promotable clone instance roles). * * \param[in,out] dependent Dependent resource in colocation * \param[in] primary Primary resource in colocation * \param[in] colocation Colocation constraint to apply * \param[in] for_dependent true if called on behalf of dependent + * + * \return The score added to the dependent's priority */ -void +int pcmk__clone_apply_coloc_score(pcmk_resource_t *dependent, const pcmk_resource_t *primary, const pcmk__colocation_t *colocation, bool for_dependent) { const GList *iter = NULL; + int priority_delta = 0; /* This should never be called for the clone itself as a dependent. Instead, * we add its colocation constraints to its instances and call the * apply_coloc_score() method for the instances as dependents. */ CRM_ASSERT(!for_dependent); CRM_ASSERT((colocation != NULL) && pcmk__is_clone(primary) && pcmk__is_primitive(dependent)); if (pcmk_is_set(primary->flags, pcmk_rsc_unassigned)) { pcmk__rsc_trace(primary, "Delaying processing colocation %s " "because cloned primary %s is still provisional", colocation->id, primary->id); - return; + return 0; } pcmk__rsc_trace(primary, "Processing colocation %s (%s with clone %s @%s)", colocation->id, dependent->id, primary->id, pcmk_readable_score(colocation->score)); // Apply role-specific colocations if (pcmk_is_set(primary->flags, pcmk_rsc_promotable) && (colocation->primary_role != pcmk_role_unknown)) { if (pcmk_is_set(dependent->flags, pcmk_rsc_unassigned)) { // We're assigning the dependent to a node pcmk__update_dependent_with_promotable(primary, dependent, colocation); - return; + return 0; } if (colocation->dependent_role == pcmk_role_promoted) { // We're choosing a role for the dependent - pcmk__update_promotable_dependent_priority(primary, dependent, - colocation); - return; + return pcmk__update_promotable_dependent_priority(primary, + dependent, + colocation); } } // Apply interleaved colocations if (can_interleave(colocation)) { const pcmk_resource_t *primary_instance = NULL; primary_instance = pcmk__find_compatible_instance(dependent, primary, pcmk_role_unknown, false); if (primary_instance != NULL) { pcmk__rsc_debug(primary, "Interleaving %s with %s", dependent->id, primary_instance->id); - dependent->cmds->apply_coloc_score(dependent, primary_instance, - colocation, true); - } else if (colocation->score >= PCMK_SCORE_INFINITY) { + return dependent->cmds->apply_coloc_score(dependent, + primary_instance, + colocation, true); + } + + if (colocation->score >= PCMK_SCORE_INFINITY) { crm_notice("%s cannot run because it cannot interleave with " "any instance of %s", dependent->id, primary->id); pcmk__assign_resource(dependent, NULL, true, true); } else { pcmk__rsc_debug(primary, "%s will not colocate with %s " "because no instance can interleave with it", dependent->id, primary->id); } - return; + return 0; } // Apply mandatory colocations if (colocation->score >= PCMK_SCORE_INFINITY) { GList *primary_nodes = NULL; // Dependent can run only where primary will have unblocked instances for (iter = primary->children; iter != NULL; iter = iter->next) { const pcmk_resource_t *instance = iter->data; pcmk_node_t *chosen = instance->fns->location(instance, NULL, 0); if ((chosen != NULL) && !is_set_recursive(instance, pcmk_rsc_blocked, TRUE)) { pcmk__rsc_trace(primary, "Allowing %s: %s %d", colocation->id, pcmk__node_name(chosen), chosen->weight); primary_nodes = g_list_prepend(primary_nodes, chosen); } } pcmk__colocation_intersect_nodes(dependent, primary, colocation, primary_nodes, false); g_list_free(primary_nodes); - return; + return 0; } // Apply optional colocations for (iter = primary->children; iter != NULL; iter = iter->next) { const pcmk_resource_t *instance = iter->data; + int instance_delta = instance->cmds->apply_coloc_score(dependent, + instance, + colocation, + false); - instance->cmds->apply_coloc_score(dependent, instance, colocation, - false); + priority_delta = pcmk__add_scores(priority_delta, instance_delta); } + return priority_delta; } // Clone implementation of pcmk_assignment_methods_t:with_this_colocations() void pcmk__with_clone_colocations(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList **list) { CRM_CHECK((rsc != NULL) && (orig_rsc != NULL) && (list != NULL), return); pcmk__add_with_this_list(list, rsc->rsc_cons_lhs, orig_rsc); if (rsc->parent != NULL) { rsc->parent->cmds->with_this_colocations(rsc->parent, orig_rsc, list); } } // Clone implementation of pcmk_assignment_methods_t:this_with_colocations() void pcmk__clone_with_colocations(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList **list) { CRM_CHECK((rsc != NULL) && (orig_rsc != NULL) && (list != NULL), return); pcmk__add_this_with_list(list, rsc->rsc_cons, orig_rsc); if (rsc->parent != NULL) { rsc->parent->cmds->this_with_colocations(rsc->parent, orig_rsc, list); } } /*! * \internal * \brief Return action flags for a given clone resource action * * \param[in,out] action Action to get flags for * \param[in] node If not NULL, limit effects to this node * * \return Flags appropriate to \p action on \p node */ uint32_t pcmk__clone_action_flags(pcmk_action_t *action, const pcmk_node_t *node) { CRM_ASSERT((action != NULL) && pcmk__is_clone(action->rsc)); return pcmk__collective_action_flags(action, action->rsc->children, node); } /*! * \internal * \brief Apply a location constraint to a clone resource's allowed node scores * * \param[in,out] rsc Clone resource to apply constraint to * \param[in,out] location Location constraint to apply */ void pcmk__clone_apply_location(pcmk_resource_t *rsc, pcmk__location_t *location) { CRM_CHECK((location != NULL) && pcmk__is_clone(rsc), return); pcmk__apply_location(rsc, location); for (GList *iter = rsc->children; iter != NULL; iter = iter->next) { pcmk_resource_t *instance = (pcmk_resource_t *) iter->data; instance->cmds->apply_location(instance, location); } } // GFunc wrapper for calling the action_flags() resource method static void call_action_flags(gpointer data, gpointer user_data) { pcmk_resource_t *rsc = user_data; rsc->cmds->action_flags((pcmk_action_t *) data, NULL); } /*! * \internal * \brief Add a clone resource's actions to the transition graph * * \param[in,out] rsc Resource whose actions should be added */ void pcmk__clone_add_actions_to_graph(pcmk_resource_t *rsc) { CRM_ASSERT(pcmk__is_clone(rsc)); g_list_foreach(rsc->actions, call_action_flags, rsc); pe__create_clone_notifications(rsc); for (GList *iter = rsc->children; iter != NULL; iter = iter->next) { pcmk_resource_t *child_rsc = (pcmk_resource_t *) iter->data; child_rsc->cmds->add_actions_to_graph(child_rsc); } pcmk__add_rsc_actions_to_graph(rsc); pe__free_clone_notification_data(rsc); } /*! * \internal * \brief Check whether a resource or any children have been probed on a node * * \param[in] rsc Resource to check * \param[in] node Node to check * * \return true if \p node is in the known_on table of \p rsc or any of its * children, otherwise false */ static bool rsc_probed_on(const pcmk_resource_t *rsc, const pcmk_node_t *node) { if (rsc->children != NULL) { for (GList *child_iter = rsc->children; child_iter != NULL; child_iter = child_iter->next) { pcmk_resource_t *child = (pcmk_resource_t *) child_iter->data; if (rsc_probed_on(child, node)) { return true; } } return false; } if (rsc->known_on != NULL) { GHashTableIter iter; pcmk_node_t *known_node = NULL; g_hash_table_iter_init(&iter, rsc->known_on); while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &known_node)) { if (pcmk__same_node(node, known_node)) { return true; } } } return false; } /*! * \internal * \brief Find clone instance that has been probed on given node * * \param[in] clone Clone resource to check * \param[in] node Node to check * * \return Instance of \p clone that has been probed on \p node if any, * otherwise NULL */ static pcmk_resource_t * find_probed_instance_on(const pcmk_resource_t *clone, const pcmk_node_t *node) { for (GList *iter = clone->children; iter != NULL; iter = iter->next) { pcmk_resource_t *instance = (pcmk_resource_t *) iter->data; if (rsc_probed_on(instance, node)) { return instance; } } return NULL; } /*! * \internal * \brief Probe an anonymous clone on a node * * \param[in,out] clone Anonymous clone to probe * \param[in,out] node Node to probe \p clone on */ static bool probe_anonymous_clone(pcmk_resource_t *clone, pcmk_node_t *node) { // Check whether we already probed an instance on this node pcmk_resource_t *child = find_probed_instance_on(clone, node); // Otherwise, check if we plan to start an instance on this node for (GList *iter = clone->children; (iter != NULL) && (child == NULL); iter = iter->next) { pcmk_resource_t *instance = (pcmk_resource_t *) iter->data; const pcmk_node_t *instance_node = NULL; instance_node = instance->fns->location(instance, NULL, 0); if (pcmk__same_node(instance_node, node)) { child = instance; } } // Otherwise, use the first clone instance if (child == NULL) { child = clone->children->data; } // Anonymous clones only need to probe a single instance return child->cmds->create_probe(child, node); } /*! * \internal * \brief Schedule any probes needed for a resource on a node * * \param[in,out] rsc Resource to create probe for * \param[in,out] node Node to create probe on * * \return true if any probe was created, otherwise false */ bool pcmk__clone_create_probe(pcmk_resource_t *rsc, pcmk_node_t *node) { CRM_ASSERT((node != NULL) && pcmk__is_clone(rsc)); if (rsc->exclusive_discover) { /* The clone is configured to be probed only where a location constraint * exists with PCMK_XA_RESOURCE_DISCOVERY set to exclusive. * * This check is not strictly necessary here since the instance's * create_probe() method would also check, but doing it here is more * efficient (especially for unique clones with a large number of * instances), and affects the CRM_meta_notify_available_uname variable * passed with notify actions. */ pcmk_node_t *allowed = g_hash_table_lookup(rsc->allowed_nodes, node->details->id); if ((allowed == NULL) || (allowed->rsc_discover_mode != pcmk_probe_exclusive)) { /* This node is not marked for resource discovery. Remove it from * allowed_nodes so that notifications contain only nodes that the * clone can possibly run on. */ pcmk__rsc_trace(rsc, "Skipping probe for %s on %s because resource has " "exclusive discovery but is not allowed on node", rsc->id, pcmk__node_name(node)); g_hash_table_remove(rsc->allowed_nodes, node->details->id); return false; } } rsc->children = g_list_sort(rsc->children, pcmk__cmp_instance_number); if (pcmk_is_set(rsc->flags, pcmk_rsc_unique)) { return pcmk__probe_resource_list(rsc->children, node); } else { return probe_anonymous_clone(rsc, node); } } /*! * \internal * \brief Add meta-attributes relevant to transition graph actions to XML * * Add clone-specific meta-attributes needed for transition graph actions. * * \param[in] rsc Clone resource whose meta-attributes should be added * \param[in,out] xml Transition graph action attributes XML to add to */ void pcmk__clone_add_graph_meta(const pcmk_resource_t *rsc, xmlNode *xml) { char *name = NULL; CRM_ASSERT(pcmk__is_clone(rsc) && (xml != NULL)); name = crm_meta_name(PCMK_META_GLOBALLY_UNIQUE); crm_xml_add(xml, name, pcmk__flag_text(rsc->flags, pcmk_rsc_unique)); free(name); name = crm_meta_name(PCMK_META_NOTIFY); crm_xml_add(xml, name, pcmk__flag_text(rsc->flags, pcmk_rsc_notify)); free(name); name = crm_meta_name(PCMK_META_CLONE_MAX); crm_xml_add_int(xml, name, pe__clone_max(rsc)); free(name); name = crm_meta_name(PCMK_META_CLONE_NODE_MAX); crm_xml_add_int(xml, name, pe__clone_node_max(rsc)); free(name); if (pcmk_is_set(rsc->flags, pcmk_rsc_promotable)) { int promoted_max = pe__clone_promoted_max(rsc); int promoted_node_max = pe__clone_promoted_node_max(rsc); name = crm_meta_name(PCMK_META_PROMOTED_MAX); crm_xml_add_int(xml, name, promoted_max); free(name); name = crm_meta_name(PCMK_META_PROMOTED_NODE_MAX); crm_xml_add_int(xml, name, promoted_node_max); free(name); /* @COMPAT Maintain backward compatibility with resource agents that * expect the old names (deprecated since 2.0.0). */ name = crm_meta_name(PCMK__META_PROMOTED_MAX_LEGACY); crm_xml_add_int(xml, name, promoted_max); free(name); name = crm_meta_name(PCMK__META_PROMOTED_NODE_MAX_LEGACY); crm_xml_add_int(xml, name, promoted_node_max); free(name); } } // Clone implementation of pcmk_assignment_methods_t:add_utilization() void pcmk__clone_add_utilization(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList *all_rscs, GHashTable *utilization) { bool existing = false; pcmk_resource_t *child = NULL; CRM_ASSERT(pcmk__is_clone(rsc) && (orig_rsc != NULL) && (utilization != NULL)); if (!pcmk_is_set(rsc->flags, pcmk_rsc_unassigned)) { return; } // Look for any child already existing in the list for (GList *iter = rsc->children; iter != NULL; iter = iter->next) { child = (pcmk_resource_t *) iter->data; if (g_list_find(all_rscs, child)) { existing = true; // Keep checking remaining children } else { // If this is a clone of a group, look for group's members for (GList *member_iter = child->children; member_iter != NULL; member_iter = member_iter->next) { pcmk_resource_t *member = (pcmk_resource_t *) member_iter->data; if (g_list_find(all_rscs, member) != NULL) { // Add *child's* utilization, not group member's child->cmds->add_utilization(child, orig_rsc, all_rscs, utilization); existing = true; break; } } } } if (!existing && (rsc->children != NULL)) { // If nothing was found, still add first child's utilization child = (pcmk_resource_t *) rsc->children->data; child->cmds->add_utilization(child, orig_rsc, all_rscs, utilization); } } // Clone implementation of pcmk_assignment_methods_t:shutdown_lock() void pcmk__clone_shutdown_lock(pcmk_resource_t *rsc) { CRM_ASSERT(pcmk__is_clone(rsc)); return; // Clones currently don't support shutdown locks } diff --git a/lib/pacemaker/pcmk_sched_colocation.c b/lib/pacemaker/pcmk_sched_colocation.c index 3c11dd8000..00155b7c4e 100644 --- a/lib/pacemaker/pcmk_sched_colocation.c +++ b/lib/pacemaker/pcmk_sched_colocation.c @@ -1,1971 +1,1994 @@ /* * Copyright 2004-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU General Public License version 2 * or later (GPLv2+) WITHOUT ANY WARRANTY. */ #include #include #include #include #include #include #include #include "crm/common/util.h" #include "crm/common/xml_internal.h" #include "crm/common/xml.h" #include "libpacemaker_private.h" // Used to temporarily mark a node as unusable #define INFINITY_HACK (PCMK_SCORE_INFINITY * -100) /*! * \internal * \brief Compare two colocations according to priority * * Compare two colocations according to the order in which they should be * considered, based on either their dependent resources or their primary * resources -- preferring (in order): * * Colocation that is not \c NULL * * Colocation whose resource has higher priority * * Colocation whose resource is of a higher-level variant * (bundle > clone > group > primitive) * * Colocation whose resource is promotable, if both are clones * * Colocation whose resource has lower ID in lexicographic order * * \param[in] colocation1 First colocation to compare * \param[in] colocation2 Second colocation to compare * \param[in] dependent If \c true, compare colocations by dependent * priority; otherwise compare them by primary priority * * \return A negative number if \p colocation1 should be considered first, * a positive number if \p colocation2 should be considered first, * or 0 if order doesn't matter */ static gint cmp_colocation_priority(const pcmk__colocation_t *colocation1, const pcmk__colocation_t *colocation2, bool dependent) { const pcmk_resource_t *rsc1 = NULL; const pcmk_resource_t *rsc2 = NULL; if (colocation1 == NULL) { return 1; } if (colocation2 == NULL) { return -1; } if (dependent) { rsc1 = colocation1->dependent; rsc2 = colocation2->dependent; CRM_ASSERT(colocation1->primary != NULL); } else { rsc1 = colocation1->primary; rsc2 = colocation2->primary; CRM_ASSERT(colocation1->dependent != NULL); } CRM_ASSERT((rsc1 != NULL) && (rsc2 != NULL)); if (rsc1->priority > rsc2->priority) { return -1; } if (rsc1->priority < rsc2->priority) { return 1; } // Process clones before primitives and groups if (rsc1->variant > rsc2->variant) { return -1; } if (rsc1->variant < rsc2->variant) { return 1; } /* @COMPAT scheduler <2.0.0: Process promotable clones before nonpromotable * clones (probably unnecessary, but avoids having to update regression * tests) */ if (pcmk__is_clone(rsc1)) { if (pcmk_is_set(rsc1->flags, pcmk_rsc_promotable) && !pcmk_is_set(rsc2->flags, pcmk_rsc_promotable)) { return -1; } if (!pcmk_is_set(rsc1->flags, pcmk_rsc_promotable) && pcmk_is_set(rsc2->flags, pcmk_rsc_promotable)) { return 1; } } return strcmp(rsc1->id, rsc2->id); } /*! * \internal * \brief Compare two colocations according to priority based on dependents * * Compare two colocations according to the order in which they should be * considered, based on their dependent resources -- preferring (in order): * * Colocation that is not \c NULL * * Colocation whose resource has higher priority * * Colocation whose resource is of a higher-level variant * (bundle > clone > group > primitive) * * Colocation whose resource is promotable, if both are clones * * Colocation whose resource has lower ID in lexicographic order * * \param[in] a First colocation to compare * \param[in] b Second colocation to compare * * \return A negative number if \p a should be considered first, * a positive number if \p b should be considered first, * or 0 if order doesn't matter */ static gint cmp_dependent_priority(gconstpointer a, gconstpointer b) { return cmp_colocation_priority(a, b, true); } /*! * \internal * \brief Compare two colocations according to priority based on primaries * * Compare two colocations according to the order in which they should be * considered, based on their primary resources -- preferring (in order): * * Colocation that is not \c NULL * * Colocation whose primary has higher priority * * Colocation whose primary is of a higher-level variant * (bundle > clone > group > primitive) * * Colocation whose primary is promotable, if both are clones * * Colocation whose primary has lower ID in lexicographic order * * \param[in] a First colocation to compare * \param[in] b Second colocation to compare * * \return A negative number if \p a should be considered first, * a positive number if \p b should be considered first, * or 0 if order doesn't matter */ static gint cmp_primary_priority(gconstpointer a, gconstpointer b) { return cmp_colocation_priority(a, b, false); } /*! * \internal * \brief Add a "this with" colocation constraint to a sorted list * * \param[in,out] list List of constraints to add \p colocation to * \param[in] colocation Colocation constraint to add to \p list * \param[in] rsc Resource whose colocations we're getting (for * logging only) * * \note The list will be sorted using cmp_primary_priority(). */ void pcmk__add_this_with(GList **list, const pcmk__colocation_t *colocation, const pcmk_resource_t *rsc) { CRM_ASSERT((list != NULL) && (colocation != NULL) && (rsc != NULL)); pcmk__rsc_trace(rsc, "Adding colocation %s (%s with %s using %s @%s) to " "'this with' list for %s", colocation->id, colocation->dependent->id, colocation->primary->id, colocation->node_attribute, pcmk_readable_score(colocation->score), rsc->id); *list = g_list_insert_sorted(*list, (gpointer) colocation, cmp_primary_priority); } /*! * \internal * \brief Add a list of "this with" colocation constraints to a list * * \param[in,out] list List of constraints to add \p addition to * \param[in] addition List of colocation constraints to add to \p list * \param[in] rsc Resource whose colocations we're getting (for * logging only) * * \note The lists must be pre-sorted by cmp_primary_priority(). */ void pcmk__add_this_with_list(GList **list, GList *addition, const pcmk_resource_t *rsc) { CRM_ASSERT((list != NULL) && (rsc != NULL)); pcmk__if_tracing( {}, // Always add each colocation individually if tracing { if (*list == NULL) { // Trivial case for efficiency if not tracing *list = g_list_copy(addition); return; } } ); for (const GList *iter = addition; iter != NULL; iter = iter->next) { pcmk__add_this_with(list, addition->data, rsc); } } /*! * \internal * \brief Add a "with this" colocation constraint to a sorted list * * \param[in,out] list List of constraints to add \p colocation to * \param[in] colocation Colocation constraint to add to \p list * \param[in] rsc Resource whose colocations we're getting (for * logging only) * * \note The list will be sorted using cmp_dependent_priority(). */ void pcmk__add_with_this(GList **list, const pcmk__colocation_t *colocation, const pcmk_resource_t *rsc) { CRM_ASSERT((list != NULL) && (colocation != NULL) && (rsc != NULL)); pcmk__rsc_trace(rsc, "Adding colocation %s (%s with %s using %s @%s) to " "'with this' list for %s", colocation->id, colocation->dependent->id, colocation->primary->id, colocation->node_attribute, pcmk_readable_score(colocation->score), rsc->id); *list = g_list_insert_sorted(*list, (gpointer) colocation, cmp_dependent_priority); } /*! * \internal * \brief Add a list of "with this" colocation constraints to a list * * \param[in,out] list List of constraints to add \p addition to * \param[in] addition List of colocation constraints to add to \p list * \param[in] rsc Resource whose colocations we're getting (for * logging only) * * \note The lists must be pre-sorted by cmp_dependent_priority(). */ void pcmk__add_with_this_list(GList **list, GList *addition, const pcmk_resource_t *rsc) { CRM_ASSERT((list != NULL) && (rsc != NULL)); pcmk__if_tracing( {}, // Always add each colocation individually if tracing { if (*list == NULL) { // Trivial case for efficiency if not tracing *list = g_list_copy(addition); return; } } ); for (const GList *iter = addition; iter != NULL; iter = iter->next) { pcmk__add_with_this(list, addition->data, rsc); } } /*! * \internal * \brief Add orderings necessary for an anti-colocation constraint * * \param[in,out] first_rsc One resource in an anti-colocation * \param[in] first_role Anti-colocation role of \p first_rsc * \param[in] then_rsc Other resource in the anti-colocation * \param[in] then_role Anti-colocation role of \p then_rsc */ static void anti_colocation_order(pcmk_resource_t *first_rsc, int first_role, pcmk_resource_t *then_rsc, int then_role) { const char *first_tasks[] = { NULL, NULL }; const char *then_tasks[] = { NULL, NULL }; /* Actions to make first_rsc lose first_role */ if (first_role == pcmk_role_promoted) { first_tasks[0] = PCMK_ACTION_DEMOTE; } else { first_tasks[0] = PCMK_ACTION_STOP; if (first_role == pcmk_role_unpromoted) { first_tasks[1] = PCMK_ACTION_PROMOTE; } } /* Actions to make then_rsc gain then_role */ if (then_role == pcmk_role_promoted) { then_tasks[0] = PCMK_ACTION_PROMOTE; } else { then_tasks[0] = PCMK_ACTION_START; if (then_role == pcmk_role_unpromoted) { then_tasks[1] = PCMK_ACTION_DEMOTE; } } for (int first_lpc = 0; (first_lpc <= 1) && (first_tasks[first_lpc] != NULL); first_lpc++) { for (int then_lpc = 0; (then_lpc <= 1) && (then_tasks[then_lpc] != NULL); then_lpc++) { pcmk__order_resource_actions(first_rsc, first_tasks[first_lpc], then_rsc, then_tasks[then_lpc], pcmk__ar_if_required_on_same_node); } } } /*! * \internal * \brief Add a new colocation constraint to scheduler data * * \param[in] id XML ID for this constraint * \param[in] node_attr Colocate by this attribute (NULL for #uname) * \param[in] score Constraint score * \param[in,out] dependent Resource to be colocated * \param[in,out] primary Resource to colocate \p dependent with * \param[in] dependent_role Current role of \p dependent * \param[in] primary_role Current role of \p primary * \param[in] flags Group of enum pcmk__coloc_flags */ void pcmk__new_colocation(const char *id, const char *node_attr, int score, pcmk_resource_t *dependent, pcmk_resource_t *primary, const char *dependent_role, const char *primary_role, uint32_t flags) { pcmk__colocation_t *new_con = NULL; CRM_CHECK(id != NULL, return); if ((dependent == NULL) || (primary == NULL)) { pcmk__config_err("Ignoring colocation '%s' because resource " "does not exist", id); return; } if (score == 0) { pcmk__rsc_trace(dependent, "Ignoring colocation '%s' (%s with %s) because score is 0", id, dependent->id, primary->id); return; } new_con = pcmk__assert_alloc(1, sizeof(pcmk__colocation_t)); if (pcmk__str_eq(dependent_role, PCMK_ROLE_STARTED, pcmk__str_null_matches|pcmk__str_casei)) { dependent_role = PCMK__ROLE_UNKNOWN; } if (pcmk__str_eq(primary_role, PCMK_ROLE_STARTED, pcmk__str_null_matches|pcmk__str_casei)) { primary_role = PCMK__ROLE_UNKNOWN; } new_con->id = id; new_con->dependent = dependent; new_con->primary = primary; new_con->score = score; new_con->dependent_role = pcmk_parse_role(dependent_role); new_con->primary_role = pcmk_parse_role(primary_role); new_con->node_attribute = pcmk__s(node_attr, CRM_ATTR_UNAME); new_con->flags = flags; pcmk__add_this_with(&(dependent->rsc_cons), new_con, dependent); pcmk__add_with_this(&(primary->rsc_cons_lhs), new_con, primary); dependent->cluster->colocation_constraints = g_list_prepend( dependent->cluster->colocation_constraints, new_con); if (score <= -PCMK_SCORE_INFINITY) { anti_colocation_order(dependent, new_con->dependent_role, primary, new_con->primary_role); anti_colocation_order(primary, new_con->primary_role, dependent, new_con->dependent_role); } } /*! * \internal * \brief Return the boolean influence corresponding to configuration * * \param[in] coloc_id Colocation XML ID (for error logging) * \param[in] rsc Resource involved in constraint (for default) * \param[in] influence_s String value of \c PCMK_XA_INFLUENCE option * * \return \c pcmk__coloc_influence if string evaluates true, or string is * \c NULL or invalid and resource's \c PCMK_META_CRITICAL option * evaluates true, otherwise \c pcmk__coloc_none */ static uint32_t unpack_influence(const char *coloc_id, const pcmk_resource_t *rsc, const char *influence_s) { if (influence_s != NULL) { int influence_i = 0; if (crm_str_to_boolean(influence_s, &influence_i) < 0) { pcmk__config_err("Constraint '%s' has invalid value for " PCMK_XA_INFLUENCE " (using default)", coloc_id); } else { return (influence_i == 0)? pcmk__coloc_none : pcmk__coloc_influence; } } if (pcmk_is_set(rsc->flags, pcmk_rsc_critical)) { return pcmk__coloc_influence; } return pcmk__coloc_none; } static void unpack_colocation_set(xmlNode *set, int score, const char *coloc_id, const char *influence_s, pcmk_scheduler_t *scheduler) { xmlNode *xml_rsc = NULL; pcmk_resource_t *other = NULL; pcmk_resource_t *resource = NULL; const char *set_id = pcmk__xe_id(set); const char *role = crm_element_value(set, PCMK_XA_ROLE); bool with_previous = false; int local_score = score; bool sequential = false; uint32_t flags = pcmk__coloc_none; const char *xml_rsc_id = NULL; const char *score_s = crm_element_value(set, PCMK_XA_SCORE); if (score_s) { local_score = char2score(score_s); } if (local_score == 0) { crm_trace("Ignoring colocation '%s' for set '%s' because score is 0", coloc_id, set_id); return; } /* @COMPAT The deprecated PCMK__XA_ORDERING attribute specifies whether * resources in a positive-score set are colocated with the previous or next * resource. */ if (pcmk__str_eq(crm_element_value(set, PCMK__XA_ORDERING), PCMK__VALUE_GROUP, pcmk__str_null_matches|pcmk__str_casei)) { with_previous = true; } else { pcmk__warn_once(pcmk__wo_set_ordering, "Support for '" PCMK__XA_ORDERING "' other than" " '" PCMK__VALUE_GROUP "' in " PCMK_XE_RESOURCE_SET " (such as %s) is deprecated and will be removed in a" " future release", set_id); } if ((pcmk__xe_get_bool_attr(set, PCMK_XA_SEQUENTIAL, &sequential) == pcmk_rc_ok) && !sequential) { return; } if (local_score > 0) { for (xml_rsc = pcmk__xe_first_child(set, PCMK_XE_RESOURCE_REF, NULL, NULL); xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) { xml_rsc_id = pcmk__xe_id(xml_rsc); resource = pcmk__find_constraint_resource(scheduler->resources, xml_rsc_id); if (resource == NULL) { // Should be possible only with validation disabled pcmk__config_err("Ignoring %s and later resources in set %s: " "No such resource", xml_rsc_id, set_id); return; } if (other != NULL) { flags = pcmk__coloc_explicit | unpack_influence(coloc_id, resource, influence_s); if (with_previous) { pcmk__rsc_trace(resource, "Colocating %s with %s in set %s", resource->id, other->id, set_id); pcmk__new_colocation(set_id, NULL, local_score, resource, other, role, role, flags); } else { pcmk__rsc_trace(resource, "Colocating %s with %s in set %s", other->id, resource->id, set_id); pcmk__new_colocation(set_id, NULL, local_score, other, resource, role, role, flags); } } other = resource; } } else { /* Anti-colocating with every prior resource is * the only way to ensure the intuitive result * (i.e. that no one in the set can run with anyone else in the set) */ for (xml_rsc = pcmk__xe_first_child(set, PCMK_XE_RESOURCE_REF, NULL, NULL); xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) { xmlNode *xml_rsc_with = NULL; xml_rsc_id = pcmk__xe_id(xml_rsc); resource = pcmk__find_constraint_resource(scheduler->resources, xml_rsc_id); if (resource == NULL) { // Should be possible only with validation disabled pcmk__config_err("Ignoring %s and later resources in set %s: " "No such resource", xml_rsc_id, set_id); return; } flags = pcmk__coloc_explicit | unpack_influence(coloc_id, resource, influence_s); for (xml_rsc_with = pcmk__xe_first_child(set, PCMK_XE_RESOURCE_REF, NULL, NULL); xml_rsc_with != NULL; xml_rsc_with = pcmk__xe_next_same(xml_rsc_with)) { xml_rsc_id = pcmk__xe_id(xml_rsc_with); if (pcmk__str_eq(resource->id, xml_rsc_id, pcmk__str_none)) { break; } other = pcmk__find_constraint_resource(scheduler->resources, xml_rsc_id); CRM_ASSERT(other != NULL); // We already processed it pcmk__new_colocation(set_id, NULL, local_score, resource, other, role, role, flags); } } } } /*! * \internal * \brief Colocate two resource sets relative to each other * * \param[in] id Colocation XML ID * \param[in] set1 Dependent set * \param[in] set2 Primary set * \param[in] score Colocation score * \param[in] influence_s Value of colocation's \c PCMK_XA_INFLUENCE * attribute * \param[in,out] scheduler Scheduler data */ static void colocate_rsc_sets(const char *id, const xmlNode *set1, const xmlNode *set2, int score, const char *influence_s, pcmk_scheduler_t *scheduler) { xmlNode *xml_rsc = NULL; pcmk_resource_t *rsc_1 = NULL; pcmk_resource_t *rsc_2 = NULL; const char *xml_rsc_id = NULL; const char *role_1 = crm_element_value(set1, PCMK_XA_ROLE); const char *role_2 = crm_element_value(set2, PCMK_XA_ROLE); int rc = pcmk_rc_ok; bool sequential = false; uint32_t flags = pcmk__coloc_none; if (score == 0) { crm_trace("Ignoring colocation '%s' between sets %s and %s " "because score is 0", id, pcmk__xe_id(set1), pcmk__xe_id(set2)); return; } rc = pcmk__xe_get_bool_attr(set1, PCMK_XA_SEQUENTIAL, &sequential); if ((rc != pcmk_rc_ok) || sequential) { // Get the first one xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF, NULL, NULL); if (xml_rsc != NULL) { xml_rsc_id = pcmk__xe_id(xml_rsc); rsc_1 = pcmk__find_constraint_resource(scheduler->resources, xml_rsc_id); if (rsc_1 == NULL) { // Should be possible only with validation disabled pcmk__config_err("Ignoring colocation of set %s with set %s " "because first resource %s not found", pcmk__xe_id(set1), pcmk__xe_id(set2), xml_rsc_id); return; } } } rc = pcmk__xe_get_bool_attr(set2, PCMK_XA_SEQUENTIAL, &sequential); if ((rc != pcmk_rc_ok) || sequential) { // Get the last one for (xml_rsc = pcmk__xe_first_child(set2, PCMK_XE_RESOURCE_REF, NULL, NULL); xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) { xml_rsc_id = pcmk__xe_id(xml_rsc); } rsc_2 = pcmk__find_constraint_resource(scheduler->resources, xml_rsc_id); if (rsc_2 == NULL) { // Should be possible only with validation disabled pcmk__config_err("Ignoring colocation of set %s with set %s " "because last resource %s not found", pcmk__xe_id(set1), pcmk__xe_id(set2), xml_rsc_id); return; } } if ((rsc_1 != NULL) && (rsc_2 != NULL)) { // Both sets are sequential flags = pcmk__coloc_explicit | unpack_influence(id, rsc_1, influence_s); pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2, role_1, role_2, flags); } else if (rsc_1 != NULL) { // Only set1 is sequential flags = pcmk__coloc_explicit | unpack_influence(id, rsc_1, influence_s); for (xml_rsc = pcmk__xe_first_child(set2, PCMK_XE_RESOURCE_REF, NULL, NULL); xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) { xml_rsc_id = pcmk__xe_id(xml_rsc); rsc_2 = pcmk__find_constraint_resource(scheduler->resources, xml_rsc_id); if (rsc_2 == NULL) { // Should be possible only with validation disabled pcmk__config_err("Ignoring set %s colocation with resource %s " "in set %s: No such resource", pcmk__xe_id(set1), xml_rsc_id, pcmk__xe_id(set2)); continue; } pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2, role_1, role_2, flags); } } else if (rsc_2 != NULL) { // Only set2 is sequential for (xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF, NULL, NULL); xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) { xml_rsc_id = pcmk__xe_id(xml_rsc); rsc_1 = pcmk__find_constraint_resource(scheduler->resources, xml_rsc_id); if (rsc_1 == NULL) { // Should be possible only with validation disabled pcmk__config_err("Ignoring colocation of set %s resource %s " "with set %s: No such resource", pcmk__xe_id(set1), xml_rsc_id, pcmk__xe_id(set2)); continue; } flags = pcmk__coloc_explicit | unpack_influence(id, rsc_1, influence_s); pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2, role_1, role_2, flags); } } else { // Neither set is sequential for (xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF, NULL, NULL); xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) { xmlNode *xml_rsc_2 = NULL; xml_rsc_id = pcmk__xe_id(xml_rsc); rsc_1 = pcmk__find_constraint_resource(scheduler->resources, xml_rsc_id); if (rsc_1 == NULL) { // Should be possible only with validation disabled pcmk__config_err("Ignoring colocation of set %s resource %s " "with set %s: No such resource", pcmk__xe_id(set1), xml_rsc_id, pcmk__xe_id(set2)); continue; } flags = pcmk__coloc_explicit | unpack_influence(id, rsc_1, influence_s); for (xml_rsc_2 = pcmk__xe_first_child(set2, PCMK_XE_RESOURCE_REF, NULL, NULL); xml_rsc_2 != NULL; xml_rsc_2 = pcmk__xe_next_same(xml_rsc_2)) { xml_rsc_id = pcmk__xe_id(xml_rsc_2); rsc_2 = pcmk__find_constraint_resource(scheduler->resources, xml_rsc_id); if (rsc_2 == NULL) { // Should be possible only with validation disabled pcmk__config_err("Ignoring colocation of set %s resource " "%s with set %s resource %s: No such " "resource", pcmk__xe_id(set1), pcmk__xe_id(xml_rsc), pcmk__xe_id(set2), xml_rsc_id); continue; } pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2, role_1, role_2, flags); } } } } static void unpack_simple_colocation(xmlNode *xml_obj, const char *id, const char *influence_s, pcmk_scheduler_t *scheduler) { int score_i = 0; uint32_t flags = pcmk__coloc_none; const char *score = crm_element_value(xml_obj, PCMK_XA_SCORE); const char *dependent_id = crm_element_value(xml_obj, PCMK_XA_RSC); const char *primary_id = crm_element_value(xml_obj, PCMK_XA_WITH_RSC); const char *dependent_role = crm_element_value(xml_obj, PCMK_XA_RSC_ROLE); const char *primary_role = crm_element_value(xml_obj, PCMK_XA_WITH_RSC_ROLE); const char *attr = crm_element_value(xml_obj, PCMK_XA_NODE_ATTRIBUTE); const char *primary_instance = NULL; const char *dependent_instance = NULL; pcmk_resource_t *primary = NULL; pcmk_resource_t *dependent = NULL; primary = pcmk__find_constraint_resource(scheduler->resources, primary_id); dependent = pcmk__find_constraint_resource(scheduler->resources, dependent_id); // @COMPAT: Deprecated since 2.1.5 primary_instance = crm_element_value(xml_obj, PCMK__XA_WITH_RSC_INSTANCE); dependent_instance = crm_element_value(xml_obj, PCMK__XA_RSC_INSTANCE); if (dependent_instance != NULL) { pcmk__warn_once(pcmk__wo_coloc_inst, "Support for " PCMK__XA_RSC_INSTANCE " is deprecated " "and will be removed in a future release"); } if (primary_instance != NULL) { pcmk__warn_once(pcmk__wo_coloc_inst, "Support for " PCMK__XA_WITH_RSC_INSTANCE " is " "deprecated and will be removed in a future release"); } if (dependent == NULL) { pcmk__config_err("Ignoring constraint '%s' because resource '%s' " "does not exist", id, dependent_id); return; } else if (primary == NULL) { pcmk__config_err("Ignoring constraint '%s' because resource '%s' " "does not exist", id, primary_id); return; } else if ((dependent_instance != NULL) && !pcmk__is_clone(dependent)) { pcmk__config_err("Ignoring constraint '%s' because resource '%s' " "is not a clone but instance '%s' was requested", id, dependent_id, dependent_instance); return; } else if ((primary_instance != NULL) && !pcmk__is_clone(primary)) { pcmk__config_err("Ignoring constraint '%s' because resource '%s' " "is not a clone but instance '%s' was requested", id, primary_id, primary_instance); return; } if (dependent_instance != NULL) { dependent = find_clone_instance(dependent, dependent_instance); if (dependent == NULL) { pcmk__config_warn("Ignoring constraint '%s' because resource '%s' " "does not have an instance '%s'", id, dependent_id, dependent_instance); return; } } if (primary_instance != NULL) { primary = find_clone_instance(primary, primary_instance); if (primary == NULL) { pcmk__config_warn("Ignoring constraint '%s' because resource '%s' " "does not have an instance '%s'", "'%s'", id, primary_id, primary_instance); return; } } if (pcmk__xe_attr_is_true(xml_obj, PCMK_XA_SYMMETRICAL)) { pcmk__config_warn("The colocation constraint " "'" PCMK_XA_SYMMETRICAL "' attribute has been " "removed"); } if (score) { score_i = char2score(score); } flags = pcmk__coloc_explicit | unpack_influence(id, dependent, influence_s); pcmk__new_colocation(id, attr, score_i, dependent, primary, dependent_role, primary_role, flags); } // \return Standard Pacemaker return code static int unpack_colocation_tags(xmlNode *xml_obj, xmlNode **expanded_xml, pcmk_scheduler_t *scheduler) { const char *id = NULL; const char *dependent_id = NULL; const char *primary_id = NULL; const char *dependent_role = NULL; const char *primary_role = NULL; pcmk_resource_t *dependent = NULL; pcmk_resource_t *primary = NULL; pcmk_tag_t *dependent_tag = NULL; pcmk_tag_t *primary_tag = NULL; xmlNode *dependent_set = NULL; xmlNode *primary_set = NULL; bool any_sets = false; *expanded_xml = NULL; CRM_CHECK(xml_obj != NULL, return EINVAL); id = pcmk__xe_id(xml_obj); if (id == NULL) { pcmk__config_err("Ignoring <%s> constraint without " PCMK_XA_ID, xml_obj->name); return pcmk_rc_unpack_error; } // Check whether there are any resource sets with template or tag references *expanded_xml = pcmk__expand_tags_in_sets(xml_obj, scheduler); if (*expanded_xml != NULL) { crm_log_xml_trace(*expanded_xml, "Expanded " PCMK_XE_RSC_COLOCATION); return pcmk_rc_ok; } dependent_id = crm_element_value(xml_obj, PCMK_XA_RSC); primary_id = crm_element_value(xml_obj, PCMK_XA_WITH_RSC); if ((dependent_id == NULL) || (primary_id == NULL)) { return pcmk_rc_ok; } if (!pcmk__valid_resource_or_tag(scheduler, dependent_id, &dependent, &dependent_tag)) { pcmk__config_err("Ignoring constraint '%s' because '%s' is not a " "valid resource or tag", id, dependent_id); return pcmk_rc_unpack_error; } if (!pcmk__valid_resource_or_tag(scheduler, primary_id, &primary, &primary_tag)) { pcmk__config_err("Ignoring constraint '%s' because '%s' is not a " "valid resource or tag", id, primary_id); return pcmk_rc_unpack_error; } if ((dependent != NULL) && (primary != NULL)) { /* Neither side references any template/tag. */ return pcmk_rc_ok; } if ((dependent_tag != NULL) && (primary_tag != NULL)) { // A colocation constraint between two templates/tags makes no sense pcmk__config_err("Ignoring constraint '%s' because two templates or " "tags cannot be colocated", id); return pcmk_rc_unpack_error; } dependent_role = crm_element_value(xml_obj, PCMK_XA_RSC_ROLE); primary_role = crm_element_value(xml_obj, PCMK_XA_WITH_RSC_ROLE); *expanded_xml = pcmk__xml_copy(NULL, xml_obj); /* Convert dependent's template/tag reference into constraint * PCMK_XE_RESOURCE_SET */ if (!pcmk__tag_to_set(*expanded_xml, &dependent_set, PCMK_XA_RSC, true, scheduler)) { free_xml(*expanded_xml); *expanded_xml = NULL; return pcmk_rc_unpack_error; } if (dependent_set != NULL) { if (dependent_role != NULL) { /* Move PCMK_XA_RSC_ROLE into converted PCMK_XE_RESOURCE_SET as * PCMK_XA_ROLE */ crm_xml_add(dependent_set, PCMK_XA_ROLE, dependent_role); pcmk__xe_remove_attr(*expanded_xml, PCMK_XA_RSC_ROLE); } any_sets = true; } /* Convert primary's template/tag reference into constraint * PCMK_XE_RESOURCE_SET */ if (!pcmk__tag_to_set(*expanded_xml, &primary_set, PCMK_XA_WITH_RSC, true, scheduler)) { free_xml(*expanded_xml); *expanded_xml = NULL; return pcmk_rc_unpack_error; } if (primary_set != NULL) { if (primary_role != NULL) { /* Move PCMK_XA_WITH_RSC_ROLE into converted PCMK_XE_RESOURCE_SET as * PCMK_XA_ROLE */ crm_xml_add(primary_set, PCMK_XA_ROLE, primary_role); pcmk__xe_remove_attr(*expanded_xml, PCMK_XA_WITH_RSC_ROLE); } any_sets = true; } if (any_sets) { crm_log_xml_trace(*expanded_xml, "Expanded " PCMK_XE_RSC_COLOCATION); } else { free_xml(*expanded_xml); *expanded_xml = NULL; } return pcmk_rc_ok; } /*! * \internal * \brief Parse a colocation constraint from XML into scheduler data * * \param[in,out] xml_obj Colocation constraint XML to unpack * \param[in,out] scheduler Scheduler data to add constraint to */ void pcmk__unpack_colocation(xmlNode *xml_obj, pcmk_scheduler_t *scheduler) { int score_i = 0; xmlNode *set = NULL; xmlNode *last = NULL; xmlNode *orig_xml = NULL; xmlNode *expanded_xml = NULL; const char *id = crm_element_value(xml_obj, PCMK_XA_ID); const char *score = NULL; const char *influence_s = NULL; if (pcmk__str_empty(id)) { pcmk__config_err("Ignoring " PCMK_XE_RSC_COLOCATION " without " CRM_ATTR_ID); return; } if (unpack_colocation_tags(xml_obj, &expanded_xml, scheduler) != pcmk_rc_ok) { return; } if (expanded_xml != NULL) { orig_xml = xml_obj; xml_obj = expanded_xml; } score = crm_element_value(xml_obj, PCMK_XA_SCORE); if (score != NULL) { score_i = char2score(score); } influence_s = crm_element_value(xml_obj, PCMK_XA_INFLUENCE); for (set = pcmk__xe_first_child(xml_obj, PCMK_XE_RESOURCE_SET, NULL, NULL); set != NULL; set = pcmk__xe_next_same(set)) { set = expand_idref(set, scheduler->input); if (set == NULL) { // Configuration error, message already logged if (expanded_xml != NULL) { free_xml(expanded_xml); } return; } if (pcmk__str_empty(pcmk__xe_id(set))) { pcmk__config_err("Ignoring " PCMK_XE_RESOURCE_SET " without " CRM_ATTR_ID); continue; } unpack_colocation_set(set, score_i, id, influence_s, scheduler); if (last != NULL) { colocate_rsc_sets(id, last, set, score_i, influence_s, scheduler); } last = set; } if (expanded_xml) { free_xml(expanded_xml); xml_obj = orig_xml; } if (last == NULL) { unpack_simple_colocation(xml_obj, id, influence_s, scheduler); } } /*! * \internal * \brief Make actions of a given type unrunnable for a given resource * * \param[in,out] rsc Resource whose actions should be blocked * \param[in] task Name of action to block * \param[in] reason Unrunnable start action causing the block */ static void mark_action_blocked(pcmk_resource_t *rsc, const char *task, const pcmk_resource_t *reason) { GList *iter = NULL; char *reason_text = crm_strdup_printf("colocation with %s", reason->id); for (iter = rsc->actions; iter != NULL; iter = iter->next) { pcmk_action_t *action = iter->data; if (pcmk_is_set(action->flags, pcmk_action_runnable) && pcmk__str_eq(action->task, task, pcmk__str_none)) { pcmk__clear_action_flags(action, pcmk_action_runnable); pe_action_set_reason(action, reason_text, false); pcmk__block_colocation_dependents(action); pcmk__update_action_for_orderings(action, rsc->cluster); } } // If parent resource can't perform an action, neither can any children for (iter = rsc->children; iter != NULL; iter = iter->next) { mark_action_blocked((pcmk_resource_t *) (iter->data), task, reason); } free(reason_text); } /*! * \internal * \brief If an action is unrunnable, block any relevant dependent actions * * If a given action is an unrunnable start or promote, block the start or * promote actions of resources colocated with it, as appropriate to the * colocations' configured roles. * * \param[in,out] action Action to check */ void pcmk__block_colocation_dependents(pcmk_action_t *action) { GList *iter = NULL; GList *colocations = NULL; pcmk_resource_t *rsc = NULL; bool is_start = false; if (pcmk_is_set(action->flags, pcmk_action_runnable)) { return; // Only unrunnable actions block dependents } is_start = pcmk__str_eq(action->task, PCMK_ACTION_START, pcmk__str_none); if (!is_start && !pcmk__str_eq(action->task, PCMK_ACTION_PROMOTE, pcmk__str_none)) { return; // Only unrunnable starts and promotes block dependents } CRM_ASSERT(action->rsc != NULL); // Start and promote are resource actions /* If this resource is part of a collective resource, dependents are blocked * only if all instances of the collective are unrunnable, so check the * collective resource. */ rsc = uber_parent(action->rsc); if (rsc->parent != NULL) { rsc = rsc->parent; // Bundle } // Colocation fails only if entire primary can't reach desired role for (iter = rsc->children; iter != NULL; iter = iter->next) { pcmk_resource_t *child = iter->data; pcmk_action_t *child_action = find_first_action(child->actions, NULL, action->task, NULL); if ((child_action == NULL) || pcmk_is_set(child_action->flags, pcmk_action_runnable)) { crm_trace("Not blocking %s colocation dependents because " "at least %s has runnable %s", rsc->id, child->id, action->task); return; // At least one child can reach desired role } } crm_trace("Blocking %s colocation dependents due to unrunnable %s %s", rsc->id, action->rsc->id, action->task); // Check each colocation where this resource is primary colocations = pcmk__with_this_colocations(rsc); for (iter = colocations; iter != NULL; iter = iter->next) { pcmk__colocation_t *colocation = iter->data; if (colocation->score < PCMK_SCORE_INFINITY) { continue; // Only mandatory colocations block dependent } /* If the primary can't start, the dependent can't reach its colocated * role, regardless of what the primary or dependent colocation role is. * * If the primary can't be promoted, the dependent can't reach its * colocated role if the primary's colocation role is promoted. */ if (!is_start && (colocation->primary_role != pcmk_role_promoted)) { continue; } // Block the dependent from reaching its colocated role if (colocation->dependent_role == pcmk_role_promoted) { mark_action_blocked(colocation->dependent, PCMK_ACTION_PROMOTE, action->rsc); } else { mark_action_blocked(colocation->dependent, PCMK_ACTION_START, action->rsc); } } g_list_free(colocations); } /*! * \internal * \brief Get the resource to use for role comparisons * * A bundle replica includes a container and possibly an instance of the bundled * resource. The dependent in a "with bundle" colocation is colocated with a * particular bundle container. However, if the colocation includes a role, then * the role must be checked on the bundled resource instance inside the * container. The container itself will never be promoted; the bundled resource * may be. * * If the given resource is a bundle replica container, return the resource * inside it, if any. Otherwise, return the resource itself. * * \param[in] rsc Resource to check * * \return Resource to use for role comparisons */ static const pcmk_resource_t * get_resource_for_role(const pcmk_resource_t *rsc) { if (pcmk_is_set(rsc->flags, pcmk_rsc_replica_container)) { const pcmk_resource_t *child = pe__get_rsc_in_container(rsc); if (child != NULL) { return child; } } return rsc; } /*! * \internal * \brief Determine how a colocation constraint should affect a resource * * Colocation constraints have different effects at different points in the * scheduler sequence. Initially, they affect a resource's location; once that * is determined, then for promotable clones they can affect a resource * instance's role; after both are determined, the constraints no longer matter. * Given a specific colocation constraint, check what has been done so far to * determine what should be affected at the current point in the scheduler. * * \param[in] dependent Dependent resource in colocation * \param[in] primary Primary resource in colocation * \param[in] colocation Colocation constraint * \param[in] preview If true, pretend resources have already been assigned * * \return How colocation constraint should be applied at this point */ enum pcmk__coloc_affects pcmk__colocation_affects(const pcmk_resource_t *dependent, const pcmk_resource_t *primary, const pcmk__colocation_t *colocation, bool preview) { const pcmk_resource_t *dependent_role_rsc = NULL; const pcmk_resource_t *primary_role_rsc = NULL; CRM_ASSERT((dependent != NULL) && (primary != NULL) && (colocation != NULL)); if (!preview && pcmk_is_set(primary->flags, pcmk_rsc_unassigned)) { // Primary resource has not been assigned yet, so we can't do anything return pcmk__coloc_affects_nothing; } dependent_role_rsc = get_resource_for_role(dependent); primary_role_rsc = get_resource_for_role(primary); if ((colocation->dependent_role >= pcmk_role_unpromoted) && (dependent_role_rsc->parent != NULL) && pcmk_is_set(dependent_role_rsc->parent->flags, pcmk_rsc_promotable) && !pcmk_is_set(dependent_role_rsc->flags, pcmk_rsc_unassigned)) { /* This is a colocation by role, and the dependent is a promotable clone * that has already been assigned, so the colocation should now affect * the role. */ return pcmk__coloc_affects_role; } if (!preview && !pcmk_is_set(dependent->flags, pcmk_rsc_unassigned)) { /* The dependent resource has already been through assignment, so the * constraint no longer has any effect. Log an error if a mandatory * colocation constraint has been violated. */ const pcmk_node_t *primary_node = primary->allocated_to; if (dependent->allocated_to == NULL) { crm_trace("Skipping colocation '%s': %s will not run anywhere", colocation->id, dependent->id); } else if (colocation->score >= PCMK_SCORE_INFINITY) { // Dependent resource must colocate with primary resource if (!pcmk__same_node(primary_node, dependent->allocated_to)) { pcmk__sched_err("%s must be colocated with %s but is not " "(%s vs. %s)", dependent->id, primary->id, pcmk__node_name(dependent->allocated_to), pcmk__node_name(primary_node)); } } else if (colocation->score <= -PCMK_SCORE_INFINITY) { // Dependent resource must anti-colocate with primary resource if (pcmk__same_node(dependent->allocated_to, primary_node)) { pcmk__sched_err("%s and %s must be anti-colocated but are " "assigned to the same node (%s)", dependent->id, primary->id, pcmk__node_name(primary_node)); } } return pcmk__coloc_affects_nothing; } if ((colocation->dependent_role != pcmk_role_unknown) && (colocation->dependent_role != dependent_role_rsc->next_role)) { crm_trace("Skipping %scolocation '%s': dependent limited to %s role " "but %s next role is %s", ((colocation->score < 0)? "anti-" : ""), colocation->id, pcmk_role_text(colocation->dependent_role), dependent_role_rsc->id, pcmk_role_text(dependent_role_rsc->next_role)); return pcmk__coloc_affects_nothing; } if ((colocation->primary_role != pcmk_role_unknown) && (colocation->primary_role != primary_role_rsc->next_role)) { crm_trace("Skipping %scolocation '%s': primary limited to %s role " "but %s next role is %s", ((colocation->score < 0)? "anti-" : ""), colocation->id, pcmk_role_text(colocation->primary_role), primary_role_rsc->id, pcmk_role_text(primary_role_rsc->next_role)); return pcmk__coloc_affects_nothing; } return pcmk__coloc_affects_location; } /*! * \internal * \brief Apply colocation to dependent for assignment purposes * * Update the allowed node scores of the dependent resource in a colocation, * for the purposes of assigning it to a node. * * \param[in,out] dependent Dependent resource in colocation * \param[in] primary Primary resource in colocation * \param[in] colocation Colocation constraint */ void pcmk__apply_coloc_to_scores(pcmk_resource_t *dependent, const pcmk_resource_t *primary, const pcmk__colocation_t *colocation) { const char *attr = colocation->node_attribute; const char *value = NULL; GHashTable *work = NULL; GHashTableIter iter; pcmk_node_t *node = NULL; if (primary->allocated_to != NULL) { value = pcmk__colocation_node_attr(primary->allocated_to, attr, primary); } else if (colocation->score < 0) { // Nothing to do (anti-colocation with something that is not running) return; } work = pcmk__copy_node_table(dependent->allowed_nodes); g_hash_table_iter_init(&iter, work); while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) { if (primary->allocated_to == NULL) { node->weight = pcmk__add_scores(-colocation->score, node->weight); pcmk__rsc_trace(dependent, "Applied %s to %s score on %s (now %s after " "subtracting %s because primary %s inactive)", colocation->id, dependent->id, pcmk__node_name(node), pcmk_readable_score(node->weight), pcmk_readable_score(colocation->score), primary->id); continue; } if (pcmk__str_eq(pcmk__colocation_node_attr(node, attr, dependent), value, pcmk__str_casei)) { /* Add colocation score only if optional (or minus infinity). A * mandatory colocation is a requirement rather than a preference, * so we don't need to consider it for relative assignment purposes. * The resource will simply be forbidden from running on the node if * the primary isn't active there (via the condition above). */ if (colocation->score < PCMK_SCORE_INFINITY) { node->weight = pcmk__add_scores(colocation->score, node->weight); pcmk__rsc_trace(dependent, "Applied %s to %s score on %s (now %s after " "adding %s)", colocation->id, dependent->id, pcmk__node_name(node), pcmk_readable_score(node->weight), pcmk_readable_score(colocation->score)); } continue; } if (colocation->score >= PCMK_SCORE_INFINITY) { /* Only mandatory colocations are relevant when the colocation * attribute doesn't match, because an attribute not matching is not * a negative preference -- the colocation is simply relevant only * where it matches. */ node->weight = -PCMK_SCORE_INFINITY; pcmk__rsc_trace(dependent, "Banned %s from %s because colocation %s attribute %s " "does not match", dependent->id, pcmk__node_name(node), colocation->id, attr); } } if ((colocation->score <= -PCMK_SCORE_INFINITY) || (colocation->score >= PCMK_SCORE_INFINITY) || pcmk__any_node_available(work)) { g_hash_table_destroy(dependent->allowed_nodes); dependent->allowed_nodes = work; work = NULL; } else { pcmk__rsc_info(dependent, "%s: Rolling back scores from %s (no available nodes)", dependent->id, primary->id); } if (work != NULL) { g_hash_table_destroy(work); } } /*! * \internal * \brief Apply colocation to dependent for role purposes * * Update the priority of the dependent resource in a colocation, for the * purposes of selecting its role * * \param[in,out] dependent Dependent resource in colocation * \param[in] primary Primary resource in colocation * \param[in] colocation Colocation constraint + * + * \return The score added to the dependent's priority */ -void +int pcmk__apply_coloc_to_priority(pcmk_resource_t *dependent, const pcmk_resource_t *primary, const pcmk__colocation_t *colocation) { const char *dependent_value = NULL; const char *primary_value = NULL; const char *attr = colocation->node_attribute; int score_multiplier = 1; + int priority_delta = 0; - const pcmk_resource_t *primary_role_rsc = NULL; + CRM_ASSERT((dependent != NULL) && (primary != NULL) + && (colocation != NULL)); - CRM_ASSERT((dependent != NULL) && (primary != NULL) && - (colocation != NULL)); + if (dependent->allocated_to == NULL) { + return 0; + } - if ((primary->allocated_to == NULL) || (dependent->allocated_to == NULL)) { - return; + if ((primary->allocated_to != NULL) + && (colocation->primary_role != pcmk_role_unknown)) { + /* Colocation applies only if the primary's next role matches. + * + * If primary->allocated_to == NULL, we want to proceed past this block, + * so that dependent->allocated_to is marked ineligible for promotion. + * + * @TODO Why ignore a mandatory colocation in this case when we apply + * its negation in the mismatched value case? + */ + const pcmk_resource_t *role_rsc = get_resource_for_role(primary); + + if (colocation->primary_role != role_rsc->next_role) { + return 0; + } } dependent_value = pcmk__colocation_node_attr(dependent->allocated_to, attr, dependent); primary_value = pcmk__colocation_node_attr(primary->allocated_to, attr, primary); - primary_role_rsc = get_resource_for_role(primary); - if (!pcmk__str_eq(dependent_value, primary_value, pcmk__str_casei)) { if ((colocation->score == PCMK_SCORE_INFINITY) && (colocation->dependent_role == pcmk_role_promoted)) { - dependent->priority = -PCMK_SCORE_INFINITY; - } - return; - } - if ((colocation->primary_role != pcmk_role_unknown) - && (colocation->primary_role != primary_role_rsc->next_role)) { - return; - } + /* For a mandatory promoted-role colocation, mark the dependent node + * ineligible to promote the dependent if its attribute value + * doesn't match the primary node's + */ + score_multiplier = -1; + + } else { + // Otherwise, ignore the colocation if attribute values don't match + return 0; + } - if (colocation->dependent_role == pcmk_role_unpromoted) { + } else if (colocation->dependent_role == pcmk_role_unpromoted) { + /* Node attribute values matched, so we want to avoid promoting the + * dependent on this node + */ score_multiplier = -1; } - dependent->priority = pcmk__add_scores(score_multiplier * colocation->score, - dependent->priority); + priority_delta = score_multiplier * colocation->score; + dependent->priority = pcmk__add_scores(priority_delta, dependent->priority); pcmk__rsc_trace(dependent, - "Applied %s to %s promotion priority (now %s after %s %s)", + "Applied %s to %s promotion priority (now %s after %s %d)", colocation->id, dependent->id, pcmk_readable_score(dependent->priority), ((score_multiplier == 1)? "adding" : "subtracting"), - pcmk_readable_score(colocation->score)); + colocation->score); + + return priority_delta; } /*! * \internal * \brief Find score of highest-scored node that matches colocation attribute * * \param[in] colocation Colocation constraint being applied * \param[in,out] rsc Resource whose allowed nodes should be searched * \param[in] attr Colocation attribute name (must not be NULL) * \param[in] value Colocation attribute value to require */ static int best_node_score_matching_attr(const pcmk__colocation_t *colocation, pcmk_resource_t *rsc, const char *attr, const char *value) { GHashTable *allowed_nodes_orig = NULL; GHashTableIter iter; pcmk_node_t *node = NULL; int best_score = -PCMK_SCORE_INFINITY; const char *best_node = NULL; if ((colocation != NULL) && (rsc == colocation->dependent) && pcmk_is_set(colocation->flags, pcmk__coloc_explicit) && pcmk__is_group(rsc->parent) && (rsc != rsc->parent->children->data)) { /* The resource is a user-configured colocation's explicit dependent, * and a group member other than the first, which means the group's * location constraint scores were not applied to it (see * pcmk__group_apply_location()). Explicitly consider those scores now. * * @TODO This does leave one suboptimal case: if the group itself or * another member other than the first is explicitly colocated with * the same primary, the primary will count the group's location scores * multiple times. This is much less likely than a single member being * explicitly colocated, so it's an acceptable tradeoff for now. */ allowed_nodes_orig = rsc->allowed_nodes; rsc->allowed_nodes = pcmk__copy_node_table(allowed_nodes_orig); for (GList *loc_iter = rsc->cluster->placement_constraints; loc_iter != NULL; loc_iter = loc_iter->next) { pcmk__location_t *location = loc_iter->data; if (location->rsc == rsc->parent) { rsc->cmds->apply_location(rsc, location); } } } // Find best allowed node with matching attribute g_hash_table_iter_init(&iter, rsc->allowed_nodes); while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) { if ((node->weight > best_score) && pcmk__node_available(node, false, false) && pcmk__str_eq(value, pcmk__colocation_node_attr(node, attr, rsc), pcmk__str_casei)) { best_score = node->weight; best_node = node->details->uname; } } if (!pcmk__str_eq(attr, CRM_ATTR_UNAME, pcmk__str_none)) { if (best_node == NULL) { crm_info("No allowed node for %s matches node attribute %s=%s", rsc->id, attr, value); } else { crm_info("Allowed node %s for %s had best score (%d) " "of those matching node attribute %s=%s", best_node, rsc->id, best_score, attr, value); } } if (allowed_nodes_orig != NULL) { g_hash_table_destroy(rsc->allowed_nodes); rsc->allowed_nodes = allowed_nodes_orig; } return best_score; } /*! * \internal * \brief Check whether a resource is allowed only on a single node * * \param[in] rsc Resource to check * * \return \c true if \p rsc is allowed only on one node, otherwise \c false */ static bool allowed_on_one(const pcmk_resource_t *rsc) { GHashTableIter iter; pcmk_node_t *allowed_node = NULL; int allowed_nodes = 0; g_hash_table_iter_init(&iter, rsc->allowed_nodes); while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &allowed_node)) { if ((allowed_node->weight >= 0) && (++allowed_nodes > 1)) { pcmk__rsc_trace(rsc, "%s is allowed on multiple nodes", rsc->id); return false; } } pcmk__rsc_trace(rsc, "%s is allowed %s", rsc->id, ((allowed_nodes == 1)? "on a single node" : "nowhere")); return (allowed_nodes == 1); } /*! * \internal * \brief Add resource's colocation matches to current node assignment scores * * For each node in a given table, if any of a given resource's allowed nodes * have a matching value for the colocation attribute, add the highest of those * nodes' scores to the node's score. * * \param[in,out] nodes Table of nodes with assignment scores so far * \param[in,out] source_rsc Resource whose node scores to add * \param[in] target_rsc Resource on whose behalf to update \p nodes * \param[in] colocation Original colocation constraint (used to get * configured primary resource's stickiness, and * to get colocation node attribute; pass NULL to * ignore stickiness and use default attribute) * \param[in] factor Factor by which to multiply scores being added * \param[in] only_positive Whether to add only positive scores */ static void add_node_scores_matching_attr(GHashTable *nodes, pcmk_resource_t *source_rsc, const pcmk_resource_t *target_rsc, const pcmk__colocation_t *colocation, float factor, bool only_positive) { GHashTableIter iter; pcmk_node_t *node = NULL; const char *attr = colocation->node_attribute; // Iterate through each node g_hash_table_iter_init(&iter, nodes); while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) { float delta_f = 0; int delta = 0; int score = 0; int new_score = 0; const char *value = pcmk__colocation_node_attr(node, attr, target_rsc); score = best_node_score_matching_attr(colocation, source_rsc, attr, value); if ((factor < 0) && (score < 0)) { /* If the dependent is anti-colocated, we generally don't want the * primary to prefer nodes that the dependent avoids. That could * lead to unnecessary shuffling of the primary when the dependent * hits its migration threshold somewhere, for example. * * However, there are cases when it is desirable. If the dependent * can't run anywhere but where the primary is, it would be * worthwhile to move the primary for the sake of keeping the * dependent active. * * We can't know that exactly at this point since we don't know * where the primary will be assigned, but we can limit considering * the preference to when the dependent is allowed only on one node. * This is less than ideal for multiple reasons: * * - the dependent could be allowed on more than one node but have * anti-colocation primaries on each; * - the dependent could be a clone or bundle with multiple * instances, and the dependent as a whole is allowed on multiple * nodes but some instance still can't run * - the dependent has considered node-specific criteria such as * location constraints and stickiness by this point, but might * have other factors that end up disallowing a node * * but the alternative is making the primary move when it doesn't * need to. * * We also consider the primary's stickiness and influence, so the * user has some say in the matter. (This is the configured primary, * not a particular instance of the primary, but that doesn't matter * unless stickiness uses a rule to vary by node, and that seems * acceptable to ignore.) */ if ((colocation->primary->stickiness >= -score) || !pcmk__colocation_has_influence(colocation, NULL) || !allowed_on_one(colocation->dependent)) { crm_trace("%s: Filtering %d + %f * %d " "(double negative disallowed)", pcmk__node_name(node), node->weight, factor, score); continue; } } if (node->weight == INFINITY_HACK) { crm_trace("%s: Filtering %d + %f * %d (node was marked unusable)", pcmk__node_name(node), node->weight, factor, score); continue; } delta_f = factor * score; // Round the number; see http://c-faq.com/fp/round.html delta = (int) ((delta_f < 0)? (delta_f - 0.5) : (delta_f + 0.5)); /* Small factors can obliterate the small scores that are often actually * used in configurations. If the score and factor are nonzero, ensure * that the result is nonzero as well. */ if ((delta == 0) && (score != 0)) { if (factor > 0.0) { delta = 1; } else if (factor < 0.0) { delta = -1; } } new_score = pcmk__add_scores(delta, node->weight); if (only_positive && (new_score < 0) && (node->weight > 0)) { crm_trace("%s: Filtering %d + %f * %d = %d " "(negative disallowed, marking node unusable)", pcmk__node_name(node), node->weight, factor, score, new_score); node->weight = INFINITY_HACK; continue; } if (only_positive && (new_score < 0) && (node->weight == 0)) { crm_trace("%s: Filtering %d + %f * %d = %d (negative disallowed)", pcmk__node_name(node), node->weight, factor, score, new_score); continue; } crm_trace("%s: %d + %f * %d = %d", pcmk__node_name(node), node->weight, factor, score, new_score); node->weight = new_score; } } /*! * \internal * \brief Update nodes with scores of colocated resources' nodes * * Given a table of nodes and a resource, update the nodes' scores with the * scores of the best nodes matching the attribute used for each of the * resource's relevant colocations. * * \param[in,out] source_rsc Resource whose node scores to add * \param[in] target_rsc Resource on whose behalf to update \p *nodes * \param[in] log_id Resource ID for logs (if \c NULL, use * \p source_rsc ID) * \param[in,out] nodes Nodes to update (set initial contents to \c NULL * to copy allowed nodes from \p source_rsc) * \param[in] colocation Original colocation constraint (used to get * configured primary resource's stickiness, and * to get colocation node attribute; if \c NULL, * source_rsc's own matching node scores * will not be added, and \p *nodes must be \c NULL * as well) * \param[in] factor Incorporate scores multiplied by this factor * \param[in] flags Bitmask of enum pcmk__coloc_select values * * \note \c NULL \p target_rsc, \c NULL \p *nodes, \c NULL \p colocation, and * the \c pcmk__coloc_select_this_with flag are used together (and only by * \c cmp_resources()). * \note The caller remains responsible for freeing \p *nodes. * \note This is the shared implementation of * \c pcmk_assignment_methods_t:add_colocated_node_scores(). */ void pcmk__add_colocated_node_scores(pcmk_resource_t *source_rsc, const pcmk_resource_t *target_rsc, const char *log_id, GHashTable **nodes, const pcmk__colocation_t *colocation, float factor, uint32_t flags) { GHashTable *work = NULL; CRM_ASSERT((source_rsc != NULL) && (nodes != NULL) && ((colocation != NULL) || ((target_rsc == NULL) && (*nodes == NULL)))); if (log_id == NULL) { log_id = source_rsc->id; } // Avoid infinite recursion if (pcmk_is_set(source_rsc->flags, pcmk_rsc_updating_nodes)) { pcmk__rsc_info(source_rsc, "%s: Breaking dependency loop at %s", log_id, source_rsc->id); return; } pcmk__set_rsc_flags(source_rsc, pcmk_rsc_updating_nodes); if (*nodes == NULL) { work = pcmk__copy_node_table(source_rsc->allowed_nodes); target_rsc = source_rsc; } else { const bool pos = pcmk_is_set(flags, pcmk__coloc_select_nonnegative); pcmk__rsc_trace(source_rsc, "%s: Merging %s scores from %s (at %.6f)", log_id, (pos? "positive" : "all"), source_rsc->id, factor); work = pcmk__copy_node_table(*nodes); add_node_scores_matching_attr(work, source_rsc, target_rsc, colocation, factor, pos); } if (work == NULL) { pcmk__clear_rsc_flags(source_rsc, pcmk_rsc_updating_nodes); return; } if (pcmk__any_node_available(work)) { GList *colocations = NULL; if (pcmk_is_set(flags, pcmk__coloc_select_this_with)) { colocations = pcmk__this_with_colocations(source_rsc); pcmk__rsc_trace(source_rsc, "Checking additional %d optional '%s with' " "constraints", g_list_length(colocations), source_rsc->id); } else { colocations = pcmk__with_this_colocations(source_rsc); pcmk__rsc_trace(source_rsc, "Checking additional %d optional 'with %s' " "constraints", g_list_length(colocations), source_rsc->id); } flags |= pcmk__coloc_select_active; for (GList *iter = colocations; iter != NULL; iter = iter->next) { pcmk__colocation_t *constraint = iter->data; pcmk_resource_t *other = NULL; float other_factor = factor * constraint->score / (float) PCMK_SCORE_INFINITY; if (pcmk_is_set(flags, pcmk__coloc_select_this_with)) { other = constraint->primary; } else if (!pcmk__colocation_has_influence(constraint, NULL)) { continue; } else { other = constraint->dependent; } pcmk__rsc_trace(source_rsc, "Optionally merging score of '%s' constraint " "(%s with %s)", constraint->id, constraint->dependent->id, constraint->primary->id); other->cmds->add_colocated_node_scores(other, target_rsc, log_id, &work, constraint, other_factor, flags); pe__show_node_scores(true, NULL, log_id, work, source_rsc->cluster); } g_list_free(colocations); } else if (pcmk_is_set(flags, pcmk__coloc_select_active)) { pcmk__rsc_info(source_rsc, "%s: Rolling back optional scores from %s", log_id, source_rsc->id); g_hash_table_destroy(work); pcmk__clear_rsc_flags(source_rsc, pcmk_rsc_updating_nodes); return; } if (pcmk_is_set(flags, pcmk__coloc_select_nonnegative)) { pcmk_node_t *node = NULL; GHashTableIter iter; g_hash_table_iter_init(&iter, work); while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) { if (node->weight == INFINITY_HACK) { node->weight = 1; } } } if (*nodes != NULL) { g_hash_table_destroy(*nodes); } *nodes = work; pcmk__clear_rsc_flags(source_rsc, pcmk_rsc_updating_nodes); } /*! * \internal * \brief Apply a "with this" colocation to a resource's allowed node scores * * \param[in,out] data Colocation to apply * \param[in,out] user_data Resource being assigned */ void pcmk__add_dependent_scores(gpointer data, gpointer user_data) { pcmk__colocation_t *colocation = data; pcmk_resource_t *target_rsc = user_data; pcmk_resource_t *source_rsc = colocation->dependent; const float factor = colocation->score / (float) PCMK_SCORE_INFINITY; uint32_t flags = pcmk__coloc_select_active; if (!pcmk__colocation_has_influence(colocation, NULL)) { return; } if (pcmk__is_clone(target_rsc)) { flags |= pcmk__coloc_select_nonnegative; } pcmk__rsc_trace(target_rsc, "%s: Incorporating attenuated %s assignment scores due " "to colocation %s", target_rsc->id, source_rsc->id, colocation->id); source_rsc->cmds->add_colocated_node_scores(source_rsc, target_rsc, source_rsc->id, &target_rsc->allowed_nodes, colocation, factor, flags); } /*! * \internal * \brief Exclude nodes from a dependent's node table if not in a given list * * Given a dependent resource in a colocation and a list of nodes where the * primary resource will run, set a node's score to \c -INFINITY in the * dependent's node table if not found in the primary nodes list. * * \param[in,out] dependent Dependent resource * \param[in] primary Primary resource (for logging only) * \param[in] colocation Colocation constraint (for logging only) * \param[in] primary_nodes List of nodes where the primary will have * unblocked instances in a suitable role * \param[in] merge_scores If \c true and a node is found in both \p table * and \p list, add the node's score in \p list to * the node's score in \p table */ void pcmk__colocation_intersect_nodes(pcmk_resource_t *dependent, const pcmk_resource_t *primary, const pcmk__colocation_t *colocation, const GList *primary_nodes, bool merge_scores) { GHashTableIter iter; pcmk_node_t *dependent_node = NULL; CRM_ASSERT((dependent != NULL) && (primary != NULL) && (colocation != NULL)); g_hash_table_iter_init(&iter, dependent->allowed_nodes); while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &dependent_node)) { const pcmk_node_t *primary_node = NULL; primary_node = pe_find_node_id(primary_nodes, dependent_node->details->id); if (primary_node == NULL) { dependent_node->weight = -PCMK_SCORE_INFINITY; pcmk__rsc_trace(dependent, "Banning %s from %s (no primary instance) for %s", dependent->id, pcmk__node_name(dependent_node), colocation->id); } else if (merge_scores) { dependent_node->weight = pcmk__add_scores(dependent_node->weight, primary_node->weight); pcmk__rsc_trace(dependent, "Added %s's score %s to %s's score for %s (now %s) " "for colocation %s", primary->id, pcmk_readable_score(primary_node->weight), dependent->id, pcmk__node_name(dependent_node), pcmk_readable_score(dependent_node->weight), colocation->id); } } } /*! * \internal * \brief Get all colocations affecting a resource as the primary * * \param[in] rsc Resource to get colocations for * * \return Newly allocated list of colocations affecting \p rsc as primary * * \note This is a convenience wrapper for the with_this_colocations() method. */ GList * pcmk__with_this_colocations(const pcmk_resource_t *rsc) { GList *list = NULL; rsc->cmds->with_this_colocations(rsc, rsc, &list); return list; } /*! * \internal * \brief Get all colocations affecting a resource as the dependent * * \param[in] rsc Resource to get colocations for * * \return Newly allocated list of colocations affecting \p rsc as dependent * * \note This is a convenience wrapper for the this_with_colocations() method. */ GList * pcmk__this_with_colocations(const pcmk_resource_t *rsc) { GList *list = NULL; rsc->cmds->this_with_colocations(rsc, rsc, &list); return list; } diff --git a/lib/pacemaker/pcmk_sched_group.c b/lib/pacemaker/pcmk_sched_group.c index 68596f9628..6d892b2c12 100644 --- a/lib/pacemaker/pcmk_sched_group.c +++ b/lib/pacemaker/pcmk_sched_group.c @@ -1,959 +1,1003 @@ /* * Copyright 2004-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU General Public License version 2 * or later (GPLv2+) WITHOUT ANY WARRANTY. */ #include #include +#include // QB_ABS() + #include #include #include "libpacemaker_private.h" /*! * \internal * \brief Assign a group resource to a node * * \param[in,out] rsc Group resource to assign to a node * \param[in] prefer Node to prefer, if all else is equal * \param[in] stop_if_fail If \c true and a child of \p rsc can't be * assigned to a node, set the child's next role to * stopped and update existing actions * * \return Node that \p rsc is assigned to, if assigned entirely to one node * * \note If \p stop_if_fail is \c false, then \c pcmk__unassign_resource() can * completely undo the assignment. A successful assignment can be either * undone or left alone as final. A failed assignment has the same effect * as calling pcmk__unassign_resource(); there are no side effects on * roles or actions. */ pcmk_node_t * pcmk__group_assign(pcmk_resource_t *rsc, const pcmk_node_t *prefer, bool stop_if_fail) { pcmk_node_t *first_assigned_node = NULL; pcmk_resource_t *first_member = NULL; CRM_ASSERT(pcmk__is_group(rsc)); if (!pcmk_is_set(rsc->flags, pcmk_rsc_unassigned)) { return rsc->allocated_to; // Assignment already done } if (pcmk_is_set(rsc->flags, pcmk_rsc_assigning)) { pcmk__rsc_debug(rsc, "Assignment dependency loop detected involving %s", rsc->id); return NULL; } if (rsc->children == NULL) { // No members to assign pcmk__clear_rsc_flags(rsc, pcmk_rsc_unassigned); return NULL; } pcmk__set_rsc_flags(rsc, pcmk_rsc_assigning); first_member = (pcmk_resource_t *) rsc->children->data; rsc->role = first_member->role; pe__show_node_scores(!pcmk_is_set(rsc->cluster->flags, pcmk_sched_output_scores), rsc, __func__, rsc->allowed_nodes, rsc->cluster); for (GList *iter = rsc->children; iter != NULL; iter = iter->next) { pcmk_resource_t *member = (pcmk_resource_t *) iter->data; pcmk_node_t *node = NULL; pcmk__rsc_trace(rsc, "Assigning group %s member %s", rsc->id, member->id); node = member->cmds->assign(member, prefer, stop_if_fail); if (first_assigned_node == NULL) { first_assigned_node = node; } } pe__set_next_role(rsc, first_member->next_role, "first group member"); pcmk__clear_rsc_flags(rsc, pcmk_rsc_assigning|pcmk_rsc_unassigned); if (!pe__group_flag_is_set(rsc, pcmk__group_colocated)) { return NULL; } return first_assigned_node; } /*! * \internal * \brief Create a pseudo-operation for a group as an ordering point * * \param[in,out] group Group resource to create action for * \param[in] action Action name * * \return Newly created pseudo-operation */ static pcmk_action_t * create_group_pseudo_op(pcmk_resource_t *group, const char *action) { pcmk_action_t *op = custom_action(group, pcmk__op_key(group->id, action, 0), action, NULL, TRUE, group->cluster); pcmk__set_action_flags(op, pcmk_action_pseudo|pcmk_action_runnable); return op; } /*! * \internal * \brief Create all actions needed for a given group resource * * \param[in,out] rsc Group resource to create actions for */ void pcmk__group_create_actions(pcmk_resource_t *rsc) { CRM_ASSERT(pcmk__is_group(rsc)); pcmk__rsc_trace(rsc, "Creating actions for group %s", rsc->id); // Create actions for individual group members for (GList *iter = rsc->children; iter != NULL; iter = iter->next) { pcmk_resource_t *member = (pcmk_resource_t *) iter->data; member->cmds->create_actions(member); } // Create pseudo-actions for group itself to serve as ordering points create_group_pseudo_op(rsc, PCMK_ACTION_START); create_group_pseudo_op(rsc, PCMK_ACTION_RUNNING); create_group_pseudo_op(rsc, PCMK_ACTION_STOP); create_group_pseudo_op(rsc, PCMK_ACTION_STOPPED); if (crm_is_true(g_hash_table_lookup(rsc->meta, PCMK_META_PROMOTABLE))) { create_group_pseudo_op(rsc, PCMK_ACTION_DEMOTE); create_group_pseudo_op(rsc, PCMK_ACTION_DEMOTED); create_group_pseudo_op(rsc, PCMK_ACTION_PROMOTE); create_group_pseudo_op(rsc, PCMK_ACTION_PROMOTED); } } // User data for member_internal_constraints() struct member_data { // These could be derived from member but this avoids some function calls bool ordered; bool colocated; bool promotable; pcmk_resource_t *last_active; pcmk_resource_t *previous_member; }; /*! * \internal * \brief Create implicit constraints needed for a group member * * \param[in,out] data Group member to create implicit constraints for * \param[in,out] user_data Member data (struct member_data *) */ static void member_internal_constraints(gpointer data, gpointer user_data) { pcmk_resource_t *member = (pcmk_resource_t *) data; struct member_data *member_data = (struct member_data *) user_data; // For ordering demote vs demote or stop vs stop uint32_t down_flags = pcmk__ar_then_implies_first_graphed; // For ordering demote vs demoted or stop vs stopped uint32_t post_down_flags = pcmk__ar_first_implies_then_graphed; // Create the individual member's implicit constraints member->cmds->internal_constraints(member); if (member_data->previous_member == NULL) { // This is first member if (member_data->ordered) { pcmk__set_relation_flags(down_flags, pcmk__ar_ordered); post_down_flags = pcmk__ar_first_implies_then; } } else if (member_data->colocated) { uint32_t flags = pcmk__coloc_none; if (pcmk_is_set(member->flags, pcmk_rsc_critical)) { flags |= pcmk__coloc_influence; } // Colocate this member with the previous one pcmk__new_colocation("#group-members", NULL, PCMK_SCORE_INFINITY, member, member_data->previous_member, NULL, NULL, flags); } if (member_data->promotable) { // Demote group -> demote member -> group is demoted pcmk__order_resource_actions(member->parent, PCMK_ACTION_DEMOTE, member, PCMK_ACTION_DEMOTE, down_flags); pcmk__order_resource_actions(member, PCMK_ACTION_DEMOTE, member->parent, PCMK_ACTION_DEMOTED, post_down_flags); // Promote group -> promote member -> group is promoted pcmk__order_resource_actions(member, PCMK_ACTION_PROMOTE, member->parent, PCMK_ACTION_PROMOTED, pcmk__ar_unrunnable_first_blocks |pcmk__ar_first_implies_then |pcmk__ar_first_implies_then_graphed); pcmk__order_resource_actions(member->parent, PCMK_ACTION_PROMOTE, member, PCMK_ACTION_PROMOTE, pcmk__ar_then_implies_first_graphed); } // Stop group -> stop member -> group is stopped pcmk__order_stops(member->parent, member, down_flags); pcmk__order_resource_actions(member, PCMK_ACTION_STOP, member->parent, PCMK_ACTION_STOPPED, post_down_flags); // Start group -> start member -> group is started pcmk__order_starts(member->parent, member, pcmk__ar_then_implies_first_graphed); pcmk__order_resource_actions(member, PCMK_ACTION_START, member->parent, PCMK_ACTION_RUNNING, pcmk__ar_unrunnable_first_blocks |pcmk__ar_first_implies_then |pcmk__ar_first_implies_then_graphed); if (!member_data->ordered) { pcmk__order_starts(member->parent, member, pcmk__ar_first_implies_then |pcmk__ar_unrunnable_first_blocks |pcmk__ar_then_implies_first_graphed); if (member_data->promotable) { pcmk__order_resource_actions(member->parent, PCMK_ACTION_PROMOTE, member, PCMK_ACTION_PROMOTE, pcmk__ar_first_implies_then |pcmk__ar_unrunnable_first_blocks |pcmk__ar_then_implies_first_graphed); } } else if (member_data->previous_member == NULL) { pcmk__order_starts(member->parent, member, pcmk__ar_none); if (member_data->promotable) { pcmk__order_resource_actions(member->parent, PCMK_ACTION_PROMOTE, member, PCMK_ACTION_PROMOTE, pcmk__ar_none); } } else { // Order this member relative to the previous one pcmk__order_starts(member_data->previous_member, member, pcmk__ar_first_implies_then |pcmk__ar_unrunnable_first_blocks); pcmk__order_stops(member, member_data->previous_member, pcmk__ar_ordered|pcmk__ar_intermediate_stop); /* In unusual circumstances (such as adding a new member to the middle * of a group with unmanaged later members), this member may be active * while the previous (new) member is inactive. In this situation, the * usual restart orderings will be irrelevant, so we need to order this * member's stop before the previous member's start. */ if ((member->running_on != NULL) && (member_data->previous_member->running_on == NULL)) { pcmk__order_resource_actions(member, PCMK_ACTION_STOP, member_data->previous_member, PCMK_ACTION_START, pcmk__ar_then_implies_first |pcmk__ar_unrunnable_first_blocks); } if (member_data->promotable) { pcmk__order_resource_actions(member_data->previous_member, PCMK_ACTION_PROMOTE, member, PCMK_ACTION_PROMOTE, pcmk__ar_first_implies_then |pcmk__ar_unrunnable_first_blocks); pcmk__order_resource_actions(member, PCMK_ACTION_DEMOTE, member_data->previous_member, PCMK_ACTION_DEMOTE, pcmk__ar_ordered); } } // Make sure partially active groups shut down in sequence if (member->running_on != NULL) { if (member_data->ordered && (member_data->previous_member != NULL) && (member_data->previous_member->running_on == NULL) && (member_data->last_active != NULL) && (member_data->last_active->running_on != NULL)) { pcmk__order_stops(member, member_data->last_active, pcmk__ar_ordered); } member_data->last_active = member; } member_data->previous_member = member; } /*! * \internal * \brief Create implicit constraints needed for a group resource * * \param[in,out] rsc Group resource to create implicit constraints for */ void pcmk__group_internal_constraints(pcmk_resource_t *rsc) { struct member_data member_data = { false, }; const pcmk_resource_t *top = NULL; CRM_ASSERT(pcmk__is_group(rsc)); /* Order group pseudo-actions relative to each other for restarting: * stop group -> group is stopped -> start group -> group is started */ pcmk__order_resource_actions(rsc, PCMK_ACTION_STOP, rsc, PCMK_ACTION_STOPPED, pcmk__ar_unrunnable_first_blocks); pcmk__order_resource_actions(rsc, PCMK_ACTION_STOPPED, rsc, PCMK_ACTION_START, pcmk__ar_ordered); pcmk__order_resource_actions(rsc, PCMK_ACTION_START, rsc, PCMK_ACTION_RUNNING, pcmk__ar_unrunnable_first_blocks); top = pe__const_top_resource(rsc, false); member_data.ordered = pe__group_flag_is_set(rsc, pcmk__group_ordered); member_data.colocated = pe__group_flag_is_set(rsc, pcmk__group_colocated); member_data.promotable = pcmk_is_set(top->flags, pcmk_rsc_promotable); g_list_foreach(rsc->children, member_internal_constraints, &member_data); } /*! * \internal * \brief Apply a colocation's score to node scores or resource priority * * Given a colocation constraint for a group with some other resource, apply the * score to the dependent's allowed node scores (if we are still placing * resources) or priority (if we are choosing promotable clone instance roles). * * \param[in,out] dependent Dependent group resource in colocation * \param[in] primary Primary resource in colocation * \param[in] colocation Colocation constraint to apply + * + * \return The score added to the dependent's priority */ -static void +static int colocate_group_with(pcmk_resource_t *dependent, const pcmk_resource_t *primary, const pcmk__colocation_t *colocation) { - pcmk_resource_t *member = NULL; + int priority_delta = 0; if (dependent->children == NULL) { - return; + return 0; } pcmk__rsc_trace(primary, "Processing %s (group %s with %s) for dependent", colocation->id, dependent->id, primary->id); if (pe__group_flag_is_set(dependent, pcmk__group_colocated)) { // Colocate first member (internal colocations will handle the rest) - member = (pcmk_resource_t *) dependent->children->data; - member->cmds->apply_coloc_score(member, primary, colocation, true); - return; - } + pcmk_resource_t *member = dependent->children->data; - if (colocation->score >= PCMK_SCORE_INFINITY) { - pcmk__config_err("%s: Cannot perform mandatory colocation between " - "non-colocated group and %s", - dependent->id, primary->id); - return; + priority_delta = member->cmds->apply_coloc_score(member, primary, + colocation, true); + + } else { + if (colocation->score >= PCMK_SCORE_INFINITY) { + pcmk__config_err("%s: Cannot perform mandatory colocation between " + "non-colocated group and %s", + dependent->id, primary->id); + return 0; + } + + // Colocate each member individually + for (GList *iter = dependent->children; iter != NULL; + iter = iter->next) { + + pcmk_resource_t *member = iter->data; + int instance_delta = member->cmds->apply_coloc_score(member, + primary, + colocation, + false); + + /* priority_delta is used for determining which instances of a + * promotable clone to promote. It's possible that colocations + * involving promotable cloned non-colocated groups may not behave + * correctly in all circumstances. Non-colocated groups are + * deprecated, and testing focused on colocated groups. + */ + priority_delta = pcmk__add_scores(priority_delta, instance_delta); + } } - // Colocate each member individually - for (GList *iter = dependent->children; iter != NULL; iter = iter->next) { - member = (pcmk_resource_t *) iter->data; - member->cmds->apply_coloc_score(member, primary, colocation, true); + if (priority_delta != 0) { + dependent->priority = pcmk__add_scores(priority_delta, + dependent->priority); + + pcmk__rsc_trace(dependent, + "Applied %s to %s promotion priority " + "(now %s after %s %d)", + colocation->id, dependent->id, + pcmk_readable_score(dependent->priority), + ((priority_delta > 0)? "adding" : "subtracting"), + QB_ABS(priority_delta)); } + return priority_delta; } /*! * \internal * \brief Apply a colocation's score to node scores or resource priority * * Given a colocation constraint for some other resource with a group, apply the * score to the dependent's allowed node scores (if we are still placing * resources) or priority (if we are choosing promotable clone instance roles). * * \param[in,out] dependent Dependent resource in colocation * \param[in] primary Primary group resource in colocation * \param[in] colocation Colocation constraint to apply + * + * \return The score added to the dependent's priority */ -static void +static int colocate_with_group(pcmk_resource_t *dependent, const pcmk_resource_t *primary, const pcmk__colocation_t *colocation) { + int priority_delta = 0; const pcmk_resource_t *member = NULL; pcmk__rsc_trace(primary, "Processing colocation %s (%s with group %s) for primary", colocation->id, dependent->id, primary->id); if (pcmk_is_set(primary->flags, pcmk_rsc_unassigned)) { - return; + return 0; } if (pe__group_flag_is_set(primary, pcmk__group_colocated)) { if (colocation->score >= PCMK_SCORE_INFINITY) { /* For mandatory colocations, the entire group must be assignable * (and in the specified role if any), so apply the colocation based * on the last member. */ member = pe__last_group_member(primary); } else if (primary->children != NULL) { /* For optional colocations, whether the group is partially or fully * up doesn't matter, so apply the colocation based on the first * member. */ member = (pcmk_resource_t *) primary->children->data; } if (member == NULL) { - return; // Nothing to colocate with + return 0; // Nothing to colocate with } - member->cmds->apply_coloc_score(dependent, member, colocation, false); - return; + return member->cmds->apply_coloc_score(dependent, member, colocation, + false); } if (colocation->score >= PCMK_SCORE_INFINITY) { pcmk__config_err("%s: Cannot perform mandatory colocation with" " non-colocated group %s", dependent->id, primary->id); - return; + return 0; } // Colocate dependent with each member individually for (const GList *iter = primary->children; iter != NULL; iter = iter->next) { + + int instance_delta = 0; + member = iter->data; - member->cmds->apply_coloc_score(dependent, member, colocation, false); + instance_delta = member->cmds->apply_coloc_score(dependent, member, + colocation, false); + priority_delta = pcmk__add_scores(priority_delta, instance_delta); } + return priority_delta; } /*! * \internal * \brief Apply a colocation's score to node scores or resource priority * * Given a colocation constraint, apply its score to the dependent's * allowed node scores (if we are still placing resources) or priority (if * we are choosing promotable clone instance roles). * * \param[in,out] dependent Dependent resource in colocation * \param[in] primary Primary resource in colocation * \param[in] colocation Colocation constraint to apply * \param[in] for_dependent true if called on behalf of dependent + * + * \return The score added to the dependent's priority */ -void +int pcmk__group_apply_coloc_score(pcmk_resource_t *dependent, const pcmk_resource_t *primary, const pcmk__colocation_t *colocation, bool for_dependent) { CRM_ASSERT((dependent != NULL) && (primary != NULL) && (colocation != NULL)); if (for_dependent) { - colocate_group_with(dependent, primary, colocation); + return colocate_group_with(dependent, primary, colocation); } else { // Method should only be called for primitive dependents CRM_ASSERT(pcmk__is_primitive(dependent)); - colocate_with_group(dependent, primary, colocation); + return colocate_with_group(dependent, primary, colocation); } } /*! * \internal * \brief Return action flags for a given group resource action * * \param[in,out] action Group action to get flags for * \param[in] node If not NULL, limit effects to this node * * \return Flags appropriate to \p action on \p node */ uint32_t pcmk__group_action_flags(pcmk_action_t *action, const pcmk_node_t *node) { // Default flags for a group action uint32_t flags = pcmk_action_optional |pcmk_action_runnable |pcmk_action_pseudo; CRM_ASSERT(action != NULL); // Update flags considering each member's own flags for same action for (GList *iter = action->rsc->children; iter != NULL; iter = iter->next) { pcmk_resource_t *member = (pcmk_resource_t *) iter->data; // Check whether member has the same action enum action_tasks task = get_complex_task(member, action->task); const char *task_s = pcmk_action_text(task); pcmk_action_t *member_action = find_first_action(member->actions, NULL, task_s, node); if (member_action != NULL) { uint32_t member_flags = member->cmds->action_flags(member_action, node); // Group action is mandatory if any member action is if (pcmk_is_set(flags, pcmk_action_optional) && !pcmk_is_set(member_flags, pcmk_action_optional)) { pcmk__rsc_trace(action->rsc, "%s is mandatory because %s is", action->uuid, member_action->uuid); pcmk__clear_raw_action_flags(flags, "group action", pcmk_action_optional); pcmk__clear_action_flags(action, pcmk_action_optional); } // Group action is unrunnable if any member action is if (!pcmk__str_eq(task_s, action->task, pcmk__str_none) && pcmk_is_set(flags, pcmk_action_runnable) && !pcmk_is_set(member_flags, pcmk_action_runnable)) { pcmk__rsc_trace(action->rsc, "%s is unrunnable because %s is", action->uuid, member_action->uuid); pcmk__clear_raw_action_flags(flags, "group action", pcmk_action_runnable); pcmk__clear_action_flags(action, pcmk_action_runnable); } /* Group (pseudo-)actions other than stop or demote are unrunnable * unless every member will do it. */ } else if ((task != pcmk_action_stop) && (task != pcmk_action_demote)) { pcmk__rsc_trace(action->rsc, "%s is not runnable because %s will not %s", action->uuid, member->id, task_s); pcmk__clear_raw_action_flags(flags, "group action", pcmk_action_runnable); } } return flags; } /*! * \internal * \brief Update two actions according to an ordering between them * * Given information about an ordering of two actions, update the actions' flags * (and runnable_before members if appropriate) as appropriate for the ordering. * Effects may cascade to other orderings involving the actions as well. * * \param[in,out] first 'First' action in an ordering * \param[in,out] then 'Then' action in an ordering * \param[in] node If not NULL, limit scope of ordering to this node * (only used when interleaving instances) * \param[in] flags Action flags for \p first for ordering purposes * \param[in] filter Action flags to limit scope of certain updates (may * include pcmk_action_optional to affect only * mandatory actions, and pcmk_action_runnable to * affect only runnable actions) * \param[in] type Group of enum pcmk__action_relation_flags to apply * \param[in,out] scheduler Scheduler data * * \return Group of enum pcmk__updated flags indicating what was updated */ uint32_t pcmk__group_update_ordered_actions(pcmk_action_t *first, pcmk_action_t *then, const pcmk_node_t *node, uint32_t flags, uint32_t filter, uint32_t type, pcmk_scheduler_t *scheduler) { uint32_t changed = pcmk__updated_none; // Group method can be called only on behalf of "then" action CRM_ASSERT((first != NULL) && (then != NULL) && (then->rsc != NULL) && (scheduler != NULL)); // Update the actions for the group itself changed |= pcmk__update_ordered_actions(first, then, node, flags, filter, type, scheduler); // Update the actions for each group member for (GList *iter = then->rsc->children; iter != NULL; iter = iter->next) { pcmk_resource_t *member = (pcmk_resource_t *) iter->data; pcmk_action_t *member_action = find_first_action(member->actions, NULL, then->task, node); if (member_action != NULL) { changed |= member->cmds->update_ordered_actions(first, member_action, node, flags, filter, type, scheduler); } } return changed; } /*! * \internal * \brief Apply a location constraint to a group's allowed node scores * * \param[in,out] rsc Group resource to apply constraint to * \param[in,out] location Location constraint to apply */ void pcmk__group_apply_location(pcmk_resource_t *rsc, pcmk__location_t *location) { GList *node_list_orig = NULL; GList *node_list_copy = NULL; CRM_ASSERT(pcmk__is_group(rsc) && (location != NULL)); // Save the constraint's original node list (with the constraint score) node_list_orig = location->nodes; // Make a copy of the nodes with all zero scores node_list_copy = pcmk__copy_node_list(node_list_orig, true); /* Apply the constraint to the group itself. This ensures that any nodes * affected by the constraint are in the group's allowed nodes, with the * constraint score added. */ pcmk__apply_location(rsc, location); // Apply the constraint for each member for (GList *iter = rsc->children; iter != NULL; iter = iter->next) { pcmk_resource_t *member = (pcmk_resource_t *) iter->data; if (pe__group_flag_is_set(rsc, pcmk__group_colocated) && (iter != rsc->children)) { /* When apply_location() is called below for the first member (iter * == rsc->children), the constraint score will be added to * the member's affected allowed nodes. * * For subsequent members, we reset the constraint's node table to * the copy with all 0 scores. Otherwise, when assigning the member, * the constraint score would be counted multiple times (once for * each later member) due to internal group colocations. Though the * 0 score will not affect these members' allowed node scores, it * ensures that affected nodes are in each member's allowed nodes, * enabling the member on those nodes in asymmetric clusters. */ location->nodes = node_list_copy; } member->cmds->apply_location(member, location); } location->nodes = node_list_orig; g_list_free_full(node_list_copy, free); } // Group implementation of pcmk_assignment_methods_t:colocated_resources() GList * pcmk__group_colocated_resources(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList *colocated_rscs) { const pcmk_resource_t *member = NULL; CRM_ASSERT(pcmk__is_group(rsc)); if (orig_rsc == NULL) { orig_rsc = rsc; } if (pe__group_flag_is_set(rsc, pcmk__group_colocated) || pcmk__is_clone(rsc->parent)) { /* This group has colocated members and/or is cloned -- either way, * add every child's colocated resources to the list. The first and last * members will include the group's own colocations. */ colocated_rscs = g_list_prepend(colocated_rscs, (gpointer) rsc); for (const GList *iter = rsc->children; iter != NULL; iter = iter->next) { member = (const pcmk_resource_t *) iter->data; colocated_rscs = member->cmds->colocated_resources(member, orig_rsc, colocated_rscs); } } else if (rsc->children != NULL) { /* This group's members are not colocated, and the group is not cloned, * so just add the group's own colocations to the list. */ colocated_rscs = pcmk__colocated_resources(rsc, orig_rsc, colocated_rscs); } return colocated_rscs; } // Group implementation of pcmk_assignment_methods_t:with_this_colocations() void pcmk__with_group_colocations(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList **list) { CRM_ASSERT((orig_rsc != NULL) && (list != NULL) && pcmk__is_group(rsc)); // Ignore empty groups if (rsc->children == NULL) { return; } /* "With this" colocations are needed only for the group itself and for its * last member. (Previous members will chain via the group internal * colocations.) */ if ((orig_rsc != rsc) && (orig_rsc != pe__last_group_member(rsc))) { return; } pcmk__rsc_trace(rsc, "Adding 'with %s' colocations to list for %s", rsc->id, orig_rsc->id); // Add the group's own colocations pcmk__add_with_this_list(list, rsc->rsc_cons_lhs, orig_rsc); // If cloned, add any relevant colocations with the clone if (rsc->parent != NULL) { rsc->parent->cmds->with_this_colocations(rsc->parent, orig_rsc, list); } if (!pe__group_flag_is_set(rsc, pcmk__group_colocated)) { // @COMPAT Non-colocated groups are deprecated return; } // Add explicit colocations with the group's (other) children for (const GList *iter = rsc->children; iter != NULL; iter = iter->next) { const pcmk_resource_t *member = iter->data; if (member != orig_rsc) { member->cmds->with_this_colocations(member, orig_rsc, list); } } } // Group implementation of pcmk_assignment_methods_t:this_with_colocations() void pcmk__group_with_colocations(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList **list) { const pcmk_resource_t *member = NULL; CRM_ASSERT((orig_rsc != NULL) && (list != NULL) && pcmk__is_group(rsc)); // Ignore empty groups if (rsc->children == NULL) { return; } /* "This with" colocations are normally needed only for the group itself and * for its first member. */ if ((rsc == orig_rsc) || (orig_rsc == (const pcmk_resource_t *) rsc->children->data)) { pcmk__rsc_trace(rsc, "Adding '%s with' colocations to list for %s", rsc->id, orig_rsc->id); // Add the group's own colocations pcmk__add_this_with_list(list, rsc->rsc_cons, orig_rsc); // If cloned, add any relevant colocations involving the clone if (rsc->parent != NULL) { rsc->parent->cmds->this_with_colocations(rsc->parent, orig_rsc, list); } if (!pe__group_flag_is_set(rsc, pcmk__group_colocated)) { // @COMPAT Non-colocated groups are deprecated return; } // Add explicit colocations involving the group's (other) children for (const GList *iter = rsc->children; iter != NULL; iter = iter->next) { member = iter->data; if (member != orig_rsc) { member->cmds->this_with_colocations(member, orig_rsc, list); } } return; } /* Later group members honor the group's colocations indirectly, due to the * internal group colocations that chain everything from the first member. * However, if an earlier group member is unmanaged, this chaining will not * happen, so the group's mandatory colocations must be explicitly added. */ for (const GList *iter = rsc->children; iter != NULL; iter = iter->next) { member = iter->data; if (orig_rsc == member) { break; // We've seen all earlier members, and none are unmanaged } if (!pcmk_is_set(member->flags, pcmk_rsc_managed)) { crm_trace("Adding mandatory '%s with' colocations to list for " "member %s because earlier member %s is unmanaged", rsc->id, orig_rsc->id, member->id); for (const GList *cons_iter = rsc->rsc_cons; cons_iter != NULL; cons_iter = cons_iter->next) { const pcmk__colocation_t *colocation = NULL; colocation = (const pcmk__colocation_t *) cons_iter->data; if (colocation->score == PCMK_SCORE_INFINITY) { pcmk__add_this_with(list, colocation, orig_rsc); } } // @TODO Add mandatory (or all?) clone constraints if cloned break; } } } /*! * \internal * \brief Update nodes with scores of colocated resources' nodes * * Given a table of nodes and a resource, update the nodes' scores with the * scores of the best nodes matching the attribute used for each of the * resource's relevant colocations. * * \param[in,out] source_rsc Group resource whose node scores to add * \param[in] target_rsc Resource on whose behalf to update \p *nodes * \param[in] log_id Resource ID for logs (if \c NULL, use * \p source_rsc ID) * \param[in,out] nodes Nodes to update (set initial contents to \c NULL * to copy allowed nodes from \p source_rsc) * \param[in] colocation Original colocation constraint (used to get * configured primary resource's stickiness, and * to get colocation node attribute; if \c NULL, * source_rsc's own matching node scores will * not be added, and \p *nodes must be \c NULL as * well) * \param[in] factor Incorporate scores multiplied by this factor * \param[in] flags Bitmask of enum pcmk__coloc_select values * * \note \c NULL \p target_rsc, \c NULL \p *nodes, \c NULL \p colocation, and * the \c pcmk__coloc_select_this_with flag are used together (and only by * \c cmp_resources()). * \note The caller remains responsible for freeing \p *nodes. * \note This is the group implementation of * \c pcmk_assignment_methods_t:add_colocated_node_scores(). */ void pcmk__group_add_colocated_node_scores(pcmk_resource_t *source_rsc, const pcmk_resource_t *target_rsc, const char *log_id, GHashTable **nodes, const pcmk__colocation_t *colocation, float factor, uint32_t flags) { pcmk_resource_t *member = NULL; CRM_ASSERT(pcmk__is_group(source_rsc) && (nodes != NULL) && ((colocation != NULL) || ((target_rsc == NULL) && (*nodes == NULL)))); if (log_id == NULL) { log_id = source_rsc->id; } // Avoid infinite recursion if (pcmk_is_set(source_rsc->flags, pcmk_rsc_updating_nodes)) { pcmk__rsc_info(source_rsc, "%s: Breaking dependency loop at %s", log_id, source_rsc->id); return; } pcmk__set_rsc_flags(source_rsc, pcmk_rsc_updating_nodes); // Ignore empty groups (only possible with schema validation disabled) if (source_rsc->children == NULL) { return; } /* Refer the operation to the first or last member as appropriate. * * cmp_resources() is the only caller that passes a NULL nodes table, * and is also the only caller using pcmk__coloc_select_this_with. * For "this with" colocations, the last member will recursively incorporate * all the other members' "this with" colocations via the internal group * colocations (and via the first member, the group's own colocations). * * For "with this" colocations, the first member works similarly. */ if (*nodes == NULL) { member = pe__last_group_member(source_rsc); } else { member = source_rsc->children->data; } pcmk__rsc_trace(source_rsc, "%s: Merging scores from group %s using member %s " "(at %.6f)", log_id, source_rsc->id, member->id, factor); member->cmds->add_colocated_node_scores(member, target_rsc, log_id, nodes, colocation, factor, flags); pcmk__clear_rsc_flags(source_rsc, pcmk_rsc_updating_nodes); } // Group implementation of pcmk_assignment_methods_t:add_utilization() void pcmk__group_add_utilization(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList *all_rscs, GHashTable *utilization) { pcmk_resource_t *member = NULL; CRM_ASSERT((orig_rsc != NULL) && (utilization != NULL) && pcmk__is_group(rsc)); if (!pcmk_is_set(rsc->flags, pcmk_rsc_unassigned)) { return; } pcmk__rsc_trace(orig_rsc, "%s: Adding group %s as colocated utilization", orig_rsc->id, rsc->id); if (pe__group_flag_is_set(rsc, pcmk__group_colocated) || pcmk__is_clone(rsc->parent)) { // Every group member will be on same node, so sum all members for (GList *iter = rsc->children; iter != NULL; iter = iter->next) { member = (pcmk_resource_t *) iter->data; if (pcmk_is_set(member->flags, pcmk_rsc_unassigned) && (g_list_find(all_rscs, member) == NULL)) { member->cmds->add_utilization(member, orig_rsc, all_rscs, utilization); } } } else if (rsc->children != NULL) { // Just add first member's utilization member = (pcmk_resource_t *) rsc->children->data; if ((member != NULL) && pcmk_is_set(member->flags, pcmk_rsc_unassigned) && (g_list_find(all_rscs, member) == NULL)) { member->cmds->add_utilization(member, orig_rsc, all_rscs, utilization); } } } void pcmk__group_shutdown_lock(pcmk_resource_t *rsc) { CRM_ASSERT(pcmk__is_group(rsc)); for (GList *iter = rsc->children; iter != NULL; iter = iter->next) { pcmk_resource_t *member = (pcmk_resource_t *) iter->data; member->cmds->shutdown_lock(member); } } diff --git a/lib/pacemaker/pcmk_sched_primitive.c b/lib/pacemaker/pcmk_sched_primitive.c index a113ccaeef..40c4b3ab6e 100644 --- a/lib/pacemaker/pcmk_sched_primitive.c +++ b/lib/pacemaker/pcmk_sched_primitive.c @@ -1,1677 +1,1681 @@ /* * Copyright 2004-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU General Public License version 2 * or later (GPLv2+) WITHOUT ANY WARRANTY. */ #include #include #include // uint8_t, uint32_t #include #include #include "libpacemaker_private.h" static void stop_resource(pcmk_resource_t *rsc, pcmk_node_t *node, bool optional); static void start_resource(pcmk_resource_t *rsc, pcmk_node_t *node, bool optional); static void demote_resource(pcmk_resource_t *rsc, pcmk_node_t *node, bool optional); static void promote_resource(pcmk_resource_t *rsc, pcmk_node_t *node, bool optional); static void assert_role_error(pcmk_resource_t *rsc, pcmk_node_t *node, bool optional); #define RSC_ROLE_MAX (pcmk_role_promoted + 1) static enum rsc_role_e rsc_state_matrix[RSC_ROLE_MAX][RSC_ROLE_MAX] = { /* This array lists the immediate next role when transitioning from one role * to a target role. For example, when going from Stopped to Promoted, the * next role is Unpromoted, because the resource must be started before it * can be promoted. The current state then becomes Started, which is fed * into this array again, giving a next role of Promoted. * * Current role Immediate next role Final target role * ------------ ------------------- ----------------- */ /* Unknown */ { pcmk_role_unknown, /* Unknown */ pcmk_role_stopped, /* Stopped */ pcmk_role_stopped, /* Started */ pcmk_role_stopped, /* Unpromoted */ pcmk_role_stopped, /* Promoted */ }, /* Stopped */ { pcmk_role_stopped, /* Unknown */ pcmk_role_stopped, /* Stopped */ pcmk_role_started, /* Started */ pcmk_role_unpromoted, /* Unpromoted */ pcmk_role_unpromoted, /* Promoted */ }, /* Started */ { pcmk_role_stopped, /* Unknown */ pcmk_role_stopped, /* Stopped */ pcmk_role_started, /* Started */ pcmk_role_unpromoted, /* Unpromoted */ pcmk_role_promoted, /* Promoted */ }, /* Unpromoted */ { pcmk_role_stopped, /* Unknown */ pcmk_role_stopped, /* Stopped */ pcmk_role_stopped, /* Started */ pcmk_role_unpromoted, /* Unpromoted */ pcmk_role_promoted, /* Promoted */ }, /* Promoted */ { pcmk_role_stopped, /* Unknown */ pcmk_role_unpromoted, /* Stopped */ pcmk_role_unpromoted, /* Started */ pcmk_role_unpromoted, /* Unpromoted */ pcmk_role_promoted, /* Promoted */ }, }; /*! * \internal * \brief Function to schedule actions needed for a role change * * \param[in,out] rsc Resource whose role is changing * \param[in,out] node Node where resource will be in its next role * \param[in] optional Whether scheduled actions should be optional */ typedef void (*rsc_transition_fn)(pcmk_resource_t *rsc, pcmk_node_t *node, bool optional); static rsc_transition_fn rsc_action_matrix[RSC_ROLE_MAX][RSC_ROLE_MAX] = { /* This array lists the function needed to transition directly from one role * to another. NULL indicates that nothing is needed. * * Current role Transition function Next role * ------------ ------------------- ---------- */ /* Unknown */ { assert_role_error, /* Unknown */ stop_resource, /* Stopped */ assert_role_error, /* Started */ assert_role_error, /* Unpromoted */ assert_role_error, /* Promoted */ }, /* Stopped */ { assert_role_error, /* Unknown */ NULL, /* Stopped */ start_resource, /* Started */ start_resource, /* Unpromoted */ assert_role_error, /* Promoted */ }, /* Started */ { assert_role_error, /* Unknown */ stop_resource, /* Stopped */ NULL, /* Started */ NULL, /* Unpromoted */ promote_resource, /* Promoted */ }, /* Unpromoted */ { assert_role_error, /* Unknown */ stop_resource, /* Stopped */ stop_resource, /* Started */ NULL, /* Unpromoted */ promote_resource, /* Promoted */ }, /* Promoted */ { assert_role_error, /* Unknown */ demote_resource, /* Stopped */ demote_resource, /* Started */ demote_resource, /* Unpromoted */ NULL, /* Promoted */ }, }; /*! * \internal * \brief Get a list of a resource's allowed nodes sorted by node score * * \param[in] rsc Resource to check * * \return List of allowed nodes sorted by node score */ static GList * sorted_allowed_nodes(const pcmk_resource_t *rsc) { if (rsc->allowed_nodes != NULL) { GList *nodes = g_hash_table_get_values(rsc->allowed_nodes); if (nodes != NULL) { return pcmk__sort_nodes(nodes, pcmk__current_node(rsc)); } } return NULL; } /*! * \internal * \brief Assign a resource to its best allowed node, if possible * * \param[in,out] rsc Resource to choose a node for * \param[in] prefer If not \c NULL, prefer this node when all else * equal * \param[in] stop_if_fail If \c true and \p rsc can't be assigned to a * node, set next role to stopped and update * existing actions * * \return true if \p rsc could be assigned to a node, otherwise false * * \note If \p stop_if_fail is \c false, then \c pcmk__unassign_resource() can * completely undo the assignment. A successful assignment can be either * undone or left alone as final. A failed assignment has the same effect * as calling pcmk__unassign_resource(); there are no side effects on * roles or actions. */ static bool assign_best_node(pcmk_resource_t *rsc, const pcmk_node_t *prefer, bool stop_if_fail) { GList *nodes = NULL; pcmk_node_t *chosen = NULL; pcmk_node_t *best = NULL; const pcmk_node_t *most_free_node = pcmk__ban_insufficient_capacity(rsc); if (prefer == NULL) { prefer = most_free_node; } if (!pcmk_is_set(rsc->flags, pcmk_rsc_unassigned)) { // We've already finished assignment of resources to nodes return rsc->allocated_to != NULL; } // Sort allowed nodes by score nodes = sorted_allowed_nodes(rsc); if (nodes != NULL) { best = (pcmk_node_t *) nodes->data; // First node has best score } if ((prefer != NULL) && (nodes != NULL)) { // Get the allowed node version of prefer chosen = g_hash_table_lookup(rsc->allowed_nodes, prefer->details->id); if (chosen == NULL) { pcmk__rsc_trace(rsc, "Preferred node %s for %s was unknown", pcmk__node_name(prefer), rsc->id); /* Favor the preferred node as long as its score is at least as good as * the best allowed node's. * * An alternative would be to favor the preferred node even if the best * node is better, when the best node's score is less than INFINITY. */ } else if (chosen->weight < best->weight) { pcmk__rsc_trace(rsc, "Preferred node %s for %s was unsuitable", pcmk__node_name(chosen), rsc->id); chosen = NULL; } else if (!pcmk__node_available(chosen, true, false)) { pcmk__rsc_trace(rsc, "Preferred node %s for %s was unavailable", pcmk__node_name(chosen), rsc->id); chosen = NULL; } else { pcmk__rsc_trace(rsc, "Chose preferred node %s for %s " "(ignoring %d candidates)", pcmk__node_name(chosen), rsc->id, g_list_length(nodes)); } } if ((chosen == NULL) && (best != NULL)) { /* Either there is no preferred node, or the preferred node is not * suitable, but another node is allowed to run the resource. */ chosen = best; if (!pcmk__is_unique_clone(rsc->parent) && (chosen->weight > 0) // Zero not acceptable && pcmk__node_available(chosen, false, false)) { /* If the resource is already running on a node, prefer that node if * it is just as good as the chosen node. * * We don't do this for unique clone instances, because * pcmk__assign_instances() has already assigned instances to their * running nodes when appropriate, and if we get here, we don't want * remaining unassigned instances to prefer a node that's already * running another instance. */ pcmk_node_t *running = pcmk__current_node(rsc); if (running == NULL) { // Nothing to do } else if (!pcmk__node_available(running, true, false)) { pcmk__rsc_trace(rsc, "Current node for %s (%s) can't run resources", rsc->id, pcmk__node_name(running)); } else { int nodes_with_best_score = 1; for (GList *iter = nodes->next; iter; iter = iter->next) { pcmk_node_t *allowed = (pcmk_node_t *) iter->data; if (allowed->weight != chosen->weight) { // The nodes are sorted by score, so no more are equal break; } if (pcmk__same_node(allowed, running)) { // Scores are equal, so prefer the current node chosen = allowed; } nodes_with_best_score++; } if (nodes_with_best_score > 1) { uint8_t log_level = LOG_INFO; if (chosen->weight >= PCMK_SCORE_INFINITY) { log_level = LOG_WARNING; } do_crm_log(log_level, "Chose %s for %s from %d nodes with score %s", pcmk__node_name(chosen), rsc->id, nodes_with_best_score, pcmk_readable_score(chosen->weight)); } } } pcmk__rsc_trace(rsc, "Chose %s for %s from %d candidates", pcmk__node_name(chosen), rsc->id, g_list_length(nodes)); } pcmk__assign_resource(rsc, chosen, false, stop_if_fail); g_list_free(nodes); return rsc->allocated_to != NULL; } /*! * \internal * \brief Apply a "this with" colocation to a node's allowed node scores * * \param[in,out] colocation Colocation to apply * \param[in,out] rsc Resource being assigned */ static void apply_this_with(pcmk__colocation_t *colocation, pcmk_resource_t *rsc) { GHashTable *archive = NULL; pcmk_resource_t *other = colocation->primary; // In certain cases, we will need to revert the node scores if ((colocation->dependent_role >= pcmk_role_promoted) || ((colocation->score < 0) && (colocation->score > -PCMK_SCORE_INFINITY))) { archive = pcmk__copy_node_table(rsc->allowed_nodes); } if (pcmk_is_set(other->flags, pcmk_rsc_unassigned)) { pcmk__rsc_trace(rsc, "%s: Assigning colocation %s primary %s first" "(score=%d role=%s)", rsc->id, colocation->id, other->id, colocation->score, pcmk_role_text(colocation->dependent_role)); other->cmds->assign(other, NULL, true); } // Apply the colocation score to this resource's allowed node scores rsc->cmds->apply_coloc_score(rsc, other, colocation, true); if ((archive != NULL) && !pcmk__any_node_available(rsc->allowed_nodes)) { pcmk__rsc_info(rsc, "%s: Reverting scores from colocation with %s " "because no nodes allowed", rsc->id, other->id); g_hash_table_destroy(rsc->allowed_nodes); rsc->allowed_nodes = archive; archive = NULL; } if (archive != NULL) { g_hash_table_destroy(archive); } } /*! * \internal * \brief Update a Pacemaker Remote node once its connection has been assigned * * \param[in] connection Connection resource that has been assigned */ static void remote_connection_assigned(const pcmk_resource_t *connection) { pcmk_node_t *remote_node = pcmk_find_node(connection->cluster, connection->id); CRM_CHECK(remote_node != NULL, return); if ((connection->allocated_to != NULL) && (connection->next_role != pcmk_role_stopped)) { crm_trace("Pacemaker Remote node %s will be online", remote_node->details->id); remote_node->details->online = TRUE; if (remote_node->details->unseen) { // Avoid unnecessary fence, since we will attempt connection remote_node->details->unclean = FALSE; } } else { crm_trace("Pacemaker Remote node %s will be shut down " "(%sassigned connection's next role is %s)", remote_node->details->id, ((connection->allocated_to == NULL)? "un" : ""), pcmk_role_text(connection->next_role)); remote_node->details->shutdown = TRUE; } } /*! * \internal * \brief Assign a primitive resource to a node * * \param[in,out] rsc Resource to assign to a node * \param[in] prefer Node to prefer, if all else is equal * \param[in] stop_if_fail If \c true and \p rsc can't be assigned to a * node, set next role to stopped and update * existing actions * * \return Node that \p rsc is assigned to, if assigned entirely to one node * * \note If \p stop_if_fail is \c false, then \c pcmk__unassign_resource() can * completely undo the assignment. A successful assignment can be either * undone or left alone as final. A failed assignment has the same effect * as calling pcmk__unassign_resource(); there are no side effects on * roles or actions. */ pcmk_node_t * pcmk__primitive_assign(pcmk_resource_t *rsc, const pcmk_node_t *prefer, bool stop_if_fail) { GList *this_with_colocations = NULL; GList *with_this_colocations = NULL; GList *iter = NULL; pcmk__colocation_t *colocation = NULL; CRM_ASSERT(pcmk__is_primitive(rsc)); // Never assign a child without parent being assigned first if ((rsc->parent != NULL) && !pcmk_is_set(rsc->parent->flags, pcmk_rsc_assigning)) { pcmk__rsc_debug(rsc, "%s: Assigning parent %s first", rsc->id, rsc->parent->id); rsc->parent->cmds->assign(rsc->parent, prefer, stop_if_fail); } if (!pcmk_is_set(rsc->flags, pcmk_rsc_unassigned)) { // Assignment has already been done const char *node_name = "no node"; if (rsc->allocated_to != NULL) { node_name = pcmk__node_name(rsc->allocated_to); } pcmk__rsc_debug(rsc, "%s: pre-assigned to %s", rsc->id, node_name); return rsc->allocated_to; } // Ensure we detect assignment loops if (pcmk_is_set(rsc->flags, pcmk_rsc_assigning)) { pcmk__rsc_debug(rsc, "Breaking assignment loop involving %s", rsc->id); return NULL; } pcmk__set_rsc_flags(rsc, pcmk_rsc_assigning); pe__show_node_scores(true, rsc, "Pre-assignment", rsc->allowed_nodes, rsc->cluster); this_with_colocations = pcmk__this_with_colocations(rsc); with_this_colocations = pcmk__with_this_colocations(rsc); // Apply mandatory colocations first, to satisfy as many as possible for (iter = this_with_colocations; iter != NULL; iter = iter->next) { colocation = iter->data; if ((colocation->score <= -PCMK_SCORE_INFINITY) || (colocation->score >= PCMK_SCORE_INFINITY)) { apply_this_with(colocation, rsc); } } for (iter = with_this_colocations; iter != NULL; iter = iter->next) { colocation = iter->data; if ((colocation->score <= -PCMK_SCORE_INFINITY) || (colocation->score >= PCMK_SCORE_INFINITY)) { pcmk__add_dependent_scores(colocation, rsc); } } pe__show_node_scores(true, rsc, "Mandatory-colocations", rsc->allowed_nodes, rsc->cluster); // Then apply optional colocations for (iter = this_with_colocations; iter != NULL; iter = iter->next) { colocation = iter->data; if ((colocation->score > -PCMK_SCORE_INFINITY) && (colocation->score < PCMK_SCORE_INFINITY)) { apply_this_with(colocation, rsc); } } for (iter = with_this_colocations; iter != NULL; iter = iter->next) { colocation = iter->data; if ((colocation->score > -PCMK_SCORE_INFINITY) && (colocation->score < PCMK_SCORE_INFINITY)) { pcmk__add_dependent_scores(colocation, rsc); } } g_list_free(this_with_colocations); g_list_free(with_this_colocations); if (rsc->next_role == pcmk_role_stopped) { pcmk__rsc_trace(rsc, "Banning %s from all nodes because it will be stopped", rsc->id); resource_location(rsc, NULL, -PCMK_SCORE_INFINITY, PCMK_META_TARGET_ROLE, rsc->cluster); } else if ((rsc->next_role > rsc->role) && !pcmk_is_set(rsc->cluster->flags, pcmk_sched_quorate) && (rsc->cluster->no_quorum_policy == pcmk_no_quorum_freeze)) { crm_notice("Resource %s cannot be elevated from %s to %s due to " PCMK_OPT_NO_QUORUM_POLICY "=" PCMK_VALUE_FREEZE, rsc->id, pcmk_role_text(rsc->role), pcmk_role_text(rsc->next_role)); pe__set_next_role(rsc, rsc->role, PCMK_OPT_NO_QUORUM_POLICY "=" PCMK_VALUE_FREEZE); } pe__show_node_scores(!pcmk_is_set(rsc->cluster->flags, pcmk_sched_output_scores), rsc, __func__, rsc->allowed_nodes, rsc->cluster); // Unmanage resource if fencing is enabled but no device is configured if (pcmk_is_set(rsc->cluster->flags, pcmk_sched_fencing_enabled) && !pcmk_is_set(rsc->cluster->flags, pcmk_sched_have_fencing)) { pcmk__clear_rsc_flags(rsc, pcmk_rsc_managed); } if (!pcmk_is_set(rsc->flags, pcmk_rsc_managed)) { // Unmanaged resources stay on their current node const char *reason = NULL; pcmk_node_t *assign_to = NULL; pe__set_next_role(rsc, rsc->role, "unmanaged"); assign_to = pcmk__current_node(rsc); if (assign_to == NULL) { reason = "inactive"; } else if (rsc->role == pcmk_role_promoted) { reason = "promoted"; } else if (pcmk_is_set(rsc->flags, pcmk_rsc_failed)) { reason = "failed"; } else { reason = "active"; } pcmk__rsc_info(rsc, "Unmanaged resource %s assigned to %s: %s", rsc->id, (assign_to? assign_to->details->uname : "no node"), reason); pcmk__assign_resource(rsc, assign_to, true, stop_if_fail); } else if (pcmk_is_set(rsc->cluster->flags, pcmk_sched_stop_all)) { // Must stop at some point, but be consistent with stop_if_fail if (stop_if_fail) { pcmk__rsc_debug(rsc, "Forcing %s to stop: " PCMK_OPT_STOP_ALL_RESOURCES, rsc->id); } pcmk__assign_resource(rsc, NULL, true, stop_if_fail); } else if (!assign_best_node(rsc, prefer, stop_if_fail)) { // Assignment failed if (!pcmk_is_set(rsc->flags, pcmk_rsc_removed)) { pcmk__rsc_info(rsc, "Resource %s cannot run anywhere", rsc->id); } else if ((rsc->running_on != NULL) && stop_if_fail) { pcmk__rsc_info(rsc, "Stopping removed resource %s", rsc->id); } } pcmk__clear_rsc_flags(rsc, pcmk_rsc_assigning); if (rsc->is_remote_node) { remote_connection_assigned(rsc); } return rsc->allocated_to; } /*! * \internal * \brief Schedule actions to bring resource down and back to current role * * \param[in,out] rsc Resource to restart * \param[in,out] current Node that resource should be brought down on * \param[in] need_stop Whether the resource must be stopped * \param[in] need_promote Whether the resource must be promoted * * \return Role that resource would have after scheduled actions are taken */ static void schedule_restart_actions(pcmk_resource_t *rsc, pcmk_node_t *current, bool need_stop, bool need_promote) { enum rsc_role_e role = rsc->role; enum rsc_role_e next_role; rsc_transition_fn fn = NULL; pcmk__set_rsc_flags(rsc, pcmk_rsc_restarting); // Bring resource down to a stop on its current node while (role != pcmk_role_stopped) { next_role = rsc_state_matrix[role][pcmk_role_stopped]; pcmk__rsc_trace(rsc, "Creating %s action to take %s down from %s to %s", (need_stop? "required" : "optional"), rsc->id, pcmk_role_text(role), pcmk_role_text(next_role)); fn = rsc_action_matrix[role][next_role]; if (fn == NULL) { break; } fn(rsc, current, !need_stop); role = next_role; } // Bring resource up to its next role on its next node while ((rsc->role <= rsc->next_role) && (role != rsc->role) && !pcmk_is_set(rsc->flags, pcmk_rsc_blocked)) { bool required = need_stop; next_role = rsc_state_matrix[role][rsc->role]; if ((next_role == pcmk_role_promoted) && need_promote) { required = true; } pcmk__rsc_trace(rsc, "Creating %s action to take %s up from %s to %s", (required? "required" : "optional"), rsc->id, pcmk_role_text(role), pcmk_role_text(next_role)); fn = rsc_action_matrix[role][next_role]; if (fn == NULL) { break; } fn(rsc, rsc->allocated_to, !required); role = next_role; } pcmk__clear_rsc_flags(rsc, pcmk_rsc_restarting); } /*! * \internal * \brief If a resource's next role is not explicitly specified, set a default * * \param[in,out] rsc Resource to set next role for * * \return "explicit" if next role was explicitly set, otherwise "implicit" */ static const char * set_default_next_role(pcmk_resource_t *rsc) { if (rsc->next_role != pcmk_role_unknown) { return "explicit"; } if (rsc->allocated_to == NULL) { pe__set_next_role(rsc, pcmk_role_stopped, "assignment"); } else { pe__set_next_role(rsc, pcmk_role_started, "assignment"); } return "implicit"; } /*! * \internal * \brief Create an action to represent an already pending start * * \param[in,out] rsc Resource to create start action for */ static void create_pending_start(pcmk_resource_t *rsc) { pcmk_action_t *start = NULL; pcmk__rsc_trace(rsc, "Creating action for %s to represent already pending start", rsc->id); start = start_action(rsc, rsc->allocated_to, TRUE); pcmk__set_action_flags(start, pcmk_action_always_in_graph); } /*! * \internal * \brief Schedule actions needed to take a resource to its next role * * \param[in,out] rsc Resource to schedule actions for */ static void schedule_role_transition_actions(pcmk_resource_t *rsc) { enum rsc_role_e role = rsc->role; while (role != rsc->next_role) { enum rsc_role_e next_role = rsc_state_matrix[role][rsc->next_role]; rsc_transition_fn fn = NULL; pcmk__rsc_trace(rsc, "Creating action to take %s from %s to %s " "(ending at %s)", rsc->id, pcmk_role_text(role), pcmk_role_text(next_role), pcmk_role_text(rsc->next_role)); fn = rsc_action_matrix[role][next_role]; if (fn == NULL) { break; } fn(rsc, rsc->allocated_to, false); role = next_role; } } /*! * \internal * \brief Create all actions needed for a given primitive resource * * \param[in,out] rsc Primitive resource to create actions for */ void pcmk__primitive_create_actions(pcmk_resource_t *rsc) { bool need_stop = false; bool need_promote = false; bool is_moving = false; bool allow_migrate = false; bool multiply_active = false; pcmk_node_t *current = NULL; unsigned int num_all_active = 0; unsigned int num_clean_active = 0; const char *next_role_source = NULL; CRM_ASSERT(pcmk__is_primitive(rsc)); next_role_source = set_default_next_role(rsc); pcmk__rsc_trace(rsc, "Creating all actions for %s transition from %s to %s " "(%s) on %s", rsc->id, pcmk_role_text(rsc->role), pcmk_role_text(rsc->next_role), next_role_source, pcmk__node_name(rsc->allocated_to)); current = rsc->fns->active_node(rsc, &num_all_active, &num_clean_active); g_list_foreach(rsc->dangling_migrations, pcmk__abort_dangling_migration, rsc); if ((current != NULL) && (rsc->allocated_to != NULL) && !pcmk__same_node(current, rsc->allocated_to) && (rsc->next_role >= pcmk_role_started)) { pcmk__rsc_trace(rsc, "Moving %s from %s to %s", rsc->id, pcmk__node_name(current), pcmk__node_name(rsc->allocated_to)); is_moving = true; allow_migrate = pcmk__rsc_can_migrate(rsc, current); // This is needed even if migrating (though I'm not sure why ...) need_stop = true; } // Check whether resource is partially migrated and/or multiply active if ((rsc->partial_migration_source != NULL) && (rsc->partial_migration_target != NULL) && allow_migrate && (num_all_active == 2) && pcmk__same_node(current, rsc->partial_migration_source) && pcmk__same_node(rsc->allocated_to, rsc->partial_migration_target)) { /* A partial migration is in progress, and the migration target remains * the same as when the migration began. */ pcmk__rsc_trace(rsc, "Partial migration of %s from %s to %s will continue", rsc->id, pcmk__node_name(rsc->partial_migration_source), pcmk__node_name(rsc->partial_migration_target)); } else if ((rsc->partial_migration_source != NULL) || (rsc->partial_migration_target != NULL)) { // A partial migration is in progress but can't be continued if (num_all_active > 2) { // The resource is migrating *and* multiply active! crm_notice("Forcing recovery of %s because it is migrating " "from %s to %s and possibly active elsewhere", rsc->id, pcmk__node_name(rsc->partial_migration_source), pcmk__node_name(rsc->partial_migration_target)); } else { // The migration source or target isn't available crm_notice("Forcing recovery of %s because it can no longer " "migrate from %s to %s", rsc->id, pcmk__node_name(rsc->partial_migration_source), pcmk__node_name(rsc->partial_migration_target)); } need_stop = true; rsc->partial_migration_source = rsc->partial_migration_target = NULL; allow_migrate = false; } else if (pcmk_is_set(rsc->flags, pcmk_rsc_needs_fencing)) { multiply_active = (num_all_active > 1); } else { /* If a resource has PCMK_META_REQUIRES set to PCMK_VALUE_NOTHING or * PCMK_VALUE_QUORUM, don't consider it active on unclean nodes (similar * to how all resources behave when PCMK_OPT_STONITH_ENABLED is false). * We can start such resources elsewhere before fencing completes, and * if we considered the resource active on the failed node, we would * attempt recovery for being active on multiple nodes. */ multiply_active = (num_clean_active > 1); } if (multiply_active) { const char *class = crm_element_value(rsc->xml, PCMK_XA_CLASS); // Resource was (possibly) incorrectly multiply active pcmk__sched_err("%s resource %s might be active on %u nodes (%s)", pcmk__s(class, "Untyped"), rsc->id, num_all_active, pcmk__multiply_active_text(rsc->recovery_type)); crm_notice("For more information, see \"What are multiply active " "resources?\" at " "https://projects.clusterlabs.org/w/clusterlabs/faq/"); switch (rsc->recovery_type) { case pcmk_multiply_active_restart: need_stop = true; break; case pcmk_multiply_active_unexpected: need_stop = true; // stop_resource() will skip expected node pcmk__set_rsc_flags(rsc, pcmk_rsc_stop_unexpected); break; default: break; } } else { pcmk__clear_rsc_flags(rsc, pcmk_rsc_stop_unexpected); } if (pcmk_is_set(rsc->flags, pcmk_rsc_start_pending)) { create_pending_start(rsc); } if (is_moving) { // Remaining tests are only for resources staying where they are } else if (pcmk_is_set(rsc->flags, pcmk_rsc_failed)) { if (pcmk_is_set(rsc->flags, pcmk_rsc_stop_if_failed)) { need_stop = true; pcmk__rsc_trace(rsc, "Recovering %s", rsc->id); } else { pcmk__rsc_trace(rsc, "Recovering %s by demotion", rsc->id); if (rsc->next_role == pcmk_role_promoted) { need_promote = true; } } } else if (pcmk_is_set(rsc->flags, pcmk_rsc_blocked)) { pcmk__rsc_trace(rsc, "Blocking further actions on %s", rsc->id); need_stop = true; } else if ((rsc->role > pcmk_role_started) && (current != NULL) && (rsc->allocated_to != NULL)) { pcmk_action_t *start = NULL; pcmk__rsc_trace(rsc, "Creating start action for promoted resource %s", rsc->id); start = start_action(rsc, rsc->allocated_to, TRUE); if (!pcmk_is_set(start->flags, pcmk_action_optional)) { // Recovery of a promoted resource pcmk__rsc_trace(rsc, "%s restart is required for recovery", rsc->id); need_stop = true; } } // Create any actions needed to bring resource down and back up to same role schedule_restart_actions(rsc, current, need_stop, need_promote); // Create any actions needed to take resource from this role to the next schedule_role_transition_actions(rsc); pcmk__create_recurring_actions(rsc); if (allow_migrate) { pcmk__create_migration_actions(rsc, current); } } /*! * \internal * \brief Ban a resource from any allowed nodes that are Pacemaker Remote nodes * * \param[in] rsc Resource to check */ static void rsc_avoids_remote_nodes(const pcmk_resource_t *rsc) { GHashTableIter iter; pcmk_node_t *node = NULL; g_hash_table_iter_init(&iter, rsc->allowed_nodes); while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) { if (node->details->remote_rsc != NULL) { node->weight = -PCMK_SCORE_INFINITY; } } } /*! * \internal * \brief Return allowed nodes as (possibly sorted) list * * Convert a resource's hash table of allowed nodes to a list. If printing to * stdout, sort the list, to keep action ID numbers consistent for regression * test output (while avoiding the performance hit on a live cluster). * * \param[in] rsc Resource to check for allowed nodes * * \return List of resource's allowed nodes * \note Callers should take care not to rely on the list being sorted. */ static GList * allowed_nodes_as_list(const pcmk_resource_t *rsc) { GList *allowed_nodes = NULL; if (rsc->allowed_nodes) { allowed_nodes = g_hash_table_get_values(rsc->allowed_nodes); } if (!pcmk__is_daemon) { allowed_nodes = g_list_sort(allowed_nodes, pe__cmp_node_name); } return allowed_nodes; } /*! * \internal * \brief Create implicit constraints needed for a primitive resource * * \param[in,out] rsc Primitive resource to create implicit constraints for */ void pcmk__primitive_internal_constraints(pcmk_resource_t *rsc) { GList *allowed_nodes = NULL; bool check_unfencing = false; bool check_utilization = false; CRM_ASSERT(pcmk__is_primitive(rsc)); if (!pcmk_is_set(rsc->flags, pcmk_rsc_managed)) { pcmk__rsc_trace(rsc, "Skipping implicit constraints for unmanaged resource " "%s", rsc->id); return; } // Whether resource requires unfencing check_unfencing = !pcmk_is_set(rsc->flags, pcmk_rsc_fence_device) && pcmk_is_set(rsc->cluster->flags, pcmk_sched_enable_unfencing) && pcmk_is_set(rsc->flags, pcmk_rsc_needs_unfencing); // Whether a non-default placement strategy is used check_utilization = (g_hash_table_size(rsc->utilization) > 0) && !pcmk__str_eq(rsc->cluster->placement_strategy, PCMK_VALUE_DEFAULT, pcmk__str_casei); // Order stops before starts (i.e. restart) pcmk__new_ordering(rsc, pcmk__op_key(rsc->id, PCMK_ACTION_STOP, 0), NULL, rsc, pcmk__op_key(rsc->id, PCMK_ACTION_START, 0), NULL, pcmk__ar_ordered |pcmk__ar_first_implies_then |pcmk__ar_intermediate_stop, rsc->cluster); // Promotable ordering: demote before stop, start before promote if (pcmk_is_set(pe__const_top_resource(rsc, false)->flags, pcmk_rsc_promotable) || (rsc->role > pcmk_role_unpromoted)) { pcmk__new_ordering(rsc, pcmk__op_key(rsc->id, PCMK_ACTION_DEMOTE, 0), NULL, rsc, pcmk__op_key(rsc->id, PCMK_ACTION_STOP, 0), NULL, pcmk__ar_promoted_then_implies_first, rsc->cluster); pcmk__new_ordering(rsc, pcmk__op_key(rsc->id, PCMK_ACTION_START, 0), NULL, rsc, pcmk__op_key(rsc->id, PCMK_ACTION_PROMOTE, 0), NULL, pcmk__ar_unrunnable_first_blocks, rsc->cluster); } // Don't clear resource history if probing on same node pcmk__new_ordering(rsc, pcmk__op_key(rsc->id, PCMK_ACTION_LRM_DELETE, 0), NULL, rsc, pcmk__op_key(rsc->id, PCMK_ACTION_MONITOR, 0), NULL, pcmk__ar_if_on_same_node|pcmk__ar_then_cancels_first, rsc->cluster); // Certain checks need allowed nodes if (check_unfencing || check_utilization || (rsc->container != NULL)) { allowed_nodes = allowed_nodes_as_list(rsc); } if (check_unfencing) { g_list_foreach(allowed_nodes, pcmk__order_restart_vs_unfence, rsc); } if (check_utilization) { pcmk__create_utilization_constraints(rsc, allowed_nodes); } if (rsc->container != NULL) { pcmk_resource_t *remote_rsc = NULL; if (rsc->is_remote_node) { // rsc is the implicit remote connection for a guest or bundle node /* Guest resources are not allowed to run on Pacemaker Remote nodes, * to avoid nesting remotes. However, bundles are allowed. */ if (!pcmk_is_set(rsc->flags, pcmk_rsc_remote_nesting_allowed)) { rsc_avoids_remote_nodes(rsc->container); } /* If someone cleans up a guest or bundle node's container, we will * likely schedule a (re-)probe of the container and recovery of the * connection. Order the connection stop after the container probe, * so that if we detect the container running, we will trigger a new * transition and avoid the unnecessary recovery. */ pcmk__order_resource_actions(rsc->container, PCMK_ACTION_MONITOR, rsc, PCMK_ACTION_STOP, pcmk__ar_ordered); /* A user can specify that a resource must start on a Pacemaker Remote * node by explicitly configuring it with the container=NODENAME * meta-attribute. This is of questionable merit, since location * constraints can accomplish the same thing. But we support it, so here * we check whether a resource (that is not itself a remote connection) * has container set to a remote node or guest node resource. */ } else if (rsc->container->is_remote_node) { remote_rsc = rsc->container; } else { remote_rsc = pe__resource_contains_guest_node(rsc->cluster, rsc->container); } if (remote_rsc != NULL) { /* Force the resource on the Pacemaker Remote node instead of * colocating the resource with the container resource. */ for (GList *item = allowed_nodes; item; item = item->next) { pcmk_node_t *node = item->data; if (node->details->remote_rsc != remote_rsc) { node->weight = -PCMK_SCORE_INFINITY; } } } else { /* This resource is either a filler for a container that does NOT * represent a Pacemaker Remote node, or a Pacemaker Remote * connection resource for a guest node or bundle. */ int score; crm_trace("Order and colocate %s relative to its container %s", rsc->id, rsc->container->id); pcmk__new_ordering(rsc->container, pcmk__op_key(rsc->container->id, PCMK_ACTION_START, 0), NULL, rsc, pcmk__op_key(rsc->id, PCMK_ACTION_START, 0), NULL, pcmk__ar_first_implies_then |pcmk__ar_unrunnable_first_blocks, rsc->cluster); pcmk__new_ordering(rsc, pcmk__op_key(rsc->id, PCMK_ACTION_STOP, 0), NULL, rsc->container, pcmk__op_key(rsc->container->id, PCMK_ACTION_STOP, 0), NULL, pcmk__ar_then_implies_first, rsc->cluster); if (pcmk_is_set(rsc->flags, pcmk_rsc_remote_nesting_allowed)) { score = 10000; /* Highly preferred but not essential */ } else { score = PCMK_SCORE_INFINITY; // Force to run on same host } pcmk__new_colocation("#resource-with-container", NULL, score, rsc, rsc->container, NULL, NULL, pcmk__coloc_influence); } } if (rsc->is_remote_node || pcmk_is_set(rsc->flags, pcmk_rsc_fence_device)) { /* Remote connections and fencing devices are not allowed to run on * Pacemaker Remote nodes */ rsc_avoids_remote_nodes(rsc); } g_list_free(allowed_nodes); } /*! * \internal * \brief Apply a colocation's score to node scores or resource priority * * Given a colocation constraint, apply its score to the dependent's * allowed node scores (if we are still placing resources) or priority (if * we are choosing promotable clone instance roles). * * \param[in,out] dependent Dependent resource in colocation * \param[in] primary Primary resource in colocation * \param[in] colocation Colocation constraint to apply * \param[in] for_dependent true if called on behalf of dependent + * + * \return The score added to the dependent's priority */ -void +int pcmk__primitive_apply_coloc_score(pcmk_resource_t *dependent, const pcmk_resource_t *primary, const pcmk__colocation_t *colocation, bool for_dependent) { enum pcmk__coloc_affects filter_results; CRM_ASSERT((dependent != NULL) && (primary != NULL) && (colocation != NULL)); if (for_dependent) { // Always process on behalf of primary resource - primary->cmds->apply_coloc_score(dependent, primary, colocation, false); - return; + return primary->cmds->apply_coloc_score(dependent, primary, colocation, + false); } filter_results = pcmk__colocation_affects(dependent, primary, colocation, false); pcmk__rsc_trace(dependent, "%s %s with %s (%s, score=%d, filter=%d)", ((colocation->score > 0)? "Colocating" : "Anti-colocating"), dependent->id, primary->id, colocation->id, colocation->score, filter_results); switch (filter_results) { case pcmk__coloc_affects_role: - pcmk__apply_coloc_to_priority(dependent, primary, colocation); - break; + return pcmk__apply_coloc_to_priority(dependent, primary, + colocation); + case pcmk__coloc_affects_location: pcmk__apply_coloc_to_scores(dependent, primary, colocation); - break; + return 0; + default: // pcmk__coloc_affects_nothing - return; + return 0; } } /* Primitive implementation of * pcmk_assignment_methods_t:with_this_colocations() */ void pcmk__with_primitive_colocations(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList **list) { CRM_ASSERT(pcmk__is_primitive(rsc) && (list != NULL)); if (rsc == orig_rsc) { /* For the resource itself, add all of its own colocations and relevant * colocations from its parent (if any). */ pcmk__add_with_this_list(list, rsc->rsc_cons_lhs, orig_rsc); if (rsc->parent != NULL) { rsc->parent->cmds->with_this_colocations(rsc->parent, orig_rsc, list); } } else { // For an ancestor, add only explicitly configured constraints for (GList *iter = rsc->rsc_cons_lhs; iter != NULL; iter = iter->next) { pcmk__colocation_t *colocation = iter->data; if (pcmk_is_set(colocation->flags, pcmk__coloc_explicit)) { pcmk__add_with_this(list, colocation, orig_rsc); } } } } /* Primitive implementation of * pcmk_assignment_methods_t:this_with_colocations() */ void pcmk__primitive_with_colocations(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList **list) { CRM_ASSERT(pcmk__is_primitive(rsc) && (list != NULL)); if (rsc == orig_rsc) { /* For the resource itself, add all of its own colocations and relevant * colocations from its parent (if any). */ pcmk__add_this_with_list(list, rsc->rsc_cons, orig_rsc); if (rsc->parent != NULL) { rsc->parent->cmds->this_with_colocations(rsc->parent, orig_rsc, list); } } else { // For an ancestor, add only explicitly configured constraints for (GList *iter = rsc->rsc_cons; iter != NULL; iter = iter->next) { pcmk__colocation_t *colocation = iter->data; if (pcmk_is_set(colocation->flags, pcmk__coloc_explicit)) { pcmk__add_this_with(list, colocation, orig_rsc); } } } } /*! * \internal * \brief Return action flags for a given primitive resource action * * \param[in,out] action Action to get flags for * \param[in] node If not NULL, limit effects to this node (ignored) * * \return Flags appropriate to \p action on \p node */ uint32_t pcmk__primitive_action_flags(pcmk_action_t *action, const pcmk_node_t *node) { CRM_ASSERT(action != NULL); return (uint32_t) action->flags; } /*! * \internal * \brief Check whether a node is a multiply active resource's expected node * * \param[in] rsc Resource to check * \param[in] node Node to check * * \return \c true if \p rsc is multiply active with * \c PCMK_META_MULTIPLE_ACTIVE set to \c PCMK_VALUE_STOP_UNEXPECTED, * and \p node is the node where it will remain active * \note This assumes that the resource's next role cannot be changed to stopped * after this is called, which should be reasonable if status has already * been unpacked and resources have been assigned to nodes. */ static bool is_expected_node(const pcmk_resource_t *rsc, const pcmk_node_t *node) { return pcmk_all_flags_set(rsc->flags, pcmk_rsc_stop_unexpected|pcmk_rsc_restarting) && (rsc->next_role > pcmk_role_stopped) && pcmk__same_node(rsc->allocated_to, node); } /*! * \internal * \brief Schedule actions needed to stop a resource wherever it is active * * \param[in,out] rsc Resource being stopped * \param[in] node Node where resource is being stopped (ignored) * \param[in] optional Whether actions should be optional */ static void stop_resource(pcmk_resource_t *rsc, pcmk_node_t *node, bool optional) { for (GList *iter = rsc->running_on; iter != NULL; iter = iter->next) { pcmk_node_t *current = (pcmk_node_t *) iter->data; pcmk_action_t *stop = NULL; if (is_expected_node(rsc, current)) { /* We are scheduling restart actions for a multiply active resource * with PCMK_META_MULTIPLE_ACTIVE=PCMK_VALUE_STOP_UNEXPECTED, and * this is where it should not be stopped. */ pcmk__rsc_trace(rsc, "Skipping stop of multiply active resource %s " "on expected node %s", rsc->id, pcmk__node_name(current)); continue; } if (rsc->partial_migration_target != NULL) { // Continue migration if node originally was and remains target if (pcmk__same_node(current, rsc->partial_migration_target) && pcmk__same_node(current, rsc->allocated_to)) { pcmk__rsc_trace(rsc, "Skipping stop of %s on %s " "because partial migration there will continue", rsc->id, pcmk__node_name(current)); continue; } else { pcmk__rsc_trace(rsc, "Forcing stop of %s on %s " "because migration target changed", rsc->id, pcmk__node_name(current)); optional = false; } } pcmk__rsc_trace(rsc, "Scheduling stop of %s on %s", rsc->id, pcmk__node_name(current)); stop = stop_action(rsc, current, optional); if (rsc->allocated_to == NULL) { pe_action_set_reason(stop, "node availability", true); } else if (pcmk_all_flags_set(rsc->flags, pcmk_rsc_restarting |pcmk_rsc_stop_unexpected)) { /* We are stopping a multiply active resource on a node that is * not its expected node, and we are still scheduling restart * actions, so the stop is for being multiply active. */ pe_action_set_reason(stop, "being multiply active", true); } if (!pcmk_is_set(rsc->flags, pcmk_rsc_managed)) { pcmk__clear_action_flags(stop, pcmk_action_runnable); } if (pcmk_is_set(rsc->cluster->flags, pcmk_sched_remove_after_stop)) { pcmk__schedule_cleanup(rsc, current, optional); } if (pcmk_is_set(rsc->flags, pcmk_rsc_needs_unfencing)) { pcmk_action_t *unfence = pe_fence_op(current, PCMK_ACTION_ON, true, NULL, false, rsc->cluster); order_actions(stop, unfence, pcmk__ar_then_implies_first); if (!pcmk__node_unfenced(current)) { pcmk__sched_err("Stopping %s until %s can be unfenced", rsc->id, pcmk__node_name(current)); } } } } /*! * \internal * \brief Schedule actions needed to start a resource on a node * * \param[in,out] rsc Resource being started * \param[in,out] node Node where resource should be started * \param[in] optional Whether actions should be optional */ static void start_resource(pcmk_resource_t *rsc, pcmk_node_t *node, bool optional) { pcmk_action_t *start = NULL; CRM_ASSERT(node != NULL); pcmk__rsc_trace(rsc, "Scheduling %s start of %s on %s (score %d)", (optional? "optional" : "required"), rsc->id, pcmk__node_name(node), node->weight); start = start_action(rsc, node, TRUE); pcmk__order_vs_unfence(rsc, node, start, pcmk__ar_first_implies_then); if (pcmk_is_set(start->flags, pcmk_action_runnable) && !optional) { pcmk__clear_action_flags(start, pcmk_action_optional); } if (is_expected_node(rsc, node)) { /* This could be a problem if the start becomes necessary for other * reasons later. */ pcmk__rsc_trace(rsc, "Start of multiply active resouce %s " "on expected node %s will be a pseudo-action", rsc->id, pcmk__node_name(node)); pcmk__set_action_flags(start, pcmk_action_pseudo); } } /*! * \internal * \brief Schedule actions needed to promote a resource on a node * * \param[in,out] rsc Resource being promoted * \param[in] node Node where resource should be promoted * \param[in] optional Whether actions should be optional */ static void promote_resource(pcmk_resource_t *rsc, pcmk_node_t *node, bool optional) { GList *iter = NULL; GList *action_list = NULL; bool runnable = true; CRM_ASSERT(node != NULL); // Any start must be runnable for promotion to be runnable action_list = pe__resource_actions(rsc, node, PCMK_ACTION_START, true); for (iter = action_list; iter != NULL; iter = iter->next) { pcmk_action_t *start = (pcmk_action_t *) iter->data; if (!pcmk_is_set(start->flags, pcmk_action_runnable)) { runnable = false; } } g_list_free(action_list); if (runnable) { pcmk_action_t *promote = promote_action(rsc, node, optional); pcmk__rsc_trace(rsc, "Scheduling %s promotion of %s on %s", (optional? "optional" : "required"), rsc->id, pcmk__node_name(node)); if (is_expected_node(rsc, node)) { /* This could be a problem if the promote becomes necessary for * other reasons later. */ pcmk__rsc_trace(rsc, "Promotion of multiply active resouce %s " "on expected node %s will be a pseudo-action", rsc->id, pcmk__node_name(node)); pcmk__set_action_flags(promote, pcmk_action_pseudo); } } else { pcmk__rsc_trace(rsc, "Not promoting %s on %s: start unrunnable", rsc->id, pcmk__node_name(node)); action_list = pe__resource_actions(rsc, node, PCMK_ACTION_PROMOTE, true); for (iter = action_list; iter != NULL; iter = iter->next) { pcmk_action_t *promote = (pcmk_action_t *) iter->data; pcmk__clear_action_flags(promote, pcmk_action_runnable); } g_list_free(action_list); } } /*! * \internal * \brief Schedule actions needed to demote a resource wherever it is active * * \param[in,out] rsc Resource being demoted * \param[in] node Node where resource should be demoted (ignored) * \param[in] optional Whether actions should be optional */ static void demote_resource(pcmk_resource_t *rsc, pcmk_node_t *node, bool optional) { /* Since this will only be called for a primitive (possibly as an instance * of a collective resource), the resource is multiply active if it is * running on more than one node, so we want to demote on all of them as * part of recovery, regardless of which one is the desired node. */ for (GList *iter = rsc->running_on; iter != NULL; iter = iter->next) { pcmk_node_t *current = (pcmk_node_t *) iter->data; if (is_expected_node(rsc, current)) { pcmk__rsc_trace(rsc, "Skipping demote of multiply active resource %s " "on expected node %s", rsc->id, pcmk__node_name(current)); } else { pcmk__rsc_trace(rsc, "Scheduling %s demotion of %s on %s", (optional? "optional" : "required"), rsc->id, pcmk__node_name(current)); demote_action(rsc, current, optional); } } } static void assert_role_error(pcmk_resource_t *rsc, pcmk_node_t *node, bool optional) { CRM_ASSERT(false); } /*! * \internal * \brief Schedule cleanup of a resource * * \param[in,out] rsc Resource to clean up * \param[in] node Node to clean up on * \param[in] optional Whether clean-up should be optional */ void pcmk__schedule_cleanup(pcmk_resource_t *rsc, const pcmk_node_t *node, bool optional) { /* If the cleanup is required, its orderings are optional, because they're * relevant only if both actions are required. Conversely, if the cleanup is * optional, the orderings make the then action required if the first action * becomes required. */ uint32_t flag = optional? pcmk__ar_first_implies_then : pcmk__ar_ordered; CRM_CHECK((rsc != NULL) && (node != NULL), return); if (pcmk_is_set(rsc->flags, pcmk_rsc_failed)) { pcmk__rsc_trace(rsc, "Skipping clean-up of %s on %s: resource failed", rsc->id, pcmk__node_name(node)); return; } if (node->details->unclean || !node->details->online) { pcmk__rsc_trace(rsc, "Skipping clean-up of %s on %s: node unavailable", rsc->id, pcmk__node_name(node)); return; } crm_notice("Scheduling clean-up of %s on %s", rsc->id, pcmk__node_name(node)); delete_action(rsc, node, optional); // stop -> clean-up -> start pcmk__order_resource_actions(rsc, PCMK_ACTION_STOP, rsc, PCMK_ACTION_DELETE, flag); pcmk__order_resource_actions(rsc, PCMK_ACTION_DELETE, rsc, PCMK_ACTION_START, flag); } /*! * \internal * \brief Add primitive meta-attributes relevant to graph actions to XML * * \param[in] rsc Primitive resource whose meta-attributes should be added * \param[in,out] xml Transition graph action attributes XML to add to */ void pcmk__primitive_add_graph_meta(const pcmk_resource_t *rsc, xmlNode *xml) { char *name = NULL; char *value = NULL; const pcmk_resource_t *parent = NULL; CRM_ASSERT(pcmk__is_primitive(rsc) && (xml != NULL)); /* Clone instance numbers get set internally as meta-attributes, and are * needed in the transition graph (for example, to tell unique clone * instances apart). */ value = g_hash_table_lookup(rsc->meta, PCMK__META_CLONE); if (value != NULL) { name = crm_meta_name(PCMK__META_CLONE); crm_xml_add(xml, name, value); free(name); } // Not sure if this one is really needed ... value = g_hash_table_lookup(rsc->meta, PCMK_META_REMOTE_NODE); if (value != NULL) { name = crm_meta_name(PCMK_META_REMOTE_NODE); crm_xml_add(xml, name, value); free(name); } /* The container meta-attribute can be set on the primitive itself or one of * its parents (for example, a group inside a container resource), so check * them all, and keep the highest one found. */ for (parent = rsc; parent != NULL; parent = parent->parent) { if (parent->container != NULL) { crm_xml_add(xml, CRM_META "_" PCMK__META_CONTAINER, parent->container->id); } } /* Bundle replica children will get their external-ip set internally as a * meta-attribute. The graph action needs it, but under a different naming * convention than other meta-attributes. */ value = g_hash_table_lookup(rsc->meta, "external-ip"); if (value != NULL) { crm_xml_add(xml, "pcmk_external_ip", value); } } // Primitive implementation of pcmk_assignment_methods_t:add_utilization() void pcmk__primitive_add_utilization(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList *all_rscs, GHashTable *utilization) { CRM_ASSERT(pcmk__is_primitive(rsc) && (orig_rsc != NULL) && (utilization != NULL)); if (!pcmk_is_set(rsc->flags, pcmk_rsc_unassigned)) { return; } pcmk__rsc_trace(orig_rsc, "%s: Adding primitive %s as colocated utilization", orig_rsc->id, rsc->id); pcmk__release_node_capacity(utilization, rsc); } /*! * \internal * \brief Get epoch time of node's shutdown attribute (or now if none) * * \param[in,out] node Node to check * * \return Epoch time corresponding to shutdown attribute if set or now if not */ static time_t shutdown_time(pcmk_node_t *node) { const char *shutdown = pcmk__node_attr(node, PCMK__NODE_ATTR_SHUTDOWN, NULL, pcmk__rsc_node_current); time_t result = 0; if (shutdown != NULL) { long long result_ll; if (pcmk__scan_ll(shutdown, &result_ll, 0LL) == pcmk_rc_ok) { result = (time_t) result_ll; } } return (result == 0)? get_effective_time(node->details->data_set) : result; } /*! * \internal * \brief Ban a resource from a node if it's not locked to the node * * \param[in] data Node to check * \param[in,out] user_data Resource to check */ static void ban_if_not_locked(gpointer data, gpointer user_data) { const pcmk_node_t *node = (const pcmk_node_t *) data; pcmk_resource_t *rsc = (pcmk_resource_t *) user_data; if (strcmp(node->details->uname, rsc->lock_node->details->uname) != 0) { resource_location(rsc, node, -PCMK_SCORE_INFINITY, PCMK_OPT_SHUTDOWN_LOCK, rsc->cluster); } } // Primitive implementation of pcmk_assignment_methods_t:shutdown_lock() void pcmk__primitive_shutdown_lock(pcmk_resource_t *rsc) { const char *class = NULL; CRM_ASSERT(pcmk__is_primitive(rsc)); class = crm_element_value(rsc->xml, PCMK_XA_CLASS); // Fence devices and remote connections can't be locked if (pcmk__str_eq(class, PCMK_RESOURCE_CLASS_STONITH, pcmk__str_null_matches) || rsc->is_remote_node) { return; } if (rsc->lock_node != NULL) { // The lock was obtained from resource history if (rsc->running_on != NULL) { /* The resource was started elsewhere even though it is now * considered locked. This shouldn't be possible, but as a * failsafe, we don't want to disturb the resource now. */ pcmk__rsc_info(rsc, "Cancelling shutdown lock " "because %s is already active", rsc->id); pe__clear_resource_history(rsc, rsc->lock_node); rsc->lock_node = NULL; rsc->lock_time = 0; } // Only a resource active on exactly one node can be locked } else if (pcmk__list_of_1(rsc->running_on)) { pcmk_node_t *node = rsc->running_on->data; if (node->details->shutdown) { if (node->details->unclean) { pcmk__rsc_debug(rsc, "Not locking %s to unclean %s for shutdown", rsc->id, pcmk__node_name(node)); } else { rsc->lock_node = node; rsc->lock_time = shutdown_time(node); } } } if (rsc->lock_node == NULL) { // No lock needed return; } if (rsc->cluster->shutdown_lock > 0) { time_t lock_expiration = rsc->lock_time + rsc->cluster->shutdown_lock; pcmk__rsc_info(rsc, "Locking %s to %s due to shutdown (expires @%lld)", rsc->id, pcmk__node_name(rsc->lock_node), (long long) lock_expiration); pe__update_recheck_time(++lock_expiration, rsc->cluster, "shutdown lock expiration"); } else { pcmk__rsc_info(rsc, "Locking %s to %s due to shutdown", rsc->id, pcmk__node_name(rsc->lock_node)); } // If resource is locked to one node, ban it from all other nodes g_list_foreach(rsc->cluster->nodes, ban_if_not_locked, rsc); } diff --git a/lib/pacemaker/pcmk_sched_promotable.c b/lib/pacemaker/pcmk_sched_promotable.c index 5977187244..e129b7df45 100644 --- a/lib/pacemaker/pcmk_sched_promotable.c +++ b/lib/pacemaker/pcmk_sched_promotable.c @@ -1,1325 +1,1297 @@ /* * Copyright 2004-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU General Public License version 2 * or later (GPLv2+) WITHOUT ANY WARRANTY. */ #include #include #include #include "libpacemaker_private.h" /*! * \internal * \brief Add implicit promotion ordering for a promotable instance * * \param[in,out] clone Clone resource * \param[in,out] child Instance of \p clone being ordered * \param[in,out] last Previous instance ordered (NULL if \p child is first) */ static void order_instance_promotion(pcmk_resource_t *clone, pcmk_resource_t *child, pcmk_resource_t *last) { // "Promote clone" -> promote instance -> "clone promoted" pcmk__order_resource_actions(clone, PCMK_ACTION_PROMOTE, child, PCMK_ACTION_PROMOTE, pcmk__ar_ordered); pcmk__order_resource_actions(child, PCMK_ACTION_PROMOTE, clone, PCMK_ACTION_PROMOTED, pcmk__ar_ordered); // If clone is ordered, order this instance relative to last if ((last != NULL) && pe__clone_is_ordered(clone)) { pcmk__order_resource_actions(last, PCMK_ACTION_PROMOTE, child, PCMK_ACTION_PROMOTE, pcmk__ar_ordered); } } /*! * \internal * \brief Add implicit demotion ordering for a promotable instance * * \param[in,out] clone Clone resource * \param[in,out] child Instance of \p clone being ordered * \param[in] last Previous instance ordered (NULL if \p child is first) */ static void order_instance_demotion(pcmk_resource_t *clone, pcmk_resource_t *child, pcmk_resource_t *last) { // "Demote clone" -> demote instance -> "clone demoted" pcmk__order_resource_actions(clone, PCMK_ACTION_DEMOTE, child, PCMK_ACTION_DEMOTE, pcmk__ar_then_implies_first_graphed); pcmk__order_resource_actions(child, PCMK_ACTION_DEMOTE, clone, PCMK_ACTION_DEMOTED, pcmk__ar_first_implies_then_graphed); // If clone is ordered, order this instance relative to last if ((last != NULL) && pe__clone_is_ordered(clone)) { pcmk__order_resource_actions(child, PCMK_ACTION_DEMOTE, last, PCMK_ACTION_DEMOTE, pcmk__ar_ordered); } } /*! * \internal * \brief Check whether an instance will be promoted or demoted * * \param[in] rsc Instance to check * \param[out] demoting If \p rsc will be demoted, this will be set to true * \param[out] promoting If \p rsc will be promoted, this will be set to true */ static void check_for_role_change(const pcmk_resource_t *rsc, bool *demoting, bool *promoting) { const GList *iter = NULL; // If this is a cloned group, check group members recursively if (rsc->children != NULL) { for (iter = rsc->children; iter != NULL; iter = iter->next) { check_for_role_change((const pcmk_resource_t *) iter->data, demoting, promoting); } return; } for (iter = rsc->actions; iter != NULL; iter = iter->next) { const pcmk_action_t *action = (const pcmk_action_t *) iter->data; if (*promoting && *demoting) { return; } else if (pcmk_is_set(action->flags, pcmk_action_optional)) { continue; } else if (pcmk__str_eq(PCMK_ACTION_DEMOTE, action->task, pcmk__str_none)) { *demoting = true; } else if (pcmk__str_eq(PCMK_ACTION_PROMOTE, action->task, pcmk__str_none)) { *promoting = true; } } } /*! * \internal * \brief Add promoted-role location constraint scores to an instance's priority * * Adjust a promotable clone instance's promotion priority by the scores of any * location constraints in a list that are both limited to the promoted role and * for the node where the instance will be placed. * * \param[in,out] child Promotable clone instance * \param[in] location_constraints List of location constraints to apply * \param[in] chosen Node where \p child will be placed */ static void apply_promoted_locations(pcmk_resource_t *child, const GList *location_constraints, const pcmk_node_t *chosen) { for (const GList *iter = location_constraints; iter; iter = iter->next) { const pcmk__location_t *location = iter->data; const pcmk_node_t *constraint_node = NULL; if (location->role_filter == pcmk_role_promoted) { constraint_node = pe_find_node_id(location->nodes, chosen->details->id); } if (constraint_node != NULL) { int new_priority = pcmk__add_scores(child->priority, constraint_node->weight); pcmk__rsc_trace(child, "Applying location %s to %s promotion priority on " "%s: %s + %s = %s", location->id, child->id, pcmk__node_name(constraint_node), pcmk_readable_score(child->priority), pcmk_readable_score(constraint_node->weight), pcmk_readable_score(new_priority)); child->priority = new_priority; } } } /*! * \internal * \brief Get the node that an instance will be promoted on * * \param[in] rsc Promotable clone instance to check * * \return Node that \p rsc will be promoted on, or NULL if none */ static pcmk_node_t * node_to_be_promoted_on(const pcmk_resource_t *rsc) { pcmk_node_t *node = NULL; pcmk_node_t *local_node = NULL; const pcmk_resource_t *parent = NULL; // If this is a cloned group, bail if any group member can't be promoted for (GList *iter = rsc->children; iter != NULL; iter = iter->next) { pcmk_resource_t *child = (pcmk_resource_t *) iter->data; if (node_to_be_promoted_on(child) == NULL) { pcmk__rsc_trace(rsc, "%s can't be promoted because member %s can't", rsc->id, child->id); return NULL; } } node = rsc->fns->location(rsc, NULL, FALSE); if (node == NULL) { pcmk__rsc_trace(rsc, "%s can't be promoted because it won't be active", rsc->id); return NULL; } else if (!pcmk_is_set(rsc->flags, pcmk_rsc_managed)) { if (rsc->fns->state(rsc, TRUE) == pcmk_role_promoted) { crm_notice("Unmanaged instance %s will be left promoted on %s", rsc->id, pcmk__node_name(node)); } else { pcmk__rsc_trace(rsc, "%s can't be promoted because it is unmanaged", rsc->id); return NULL; } } else if (rsc->priority < 0) { pcmk__rsc_trace(rsc, "%s can't be promoted because its promotion priority " "%d is negative", rsc->id, rsc->priority); return NULL; } else if (!pcmk__node_available(node, false, true)) { pcmk__rsc_trace(rsc, "%s can't be promoted because %s can't run resources", rsc->id, pcmk__node_name(node)); return NULL; } parent = pe__const_top_resource(rsc, false); local_node = g_hash_table_lookup(parent->allowed_nodes, node->details->id); if (local_node == NULL) { /* It should not be possible for the scheduler to have assigned the * instance to a node where its parent is not allowed, but it's good to * have a fail-safe. */ if (pcmk_is_set(rsc->flags, pcmk_rsc_managed)) { pcmk__sched_err("%s can't be promoted because %s is not allowed " "on %s (scheduler bug?)", rsc->id, parent->id, pcmk__node_name(node)); } // else the instance is unmanaged and already promoted return NULL; } else if ((local_node->count >= pe__clone_promoted_node_max(parent)) && pcmk_is_set(rsc->flags, pcmk_rsc_managed)) { pcmk__rsc_trace(rsc, "%s can't be promoted because %s has " "maximum promoted instances already", rsc->id, pcmk__node_name(node)); return NULL; } return local_node; } /*! * \internal * \brief Compare two promotable clone instances by promotion priority * * \param[in] a First instance to compare * \param[in] b Second instance to compare * * \return A negative number if \p a has higher promotion priority, * a positive number if \p b has higher promotion priority, * or 0 if promotion priorities are equal */ static gint cmp_promotable_instance(gconstpointer a, gconstpointer b) { const pcmk_resource_t *rsc1 = (const pcmk_resource_t *) a; const pcmk_resource_t *rsc2 = (const pcmk_resource_t *) b; enum rsc_role_e role1 = pcmk_role_unknown; enum rsc_role_e role2 = pcmk_role_unknown; CRM_ASSERT((rsc1 != NULL) && (rsc2 != NULL)); // Check sort index set by pcmk__set_instance_roles() if (rsc1->sort_index > rsc2->sort_index) { pcmk__rsc_trace(rsc1, "%s has higher promotion priority than %s " "(sort index %d > %d)", rsc1->id, rsc2->id, rsc1->sort_index, rsc2->sort_index); return -1; } else if (rsc1->sort_index < rsc2->sort_index) { pcmk__rsc_trace(rsc1, "%s has lower promotion priority than %s " "(sort index %d < %d)", rsc1->id, rsc2->id, rsc1->sort_index, rsc2->sort_index); return 1; } // If those are the same, prefer instance whose current role is higher role1 = rsc1->fns->state(rsc1, TRUE); role2 = rsc2->fns->state(rsc2, TRUE); if (role1 > role2) { pcmk__rsc_trace(rsc1, "%s has higher promotion priority than %s " "(higher current role)", rsc1->id, rsc2->id); return -1; } else if (role1 < role2) { pcmk__rsc_trace(rsc1, "%s has lower promotion priority than %s " "(lower current role)", rsc1->id, rsc2->id); return 1; } // Finally, do normal clone instance sorting return pcmk__cmp_instance(a, b); } /*! * \internal * \brief Add a promotable clone instance's sort index to its node's score * * Add a promotable clone instance's sort index (which sums its promotion * preferences and scores of relevant location constraints for the promoted * role) to the node score of the instance's assigned node. * * \param[in] data Promotable clone instance * \param[in,out] user_data Clone parent of \p data */ static void add_sort_index_to_node_score(gpointer data, gpointer user_data) { const pcmk_resource_t *child = (const pcmk_resource_t *) data; pcmk_resource_t *clone = (pcmk_resource_t *) user_data; pcmk_node_t *node = NULL; const pcmk_node_t *chosen = NULL; if (child->sort_index < 0) { pcmk__rsc_trace(clone, "Not adding sort index of %s: negative", child->id); return; } chosen = child->fns->location(child, NULL, FALSE); if (chosen == NULL) { pcmk__rsc_trace(clone, "Not adding sort index of %s: inactive", child->id); return; } node = g_hash_table_lookup(clone->allowed_nodes, chosen->details->id); CRM_ASSERT(node != NULL); node->weight = pcmk__add_scores(child->sort_index, node->weight); pcmk__rsc_trace(clone, "Added cumulative priority of %s (%s) to score on %s " "(now %s)", child->id, pcmk_readable_score(child->sort_index), pcmk__node_name(node), pcmk_readable_score(node->weight)); } -/*! - * \internal - * \brief Apply colocation to dependent's node scores if for promoted role - * - * \param[in,out] data Colocation constraint to apply - * \param[in,out] user_data Promotable clone that is constraint's dependent - */ -static void -apply_coloc_to_dependent(gpointer data, gpointer user_data) -{ - pcmk__colocation_t *colocation = data; - pcmk_resource_t *clone = user_data; - pcmk_resource_t *primary = colocation->primary; - uint32_t flags = pcmk__coloc_select_default; - float factor = colocation->score / (float) PCMK_SCORE_INFINITY; - - if (colocation->dependent_role != pcmk_role_promoted) { - return; - } - if (colocation->score < PCMK_SCORE_INFINITY) { - flags = pcmk__coloc_select_active; - } - pcmk__rsc_trace(clone, "Applying colocation %s (promoted %s with %s) @%s", - colocation->id, colocation->dependent->id, - colocation->primary->id, - pcmk_readable_score(colocation->score)); - primary->cmds->add_colocated_node_scores(primary, clone, clone->id, - &clone->allowed_nodes, colocation, - factor, flags); -} - /*! * \internal * \brief Apply colocation to primary's node scores if for promoted role * * \param[in,out] data Colocation constraint to apply * \param[in,out] user_data Promotable clone that is constraint's primary */ static void apply_coloc_to_primary(gpointer data, gpointer user_data) { pcmk__colocation_t *colocation = data; pcmk_resource_t *clone = user_data; pcmk_resource_t *dependent = colocation->dependent; const float factor = colocation->score / (float) PCMK_SCORE_INFINITY; const uint32_t flags = pcmk__coloc_select_active |pcmk__coloc_select_nonnegative; if ((colocation->primary_role != pcmk_role_promoted) || !pcmk__colocation_has_influence(colocation, NULL)) { return; } pcmk__rsc_trace(clone, "Applying colocation %s (%s with promoted %s) @%s", colocation->id, colocation->dependent->id, colocation->primary->id, pcmk_readable_score(colocation->score)); dependent->cmds->add_colocated_node_scores(dependent, clone, clone->id, &clone->allowed_nodes, colocation, factor, flags); } /*! * \internal * \brief Set clone instance's sort index to its node's score * * \param[in,out] data Promotable clone instance * \param[in] user_data Parent clone of \p data */ static void set_sort_index_to_node_score(gpointer data, gpointer user_data) { pcmk_resource_t *child = (pcmk_resource_t *) data; const pcmk_resource_t *clone = (const pcmk_resource_t *) user_data; pcmk_node_t *chosen = child->fns->location(child, NULL, FALSE); if (!pcmk_is_set(child->flags, pcmk_rsc_managed) && (child->next_role == pcmk_role_promoted)) { child->sort_index = PCMK_SCORE_INFINITY; pcmk__rsc_trace(clone, "Final sort index for %s is INFINITY " "(unmanaged promoted)", child->id); } else if (chosen == NULL) { child->sort_index = -PCMK_SCORE_INFINITY; pcmk__rsc_trace(clone, "Final promotion priority for %s is %s " "(will not be active)", child->id, pcmk_readable_score(-PCMK_SCORE_INFINITY)); } else if (child->sort_index < 0) { pcmk__rsc_trace(clone, "Final sort index for %s is %d (ignoring node score)", child->id, child->sort_index); } else { const pcmk_node_t *node = g_hash_table_lookup(clone->allowed_nodes, chosen->details->id); CRM_ASSERT(node != NULL); child->sort_index = node->weight; pcmk__rsc_trace(clone, "Adding scores for %s: final sort index for %s is %d", clone->id, child->id, child->sort_index); } } /*! * \internal * \brief Sort a promotable clone's instances by descending promotion priority * * \param[in,out] clone Promotable clone to sort */ static void sort_promotable_instances(pcmk_resource_t *clone) { GList *colocations = NULL; if (pe__set_clone_flag(clone, pcmk__clone_promotion_constrained) == pcmk_rc_already) { return; } pcmk__set_rsc_flags(clone, pcmk_rsc_updating_nodes); for (GList *iter = clone->children; iter != NULL; iter = iter->next) { pcmk_resource_t *child = (pcmk_resource_t *) iter->data; pcmk__rsc_trace(clone, "Adding scores for %s: initial sort index for %s is %d", clone->id, child->id, child->sort_index); } pe__show_node_scores(true, clone, "Before", clone->allowed_nodes, clone->cluster); g_list_foreach(clone->children, add_sort_index_to_node_score, clone); - colocations = pcmk__this_with_colocations(clone); - g_list_foreach(colocations, apply_coloc_to_dependent, clone); - g_list_free(colocations); - + // "this with" colocations were already applied via set_instance_priority() colocations = pcmk__with_this_colocations(clone); g_list_foreach(colocations, apply_coloc_to_primary, clone); g_list_free(colocations); // Ban resource from all nodes if it needs a ticket but doesn't have it pcmk__require_promotion_tickets(clone); pe__show_node_scores(true, clone, "After", clone->allowed_nodes, clone->cluster); // Reset sort indexes to final node scores g_list_foreach(clone->children, set_sort_index_to_node_score, clone); // Finally, sort instances in descending order of promotion priority clone->children = g_list_sort(clone->children, cmp_promotable_instance); pcmk__clear_rsc_flags(clone, pcmk_rsc_updating_nodes); } /*! * \internal * \brief Find the active instance (if any) of an anonymous clone on a node * * \param[in] clone Anonymous clone to check * \param[in] id Instance ID (without instance number) to check * \param[in] node Node to check * * \return */ static pcmk_resource_t * find_active_anon_instance(const pcmk_resource_t *clone, const char *id, const pcmk_node_t *node) { for (GList *iter = clone->children; iter; iter = iter->next) { pcmk_resource_t *child = iter->data; pcmk_resource_t *active = NULL; // Use ->find_rsc() in case this is a cloned group active = clone->fns->find_rsc(child, id, node, pcmk_rsc_match_clone_only |pcmk_rsc_match_current_node); if (active != NULL) { return active; } } return NULL; } /* * \brief Check whether an anonymous clone instance is known on a node * * \param[in] clone Anonymous clone to check * \param[in] id Instance ID (without instance number) to check * \param[in] node Node to check * * \return true if \p id instance of \p clone is known on \p node, * otherwise false */ static bool anonymous_known_on(const pcmk_resource_t *clone, const char *id, const pcmk_node_t *node) { for (GList *iter = clone->children; iter; iter = iter->next) { pcmk_resource_t *child = iter->data; /* Use ->find_rsc() because this might be a cloned group, and knowing * that other members of the group are known here implies nothing. */ child = clone->fns->find_rsc(child, id, NULL, pcmk_rsc_match_clone_only); CRM_LOG_ASSERT(child != NULL); if (child != NULL) { if (g_hash_table_lookup(child->known_on, node->details->id)) { return true; } } } return false; } /*! * \internal * \brief Check whether a node is allowed to run a resource * * \param[in] rsc Resource to check * \param[in] node Node to check * * \return true if \p node is allowed to run \p rsc, otherwise false */ static bool is_allowed(const pcmk_resource_t *rsc, const pcmk_node_t *node) { pcmk_node_t *allowed = g_hash_table_lookup(rsc->allowed_nodes, node->details->id); return (allowed != NULL) && (allowed->weight >= 0); } /*! * \brief Check whether a clone instance's promotion score should be considered * * \param[in] rsc Promotable clone instance to check * \param[in] node Node where score would be applied * * \return true if \p rsc's promotion score should be considered on \p node, * otherwise false */ static bool promotion_score_applies(const pcmk_resource_t *rsc, const pcmk_node_t *node) { char *id = clone_strip(rsc->id); const pcmk_resource_t *parent = pe__const_top_resource(rsc, false); pcmk_resource_t *active = NULL; const char *reason = "allowed"; // Some checks apply only to anonymous clone instances if (!pcmk_is_set(rsc->flags, pcmk_rsc_unique)) { // If instance is active on the node, its score definitely applies active = find_active_anon_instance(parent, id, node); if (active == rsc) { reason = "active"; goto check_allowed; } /* If *no* instance is active on this node, this instance's score will * count if it has been probed on this node. */ if ((active == NULL) && anonymous_known_on(parent, id, node)) { reason = "probed"; goto check_allowed; } } /* If this clone's status is unknown on *all* nodes (e.g. cluster startup), * take all instances' scores into account, to make sure we use any * permanent promotion scores. */ if ((rsc->running_on == NULL) && (g_hash_table_size(rsc->known_on) == 0)) { reason = "none probed"; goto check_allowed; } /* Otherwise, we've probed and/or started the resource *somewhere*, so * consider promotion scores on nodes where we know the status. */ if ((g_hash_table_lookup(rsc->known_on, node->details->id) != NULL) || (pe_find_node_id(rsc->running_on, node->details->id) != NULL)) { reason = "known"; } else { pcmk__rsc_trace(rsc, "Ignoring %s promotion score (for %s) on %s: " "not probed", rsc->id, id, pcmk__node_name(node)); free(id); return false; } check_allowed: if (is_allowed(rsc, node)) { pcmk__rsc_trace(rsc, "Counting %s promotion score (for %s) on %s: %s", rsc->id, id, pcmk__node_name(node), reason); free(id); return true; } pcmk__rsc_trace(rsc, "Ignoring %s promotion score (for %s) on %s: not allowed", rsc->id, id, pcmk__node_name(node)); free(id); return false; } /*! * \internal * \brief Get the value of a promotion score node attribute * * \param[in] rsc Promotable clone instance to get promotion score for * \param[in] node Node to get promotion score for * \param[in] name Resource name to use in promotion score attribute name * * \return Value of promotion score node attribute for \p rsc on \p node */ static const char * promotion_attr_value(const pcmk_resource_t *rsc, const pcmk_node_t *node, const char *name) { char *attr_name = NULL; const char *attr_value = NULL; const char *target = NULL; enum pcmk__rsc_node node_type = pcmk__rsc_node_assigned; if (pcmk_is_set(rsc->flags, pcmk_rsc_unassigned)) { // Not assigned yet node_type = pcmk__rsc_node_current; } target = g_hash_table_lookup(rsc->meta, PCMK_META_CONTAINER_ATTRIBUTE_TARGET); attr_name = pcmk_promotion_score_name(name); attr_value = pcmk__node_attr(node, attr_name, target, node_type); free(attr_name); return attr_value; } /*! * \internal * \brief Get the promotion score for a clone instance on a node * * \param[in] rsc Promotable clone instance to get score for * \param[in] node Node to get score for * \param[out] is_default If non-NULL, will be set true if no score available * * \return Promotion score for \p rsc on \p node (or 0 if none) */ static int promotion_score(const pcmk_resource_t *rsc, const pcmk_node_t *node, bool *is_default) { char *name = NULL; const char *attr_value = NULL; if (is_default != NULL) { *is_default = true; } CRM_CHECK((rsc != NULL) && (node != NULL), return 0); /* If this is an instance of a cloned group, the promotion score is the sum * of all members' promotion scores. */ if (rsc->children != NULL) { int score = 0; for (const GList *iter = rsc->children; iter != NULL; iter = iter->next) { const pcmk_resource_t *child = (const pcmk_resource_t *) iter->data; bool child_default = false; int child_score = promotion_score(child, node, &child_default); if (!child_default && (is_default != NULL)) { *is_default = false; } score += child_score; } return score; } if (!promotion_score_applies(rsc, node)) { return 0; } /* For the promotion score attribute name, use the name the resource is * known as in resource history, since that's what crm_attribute --promotion * would have used. */ name = (rsc->clone_name == NULL)? rsc->id : rsc->clone_name; attr_value = promotion_attr_value(rsc, node, name); if (attr_value != NULL) { pcmk__rsc_trace(rsc, "Promotion score for %s on %s = %s", name, pcmk__node_name(node), pcmk__s(attr_value, "(unset)")); } else if (!pcmk_is_set(rsc->flags, pcmk_rsc_unique)) { /* If we don't have any resource history yet, we won't have clone_name. * In that case, for anonymous clones, try the resource name without * any instance number. */ name = clone_strip(rsc->id); if (strcmp(rsc->id, name) != 0) { attr_value = promotion_attr_value(rsc, node, name); pcmk__rsc_trace(rsc, "Promotion score for %s on %s (for %s) = %s", name, pcmk__node_name(node), rsc->id, pcmk__s(attr_value, "(unset)")); } free(name); } if (attr_value == NULL) { return 0; } if (is_default != NULL) { *is_default = false; } return char2score(attr_value); } /*! * \internal * \brief Include promotion scores in instances' node scores and priorities * * \param[in,out] rsc Promotable clone resource to update */ void pcmk__add_promotion_scores(pcmk_resource_t *rsc) { if (pe__set_clone_flag(rsc, pcmk__clone_promotion_added) == pcmk_rc_already) { return; } for (GList *iter = rsc->children; iter != NULL; iter = iter->next) { pcmk_resource_t *child_rsc = (pcmk_resource_t *) iter->data; GHashTableIter iter; pcmk_node_t *node = NULL; int score, new_score; g_hash_table_iter_init(&iter, child_rsc->allowed_nodes); while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) { if (!pcmk__node_available(node, false, false)) { /* This node will never be promoted, so don't apply the * promotion score, as that may lead to clone shuffling. */ continue; } score = promotion_score(child_rsc, node, NULL); if (score > 0) { new_score = pcmk__add_scores(node->weight, score); if (new_score != node->weight) { // Could remain INFINITY node->weight = new_score; pcmk__rsc_trace(rsc, "Added %s promotion priority (%s) to score " "on %s (now %s)", child_rsc->id, pcmk_readable_score(score), pcmk__node_name(node), pcmk_readable_score(new_score)); } } if (score > child_rsc->priority) { pcmk__rsc_trace(rsc, "Updating %s priority to promotion score " "(%d->%d)", child_rsc->id, child_rsc->priority, score); child_rsc->priority = score; } } } } /*! * \internal * \brief If a resource's current role is started, change it to unpromoted * * \param[in,out] data Resource to update * \param[in] user_data Ignored */ static void set_current_role_unpromoted(void *data, void *user_data) { pcmk_resource_t *rsc = (pcmk_resource_t *) data; if (rsc->role == pcmk_role_started) { // Promotable clones should use unpromoted role instead of started rsc->role = pcmk_role_unpromoted; } g_list_foreach(rsc->children, set_current_role_unpromoted, NULL); } /*! * \internal * \brief Set a resource's next role to unpromoted (or stopped if unassigned) * * \param[in,out] data Resource to update * \param[in] user_data Ignored */ static void set_next_role_unpromoted(void *data, void *user_data) { pcmk_resource_t *rsc = (pcmk_resource_t *) data; GList *assigned = NULL; rsc->fns->location(rsc, &assigned, FALSE); if (assigned == NULL) { pe__set_next_role(rsc, pcmk_role_stopped, "stopped instance"); } else { pe__set_next_role(rsc, pcmk_role_unpromoted, "unpromoted instance"); g_list_free(assigned); } g_list_foreach(rsc->children, set_next_role_unpromoted, NULL); } /*! * \internal * \brief Set a resource's next role to promoted if not already set * * \param[in,out] data Resource to update * \param[in] user_data Ignored */ static void set_next_role_promoted(void *data, gpointer user_data) { pcmk_resource_t *rsc = (pcmk_resource_t *) data; if (rsc->next_role == pcmk_role_unknown) { pe__set_next_role(rsc, pcmk_role_promoted, "promoted instance"); } g_list_foreach(rsc->children, set_next_role_promoted, NULL); } /*! * \internal * \brief Show instance's promotion score on node where it will be active * * \param[in,out] instance Promotable clone instance to show */ static void show_promotion_score(pcmk_resource_t *instance) { pcmk_node_t *chosen = instance->fns->location(instance, NULL, FALSE); if (pcmk_is_set(instance->cluster->flags, pcmk_sched_output_scores) && !pcmk__is_daemon && (instance->cluster->priv != NULL)) { pcmk__output_t *out = instance->cluster->priv; out->message(out, "promotion-score", instance, chosen, pcmk_readable_score(instance->sort_index)); } else if (chosen == NULL) { pcmk__rsc_debug(pe__const_top_resource(instance, false), "%s promotion score (inactive): %s (priority=%d)", instance->id, pcmk_readable_score(instance->sort_index), instance->priority); } else { pcmk__rsc_debug(pe__const_top_resource(instance, false), "%s promotion score on %s: %s (priority=%d)", instance->id, pcmk__node_name(chosen), pcmk_readable_score(instance->sort_index), instance->priority); } } /*! * \internal * \brief Set a clone instance's promotion priority * * \param[in,out] data Promotable clone instance to update * \param[in] user_data Instance's parent clone */ static void set_instance_priority(gpointer data, gpointer user_data) { pcmk_resource_t *instance = (pcmk_resource_t *) data; const pcmk_resource_t *clone = (const pcmk_resource_t *) user_data; const pcmk_node_t *chosen = NULL; enum rsc_role_e next_role = pcmk_role_unknown; GList *list = NULL; pcmk__rsc_trace(clone, "Assigning priority for %s: %s", instance->id, pcmk_role_text(instance->next_role)); if (instance->fns->state(instance, TRUE) == pcmk_role_started) { set_current_role_unpromoted(instance, NULL); } // Only an instance that will be active can be promoted chosen = instance->fns->location(instance, &list, FALSE); if (pcmk__list_of_multiple(list)) { pcmk__config_err("Cannot promote non-colocated child %s", instance->id); } g_list_free(list); if (chosen == NULL) { return; } next_role = instance->fns->state(instance, FALSE); switch (next_role) { case pcmk_role_started: case pcmk_role_unknown: // Set instance priority to its promotion score (or -1 if none) { bool is_default = false; instance->priority = promotion_score(instance, chosen, &is_default); if (is_default) { /* Default to -1 if no value is set. This allows instances * eligible for promotion to be specified based solely on * PCMK_XE_RSC_LOCATION constraints, but prevents any * instance from being promoted if neither a constraint nor * a promotion score is present. */ instance->priority = -1; } } break; case pcmk_role_unpromoted: case pcmk_role_stopped: // Instance can't be promoted instance->priority = -PCMK_SCORE_INFINITY; break; case pcmk_role_promoted: // Nothing needed (re-creating actions after scheduling fencing) break; default: CRM_CHECK(FALSE, crm_err("Unknown resource role %d for %s", next_role, instance->id)); } // Add relevant location constraint scores for promoted role apply_promoted_locations(instance, instance->rsc_location, chosen); apply_promoted_locations(instance, clone->rsc_location, chosen); // Consider instance's role-based colocations with other resources list = pcmk__this_with_colocations(instance); for (GList *iter = list; iter != NULL; iter = iter->next) { pcmk__colocation_t *cons = (pcmk__colocation_t *) iter->data; instance->cmds->apply_coloc_score(instance, cons->primary, cons, true); } g_list_free(list); instance->sort_index = instance->priority; if (next_role == pcmk_role_promoted) { instance->sort_index = PCMK_SCORE_INFINITY; } pcmk__rsc_trace(clone, "Assigning %s priority = %d", instance->id, instance->priority); } /*! * \internal * \brief Set a promotable clone instance's role * * \param[in,out] data Promotable clone instance to update * \param[in,out] user_data Pointer to count of instances chosen for promotion */ static void set_instance_role(gpointer data, gpointer user_data) { pcmk_resource_t *instance = (pcmk_resource_t *) data; int *count = (int *) user_data; const pcmk_resource_t *clone = pe__const_top_resource(instance, false); pcmk_node_t *chosen = NULL; show_promotion_score(instance); if (instance->sort_index < 0) { pcmk__rsc_trace(clone, "Not supposed to promote instance %s", instance->id); } else if ((*count < pe__clone_promoted_max(instance)) || !pcmk_is_set(clone->flags, pcmk_rsc_managed)) { chosen = node_to_be_promoted_on(instance); } if (chosen == NULL) { set_next_role_unpromoted(instance, NULL); return; } if ((instance->role < pcmk_role_promoted) && !pcmk_is_set(instance->cluster->flags, pcmk_sched_quorate) && (instance->cluster->no_quorum_policy == pcmk_no_quorum_freeze)) { crm_notice("Clone instance %s cannot be promoted without quorum", instance->id); set_next_role_unpromoted(instance, NULL); return; } chosen->count++; pcmk__rsc_info(clone, "Choosing %s (%s) on %s for promotion", instance->id, pcmk_role_text(instance->role), pcmk__node_name(chosen)); set_next_role_promoted(instance, NULL); (*count)++; } /*! * \internal * \brief Set roles for all instances of a promotable clone * * \param[in,out] rsc Promotable clone resource to update */ void pcmk__set_instance_roles(pcmk_resource_t *rsc) { int promoted = 0; GHashTableIter iter; pcmk_node_t *node = NULL; // Repurpose count to track the number of promoted instances assigned g_hash_table_iter_init(&iter, rsc->allowed_nodes); while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) { node->count = 0; } // Set instances' promotion priorities and sort by highest priority first g_list_foreach(rsc->children, set_instance_priority, rsc); sort_promotable_instances(rsc); // Choose the first N eligible instances to be promoted g_list_foreach(rsc->children, set_instance_role, &promoted); pcmk__rsc_info(rsc, "%s: Promoted %d instances of a possible %d", rsc->id, promoted, pe__clone_promoted_max(rsc)); } /*! * * \internal * \brief Create actions for promotable clone instances * * \param[in,out] clone Promotable clone to create actions for * \param[out] any_promoting Will be set true if any instance is promoting * \param[out] any_demoting Will be set true if any instance is demoting */ static void create_promotable_instance_actions(pcmk_resource_t *clone, bool *any_promoting, bool *any_demoting) { for (GList *iter = clone->children; iter != NULL; iter = iter->next) { pcmk_resource_t *instance = (pcmk_resource_t *) iter->data; instance->cmds->create_actions(instance); check_for_role_change(instance, any_demoting, any_promoting); } } /*! * \internal * \brief Reset each promotable instance's resource priority * * Reset the priority of each instance of a promotable clone to the clone's * priority (after promotion actions are scheduled, when instance priorities * were repurposed as promotion scores). * * \param[in,out] clone Promotable clone to reset */ static void reset_instance_priorities(pcmk_resource_t *clone) { for (GList *iter = clone->children; iter != NULL; iter = iter->next) { pcmk_resource_t *instance = (pcmk_resource_t *) iter->data; instance->priority = clone->priority; } } /*! * \internal * \brief Create actions specific to promotable clones * * \param[in,out] clone Promotable clone to create actions for */ void pcmk__create_promotable_actions(pcmk_resource_t *clone) { bool any_promoting = false; bool any_demoting = false; // Create actions for each clone instance individually create_promotable_instance_actions(clone, &any_promoting, &any_demoting); // Create pseudo-actions for clone as a whole pe__create_promotable_pseudo_ops(clone, any_promoting, any_demoting); // Undo our temporary repurposing of resource priority for instances reset_instance_priorities(clone); } /*! * \internal * \brief Create internal orderings for a promotable clone's instances * * \param[in,out] clone Promotable clone instance to order */ void pcmk__order_promotable_instances(pcmk_resource_t *clone) { pcmk_resource_t *previous = NULL; // Needed for ordered clones pcmk__promotable_restart_ordering(clone); for (GList *iter = clone->children; iter != NULL; iter = iter->next) { pcmk_resource_t *instance = (pcmk_resource_t *) iter->data; // Demote before promote pcmk__order_resource_actions(instance, PCMK_ACTION_DEMOTE, instance, PCMK_ACTION_PROMOTE, pcmk__ar_ordered); order_instance_promotion(clone, instance, previous); order_instance_demotion(clone, instance, previous); previous = instance; } } /*! * \internal * \brief Update dependent's allowed nodes for colocation with promotable * * \param[in,out] dependent Dependent resource to update * \param[in] primary Primary resource * \param[in] primary_node Node where an instance of the primary will be * \param[in] colocation Colocation constraint to apply */ static void update_dependent_allowed_nodes(pcmk_resource_t *dependent, const pcmk_resource_t *primary, const pcmk_node_t *primary_node, const pcmk__colocation_t *colocation) { GHashTableIter iter; pcmk_node_t *node = NULL; const char *primary_value = NULL; const char *attr = colocation->node_attribute; if (colocation->score >= PCMK_SCORE_INFINITY) { return; // Colocation is mandatory, so allowed node scores don't matter } primary_value = pcmk__colocation_node_attr(primary_node, attr, primary); pcmk__rsc_trace(colocation->primary, "Applying %s (%s with %s on %s by %s @%d) to %s", colocation->id, colocation->dependent->id, colocation->primary->id, pcmk__node_name(primary_node), attr, colocation->score, dependent->id); g_hash_table_iter_init(&iter, dependent->allowed_nodes); while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) { const char *dependent_value = pcmk__colocation_node_attr(node, attr, dependent); if (pcmk__str_eq(primary_value, dependent_value, pcmk__str_casei)) { node->weight = pcmk__add_scores(node->weight, colocation->score); pcmk__rsc_trace(colocation->primary, "Added %s score (%s) to %s (now %s)", colocation->id, pcmk_readable_score(colocation->score), pcmk__node_name(node), pcmk_readable_score(node->weight)); } } } /*! * \brief Update dependent for a colocation with a promotable clone * * \param[in] primary Primary resource in the colocation * \param[in,out] dependent Dependent resource in the colocation * \param[in] colocation Colocation constraint to apply */ void pcmk__update_dependent_with_promotable(const pcmk_resource_t *primary, pcmk_resource_t *dependent, const pcmk__colocation_t *colocation) { GList *affected_nodes = NULL; /* Build a list of all nodes where an instance of the primary will be, and * (for optional colocations) update the dependent's allowed node scores for * each one. */ for (GList *iter = primary->children; iter != NULL; iter = iter->next) { pcmk_resource_t *instance = (pcmk_resource_t *) iter->data; pcmk_node_t *node = instance->fns->location(instance, NULL, FALSE); if (node == NULL) { continue; } if (instance->fns->state(instance, FALSE) == colocation->primary_role) { update_dependent_allowed_nodes(dependent, primary, node, colocation); affected_nodes = g_list_prepend(affected_nodes, node); } } /* For mandatory colocations, add the primary's node score to the * dependent's node score for each affected node, and ban the dependent * from all other nodes. * * However, skip this for promoted-with-promoted colocations, otherwise * inactive dependent instances can't start (in the unpromoted role). */ if ((colocation->score >= PCMK_SCORE_INFINITY) && ((colocation->dependent_role != pcmk_role_promoted) || (colocation->primary_role != pcmk_role_promoted))) { pcmk__rsc_trace(colocation->primary, "Applying %s (mandatory %s with %s) to %s", colocation->id, colocation->dependent->id, colocation->primary->id, dependent->id); pcmk__colocation_intersect_nodes(dependent, primary, colocation, affected_nodes, true); } g_list_free(affected_nodes); } /*! * \internal * \brief Update dependent priority for colocation with promotable * * \param[in] primary Primary resource in the colocation * \param[in,out] dependent Dependent resource in the colocation * \param[in] colocation Colocation constraint to apply + * + * \return The score added to the dependent's priority */ -void +int pcmk__update_promotable_dependent_priority(const pcmk_resource_t *primary, pcmk_resource_t *dependent, const pcmk__colocation_t *colocation) { pcmk_resource_t *primary_instance = NULL; // Look for a primary instance where dependent will be primary_instance = pcmk__find_compatible_instance(dependent, primary, colocation->primary_role, false); if (primary_instance != NULL) { // Add primary instance's priority to dependent's int new_priority = pcmk__add_scores(dependent->priority, colocation->score); pcmk__rsc_trace(colocation->primary, "Applying %s (%s with %s) to %s priority " "(%s + %s = %s)", colocation->id, colocation->dependent->id, colocation->primary->id, dependent->id, pcmk_readable_score(dependent->priority), pcmk_readable_score(colocation->score), pcmk_readable_score(new_priority)); dependent->priority = new_priority; + return colocation->score; + } - } else if (colocation->score >= PCMK_SCORE_INFINITY) { + if (colocation->score >= PCMK_SCORE_INFINITY) { // Mandatory colocation, but primary won't be here pcmk__rsc_trace(colocation->primary, "Applying %s (%s with %s) to %s: can't be promoted", colocation->id, colocation->dependent->id, colocation->primary->id, dependent->id); dependent->priority = -PCMK_SCORE_INFINITY; + return -PCMK_SCORE_INFINITY; } + return 0; }