Page Menu
Home
ClusterLabs Projects
Search
Configure Global Search
Log In
Files
F4149439
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
77 KB
Referenced Files
None
Subscribers
None
View Options
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
Details
Attached
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)
Attached To
Mode
rP Pacemaker
Attached
Detach File
Event Timeline
Log In to Comment