diff options
Diffstat (limited to 'daemons/attrd')
-rw-r--r-- | daemons/attrd/attrd_alerts.c | 14 | ||||
-rw-r--r-- | daemons/attrd/attrd_attributes.c | 125 | ||||
-rw-r--r-- | daemons/attrd/attrd_cib.c | 316 | ||||
-rw-r--r-- | daemons/attrd/attrd_corosync.c | 295 | ||||
-rw-r--r-- | daemons/attrd/attrd_elections.c | 10 | ||||
-rw-r--r-- | daemons/attrd/attrd_ipc.c | 99 | ||||
-rw-r--r-- | daemons/attrd/attrd_messages.c | 57 | ||||
-rw-r--r-- | daemons/attrd/attrd_sync.c | 48 | ||||
-rw-r--r-- | daemons/attrd/attrd_utils.c | 43 | ||||
-rw-r--r-- | daemons/attrd/pacemaker-attrd.c | 10 | ||||
-rw-r--r-- | daemons/attrd/pacemaker-attrd.h | 104 |
11 files changed, 579 insertions, 542 deletions
diff --git a/daemons/attrd/attrd_alerts.c b/daemons/attrd/attrd_alerts.c index 495e18f..4e97743 100644 --- a/daemons/attrd/attrd_alerts.c +++ b/daemons/attrd/attrd_alerts.c @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the Pacemaker project contributors + * Copyright 2015-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -10,11 +10,11 @@ #include <crm_internal.h> #include <crm/crm.h> #include <crm/cib/internal.h> -#include <crm/msg_xml.h> #include <crm/cluster/internal.h> #include <crm/cluster/election_internal.h> #include <crm/common/alerts_internal.h> #include <crm/common/cib_internal.h> +#include <crm/common/xml.h> #include <crm/pengine/rules_internal.h> #include <crm/lrmd_internal.h> #include "pacemaker-attrd.h" @@ -48,7 +48,7 @@ attrd_lrmd_connect(void) int ret = -ENOTCONN; for (int fails = 0; fails < max_attempts; ++fails) { - ret = the_lrmd->cmds->connect(the_lrmd, T_ATTRD, NULL); + ret = the_lrmd->cmds->connect(the_lrmd, PCMK__VALUE_ATTRD, NULL); if (ret == pcmk_ok) { break; } @@ -93,11 +93,11 @@ config_query_callback(xmlNode * msg, int call_id, int rc, xmlNode * output, void } crmalerts = output; - if ((crmalerts != NULL) && !pcmk__xe_is(crmalerts, XML_CIB_TAG_ALERTS)) { - crmalerts = first_named_child(crmalerts, XML_CIB_TAG_ALERTS); + if ((crmalerts != NULL) && !pcmk__xe_is(crmalerts, PCMK_XE_ALERTS)) { + crmalerts = pcmk__xe_first_child(crmalerts, PCMK_XE_ALERTS, NULL, NULL); } if (!crmalerts) { - crm_notice("CIB query result has no " XML_CIB_TAG_ALERTS " section"); + crm_notice("CIB query result has no " PCMK_XE_ALERTS " section"); return; } @@ -113,7 +113,7 @@ attrd_read_options(gpointer user_data) CRM_CHECK(the_cib != NULL, return TRUE); call_id = the_cib->cmds->query(the_cib, - pcmk__cib_abs_xpath_for(XML_CIB_TAG_ALERTS), + pcmk__cib_abs_xpath_for(PCMK_XE_ALERTS), NULL, cib_xpath|cib_scope_local); the_cib->cmds->register_callback_full(the_cib, call_id, 120, FALSE, NULL, diff --git a/daemons/attrd/attrd_attributes.c b/daemons/attrd/attrd_attributes.c index 388c181..974de89 100644 --- a/daemons/attrd/attrd_attributes.c +++ b/daemons/attrd/attrd_attributes.c @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the Pacemaker project contributors + * Copyright 2013-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -14,7 +14,6 @@ #include <stdlib.h> #include <glib.h> -#include <crm/msg_xml.h> #include <crm/common/logging.h> #include <crm/common/results.h> #include <crm/common/strings_internal.h> @@ -26,55 +25,56 @@ static attribute_t * attrd_create_attribute(xmlNode *xml) { int is_private = 0; - int dampen = 0; + long long dampen = 0; const char *name = crm_element_value(xml, PCMK__XA_ATTR_NAME); const char *set_type = crm_element_value(xml, PCMK__XA_ATTR_SET_TYPE); const char *dampen_s = crm_element_value(xml, PCMK__XA_ATTR_DAMPENING); attribute_t *a = NULL; if (set_type == NULL) { - set_type = XML_TAG_ATTR_SETS; + set_type = PCMK_XE_INSTANCE_ATTRIBUTES; } /* Set type is meaningful only when writing to the CIB. Private * attributes are not written. */ crm_element_value_int(xml, PCMK__XA_ATTR_IS_PRIVATE, &is_private); - if ((is_private != 0) - && !pcmk__str_any_of(set_type, XML_TAG_ATTR_SETS, XML_TAG_UTILIZATION, - NULL)) { + if (!is_private && !pcmk__str_any_of(set_type, + PCMK_XE_INSTANCE_ATTRIBUTES, + PCMK_XE_UTILIZATION, NULL)) { crm_warn("Ignoring attribute %s with invalid set type %s", pcmk__s(name, "(unidentified)"), set_type); return NULL; } - a = calloc(1, sizeof(attribute_t)); - CRM_ASSERT(a != NULL); - - a->is_private = is_private; - pcmk__str_update(&a->id, name); - pcmk__str_update(&a->set_type, set_type); + a = pcmk__assert_alloc(1, sizeof(attribute_t)); + a->id = pcmk__str_copy(name); + a->set_type = pcmk__str_copy(set_type); a->set_id = crm_element_value_copy(xml, PCMK__XA_ATTR_SET); - a->uuid = crm_element_value_copy(xml, PCMK__XA_ATTR_UUID); + a->user = crm_element_value_copy(xml, PCMK__XA_ATTR_USER); a->values = pcmk__strikey_table(NULL, attrd_free_attribute_value); - a->user = crm_element_value_copy(xml, PCMK__XA_ATTR_USER); - crm_trace("Performing all %s operations as user '%s'", a->id, a->user); + if (is_private) { + attrd_set_attr_flags(a, attrd_attr_is_private); + } if (dampen_s != NULL) { dampen = crm_get_msec(dampen_s); } - crm_trace("Created attribute %s with %s write delay", a->id, - (a->timeout_ms == 0)? "no" : pcmk__readable_interval(a->timeout_ms)); - if(dampen > 0) { - a->timeout_ms = dampen; + if (dampen > 0) { + a->timeout_ms = (int) QB_MIN(dampen, INT_MAX); a->timer = attrd_add_timer(a->id, a->timeout_ms, a); } else if (dampen < 0) { crm_warn("Ignoring invalid delay %s for attribute %s", dampen_s, a->id); } + crm_trace("Created attribute %s with %s write delay and %s CIB user", + a->id, + ((dampen > 0)? pcmk__readable_interval(a->timeout_ms) : "no"), + pcmk__s(a->user, "default")); + g_hash_table_replace(attributes, a->id, a); return a; } @@ -83,7 +83,7 @@ static int attrd_update_dampening(attribute_t *a, xmlNode *xml, const char *attr) { const char *dvalue = crm_element_value(xml, PCMK__XA_ATTR_DAMPENING); - int dampen = 0; + long long dampen = 0; if (dvalue == NULL) { crm_warn("Could not update %s: peer did not specify value for delay", @@ -100,7 +100,7 @@ attrd_update_dampening(attribute_t *a, xmlNode *xml, const char *attr) if (a->timeout_ms != dampen) { mainloop_timer_del(a->timer); - a->timeout_ms = dampen; + a->timeout_ms = (int) QB_MIN(dampen, INT_MAX); if (dampen > 0) { a->timer = attrd_add_timer(attr, a->timeout_ms, a); crm_info("Update attribute %s delay to %dms (%s)", @@ -136,20 +136,21 @@ xmlNode * attrd_add_value_xml(xmlNode *parent, const attribute_t *a, const attribute_value_t *v, bool force_write) { - xmlNode *xml = create_xml_node(parent, __func__); + xmlNode *xml = pcmk__xe_create(parent, __func__); crm_xml_add(xml, PCMK__XA_ATTR_NAME, a->id); + crm_xml_add(xml, PCMK__XA_ATTR_SET_TYPE, a->set_type); crm_xml_add(xml, PCMK__XA_ATTR_SET, a->set_id); - crm_xml_add(xml, PCMK__XA_ATTR_UUID, a->uuid); crm_xml_add(xml, PCMK__XA_ATTR_USER, a->user); pcmk__xe_add_node(xml, v->nodename, v->nodeid); - if (v->is_remote != 0) { + if (pcmk_is_set(v->flags, attrd_value_remote)) { crm_xml_add_int(xml, PCMK__XA_ATTR_IS_REMOTE, 1); } crm_xml_add(xml, PCMK__XA_ATTR_VALUE, v->current); crm_xml_add_int(xml, PCMK__XA_ATTR_DAMPENING, a->timeout_ms / 1000); - crm_xml_add_int(xml, PCMK__XA_ATTR_IS_PRIVATE, a->is_private); - crm_xml_add_int(xml, PCMK__XA_ATTR_FORCE, force_write); + crm_xml_add_int(xml, PCMK__XA_ATTR_IS_PRIVATE, + pcmk_is_set(a->flags, attrd_attr_is_private)); + crm_xml_add_int(xml, PCMK__XA_ATTRD_IS_FORCE_WRITE, force_write); return xml; } @@ -166,8 +167,7 @@ attrd_clear_value_seen(void) while (g_hash_table_iter_next(&aIter, NULL, (gpointer *) & a)) { g_hash_table_iter_init(&vIter, a->values); while (g_hash_table_iter_next(&vIter, NULL, (gpointer *) & v)) { - v->seen = FALSE; - crm_trace("Clear seen flag %s[%s] = %s.", a->id, v->nodename, v->current); + attrd_clear_value_flags(v, attrd_value_from_peer); } } } @@ -178,9 +178,9 @@ attrd_populate_attribute(xmlNode *xml, const char *attr) attribute_t *a = NULL; bool update_both = false; - const char *op = crm_element_value(xml, PCMK__XA_TASK); + const char *op = crm_element_value(xml, PCMK_XA_TASK); - // NULL because PCMK__ATTRD_CMD_SYNC_RESPONSE has no PCMK__XA_TASK + // NULL because PCMK__ATTRD_CMD_SYNC_RESPONSE has no PCMK_XA_TASK update_both = pcmk__str_eq(op, PCMK__ATTRD_CMD_UPDATE_BOTH, pcmk__str_null_matches); @@ -210,3 +210,66 @@ attrd_populate_attribute(xmlNode *xml, const char *attr) return a; } + +/*! + * \internal + * \brief Get the XML ID used to write out an attribute set + * + * \param[in] attr Attribute to get set ID for + * \param[in] node_state_id XML ID of node state that attribute value is for + * + * \return Newly allocated string with XML ID to use for \p attr set + */ +char * +attrd_set_id(const attribute_t *attr, const char *node_state_id) +{ + char *set_id = NULL; + + CRM_ASSERT((attr != NULL) && (node_state_id != NULL)); + + if (attr->set_id == NULL) { + /* @COMPAT This should really take the set type into account. Currently + * we use the same XML ID for transient attributes and utilization + * attributes. It doesn't cause problems because the status section is + * not limited by the schema in any way, but it's still unfortunate. + * For backward compatibility reasons, we can't change this. + */ + set_id = crm_strdup_printf("%s-%s", PCMK_XE_STATUS, node_state_id); + } else { + /* @COMPAT When the user specifies a set ID for an attribute, it is the + * same for every node. That is less than ideal, but again, the schema + * doesn't enforce anything for the status section. We couldn't change + * it without allowing the set ID to vary per value rather than per + * attribute, which would break backward compatibility, pose design + * challenges, and potentially cause problems in rolling upgrades. + */ + set_id = pcmk__str_copy(attr->set_id); + } + crm_xml_sanitize_id(set_id); + return set_id; +} + +/*! + * \internal + * \brief Get the XML ID used to write out an attribute value + * + * \param[in] attr Attribute to get value XML ID for + * \param[in] node_state_id UUID of node that attribute value is for + * + * \return Newly allocated string with XML ID of \p attr value + */ +char * +attrd_nvpair_id(const attribute_t *attr, const char *node_state_id) +{ + char *nvpair_id = NULL; + + if (attr->set_id != NULL) { + nvpair_id = crm_strdup_printf("%s-%s", attr->set_id, attr->id); + + } else { + nvpair_id = crm_strdup_printf(PCMK_XE_STATUS "-%s-%s", + node_state_id, attr->id); + } + crm_xml_sanitize_id(nvpair_id); + return nvpair_id; +} diff --git a/daemons/attrd/attrd_cib.c b/daemons/attrd/attrd_cib.c index 80e5580..2537ade 100644 --- a/daemons/attrd/attrd_cib.c +++ b/daemons/attrd/attrd_cib.c @@ -1,5 +1,5 @@ /* - * Copyright 2013-2023 the Pacemaker project contributors + * Copyright 2013-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -15,11 +15,12 @@ #include <stdlib.h> #include <glib.h> -#include <crm/msg_xml.h> +#include <crm/cib/internal.h> // cib__* #include <crm/common/logging.h> #include <crm/common/results.h> #include <crm/common/strings_internal.h> #include <crm/common/xml.h> +#include <crm/cluster/internal.h> // pcmk__get_node() #include "pacemaker-attrd.h" @@ -50,8 +51,10 @@ attrd_cib_updated_cb(const char *event, xmlNode *msg) { const xmlNode *patchset = NULL; const char *client_name = NULL; + bool status_changed = false; if (attrd_shutting_down(true)) { + crm_debug("Ignoring CIB change during shutdown"); return; } @@ -59,29 +62,32 @@ attrd_cib_updated_cb(const char *event, xmlNode *msg) return; } - if (cib__element_in_patchset(patchset, XML_CIB_TAG_ALERTS)) { + if (cib__element_in_patchset(patchset, PCMK_XE_ALERTS)) { mainloop_set_trigger(attrd_config_read); } - if (!attrd_election_won()) { - // Don't write attributes if we're not the writer - return; - } + status_changed = cib__element_in_patchset(patchset, PCMK_XE_STATUS); - client_name = crm_element_value(msg, F_CIB_CLIENTNAME); + client_name = crm_element_value(msg, PCMK__XA_CIB_CLIENTNAME); if (!cib__client_triggers_refresh(client_name)) { - // The CIB is still accurate + /* This change came from a source that ensured the CIB is consistent + * with our attributes table, so we don't need to write anything out. + */ return; } - if (cib__element_in_patchset(patchset, XML_CIB_TAG_NODES) - || cib__element_in_patchset(patchset, XML_CIB_TAG_STATUS)) { + if (!attrd_election_won()) { + // Don't write attributes if we're not the writer + return; + } - /* An unsafe client modified the nodes or status section. Write - * transient attributes to ensure they're up-to-date in the CIB. + if (status_changed || cib__element_in_patchset(patchset, PCMK_XE_NODES)) { + /* An unsafe client modified the PCMK_XE_NODES or PCMK_XE_STATUS + * section. Write transient attributes to ensure they're up-to-date in + * the CIB. */ if (client_name == NULL) { - client_name = crm_element_value(msg, F_CIB_CLIENTID); + client_name = crm_element_value(msg, PCMK__XA_CIB_CLIENTID); } crm_notice("Updating all attributes after %s event triggered by %s", event, pcmk__s(client_name, "(unidentified client)")); @@ -108,7 +114,7 @@ attrd_cib_connect(int max_retry) } attempts++; crm_debug("Connection attempt %d to the CIB manager", attempts); - rc = the_cib->cmds->signon(the_cib, T_ATTRD, cib_command); + rc = the_cib->cmds->signon(the_cib, PCMK__VALUE_ATTRD, cib_command); } while ((rc != pcmk_ok) && (attempts < max_retry)); @@ -126,7 +132,8 @@ attrd_cib_connect(int max_retry) goto cleanup; } - rc = the_cib->cmds->add_notify_callback(the_cib, T_CIB_DIFF_NOTIFY, + rc = the_cib->cmds->add_notify_callback(the_cib, + PCMK__VALUE_CIB_DIFF_NOTIFY, attrd_cib_updated_cb); if (rc != pcmk_ok) { crm_err("Could not set CIB notification callback"); @@ -144,7 +151,7 @@ void attrd_cib_disconnect(void) { CRM_CHECK(the_cib != NULL, return); - the_cib->cmds->del_notify_callback(the_cib, T_CIB_DIFF_NOTIFY, + the_cib->cmds->del_notify_callback(the_cib, PCMK__VALUE_CIB_DIFF_NOTIFY, attrd_cib_updated_cb); cib__clean_up_connection(&the_cib); } @@ -153,39 +160,44 @@ static void attrd_erase_cb(xmlNode *msg, int call_id, int rc, xmlNode *output, void *user_data) { - do_crm_log_unlikely(((rc != pcmk_ok)? LOG_NOTICE : LOG_DEBUG), - "Cleared transient attributes: %s " - CRM_XS " xpath=%s rc=%d", - pcmk_strerror(rc), (char *) user_data, rc); + const char *node = pcmk__s((const char *) user_data, "a node"); + + if (rc == pcmk_ok) { + crm_info("Cleared transient node attributes for %s from CIB", node); + } else { + crm_err("Unable to clear transient node attributes for %s from CIB: %s", + node, pcmk_strerror(rc)); + } } -#define XPATH_TRANSIENT "//node_state[@uname='%s']/" XML_TAG_TRANSIENT_NODEATTRS +#define XPATH_TRANSIENT "//" PCMK__XE_NODE_STATE \ + "[@" PCMK_XA_UNAME "='%s']" \ + "/" PCMK__XE_TRANSIENT_ATTRIBUTES /*! * \internal - * \brief Wipe all transient attributes for this node from the CIB - * - * Clear any previous transient node attributes from the CIB. This is - * normally done by the DC's controller when this node leaves the cluster, but - * this handles the case where the node restarted so quickly that the - * cluster layer didn't notice. + * \brief Wipe all transient node attributes for a node from the CIB * - * \todo If pacemaker-attrd respawns after crashing (see PCMK_ENV_RESPAWNED), - * ideally we'd skip this and sync our attributes from the writer. - * However, currently we reject any values for us that the writer has, in - * attrd_peer_update(). + * \param[in] node Node to clear attributes for */ -static void -attrd_erase_attrs(void) +void +attrd_cib_erase_transient_attrs(const char *node) { int call_id = 0; - char *xpath = crm_strdup_printf(XPATH_TRANSIENT, attrd_cluster->uname); + char *xpath = NULL; + + CRM_CHECK(node != NULL, return); + + xpath = crm_strdup_printf(XPATH_TRANSIENT, node); - crm_info("Clearing transient attributes from CIB " CRM_XS " xpath=%s", - xpath); + crm_debug("Clearing transient node attributes for %s from CIB using %s", + node, xpath); call_id = the_cib->cmds->remove(the_cib, xpath, NULL, cib_xpath); - the_cib->cmds->register_callback_full(the_cib, call_id, 120, FALSE, xpath, + free(xpath); + + the_cib->cmds->register_callback_full(the_cib, call_id, 120, FALSE, + pcmk__str_copy(node), "attrd_erase_cb", attrd_erase_cb, free); } @@ -197,8 +209,17 @@ attrd_erase_attrs(void) void attrd_cib_init(void) { - // We have no attribute values in memory, wipe the CIB to match - attrd_erase_attrs(); + /* We have no attribute values in memory, so wipe the CIB to match. This is + * normally done by the DC's controller when this node leaves the cluster, but + * this handles the case where the node restarted so quickly that the + * cluster layer didn't notice. + * + * \todo If pacemaker-attrd respawns after crashing (see PCMK_ENV_RESPAWNED), + * ideally we'd skip this and sync our attributes from the writer. + * However, currently we reject any values for us that the writer has, in + * attrd_peer_update(). + */ + attrd_cib_erase_transient_attrs(attrd_cluster->uname); // Set a trigger for reading the CIB (for the alerts section) attrd_config_read = mainloop_add_trigger(G_PRIORITY_HIGH, attrd_read_options, NULL); @@ -262,19 +283,24 @@ attrd_cib_callback(xmlNode *msg, int call_id, int rc, xmlNode *output, void *use g_hash_table_iter_init(&iter, a->values); while (g_hash_table_iter_next(&iter, (gpointer *) & peer, (gpointer *) & v)) { - do_crm_log(level, "* %s[%s]=%s", a->id, peer, v->requested); - free(v->requested); - v->requested = NULL; - if (rc != pcmk_ok) { - a->changed = true; /* Attempt write out again */ + if (rc == pcmk_ok) { + crm_info("* Wrote %s[%s]=%s", + a->id, peer, pcmk__s(v->requested, "(unset)")); + pcmk__str_update(&(v->requested), NULL); + } else { + do_crm_log(level, "* Could not write %s[%s]=%s", + a->id, peer, pcmk__s(v->requested, "(unset)")); + /* Reattempt write below if we are still the writer */ + attrd_set_attr_flags(a, attrd_attr_changed); } } - if (a->changed && attrd_election_won()) { + if (pcmk_is_set(a->flags, attrd_attr_changed) && attrd_election_won()) { if (rc == pcmk_ok) { /* We deferred a write of a new update because this update was in * progress. Write out the new value without additional delay. */ + crm_debug("Pending update for %s can be written now", a->id); write_attribute(a, false); /* We're re-attempting a write because the original failed; delay @@ -320,40 +346,27 @@ static int add_set_attr_update(const attribute_t *attr, const char *attr_id, const char *node_id, const char *set_id, const char *value) { - xmlNode *update = create_xml_node(NULL, XML_CIB_TAG_STATE); + xmlNode *update = pcmk__xe_create(NULL, PCMK__XE_NODE_STATE); xmlNode *child = update; int rc = ENOMEM; - if (child == NULL) { - goto done; - } - crm_xml_add(child, XML_ATTR_ID, node_id); + crm_xml_add(child, PCMK_XA_ID, node_id); - child = create_xml_node(child, XML_TAG_TRANSIENT_NODEATTRS); - if (child == NULL) { - goto done; - } - crm_xml_add(child, XML_ATTR_ID, node_id); + child = pcmk__xe_create(child, PCMK__XE_TRANSIENT_ATTRIBUTES); + crm_xml_add(child, PCMK_XA_ID, node_id); - child = create_xml_node(child, attr->set_type); - if (child == NULL) { - goto done; - } - crm_xml_add(child, XML_ATTR_ID, set_id); + child = pcmk__xe_create(child, attr->set_type); + crm_xml_add(child, PCMK_XA_ID, set_id); - child = create_xml_node(child, XML_CIB_TAG_NVPAIR); - if (child == NULL) { - goto done; - } - crm_xml_add(child, XML_ATTR_ID, attr_id); - crm_xml_add(child, XML_NVPAIR_ATTR_NAME, attr->id); - crm_xml_add(child, XML_NVPAIR_ATTR_VALUE, value); + child = pcmk__xe_create(child, PCMK_XE_NVPAIR); + crm_xml_add(child, PCMK_XA_ID, attr_id); + crm_xml_add(child, PCMK_XA_NAME, attr->id); + crm_xml_add(child, PCMK_XA_VALUE, value); - rc = the_cib->cmds->modify(the_cib, XML_CIB_TAG_STATUS, update, + rc = the_cib->cmds->modify(the_cib, PCMK_XE_STATUS, update, cib_can_create|cib_transaction); rc = pcmk_legacy2rc(rc); -done: free_xml(update); return rc; } @@ -373,16 +386,16 @@ static int add_unset_attr_update(const attribute_t *attr, const char *attr_id, const char *node_id, const char *set_id) { - char *xpath = crm_strdup_printf("/" XML_TAG_CIB - "/" XML_CIB_TAG_STATUS - "/" XML_CIB_TAG_STATE - "[@" XML_ATTR_ID "='%s']" - "/" XML_TAG_TRANSIENT_NODEATTRS - "[@" XML_ATTR_ID "='%s']" - "/%s[@" XML_ATTR_ID "='%s']" - "/" XML_CIB_TAG_NVPAIR - "[@" XML_ATTR_ID "='%s' " - "and @" XML_NVPAIR_ATTR_NAME "='%s']", + char *xpath = crm_strdup_printf("/" PCMK_XE_CIB + "/" PCMK_XE_STATUS + "/" PCMK__XE_NODE_STATE + "[@" PCMK_XA_ID "='%s']" + "/" PCMK__XE_TRANSIENT_ATTRIBUTES + "[@" PCMK_XA_ID "='%s']" + "/%s[@" PCMK_XA_ID "='%s']" + "/" PCMK_XE_NVPAIR + "[@" PCMK_XA_ID "='%s' " + "and @" PCMK_XA_NAME "='%s']", node_id, node_id, attr->set_type, set_id, attr_id, attr->id); @@ -406,31 +419,17 @@ add_unset_attr_update(const attribute_t *attr, const char *attr_id, static int add_attr_update(const attribute_t *attr, const char *value, const char *node_id) { - char *set_id = NULL; - char *attr_id = NULL; + char *set_id = attrd_set_id(attr, node_id); + char *nvpair_id = attrd_nvpair_id(attr, node_id); int rc = pcmk_rc_ok; - if (attr->set_id != NULL) { - pcmk__str_update(&set_id, attr->set_id); - } else { - set_id = crm_strdup_printf("%s-%s", XML_CIB_TAG_STATUS, node_id); - } - crm_xml_sanitize_id(set_id); - - if (attr->uuid != NULL) { - pcmk__str_update(&attr_id, attr->uuid); - } else { - attr_id = crm_strdup_printf("%s-%s", set_id, attr->id); - } - crm_xml_sanitize_id(attr_id); - - if (value != NULL) { - rc = add_set_attr_update(attr, attr_id, node_id, set_id, value); + if (value == NULL) { + rc = add_unset_attr_update(attr, nvpair_id, node_id, set_id); } else { - rc = add_unset_attr_update(attr, attr_id, node_id, set_id); + rc = add_set_attr_update(attr, nvpair_id, node_id, set_id, value); } free(set_id); - free(attr_id); + free(nvpair_id); return rc; } @@ -454,13 +453,11 @@ send_alert_attributes_value(attribute_t *a, GHashTable *t) static void set_alert_attribute_value(GHashTable *t, attribute_value_t *v) { - attribute_value_t *a_v = NULL; - a_v = calloc(1, sizeof(attribute_value_t)); - CRM_ASSERT(a_v != NULL); + attribute_value_t *a_v = pcmk__assert_alloc(1, sizeof(attribute_value_t)); a_v->nodeid = v->nodeid; - a_v->nodename = strdup(v->nodename); - pcmk__str_update(&a_v->current, v->current); + a_v->nodename = pcmk__str_copy(v->nodename); + a_v->current = pcmk__str_copy(v->current); g_hash_table_replace(t, a_v->nodename, a_v); } @@ -493,7 +490,7 @@ write_attribute(attribute_t *a, bool ignore_delay) } /* If this attribute will be written to the CIB ... */ - if (!stand_alone && !a->is_private) { + if (!stand_alone && !pcmk_is_set(a->flags, attrd_attr_is_private)) { /* Defer the write if now's not a good time */ if (a->update && (a->update < last_cib_op_done)) { crm_info("Write out of '%s' continuing: update %d considered lost", @@ -520,21 +517,17 @@ write_attribute(attribute_t *a, bool ignore_delay) the_cib->cmds->set_user(the_cib, a->user); rc = the_cib->cmds->init_transaction(the_cib); if (rc != pcmk_ok) { - crm_err("Failed to write %s (id %s, set %s): Could not initiate " + crm_err("Failed to write %s (set %s): Could not initiate " "CIB transaction", - a->id, pcmk__s(a->uuid, "n/a"), pcmk__s(a->set_id, "n/a")); + a->id, pcmk__s(a->set_id, "unspecified")); goto done; } } - /* Attribute will be written shortly, so clear changed flag */ - a->changed = false; - - /* We will check all peers' uuids shortly, so initialize this to false */ - a->unknown_peer_uuids = false; - - /* Attribute will be written shortly, so clear forced write flag */ - a->force_write = FALSE; + /* Attribute will be written shortly, so clear changed flag and force + * write flag, and initialize UUID missing flag to false. + */ + attrd_clear_attr_flags(a, attrd_attr_changed|attrd_attr_uuid_missing|attrd_attr_force_write); /* Make the table for the attribute trap */ alert_attribute_value = pcmk__strikey_table(NULL, @@ -543,79 +536,80 @@ write_attribute(attribute_t *a, bool ignore_delay) /* Iterate over each peer value of this attribute */ g_hash_table_iter_init(&iter, a->values); while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &v)) { - crm_node_t *peer = crm_get_peer_full(v->nodeid, v->nodename, - CRM_GET_PEER_ANY); + const char *uuid = NULL; - /* If the value's peer info does not correspond to a peer, ignore it */ - if (peer == NULL) { - crm_notice("Cannot update %s[%s]=%s because peer not known", - a->id, v->nodename, v->current); - continue; - } + if (pcmk_is_set(v->flags, attrd_value_remote)) { + /* If this is a Pacemaker Remote node, the node's UUID is the same + * as its name, which we already have. + */ + uuid = v->nodename; - /* If we're just learning the peer's node id, remember it */ - if (peer->id && (v->nodeid == 0)) { - crm_trace("Learned ID %u for node %s", peer->id, v->nodename); - v->nodeid = peer->id; + } else { + // This will create a cluster node cache entry if none exists + crm_node_t *peer = pcmk__get_node(v->nodeid, v->nodename, NULL, + pcmk__node_search_any); + + uuid = peer->uuid; + + // Remember peer's node ID if we're just now learning it + if ((peer->id != 0) && (v->nodeid == 0)) { + crm_trace("Learned ID %u for node %s", peer->id, v->nodename); + v->nodeid = peer->id; + } } /* If this is a private attribute, no update needs to be sent */ - if (stand_alone || a->is_private) { + if (stand_alone || pcmk_is_set(a->flags, attrd_attr_is_private)) { private_updates++; continue; } - /* If the peer is found, but its uuid is unknown, defer write */ - if (peer->uuid == NULL) { - a->unknown_peer_uuids = true; - crm_notice("Cannot update %s[%s]=%s because peer UUID not known " - "(will retry if learned)", + // Defer write if this is a cluster node that's never been seen + if (uuid == NULL) { + attrd_set_attr_flags(a, attrd_attr_uuid_missing); + crm_notice("Cannot update %s[%s]='%s' now because node's UUID is " + "unknown (will retry if learned)", a->id, v->nodename, v->current); continue; } // Update this value as part of the CIB transaction we're building - rc = add_attr_update(a, v->current, peer->uuid); + rc = add_attr_update(a, v->current, uuid); if (rc != pcmk_rc_ok) { - crm_err("Failed to update %s[%s]=%s (peer known as %s, UUID %s, " - "ID %" PRIu32 "/%" PRIu32 "): %s", - a->id, v->nodename, v->current, peer->uname, peer->uuid, - peer->id, v->nodeid, pcmk_rc_str(rc)); + crm_err("Failed to update %s[%s]='%s': %s " + CRM_XS " node uuid=%s id=%" PRIu32, + a->id, v->nodename, v->current, pcmk_rc_str(rc), + uuid, v->nodeid); continue; } - crm_debug("Updating %s[%s]=%s (peer known as %s, UUID %s, ID " - "%" PRIu32 "/%" PRIu32 ")", - a->id, v->nodename, v->current, - peer->uname, peer->uuid, peer->id, v->nodeid); + crm_debug("Writing %s[%s]=%s (node-state-id=%s node-id=%" PRIu32 ")", + a->id, v->nodename, pcmk__s(v->current, "(unset)"), + uuid, v->nodeid); cib_updates++; /* Preservation of the attribute to transmit alert */ set_alert_attribute_value(alert_attribute_value, v); - free(v->requested); - v->requested = NULL; - if (v->current) { - v->requested = strdup(v->current); - } + // Save this value so we can log it when write completes + pcmk__str_update(&(v->requested), v->current); } if (private_updates) { - crm_info("Processed %d private change%s for %s, id=%s, set=%s", + crm_info("Processed %d private change%s for %s (set %s)", private_updates, pcmk__plural_s(private_updates), - a->id, pcmk__s(a->uuid, "n/a"), pcmk__s(a->set_id, "n/a")); + a->id, pcmk__s(a->set_id, "unspecified")); } if (cib_updates > 0) { - char *id = NULL; + char *id = pcmk__str_copy(a->id); // Commit transaction a->update = the_cib->cmds->end_transaction(the_cib, true, cib_none); - crm_info("Sent CIB request %d with %d change%s for %s (id %s, set %s)", + crm_info("Sent CIB request %d with %d change%s for %s (set %s)", a->update, cib_updates, pcmk__plural_s(cib_updates), - a->id, pcmk__s(a->uuid, "n/a"), pcmk__s(a->set_id, "n/a")); + a->id, pcmk__s(a->set_id, "unspecified")); - pcmk__str_update(&id, a->id); if (the_cib->cmds->register_callback_full(the_cib, a->update, CIB_OP_TIMEOUT_S, FALSE, id, "attrd_cib_callback", @@ -653,18 +647,20 @@ attrd_write_attributes(uint32_t options) pcmk_is_set(options, attrd_write_all)? "all" : "changed"); g_hash_table_iter_init(&iter, attributes); while (g_hash_table_iter_next(&iter, NULL, (gpointer *) & a)) { - if (!pcmk_is_set(options, attrd_write_all) && a->unknown_peer_uuids) { + if (!pcmk_is_set(options, attrd_write_all) && + pcmk_is_set(a->flags, attrd_attr_uuid_missing)) { // Try writing this attribute again, in case peer ID was learned - a->changed = true; - } else if (a->force_write) { + attrd_set_attr_flags(a, attrd_attr_changed); + } else if (pcmk_is_set(a->flags, attrd_attr_force_write)) { /* If the force_write flag is set, write the attribute. */ - a->changed = true; + attrd_set_attr_flags(a, attrd_attr_changed); } - if (pcmk_is_set(options, attrd_write_all) || a->changed) { + if (pcmk_is_set(options, attrd_write_all) || + pcmk_is_set(a->flags, attrd_attr_changed)) { bool ignore_delay = pcmk_is_set(options, attrd_write_no_delay); - if (a->force_write) { + if (pcmk_is_set(a->flags, attrd_attr_force_write)) { // Always ignore delay when forced write flag is set ignore_delay = true; } diff --git a/daemons/attrd/attrd_corosync.c b/daemons/attrd/attrd_corosync.c index 86dc67b..fb3b4e5 100644 --- a/daemons/attrd/attrd_corosync.c +++ b/daemons/attrd/attrd_corosync.c @@ -1,5 +1,5 @@ /* - * Copyright 2013-2023 the Pacemaker project contributors + * Copyright 2013-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -19,19 +19,19 @@ #include <crm/common/logging.h> #include <crm/common/results.h> #include <crm/common/strings_internal.h> -#include <crm/msg_xml.h> +#include <crm/common/xml.h> #include "pacemaker-attrd.h" static xmlNode * attrd_confirmation(int callid) { - xmlNode *node = create_xml_node(NULL, __func__); + xmlNode *node = pcmk__xe_create(NULL, __func__); - crm_xml_add(node, F_TYPE, T_ATTRD); - crm_xml_add(node, F_ORIG, get_local_node_name()); - crm_xml_add(node, PCMK__XA_TASK, PCMK__ATTRD_CMD_CONFIRM); - crm_xml_add_int(node, XML_LRM_ATTR_CALLID, callid); + crm_xml_add(node, PCMK__XA_T, PCMK__VALUE_ATTRD); + crm_xml_add(node, PCMK__XA_SRC, pcmk__cluster_local_node_name()); + crm_xml_add(node, PCMK_XA_TASK, PCMK__ATTRD_CMD_CONFIRM); + crm_xml_add_int(node, PCMK__XA_CALL_ID, callid); return node; } @@ -39,7 +39,7 @@ attrd_confirmation(int callid) static void attrd_peer_message(crm_node_t *peer, xmlNode *xml) { - const char *election_op = crm_element_value(xml, F_CRM_TASK); + const char *election_op = crm_element_value(xml, PCMK__XA_CRM_TASK); if (election_op) { attrd_handle_election_op(peer, xml); @@ -64,7 +64,7 @@ attrd_peer_message(crm_node_t *peer, xmlNode *xml) .result = PCMK__UNKNOWN_RESULT, }; - request.op = crm_element_value_copy(request.xml, PCMK__XA_TASK); + request.op = crm_element_value_copy(request.xml, PCMK_XA_TASK); CRM_CHECK(request.op != NULL, return); attrd_handle_request(&request); @@ -81,7 +81,7 @@ attrd_peer_message(crm_node_t *peer, xmlNode *xml) * response so the originating peer knows what they're a confirmation * for. */ - crm_element_value_int(xml, XML_LRM_ATTR_CALLID, &callid); + crm_element_value_int(xml, PCMK__XA_CALL_ID, &callid); reply = attrd_confirmation(callid); /* And then send the confirmation back to the originating peer. This @@ -106,22 +106,22 @@ attrd_cpg_dispatch(cpg_handle_t handle, uint32_t kind = 0; xmlNode *xml = NULL; const char *from = NULL; - char *data = pcmk_message_common_cs(handle, nodeid, pid, msg, &kind, &from); + char *data = pcmk__cpg_message_data(handle, nodeid, pid, msg, &kind, &from); if(data == NULL) { return; } if (kind == crm_class_cluster) { - xml = string2xml(data); + xml = pcmk__xml_parse(data); } if (xml == NULL) { crm_err("Bad message of class %d received from %s[%u]: '%.120s'", kind, from, nodeid, data); } else { - crm_node_t *peer = crm_get_peer(nodeid, from); - - attrd_peer_message(peer, xml); + attrd_peer_message(pcmk__get_node(nodeid, from, NULL, + pcmk__node_search_cluster_member), + xml); } free_xml(xml); @@ -143,86 +143,24 @@ attrd_cpg_destroy(gpointer unused) /*! * \internal - * \brief Override an attribute sync with a local value - * - * Broadcast the local node's value for an attribute that's different from the - * value provided in a peer's attribute synchronization response. This ensures a - * node's values for itself take precedence and all peers are kept in sync. + * \brief Broadcast an update for a single attribute value * - * \param[in] a Attribute entry to override - * - * \return Local instance of attribute value + * \param[in] a Attribute to broadcast + * \param[in] v Attribute value to broadcast */ -static attribute_value_t * -broadcast_local_value(const attribute_t *a) -{ - attribute_value_t *v = g_hash_table_lookup(a->values, attrd_cluster->uname); - xmlNode *sync = create_xml_node(NULL, __func__); - - crm_xml_add(sync, PCMK__XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE); - attrd_add_value_xml(sync, a, v, false); - attrd_send_message(NULL, sync, false); - free_xml(sync); - return v; -} - -/*! - * \internal - * \brief Ensure a Pacemaker Remote node is in the correct peer cache - * - * \param[in] node_name Name of Pacemaker Remote node to check - */ -static void -cache_remote_node(const char *node_name) +void +attrd_broadcast_value(const attribute_t *a, const attribute_value_t *v) { - /* If we previously assumed this node was an unseen cluster node, - * remove its entry from the cluster peer cache. - */ - crm_node_t *dup = pcmk__search_cluster_node_cache(0, node_name, NULL); + xmlNode *op = pcmk__xe_create(NULL, PCMK_XE_OP); - if (dup && (dup->uuid == NULL)) { - reap_crm_member(0, node_name); - } - - // Ensure node is in the remote peer cache - CRM_ASSERT(crm_remote_peer_get(node_name) != NULL); + crm_xml_add(op, PCMK_XA_TASK, PCMK__ATTRD_CMD_UPDATE); + attrd_add_value_xml(op, a, v, false); + attrd_send_message(NULL, op, false); + free_xml(op); } #define state_text(state) pcmk__s((state), "in unknown state") -/*! - * \internal - * \brief Return host's hash table entry (creating one if needed) - * - * \param[in,out] values Hash table of values - * \param[in] host Name of peer to look up - * \param[in] xml XML describing the attribute - * - * \return Pointer to new or existing hash table entry - */ -static attribute_value_t * -attrd_lookup_or_create_value(GHashTable *values, const char *host, - const xmlNode *xml) -{ - attribute_value_t *v = g_hash_table_lookup(values, host); - int is_remote = 0; - - crm_element_value_int(xml, PCMK__XA_ATTR_IS_REMOTE, &is_remote); - if (is_remote) { - cache_remote_node(host); - } - - if (v == NULL) { - v = calloc(1, sizeof(attribute_value_t)); - CRM_ASSERT(v != NULL); - - pcmk__str_update(&v->nodename, host); - v->is_remote = is_remote; - g_hash_table_replace(values, v->nodename, v); - } - return(v); -} - static void attrd_peer_change_cb(enum crm_status_type kind, crm_node_t *peer, const void *data) { @@ -254,7 +192,7 @@ attrd_peer_change_cb(enum crm_status_type kind, crm_node_t *peer, const void *da */ if (attrd_election_won() && !pcmk_is_set(peer->flags, crm_remote_node)) { - attrd_peer_sync(peer, NULL); + attrd_peer_sync(peer); } } else { // Remove all attribute values associated with lost nodes @@ -269,17 +207,14 @@ attrd_peer_change_cb(enum crm_status_type kind, crm_node_t *peer, const void *da attrd_remove_voter(peer); attrd_remove_peer_protocol_ver(peer->uname); attrd_do_not_expect_from_peer(peer->uname); - - // Ensure remote nodes that come up are in the remote node cache - } else if (!gone && is_remote) { - cache_remote_node(peer->uname); } } static void record_peer_nodeid(attribute_value_t *v, const char *host) { - crm_node_t *known_peer = crm_get_peer(v->nodeid, host); + crm_node_t *known_peer = pcmk__get_node(v->nodeid, host, NULL, + pcmk__node_search_cluster_member); crm_trace("Learned %s has node id %s", known_peer->uname, known_peer->uuid); if (attrd_election_won()) { @@ -287,34 +222,63 @@ record_peer_nodeid(attribute_value_t *v, const char *host) } } +#define readable_value(rv_v) pcmk__s((rv_v)->current, "(unset)") + +#define readable_peer(p) \ + (((p) == NULL)? "all peers" : pcmk__s((p)->uname, "unknown peer")) + static void update_attr_on_host(attribute_t *a, const crm_node_t *peer, const xmlNode *xml, const char *attr, const char *value, const char *host, - bool filter, int is_force_write) + bool filter) { + int is_remote = 0; + bool changed = false; attribute_value_t *v = NULL; - v = attrd_lookup_or_create_value(a->values, host, xml); + // Create entry for value if not already existing + v = g_hash_table_lookup(a->values, host); + if (v == NULL) { + v = pcmk__assert_alloc(1, sizeof(attribute_value_t)); + + v->nodename = pcmk__str_copy(host); + g_hash_table_replace(a->values, v->nodename, v); + } + + // If value is for a Pacemaker Remote node, remember that + crm_element_value_int(xml, PCMK__XA_ATTR_IS_REMOTE, &is_remote); + if (is_remote) { + attrd_set_value_flags(v, attrd_value_remote); + CRM_ASSERT(pcmk__cluster_lookup_remote_node(host) != NULL); + } - if (filter && !pcmk__str_eq(v->current, value, pcmk__str_casei) - && pcmk__str_eq(host, attrd_cluster->uname, pcmk__str_casei)) { + // Check whether the value changed + changed = !pcmk__str_eq(v->current, value, pcmk__str_casei); + if (changed && filter && pcmk__str_eq(host, attrd_cluster->uname, + pcmk__str_casei)) { + /* Broadcast the local value for an attribute that differs from the + * value provided in a peer's attribute synchronization response. This + * ensures a node's values for itself take precedence and all peers are + * kept in sync. + */ + v = g_hash_table_lookup(a->values, attrd_cluster->uname); crm_notice("%s[%s]: local value '%s' takes priority over '%s' from %s", - attr, host, v->current, value, peer->uname); - v = broadcast_local_value(a); + attr, host, readable_value(v), value, peer->uname); + attrd_broadcast_value(a, v); - } else if (!pcmk__str_eq(v->current, value, pcmk__str_casei)) { + } else if (changed) { crm_notice("Setting %s[%s]%s%s: %s -> %s " CRM_XS " from %s with %s write delay", attr, host, a->set_type ? " in " : "", - pcmk__s(a->set_type, ""), pcmk__s(v->current, "(unset)"), + pcmk__s(a->set_type, ""), readable_value(v), pcmk__s(value, "(unset)"), peer->uname, (a->timeout_ms == 0)? "no" : pcmk__readable_interval(a->timeout_ms)); pcmk__str_update(&v->current, value); - a->changed = true; + attrd_set_attr_flags(a, attrd_attr_changed); if (pcmk__str_eq(host, attrd_cluster->uname, pcmk__str_casei) - && pcmk__str_eq(attr, XML_CIB_ATTR_SHUTDOWN, pcmk__str_none)) { + && pcmk__str_eq(attr, PCMK__NODE_ATTR_SHUTDOWN, pcmk__str_none)) { if (!pcmk__str_eq(value, "0", pcmk__str_null_matches)) { attrd_set_requesting_shutdown(); @@ -326,30 +290,37 @@ update_attr_on_host(attribute_t *a, const crm_node_t *peer, const xmlNode *xml, // Write out new value or start dampening timer if (a->timeout_ms && a->timer) { - crm_trace("Delayed write out (%dms) for %s", a->timeout_ms, attr); + crm_trace("Delaying write of %s %s for dampening", + attr, pcmk__readable_interval(a->timeout_ms)); mainloop_timer_start(a->timer); } else { attrd_write_or_elect_attribute(a); } } else { + int is_force_write = 0; + + crm_element_value_int(xml, PCMK__XA_ATTRD_IS_FORCE_WRITE, + &is_force_write); + if (is_force_write == 1 && a->timeout_ms && a->timer) { /* Save forced writing and set change flag. */ /* The actual attribute is written by Writer after election. */ - crm_trace("Unchanged %s[%s] from %s is %s(Set the forced write flag)", - attr, host, peer->uname, value); - a->force_write = TRUE; + crm_trace("%s[%s] from %s is unchanged (%s), forcing write", + attr, host, peer->uname, pcmk__s(value, "unset")); + attrd_set_attr_flags(a, attrd_attr_force_write); } else { - crm_trace("Unchanged %s[%s] from %s is %s", attr, host, peer->uname, value); + crm_trace("%s[%s] from %s is unchanged (%s)", + attr, host, peer->uname, pcmk__s(value, "unset")); } } - /* Set the seen flag for attribute processing held only in the own node. */ - v->seen = TRUE; + // This allows us to later detect local values that peer doesn't know about + attrd_set_value_flags(v, attrd_value_from_peer); /* If this is a cluster node whose node ID we are learning, remember it */ - if ((v->nodeid == 0) && (v->is_remote == FALSE) - && (crm_element_value_int(xml, PCMK__XA_ATTR_NODE_ID, + if ((v->nodeid == 0) && !pcmk_is_set(v->flags, attrd_value_remote) + && (crm_element_value_int(xml, PCMK__XA_ATTR_HOST_ID, (int*)&v->nodeid) == 0) && (v->nodeid > 0)) { record_peer_nodeid(v, host); } @@ -361,16 +332,13 @@ attrd_peer_update_one(const crm_node_t *peer, xmlNode *xml, bool filter) attribute_t *a = NULL; const char *attr = crm_element_value(xml, PCMK__XA_ATTR_NAME); const char *value = crm_element_value(xml, PCMK__XA_ATTR_VALUE); - const char *host = crm_element_value(xml, PCMK__XA_ATTR_NODE_NAME); - int is_force_write = 0; + const char *host = crm_element_value(xml, PCMK__XA_ATTR_HOST); if (attr == NULL) { crm_warn("Could not update attribute: peer did not specify name"); return; } - crm_element_value_int(xml, PCMK__XA_ATTR_FORCE, &is_force_write); - a = attrd_populate_attribute(xml, attr); if (a == NULL) { return; @@ -381,16 +349,16 @@ attrd_peer_update_one(const crm_node_t *peer, xmlNode *xml, bool filter) GHashTableIter vIter; crm_debug("Setting %s for all hosts to %s", attr, value); - xml_remove_prop(xml, PCMK__XA_ATTR_NODE_ID); + pcmk__xe_remove_attr(xml, PCMK__XA_ATTR_HOST_ID); g_hash_table_iter_init(&vIter, a->values); while (g_hash_table_iter_next(&vIter, (gpointer *) & host, NULL)) { - update_attr_on_host(a, peer, xml, attr, value, host, filter, is_force_write); + update_attr_on_host(a, peer, xml, attr, value, host, filter); } } else { // Update attribute value for the given host - update_attr_on_host(a, peer, xml, attr, value, host, filter, is_force_write); + update_attr_on_host(a, peer, xml, attr, value, host, filter); } /* If this is a message from some attrd instance broadcasting its protocol @@ -412,13 +380,18 @@ broadcast_unseen_local_values(void) g_hash_table_iter_init(&aIter, attributes); while (g_hash_table_iter_next(&aIter, NULL, (gpointer *) & a)) { + g_hash_table_iter_init(&vIter, a->values); while (g_hash_table_iter_next(&vIter, NULL, (gpointer *) & v)) { - if (!(v->seen) && pcmk__str_eq(v->nodename, attrd_cluster->uname, - pcmk__str_casei)) { + + if (!pcmk_is_set(v->flags, attrd_value_from_peer) + && pcmk__str_eq(v->nodename, attrd_cluster->uname, + pcmk__str_casei)) { + crm_trace("* %s[%s]='%s' is local-only", + a->id, v->nodename, readable_value(v)); if (sync == NULL) { - sync = create_xml_node(NULL, __func__); - crm_xml_add(sync, PCMK__XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE); + sync = pcmk__xe_create(NULL, __func__); + crm_xml_add(sync, PCMK_XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE); } attrd_add_value_xml(sync, a, v, a->timeout_ms && a->timer); } @@ -435,17 +408,21 @@ broadcast_unseen_local_values(void) int attrd_cluster_connect(void) { + int rc = pcmk_rc_ok; + attrd_cluster = pcmk_cluster_new(); - attrd_cluster->destroy = attrd_cpg_destroy; - attrd_cluster->cpg.cpg_deliver_fn = attrd_cpg_dispatch; - attrd_cluster->cpg.cpg_confchg_fn = pcmk_cpg_membership; + pcmk_cluster_set_destroy_fn(attrd_cluster, attrd_cpg_destroy); + pcmk_cpg_set_deliver_fn(attrd_cluster, attrd_cpg_dispatch); + pcmk_cpg_set_confchg_fn(attrd_cluster, pcmk__cpg_confchg_cb); - crm_set_status_callback(&attrd_peer_change_cb); + pcmk__cluster_set_status_callback(&attrd_peer_change_cb); - if (crm_cluster_connect(attrd_cluster) == FALSE) { + rc = pcmk_cluster_connect(attrd_cluster); + rc = pcmk_rc2legacy(rc); + if (rc != pcmk_ok) { crm_err("Cluster connection failed"); - return -ENOTCONN; + return rc; } return pcmk_ok; } @@ -455,15 +432,19 @@ attrd_peer_clear_failure(pcmk__request_t *request) { xmlNode *xml = request->xml; const char *rsc = crm_element_value(xml, PCMK__XA_ATTR_RESOURCE); - const char *host = crm_element_value(xml, PCMK__XA_ATTR_NODE_NAME); - const char *op = crm_element_value(xml, PCMK__XA_ATTR_OPERATION); - const char *interval_spec = crm_element_value(xml, PCMK__XA_ATTR_INTERVAL); - guint interval_ms = crm_parse_interval_spec(interval_spec); + const char *host = crm_element_value(xml, PCMK__XA_ATTR_HOST); + const char *op = crm_element_value(xml, PCMK__XA_ATTR_CLEAR_OPERATION); + const char *interval_spec = crm_element_value(xml, + PCMK__XA_ATTR_CLEAR_INTERVAL); + guint interval_ms = 0U; char *attr = NULL; GHashTableIter iter; regex_t regex; - crm_node_t *peer = crm_get_peer(0, request->peer); + crm_node_t *peer = pcmk__get_node(0, request->peer, NULL, + pcmk__node_search_cluster_member); + + pcmk_parse_interval_spec(interval_spec, &interval_ms); if (attrd_failure_regex(®ex, rsc, op, interval_ms) != pcmk_ok) { crm_info("Ignoring invalid request to clear failures for %s", @@ -471,10 +452,10 @@ attrd_peer_clear_failure(pcmk__request_t *request) return; } - crm_xml_add(xml, PCMK__XA_TASK, PCMK__ATTRD_CMD_UPDATE); + crm_xml_add(xml, PCMK_XA_TASK, PCMK__ATTRD_CMD_UPDATE); /* Make sure value is not set, so we delete */ - xml_remove_prop(xml, PCMK__XA_ATTR_VALUE); + pcmk__xe_remove_attr(xml, PCMK__XA_ATTR_VALUE); g_hash_table_iter_init(&iter, attributes); while (g_hash_table_iter_next(&iter, (gpointer *) &attr, NULL)) { @@ -492,7 +473,7 @@ attrd_peer_clear_failure(pcmk__request_t *request) * \internal * \brief Load attributes from a peer sync response * - * \param[in] peer Peer that sent clear request + * \param[in] peer Peer that sent sync response * \param[in] peer_won Whether peer is the attribute writer * \param[in,out] xml Request XML */ @@ -510,11 +491,11 @@ attrd_peer_sync_response(const crm_node_t *peer, bool peer_won, xmlNode *xml) } // Process each attribute update in the sync response - for (xmlNode *child = pcmk__xml_first_child(xml); child != NULL; - child = pcmk__xml_next(child)) { + for (xmlNode *child = pcmk__xe_first_child(xml, NULL, NULL, NULL); + child != NULL; child = pcmk__xe_next(child)) { + attrd_peer_update(peer, child, - crm_element_value(child, PCMK__XA_ATTR_NODE_NAME), - true); + crm_element_value(child, PCMK__XA_ATTR_HOST), true); } if (peer_won) { @@ -540,7 +521,9 @@ attrd_peer_remove(const char *host, bool uncache, const char *source) GHashTableIter aIter; CRM_CHECK(host != NULL, return); - crm_notice("Removing all %s attributes for peer %s", host, source); + crm_notice("Removing all %s attributes for node %s " + CRM_XS " %s reaping node from cache", + host, source, (uncache? "and" : "without")); g_hash_table_iter_init(&aIter, attributes); while (g_hash_table_iter_next(&aIter, NULL, (gpointer *) & a)) { @@ -550,33 +533,40 @@ attrd_peer_remove(const char *host, bool uncache, const char *source) } if (uncache) { - crm_remote_peer_cache_remove(host); - reap_crm_member(0, host); + pcmk__purge_node_from_cache(host, 0); } } +/*! + * \internal + * \brief Send all known attributes and values to a peer + * + * \param[in] peer Peer to send sync to (if NULL, broadcast to all peers) + */ void -attrd_peer_sync(crm_node_t *peer, xmlNode *xml) +attrd_peer_sync(crm_node_t *peer) { GHashTableIter aIter; GHashTableIter vIter; attribute_t *a = NULL; attribute_value_t *v = NULL; - xmlNode *sync = create_xml_node(NULL, __func__); + xmlNode *sync = pcmk__xe_create(NULL, __func__); - crm_xml_add(sync, PCMK__XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE); + crm_xml_add(sync, PCMK_XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE); g_hash_table_iter_init(&aIter, attributes); while (g_hash_table_iter_next(&aIter, NULL, (gpointer *) & a)) { g_hash_table_iter_init(&vIter, a->values); while (g_hash_table_iter_next(&vIter, NULL, (gpointer *) & v)) { - crm_debug("Syncing %s[%s] = %s to %s", a->id, v->nodename, v->current, peer?peer->uname:"everyone"); + crm_debug("Syncing %s[%s]='%s' to %s", + a->id, v->nodename, readable_value(v), + readable_peer(peer)); attrd_add_value_xml(sync, a, v, false); } } - crm_debug("Syncing values to %s", peer?peer->uname:"everyone"); + crm_debug("Syncing values to %s", readable_peer(peer)); attrd_send_message(peer, sync, false); free_xml(sync); } @@ -589,9 +579,10 @@ attrd_peer_update(const crm_node_t *peer, xmlNode *xml, const char *host, CRM_CHECK((peer != NULL) && (xml != NULL), return); if (xml->children != NULL) { - for (xmlNode *child = first_named_child(xml, XML_ATTR_OP); child != NULL; - child = crm_next_same_xml(child)) { - attrd_copy_xml_attributes(xml, child); + for (xmlNode *child = pcmk__xe_first_child(xml, PCMK_XE_OP, NULL, NULL); + child != NULL; child = pcmk__xe_next_same(child)) { + + pcmk__xe_copy_attrs(child, xml, pcmk__xaf_no_overwrite); attrd_peer_update_one(peer, child, filter); if (attrd_request_has_sync_point(child)) { diff --git a/daemons/attrd/attrd_elections.c b/daemons/attrd/attrd_elections.c index 82fbe8a..0abd9c0 100644 --- a/daemons/attrd/attrd_elections.c +++ b/daemons/attrd/attrd_elections.c @@ -1,5 +1,5 @@ /* - * Copyright 2013-2023 the Pacemaker project contributors + * Copyright 2013-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -8,9 +8,9 @@ */ #include <crm_internal.h> -#include <crm/msg_xml.h> #include <crm/cluster.h> #include <crm/cluster/election_internal.h> +#include <crm/common/xml.h> #include "pacemaker-attrd.h" @@ -23,7 +23,7 @@ attrd_election_cb(gpointer user_data) attrd_declare_winner(); /* Update the peers after an election */ - attrd_peer_sync(NULL, NULL); + attrd_peer_sync(NULL); /* After winning an election, update the CIB with the values of all * attributes as the winner knows them. @@ -35,7 +35,7 @@ attrd_election_cb(gpointer user_data) void attrd_election_init(void) { - writer = election_init(T_ATTRD, attrd_cluster->uname, 120000, + writer = election_init(PCMK__VALUE_ATTRD, attrd_cluster->uname, 120000, attrd_election_cb); } @@ -69,7 +69,7 @@ attrd_handle_election_op(const crm_node_t *peer, xmlNode *xml) enum election_result rc = 0; enum election_result previous = election_state(writer); - crm_xml_add(xml, F_CRM_HOST_FROM, peer->uname); + crm_xml_add(xml, PCMK__XA_SRC, peer->uname); // Don't become writer if we're shutting down rc = election_count_vote(writer, xml, !attrd_shutting_down(false)); diff --git a/daemons/attrd/attrd_ipc.c b/daemons/attrd/attrd_ipc.c index 05c4a69..0a2688e 100644 --- a/daemons/attrd/attrd_ipc.c +++ b/daemons/attrd/attrd_ipc.c @@ -1,5 +1,5 @@ /* - * Copyright 2004-2023 the Pacemaker project contributors + * Copyright 2004-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -16,13 +16,13 @@ #include <crm/cluster.h> #include <crm/cluster/internal.h> -#include <crm/msg_xml.h> #include <crm/common/acl_internal.h> #include <crm/common/ipc_internal.h> #include <crm/common/logging.h> #include <crm/common/results.h> #include <crm/common/strings_internal.h> #include <crm/common/util.h> +#include <crm/common/xml.h> #include "pacemaker-attrd.h" @@ -32,22 +32,19 @@ static qb_ipcs_service_t *ipcs = NULL; * \internal * \brief Build the XML reply to a client query * - * param[in] attr Name of requested attribute - * param[in] host Name of requested host (or NULL for all hosts) + * \param[in] attr Name of requested attribute + * \param[in] host Name of requested host (or NULL for all hosts) * * \return New XML reply * \note Caller is responsible for freeing the resulting XML */ static xmlNode *build_query_reply(const char *attr, const char *host) { - xmlNode *reply = create_xml_node(NULL, __func__); + xmlNode *reply = pcmk__xe_create(NULL, __func__); attribute_t *a; - if (reply == NULL) { - return NULL; - } - crm_xml_add(reply, F_TYPE, T_ATTRD); - crm_xml_add(reply, F_SUBTYPE, PCMK__ATTRD_CMD_QUERY); + crm_xml_add(reply, PCMK__XA_T, PCMK__VALUE_ATTRD); + crm_xml_add(reply, PCMK__XA_SUBT, PCMK__ATTRD_CMD_QUERY); crm_xml_add(reply, PCMK__XA_ATTR_VERSION, ATTRD_PROTOCOL_VERSION); /* If desired attribute exists, add its value(s) to the reply */ @@ -67,11 +64,7 @@ static xmlNode *build_query_reply(const char *attr, const char *host) /* If a specific node was requested, add its value */ if (host) { v = g_hash_table_lookup(a->values, host); - host_value = create_xml_node(reply, XML_CIB_TAG_NODE); - if (host_value == NULL) { - free_xml(reply); - return NULL; - } + host_value = pcmk__xe_create(reply, PCMK_XE_NODE); pcmk__xe_add_node(host_value, host, 0); crm_xml_add(host_value, PCMK__XA_ATTR_VALUE, (v? v->current : NULL)); @@ -82,11 +75,7 @@ static xmlNode *build_query_reply(const char *attr, const char *host) g_hash_table_iter_init(&iter, a->values); while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &v)) { - host_value = create_xml_node(reply, XML_CIB_TAG_NODE); - if (host_value == NULL) { - free_xml(reply); - return NULL; - } + host_value = pcmk__xe_create(reply, PCMK_XE_NODE); pcmk__xe_add_node(host_value, v->nodename, 0); crm_xml_add(host_value, PCMK__XA_ATTR_VALUE, v->current); } @@ -111,11 +100,11 @@ attrd_client_clear_failure(pcmk__request_t *request) } rsc = crm_element_value(xml, PCMK__XA_ATTR_RESOURCE); - op = crm_element_value(xml, PCMK__XA_ATTR_OPERATION); - interval_spec = crm_element_value(xml, PCMK__XA_ATTR_INTERVAL); + op = crm_element_value(xml, PCMK__XA_ATTR_CLEAR_OPERATION); + interval_spec = crm_element_value(xml, PCMK__XA_ATTR_CLEAR_INTERVAL); /* Map this to an update */ - crm_xml_add(xml, PCMK__XA_TASK, PCMK__ATTRD_CMD_UPDATE); + crm_xml_add(xml, PCMK_XA_TASK, PCMK__ATTRD_CMD_UPDATE); /* Add regular expression matching desired attributes */ @@ -126,22 +115,23 @@ attrd_client_clear_failure(pcmk__request_t *request) pattern = crm_strdup_printf(ATTRD_RE_CLEAR_ONE, rsc); } else { - guint interval_ms = crm_parse_interval_spec(interval_spec); + guint interval_ms = 0U; + pcmk_parse_interval_spec(interval_spec, &interval_ms); pattern = crm_strdup_printf(ATTRD_RE_CLEAR_OP, rsc, op, interval_ms); } - crm_xml_add(xml, PCMK__XA_ATTR_PATTERN, pattern); + crm_xml_add(xml, PCMK__XA_ATTR_REGEX, pattern); free(pattern); } else { - crm_xml_add(xml, PCMK__XA_ATTR_PATTERN, ATTRD_RE_CLEAR_ALL); + crm_xml_add(xml, PCMK__XA_ATTR_REGEX, ATTRD_RE_CLEAR_ALL); } /* Make sure attribute and value are not set, so we delete via regex */ - xml_remove_prop(xml, PCMK__XA_ATTR_NAME); - xml_remove_prop(xml, PCMK__XA_ATTR_VALUE); + pcmk__xe_remove_attr(xml, PCMK__XA_ATTR_NAME); + pcmk__xe_remove_attr(xml, PCMK__XA_ATTR_VALUE); return attrd_client_update(request); } @@ -152,7 +142,7 @@ attrd_client_peer_remove(pcmk__request_t *request) xmlNode *xml = request->xml; // Host and ID are not used in combination, rather host has precedence - const char *host = crm_element_value(xml, PCMK__XA_ATTR_NODE_NAME); + const char *host = crm_element_value(xml, PCMK__XA_ATTR_HOST); char *host_alloc = NULL; attrd_send_ack(request->ipc_client, request->ipc_id, request->ipc_flags); @@ -160,18 +150,19 @@ attrd_client_peer_remove(pcmk__request_t *request) if (host == NULL) { int nodeid = 0; - crm_element_value_int(xml, PCMK__XA_ATTR_NODE_ID, &nodeid); + crm_element_value_int(xml, PCMK__XA_ATTR_HOST_ID, &nodeid); if (nodeid > 0) { - crm_node_t *node = pcmk__search_cluster_node_cache(nodeid, NULL, - NULL); + crm_node_t *node = NULL; char *host_alloc = NULL; + node = pcmk__search_node_caches(nodeid, NULL, + pcmk__node_search_cluster_member); if (node && node->uname) { // Use cached name if available host = node->uname; } else { // Otherwise ask cluster layer - host_alloc = get_node_name(nodeid); + host_alloc = pcmk__cluster_node_name(nodeid); host = host_alloc; } pcmk__xe_add_node(xml, host, 0); @@ -211,8 +202,8 @@ attrd_client_query(pcmk__request_t *request) } /* Build the XML reply */ - reply = build_query_reply(attr, crm_element_value(query, - PCMK__XA_ATTR_NODE_NAME)); + reply = build_query_reply(attr, + crm_element_value(query, PCMK__XA_ATTR_HOST)); if (reply == NULL) { pcmk__format_result(&request->result, CRM_EX_ERROR, PCMK_EXEC_ERROR, "Could not respond to query from %s: could not create XML reply", @@ -241,7 +232,7 @@ attrd_client_refresh(pcmk__request_t *request) static void handle_missing_host(xmlNode *xml) { - const char *host = crm_element_value(xml, PCMK__XA_ATTR_NODE_NAME); + const char *host = crm_element_value(xml, PCMK__XA_ATTR_HOST); if (host == NULL) { crm_trace("Inferring host"); @@ -270,16 +261,16 @@ expand_regexes(xmlNode *xml, const char *attr, const char *value, const char *re int status = regexec(&r_patt, attr, 0, NULL, 0); if (status == 0) { - xmlNode *child = create_xml_node(xml, XML_ATTR_OP); + xmlNode *child = pcmk__xe_create(xml, PCMK_XE_OP); crm_trace("Matched %s with %s", attr, regex); matched = true; - /* Copy all the attributes from the parent over, but remove the - * regex and replace it with the name. + /* Copy all the non-conflicting attributes from the parent over, + * but remove the regex and replace it with the name. */ - attrd_copy_xml_attributes(xml, child); - xml_remove_prop(child, PCMK__XA_ATTR_PATTERN); + pcmk__xe_copy_attrs(child, xml, pcmk__xaf_no_overwrite); + pcmk__xe_remove_attr(child, PCMK__XA_ATTR_REGEX); crm_xml_add(child, PCMK__XA_ATTR_NAME, attr); } } @@ -310,7 +301,7 @@ handle_regexes(pcmk__request_t *request) const char *attr = crm_element_value(xml, PCMK__XA_ATTR_NAME); const char *value = crm_element_value(xml, PCMK__XA_ATTR_VALUE); - const char *regex = crm_element_value(xml, PCMK__XA_ATTR_PATTERN); + const char *regex = crm_element_value(xml, PCMK__XA_ATTR_REGEX); rc = expand_regexes(xml, attr, value, regex); @@ -344,7 +335,7 @@ handle_value_expansion(const char **value, xmlNode *xml, const char *op, attribute_value_t *v = NULL; if (a) { - const char *host = crm_element_value(xml, PCMK__XA_ATTR_NODE_NAME); + const char *host = crm_element_value(xml, PCMK__XA_ATTR_HOST); v = g_hash_table_lookup(a->values, host); } @@ -416,8 +407,10 @@ attrd_client_update(pcmk__request_t *request) * we also need to apply all the transformations in this function * to the children since they don't happen anywhere else. */ - for (xmlNode *child = first_named_child(xml, XML_ATTR_OP); child != NULL; - child = crm_next_same_xml(child)) { + for (xmlNode *child = pcmk__xe_first_child(xml, PCMK_XE_OP, NULL, + NULL); + child != NULL; child = pcmk__xe_next_same(child)) { + attr = crm_element_value(child, PCMK__XA_ATTR_NAME); value = crm_element_value(child, PCMK__XA_ATTR_VALUE); @@ -443,7 +436,7 @@ attrd_client_update(pcmk__request_t *request) * up into individual messages and call attrd_client_update on * each one. */ - pcmk__xe_foreach_child(xml, XML_ATTR_OP, send_child_update, request); + pcmk__xe_foreach_child(xml, PCMK_XE_OP, send_child_update, request); request->xml = orig_xml; } @@ -452,7 +445,7 @@ attrd_client_update(pcmk__request_t *request) attr = crm_element_value(xml, PCMK__XA_ATTR_NAME); value = crm_element_value(xml, PCMK__XA_ATTR_VALUE); - regex = crm_element_value(xml, PCMK__XA_ATTR_PATTERN); + regex = crm_element_value(xml, PCMK__XA_ATTR_REGEX); if (handle_regexes(request) != pcmk_rc_ok) { /* Error handling was already dealt with in handle_regexes, so just return. */ @@ -473,7 +466,8 @@ attrd_client_update(pcmk__request_t *request) return NULL; } - crm_debug("Broadcasting %s[%s]=%s%s", attr, crm_element_value(xml, PCMK__XA_ATTR_NODE_NAME), + crm_debug("Broadcasting %s[%s]=%s%s", + attr, crm_element_value(xml, PCMK__XA_ATTR_HOST), value, (attrd_election_won()? " (writer)" : "")); send_update_msg_to_cluster(request, xml); @@ -498,11 +492,11 @@ attrd_ipc_accept(qb_ipcs_connection_t *c, uid_t uid, gid_t gid) if (attrd_shutting_down(false)) { crm_info("Ignoring new connection from pid %d during shutdown", pcmk__client_pid(c)); - return -EPERM; + return -ECONNREFUSED; } if (pcmk__new_client(c, uid, gid) == NULL) { - return -EIO; + return -ENOMEM; } return pcmk_ok; } @@ -572,7 +566,8 @@ attrd_ipc_dispatch(qb_ipcs_connection_t * c, void *data, size_t size) if (xml == NULL) { crm_debug("Unrecognizable IPC data from PID %d", pcmk__client_pid(c)); - pcmk__ipc_send_ack(client, id, flags, "ack", NULL, CRM_EX_PROTOCOL); + pcmk__ipc_send_ack(client, id, flags, PCMK__XE_ACK, NULL, + CRM_EX_PROTOCOL); return 0; } else { @@ -589,7 +584,7 @@ attrd_ipc_dispatch(qb_ipcs_connection_t * c, void *data, size_t size) CRM_ASSERT(client->user != NULL); pcmk__update_acl_user(xml, PCMK__XA_ATTR_USER, client->user); - request.op = crm_element_value_copy(request.xml, PCMK__XA_TASK); + request.op = crm_element_value_copy(request.xml, PCMK_XA_TASK); CRM_CHECK(request.op != NULL, return 0); attrd_handle_request(&request); diff --git a/daemons/attrd/attrd_messages.c b/daemons/attrd/attrd_messages.c index 89da6d8..edb33a5 100644 --- a/daemons/attrd/attrd_messages.c +++ b/daemons/attrd/attrd_messages.c @@ -1,5 +1,5 @@ /* - * Copyright 2022 the Pacemaker project contributors + * Copyright 2022-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -12,7 +12,8 @@ #include <glib.h> #include <crm/common/messages_internal.h> -#include <crm/msg_xml.h> +#include <crm/cluster/internal.h> // pcmk__get_node() +#include <crm/common/xml.h> #include "pacemaker-attrd.h" @@ -30,7 +31,7 @@ static int remove_sync_point_attribute(xmlNode *xml, void *data) { pcmk__xe_remove_matching_attrs(xml, is_sync_point_attr, NULL); - pcmk__xe_foreach_child(xml, XML_ATTR_OP, remove_sync_point_attribute, NULL); + pcmk__xe_foreach_child(xml, PCMK_XE_OP, remove_sync_point_attribute, NULL); return pcmk_rc_ok; } @@ -105,7 +106,8 @@ handle_confirm_request(pcmk__request_t *request) crm_debug("Received confirmation from %s", request->peer); - if (crm_element_value_int(request->xml, XML_LRM_ATTR_CALLID, &callid) == -1) { + if (crm_element_value_int(request->xml, PCMK__XA_CALL_ID, + &callid) == -1) { pcmk__set_result(&request->result, CRM_EX_PROTOCOL, PCMK_EXEC_INVALID, "Could not get callid from XML"); } else { @@ -147,8 +149,14 @@ static xmlNode * handle_remove_request(pcmk__request_t *request) { if (request->peer != NULL) { - const char *host = crm_element_value(request->xml, PCMK__XA_ATTR_NODE_NAME); - attrd_peer_remove(host, true, request->peer); + const char *host = crm_element_value(request->xml, PCMK__XA_ATTR_HOST); + bool reap = false; + + if (pcmk__xe_get_bool_attr(request->xml, PCMK__XA_REAP, + &reap) != pcmk_rc_ok) { + reap = true; // Default to true for backward compatibility + } + attrd_peer_remove(host, reap, request->peer); pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL); return NULL; } else { @@ -167,27 +175,14 @@ handle_refresh_request(pcmk__request_t *request) } static xmlNode * -handle_sync_request(pcmk__request_t *request) -{ - if (request->peer != NULL) { - crm_node_t *peer = crm_get_peer(0, request->peer); - - attrd_peer_sync(peer, request->xml); - pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL); - return NULL; - } else { - return handle_unknown_request(request); - } -} - -static xmlNode * handle_sync_response_request(pcmk__request_t *request) { if (request->ipc_client != NULL) { return handle_unknown_request(request); } else { if (request->peer != NULL) { - crm_node_t *peer = crm_get_peer(0, request->peer); + crm_node_t *peer = pcmk__get_node(0, request->peer, NULL, + pcmk__node_search_cluster_member); bool peer_won = attrd_check_for_new_writer(peer, request->xml); if (!pcmk__str_eq(peer->uname, attrd_cluster->uname, pcmk__str_casei)) { @@ -204,8 +199,9 @@ static xmlNode * handle_update_request(pcmk__request_t *request) { if (request->peer != NULL) { - const char *host = crm_element_value(request->xml, PCMK__XA_ATTR_NODE_NAME); - crm_node_t *peer = crm_get_peer(0, request->peer); + const char *host = crm_element_value(request->xml, PCMK__XA_ATTR_HOST); + crm_node_t *peer = pcmk__get_node(0, request->peer, NULL, + pcmk__node_search_cluster_member); attrd_peer_update(peer, request->xml, host, false); pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL); @@ -251,7 +247,6 @@ attrd_register_handlers(void) { PCMK__ATTRD_CMD_PEER_REMOVE, handle_remove_request }, { PCMK__ATTRD_CMD_QUERY, handle_query_request }, { PCMK__ATTRD_CMD_REFRESH, handle_refresh_request }, - { PCMK__ATTRD_CMD_SYNC, handle_sync_request }, { PCMK__ATTRD_CMD_SYNC_RESPONSE, handle_sync_response_request }, { PCMK__ATTRD_CMD_UPDATE, handle_update_request }, { PCMK__ATTRD_CMD_UPDATE_DELAY, handle_update_request }, @@ -323,11 +318,11 @@ attrd_handle_request(pcmk__request_t *request) void attrd_broadcast_protocol(void) { - xmlNode *attrd_op = create_xml_node(NULL, __func__); + xmlNode *attrd_op = pcmk__xe_create(NULL, __func__); - crm_xml_add(attrd_op, F_TYPE, T_ATTRD); - crm_xml_add(attrd_op, F_ORIG, crm_system_name); - crm_xml_add(attrd_op, PCMK__XA_TASK, PCMK__ATTRD_CMD_UPDATE); + crm_xml_add(attrd_op, PCMK__XA_T, PCMK__VALUE_ATTRD); + crm_xml_add(attrd_op, PCMK__XA_SRC, crm_system_name); + crm_xml_add(attrd_op, PCMK_XA_TASK, PCMK__ATTRD_CMD_UPDATE); crm_xml_add(attrd_op, PCMK__XA_ATTR_NAME, CRM_ATTR_PROTOCOL); crm_xml_add(attrd_op, PCMK__XA_ATTR_VALUE, ATTRD_PROTOCOL_VERSION); crm_xml_add_int(attrd_op, PCMK__XA_ATTR_IS_PRIVATE, 1); @@ -344,9 +339,9 @@ attrd_broadcast_protocol(void) gboolean attrd_send_message(crm_node_t *node, xmlNode *data, bool confirm) { - const char *op = crm_element_value(data, PCMK__XA_TASK); + const char *op = crm_element_value(data, PCMK_XA_TASK); - crm_xml_add(data, F_TYPE, T_ATTRD); + crm_xml_add(data, PCMK__XA_T, PCMK__VALUE_ATTRD); crm_xml_add(data, PCMK__XA_ATTR_VERSION, ATTRD_PROTOCOL_VERSION); /* Request a confirmation from the destination peer node (which could @@ -358,5 +353,5 @@ attrd_send_message(crm_node_t *node, xmlNode *data, bool confirm) } attrd_xml_add_writer(data); - return send_cluster_message(node, crm_msg_attrd, data, TRUE); + return pcmk__cluster_send_message(node, crm_msg_attrd, data); } diff --git a/daemons/attrd/attrd_sync.c b/daemons/attrd/attrd_sync.c index 1a6c24c..de99db2 100644 --- a/daemons/attrd/attrd_sync.c +++ b/daemons/attrd/attrd_sync.c @@ -1,5 +1,5 @@ /* - * Copyright 2022-2023 the Pacemaker project contributors + * Copyright 2022-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -9,8 +9,8 @@ #include <crm_internal.h> -#include <crm/msg_xml.h> -#include <crm/common/attrd_internal.h> +#include <crm/common/xml.h> +#include <crm/common/attrs_internal.h> #include "pacemaker-attrd.h" @@ -113,7 +113,7 @@ sync_point_str(enum attrd_sync_point sync_point) } else if (sync_point == attrd_sync_point_cluster) { return PCMK__VALUE_CLUSTER; } else { - return "unknown"; + return PCMK_VALUE_UNKNOWN; } } @@ -145,13 +145,7 @@ attrd_add_client_to_waitlist(pcmk__request_t *request) waitlist = pcmk__intkey_table(free_waitlist_node); } - wl = calloc(sizeof(struct waitlist_node), 1); - - CRM_ASSERT(wl != NULL); - - wl->client_id = strdup(request->ipc_client->id); - - CRM_ASSERT(wl->client_id); + wl = pcmk__assert_alloc(1, sizeof(struct waitlist_node)); if (pcmk__str_eq(sync_point, PCMK__VALUE_LOCAL, pcmk__str_none)) { wl->sync_point = attrd_sync_point_local; @@ -162,6 +156,7 @@ attrd_add_client_to_waitlist(pcmk__request_t *request) return; } + wl->client_id = pcmk__str_copy(request->ipc_client->id); wl->ipc_id = request->ipc_id; wl->flags = request->flags; @@ -175,7 +170,7 @@ attrd_add_client_to_waitlist(pcmk__request_t *request) /* And then add the key to the request XML so we can uniquely identify * it when it comes time to issue the ACK. */ - crm_xml_add_int(request->xml, XML_LRM_ATTR_CALLID, waitlist_client); + crm_xml_add_int(request->xml, PCMK__XA_CALL_ID, waitlist_client); } /*! @@ -245,7 +240,7 @@ attrd_ack_waitlist_clients(enum attrd_sync_point sync_point, const xmlNode *xml) return; } - if (crm_element_value_int(xml, XML_LRM_ATTR_CALLID, &callid) == -1) { + if (crm_element_value_int(xml, PCMK__XA_CALL_ID, &callid) == -1) { crm_warn("Could not get callid from request XML"); return; } @@ -316,7 +311,8 @@ attrd_request_sync_point(xmlNode *xml) CRM_CHECK(xml != NULL, return NULL); if (xml->children != NULL) { - xmlNode *child = pcmk__xe_match(xml, XML_ATTR_OP, PCMK__XA_ATTR_SYNC_POINT, NULL); + xmlNode *child = pcmk__xe_first_child(xml, PCMK_XE_OP, + PCMK__XA_ATTR_SYNC_POINT, NULL); if (child) { return crm_element_value(child, PCMK__XA_ATTR_SYNC_POINT); @@ -381,8 +377,10 @@ confirmation_timeout_cb(gpointer data) } crm_trace("Timed out waiting for confirmations for client %s", client->id); - pcmk__ipc_send_ack(client, action->ipc_id, action->flags | crm_ipc_client_response, - "ack", ATTRD_PROTOCOL_VERSION, CRM_EX_TIMEOUT); + pcmk__ipc_send_ack(client, action->ipc_id, + action->flags|crm_ipc_client_response, + PCMK__XE_ACK, ATTRD_PROTOCOL_VERSION, + CRM_EX_TIMEOUT); g_hash_table_iter_remove(&iter); crm_trace("%d requests now in expected confirmations table", g_hash_table_size(expected_confirmations)); @@ -486,7 +484,7 @@ attrd_expect_confirmations(pcmk__request_t *request, attrd_confirmation_action_f expected_confirmations = pcmk__intkey_table((GDestroyNotify) free_action); } - if (crm_element_value_int(request->xml, XML_LRM_ATTR_CALLID, &callid) == -1) { + if (crm_element_value_int(request->xml, PCMK__XA_CALL_ID, &callid) == -1) { crm_err("Could not get callid from xml"); return; } @@ -499,23 +497,17 @@ attrd_expect_confirmations(pcmk__request_t *request, attrd_confirmation_action_f g_hash_table_iter_init(&iter, peer_protocol_vers); while (g_hash_table_iter_next(&iter, &host, &ver)) { if (ATTRD_SUPPORTS_CONFIRMATION(GPOINTER_TO_INT(ver))) { - char *s = strdup((char *) host); - - CRM_ASSERT(s != NULL); - respondents = g_list_prepend(respondents, s); + respondents = g_list_prepend(respondents, + pcmk__str_copy((char *) host)); } } - action = calloc(1, sizeof(struct confirmation_action)); - CRM_ASSERT(action != NULL); + action = pcmk__assert_alloc(1, sizeof(struct confirmation_action)); action->respondents = respondents; action->fn = fn; - action->xml = copy_xml(request->xml); - - action->client_id = strdup(request->ipc_client->id); - CRM_ASSERT(action->client_id != NULL); - + action->xml = pcmk__xml_copy(NULL, request->xml); + action->client_id = pcmk__str_copy(request->ipc_client->id); action->ipc_id = request->ipc_id; action->flags = request->flags; diff --git a/daemons/attrd/attrd_utils.c b/daemons/attrd/attrd_utils.c index 341ee1a..2d0bc76 100644 --- a/daemons/attrd/attrd_utils.c +++ b/daemons/attrd/attrd_utils.c @@ -1,5 +1,5 @@ /* - * Copyright 2004-2023 the Pacemaker project contributors + * Copyright 2004-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -19,7 +19,7 @@ #include <crm/crm.h> #include <crm/common/ipc_internal.h> #include <crm/common/mainloop.h> -#include <crm/msg_xml.h> +#include <crm/common/xml.h> #include "pacemaker-attrd.h" @@ -58,11 +58,12 @@ attrd_clear_requesting_shutdown(void) * \internal * \brief Check whether local attribute manager is shutting down * - * \param[in] if_requested Also consider presence of "shutdown" attribute + * \param[in] if_requested If \c true, also consider presence of + * \c PCMK__NODE_ATTR_SHUTDOWN attribute * * \return \c true if local attribute manager has begun shutdown sequence * or (if \p if_requested is \c true) whether local node has a nonzero - * "shutdown" attribute set, otherwise \c false + * \c PCMK__NODE_ATTR_SHUTDOWN attribute set, otherwise \c false * \note Most callers should pass \c false for \p if_requested, because the * attribute manager needs to continue performing while the controller is * shutting down, and even needs to be eligible for election in case all @@ -175,8 +176,8 @@ attrd_expand_value(const char *value, const char *old_value) } int_value += offset; - if (int_value > INFINITY) { - int_value = INFINITY; + if (int_value > PCMK_SCORE_INFINITY) { + int_value = PCMK_SCORE_INFINITY; } return int_value; } @@ -204,7 +205,7 @@ attrd_failure_regex(regex_t *regex, const char *rsc, const char *op, /* Create a pattern that matches desired attributes */ if (rsc == NULL) { - pattern = strdup(ATTRD_RE_CLEAR_ALL); + pattern = pcmk__str_copy(ATTRD_RE_CLEAR_ALL); } else if (op == NULL) { pattern = crm_strdup_printf(ATTRD_RE_CLEAR_ONE, rsc); } else { @@ -238,7 +239,6 @@ attrd_free_attribute(gpointer data) free(a->id); free(a->set_id); free(a->set_type); - free(a->uuid); free(a->user); mainloop_timer_del(a->timer); @@ -288,11 +288,9 @@ attrd_update_minimum_protocol_ver(const char *host, const char *value) pcmk__scan_min_int(value, &ver, 0); if (ver > 0) { - char *host_name = strdup(host); - /* Record the peer attrd's protocol version. */ - CRM_ASSERT(host_name != NULL); - g_hash_table_insert(peer_protocol_vers, host_name, GINT_TO_POINTER(ver)); + g_hash_table_insert(peer_protocol_vers, pcmk__str_copy(host), + GINT_TO_POINTER(ver)); /* If the protocol version is a new minimum, record it as such. */ if (minimum_protocol_version == -1 || ver < minimum_protocol_version) { @@ -302,24 +300,3 @@ attrd_update_minimum_protocol_ver(const char *host, const char *value) } } } - -void -attrd_copy_xml_attributes(xmlNode *src, xmlNode *dest) -{ - /* Copy attributes from the wrapper parent node into the child node. - * We can't just use copy_in_properties because we want to skip any - * attributes that are already set on the child. For instance, if - * we were told to use a specific node, there will already be a node - * attribute on the child. Copying the parent's node attribute over - * could result in the wrong value. - */ - for (xmlAttrPtr a = pcmk__xe_first_attr(src); a != NULL; a = a->next) { - const char *p_name = (const char *) a->name; - const char *p_value = ((a == NULL) || (a->children == NULL)) ? NULL : - (const char *) a->children->content; - - if (crm_element_value(dest, p_name) == NULL) { - crm_xml_add(dest, p_name, p_value); - } - } -} diff --git a/daemons/attrd/pacemaker-attrd.c b/daemons/attrd/pacemaker-attrd.c index 8091c5b..4ae5c8a 100644 --- a/daemons/attrd/pacemaker-attrd.c +++ b/daemons/attrd/pacemaker-attrd.c @@ -1,5 +1,5 @@ /* - * Copyright 2013-2023 the Pacemaker project contributors + * Copyright 2013-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -20,8 +20,6 @@ #include <fcntl.h> #include <crm/crm.h> -#include <crm/cib/internal.h> -#include <crm/msg_xml.h> #include <crm/pengine/rules.h> #include <crm/common/cmdline_internal.h> #include <crm/common/iso8601.h> @@ -31,7 +29,7 @@ #include <crm/common/xml.h> #include <crm/cluster/internal.h> -#include <crm/common/attrd_internal.h> +#include <crm/common/attrs_internal.h> #include "pacemaker-attrd.h" #define SUMMARY "daemon for managing Pacemaker node attributes" @@ -59,7 +57,7 @@ static pcmk__supported_format_t formats[] = { }; lrmd_t *the_lrmd = NULL; -crm_cluster_t *attrd_cluster = NULL; +pcmk_cluster_t *attrd_cluster = NULL; crm_trigger_t *attrd_config_read = NULL; crm_exit_t attrd_exit_status = CRM_EX_OK; @@ -136,7 +134,7 @@ main(int argc, char **argv) // Open additional log files pcmk__add_logfiles(log_files, out); - crm_log_init(T_ATTRD, LOG_INFO, TRUE, FALSE, argc, argv, FALSE); + crm_log_init(PCMK__VALUE_ATTRD, LOG_INFO, TRUE, FALSE, argc, argv, FALSE); crm_notice("Starting Pacemaker node attribute manager%s", stand_alone ? " in standalone mode" : ""); diff --git a/daemons/attrd/pacemaker-attrd.h b/daemons/attrd/pacemaker-attrd.h index b8929a7..76faf04 100644 --- a/daemons/attrd/pacemaker-attrd.h +++ b/daemons/attrd/pacemaker-attrd.h @@ -1,5 +1,5 @@ /* - * Copyright 2013-2023 the Pacemaker project contributors + * Copyright 2013-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -16,7 +16,7 @@ #include <crm/cluster.h> #include <crm/cluster/election_internal.h> #include <crm/common/messages_internal.h> -#include <crm/cib/internal.h> +#include <crm/cib/cib_types.h> /* * Legacy attrd (all pre-1.1.11 Pacemaker versions, plus all versions when used @@ -31,9 +31,8 @@ * -------- --------- ------------------- * 1 1.1.11 PCMK__ATTRD_CMD_UPDATE (PCMK__XA_ATTR_NAME only), * PCMK__ATTRD_CMD_PEER_REMOVE, PCMK__ATTRD_CMD_REFRESH, - * PCMK__ATTRD_CMD_FLUSH, PCMK__ATTRD_CMD_SYNC, - * PCMK__ATTRD_CMD_SYNC_RESPONSE - * 1 1.1.13 PCMK__ATTRD_CMD_UPDATE (with PCMK__XA_ATTR_PATTERN), + * PCMK__ATTRD_CMD_FLUSH, PCMK__ATTRD_CMD_SYNC_RESPONSE + * 1 1.1.13 PCMK__ATTRD_CMD_UPDATE (with PCMK__XA_ATTR_REGEX), * PCMK__ATTRD_CMD_QUERY * 1 1.1.15 PCMK__ATTRD_CMD_UPDATE_BOTH, * PCMK__ATTRD_CMD_UPDATE_DELAY @@ -42,14 +41,16 @@ * 4 2.1.5 Multiple attributes can be updated in a single IPC * message * 5 2.1.5 Peers can request confirmation of a sent message + * 6 2.1.7 PCMK__ATTRD_CMD_PEER_REMOVE supports PCMK__XA_REAP */ -#define ATTRD_PROTOCOL_VERSION "5" +#define ATTRD_PROTOCOL_VERSION "6" #define ATTRD_SUPPORTS_MULTI_MESSAGE(x) ((x) >= 4) #define ATTRD_SUPPORTS_CONFIRMATION(x) ((x) >= 5) -#define attrd_send_ack(client, id, flags) \ - pcmk__ipc_send_ack((client), (id), (flags), "ack", ATTRD_PROTOCOL_VERSION, CRM_EX_INDETERMINATE) +#define attrd_send_ack(client, id, flags) \ + pcmk__ipc_send_ack((client), (id), (flags), PCMK__XE_ACK, \ + ATTRD_PROTOCOL_VERSION, CRM_EX_INDETERMINATE) void attrd_init_mainloop(void); void attrd_run_mainloop(void); @@ -65,6 +66,7 @@ void attrd_ipc_fini(void); int attrd_cib_connect(int max_retry); void attrd_cib_disconnect(void); void attrd_cib_init(void); +void attrd_cib_erase_transient_attrs(const char *node); bool attrd_value_needs_expansion(const char *value); int attrd_expand_value(const char *value, const char *old_value); @@ -116,47 +118,75 @@ void attrd_declare_winner(void); void attrd_remove_voter(const crm_node_t *peer); void attrd_xml_add_writer(xmlNode *xml); -typedef struct attribute_s { - char *uuid; /* TODO: Remove if at all possible */ - char *id; - char *set_id; - char *set_type; - GHashTable *values; - int update; - int timeout_ms; - - /* TODO: refactor these three as a bitmask */ - bool changed; /* whether attribute value has changed since last write */ - bool unknown_peer_uuids; /* whether we know we're missing a peer uuid */ - gboolean is_private; /* whether to keep this attribute out of the CIB */ - - mainloop_timer_t *timer; - - char *user; - - gboolean force_write; /* Flag for updating attribute by ignoring delay */ +enum attrd_attr_flags { + attrd_attr_none = 0U, + attrd_attr_changed = (1U << 0), // Attribute value has changed since last write + attrd_attr_uuid_missing = (1U << 1), // Whether we know we're missing a peer UUID + attrd_attr_is_private = (1U << 2), // Whether to keep this attribute out of the CIB + attrd_attr_force_write = (1U << 3), // Update attribute by ignoring delay +}; +typedef struct attribute_s { + char *id; // Attribute name + char *set_type; // PCMK_XE_INSTANCE_ATTRIBUTES or PCMK_XE_UTILIZATION + char *set_id; // Set's XML ID to use when writing + char *user; // ACL user to use for CIB writes + int update; // Call ID of pending write + int timeout_ms; // How long to wait for more changes before writing + uint32_t flags; // Group of enum attrd_attr_flags + GHashTable *values; // Key: node name, value: attribute_value_t + mainloop_timer_t *timer; // Timer to use for timeout_ms } attribute_t; +#define attrd_set_attr_flags(attr, flags_to_set) do { \ + (attr)->flags = pcmk__set_flags_as(__func__, __LINE__, \ + LOG_TRACE, "Value for attribute", (attr)->id, \ + (attr)->flags, (flags_to_set), #flags_to_set); \ + } while (0) + +#define attrd_clear_attr_flags(attr, flags_to_clear) do { \ + (attr)->flags = pcmk__clear_flags_as(__func__, __LINE__, \ + LOG_TRACE, "Value for attribute", (attr)->id, \ + (attr)->flags, (flags_to_clear), #flags_to_clear); \ + } while (0) + +enum attrd_value_flags { + attrd_value_none = 0U, + attrd_value_remote = (1U << 0), // Value is for Pacemaker Remote node + attrd_value_from_peer = (1U << 1), // Value is from peer sync response +}; + typedef struct attribute_value_s { - uint32_t nodeid; - gboolean is_remote; - char *nodename; - char *current; - char *requested; - gboolean seen; + char *nodename; // Node that this value is for + char *current; // Attribute value + char *requested; // Value specified in pending CIB write, if any + uint32_t nodeid; // Cluster node ID of node that this value is for + uint32_t flags; // Group of attrd_value_flags } attribute_value_t; -extern crm_cluster_t *attrd_cluster; +#define attrd_set_value_flags(attr_value, flags_to_set) do { \ + (attr_value)->flags = pcmk__set_flags_as(__func__, __LINE__, \ + LOG_TRACE, "Value for node", (attr_value)->nodename, \ + (attr_value)->flags, (flags_to_set), #flags_to_set); \ + } while (0) + +#define attrd_clear_value_flags(attr_value, flags_to_clear) do { \ + (attr_value)->flags = pcmk__clear_flags_as(__func__, __LINE__, \ + LOG_TRACE, "Value for node", (attr_value)->nodename, \ + (attr_value)->flags, (flags_to_clear), #flags_to_clear); \ + } while (0) + +extern pcmk_cluster_t *attrd_cluster; extern GHashTable *attributes; extern GHashTable *peer_protocol_vers; #define CIB_OP_TIMEOUT_S 120 int attrd_cluster_connect(void); +void attrd_broadcast_value(const attribute_t *a, const attribute_value_t *v); void attrd_peer_update(const crm_node_t *peer, xmlNode *xml, const char *host, bool filter); -void attrd_peer_sync(crm_node_t *peer, xmlNode *xml); +void attrd_peer_sync(crm_node_t *peer); void attrd_peer_remove(const char *host, bool uncache, const char *source); void attrd_peer_clear_failure(pcmk__request_t *request); void attrd_peer_sync_response(const crm_node_t *peer, bool peer_won, @@ -176,6 +206,8 @@ void attrd_clear_value_seen(void); void attrd_free_attribute(gpointer data); void attrd_free_attribute_value(gpointer data); attribute_t *attrd_populate_attribute(xmlNode *xml, const char *attr); +char *attrd_set_id(const attribute_t *attr, const char *node_state_id); +char *attrd_nvpair_id(const attribute_t *attr, const char *node_state_id); enum attrd_write_options { attrd_write_changed = 0, @@ -214,8 +246,6 @@ void attrd_remove_client_from_waitlist(pcmk__client_t *client); const char *attrd_request_sync_point(xmlNode *xml); bool attrd_request_has_sync_point(xmlNode *xml); -void attrd_copy_xml_attributes(xmlNode *src, xmlNode *dest); - extern gboolean stand_alone; #endif /* PACEMAKER_ATTRD__H */ |