Page MenuHomeClusterLabs Projects

No OneTemporary

diff --git a/daemons/controld/controld_te_events.c b/daemons/controld/controld_te_events.c
index 70c3442498..6f71b80441 100644
--- a/daemons/controld/controld_te_events.c
+++ b/daemons/controld/controld_te_events.c
@@ -1,611 +1,611 @@
/*
* Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
* This source code is licensed under the GNU General Public License version 2
* or later (GPLv2+) WITHOUT ANY WARRANTY.
*/
#include <crm_internal.h>
#include <sys/param.h>
#include <crm/crm.h>
#include <crm/cib.h>
#include <crm/common/xml.h>
#include <pacemaker-controld.h>
#include <crm/common/attrs_internal.h>
#include <crm/common/ipc_attrd_internal.h>
/*!
* \internal
* \brief Action numbers of outside events processed in current update diff
*
* This table is to be used as a set. It should be empty when the transitioner
* begins processing a CIB update diff. It ensures that if there are multiple
* events (for example, "_last_0" and "_last_failure_0") for the same action,
* only one of them updates the failcount. Events that originate outside the
* cluster can't be confirmed, since they're not in the transition graph.
*/
static GHashTable *outside_events = NULL;
/*!
* \internal
* \brief Empty the hash table containing action numbers of outside events
*/
void
controld_remove_all_outside_events(void)
{
if (outside_events != NULL) {
g_hash_table_remove_all(outside_events);
}
}
/*!
* \internal
* \brief Destroy the hash table containing action numbers of outside events
*/
void
controld_destroy_outside_events_table(void)
{
if (outside_events != NULL) {
g_hash_table_destroy(outside_events);
outside_events = NULL;
}
}
/*!
* \internal
* \brief Add an outside event's action number to a set
*
* \return Standard Pacemaker return code. Specifically, \p pcmk_rc_ok if the
* event was not already in the set, or \p pcmk_rc_already otherwise.
*/
static int
record_outside_event(gint action_num)
{
if (outside_events == NULL) {
outside_events = g_hash_table_new(NULL, NULL);
}
if (g_hash_table_add(outside_events, GINT_TO_POINTER(action_num))) {
return pcmk_rc_ok;
}
return pcmk_rc_already;
}
gboolean
fail_incompletable_actions(pcmk__graph_t *graph, const char *down_node)
{
const char *target_uuid = NULL;
const char *router = NULL;
const char *router_uuid = NULL;
xmlNode *last_action = NULL;
GList *gIter = NULL;
GList *gIter2 = NULL;
if (graph == NULL || graph->complete) {
return FALSE;
}
gIter = graph->synapses;
for (; gIter != NULL; gIter = gIter->next) {
pcmk__graph_synapse_t *synapse = (pcmk__graph_synapse_t *) gIter->data;
if (pcmk_any_flags_set(synapse->flags, pcmk__synapse_confirmed|pcmk__synapse_failed)) {
/* We've already been here */
continue;
}
gIter2 = synapse->actions;
for (; gIter2 != NULL; gIter2 = gIter2->next) {
pcmk__graph_action_t *action = (pcmk__graph_action_t *) gIter2->data;
if ((action->type == pcmk__pseudo_graph_action)
|| pcmk_is_set(action->flags, pcmk__graph_action_confirmed)) {
continue;
} else if (action->type == pcmk__cluster_graph_action) {
const char *task = crm_element_value(action->xml,
PCMK_XA_OPERATION);
if (pcmk__str_eq(task, PCMK_ACTION_STONITH, pcmk__str_casei)) {
continue;
}
}
target_uuid = crm_element_value(action->xml,
PCMK__META_ON_NODE_UUID);
router = crm_element_value(action->xml, PCMK__XA_ROUTER_NODE);
if (router) {
crm_node_t *node = pcmk__get_node(0, router, NULL,
pcmk__node_search_cluster);
if (node) {
router_uuid = node->uuid;
}
}
if (pcmk__str_eq(target_uuid, down_node, pcmk__str_casei) || pcmk__str_eq(router_uuid, down_node, pcmk__str_casei)) {
pcmk__set_graph_action_flags(action, pcmk__graph_action_failed);
pcmk__set_synapse_flags(synapse, pcmk__synapse_failed);
last_action = action->xml;
stop_te_timer(action);
pcmk__update_graph(graph, action);
if (pcmk_is_set(synapse->flags, pcmk__synapse_executed)) {
crm_notice("Action %d (%s) was pending on %s (offline)",
action->id,
crm_element_value(action->xml,
PCMK__XA_OPERATION_KEY),
down_node);
} else {
crm_info("Action %d (%s) is scheduled for %s (offline)",
action->id,
crm_element_value(action->xml, PCMK__XA_OPERATION_KEY),
down_node);
}
}
}
}
if (last_action != NULL) {
crm_info("Node %s shutdown resulted in un-runnable actions", down_node);
abort_transition(INFINITY, pcmk__graph_restart, "Node failure",
last_action);
return TRUE;
}
return FALSE;
}
/*!
* \internal
* \brief Update failure-related node attributes if warranted
*
* \param[in] event XML describing operation that (maybe) failed
* \param[in] event_node_uuid Node that event occurred on
* \param[in] rc Actual operation return code
* \param[in] target_rc Expected operation return code
* \param[in] do_update If TRUE, do update regardless of operation type
* \param[in] ignore_failures If TRUE, update last failure but not fail count
*
* \return TRUE if this was not a direct nack, success or lrm status refresh
*/
static gboolean
update_failcount(const xmlNode *event, const char *event_node_uuid, int rc,
int target_rc, gboolean do_update, gboolean ignore_failures)
{
guint interval_ms = 0;
char *task = NULL;
char *rsc_id = NULL;
const char *value = NULL;
const char *id = crm_element_value(event, PCMK__XA_OPERATION_KEY);
const char *on_uname = crm_peer_uname(event_node_uuid);
const char *origin = crm_element_value(event, PCMK_XA_CRM_DEBUG_ORIGIN);
// Nothing needs to be done for success or status refresh
if (rc == target_rc) {
return FALSE;
} else if (pcmk__str_eq(origin, "build_active_RAs", pcmk__str_casei)) {
crm_debug("No update for %s (rc=%d) on %s: Old failure from lrm status refresh",
id, rc, on_uname);
return FALSE;
}
/* Sanity check */
CRM_CHECK(on_uname != NULL, return TRUE);
CRM_CHECK(parse_op_key(id, &rsc_id, &task, &interval_ms),
crm_err("Couldn't parse: %s", pcmk__xe_id(event)); goto bail);
/* Decide whether update is necessary and what value to use */
if ((interval_ms > 0)
|| pcmk__str_eq(task, PCMK_ACTION_PROMOTE, pcmk__str_none)
|| pcmk__str_eq(task, PCMK_ACTION_DEMOTE, pcmk__str_none)) {
do_update = TRUE;
} else if (pcmk__str_eq(task, PCMK_ACTION_START, pcmk__str_none)) {
do_update = TRUE;
value = pcmk__s(controld_globals.transition_graph->failed_start_offset,
- CRM_INFINITY_S);
+ PCMK_VALUE_INFINITY);
} else if (pcmk__str_eq(task, PCMK_ACTION_STOP, pcmk__str_none)) {
do_update = TRUE;
value = pcmk__s(controld_globals.transition_graph->failed_stop_offset,
- CRM_INFINITY_S);
+ PCMK_VALUE_INFINITY);
}
if (do_update) {
pcmk__attrd_query_pair_t *fail_pair = NULL;
pcmk__attrd_query_pair_t *last_pair = NULL;
char *fail_name = NULL;
char *last_name = NULL;
GList *attrs = NULL;
uint32_t opts = pcmk__node_attr_none;
char *now = pcmk__ttoa(time(NULL));
// Fail count will be either incremented or set to infinity
if (!pcmk_str_is_infinity(value)) {
value = PCMK_XA_VALUE "++";
}
if (g_hash_table_lookup(crm_remote_peer_cache, event_node_uuid)) {
opts |= pcmk__node_attr_remote;
}
crm_info("Updating %s for %s on %s after failed %s: rc=%d (update=%s, time=%s)",
(ignore_failures? "last failure" : "failcount"),
rsc_id, on_uname, task, rc, value, now);
/* Update the fail count, if we're not ignoring failures */
if (!ignore_failures) {
fail_pair = calloc(1, sizeof(pcmk__attrd_query_pair_t));
CRM_ASSERT(fail_pair != NULL);
fail_name = pcmk__failcount_name(rsc_id, task, interval_ms);
fail_pair->name = fail_name;
fail_pair->value = value;
fail_pair->node = on_uname;
attrs = g_list_prepend(attrs, fail_pair);
}
/* Update the last failure time (even if we're ignoring failures,
* so that failure can still be detected and shown, e.g. by crm_mon)
*/
last_pair = calloc(1, sizeof(pcmk__attrd_query_pair_t));
CRM_ASSERT(last_pair != NULL);
last_name = pcmk__lastfailure_name(rsc_id, task, interval_ms);
last_pair->name = last_name;
last_pair->value = now;
last_pair->node = on_uname;
attrs = g_list_prepend(attrs, last_pair);
update_attrd_list(attrs, opts);
free(fail_name);
free(fail_pair);
free(last_name);
free(last_pair);
g_list_free(attrs);
free(now);
}
bail:
free(rsc_id);
free(task);
return TRUE;
}
pcmk__graph_action_t *
controld_get_action(int id)
{
for (GList *item = controld_globals.transition_graph->synapses;
item != NULL; item = item->next) {
pcmk__graph_synapse_t *synapse = (pcmk__graph_synapse_t *) item->data;
for (GList *item2 = synapse->actions; item2; item2 = item2->next) {
pcmk__graph_action_t *action = (pcmk__graph_action_t *) item2->data;
if (action->id == id) {
return action;
}
}
}
return NULL;
}
pcmk__graph_action_t *
get_cancel_action(const char *id, const char *node)
{
GList *gIter = NULL;
GList *gIter2 = NULL;
gIter = controld_globals.transition_graph->synapses;
for (; gIter != NULL; gIter = gIter->next) {
pcmk__graph_synapse_t *synapse = (pcmk__graph_synapse_t *) gIter->data;
gIter2 = synapse->actions;
for (; gIter2 != NULL; gIter2 = gIter2->next) {
const char *task = NULL;
const char *target = NULL;
pcmk__graph_action_t *action = (pcmk__graph_action_t *) gIter2->data;
task = crm_element_value(action->xml, PCMK_XA_OPERATION);
if (!pcmk__str_eq(PCMK_ACTION_CANCEL, task, pcmk__str_casei)) {
continue;
}
task = crm_element_value(action->xml, PCMK__XA_OPERATION_KEY);
if (!pcmk__str_eq(task, id, pcmk__str_casei)) {
crm_trace("Wrong key %s for %s on %s", task, id, node);
continue;
}
target = crm_element_value(action->xml, PCMK__META_ON_NODE_UUID);
if (node && !pcmk__str_eq(target, node, pcmk__str_casei)) {
crm_trace("Wrong node %s for %s on %s", target, id, node);
continue;
}
crm_trace("Found %s on %s", id, node);
return action;
}
}
return NULL;
}
bool
confirm_cancel_action(const char *id, const char *node_id)
{
const char *op_key = NULL;
const char *node_name = NULL;
pcmk__graph_action_t *cancel = get_cancel_action(id, node_id);
if (cancel == NULL) {
return FALSE;
}
op_key = crm_element_value(cancel->xml, PCMK__XA_OPERATION_KEY);
node_name = crm_element_value(cancel->xml, PCMK__META_ON_NODE);
stop_te_timer(cancel);
te_action_confirmed(cancel, controld_globals.transition_graph);
crm_info("Cancellation of %s on %s confirmed (action %d)",
op_key, node_name, cancel->id);
return TRUE;
}
/* downed nodes are listed like: <downed> <node id="UUID1" /> ... </downed> */
#define XPATH_DOWNED "//" PCMK__XE_DOWNED \
"/" PCMK_XE_NODE "[@" PCMK_XA_ID "='%s']"
/*!
* \brief Find a transition event that would have made a specified node down
*
* \param[in] target UUID of node to match
*
* \return Matching event if found, NULL otherwise
*/
pcmk__graph_action_t *
match_down_event(const char *target)
{
pcmk__graph_action_t *match = NULL;
xmlXPathObjectPtr xpath_ret = NULL;
GList *gIter, *gIter2;
char *xpath = crm_strdup_printf(XPATH_DOWNED, target);
for (gIter = controld_globals.transition_graph->synapses;
gIter != NULL && match == NULL;
gIter = gIter->next) {
for (gIter2 = ((pcmk__graph_synapse_t * ) gIter->data)->actions;
gIter2 != NULL && match == NULL;
gIter2 = gIter2->next) {
match = (pcmk__graph_action_t *) gIter2->data;
if (pcmk_is_set(match->flags, pcmk__graph_action_executed)) {
xpath_ret = xpath_search(match->xml, xpath);
if (numXpathResults(xpath_ret) < 1) {
match = NULL;
}
freeXpathObject(xpath_ret);
} else {
// Only actions that were actually started can match
match = NULL;
}
}
}
free(xpath);
if (match != NULL) {
crm_debug("Shutdown action %d (%s) found for node %s", match->id,
crm_element_value(match->xml, PCMK__XA_OPERATION_KEY),
target);
} else {
crm_debug("No reason to expect node %s to be down", target);
}
return match;
}
void
process_graph_event(xmlNode *event, const char *event_node)
{
int rc = -1; // Actual result
int target_rc = -1; // Expected result
int status = -1; // Executor status
int callid = -1; // Executor call ID
int transition_num = -1; // Transition number
int action_num = -1; // Action number within transition
char *update_te_uuid = NULL;
bool ignore_failures = FALSE;
const char *id = NULL;
const char *desc = NULL;
const char *magic = NULL;
const char *uname = NULL;
CRM_ASSERT(event != NULL);
/*
<lrm_rsc_op id="rsc_east-05_last_0" operation_key="rsc_east-05_monitor_0" operation="monitor" crm-debug-origin="do_update_resource" crm_feature_set="3.0.6" transition-key="9:2:7:be2e97d9-05e2-439d-863e-48f7aecab2aa" transition-magic="0:7;9:2:7:be2e97d9-05e2-439d-863e-48f7aecab2aa" call-id="17" rc-code="7" op-status="0" interval="0" last-rc-change="1355361636" exec-time="128" queue-time="0" op-digest="c81f5f40b1c9e859c992e800b1aa6972"/>
*/
magic = crm_element_value(event, PCMK__XA_TRANSITION_KEY);
if (magic == NULL) {
/* non-change */
return;
}
crm_element_value_int(event, PCMK__XA_OP_STATUS, &status);
if (status == PCMK_EXEC_PENDING) {
return;
}
id = crm_element_value(event, PCMK__XA_OPERATION_KEY);
crm_element_value_int(event, PCMK__XA_RC_CODE, &rc);
crm_element_value_int(event, PCMK__XA_CALL_ID, &callid);
rc = pcmk__effective_rc(rc);
if (decode_transition_key(magic, &update_te_uuid, &transition_num,
&action_num, &target_rc) == FALSE) {
// decode_transition_key() already logged the bad key
crm_err("Can't process action %s result: Incompatible versions? "
CRM_XS " call-id=%d", id, callid);
abort_transition(INFINITY, pcmk__graph_restart, "Bad event", event);
return;
}
if (transition_num == -1) {
// E.g. crm_resource --fail
if (record_outside_event(action_num) != pcmk_rc_ok) {
crm_debug("Outside event with transition key '%s' has already been "
"processed", magic);
goto bail;
}
desc = "initiated outside of the cluster";
abort_transition(INFINITY, pcmk__graph_restart, "Unexpected event",
event);
} else if ((action_num < 0)
|| !pcmk__str_eq(update_te_uuid, controld_globals.te_uuid,
pcmk__str_none)) {
desc = "initiated by a different DC";
abort_transition(INFINITY, pcmk__graph_restart, "Foreign event", event);
} else if ((controld_globals.transition_graph->id != transition_num)
|| controld_globals.transition_graph->complete) {
// Action is not from currently active transition
guint interval_ms = 0;
if (parse_op_key(id, NULL, NULL, &interval_ms)
&& (interval_ms != 0)) {
/* Recurring actions have the transition number they were first
* scheduled in.
*/
if (status == PCMK_EXEC_CANCELLED) {
confirm_cancel_action(id, get_node_id(event));
goto bail;
}
desc = "arrived after initial scheduling";
abort_transition(INFINITY, pcmk__graph_restart,
"Change in recurring result", event);
} else if (controld_globals.transition_graph->id != transition_num) {
desc = "arrived really late";
abort_transition(INFINITY, pcmk__graph_restart, "Old event", event);
} else {
desc = "arrived late";
abort_transition(INFINITY, pcmk__graph_restart, "Inactive graph",
event);
}
} else {
// Event is result of an action from currently active transition
pcmk__graph_action_t *action = controld_get_action(action_num);
if (action == NULL) {
// Should never happen
desc = "unknown";
abort_transition(INFINITY, pcmk__graph_restart, "Unknown event",
event);
} else if (pcmk_is_set(action->flags, pcmk__graph_action_confirmed)) {
/* Nothing further needs to be done if the action has already been
* confirmed. This can happen e.g. when processing both an
* "xxx_last_0" or "xxx_last_failure_0" record as well as the main
* history record, which would otherwise result in incorrectly
* bumping the fail count twice.
*/
crm_log_xml_debug(event, "Event already confirmed:");
goto bail;
} else {
/* An action result needs to be confirmed.
* (This is the only case where desc == NULL.)
*/
if (pcmk__str_eq(crm_meta_value(action->params, PCMK_META_ON_FAIL),
PCMK_VALUE_IGNORE, pcmk__str_casei)) {
ignore_failures = TRUE;
} else if (rc != target_rc) {
pcmk__set_graph_action_flags(action, pcmk__graph_action_failed);
}
stop_te_timer(action);
te_action_confirmed(action, controld_globals.transition_graph);
if (pcmk_is_set(action->flags, pcmk__graph_action_failed)) {
abort_transition(action->synapse->priority + 1,
pcmk__graph_restart, "Event failed", event);
}
}
}
if (id == NULL) {
id = "unknown action";
}
uname = crm_element_value(event, PCMK__META_ON_NODE);
if (uname == NULL) {
uname = "unknown node";
}
if (status == PCMK_EXEC_INVALID) {
// We couldn't attempt the action
crm_info("Transition %d action %d (%s on %s): %s",
transition_num, action_num, id, uname,
pcmk_exec_status_str(status));
} else if (desc && update_failcount(event, event_node, rc, target_rc,
(transition_num == -1), FALSE)) {
crm_notice("Transition %d action %d (%s on %s): expected '%s' but got '%s' "
CRM_XS " target-rc=%d rc=%d call-id=%d event='%s'",
transition_num, action_num, id, uname,
services_ocf_exitcode_str(target_rc),
services_ocf_exitcode_str(rc),
target_rc, rc, callid, desc);
} else if (desc) {
crm_info("Transition %d action %d (%s on %s): %s "
CRM_XS " rc=%d target-rc=%d call-id=%d",
transition_num, action_num, id, uname,
desc, rc, target_rc, callid);
} else if (rc == target_rc) {
crm_info("Transition %d action %d (%s on %s) confirmed: %s "
CRM_XS " rc=%d call-id=%d",
transition_num, action_num, id, uname,
services_ocf_exitcode_str(rc), rc, callid);
} else {
update_failcount(event, event_node, rc, target_rc,
(transition_num == -1), ignore_failures);
crm_notice("Transition %d action %d (%s on %s): expected '%s' but got '%s' "
CRM_XS " target-rc=%d rc=%d call-id=%d",
transition_num, action_num, id, uname,
services_ocf_exitcode_str(target_rc),
services_ocf_exitcode_str(rc),
target_rc, rc, callid);
}
bail:
free(update_te_uuid);
}
diff --git a/include/crm/common/options.h b/include/crm/common/options.h
index 962e3433ca..a7a3b37a66 100644
--- a/include/crm/common/options.h
+++ b/include/crm/common/options.h
@@ -1,212 +1,213 @@
/*
* Copyright 2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
* This source code is licensed under the GNU Lesser General Public License
* version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
*/
#ifndef PCMK__CRM_COMMON_OPTIONS__H
# define PCMK__CRM_COMMON_OPTIONS__H
#ifdef __cplusplus
extern "C" {
#endif
/**
* \file
* \brief API related to options
* \ingroup core
*/
/*
* Cluster options
*/
#define PCMK_OPT_BATCH_LIMIT "batch-limit"
#define PCMK_OPT_CLUSTER_DELAY "cluster-delay"
#define PCMK_OPT_CLUSTER_INFRASTRUCTURE "cluster-infrastructure"
#define PCMK_OPT_CLUSTER_IPC_LIMIT "cluster-ipc-limit"
#define PCMK_OPT_CLUSTER_NAME "cluster-name"
#define PCMK_OPT_CLUSTER_RECHECK_INTERVAL "cluster-recheck-interval"
#define PCMK_OPT_CONCURRENT_FENCING "concurrent-fencing"
#define PCMK_OPT_DC_DEADTIME "dc-deadtime"
#define PCMK_OPT_DC_VERSION "dc-version"
#define PCMK_OPT_ELECTION_TIMEOUT "election-timeout"
#define PCMK_OPT_ENABLE_ACL "enable-acl"
#define PCMK_OPT_ENABLE_STARTUP_PROBES "enable-startup-probes"
#define PCMK_OPT_FENCE_REACTION "fence-reaction"
#define PCMK_OPT_HAVE_WATCHDOG "have-watchdog"
#define PCMK_OPT_JOIN_FINALIZATION_TIMEOUT "join-finalization-timeout"
#define PCMK_OPT_JOIN_INTEGRATION_TIMEOUT "join-integration-timeout"
#define PCMK_OPT_LOAD_THRESHOLD "load-threshold"
#define PCMK_OPT_MAINTENANCE_MODE "maintenance-mode"
#define PCMK_OPT_MIGRATION_LIMIT "migration-limit"
#define PCMK_OPT_NO_QUORUM_POLICY "no-quorum-policy"
#define PCMK_OPT_NODE_ACTION_LIMIT "node-action-limit"
#define PCMK_OPT_NODE_HEALTH_BASE "node-health-base"
#define PCMK_OPT_NODE_HEALTH_GREEN "node-health-green"
#define PCMK_OPT_NODE_HEALTH_RED "node-health-red"
#define PCMK_OPT_NODE_HEALTH_STRATEGY "node-health-strategy"
#define PCMK_OPT_NODE_HEALTH_YELLOW "node-health-yellow"
#define PCMK_OPT_NODE_PENDING_TIMEOUT "node-pending-timeout"
#define PCMK_OPT_PE_ERROR_SERIES_MAX "pe-error-series-max"
#define PCMK_OPT_PE_INPUT_SERIES_MAX "pe-input-series-max"
#define PCMK_OPT_PE_WARN_SERIES_MAX "pe-warn-series-max"
#define PCMK_OPT_PLACEMENT_STRATEGY "placement-strategy"
#define PCMK_OPT_PRIORITY_FENCING_DELAY "priority-fencing-delay"
#define PCMK_OPT_SHUTDOWN_ESCALATION "shutdown-escalation"
#define PCMK_OPT_SHUTDOWN_LOCK "shutdown-lock"
#define PCMK_OPT_SHUTDOWN_LOCK_LIMIT "shutdown-lock-limit"
#define PCMK_OPT_START_FAILURE_IS_FATAL "start-failure-is-fatal"
#define PCMK_OPT_STARTUP_FENCING "startup-fencing"
#define PCMK_OPT_STONITH_ACTION "stonith-action"
#define PCMK_OPT_STONITH_ENABLED "stonith-enabled"
#define PCMK_OPT_STONITH_MAX_ATTEMPTS "stonith-max-attempts"
#define PCMK_OPT_STONITH_TIMEOUT "stonith-timeout"
#define PCMK_OPT_STONITH_WATCHDOG_TIMEOUT "stonith-watchdog-timeout"
#define PCMK_OPT_STOP_ALL_RESOURCES "stop-all-resources"
#define PCMK_OPT_STOP_ORPHAN_ACTIONS "stop-orphan-actions"
#define PCMK_OPT_STOP_ORPHAN_RESOURCES "stop-orphan-resources"
#define PCMK_OPT_SYMMETRIC_CLUSTER "symmetric-cluster"
#define PCMK_OPT_TRANSITION_DELAY "transition-delay"
/*
* Meta-attributes
*/
#define PCMK_META_ALLOW_MIGRATE "allow-migrate"
#define PCMK_META_ALLOW_UNHEALTHY_NODES "allow-unhealthy-nodes"
#define PCMK_META_CLONE_MAX "clone-max"
#define PCMK_META_CLONE_MIN "clone-min"
#define PCMK_META_CLONE_NODE_MAX "clone-node-max"
#define PCMK_META_CONTAINER_ATTRIBUTE_TARGET "container-attribute-target"
#define PCMK_META_CRITICAL "critical"
#define PCMK_META_ENABLED "enabled"
#define PCMK_META_FAILURE_TIMEOUT "failure-timeout"
#define PCMK_META_GLOBALLY_UNIQUE "globally-unique"
#define PCMK_META_INTERLEAVE "interleave"
#define PCMK_META_INTERVAL "interval"
#define PCMK_META_IS_MANAGED "is-managed"
#define PCMK_META_INTERVAL_ORIGIN "interval-origin"
#define PCMK_META_MAINTENANCE "maintenance"
#define PCMK_META_MIGRATION_THRESHOLD "migration-threshold"
#define PCMK_META_MULTIPLE_ACTIVE "multiple-active"
#define PCMK_META_NOTIFY "notify"
#define PCMK_META_ON_FAIL "on-fail"
#define PCMK_META_ORDERED "ordered"
#define PCMK_META_PRIORITY "priority"
#define PCMK_META_PROMOTABLE "promotable"
#define PCMK_META_PROMOTED_MAX "promoted-max"
#define PCMK_META_PROMOTED_NODE_MAX "promoted-node-max"
#define PCMK_META_RECORD_PENDING "record-pending"
#define PCMK_META_REMOTE_ADDR "remote-addr"
#define PCMK_META_REMOTE_ALLOW_MIGRATE "remote-allow-migrate"
#define PCMK_META_REMOTE_CONNECT_TIMEOUT "remote-connect-timeout"
#define PCMK_META_REMOTE_NODE "remote-node"
#define PCMK_META_REMOTE_PORT "remote-port"
#define PCMK_META_REQUIRES "requires"
#define PCMK_META_RESOURCE_STICKINESS "resource-stickiness"
#define PCMK_META_START_DELAY "start-delay"
#define PCMK_META_TARGET_ROLE "target-role"
#define PCMK_META_TIMEOUT "timeout"
#define PCMK_META_TIMESTAMP_FORMAT "timestamp-format"
/*
* Remote resource instance attributes
*/
#define PCMK_REMOTE_RA_ADDR "addr"
#define PCMK_REMOTE_RA_PORT "port"
#define PCMK_REMOTE_RA_RECONNECT_INTERVAL "reconnect_interval"
#define PCMK_REMOTE_RA_SERVER "server"
/*
* Enumerated values
*/
#define PCMK_VALUE_ALWAYS "always"
#define PCMK_VALUE_AND "and"
#define PCMK_VALUE_BALANCED "balanced"
#define PCMK_VALUE_BLOCK "block"
#define PCMK_VALUE_CIB_BOOTSTRAP_OPTIONS "cib-bootstrap-options"
#define PCMK_VALUE_CREATE "create"
#define PCMK_VALUE_CUSTOM "custom"
#define PCMK_VALUE_DATE_SPEC "date_spec"
#define PCMK_VALUE_DEFAULT "default"
#define PCMK_VALUE_DEFINED "defined"
#define PCMK_VALUE_DELETE "delete"
#define PCMK_VALUE_DEMOTE "demote"
#define PCMK_VALUE_DENY "deny"
#define PCMK_VALUE_EQ "eq"
#define PCMK_VALUE_EXCLUSIVE "exclusive"
#define PCMK_VALUE_FAILED "failed"
#define PCMK_VALUE_FALSE "false"
#define PCMK_VALUE_FENCE "fence"
#define PCMK_VALUE_FENCING "fencing"
#define PCMK_VALUE_FREEZE "freeze"
#define PCMK_VALUE_GRANTED "granted"
#define PCMK_VALUE_GREEN "green"
#define PCMK_VALUE_GT "gt"
#define PCMK_VALUE_GTE "gte"
#define PCMK_VALUE_HOST "host"
#define PCMK_VALUE_IGNORE "ignore"
+#define PCMK_VALUE_INFINITY "INFINITY"
#define PCMK_VALUE_INTEGER "integer"
#define PCMK_VALUE_LITERAL "literal"
#define PCMK_VALUE_LT "lt"
#define PCMK_VALUE_LTE "lte"
#define PCMK_VALUE_MANDATORY "Mandatory"
#define PCMK_VALUE_MEMBER "member"
#define PCMK_VALUE_META "meta"
#define PCMK_VALUE_MIGRATE_ON_RED "migrate-on-red"
#define PCMK_VALUE_MINIMAL "minimal"
#define PCMK_VALUE_MODIFY "modify"
#define PCMK_VALUE_MOVE "move"
#define PCMK_VALUE_NE "ne"
#define PCMK_VALUE_NEVER "never"
#define PCMK_VALUE_NONE "none"
#define PCMK_VALUE_NOT_DEFINED "not_defined"
#define PCMK_VALUE_NOTHING "nothing"
#define PCMK_VALUE_NUMBER "number"
#define PCMK_VALUE_OFFLINE "offline"
#define PCMK_VALUE_ONLINE "online"
#define PCMK_VALUE_ONLY_GREEN "only-green"
#define PCMK_VALUE_OPTIONAL "Optional"
#define PCMK_VALUE_OR "or"
#define PCMK_VALUE_PANIC "panic"
#define PCMK_VALUE_PARAM "param"
#define PCMK_VALUE_PENDING "pending"
#define PCMK_VALUE_PROGRESSIVE "progressive"
#define PCMK_VALUE_QUORUM "quorum"
#define PCMK_VALUE_READ "read"
#define PCMK_VALUE_RED "red"
#define PCMK_VALUE_REMOTE "remote"
#define PCMK_VALUE_RESTART "restart"
#define PCMK_VALUE_RESTART_CONTAINER "restart-container"
#define PCMK_VALUE_REVOKED "revoked"
#define PCMK_VALUE_SERIALIZE "Serialize"
#define PCMK_VALUE_STANDBY "standby"
#define PCMK_VALUE_STRING "string"
#define PCMK_VALUE_STOP "stop"
#define PCMK_VALUE_SUCCESS "success"
#define PCMK_VALUE_TRUE "true"
#define PCMK_VALUE_UNFENCING "unfencing"
#define PCMK_VALUE_UNKNOWN "unknown"
#define PCMK_VALUE_UTILIZATION "utilization"
#define PCMK_VALUE_VERSION "version"
#define PCMK_VALUE_WRITE "write"
#define PCMK_VALUE_YELLOW "yellow"
// @COMPAT This will become a deprecated alias for PCMK_VALUE_FENCE (see T279)
#define PCMK_VALUE_FENCE_LEGACY "suicide"
#ifdef __cplusplus
}
#endif
#endif // PCMK__CRM_COMMON_OPTIONS__H
diff --git a/include/crm/crm.h b/include/crm/crm.h
index 000b05d108..9e23b42138 100644
--- a/include/crm/crm.h
+++ b/include/crm/crm.h
@@ -1,177 +1,177 @@
/*
* 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 PCMK__CRM_CRM__H
# define PCMK__CRM_CRM__H
# include <crm_config.h>
# include <stdlib.h>
# include <glib.h>
# include <stdbool.h>
# include <string.h>
# include <libxml/tree.h>
#include <crm/common/options.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* \file
* \brief A dumping ground
* \ingroup core
*/
#ifndef PCMK_ALLOW_DEPRECATED
/*!
* \brief Allow use of deprecated Pacemaker APIs
*
* By default, external code using Pacemaker headers is allowed to use
* deprecated Pacemaker APIs. If PCMK_ALLOW_DEPRECATED is defined to 0 before
* including any Pacemaker headers, deprecated APIs will be unusable. It is
* strongly recommended to leave this unchanged for production and release
* builds, to avoid breakage when users upgrade to new Pacemaker releases that
* deprecate more APIs. This should be defined to 0 only for development and
* testing builds when desiring to check for usage of currently deprecated APIs.
*/
#define PCMK_ALLOW_DEPRECATED 1
#endif
/*!
* The CRM feature set assists with compatibility in mixed-version clusters.
* The major version number increases when nodes with different versions
* would not work (rolling upgrades are not allowed). The minor version
* number increases when mixed-version clusters are allowed only during
* rolling upgrades (a node with the oldest feature set will be elected DC). The
* minor-minor version number is ignored, but allows resource agents to detect
* cluster support for various features.
*
* The feature set also affects the processing of old saved CIBs (such as for
* many scheduler regression tests).
*
* Particular feature points currently tested by Pacemaker code:
*
* >2.1: Operation updates include timing data
* >=3.0.5: XML v2 digests are created
* >=3.0.8: Peers do not need acks for cancellations
* >=3.0.9: DC will send its own shutdown request to all peers
* XML v2 patchsets are created by default
* >=3.0.13: Fail counts include operation name and interval
* >=3.2.0: DC supports PCMK_EXEC_INVALID and PCMK_EXEC_NOT_CONNECTED
* >=3.19.0: DC supports PCMK__CIB_REQUEST_COMMIT_TRANSACT
*/
# define CRM_FEATURE_SET "3.19.0"
/* Pacemaker's CPG protocols use fixed-width binary fields for the sender and
* recipient of a CPG message. This imposes an arbitrary limit on cluster node
* names.
*/
//! \brief Maximum length of a Corosync cluster node name (in bytes)
#define MAX_NAME 256
# define CRM_META "CRM_meta"
extern char *crm_system_name;
// How we represent "infinite" scores
# define CRM_SCORE_INFINITY 1000000
-# define CRM_INFINITY_S "INFINITY"
-# define CRM_PLUS_INFINITY_S "+" CRM_INFINITY_S
-# define CRM_MINUS_INFINITY_S "-" CRM_INFINITY_S
+# define CRM_INFINITY_S PCMK_VALUE_INFINITY
+# define CRM_PLUS_INFINITY_S "+" PCMK_VALUE_INFINITY
+# define CRM_MINUS_INFINITY_S "-" PCMK_VALUE_INFINITY
/* @COMPAT API < 2.0.0 Deprecated "infinity" aliases
*
* INFINITY might be defined elsewhere (e.g. math.h), so undefine it first.
* This, of course, complicates any attempt to use the other definition in any
* code that includes this header.
*/
# undef INFINITY
# define INFINITY_S "INFINITY"
# define MINUS_INFINITY_S "-INFINITY"
# define INFINITY 1000000
/* Sub-systems */
# define CRM_SYSTEM_DC "dc"
#define CRM_SYSTEM_DCIB "dcib" // Primary instance of CIB manager
# define CRM_SYSTEM_CIB "cib"
# define CRM_SYSTEM_CRMD "crmd"
# define CRM_SYSTEM_LRMD "lrmd"
# define CRM_SYSTEM_PENGINE "pengine"
# define CRM_SYSTEM_TENGINE "tengine"
# define CRM_SYSTEM_STONITHD "stonithd"
# define CRM_SYSTEM_MCP "pacemakerd"
// Names of internally generated node attributes
// @TODO Replace these with PCMK_NODE_ATTR_*
# define CRM_ATTR_UNAME "#uname"
# define CRM_ATTR_ID "#id"
# define CRM_ATTR_KIND "#kind"
# define CRM_ATTR_ROLE "#role"
# define CRM_ATTR_IS_DC "#is_dc"
# define CRM_ATTR_CLUSTER_NAME "#cluster-name"
# define CRM_ATTR_SITE_NAME "#site-name"
# define CRM_ATTR_UNFENCED "#node-unfenced"
# define CRM_ATTR_DIGESTS_ALL "#digests-all"
# define CRM_ATTR_DIGESTS_SECURE "#digests-secure"
# define CRM_ATTR_PROTOCOL "#attrd-protocol"
# define CRM_ATTR_FEATURE_SET "#feature-set"
/* Valid operations */
# define CRM_OP_NOOP "noop"
# define CRM_OP_JOIN_ANNOUNCE "join_announce"
# define CRM_OP_JOIN_OFFER "join_offer"
# define CRM_OP_JOIN_REQUEST "join_request"
# define CRM_OP_JOIN_ACKNAK "join_ack_nack"
# define CRM_OP_JOIN_CONFIRM "join_confirm"
# define CRM_OP_PING "ping"
# define CRM_OP_NODE_INFO "node-info"
# define CRM_OP_THROTTLE "throttle"
# define CRM_OP_VOTE "vote"
# define CRM_OP_NOVOTE "no-vote"
# define CRM_OP_HELLO "hello"
# define CRM_OP_PECALC "pe_calc"
# define CRM_OP_QUIT "quit"
# define CRM_OP_SHUTDOWN_REQ "req_shutdown"
# define CRM_OP_SHUTDOWN PCMK_ACTION_DO_SHUTDOWN
# define CRM_OP_REGISTER "register"
# define CRM_OP_IPC_FWD "ipc_fwd"
# define CRM_OP_INVOKE_LRM "lrm_invoke"
# define CRM_OP_LRM_REFRESH "lrm_refresh" //!< Deprecated since 1.1.10
# define CRM_OP_LRM_DELETE PCMK_ACTION_LRM_DELETE
# define CRM_OP_LRM_FAIL "lrm_fail"
# define CRM_OP_PROBED "probe_complete"
# define CRM_OP_REPROBE "probe_again"
# define CRM_OP_CLEAR_FAILCOUNT PCMK_ACTION_CLEAR_FAILCOUNT
# define CRM_OP_REMOTE_STATE "remote_state"
# define CRM_OP_RM_NODE_CACHE "rm_node_cache"
# define CRM_OP_MAINTENANCE_NODES PCMK_ACTION_MAINTENANCE_NODES
/* Possible cluster membership states */
# define CRMD_JOINSTATE_DOWN "down"
# define CRMD_JOINSTATE_PENDING "pending"
# define CRMD_JOINSTATE_MEMBER "member"
# define CRMD_JOINSTATE_NACK "banned"
# include <crm/common/actions.h>
# include <crm/common/cib.h>
# include <crm/common/logging.h>
# include <crm/common/util.h>
#if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1)
#include <crm/crm_compat.h>
#endif
#ifdef __cplusplus
}
#endif
#endif
diff --git a/lib/common/scores.c b/lib/common/scores.c
index e2afb6dec2..a291e6dda2 100644
--- a/lib/common/scores.c
+++ b/lib/common/scores.c
@@ -1,166 +1,166 @@
/*
* 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.
*/
#include <crm_internal.h>
#ifndef _GNU_SOURCE
# define _GNU_SOURCE
#endif
#include <stdio.h> // snprintf(), NULL
#include <string.h> // strcpy(), strdup()
#include <sys/types.h> // size_t
int pcmk__score_red = 0;
int pcmk__score_green = 0;
int pcmk__score_yellow = 0;
/*!
* \brief Get the integer value of a score string
*
* Given a string representation of a score, return the integer equivalent.
* This accepts infinity strings as well as red, yellow, and green, and
* bounds the result to +/-INFINITY.
*
* \param[in] score Score as string
*
* \return Integer value corresponding to \p score
*/
int
char2score(const char *score)
{
if (score == NULL) {
return 0;
} else if (pcmk_str_is_minus_infinity(score)) {
return -CRM_SCORE_INFINITY;
} else if (pcmk_str_is_infinity(score)) {
return CRM_SCORE_INFINITY;
} else if (pcmk__str_eq(score, PCMK_VALUE_RED, pcmk__str_casei)) {
return pcmk__score_red;
} else if (pcmk__str_eq(score, PCMK_VALUE_YELLOW, pcmk__str_casei)) {
return pcmk__score_yellow;
} else if (pcmk__str_eq(score, PCMK_VALUE_GREEN, pcmk__str_casei)) {
return pcmk__score_green;
} else {
long long score_ll;
pcmk__scan_ll(score, &score_ll, 0LL);
if (score_ll > CRM_SCORE_INFINITY) {
return CRM_SCORE_INFINITY;
} else if (score_ll < -CRM_SCORE_INFINITY) {
return -CRM_SCORE_INFINITY;
} else {
return (int) score_ll;
}
}
}
/*!
* \brief Return a displayable static string for a score value
*
* Given a score value, return a pointer to a static string representation of
* the score suitable for log messages, output, etc.
*
* \param[in] score Score to display
*
* \return Pointer to static memory containing string representation of \p score
* \note Subsequent calls to this function will overwrite the returned value, so
* it should be used only in a local context such as a printf()-style
* statement.
*/
const char *
pcmk_readable_score(int score)
{
// The longest possible result is "-INFINITY"
static char score_s[sizeof(CRM_MINUS_INFINITY_S)];
if (score >= CRM_SCORE_INFINITY) {
- strcpy(score_s, CRM_INFINITY_S);
+ strcpy(score_s, PCMK_VALUE_INFINITY);
} else if (score <= -CRM_SCORE_INFINITY) {
strcpy(score_s, CRM_MINUS_INFINITY_S);
} else {
// Range is limited to +/-1000000, so no chance of overflow
snprintf(score_s, sizeof(score_s), "%d", score);
}
return score_s;
}
/*!
* \internal
* \brief Add two scores, bounding to +/-INFINITY
*
* \param[in] score1 First score to add
* \param[in] score2 Second score to add
*
* \note This function does not have context about what the scores mean, so it
* does not log any messages.
*/
int
pcmk__add_scores(int score1, int score2)
{
/* As long as CRM_SCORE_INFINITY is less than half of the maximum integer,
* we can ignore the possibility of integer overflow.
*/
int result = score1 + score2;
// First handle the cases where one or both is infinite
if ((score1 <= -CRM_SCORE_INFINITY) || (score2 <= -CRM_SCORE_INFINITY)) {
return -CRM_SCORE_INFINITY;
}
if ((score1 >= CRM_SCORE_INFINITY) || (score2 >= CRM_SCORE_INFINITY)) {
return CRM_SCORE_INFINITY;
}
// Bound result to infinity.
if (result >= CRM_SCORE_INFINITY) {
return CRM_SCORE_INFINITY;
}
if (result <= -CRM_SCORE_INFINITY) {
return -CRM_SCORE_INFINITY;
}
return result;
}
// Deprecated functions kept only for backward API compatibility
// LCOV_EXCL_START
#include <crm/common/util_compat.h>
char *
score2char(int score)
{
char *result = strdup(pcmk_readable_score(score));
CRM_ASSERT(result != NULL);
return result;
}
char *
score2char_stack(int score, char *buf, size_t len)
{
CRM_CHECK((buf != NULL) && (len >= sizeof(CRM_MINUS_INFINITY_S)),
return NULL);
strcpy(buf, pcmk_readable_score(score));
return buf;
}
// LCOV_EXCL_STOP
// End deprecated API
diff --git a/lib/common/tests/scores/pcmk_readable_score_test.c b/lib/common/tests/scores/pcmk_readable_score_test.c
index ae24159143..6a0275600a 100644
--- a/lib/common/tests/scores/pcmk_readable_score_test.c
+++ b/lib/common/tests/scores/pcmk_readable_score_test.c
@@ -1,33 +1,33 @@
/*
- * Copyright 2022 the Pacemaker project contributors
+ * Copyright 2022-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
* This source code is licensed under the GNU General Public License version 2
* or later (GPLv2+) WITHOUT ANY WARRANTY.
*/
#include <crm_internal.h>
#include <crm/common/unittest_internal.h>
static void
outside_limits(void **state)
{
assert_string_equal(pcmk_readable_score(CRM_SCORE_INFINITY * 2),
- CRM_INFINITY_S);
+ PCMK_VALUE_INFINITY);
assert_string_equal(pcmk_readable_score(-CRM_SCORE_INFINITY * 2),
CRM_MINUS_INFINITY_S);
}
static void
inside_limits(void **state)
{
assert_string_equal(pcmk_readable_score(0), "0");
assert_string_equal(pcmk_readable_score(1024), "1024");
assert_string_equal(pcmk_readable_score(-1024), "-1024");
}
PCMK__UNIT_TEST(NULL, NULL,
cmocka_unit_test(outside_limits),
cmocka_unit_test(inside_limits))
diff --git a/lib/common/utils.c b/lib/common/utils.c
index a6bf4f9a8c..dfa64b21e1 100644
--- a/lib/common/utils.c
+++ b/lib/common/utils.c
@@ -1,553 +1,553 @@
/*
* 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.
*/
#include <crm_internal.h>
#ifndef _GNU_SOURCE
# define _GNU_SOURCE
#endif
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/utsname.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <pwd.h>
#include <time.h>
#include <libgen.h>
#include <signal.h>
#include <grp.h>
#include <qb/qbdefs.h>
#include <crm/crm.h>
#include <crm/services.h>
#include <crm/cib/internal.h>
#include <crm/common/xml.h>
#include <crm/common/util.h>
#include <crm/common/ipc.h>
#include <crm/common/iso8601.h>
#include <crm/common/mainloop.h>
#include <libxml2/libxml/relaxng.h>
#include "crmcommon_private.h"
CRM_TRACE_INIT_DATA(common);
gboolean crm_config_error = FALSE;
gboolean crm_config_warning = FALSE;
char *crm_system_name = NULL;
bool
pcmk__is_user_in_group(const char *user, const char *group)
{
struct group *grent;
char **gr_mem;
if (user == NULL || group == NULL) {
return false;
}
setgrent();
while ((grent = getgrent()) != NULL) {
if (grent->gr_mem == NULL) {
continue;
}
if(strcmp(group, grent->gr_name) != 0) {
continue;
}
gr_mem = grent->gr_mem;
while (*gr_mem != NULL) {
if (!strcmp(user, *gr_mem++)) {
endgrent();
return true;
}
}
}
endgrent();
return false;
}
int
crm_user_lookup(const char *name, uid_t * uid, gid_t * gid)
{
int rc = pcmk_ok;
char *buffer = NULL;
struct passwd pwd;
struct passwd *pwentry = NULL;
buffer = calloc(1, PCMK__PW_BUFFER_LEN);
if (buffer == NULL) {
return -ENOMEM;
}
rc = getpwnam_r(name, &pwd, buffer, PCMK__PW_BUFFER_LEN, &pwentry);
if (pwentry) {
if (uid) {
*uid = pwentry->pw_uid;
}
if (gid) {
*gid = pwentry->pw_gid;
}
crm_trace("User %s has uid=%d gid=%d", name, pwentry->pw_uid, pwentry->pw_gid);
} else {
rc = rc? -rc : -EINVAL;
crm_info("User %s lookup: %s", name, pcmk_strerror(rc));
}
free(buffer);
return rc;
}
/*!
* \brief Get user and group IDs of pacemaker daemon user
*
* \param[out] uid If non-NULL, where to store daemon user ID
* \param[out] gid If non-NULL, where to store daemon group ID
*
* \return pcmk_ok on success, -errno otherwise
*/
int
pcmk_daemon_user(uid_t *uid, gid_t *gid)
{
static uid_t daemon_uid;
static gid_t daemon_gid;
static bool found = false;
int rc = pcmk_ok;
if (!found) {
rc = crm_user_lookup(CRM_DAEMON_USER, &daemon_uid, &daemon_gid);
if (rc == pcmk_ok) {
found = true;
}
}
if (found) {
if (uid) {
*uid = daemon_uid;
}
if (gid) {
*gid = daemon_gid;
}
}
return rc;
}
/*!
* \internal
* \brief Return the integer equivalent of a portion of a string
*
* \param[in] text Pointer to beginning of string portion
* \param[out] end_text This will point to next character after integer
*/
static int
version_helper(const char *text, const char **end_text)
{
int atoi_result = -1;
CRM_ASSERT(end_text != NULL);
errno = 0;
if (text != NULL && text[0] != 0) {
/* seemingly sacrificing const-correctness -- because while strtol
doesn't modify the input, it doesn't want to artificially taint the
"end_text" pointer-to-pointer-to-first-char-in-string with constness
in case the input wasn't actually constant -- by semantic definition
not a single character will get modified so it shall be perfectly
safe to make compiler happy with dropping "const" qualifier here */
atoi_result = (int) strtol(text, (char **) end_text, 10);
if (errno == EINVAL) {
crm_err("Conversion of '%s' %c failed", text, text[0]);
atoi_result = -1;
}
}
return atoi_result;
}
/*
* version1 < version2 : -1
* version1 = version2 : 0
* version1 > version2 : 1
*/
int
compare_version(const char *version1, const char *version2)
{
int rc = 0;
int lpc = 0;
const char *ver1_iter, *ver2_iter;
if (version1 == NULL && version2 == NULL) {
return 0;
} else if (version1 == NULL) {
return -1;
} else if (version2 == NULL) {
return 1;
}
ver1_iter = version1;
ver2_iter = version2;
while (1) {
int digit1 = 0;
int digit2 = 0;
lpc++;
if (ver1_iter == ver2_iter) {
break;
}
if (ver1_iter != NULL) {
digit1 = version_helper(ver1_iter, &ver1_iter);
}
if (ver2_iter != NULL) {
digit2 = version_helper(ver2_iter, &ver2_iter);
}
if (digit1 < digit2) {
rc = -1;
break;
} else if (digit1 > digit2) {
rc = 1;
break;
}
if (ver1_iter != NULL && *ver1_iter == '.') {
ver1_iter++;
}
if (ver1_iter != NULL && *ver1_iter == '\0') {
ver1_iter = NULL;
}
if (ver2_iter != NULL && *ver2_iter == '.') {
ver2_iter++;
}
if (ver2_iter != NULL && *ver2_iter == 0) {
ver2_iter = NULL;
}
}
if (rc == 0) {
crm_trace("%s == %s (%d)", version1, version2, lpc);
} else if (rc < 0) {
crm_trace("%s < %s (%d)", version1, version2, lpc);
} else if (rc > 0) {
crm_trace("%s > %s (%d)", version1, version2, lpc);
}
return rc;
}
/*!
* \internal
* \brief Log a failed assertion
*
* \param[in] file File making the assertion
* \param[in] function Function making the assertion
* \param[in] line Line of file making the assertion
* \param[in] assert_condition String representation of assertion
*/
static void
log_assertion_as(const char *file, const char *function, int line,
const char *assert_condition)
{
if (!pcmk__is_daemon) {
crm_enable_stderr(TRUE); // Make sure command-line user sees message
}
crm_err("%s: Triggered fatal assertion at %s:%d : %s",
function, file, line, assert_condition);
}
/* coverity[+kill] */
/*!
* \internal
* \brief Log a failed assertion and abort
*
* \param[in] file File making the assertion
* \param[in] function Function making the assertion
* \param[in] line Line of file making the assertion
* \param[in] assert_condition String representation of assertion
*
* \note This does not return
*/
static _Noreturn void
abort_as(const char *file, const char *function, int line,
const char *assert_condition)
{
log_assertion_as(file, function, line, assert_condition);
abort();
}
/* coverity[+kill] */
/*!
* \internal
* \brief Handle a failed assertion
*
* When called by a daemon, fork a child that aborts (to dump core), otherwise
* abort the current process.
*
* \param[in] file File making the assertion
* \param[in] function Function making the assertion
* \param[in] line Line of file making the assertion
* \param[in] assert_condition String representation of assertion
*/
static void
fail_assert_as(const char *file, const char *function, int line,
const char *assert_condition)
{
int status = 0;
pid_t pid = 0;
if (!pcmk__is_daemon) {
abort_as(file, function, line, assert_condition); // does not return
}
pid = fork();
switch (pid) {
case -1: // Fork failed
crm_warn("%s: Cannot dump core for non-fatal assertion at %s:%d "
": %s", function, file, line, assert_condition);
break;
case 0: // Child process: just abort to dump core
abort();
break;
default: // Parent process: wait for child
crm_err("%s: Forked child [%d] to record non-fatal assertion at "
"%s:%d : %s", function, pid, file, line, assert_condition);
crm_write_blackbox(SIGTRAP, NULL);
do {
if (waitpid(pid, &status, 0) == pid) {
return; // Child finished dumping core
}
} while (errno == EINTR);
if (errno == ECHILD) {
// crm_mon ignores SIGCHLD
crm_trace("Cannot wait on forked child [%d] "
"(SIGCHLD is probably ignored)", pid);
} else {
crm_err("Cannot wait on forked child [%d]: %s",
pid, pcmk_rc_str(errno));
}
break;
}
}
/* coverity[+kill] */
void
crm_abort(const char *file, const char *function, int line,
const char *assert_condition, gboolean do_core, gboolean do_fork)
{
if (!do_fork) {
abort_as(file, function, line, assert_condition);
} else if (do_core) {
fail_assert_as(file, function, line, assert_condition);
} else {
log_assertion_as(file, function, line, assert_condition);
}
}
/*!
* \internal
* \brief Convert the current process to a daemon process
*
* Fork a child process, exit the parent, create a PID file with the current
* process ID, and close the standard input/output/error file descriptors.
* Exit instead if a daemon is already running and using the PID file.
*
* \param[in] name Daemon executable name
* \param[in] pidfile File name to use as PID file
*/
void
pcmk__daemonize(const char *name, const char *pidfile)
{
int rc;
pid_t pid;
/* Check before we even try... */
rc = pcmk__pidfile_matches(pidfile, 1, name, &pid);
if ((rc != pcmk_rc_ok) && (rc != ENOENT)) {
crm_err("%s: already running [pid %lld in %s]",
name, (long long) pid, pidfile);
printf("%s: already running [pid %lld in %s]\n",
name, (long long) pid, pidfile);
crm_exit(CRM_EX_ERROR);
}
pid = fork();
if (pid < 0) {
fprintf(stderr, "%s: could not start daemon\n", name);
crm_perror(LOG_ERR, "fork");
crm_exit(CRM_EX_OSERR);
} else if (pid > 0) {
crm_exit(CRM_EX_OK);
}
rc = pcmk__lock_pidfile(pidfile, name);
if (rc != pcmk_rc_ok) {
crm_err("Could not lock '%s' for %s: %s " CRM_XS " rc=%d",
pidfile, name, pcmk_rc_str(rc), rc);
printf("Could not lock '%s' for %s: %s (%d)\n",
pidfile, name, pcmk_rc_str(rc), rc);
crm_exit(CRM_EX_ERROR);
}
umask(S_IWGRP | S_IWOTH | S_IROTH);
close(STDIN_FILENO);
pcmk__open_devnull(O_RDONLY); // stdin (fd 0)
close(STDOUT_FILENO);
pcmk__open_devnull(O_WRONLY); // stdout (fd 1)
close(STDERR_FILENO);
pcmk__open_devnull(O_WRONLY); // stderr (fd 2)
}
#ifdef HAVE_UUID_UUID_H
# include <uuid/uuid.h>
#endif
char *
crm_generate_uuid(void)
{
unsigned char uuid[16];
char *buffer = malloc(37); /* Including NUL byte */
CRM_ASSERT(buffer != NULL);
uuid_generate(uuid);
uuid_unparse(uuid, buffer);
return buffer;
}
#ifdef HAVE_GNUTLS_GNUTLS_H
void
crm_gnutls_global_init(void)
{
signal(SIGPIPE, SIG_IGN);
gnutls_global_init();
}
#endif
/*!
* \brief Get the local hostname
*
* \return Newly allocated string with name, or NULL (and set errno) on error
*/
char *
pcmk_hostname(void)
{
struct utsname hostinfo;
return (uname(&hostinfo) < 0)? NULL : strdup(hostinfo.nodename);
}
bool
pcmk_str_is_infinity(const char *s) {
- return pcmk__str_any_of(s, CRM_INFINITY_S, CRM_PLUS_INFINITY_S, NULL);
+ return pcmk__str_any_of(s, PCMK_VALUE_INFINITY, CRM_PLUS_INFINITY_S, NULL);
}
bool
pcmk_str_is_minus_infinity(const char *s) {
return pcmk__str_eq(s, CRM_MINUS_INFINITY_S, pcmk__str_none);
}
/*!
* \internal
* \brief Sleep for given milliseconds
*
* \param[in] ms Time to sleep
*
* \note The full time might not be slept if a signal is received.
*/
void
pcmk__sleep_ms(unsigned int ms)
{
// @TODO Impose a sane maximum sleep to avoid hanging a process for long
//CRM_CHECK(ms <= MAX_SLEEP, ms = MAX_SLEEP);
// Use sleep() for any whole seconds
if (ms >= 1000) {
sleep(ms / 1000);
ms -= ms / 1000;
}
if (ms == 0) {
return;
}
#if defined(HAVE_NANOSLEEP)
// nanosleep() is POSIX-2008, so prefer that
{
struct timespec req = { .tv_sec = 0, .tv_nsec = (long) (ms * 1000000) };
nanosleep(&req, NULL);
}
#elif defined(HAVE_USLEEP)
// usleep() is widely available, though considered obsolete
usleep((useconds_t) ms);
#else
// Otherwise use a trick with select() timeout
{
struct timeval tv = { .tv_sec = 0, .tv_usec = (suseconds_t) ms };
select(0, NULL, NULL, NULL, &tv);
}
#endif
}
// Deprecated functions kept only for backward API compatibility
// LCOV_EXCL_START
#include <crm/common/util_compat.h>
guint
crm_parse_interval_spec(const char *input)
{
long long msec = -1;
errno = 0;
if (input == NULL) {
return 0;
} else if (input[0] == 'P') {
crm_time_t *period_s = crm_time_parse_duration(input);
if (period_s) {
msec = 1000 * crm_time_get_seconds(period_s);
crm_time_free(period_s);
}
} else {
msec = crm_get_msec(input);
}
if (msec < 0) {
crm_warn("Using 0 instead of '%s'", input);
errno = EINVAL;
return 0;
}
return (msec >= G_MAXUINT)? G_MAXUINT : (guint) msec;
}
// LCOV_EXCL_STOP
// End deprecated API
diff --git a/tools/crm_resource_ban.c b/tools/crm_resource_ban.c
index a9ec72be84..a1da9b0def 100644
--- a/tools/crm_resource_ban.c
+++ b/tools/crm_resource_ban.c
@@ -1,502 +1,502 @@
/*
* Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
* This source code is licensed under the GNU General Public License version 2
* or later (GPLv2+) WITHOUT ANY WARRANTY.
*/
#include <crm_internal.h>
#include <crm_resource.h>
static char *
parse_cli_lifetime(pcmk__output_t *out, const char *move_lifetime)
{
char *later_s = NULL;
crm_time_t *now = NULL;
crm_time_t *later = NULL;
crm_time_t *duration = NULL;
if (move_lifetime == NULL) {
return NULL;
}
duration = crm_time_parse_duration(move_lifetime);
if (duration == NULL) {
out->err(out, "Invalid duration specified: %s\n"
"Please refer to https://en.wikipedia.org/wiki/ISO_8601#Durations "
"for examples of valid durations", move_lifetime);
return NULL;
}
now = crm_time_new(NULL);
later = crm_time_add(now, duration);
if (later == NULL) {
out->err(out, "Unable to add %s to current time\n"
"Please report to " PACKAGE_BUGREPORT " as possible bug",
move_lifetime);
crm_time_free(now);
crm_time_free(duration);
return NULL;
}
crm_time_log(LOG_INFO, "now ", now,
crm_time_log_date | crm_time_log_timeofday | crm_time_log_with_timezone);
crm_time_log(LOG_INFO, "later ", later,
crm_time_log_date | crm_time_log_timeofday | crm_time_log_with_timezone);
crm_time_log(LOG_INFO, "duration", duration, crm_time_log_date | crm_time_log_timeofday);
later_s = crm_time_as_string(later, crm_time_log_date | crm_time_log_timeofday | crm_time_log_with_timezone);
out->info(out, "Migration will take effect until: %s", later_s);
crm_time_free(duration);
crm_time_free(later);
crm_time_free(now);
return later_s;
}
// \return Standard Pacemaker return code
int
cli_resource_ban(pcmk__output_t *out, const char *rsc_id, const char *host,
const char *move_lifetime, cib_t * cib_conn, int cib_options,
gboolean promoted_role_only, const char *promoted_role)
{
char *later_s = NULL;
int rc = pcmk_rc_ok;
xmlNode *fragment = NULL;
xmlNode *location = NULL;
later_s = parse_cli_lifetime(out, move_lifetime);
if(move_lifetime && later_s == NULL) {
return EINVAL;
}
fragment = create_xml_node(NULL, PCMK_XE_CONSTRAINTS);
location = create_xml_node(fragment, PCMK_XE_RSC_LOCATION);
crm_xml_set_id(location, "cli-ban-%s-on-%s", rsc_id, host);
out->info(out,
"WARNING: Creating " PCMK_XE_RSC_LOCATION " constraint '%s' with "
"a score of " CRM_MINUS_INFINITY_S " for resource %s on %s.\n"
"\tThis will prevent %s from %s on %s until the constraint is "
"removed using the clear option or by editing the CIB with an "
"appropriate tool.\n"
"\tThis will be the case even if %s is the last node in the "
"cluster",
pcmk__xe_id(location), rsc_id, host, rsc_id,
(promoted_role_only? "being promoted" : "running"), host, host);
crm_xml_add(location, PCMK_XA_RSC, rsc_id);
if(promoted_role_only) {
crm_xml_add(location, PCMK_XA_ROLE, promoted_role);
} else {
crm_xml_add(location, PCMK_XA_ROLE, PCMK__ROLE_STARTED);
}
if (later_s == NULL) {
/* Short form */
crm_xml_add(location, PCMK_XE_NODE, host);
crm_xml_add(location, PCMK_XA_SCORE, CRM_MINUS_INFINITY_S);
} else {
xmlNode *rule = create_xml_node(location, PCMK_XE_RULE);
xmlNode *expr = create_xml_node(rule, PCMK_XE_EXPRESSION);
crm_xml_set_id(rule, "cli-ban-%s-on-%s-rule", rsc_id, host);
crm_xml_add(rule, PCMK_XA_SCORE, CRM_MINUS_INFINITY_S);
crm_xml_add(rule, PCMK_XA_BOOLEAN_OP, PCMK_VALUE_AND);
crm_xml_set_id(expr, "cli-ban-%s-on-%s-expr", rsc_id, host);
crm_xml_add(expr, PCMK_XA_ATTRIBUTE, CRM_ATTR_UNAME);
crm_xml_add(expr, PCMK_XA_OPERATION, PCMK_VALUE_EQ);
crm_xml_add(expr, PCMK_XA_VALUE, host);
crm_xml_add(expr, PCMK_XA_TYPE, PCMK_VALUE_STRING);
expr = create_xml_node(rule, PCMK_XE_DATE_EXPRESSION);
crm_xml_set_id(expr, "cli-ban-%s-on-%s-lifetime", rsc_id, host);
crm_xml_add(expr, PCMK_XA_OPERATION, PCMK_VALUE_LT);
crm_xml_add(expr, PCMK_XA_END, later_s);
}
crm_log_xml_notice(fragment, "Modify");
rc = cib_conn->cmds->modify(cib_conn, PCMK_XE_CONSTRAINTS, fragment,
cib_options);
rc = pcmk_legacy2rc(rc);
free_xml(fragment);
free(later_s);
if (rc != pcmk_rc_ok && promoted_role_only && strcmp(promoted_role, PCMK__ROLE_PROMOTED) == 0) {
int banrc = cli_resource_ban(out, rsc_id, host, move_lifetime,
cib_conn, cib_options, promoted_role_only,
PCMK__ROLE_PROMOTED_LEGACY);
if (banrc == pcmk_rc_ok) {
rc = banrc;
}
}
return rc;
}
// \return Standard Pacemaker return code
int
cli_resource_prefer(pcmk__output_t *out,const char *rsc_id, const char *host,
const char *move_lifetime, cib_t *cib_conn, int cib_options,
gboolean promoted_role_only, const char *promoted_role)
{
char *later_s = parse_cli_lifetime(out, move_lifetime);
int rc = pcmk_rc_ok;
xmlNode *location = NULL;
xmlNode *fragment = NULL;
if(move_lifetime && later_s == NULL) {
return EINVAL;
}
if(cib_conn == NULL) {
free(later_s);
return ENOTCONN;
}
fragment = create_xml_node(NULL, PCMK_XE_CONSTRAINTS);
location = create_xml_node(fragment, PCMK_XE_RSC_LOCATION);
crm_xml_set_id(location, "cli-prefer-%s", rsc_id);
crm_xml_add(location, PCMK_XA_RSC, rsc_id);
if(promoted_role_only) {
crm_xml_add(location, PCMK_XA_ROLE, promoted_role);
} else {
crm_xml_add(location, PCMK_XA_ROLE, PCMK__ROLE_STARTED);
}
if (later_s == NULL) {
/* Short form */
crm_xml_add(location, PCMK_XE_NODE, host);
- crm_xml_add(location, PCMK_XA_SCORE, CRM_INFINITY_S);
+ crm_xml_add(location, PCMK_XA_SCORE, PCMK_VALUE_INFINITY);
} else {
xmlNode *rule = create_xml_node(location, PCMK_XE_RULE);
xmlNode *expr = create_xml_node(rule, PCMK_XE_EXPRESSION);
crm_xml_set_id(rule, "cli-prefer-rule-%s", rsc_id);
- crm_xml_add(rule, PCMK_XA_SCORE, CRM_INFINITY_S);
+ crm_xml_add(rule, PCMK_XA_SCORE, PCMK_VALUE_INFINITY);
crm_xml_add(rule, PCMK_XA_BOOLEAN_OP, PCMK_VALUE_AND);
crm_xml_set_id(expr, "cli-prefer-expr-%s", rsc_id);
crm_xml_add(expr, PCMK_XA_ATTRIBUTE, CRM_ATTR_UNAME);
crm_xml_add(expr, PCMK_XA_OPERATION, PCMK_VALUE_EQ);
crm_xml_add(expr, PCMK_XA_VALUE, host);
crm_xml_add(expr, PCMK_XA_TYPE, PCMK_VALUE_STRING);
expr = create_xml_node(rule, PCMK_XE_DATE_EXPRESSION);
crm_xml_set_id(expr, "cli-prefer-lifetime-end-%s", rsc_id);
crm_xml_add(expr, PCMK_XA_OPERATION, PCMK_VALUE_LT);
crm_xml_add(expr, PCMK_XA_END, later_s);
}
crm_log_xml_info(fragment, "Modify");
rc = cib_conn->cmds->modify(cib_conn, PCMK_XE_CONSTRAINTS, fragment,
cib_options);
rc = pcmk_legacy2rc(rc);
free_xml(fragment);
free(later_s);
if (rc != pcmk_rc_ok && promoted_role_only && strcmp(promoted_role, PCMK__ROLE_PROMOTED) == 0) {
int preferrc = cli_resource_prefer(out, rsc_id, host, move_lifetime,
cib_conn, cib_options, promoted_role_only,
PCMK__ROLE_PROMOTED_LEGACY);
if (preferrc == pcmk_rc_ok) {
rc = preferrc;
}
}
return rc;
}
/* Nodes can be specified two different ways in the CIB, so we have two different
* functions to try clearing out any constraints on them:
*
* (1) The node could be given by attribute=/value= in an expression XML node.
* That's what resource_clear_node_in_expr handles. That XML looks like this:
*
* <rsc_location id="cli-prefer-dummy" rsc="dummy" role="Started">
* <rule id="cli-prefer-rule-dummy" score="INFINITY" boolean-op="and">
* <expression id="cli-prefer-expr-dummy" attribute="#uname" operation="eq" value="test02" type="string"/>
* <date_expression id="cli-prefer-lifetime-end-dummy" operation="lt" end="2018-12-12 14:05:37 -05:00"/>
* </rule>
* </rsc_location>
*
* (2) The node could be given by node= in a PCMK_XE_RSC_LOCATION XML node.
* That's what resource_clear_node_in_location handles. That XML looks like
* this:
*
* <rsc_location id="cli-prefer-dummy" rsc="dummy" role="Started" node="node1" score="INFINITY"/>
*
* \return Standard Pacemaker return code
*/
static int
resource_clear_node_in_expr(const char *rsc_id, const char *host, cib_t * cib_conn,
int cib_options)
{
int rc = pcmk_rc_ok;
char *xpath_string = NULL;
#define XPATH_FMT \
"//" PCMK_XE_RSC_LOCATION "[@" PCMK_XA_ID "='cli-prefer-%s']" \
"[" PCMK_XE_RULE \
"[@" PCMK_XA_ID "='cli-prefer-rule-%s']" \
"/" PCMK_XE_EXPRESSION \
"[@" PCMK_XA_ATTRIBUTE "='" CRM_ATTR_UNAME "' " \
"and @" PCMK_XA_VALUE "='%s']" \
"]"
xpath_string = crm_strdup_printf(XPATH_FMT, rsc_id, rsc_id, host);
rc = cib_conn->cmds->remove(cib_conn, xpath_string, NULL, cib_xpath | cib_options);
if (rc == -ENXIO) {
rc = pcmk_rc_ok;
} else {
rc = pcmk_legacy2rc(rc);
}
free(xpath_string);
return rc;
}
// \return Standard Pacemaker return code
static int
resource_clear_node_in_location(const char *rsc_id, const char *host, cib_t * cib_conn,
int cib_options, bool clear_ban_constraints, gboolean force)
{
int rc = pcmk_rc_ok;
xmlNode *fragment = NULL;
xmlNode *location = NULL;
fragment = create_xml_node(NULL, PCMK_XE_CONSTRAINTS);
if (clear_ban_constraints == TRUE) {
location = create_xml_node(fragment, PCMK_XE_RSC_LOCATION);
crm_xml_set_id(location, "cli-ban-%s-on-%s", rsc_id, host);
}
location = create_xml_node(fragment, PCMK_XE_RSC_LOCATION);
crm_xml_set_id(location, "cli-prefer-%s", rsc_id);
if (force == FALSE) {
crm_xml_add(location, PCMK_XE_NODE, host);
}
crm_log_xml_info(fragment, "Delete");
rc = cib_conn->cmds->remove(cib_conn, PCMK_XE_CONSTRAINTS, fragment,
cib_options);
if (rc == -ENXIO) {
rc = pcmk_rc_ok;
} else {
rc = pcmk_legacy2rc(rc);
}
free_xml(fragment);
return rc;
}
// \return Standard Pacemaker return code
int
cli_resource_clear(const char *rsc_id, const char *host, GList *allnodes, cib_t * cib_conn,
int cib_options, bool clear_ban_constraints, gboolean force)
{
int rc = pcmk_rc_ok;
if(cib_conn == NULL) {
return ENOTCONN;
}
if (host) {
rc = resource_clear_node_in_expr(rsc_id, host, cib_conn, cib_options);
/* rc does not tell us whether the previous operation did anything, only
* whether it failed or not. Thus, as long as it did not fail, we need
* to try the second clear method.
*/
if (rc == pcmk_rc_ok) {
rc = resource_clear_node_in_location(rsc_id, host, cib_conn,
cib_options, clear_ban_constraints,
force);
}
} else {
GList *n = allnodes;
/* Iterate over all nodes, attempting to clear the constraint from each.
* On the first error, abort.
*/
for(; n; n = n->next) {
pcmk_node_t *target = n->data;
rc = cli_resource_clear(rsc_id, target->details->uname, NULL,
cib_conn, cib_options, clear_ban_constraints,
force);
if (rc != pcmk_rc_ok) {
break;
}
}
}
return rc;
}
static void
build_clear_xpath_string(GString *buf, const xmlNode *constraint_node,
const char *rsc, const char *node,
bool promoted_role_only)
{
const char *cons_id = pcmk__xe_id(constraint_node);
const char *cons_rsc = crm_element_value(constraint_node, PCMK_XA_RSC);
GString *rsc_role_substr = NULL;
const char *promoted_role_rule = "@" PCMK_XA_ROLE "='" PCMK__ROLE_PROMOTED
"' or @" PCMK_XA_ROLE "='"
PCMK__ROLE_PROMOTED_LEGACY "'";
CRM_ASSERT(buf != NULL);
g_string_truncate(buf, 0);
if (!pcmk__starts_with(cons_id, "cli-ban-")
&& !pcmk__starts_with(cons_id, "cli-prefer-")) {
return;
}
g_string_append(buf, "//" PCMK_XE_RSC_LOCATION);
if ((node != NULL) || (rsc != NULL) || promoted_role_only) {
g_string_append_c(buf, '[');
if (node != NULL) {
pcmk__g_strcat(buf, "@" PCMK_XE_NODE "='", node, "'", NULL);
if (promoted_role_only || (rsc != NULL)) {
g_string_append(buf, " and ");
}
}
if ((rsc != NULL) && promoted_role_only) {
rsc_role_substr = g_string_sized_new(64);
pcmk__g_strcat(rsc_role_substr,
"@" PCMK_XA_RSC "='", rsc, "' "
"and (" , promoted_role_rule, ")", NULL);
} else if (rsc != NULL) {
rsc_role_substr = g_string_sized_new(64);
pcmk__g_strcat(rsc_role_substr,
"@" PCMK_XA_RSC "='", rsc, "'", NULL);
} else if (promoted_role_only) {
rsc_role_substr = g_string_sized_new(64);
g_string_append(rsc_role_substr, promoted_role_rule);
}
if (rsc_role_substr != NULL) {
g_string_append(buf, rsc_role_substr->str);
}
g_string_append_c(buf, ']');
}
if (node != NULL) {
g_string_append(buf, "|//" PCMK_XE_RSC_LOCATION);
if (rsc_role_substr != NULL) {
pcmk__g_strcat(buf, "[", rsc_role_substr, "]", NULL);
}
pcmk__g_strcat(buf,
"/" PCMK_XE_RULE "[" PCMK_XE_EXPRESSION
"[@" PCMK_XA_ATTRIBUTE "='" CRM_ATTR_UNAME "' "
"and @" PCMK_XA_VALUE "='", node, "']]", NULL);
}
g_string_append(buf, "//" PCMK_XE_DATE_EXPRESSION "[@" PCMK_XA_ID "='");
if (pcmk__starts_with(cons_id, "cli-ban-")) {
pcmk__g_strcat(buf, cons_id, "-lifetime']", NULL);
} else { // starts with "cli-prefer-"
pcmk__g_strcat(buf,
"cli-prefer-lifetime-end-", cons_rsc, "']", NULL);
}
if (rsc_role_substr != NULL) {
g_string_free(rsc_role_substr, TRUE);
}
}
// \return Standard Pacemaker return code
int
cli_resource_clear_all_expired(xmlNode *root, cib_t *cib_conn, int cib_options,
const char *rsc, const char *node, gboolean promoted_role_only)
{
GString *buf = NULL;
xmlXPathObject *xpathObj = NULL;
xmlNode *cib_constraints = NULL;
crm_time_t *now = crm_time_new(NULL);
int i;
int rc = pcmk_rc_ok;
cib_constraints = pcmk_find_cib_element(root, PCMK_XE_CONSTRAINTS);
xpathObj = xpath_search(cib_constraints, "//" PCMK_XE_RSC_LOCATION);
for (i = 0; i < numXpathResults(xpathObj); i++) {
xmlNode *constraint_node = getXpathResult(xpathObj, i);
xmlNode *date_expr_node = NULL;
crm_time_t *end = NULL;
if (buf == NULL) {
buf = g_string_sized_new(1024);
}
build_clear_xpath_string(buf, constraint_node, rsc, node,
promoted_role_only);
if (buf->len == 0) {
continue;
}
date_expr_node = get_xpath_object((const char *) buf->str,
constraint_node, LOG_DEBUG);
if (date_expr_node == NULL) {
continue;
}
/* And then finally, see if the date expression is expired. If so,
* clear the constraint.
*/
end = crm_time_new(crm_element_value(date_expr_node, PCMK_XA_END));
if (crm_time_compare(now, end) == 1) {
xmlNode *fragment = NULL;
xmlNode *location = NULL;
fragment = create_xml_node(NULL, PCMK_XE_CONSTRAINTS);
location = create_xml_node(fragment, PCMK_XE_RSC_LOCATION);
crm_xml_set_id(location, "%s", pcmk__xe_id(constraint_node));
crm_log_xml_info(fragment, "Delete");
rc = cib_conn->cmds->remove(cib_conn, PCMK_XE_CONSTRAINTS, fragment,
cib_options);
rc = pcmk_legacy2rc(rc);
if (rc != pcmk_rc_ok) {
goto done;
}
free_xml(fragment);
}
crm_time_free(end);
}
done:
if (buf != NULL) {
g_string_free(buf, TRUE);
}
freeXpathObject(xpathObj);
crm_time_free(now);
return rc;
}

File Metadata

Mime Type
text/x-diff
Expires
Wed, Jun 4, 6:34 AM (6 h, 48 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1826144
Default Alt Text
(77 KB)

Event Timeline