diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-03 13:39:28 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-03 13:39:28 +0000 |
commit | 924f5ea83e48277e014ebf0d19a27187cb93e2f7 (patch) | |
tree | 75920a275bba045f6d108204562c218a9a26ea15 /lib/common/options.c | |
parent | Adding upstream version 2.1.7. (diff) | |
download | pacemaker-upstream.tar.xz pacemaker-upstream.zip |
Adding upstream version 2.1.8~rc1.upstream/2.1.8_rc1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'lib/common/options.c')
-rw-r--r-- | lib/common/options.c | 1489 |
1 files changed, 1272 insertions, 217 deletions
diff --git a/lib/common/options.c b/lib/common/options.c index 2d86ebc..ba64959 100644 --- a/lib/common/options.c +++ b/lib/common/options.c @@ -1,5 +1,5 @@ /* - * Copyright 2004-2022 the Pacemaker project contributors + * Copyright 2004-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -20,6 +20,7 @@ #include <sys/stat.h> #include <crm/crm.h> +#include <crm/common/xml.h> void pcmk__cli_help(char cmd) @@ -39,6 +40,1035 @@ pcmk__cli_help(char cmd) /* + * Option metadata + */ + +static const pcmk__cluster_option_t cluster_options[] = { + /* name, old name, type, allowed values, + * default value, validator, + * flags, + * short description, + * long description + */ + { + PCMK_OPT_DC_VERSION, NULL, PCMK_VALUE_VERSION, NULL, + NULL, NULL, + pcmk__opt_controld|pcmk__opt_generated, + N_("Pacemaker version on cluster node elected Designated Controller " + "(DC)"), + N_("Includes a hash which identifies the exact revision the code was " + "built from. Used for diagnostic purposes."), + }, + { + PCMK_OPT_CLUSTER_INFRASTRUCTURE, NULL, PCMK_VALUE_STRING, NULL, + NULL, NULL, + pcmk__opt_controld|pcmk__opt_generated, + N_("The messaging layer on which Pacemaker is currently running"), + N_("Used for informational and diagnostic purposes."), + }, + { + PCMK_OPT_CLUSTER_NAME, NULL, PCMK_VALUE_STRING, NULL, + NULL, NULL, + pcmk__opt_controld, + N_("An arbitrary name for the cluster"), + N_("This optional value is mostly for users' convenience as desired " + "in administration, but may also be used in Pacemaker " + "configuration rules via the #cluster-name node attribute, and " + "by higher-level tools and resource agents."), + }, + { + PCMK_OPT_DC_DEADTIME, NULL, PCMK_VALUE_DURATION, NULL, + "20s", pcmk__valid_interval_spec, + pcmk__opt_controld, + N_("How long to wait for a response from other nodes during start-up"), + N_("The optimal value will depend on the speed and load of your " + "network and the type of switches used."), + }, + { + PCMK_OPT_CLUSTER_RECHECK_INTERVAL, NULL, PCMK_VALUE_DURATION, NULL, + "15min", pcmk__valid_interval_spec, + pcmk__opt_controld, + N_("Polling interval to recheck cluster state and evaluate rules " + "with date specifications"), + N_("Pacemaker is primarily event-driven, and looks ahead to know when " + "to recheck cluster state for failure-timeout settings and most " + "time-based rules. However, it will also recheck the cluster after " + "this amount of inactivity, to evaluate rules with date " + "specifications and serve as a fail-safe for certain types of " + "scheduler bugs. A value of 0 disables polling. A positive value " + "sets an interval in seconds, unless other units are specified " + "(for example, \"5min\")."), + }, + { + PCMK_OPT_FENCE_REACTION, NULL, PCMK_VALUE_SELECT, + PCMK_VALUE_STOP ", " PCMK_VALUE_PANIC, + PCMK_VALUE_STOP, NULL, + pcmk__opt_controld, + N_("How a cluster node should react if notified of its own fencing"), + N_("A cluster node may receive notification of a \"succeeded\" " + "fencing that targeted it if fencing is misconfigured, or if " + "fabric fencing is in use that doesn't cut cluster communication. " + "Use \"stop\" to attempt to immediately stop Pacemaker and stay " + "stopped, or \"panic\" to attempt to immediately reboot the local " + "node, falling back to stop on failure."), + }, + { + PCMK_OPT_ELECTION_TIMEOUT, NULL, PCMK_VALUE_DURATION, NULL, + "2min", pcmk__valid_interval_spec, + pcmk__opt_controld|pcmk__opt_advanced, + N_("Declare an election failed if it is not decided within this much " + "time. If you need to adjust this value, it probably indicates " + "the presence of a bug."), + NULL, + }, + { + PCMK_OPT_SHUTDOWN_ESCALATION, NULL, PCMK_VALUE_DURATION, NULL, + "20min", pcmk__valid_interval_spec, + pcmk__opt_controld|pcmk__opt_advanced, + N_("Exit immediately if shutdown does not complete within this much " + "time. If you need to adjust this value, it probably indicates " + "the presence of a bug."), + NULL, + }, + { + PCMK_OPT_JOIN_INTEGRATION_TIMEOUT, "crmd-integration-timeout", + PCMK_VALUE_DURATION, NULL, + "3min", pcmk__valid_interval_spec, + pcmk__opt_controld|pcmk__opt_advanced, + N_("If you need to adjust this value, it probably indicates " + "the presence of a bug."), + NULL, + }, + { + PCMK_OPT_JOIN_FINALIZATION_TIMEOUT, "crmd-finalization-timeout", + PCMK_VALUE_DURATION, NULL, + "30min", pcmk__valid_interval_spec, + pcmk__opt_controld|pcmk__opt_advanced, + N_("If you need to adjust this value, it probably indicates " + "the presence of a bug."), + NULL, + }, + { + PCMK_OPT_TRANSITION_DELAY, "crmd-transition-delay", PCMK_VALUE_DURATION, + NULL, + "0s", pcmk__valid_interval_spec, + pcmk__opt_controld|pcmk__opt_advanced, + N_("Enabling this option will slow down cluster recovery under all " + "conditions"), + N_("Delay cluster recovery for this much time to allow for additional " + "events to occur. Useful if your configuration is sensitive to " + "the order in which ping updates arrive."), + }, + { + PCMK_OPT_NO_QUORUM_POLICY, NULL, PCMK_VALUE_SELECT, + PCMK_VALUE_STOP ", " PCMK_VALUE_FREEZE ", " PCMK_VALUE_IGNORE + ", " PCMK_VALUE_DEMOTE ", " PCMK_VALUE_FENCE_LEGACY, + PCMK_VALUE_STOP, pcmk__valid_no_quorum_policy, + pcmk__opt_schedulerd, + N_("What to do when the cluster does not have quorum"), + NULL, + }, + { + PCMK_OPT_SHUTDOWN_LOCK, NULL, PCMK_VALUE_BOOLEAN, NULL, + PCMK_VALUE_FALSE, pcmk__valid_boolean, + pcmk__opt_schedulerd, + N_("Whether to lock resources to a cleanly shut down node"), + N_("When true, resources active on a node when it is cleanly shut down " + "are kept \"locked\" to that node (not allowed to run elsewhere) " + "until they start again on that node after it rejoins (or for at " + "most shutdown-lock-limit, if set). Stonith resources and " + "Pacemaker Remote connections are never locked. Clone and bundle " + "instances and the promoted role of promotable clones are " + "currently never locked, though support could be added in a future " + "release."), + }, + { + PCMK_OPT_SHUTDOWN_LOCK_LIMIT, NULL, PCMK_VALUE_DURATION, NULL, + "0", pcmk__valid_interval_spec, + pcmk__opt_schedulerd, + N_("Do not lock resources to a cleanly shut down node longer than " + "this"), + N_("If shutdown-lock is true and this is set to a nonzero time " + "duration, shutdown locks will expire after this much time has " + "passed since the shutdown was initiated, even if the node has not " + "rejoined."), + }, + { + PCMK_OPT_ENABLE_ACL, NULL, PCMK_VALUE_BOOLEAN, NULL, + PCMK_VALUE_FALSE, pcmk__valid_boolean, + pcmk__opt_based, + N_("Enable Access Control Lists (ACLs) for the CIB"), + NULL, + }, + { + PCMK_OPT_SYMMETRIC_CLUSTER, NULL, PCMK_VALUE_BOOLEAN, NULL, + PCMK_VALUE_TRUE, pcmk__valid_boolean, + pcmk__opt_schedulerd, + N_("Whether resources can run on any node by default"), + NULL, + }, + { + PCMK_OPT_MAINTENANCE_MODE, NULL, PCMK_VALUE_BOOLEAN, NULL, + PCMK_VALUE_FALSE, pcmk__valid_boolean, + pcmk__opt_schedulerd, + N_("Whether the cluster should refrain from monitoring, starting, and " + "stopping resources"), + NULL, + }, + { + PCMK_OPT_START_FAILURE_IS_FATAL, NULL, PCMK_VALUE_BOOLEAN, NULL, + PCMK_VALUE_TRUE, pcmk__valid_boolean, + pcmk__opt_schedulerd, + N_("Whether a start failure should prevent a resource from being " + "recovered on the same node"), + N_("When true, the cluster will immediately ban a resource from a node " + "if it fails to start there. When false, the cluster will instead " + "check the resource's fail count against its migration-threshold.") + }, + { + PCMK_OPT_ENABLE_STARTUP_PROBES, NULL, PCMK_VALUE_BOOLEAN, NULL, + PCMK_VALUE_TRUE, pcmk__valid_boolean, + pcmk__opt_schedulerd, + N_("Whether the cluster should check for active resources during " + "start-up"), + NULL, + }, + + // Fencing-related options + { + PCMK_OPT_STONITH_ENABLED, NULL, PCMK_VALUE_BOOLEAN, NULL, + PCMK_VALUE_TRUE, pcmk__valid_boolean, + pcmk__opt_schedulerd|pcmk__opt_advanced, + N_("Whether nodes may be fenced as part of recovery"), + N_("If false, unresponsive nodes are immediately assumed to be " + "harmless, and resources that were active on them may be recovered " + "elsewhere. This can result in a \"split-brain\" situation, " + "potentially leading to data loss and/or service unavailability."), + }, + { + PCMK_OPT_STONITH_ACTION, NULL, PCMK_VALUE_SELECT, + PCMK_ACTION_REBOOT ", " PCMK_ACTION_OFF ", " PCMK__ACTION_POWEROFF, + PCMK_ACTION_REBOOT, pcmk__is_fencing_action, + pcmk__opt_schedulerd, + N_("Action to send to fence device when a node needs to be fenced " + "(\"poweroff\" is a deprecated alias for \"off\")"), + NULL, + }, + { + PCMK_OPT_STONITH_TIMEOUT, NULL, PCMK_VALUE_DURATION, NULL, + "60s", pcmk__valid_interval_spec, + pcmk__opt_schedulerd, + N_("How long to wait for on, off, and reboot fence actions to complete " + "by default"), + NULL, + }, + { + PCMK_OPT_HAVE_WATCHDOG, NULL, PCMK_VALUE_BOOLEAN, NULL, + PCMK_VALUE_FALSE, pcmk__valid_boolean, + pcmk__opt_schedulerd|pcmk__opt_generated, + N_("Whether watchdog integration is enabled"), + N_("This is set automatically by the cluster according to whether SBD " + "is detected to be in use. User-configured values are ignored. " + "The value `true` is meaningful if diskless SBD is used and " + "`stonith-watchdog-timeout` is nonzero. In that case, if fencing " + "is required, watchdog-based self-fencing will be performed via " + "SBD without requiring a fencing resource explicitly configured."), + }, + { + /* @COMPAT Currently, unparsable values default to -1 (auto-calculate), + * while missing values default to 0 (disable). All values are accepted + * (unless the controller finds that the value conflicts with the + * SBD_WATCHDOG_TIMEOUT). + * + * At a compatibility break: properly validate as a timeout, let + * either negative values or a particular string like "auto" mean auto- + * calculate, and use 0 as the single default for when the option either + * is unset or fails to validate. + */ + PCMK_OPT_STONITH_WATCHDOG_TIMEOUT, NULL, PCMK_VALUE_TIMEOUT, NULL, + "0", NULL, + pcmk__opt_controld, + N_("How long before nodes can be assumed to be safely down when " + "watchdog-based self-fencing via SBD is in use"), + N_("If this is set to a positive value, lost nodes are assumed to " + "achieve self-fencing using watchdog-based SBD within this much " + "time. This does not require a fencing resource to be explicitly " + "configured, though a fence_watchdog resource can be configured, to " + "limit use to specific nodes. If this is set to 0 (the default), " + "the cluster will never assume watchdog-based self-fencing. If this " + "is set to a negative value, the cluster will use twice the local " + "value of the `SBD_WATCHDOG_TIMEOUT` environment variable if that " + "is positive, or otherwise treat this as 0. WARNING: When used, " + "this timeout must be larger than `SBD_WATCHDOG_TIMEOUT` on all " + "nodes that use watchdog-based SBD, and Pacemaker will refuse to " + "start on any of those nodes where this is not true for the local " + "value or SBD is not active. When this is set to a negative value, " + "`SBD_WATCHDOG_TIMEOUT` must be set to the same value on all nodes " + "that use SBD, otherwise data corruption or loss could occur."), + }, + { + PCMK_OPT_STONITH_MAX_ATTEMPTS, NULL, PCMK_VALUE_SCORE, NULL, + "10", pcmk__valid_positive_int, + pcmk__opt_controld, + N_("How many times fencing can fail before it will no longer be " + "immediately re-attempted on a target"), + NULL, + }, + { + PCMK_OPT_CONCURRENT_FENCING, NULL, PCMK_VALUE_BOOLEAN, NULL, + PCMK__CONCURRENT_FENCING_DEFAULT, pcmk__valid_boolean, + pcmk__opt_schedulerd, + N_("Allow performing fencing operations in parallel"), + NULL, + }, + { + PCMK_OPT_STARTUP_FENCING, NULL, PCMK_VALUE_BOOLEAN, NULL, + PCMK_VALUE_TRUE, pcmk__valid_boolean, + pcmk__opt_schedulerd|pcmk__opt_advanced, + N_("Whether to fence unseen nodes at start-up"), + N_("Setting this to false may lead to a \"split-brain\" situation, " + "potentially leading to data loss and/or service unavailability."), + }, + { + PCMK_OPT_PRIORITY_FENCING_DELAY, NULL, PCMK_VALUE_DURATION, NULL, + "0", pcmk__valid_interval_spec, + pcmk__opt_schedulerd, + N_("Apply fencing delay targeting the lost nodes with the highest " + "total resource priority"), + N_("Apply specified delay for the fencings that are targeting the lost " + "nodes with the highest total resource priority in case we don't " + "have the majority of the nodes in our cluster partition, so that " + "the more significant nodes potentially win any fencing match, " + "which is especially meaningful under split-brain of 2-node " + "cluster. A promoted resource instance takes the base priority + 1 " + "on calculation if the base priority is not 0. Any static/random " + "delays that are introduced by `pcmk_delay_base/max` configured " + "for the corresponding fencing resources will be added to this " + "delay. This delay should be significantly greater than, safely " + "twice, the maximum `pcmk_delay_base/max`. By default, priority " + "fencing delay is disabled."), + }, + { + PCMK_OPT_NODE_PENDING_TIMEOUT, NULL, PCMK_VALUE_DURATION, NULL, + "0", pcmk__valid_interval_spec, + pcmk__opt_schedulerd, + N_("How long to wait for a node that has joined the cluster to join " + "the controller process group"), + N_("Fence nodes that do not join the controller process group within " + "this much time after joining the cluster, to allow the cluster " + "to continue managing resources. A value of 0 means never fence " + "pending nodes. Setting the value to 2h means fence nodes after " + "2 hours."), + }, + { + PCMK_OPT_CLUSTER_DELAY, NULL, PCMK_VALUE_DURATION, NULL, + "60s", pcmk__valid_interval_spec, + pcmk__opt_schedulerd, + N_("Maximum time for node-to-node communication"), + N_("The node elected Designated Controller (DC) will consider an action " + "failed if it does not get a response from the node executing the " + "action within this time (after considering the action's own " + "timeout). The \"correct\" value will depend on the speed and " + "load of your network and cluster nodes.") + }, + + // Limits + { + PCMK_OPT_LOAD_THRESHOLD, NULL, PCMK_VALUE_PERCENTAGE, NULL, + "80%", pcmk__valid_percentage, + pcmk__opt_controld, + N_("Maximum amount of system load that should be used by cluster " + "nodes"), + N_("The cluster will slow down its recovery process when the amount of " + "system resources used (currently CPU) approaches this limit"), + }, + { + PCMK_OPT_NODE_ACTION_LIMIT, NULL, PCMK_VALUE_INTEGER, NULL, + "0", pcmk__valid_int, + pcmk__opt_controld, + N_("Maximum number of jobs that can be scheduled per node (defaults to " + "2x cores)"), + NULL, + }, + { + PCMK_OPT_BATCH_LIMIT, NULL, PCMK_VALUE_INTEGER, NULL, + "0", pcmk__valid_int, + pcmk__opt_schedulerd, + N_("Maximum number of jobs that the cluster may execute in parallel " + "across all nodes"), + N_("The \"correct\" value will depend on the speed and load of your " + "network and cluster nodes. If set to 0, the cluster will " + "impose a dynamically calculated limit when any node has a " + "high load."), + }, + { + PCMK_OPT_MIGRATION_LIMIT, NULL, PCMK_VALUE_INTEGER, NULL, + "-1", pcmk__valid_int, + pcmk__opt_schedulerd, + N_("The number of live migration actions that the cluster is allowed " + "to execute in parallel on a node (-1 means no limit)"), + NULL, + }, + { + /* @TODO This is actually ignored if not strictly positive. We should + * overhaul value types in Pacemaker Explained. There are lots of + * inaccurate ranges (assumptions of 32-bit width, "nonnegative" when + * positive is required, etc.). + * + * Maybe a single integer type with the allowed range specified would be + * better. + * + * Drop the PCMK_VALUE_NONNEGATIVE_INTEGER constant if we do this before + * a release. + */ + PCMK_OPT_CLUSTER_IPC_LIMIT, NULL, PCMK_VALUE_NONNEGATIVE_INTEGER, NULL, + "500", pcmk__valid_positive_int, + pcmk__opt_based, + N_("Maximum IPC message backlog before disconnecting a cluster daemon"), + N_("Raise this if log has \"Evicting client\" messages for cluster " + "daemon PIDs (a good value is the number of resources in the " + "cluster multiplied by the number of nodes)."), + }, + + // Orphans and stopping + { + PCMK_OPT_STOP_ALL_RESOURCES, NULL, PCMK_VALUE_BOOLEAN, NULL, + PCMK_VALUE_FALSE, pcmk__valid_boolean, + pcmk__opt_schedulerd, + N_("Whether the cluster should stop all active resources"), + NULL, + }, + { + PCMK_OPT_STOP_ORPHAN_RESOURCES, NULL, PCMK_VALUE_BOOLEAN, NULL, + PCMK_VALUE_TRUE, pcmk__valid_boolean, + pcmk__opt_schedulerd, + N_("Whether to stop resources that were removed from the " + "configuration"), + NULL, + }, + { + PCMK_OPT_STOP_ORPHAN_ACTIONS, NULL, PCMK_VALUE_BOOLEAN, NULL, + PCMK_VALUE_TRUE, pcmk__valid_boolean, + pcmk__opt_schedulerd, + N_("Whether to cancel recurring actions removed from the " + "configuration"), + NULL, + }, + { + PCMK__OPT_REMOVE_AFTER_STOP, NULL, PCMK_VALUE_BOOLEAN, NULL, + PCMK_VALUE_FALSE, pcmk__valid_boolean, + pcmk__opt_schedulerd|pcmk__opt_deprecated, + N_("Whether to remove stopped resources from the executor"), + N_("Values other than default are poorly tested and potentially " + "dangerous."), + }, + + // Storing inputs + { + PCMK_OPT_PE_ERROR_SERIES_MAX, NULL, PCMK_VALUE_INTEGER, NULL, + "-1", pcmk__valid_int, + pcmk__opt_schedulerd, + N_("The number of scheduler inputs resulting in errors to save"), + N_("Zero to disable, -1 to store unlimited."), + }, + { + PCMK_OPT_PE_WARN_SERIES_MAX, NULL, PCMK_VALUE_INTEGER, NULL, + "5000", pcmk__valid_int, + pcmk__opt_schedulerd, + N_("The number of scheduler inputs resulting in warnings to save"), + N_("Zero to disable, -1 to store unlimited."), + }, + { + PCMK_OPT_PE_INPUT_SERIES_MAX, NULL, PCMK_VALUE_INTEGER, NULL, + "4000", pcmk__valid_int, + pcmk__opt_schedulerd, + N_("The number of scheduler inputs without errors or warnings to save"), + N_("Zero to disable, -1 to store unlimited."), + }, + + // Node health + { + PCMK_OPT_NODE_HEALTH_STRATEGY, NULL, PCMK_VALUE_SELECT, + PCMK_VALUE_NONE ", " PCMK_VALUE_MIGRATE_ON_RED ", " + PCMK_VALUE_ONLY_GREEN ", " PCMK_VALUE_PROGRESSIVE ", " + PCMK_VALUE_CUSTOM, + PCMK_VALUE_NONE, pcmk__validate_health_strategy, + pcmk__opt_schedulerd, + N_("How cluster should react to node health attributes"), + N_("Requires external entities to create node attributes (named with " + "the prefix \"#health\") with values \"red\", \"yellow\", or " + "\"green\".") + }, + { + PCMK_OPT_NODE_HEALTH_BASE, NULL, PCMK_VALUE_SCORE, NULL, + "0", pcmk__valid_int, + pcmk__opt_schedulerd, + N_("Base health score assigned to a node"), + N_("Only used when \"node-health-strategy\" is set to " + "\"progressive\"."), + }, + { + PCMK_OPT_NODE_HEALTH_GREEN, NULL, PCMK_VALUE_SCORE, NULL, + "0", pcmk__valid_int, + pcmk__opt_schedulerd, + N_("The score to use for a node health attribute whose value is " + "\"green\""), + N_("Only used when \"node-health-strategy\" is set to \"custom\" or " + "\"progressive\"."), + }, + { + PCMK_OPT_NODE_HEALTH_YELLOW, NULL, PCMK_VALUE_SCORE, NULL, + "0", pcmk__valid_int, + pcmk__opt_schedulerd, + N_("The score to use for a node health attribute whose value is " + "\"yellow\""), + N_("Only used when \"node-health-strategy\" is set to \"custom\" or " + "\"progressive\"."), + }, + { + PCMK_OPT_NODE_HEALTH_RED, NULL, PCMK_VALUE_SCORE, NULL, + "-INFINITY", pcmk__valid_int, + pcmk__opt_schedulerd, + N_("The score to use for a node health attribute whose value is " + "\"red\""), + N_("Only used when \"node-health-strategy\" is set to \"custom\" or " + "\"progressive\".") + }, + + // Placement strategy + { + PCMK_OPT_PLACEMENT_STRATEGY, NULL, PCMK_VALUE_SELECT, + PCMK_VALUE_DEFAULT ", " PCMK_VALUE_UTILIZATION ", " + PCMK_VALUE_MINIMAL ", " PCMK_VALUE_BALANCED, + PCMK_VALUE_DEFAULT, pcmk__valid_placement_strategy, + pcmk__opt_schedulerd, + N_("How the cluster should allocate resources to nodes"), + NULL, + }, + + { NULL, }, +}; + +static const pcmk__cluster_option_t fencing_params[] = { + /* name, old name, type, allowed values, + * default value, validator, + * flags, + * short description, + * long description + */ + { + PCMK_STONITH_HOST_ARGUMENT, NULL, PCMK_VALUE_STRING, NULL, + "port", NULL, + pcmk__opt_advanced, + N_("An alternate parameter to supply instead of 'port'"), + N_("Some devices do not support the standard 'port' parameter or may " + "provide additional ones. Use this to specify an alternate, device-" + "specific, parameter that should indicate the machine to be " + "fenced. A value of \"none\" can be used to tell the cluster not " + "to supply any additional parameters."), + }, + { + PCMK_STONITH_HOST_MAP, NULL, PCMK_VALUE_STRING, NULL, + NULL, NULL, + pcmk__opt_none, + N_("A mapping of node names to port numbers for devices that do not " + "support node names."), + N_("For example, \"node1:1;node2:2,3\" would tell the cluster to use " + "port 1 for node1 and ports 2 and 3 for node2."), + }, + { + PCMK_STONITH_HOST_LIST, NULL, PCMK_VALUE_STRING, NULL, + NULL, NULL, + pcmk__opt_none, + N_("Nodes targeted by this device"), + N_("Comma-separated list of nodes that can be targeted by this device " + "(for example, \"node1,node2,node3\"). If pcmk_host_check is " + "\"static-list\", either this or pcmk_host_map must be set."), + }, + { + PCMK_STONITH_HOST_CHECK, NULL, PCMK_VALUE_SELECT, + PCMK_VALUE_DYNAMIC_LIST ", " PCMK_VALUE_STATIC_LIST ", " + PCMK_VALUE_STATUS ", " PCMK_VALUE_NONE, + NULL, NULL, + pcmk__opt_none, + N_("How to determine which nodes can be targeted by the device"), + N_("Use \"dynamic-list\" to query the device via the 'list' command; " + "\"static-list\" to check the pcmk_host_list attribute; " + "\"status\" to query the device via the 'status' command; or " + "\"none\" to assume every device can fence every node. " + "The default value is \"static-list\" if pcmk_host_map or " + "pcmk_host_list is set; otherwise \"dynamic-list\" if the device " + "supports the list operation; otherwise \"status\" if the device " + "supports the status operation; otherwise \"none\""), + }, + { + PCMK_STONITH_DELAY_MAX, NULL, PCMK_VALUE_DURATION, NULL, + "0s", NULL, + pcmk__opt_none, + N_("Enable a delay of no more than the time specified before executing " + "fencing actions."), + N_("Enable a delay of no more than the time specified before executing " + "fencing actions. Pacemaker derives the overall delay by taking " + "the value of pcmk_delay_base and adding a random delay value such " + "that the sum is kept below this maximum."), + }, + { + PCMK_STONITH_DELAY_BASE, NULL, PCMK_VALUE_STRING, NULL, + "0s", NULL, + pcmk__opt_none, + N_("Enable a base delay for fencing actions and specify base delay " + "value."), + N_("This enables a static delay for fencing actions, which can help " + "avoid \"death matches\" where two nodes try to fence each other " + "at the same time. If pcmk_delay_max is also used, a random delay " + "will be added such that the total delay is kept below that value. " + "This can be set to a single time value to apply to any node " + "targeted by this device (useful if a separate device is " + "configured for each target), or to a node map (for example, " + "\"node1:1s;node2:5\") to set a different value for each target."), + }, + { + PCMK_STONITH_ACTION_LIMIT, NULL, PCMK_VALUE_INTEGER, NULL, + "1", NULL, + pcmk__opt_none, + N_("The maximum number of actions can be performed in parallel on this " + "device"), + N_("Cluster property concurrent-fencing=\"true\" needs to be " + "configured first. Then use this to specify the maximum number of " + "actions can be performed in parallel on this device. A value of " + "-1 means an unlimited number of actions can be performed in " + "parallel."), + }, + { + "pcmk_reboot_action", NULL, PCMK_VALUE_STRING, NULL, + PCMK_ACTION_REBOOT, NULL, + pcmk__opt_advanced, + N_("An alternate command to run instead of 'reboot'"), + N_("Some devices do not support the standard commands or may provide " + "additional ones. Use this to specify an alternate, device-" + "specific, command that implements the 'reboot' action."), + }, + { + "pcmk_reboot_timeout", NULL, PCMK_VALUE_TIMEOUT, NULL, + "60s", NULL, + pcmk__opt_advanced, + N_("Specify an alternate timeout to use for 'reboot' actions instead " + "of stonith-timeout"), + N_("Some devices need much more/less time to complete than normal. " + "Use this to specify an alternate, device-specific, timeout for " + "'reboot' actions."), + }, + { + "pcmk_reboot_retries", NULL, PCMK_VALUE_INTEGER, NULL, + "2", NULL, + pcmk__opt_advanced, + N_("The maximum number of times to try the 'reboot' command within the " + "timeout period"), + N_("Some devices do not support multiple connections. Operations may " + "\"fail\" if the device is busy with another task. In that case, " + "Pacemaker will automatically retry the operation if there is time " + "remaining. Use this option to alter the number of times Pacemaker " + "tries a 'reboot' action before giving up."), + }, + { + "pcmk_off_action", NULL, PCMK_VALUE_STRING, NULL, + PCMK_ACTION_OFF, NULL, + pcmk__opt_advanced, + N_("An alternate command to run instead of 'off'"), + N_("Some devices do not support the standard commands or may provide " + "additional ones. Use this to specify an alternate, device-" + "specific, command that implements the 'off' action."), + }, + { + "pcmk_off_timeout", NULL, PCMK_VALUE_TIMEOUT, NULL, + "60s", NULL, + pcmk__opt_advanced, + N_("Specify an alternate timeout to use for 'off' actions instead of " + "stonith-timeout"), + N_("Some devices need much more/less time to complete than normal. " + "Use this to specify an alternate, device-specific, timeout for " + "'off' actions."), + }, + { + "pcmk_off_retries", NULL, PCMK_VALUE_INTEGER, NULL, + "2", NULL, + pcmk__opt_advanced, + N_("The maximum number of times to try the 'off' command within the " + "timeout period"), + N_("Some devices do not support multiple connections. Operations may " + "\"fail\" if the device is busy with another task. In that case, " + "Pacemaker will automatically retry the operation if there is time " + "remaining. Use this option to alter the number of times Pacemaker " + "tries a 'off' action before giving up."), + }, + { + "pcmk_on_action", NULL, PCMK_VALUE_STRING, NULL, + PCMK_ACTION_ON, NULL, + pcmk__opt_advanced, + N_("An alternate command to run instead of 'on'"), + N_("Some devices do not support the standard commands or may provide " + "additional ones. Use this to specify an alternate, device-" + "specific, command that implements the 'on' action."), + }, + { + "pcmk_on_timeout", NULL, PCMK_VALUE_TIMEOUT, NULL, + "60s", NULL, + pcmk__opt_advanced, + N_("Specify an alternate timeout to use for 'on' actions instead of " + "stonith-timeout"), + N_("Some devices need much more/less time to complete than normal. " + "Use this to specify an alternate, device-specific, timeout for " + "'on' actions."), + }, + { + "pcmk_on_retries", NULL, PCMK_VALUE_INTEGER, NULL, + "2", NULL, + pcmk__opt_advanced, + N_("The maximum number of times to try the 'on' command within the " + "timeout period"), + N_("Some devices do not support multiple connections. Operations may " + "\"fail\" if the device is busy with another task. In that case, " + "Pacemaker will automatically retry the operation if there is time " + "remaining. Use this option to alter the number of times Pacemaker " + "tries a 'on' action before giving up."), + }, + { + "pcmk_list_action", NULL, PCMK_VALUE_STRING, NULL, + PCMK_ACTION_LIST, NULL, + pcmk__opt_advanced, + N_("An alternate command to run instead of 'list'"), + N_("Some devices do not support the standard commands or may provide " + "additional ones. Use this to specify an alternate, device-" + "specific, command that implements the 'list' action."), + }, + { + "pcmk_list_timeout", NULL, PCMK_VALUE_TIMEOUT, NULL, + "60s", NULL, + pcmk__opt_advanced, + N_("Specify an alternate timeout to use for 'list' actions instead of " + "stonith-timeout"), + N_("Some devices need much more/less time to complete than normal. " + "Use this to specify an alternate, device-specific, timeout for " + "'list' actions."), + }, + { + "pcmk_list_retries", NULL, PCMK_VALUE_INTEGER, NULL, + "2", NULL, + pcmk__opt_advanced, + N_("The maximum number of times to try the 'list' command within the " + "timeout period"), + N_("Some devices do not support multiple connections. Operations may " + "\"fail\" if the device is busy with another task. In that case, " + "Pacemaker will automatically retry the operation if there is time " + "remaining. Use this option to alter the number of times Pacemaker " + "tries a 'list' action before giving up."), + }, + { + "pcmk_monitor_action", NULL, PCMK_VALUE_STRING, NULL, + PCMK_ACTION_MONITOR, NULL, + pcmk__opt_advanced, + N_("An alternate command to run instead of 'monitor'"), + N_("Some devices do not support the standard commands or may provide " + "additional ones. Use this to specify an alternate, device-" + "specific, command that implements the 'monitor' action."), + }, + { + "pcmk_monitor_timeout", NULL, PCMK_VALUE_TIMEOUT, NULL, + "60s", NULL, + pcmk__opt_advanced, + N_("Specify an alternate timeout to use for 'monitor' actions instead " + "of stonith-timeout"), + N_("Some devices need much more/less time to complete than normal. " + "Use this to specify an alternate, device-specific, timeout for " + "'monitor' actions."), + }, + { + "pcmk_monitor_retries", NULL, PCMK_VALUE_INTEGER, NULL, + "2", NULL, + pcmk__opt_advanced, + N_("The maximum number of times to try the 'monitor' command within " + "the timeout period"), + N_("Some devices do not support multiple connections. Operations may " + "\"fail\" if the device is busy with another task. In that case, " + "Pacemaker will automatically retry the operation if there is time " + "remaining. Use this option to alter the number of times Pacemaker " + "tries a 'monitor' action before giving up."), + }, + { + "pcmk_status_action", NULL, PCMK_VALUE_STRING, NULL, + PCMK_ACTION_STATUS, NULL, + pcmk__opt_advanced, + N_("An alternate command to run instead of 'status'"), + N_("Some devices do not support the standard commands or may provide " + "additional ones. Use this to specify an alternate, device-" + "specific, command that implements the 'status' action."), + }, + { + "pcmk_status_timeout", NULL, PCMK_VALUE_TIMEOUT, NULL, + "60s", NULL, + pcmk__opt_advanced, + N_("Specify an alternate timeout to use for 'status' actions instead " + "of stonith-timeout"), + N_("Some devices need much more/less time to complete than normal. " + "Use this to specify an alternate, device-specific, timeout for " + "'status' actions."), + }, + { + "pcmk_status_retries", NULL, PCMK_VALUE_INTEGER, NULL, + "2", NULL, + pcmk__opt_advanced, + N_("The maximum number of times to try the 'status' command within " + "the timeout period"), + N_("Some devices do not support multiple connections. Operations may " + "\"fail\" if the device is busy with another task. In that case, " + "Pacemaker will automatically retry the operation if there is time " + "remaining. Use this option to alter the number of times Pacemaker " + "tries a 'status' action before giving up."), + }, + + { NULL, }, +}; + +static const pcmk__cluster_option_t primitive_meta[] = { + /* name, old name, type, allowed values, + * default value, validator, + * flags, + * short description, + * long description + */ + { + PCMK_META_PRIORITY, NULL, PCMK_VALUE_SCORE, NULL, + "0", NULL, + pcmk__opt_none, + N_("Resource assignment priority"), + N_("If not all resources can be active, the cluster will stop " + "lower-priority resources in order to keep higher-priority ones " + "active."), + }, + { + PCMK_META_CRITICAL, NULL, PCMK_VALUE_BOOLEAN, NULL, + PCMK_VALUE_TRUE, NULL, + pcmk__opt_none, + N_("Default value for influence in colocation constraints"), + N_("Use this value as the default for influence in all colocation " + "constraints involving this resource, as well as in the implicit " + "colocation constraints created if this resource is in a group."), + }, + { + PCMK_META_TARGET_ROLE, NULL, PCMK_VALUE_SELECT, + PCMK_ROLE_STOPPED ", " PCMK_ROLE_STARTED ", " + PCMK_ROLE_UNPROMOTED ", " PCMK_ROLE_PROMOTED, + PCMK_ROLE_STARTED, NULL, + pcmk__opt_none, + N_("State the cluster should attempt to keep this resource in"), + N_("\"Stopped\" forces the resource to be stopped. " + "\"Started\" allows the resource to be started (and in the case of " + "promotable clone resources, promoted if appropriate). " + "\"Unpromoted\" allows the resource to be started, but only in the " + "unpromoted role if the resource is promotable. " + "\"Promoted\" is equivalent to \"Started\"."), + }, + { + PCMK_META_IS_MANAGED, NULL, PCMK_VALUE_BOOLEAN, NULL, + PCMK_VALUE_TRUE, NULL, + pcmk__opt_none, + N_("Whether the cluster is allowed to actively change the resource's " + "state"), + N_("If false, the cluster will not start, stop, promote, or demote the " + "resource on any node. Recurring actions for the resource are " + "unaffected. If true, a true value for the maintenance-mode " + "cluster option, the maintenance node attribute, or the " + "maintenance resource meta-attribute overrides this."), + }, + { + PCMK_META_MAINTENANCE, NULL, PCMK_VALUE_BOOLEAN, NULL, + PCMK_VALUE_FALSE, NULL, + pcmk__opt_none, + N_("If true, the cluster will not schedule any actions involving the " + "resource"), + N_("If true, the cluster will not start, stop, promote, or demote the " + "resource on any node, and will pause any recurring monitors " + "(except those specifying role as \"Stopped\"). If false, a true " + "value for the maintenance-mode cluster option or maintenance node " + "attribute overrides this."), + }, + { + PCMK_META_RESOURCE_STICKINESS, NULL, PCMK_VALUE_SCORE, NULL, + NULL, NULL, + pcmk__opt_none, + N_("Score to add to the current node when a resource is already " + "active"), + N_("Score to add to the current node when a resource is already " + "active. This allows running resources to stay where they are, " + "even if they would be placed elsewhere if they were being started " + "from a stopped state. " + "The default is 1 for individual clone instances, and 0 for all " + "other resources."), + }, + { + PCMK_META_REQUIRES, NULL, PCMK_VALUE_SELECT, + PCMK_VALUE_NOTHING ", " PCMK_VALUE_QUORUM ", " + PCMK_VALUE_FENCING ", " PCMK_VALUE_UNFENCING, + NULL, NULL, + pcmk__opt_none, + N_("Conditions under which the resource can be started"), + N_("Conditions under which the resource can be started. " + "\"nothing\" means the cluster can always start this resource. " + "\"quorum\" means the cluster can start this resource only if a " + "majority of the configured nodes are active. " + "\"fencing\" means the cluster can start this resource only if a " + "majority of the configured nodes are active and any failed or " + "unknown nodes have been fenced. " + "\"unfencing\" means the cluster can start this resource only if " + "a majority of the configured nodes are active and any failed or " + "unknown nodes have been fenced, and only on nodes that have been " + "unfenced. " + "The default is \"quorum\" for resources with a class of stonith; " + "otherwise, \"unfencing\" if unfencing is active in the cluster; " + "otherwise, \"fencing\" if the stonith-enabled cluster option is " + "true; " + "otherwise, \"quorum\"."), + }, + { + PCMK_META_MIGRATION_THRESHOLD, NULL, PCMK_VALUE_SCORE, NULL, + PCMK_VALUE_INFINITY, NULL, + pcmk__opt_none, + N_("Number of failures on a node before the resource becomes " + "ineligible to run there."), + N_("Number of failures that may occur for this resource on a node, " + "before that node is marked ineligible to host this resource. A " + "value of 0 indicates that this feature is disabled (the node will " + "never be marked ineligible). By contrast, the cluster treats " + "\"INFINITY\" (the default) as a very large but finite number. " + "This option has an effect only if the failed operation specifies " + "its on-fail attribute as \"restart\" (the default), and " + "additionally for failed start operations, if the " + "start-failure-is-fatal cluster property is set to false."), + }, + { + PCMK_META_FAILURE_TIMEOUT, NULL, PCMK_VALUE_DURATION, NULL, + "0", NULL, + pcmk__opt_none, + N_("Number of seconds before acting as if a failure had not occurred"), + N_("Number of seconds after a failed action for this resource before " + "acting as if the failure had not occurred, and potentially " + "allowing the resource back to the node on which it failed. " + "A value of 0 indicates that this feature is disabled."), + }, + { + PCMK_META_MULTIPLE_ACTIVE, NULL, PCMK_VALUE_SELECT, + PCMK_VALUE_BLOCK ", " PCMK_VALUE_STOP_ONLY ", " + PCMK_VALUE_STOP_START ", " PCMK_VALUE_STOP_UNEXPECTED, + PCMK_VALUE_STOP_START, NULL, + pcmk__opt_none, + N_("What to do if the cluster finds the resource active on more than " + "one node"), + N_("What to do if the cluster finds the resource active on more than " + "one node. " + "\"block\" means to mark the resource as unmanaged. " + "\"stop_only\" means to stop all active instances of this resource " + "and leave them stopped. " + "\"stop_start\" means to stop all active instances of this " + "resource and start the resource in one location only. " + "\"stop_unexpected\" means to stop all active instances of this " + "resource except where the resource should be active. (This should " + "be used only when extra instances are not expected to disrupt " + "existing instances, and the resource agent's monitor of an " + "existing instance is capable of detecting any problems that could " + "be caused. Note that any resources ordered after this one will " + "still need to be restarted.)"), + }, + { + PCMK_META_ALLOW_MIGRATE, NULL, PCMK_VALUE_BOOLEAN, NULL, + NULL, NULL, + pcmk__opt_none, + N_("Whether the cluster should try to \"live migrate\" this resource " + "when it needs to be moved"), + N_("Whether the cluster should try to \"live migrate\" this resource " + "when it needs to be moved. " + "The default is true for ocf:pacemaker:remote resources, and false " + "otherwise."), + }, + { + PCMK_META_ALLOW_UNHEALTHY_NODES, NULL, PCMK_VALUE_BOOLEAN, NULL, + PCMK_VALUE_FALSE, NULL, + pcmk__opt_none, + N_("Whether the resource should be allowed to run on a node even if " + "the node's health score would otherwise prevent it"), + NULL, + }, + { + PCMK_META_CONTAINER_ATTRIBUTE_TARGET, NULL, PCMK_VALUE_STRING, NULL, + NULL, NULL, + pcmk__opt_none, + N_("Where to check user-defined node attributes"), + N_("Whether to check user-defined node attributes on the physical host " + "where a container is running or on the local node. This is " + "usually set for a bundle resource and inherited by the bundle's " + "primitive resource. " + "A value of \"host\" means to check user-defined node attributes " + "on the underlying physical host. Any other value means to check " + "user-defined node attributes on the local node (for a bundled " + "primitive resource, this is the bundle node)."), + }, + { + PCMK_META_REMOTE_NODE, NULL, PCMK_VALUE_STRING, NULL, + NULL, NULL, + pcmk__opt_none, + N_("Name of the Pacemaker Remote guest node this resource is " + "associated with, if any"), + N_("Name of the Pacemaker Remote guest node this resource is " + "associated with, if any. If specified, this both enables the " + "resource as a guest node and defines the unique name used to " + "identify the guest node. The guest must be configured to run the " + "Pacemaker Remote daemon when it is started. " + "WARNING: This value cannot overlap with any resource or node " + "IDs."), + }, + { + PCMK_META_REMOTE_ADDR, NULL, PCMK_VALUE_STRING, NULL, + NULL, NULL, + pcmk__opt_none, + N_("If remote-node is specified, the IP address or hostname used to " + "connect to the guest via Pacemaker Remote"), + N_("If remote-node is specified, the IP address or hostname used to " + "connect to the guest via Pacemaker Remote. The Pacemaker Remote " + "daemon on the guest must be configured to accept connections on " + "this address. " + "The default is the value of the remote-node meta-attribute."), + }, + { + PCMK_META_REMOTE_PORT, NULL, PCMK_VALUE_PORT, NULL, + "3121", NULL, + pcmk__opt_none, + N_("If remote-node is specified, port on the guest used for its " + "Pacemaker Remote connection"), + N_("If remote-node is specified, the port on the guest used for its " + "Pacemaker Remote connection. The Pacemaker Remote daemon on the " + "guest must be configured to listen on this port."), + }, + { + PCMK_META_REMOTE_CONNECT_TIMEOUT, NULL, PCMK_VALUE_TIMEOUT, NULL, + "60s", NULL, + pcmk__opt_none, + N_("If remote-node is specified, how long before a pending Pacemaker " + "Remote guest connection times out."), + NULL, + }, + { + PCMK_META_REMOTE_ALLOW_MIGRATE, NULL, PCMK_VALUE_BOOLEAN, NULL, + PCMK_VALUE_TRUE, NULL, + pcmk__opt_none, + N_("If remote-node is specified, this acts as the allow-migrate " + "meta-attribute for the implicit remote connection resource " + "(ocf:pacemaker:remote)."), + NULL, + }, + + { NULL, }, +}; + +/* * Environment variable option handling */ @@ -176,161 +1206,195 @@ pcmk__env_option_enabled(const char *daemon, const char *option) * Cluster option handling */ +/*! + * \internal + * \brief Check whether a string represents a valid interval specification + * + * \param[in] value String to validate + * + * \return \c true if \p value is a valid interval specification, or \c false + * otherwise + */ bool pcmk__valid_interval_spec(const char *value) { - (void) crm_parse_interval_spec(value); - return errno == 0; + return pcmk_parse_interval_spec(value, NULL) == pcmk_rc_ok; } +/*! + * \internal + * \brief Check whether a string represents a valid boolean value + * + * \param[in] value String to validate + * + * \return \c true if \p value is a valid boolean value, or \c false otherwise + */ bool pcmk__valid_boolean(const char *value) { - int tmp; - - return crm_str_to_boolean(value, &tmp) == 1; + return crm_str_to_boolean(value, NULL) == 1; } +/*! + * \internal + * \brief Check whether a string represents a valid integer + * + * Valid values include \c INFINITY, \c -INFINITY, and all 64-bit integers. + * + * \param[in] value String to validate + * + * \return \c true if \p value is a valid integer, or \c false otherwise + */ bool -pcmk__valid_number(const char *value) +pcmk__valid_int(const char *value) { - if (value == NULL) { - return false; - - } else if (pcmk_str_is_minus_infinity(value) || - pcmk_str_is_infinity(value)) { - return true; - } - - return pcmk__scan_ll(value, NULL, 0LL) == pcmk_rc_ok; + return (value != NULL) + && (pcmk_str_is_infinity(value) + || pcmk_str_is_minus_infinity(value) + || (pcmk__scan_ll(value, NULL, 0LL) == pcmk_rc_ok)); } +/*! + * \internal + * \brief Check whether a string represents a valid positive integer + * + * Valid values include \c INFINITY and all 64-bit positive integers. + * + * \param[in] value String to validate + * + * \return \c true if \p value is a valid positive integer, or \c false + * otherwise + */ bool -pcmk__valid_positive_number(const char *value) +pcmk__valid_positive_int(const char *value) { long long num = 0LL; return pcmk_str_is_infinity(value) - || ((pcmk__scan_ll(value, &num, 0LL) == pcmk_rc_ok) && (num > 0)); + || ((pcmk__scan_ll(value, &num, 0LL) == pcmk_rc_ok) + && (num > 0)); } +/*! + * \internal + * \brief Check whether a string represents a valid + * \c PCMK__OPT_NO_QUORUM_POLICY value + * + * \param[in] value String to validate + * + * \return \c true if \p value is a valid \c PCMK__OPT_NO_QUORUM_POLICY value, + * or \c false otherwise + */ bool -pcmk__valid_quorum(const char *value) +pcmk__valid_no_quorum_policy(const char *value) { - return pcmk__strcase_any_of(value, "stop", "freeze", "ignore", "demote", "suicide", NULL); + return pcmk__strcase_any_of(value, + PCMK_VALUE_STOP, PCMK_VALUE_FREEZE, + PCMK_VALUE_IGNORE, PCMK_VALUE_DEMOTE, + PCMK_VALUE_FENCE_LEGACY, NULL); } +/*! + * \internal + * \brief Check whether a string represents a valid percentage + * + * Valid values include long integers, with an optional trailing string + * beginning with '%'. + * + * \param[in] value String to validate + * + * \return \c true if \p value is a valid percentage value, or \c false + * otherwise + */ bool -pcmk__valid_script(const char *value) +pcmk__valid_percentage(const char *value) { - struct stat st; - - if (pcmk__str_eq(value, "/dev/null", pcmk__str_casei)) { - return true; - } - - if (stat(value, &st) != 0) { - crm_err("Script %s does not exist", value); - return false; - } - - if (S_ISREG(st.st_mode) == 0) { - crm_err("Script %s is not a regular file", value); - return false; - } - - if ((st.st_mode & (S_IXUSR | S_IXGRP)) == 0) { - crm_err("Script %s is not executable", value); - return false; - } + char *end = NULL; + float number = strtof(value, &end); - return true; + return ((end == NULL) || (end[0] == '%')) && (number >= 0); } +/*! + * \internal + * \brief Check whether a string represents a valid placement strategy + * + * \param[in] value String to validate + * + * \return \c true if \p value is a valid placement strategy, or \c false + * otherwise + */ bool -pcmk__valid_percentage(const char *value) +pcmk__valid_placement_strategy(const char *value) { - char *end = NULL; - long number = strtol(value, &end, 10); - - if (end && (end[0] != '%')) { - return false; - } - return number >= 0; + return pcmk__strcase_any_of(value, + PCMK_VALUE_DEFAULT, PCMK_VALUE_UTILIZATION, + PCMK_VALUE_MINIMAL, PCMK_VALUE_BALANCED, NULL); } /*! * \internal * \brief Check a table of configured options for a particular option * - * \param[in,out] options Name/value pairs for configured options - * \param[in] validate If not NULL, validator function for option value - * \param[in] name Option name to look for - * \param[in] old_name Alternative option name to look for - * \param[in] def_value Default to use if option not configured + * \param[in,out] table Name/value pairs for configured options + * \param[in] option Option to look up * * \return Option value (from supplied options table or default value) */ static const char * -cluster_option_value(GHashTable *options, bool (*validate)(const char *), - const char *name, const char *old_name, - const char *def_value) +cluster_option_value(GHashTable *table, const pcmk__cluster_option_t *option) { const char *value = NULL; - char *new_value = NULL; - CRM_ASSERT(name != NULL); + CRM_ASSERT((option != NULL) && (option->name != NULL)); - if (options) { - value = g_hash_table_lookup(options, name); + if (table != NULL) { + value = g_hash_table_lookup(table, option->name); - if ((value == NULL) && old_name) { - value = g_hash_table_lookup(options, old_name); + if ((value == NULL) && (option->alt_name != NULL)) { + value = g_hash_table_lookup(table, option->alt_name); if (value != NULL) { pcmk__config_warn("Support for legacy name '%s' for cluster " "option '%s' is deprecated and will be " "removed in a future release", - old_name, name); + option->alt_name, option->name); // Inserting copy with current name ensures we only warn once - new_value = strdup(value); - g_hash_table_insert(options, strdup(name), new_value); - value = new_value; + pcmk__insert_dup(table, option->name, value); } } - if (value && validate && (validate(value) == FALSE)) { + if ((value != NULL) && (option->is_valid != NULL) + && !option->is_valid(value)) { + pcmk__config_err("Using default value for cluster option '%s' " - "because '%s' is invalid", name, value); + "because '%s' is invalid", option->name, value); value = NULL; } - if (value) { + if (value != NULL) { return value; } } // No value found, use default - value = def_value; + value = option->default_value; if (value == NULL) { crm_trace("No value or default provided for cluster option '%s'", - name); + option->name); return NULL; } - if (validate) { - CRM_CHECK(validate(value) != FALSE, - crm_err("Bug: default value for cluster option '%s' is invalid", name); - return NULL); - } + CRM_CHECK((option->is_valid == NULL) || option->is_valid(value), + crm_err("Bug: default value for cluster option '%s' is invalid", + option->name); + return NULL); crm_trace("Using default value '%s' for cluster option '%s'", - value, name); - if (options) { - new_value = strdup(value); - g_hash_table_insert(options, strdup(name), new_value); - value = new_value; + value, option->name); + if (table != NULL) { + pcmk__insert_dup(table, option->name, value); } return value; } @@ -339,27 +1403,19 @@ cluster_option_value(GHashTable *options, bool (*validate)(const char *), * \internal * \brief Get the value of a cluster option * - * \param[in,out] options Name/value pairs for configured options - * \param[in] option_list Possible cluster options - * \param[in] len Length of \p option_list - * \param[in] name (Primary) option name to look for + * \param[in,out] options Name/value pairs for configured options + * \param[in] name (Primary) option name to look for * * \return Option value */ const char * -pcmk__cluster_option(GHashTable *options, - const pcmk__cluster_option_t *option_list, - int len, const char *name) +pcmk__cluster_option(GHashTable *options, const char *name) { - const char *value = NULL; + for (const pcmk__cluster_option_t *option = cluster_options; + option->name != NULL; option++) { - for (int lpc = 0; lpc < len; lpc++) { - if (pcmk__str_eq(name, option_list[lpc].name, pcmk__str_casei)) { - value = cluster_option_value(options, option_list[lpc].is_valid, - option_list[lpc].name, - option_list[lpc].alt_name, - option_list[lpc].default_value); - return value; + if (pcmk__str_eq(name, option->name, pcmk__str_casei)) { + return cluster_option_value(options, option); } } CRM_CHECK(FALSE, crm_err("Bug: looking for unknown option '%s'", name)); @@ -368,143 +1424,142 @@ pcmk__cluster_option(GHashTable *options, /*! * \internal - * \brief Add a description element to a meta-data string - * - * \param[in,out] s Meta-data string to add to - * \param[in] tag Name of element to add ("longdesc" or "shortdesc") - * \param[in] desc Textual description to add - * \param[in] values If not \p NULL, the allowed values for the parameter - * \param[in] spaces If not \p NULL, spaces to insert at the beginning of - * each line + * \brief Output cluster option metadata as OCF-like XML + * + * \param[in,out] out Output object + * \param[in] name Fake resource agent name for the option list + * \param[in] desc_short Short description of the option list + * \param[in] desc_long Long description of the option list + * \param[in] filter Group of <tt>enum pcmk__opt_flags</tt>; output an + * option only if its \c flags member has all these + * flags set + * \param[in] all If \c true, output all options; otherwise, exclude + * advanced and deprecated options unless + * \c pcmk__opt_advanced and \c pcmk__opt_deprecated + * flags (respectively) are set in \p filter. This is + * always treated as true for XML output objects. + * + * \return Standard Pacemaker return code */ -static void -add_desc(GString *s, const char *tag, const char *desc, const char *values, - const char *spaces) +int +pcmk__output_cluster_options(pcmk__output_t *out, const char *name, + const char *desc_short, const char *desc_long, + uint32_t filter, bool all) { - char *escaped_en = crm_xml_escape(desc); - - if (spaces != NULL) { - g_string_append(s, spaces); - } - pcmk__g_strcat(s, "<", tag, " lang=\"en\">", escaped_en, NULL); - - if (values != NULL) { - pcmk__g_strcat(s, " Allowed values: ", values, NULL); - } - pcmk__g_strcat(s, "</", tag, ">\n", NULL); - -#ifdef ENABLE_NLS - { - static const char *locale = NULL; - - char *localized = crm_xml_escape(_(desc)); - - if (strcmp(escaped_en, localized) != 0) { - if (locale == NULL) { - locale = strtok(setlocale(LC_ALL, NULL), "_"); - } - - if (spaces != NULL) { - g_string_append(s, spaces); - } - pcmk__g_strcat(s, "<", tag, " lang=\"", locale, "\">", localized, - NULL); - - if (values != NULL) { - pcmk__g_strcat(s, _(" Allowed values: "), _(values), NULL); - } - pcmk__g_strcat(s, "</", tag, ">\n", NULL); - } - free(localized); - } -#endif - - free(escaped_en); + return out->message(out, "option-list", name, desc_short, desc_long, filter, + cluster_options, all); } -gchar * -pcmk__format_option_metadata(const char *name, const char *desc_short, - const char *desc_long, - pcmk__cluster_option_t *option_list, int len) +/*! + * \internal + * \brief Output primitive resource meta-attributes as OCF-like XML + * + * \param[in,out] out Output object + * \param[in] name Fake resource agent name for the option list + * \param[in] desc_short Short description of the option list + * \param[in] desc_long Long description of the option list + * \param[in] all If \c true, output all options; otherwise, exclude + * advanced and deprecated options. This is always + * treated as true for XML output objects. + * + * \return Standard Pacemaker return code + */ +int +pcmk__output_primitive_meta(pcmk__output_t *out, const char *name, + const char *desc_short, const char *desc_long, + bool all) { - /* big enough to hold "pacemaker-schedulerd metadata" output */ - GString *s = g_string_sized_new(13000); - - pcmk__g_strcat(s, - "<?xml version=\"1.0\"?>\n" - "<resource-agent name=\"", name, "\" " - "version=\"" PACEMAKER_VERSION "\">\n" - " <version>" PCMK_OCF_VERSION "</version>\n", NULL); - - add_desc(s, "longdesc", desc_long, NULL, " "); - add_desc(s, "shortdesc", desc_short, NULL, " "); - - g_string_append(s, " <parameters>\n"); - - for (int lpc = 0; lpc < len; lpc++) { - const char *opt_name = option_list[lpc].name; - const char *opt_type = option_list[lpc].type; - const char *opt_values = option_list[lpc].values; - const char *opt_default = option_list[lpc].default_value; - const char *opt_desc_short = option_list[lpc].description_short; - const char *opt_desc_long = option_list[lpc].description_long; - - // The standard requires long and short parameter descriptions - CRM_ASSERT((opt_desc_short != NULL) || (opt_desc_long != NULL)); - - if (opt_desc_short == NULL) { - opt_desc_short = opt_desc_long; - } else if (opt_desc_long == NULL) { - opt_desc_long = opt_desc_short; - } + return out->message(out, "option-list", name, desc_short, desc_long, + pcmk__opt_none, primitive_meta, all); +} - // The standard requires a parameter type - CRM_ASSERT(opt_type != NULL); +/*! + * \internal + * \brief Output fence device common parameter metadata as OCF-like XML + * + * These are parameters that are available for all fencing resources, regardless + * of type. They are processed by Pacemaker, rather than by the fence agent or + * the fencing library. + * + * \param[in,out] out Output object + * \param[in] name Fake resource agent name for the option list + * \param[in] desc_short Short description of the option list + * \param[in] desc_long Long description of the option list + * \param[in] all If \c true, output all options; otherwise, exclude + * advanced and deprecated options. This is always + * treated as true for XML output objects. + * + * \return Standard Pacemaker return code + */ +int +pcmk__output_fencing_params(pcmk__output_t *out, const char *name, + const char *desc_short, const char *desc_long, + bool all) +{ + return out->message(out, "option-list", name, desc_short, desc_long, + pcmk__opt_none, fencing_params, all); +} - pcmk__g_strcat(s, " <parameter name=\"", opt_name, "\">\n", NULL); +/*! + * \internal + * \brief Output a list of cluster options for a daemon + * + * \brief[in,out] out Output object + * \brief[in] name Daemon name + * \brief[in] desc_short Short description of the option list + * \brief[in] desc_long Long description of the option list + * \brief[in] filter <tt>enum pcmk__opt_flags</tt> flag corresponding + * to daemon + * + * \return Standard Pacemaker return code + */ +int +pcmk__daemon_metadata(pcmk__output_t *out, const char *name, + const char *desc_short, const char *desc_long, + enum pcmk__opt_flags filter) +{ + // @COMPAT Drop this function when we drop daemon metadata + pcmk__output_t *tmp_out = NULL; + xmlNode *top = NULL; + const xmlNode *metadata = NULL; + GString *metadata_s = NULL; - add_desc(s, "longdesc", opt_desc_long, opt_values, " "); - add_desc(s, "shortdesc", opt_desc_short, NULL, " "); + int rc = pcmk__output_new(&tmp_out, "xml", "/dev/null", NULL); - pcmk__g_strcat(s, " <content type=\"", opt_type, "\"", NULL); - if (opt_default != NULL) { - pcmk__g_strcat(s, " default=\"", opt_default, "\"", NULL); - } + if (rc != pcmk_rc_ok) { + return rc; + } - if ((opt_values != NULL) && (strcmp(opt_type, "select") == 0)) { - char *str = strdup(opt_values); - const char *delim = ", "; - char *ptr = strtok(str, delim); + pcmk__output_set_legacy_xml(tmp_out); - g_string_append(s, ">\n"); + if (filter == pcmk__opt_fencing) { + pcmk__output_fencing_params(tmp_out, name, desc_short, desc_long, true); + } else { + pcmk__output_cluster_options(tmp_out, name, desc_short, desc_long, + (uint32_t) filter, true); + } - while (ptr != NULL) { - pcmk__g_strcat(s, " <option value=\"", ptr, "\" />\n", - NULL); - ptr = strtok(NULL, delim); - } - g_string_append_printf(s, " </content>\n"); - free(str); + tmp_out->finish(tmp_out, CRM_EX_OK, false, (void **) &top); + metadata = pcmk__xe_first_child(top, PCMK_XE_RESOURCE_AGENT, NULL, NULL); - } else { - g_string_append(s, "/>\n"); - } + metadata_s = g_string_sized_new(16384); + pcmk__xml_string(metadata, pcmk__xml_fmt_pretty|pcmk__xml_fmt_text, + metadata_s, 0); - g_string_append(s, " </parameter>\n"); - } - g_string_append(s, " </parameters>\n</resource-agent>\n"); + out->output_xml(out, PCMK_XE_METADATA, metadata_s->str); - return g_string_free(s, FALSE); + pcmk__output_free(tmp_out); + free_xml(top); + g_string_free(metadata_s, TRUE); + return pcmk_rc_ok; } void -pcmk__validate_cluster_options(GHashTable *options, - pcmk__cluster_option_t *option_list, int len) +pcmk__validate_cluster_options(GHashTable *options) { - for (int lpc = 0; lpc < len; lpc++) { - cluster_option_value(options, option_list[lpc].is_valid, - option_list[lpc].name, - option_list[lpc].alt_name, - option_list[lpc].default_value); + for (const pcmk__cluster_option_t *option = cluster_options; + option->name != NULL; option++) { + + cluster_option_value(options, option); } } |