Page MenuHomeClusterLabs Projects

No OneTemporary

diff --git a/lib/common/options.c b/lib/common/options.c
index 69f3f6b690..45277a9873 100644
--- a/lib/common/options.c
+++ b/lib/common/options.c
@@ -1,612 +1,1110 @@
/*
* 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 Lesser General Public License
* version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
*/
#ifndef _GNU_SOURCE
# define _GNU_SOURCE
#endif
#include <crm_internal.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <crm/crm.h>
#include <crm/msg_xml.h>
void
pcmk__cli_help(char cmd)
{
if (cmd == 'v' || cmd == '$') {
printf("Pacemaker %s\n", PACEMAKER_VERSION);
printf("Written by Andrew Beekhof and "
"the Pacemaker project contributors\n");
} else if (cmd == '!') {
printf("Pacemaker %s (Build: %s): %s\n", PACEMAKER_VERSION, BUILD_VERSION, CRM_FEATURES);
}
crm_exit(CRM_EX_OK);
while(1); // above does not return
}
+/*
+ * Option metadata
+ */
+
+#if 0
+static pcmk__cluster_option_t cluster_options[] = {
+ /* name, old name, type, allowed values,
+ * default value, validator,
+ * context,
+ * short description,
+ * long description
+ */
+ {
+ PCMK_OPT_DC_VERSION, NULL, "string", NULL,
+ PCMK__VALUE_NONE, NULL,
+ pcmk__opt_context_controld,
+ N_("Pacemaker version on cluster node elected Designated Controller "
+ "(DC)"),
+ N_("Includes a hash which identifies the exact changeset the code was "
+ "built from. Used for diagnostic purposes."),
+ },
+ {
+ PCMK_OPT_CLUSTER_INFRASTRUCTURE, NULL, "string", NULL,
+ "corosync", NULL,
+ pcmk__opt_context_controld,
+ N_("The messaging stack on which Pacemaker is currently running"),
+ N_("Used for informational and diagnostic purposes."),
+ },
+ {
+ PCMK_OPT_CLUSTER_NAME, NULL, "string", NULL,
+ NULL, NULL,
+ pcmk__opt_context_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, "time", NULL,
+ "20s", pcmk__valid_interval_spec,
+ pcmk__opt_context_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, "time",
+ N_("Zero disables polling, while positive values are an interval in "
+ "seconds (unless other units are specified, for example \"5min\")"),
+ "15min", pcmk__valid_interval_spec,
+ pcmk__opt_context_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 timeouts 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."),
+ },
+ {
+ PCMK_OPT_FENCE_REACTION, NULL, "select", "stop, panic",
+ "stop", NULL,
+ pcmk__opt_context_controld,
+ N_("How a cluster node should react if notified of its own fencing"),
+ N_("A cluster node may receive notification of its own fencing 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, "time", NULL,
+ "2min", pcmk__valid_interval_spec,
+ pcmk__opt_context_controld,
+ N_("*** Advanced Use Only ***"),
+ 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."),
+ },
+ {
+ PCMK_OPT_SHUTDOWN_ESCALATION, NULL, "time", NULL,
+ "20min", pcmk__valid_interval_spec,
+ pcmk__opt_context_controld,
+ N_("*** Advanced Use Only ***"),
+ 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."),
+ },
+ {
+ PCMK_OPT_JOIN_INTEGRATION_TIMEOUT, "crmd-integration-timeout", "time",
+ NULL,
+ "3min", pcmk__valid_interval_spec,
+ pcmk__opt_context_controld,
+ N_("*** Advanced Use Only ***"),
+ N_("If you need to adjust this value, it probably indicates "
+ "the presence of a bug."),
+ },
+ {
+ PCMK_OPT_JOIN_FINALIZATION_TIMEOUT, "crmd-finalization-timeout",
+ "time", NULL,
+ "30min", pcmk__valid_interval_spec,
+ pcmk__opt_context_controld,
+ N_("*** Advanced Use Only ***"),
+ N_("If you need to adjust this value, it probably indicates "
+ "the presence of a bug."),
+ },
+ {
+ PCMK_OPT_TRANSITION_DELAY, "crmd-transition-delay", "time", NULL,
+ "0s", pcmk__valid_interval_spec,
+ pcmk__opt_context_controld,
+ N_("*** Advanced Use Only *** "
+ "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, "select",
+ "stop, freeze, ignore, demote, suicide",
+ "stop", pcmk__valid_no_quorum_policy,
+ pcmk__opt_context_schedulerd,
+ N_("What to do when the cluster does not have quorum"),
+ NULL,
+ },
+ {
+ PCMK_OPT_SHUTDOWN_LOCK, NULL, "boolean", NULL,
+ PCMK_VALUE_FALSE, pcmk__valid_boolean,
+ pcmk__opt_context_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, "time", NULL,
+ "0", pcmk__valid_interval_spec,
+ pcmk__opt_context_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, "boolean", NULL,
+ PCMK_VALUE_FALSE, pcmk__valid_boolean,
+ pcmk__opt_context_based,
+ N_("Enable Access Control Lists (ACLs) for the CIB"),
+ NULL,
+ },
+ {
+ PCMK_OPT_SYMMETRIC_CLUSTER, NULL, "boolean", NULL,
+ PCMK_VALUE_TRUE, pcmk__valid_boolean,
+ pcmk__opt_context_schedulerd,
+ N_("Whether resources can run on any node by default"),
+ NULL,
+ },
+ {
+ PCMK_OPT_MAINTENANCE_MODE, NULL, "boolean", NULL,
+ PCMK_VALUE_FALSE, pcmk__valid_boolean,
+ pcmk__opt_context_schedulerd,
+ N_("Whether the cluster should refrain from monitoring, starting, and "
+ "stopping resources"),
+ NULL,
+ },
+ {
+ PCMK_OPT_START_FAILURE_IS_FATAL, NULL, "boolean", NULL,
+ PCMK_VALUE_TRUE, pcmk__valid_boolean,
+ pcmk__opt_context_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, "boolean", NULL,
+ PCMK_VALUE_TRUE, pcmk__valid_boolean,
+ pcmk__opt_context_schedulerd,
+ N_("Whether the cluster should check for active resources during "
+ "start-up"),
+ NULL,
+ },
+
+ // Fencing-related options
+ {
+ PCMK_OPT_STONITH_ENABLED, NULL, "boolean", NULL,
+ PCMK_VALUE_TRUE, pcmk__valid_boolean,
+ pcmk__opt_context_schedulerd,
+ N_("*** Advanced Use Only *** "
+ "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, "select", "reboot, off, poweroff",
+ PCMK_ACTION_REBOOT, pcmk__is_fencing_action,
+ pcmk__opt_context_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, "time", NULL,
+ "60s", pcmk__valid_interval_spec,
+ pcmk__opt_context_schedulerd,
+ N_("How long to wait for on, off, and reboot fence actions to complete "
+ "by default"),
+ NULL,
+ },
+ {
+ PCMK_OPT_HAVE_WATCHDOG, NULL, "boolean", NULL,
+ PCMK_VALUE_FALSE, pcmk__valid_boolean,
+ pcmk__opt_context_schedulerd,
+ 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, "time", NULL,
+ "0", NULL,
+ pcmk__opt_context_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 "
+ "self-fence 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, "integer", NULL,
+ "10", pcmk__valid_positive_int,
+ pcmk__opt_context_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, "boolean", NULL,
+ PCMK__CONCURRENT_FENCING_DEFAULT, pcmk__valid_boolean,
+ pcmk__opt_context_schedulerd,
+ N_("Allow performing fencing operations in parallel"),
+ NULL,
+ },
+ {
+ PCMK_OPT_STARTUP_FENCING, NULL, "boolean", NULL,
+ PCMK_VALUE_TRUE, pcmk__valid_boolean,
+ pcmk__opt_context_schedulerd,
+ N_("*** Advanced Use Only *** "
+ "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, "time", NULL,
+ "0", pcmk__valid_interval_spec,
+ pcmk__opt_context_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, "time", NULL,
+ "0", pcmk__valid_interval_spec,
+ pcmk__opt_context_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, "time", NULL,
+ "60s", pcmk__valid_interval_spec,
+ pcmk__opt_context_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, "percentage", NULL,
+ "80%", pcmk__valid_percentage,
+ pcmk__opt_context_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, "integer", NULL,
+ "0", pcmk__valid_int,
+ pcmk__opt_context_controld,
+ N_("Maximum number of jobs that can be scheduled per node (defaults to "
+ "2x cores)"),
+ NULL,
+ },
+ {
+ PCMK_OPT_BATCH_LIMIT, NULL, "integer", NULL,
+ "0", pcmk__valid_int,
+ pcmk__opt_context_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, "integer", NULL,
+ "-1", pcmk__valid_int,
+ pcmk__opt_context_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,
+ },
+ {
+ PCMK_OPT_CLUSTER_IPC_LIMIT, NULL, "integer", NULL,
+ "500", pcmk__valid_positive_int,
+ pcmk__opt_context_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, "boolean", NULL,
+ PCMK_VALUE_FALSE, pcmk__valid_boolean,
+ pcmk__opt_context_schedulerd,
+ N_("Whether the cluster should stop all active resources"),
+ NULL,
+ },
+ {
+ PCMK_OPT_STOP_ORPHAN_RESOURCES, NULL, "boolean", NULL,
+ PCMK_VALUE_TRUE, pcmk__valid_boolean,
+ pcmk__opt_context_schedulerd,
+ N_("Whether to stop resources that were removed from the "
+ "configuration"),
+ NULL,
+ },
+ {
+ PCMK_OPT_STOP_ORPHAN_ACTIONS, NULL, "boolean", NULL,
+ PCMK_VALUE_TRUE, pcmk__valid_boolean,
+ pcmk__opt_context_schedulerd,
+ N_("Whether to cancel recurring actions removed from the "
+ "configuration"),
+ NULL,
+ },
+ {
+ PCMK__OPT_REMOVE_AFTER_STOP, NULL, "boolean", NULL,
+ PCMK_VALUE_FALSE, pcmk__valid_boolean,
+ pcmk__opt_context_schedulerd,
+ N_("*** Deprecated *** "
+ "Whether to remove stopped resources from the executor"),
+ N_("Values other than default are poorly tested and potentially "
+ "dangerous. This option will be removed in a future release."),
+ },
+
+ // Storing inputs
+ {
+ PCMK_OPT_PE_ERROR_SERIES_MAX, NULL, "integer", NULL,
+ "-1", pcmk__valid_int,
+ pcmk__opt_context_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, "integer", NULL,
+ "5000", pcmk__valid_int,
+ pcmk__opt_context_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, "integer", NULL,
+ "4000", pcmk__valid_int,
+ pcmk__opt_context_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, "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_context_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, "integer", NULL,
+ "0", pcmk__valid_int,
+ pcmk__opt_context_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, "integer", NULL,
+ "0", pcmk__valid_int,
+ pcmk__opt_context_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, "integer", NULL,
+ "0", pcmk__valid_int,
+ pcmk__opt_context_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, "integer", NULL,
+ "-INFINITY", pcmk__valid_int,
+ pcmk__opt_context_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, "select",
+ "default, utilization, minimal, balanced",
+ "default", pcmk__valid_placement_strategy,
+ pcmk__opt_context_schedulerd,
+ N_("How the cluster should allocate resources to nodes"),
+ NULL,
+ },
+};
+#endif // 0
+
+
/*
* Environment variable option handling
*/
/*!
* \internal
* \brief Get the value of a Pacemaker environment variable option
*
* If an environment variable option is set, with either a PCMK_ or (for
* backward compatibility) HA_ prefix, log and return the value.
*
* \param[in] option Environment variable name (without prefix)
*
* \return Value of environment variable option, or NULL in case of
* option name too long or value not found
*/
const char *
pcmk__env_option(const char *option)
{
const char *const prefixes[] = {"PCMK_", "HA_"};
char env_name[NAME_MAX];
const char *value = NULL;
CRM_CHECK(!pcmk__str_empty(option), return NULL);
for (int i = 0; i < PCMK__NELEM(prefixes); i++) {
int rv = snprintf(env_name, NAME_MAX, "%s%s", prefixes[i], option);
if (rv < 0) {
crm_err("Failed to write %s%s to buffer: %s", prefixes[i], option,
strerror(errno));
return NULL;
}
if (rv >= sizeof(env_name)) {
crm_trace("\"%s%s\" is too long", prefixes[i], option);
continue;
}
value = getenv(env_name);
if (value != NULL) {
crm_trace("Found %s = %s", env_name, value);
return value;
}
}
crm_trace("Nothing found for %s", option);
return NULL;
}
/*!
* \brief Set or unset a Pacemaker environment variable option
*
* Set an environment variable option with a \c "PCMK_" prefix and optionally
* an \c "HA_" prefix for backward compatibility.
*
* \param[in] option Environment variable name (without prefix)
* \param[in] value New value (or NULL to unset)
* \param[in] compat If false and \p value is not \c NULL, set only
* \c "PCMK_<option>"; otherwise, set (or unset) both
* \c "PCMK_<option>" and \c "HA_<option>"
*
* \note \p compat is ignored when \p value is \c NULL. A \c NULL \p value
* means we're unsetting \p option. \c pcmk__get_env_option() checks for
* both prefixes, so we want to clear them both.
*/
void
pcmk__set_env_option(const char *option, const char *value, bool compat)
{
// @COMPAT Drop support for "HA_" options eventually
const char *const prefixes[] = {"PCMK_", "HA_"};
char env_name[NAME_MAX];
CRM_CHECK(!pcmk__str_empty(option) && (strchr(option, '=') == NULL),
return);
for (int i = 0; i < PCMK__NELEM(prefixes); i++) {
int rv = snprintf(env_name, NAME_MAX, "%s%s", prefixes[i], option);
if (rv < 0) {
crm_err("Failed to write %s%s to buffer: %s", prefixes[i], option,
strerror(errno));
return;
}
if (rv >= sizeof(env_name)) {
crm_trace("\"%s%s\" is too long", prefixes[i], option);
continue;
}
if (value != NULL) {
crm_trace("Setting %s to %s", env_name, value);
rv = setenv(env_name, value, 1);
} else {
crm_trace("Unsetting %s", env_name);
rv = unsetenv(env_name);
}
if (rv < 0) {
crm_err("Failed to %sset %s: %s", (value != NULL)? "" : "un",
env_name, strerror(errno));
}
if (!compat && (value != NULL)) {
// For set, don't proceed to HA_<option> unless compat is enabled
break;
}
}
}
/*!
* \internal
* \brief Check whether Pacemaker environment variable option is enabled
*
* Given a Pacemaker environment variable option that can either be boolean
* or a list of daemon names, return true if the option is enabled for a given
* daemon.
*
* \param[in] daemon Daemon name (can be NULL)
* \param[in] option Pacemaker environment variable name
*
* \return true if variable is enabled for daemon, otherwise false
*/
bool
pcmk__env_option_enabled(const char *daemon, const char *option)
{
const char *value = pcmk__env_option(option);
return (value != NULL)
&& (crm_is_true(value)
|| ((daemon != NULL) && (strstr(value, daemon) != NULL)));
}
/*
* 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)
{
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)
{
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_int(const char *value)
{
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_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));
}
/*!
* \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_no_quorum_policy(const char *value)
{
return pcmk__strcase_any_of(value,
"stop", "freeze", "ignore", "demote", "suicide",
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_percentage(const char *value)
{
char *end = NULL;
float number = strtof(value, &end);
return ((end == NULL) || (end[0] == '%')) && (number >= 0);
}
/*!
* \internal
* \brief Check whether a string represents a valid script
*
* Valid values include \c /dev/null and paths of executable regular files
*
* \param[in] value String to validate
*
* \return \c true if \p value is a valid script, or \c false otherwise
*/
bool
pcmk__valid_script(const char *value)
{
struct stat st;
if (pcmk__str_eq(value, "/dev/null", pcmk__str_none)) {
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;
}
return true;
}
/*!
* \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_placement_strategy(const char *value)
{
return pcmk__strcase_any_of(value, "default", "utilization", "minimal",
"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
*
* \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)
{
const char *value = NULL;
char *new_value = NULL;
CRM_ASSERT(name != NULL);
if (options) {
value = g_hash_table_lookup(options, name);
if ((value == NULL) && old_name) {
value = g_hash_table_lookup(options, old_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);
// 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;
}
}
if (value && validate && (validate(value) == FALSE)) {
pcmk__config_err("Using default value for cluster option '%s' "
"because '%s' is invalid", name, value);
value = NULL;
}
if (value) {
return value;
}
}
// No value found, use default
value = def_value;
if (value == NULL) {
crm_trace("No value or default provided for cluster option '%s'",
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_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;
}
return value;
}
/*!
* \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
*
* \return Option value
*/
const char *
pcmk__cluster_option(GHashTable *options,
const pcmk__cluster_option_t *option_list,
int len, const char *name)
{
const char *value = NULL;
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;
}
}
CRM_CHECK(FALSE, crm_err("Bug: looking for unknown option '%s'", name));
return NULL;
}
/*!
* \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 (\c PCMK_XE_LONGDESC or
* \c PCMK_XE_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
*/
static void
add_desc(GString *s, const char *tag, const char *desc, const char *values,
const char *spaces)
{
char *escaped_en = crm_xml_escape(desc);
if (spaces != NULL) {
g_string_append(s, spaces);
}
pcmk__g_strcat(s,
"<", tag, " " PCMK_XA_LANG "=\"" PCMK__VALUE_EN "\">",
escaped_en, NULL);
if (values != NULL) {
// Append a period if desc doesn't end in "." or ".)"
if (!pcmk__str_empty(escaped_en)
&& (s->str[s->len - 1] != '.')
&& ((s->str[s->len - 2] != '.') || (s->str[s->len - 1] != ')'))) {
g_string_append_c(s, '.');
}
pcmk__g_strcat(s, " Allowed values: ", values, NULL);
g_string_append_c(s, '.');
}
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, " " PCMK_XA_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);
}
gchar *
pcmk__format_option_metadata(const char *name, const char *desc_short,
const char *desc_long,
pcmk__cluster_option_t *option_list, int len)
{
/* big enough to hold "pacemaker-schedulerd metadata" output */
GString *s = g_string_sized_new(13000);
pcmk__g_strcat(s,
"<?xml " PCMK_XA_VERSION "=\"1.0\"?>\n"
"<" PCMK_XE_RESOURCE_AGENT " "
PCMK_XA_NAME "=\"", name, "\" "
PCMK_XA_VERSION "=\"" PACEMAKER_VERSION "\">\n"
" <" PCMK_XE_VERSION ">" PCMK_OCF_VERSION
"</" PCMK_XE_VERSION ">\n", NULL);
add_desc(s, PCMK_XE_LONGDESC, desc_long, NULL, " ");
add_desc(s, PCMK_XE_SHORTDESC, desc_short, NULL, " ");
g_string_append(s, " <" PCMK_XE_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;
}
// The standard requires a parameter type
CRM_ASSERT(opt_type != NULL);
pcmk__g_strcat(s,
" <" PCMK_XE_PARAMETER " "
PCMK_XA_NAME "=\"", opt_name, "\">\n", NULL);
add_desc(s, PCMK_XE_LONGDESC, opt_desc_long, opt_values, " ");
add_desc(s, PCMK_XE_SHORTDESC, opt_desc_short, NULL, " ");
pcmk__g_strcat(s, " <" PCMK_XE_CONTENT " "
PCMK_XA_TYPE "=\"", opt_type, "\"", NULL);
if (opt_default != NULL) {
pcmk__g_strcat(s,
" " PCMK_XA_DEFAULT "=\"", opt_default, "\"", NULL);
}
if ((opt_values != NULL) && (strcmp(opt_type, "select") == 0)) {
char *str = strdup(opt_values);
const char *delim = ", ";
char *ptr = strtok(str, delim);
g_string_append(s, ">\n");
while (ptr != NULL) {
pcmk__g_strcat(s,
" <" PCMK_XE_OPTION " "
PCMK_XA_VALUE "=\"", ptr, "\" />\n",
NULL);
ptr = strtok(NULL, delim);
}
g_string_append(s, " </" PCMK_XE_CONTENT ">\n");
free(str);
} else {
g_string_append(s, "/>\n");
}
g_string_append(s, " </" PCMK_XE_PARAMETER ">\n");
}
g_string_append(s,
" </" PCMK_XE_PARAMETERS ">\n"
"</" PCMK_XE_RESOURCE_AGENT ">\n");
return g_string_free(s, FALSE);
}
void
pcmk__validate_cluster_options(GHashTable *options,
pcmk__cluster_option_t *option_list, int len)
{
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);
}
}

File Metadata

Mime Type
text/x-diff
Expires
Sat, Nov 23, 4:24 PM (13 h, 47 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1012690
Default Alt Text
(40 KB)

Event Timeline