diff --git a/attrd/attrd_alerts.c b/attrd/attrd_alerts.c index c86d431e6b..d2fedfdcd9 100644 --- a/attrd/attrd_alerts.c +++ b/attrd/attrd_alerts.c @@ -1,342 +1,383 @@ /* * Copyright (C) 2015 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include "attrd_alerts.h" #include #include #include #include static GListPtr attrd_alert_list = NULL; +static void +attrd_lrmd_callback(lrmd_event_data_t * op) +{ + CRM_CHECK(op != NULL, return); + switch (op->type) { + case lrmd_event_disconnect: + crm_info("Lost connection to LRMD"); + attrd_lrmd_disconnect(); + break; + default: + break; + } +} + lrmd_t * -attrd_lrmd_connect(int max_attempts, void callback(lrmd_event_data_t * op)) +attrd_lrmd_connect() { int ret = -ENOTCONN; int fails = 0; + const unsigned int max_attempts = 10; if (!the_lrmd) { the_lrmd = lrmd_api_new(); } - the_lrmd->cmds->set_callback(the_lrmd, callback); + the_lrmd->cmds->set_callback(the_lrmd, attrd_lrmd_callback); while (fails < max_attempts) { ret = the_lrmd->cmds->connect(the_lrmd, T_ATTRD, NULL); if (ret != pcmk_ok) { fails++; crm_debug("Could not connect to LRMD, %d tries remaining", (max_attempts - fails)); /* @TODO We don't want to block here with sleep, but we should wait * some time between connection attempts. We could possibly add a * timer with a callback, but then we'd likely need an alert queue. */ } else { break; } } if (ret != pcmk_ok) { attrd_lrmd_disconnect(); } return the_lrmd; } void attrd_lrmd_disconnect() { if (the_lrmd) { lrmd_t *conn = the_lrmd; the_lrmd = NULL; /* in case we're called recursively */ lrmd_api_delete(conn); /* will disconnect if necessary */ } } static void config_query_callback(xmlNode * msg, int call_id, int rc, xmlNode * output, void *user_data) { crm_time_t *now = crm_time_new(NULL); xmlNode *crmalerts = NULL; if (rc != pcmk_ok) { crm_err("Local CIB query resulted in an error: %s", pcmk_strerror(rc)); goto bail; } crmalerts = output; if ((crmalerts) && (crm_element_name(crmalerts)) && (strcmp(crm_element_name(crmalerts), XML_CIB_TAG_ALERTS) != 0)) { crmalerts = first_named_child(crmalerts, XML_CIB_TAG_ALERTS); } if (!crmalerts) { crm_err("Local CIB query for " XML_CIB_TAG_ALERTS " section failed"); goto bail; } pe_free_alert_list(attrd_alert_list); attrd_alert_list = pe_unpack_alerts(crmalerts); bail: crm_time_free(now); } gboolean attrd_read_options(gpointer user_data) { int call_id; if (the_cib) { call_id = the_cib->cmds->query(the_cib, "//" XML_CIB_TAG_ALERTS, NULL, cib_xpath | cib_scope_local); the_cib->cmds->register_callback_full(the_cib, call_id, 120, FALSE, NULL, "config_query_callback", config_query_callback, free); crm_trace("Querying the CIB... call %d", call_id); } else { crm_err("Querying the CIB...CIB connection not active"); } return TRUE; } void attrd_cib_updated_cb(const char *event, xmlNode * msg) { int rc = -1; int format= 1; xmlNode *patchset = get_message_xml(msg, F_CIB_UPDATE_RESULT); xmlNode *change = NULL; xmlXPathObject *xpathObj = NULL; CRM_CHECK(msg != NULL, return); crm_element_value_int(msg, F_CIB_RC, &rc); if (rc < pcmk_ok) { crm_trace("Filter rc=%d (%s)", rc, pcmk_strerror(rc)); return; } crm_element_value_int(patchset, "format", &format); if (format == 1) { if ((xpathObj = xpath_search( msg, "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_ADDED "//" XML_CIB_TAG_ALERTS )) != NULL) { freeXpathObject(xpathObj); mainloop_set_trigger(attrd_config_read); } } else if (format == 2) { for (change = __xml_first_child(patchset); change != NULL; change = __xml_next(change)) { const char *xpath = crm_element_value(change, XML_DIFF_PATH); if (xpath == NULL) { continue; } if (!strstr(xpath, "/" XML_TAG_CIB "/" XML_CIB_TAG_CONFIGURATION "/" XML_CIB_TAG_ALERTS)) { /* this is not a change to an existing alerts section */ xmlNode *section = NULL; const char *name = NULL; if ((strcmp(xpath, "/" XML_TAG_CIB "/" XML_CIB_TAG_CONFIGURATION) != 0) || ((section = __xml_first_child(change)) == NULL) || ((name = crm_element_name(section)) == NULL) || (strcmp(name, XML_CIB_TAG_ALERTS) != 0)) { /* this is not a newly added alerts section */ continue; } } mainloop_set_trigger(attrd_config_read); break; } } else { crm_warn("Unknown patch format: %d", format); } } -static void -attrd_lrmd_callback(lrmd_event_data_t * op) +/*! + * \internal + * \brief Execute alert agents for an event + * + * \param[in] alert_list Alerts to execute + * \param[in] lrmd_connect_func Function that returns an LRMD connection + * \param[in] kind Type of event that is being alerted for + * \param[in] attr_name If crm_alert_attribute, the attribute name + * \param[in,out] params Environment variables to pass to agents + * + * \retval pcmk_ok on success + * \retval -1 if some alerts failed + * \retval -2 if all alerts failed + */ +static int +exec_alert_list(GList *alert_list, lrmd_t *(*lrmd_connect_func)(void), + enum crm_alert_flags kind, const char *attr_name, + lrmd_key_value_t *params) { - CRM_CHECK(op != NULL, return); - switch (op->type) { - case lrmd_event_disconnect: - crm_info("Lost connection to LRMD"); - attrd_lrmd_disconnect(); - break; - default: - break; - } -} + bool any_success = FALSE, any_failure = FALSE; + const char *kind_s = crm_alert_flag2text(kind); + crm_time_hr_t *now = NULL; -static int -exec_alerts(enum crm_alert_flags kind, const char *attribute_name, - lrmd_key_value_t *params) -{ - int rc = pcmk_ok; - GListPtr l; - crm_time_hr_t *now = crm_time_hr_new(NULL); - - params = lrmd_set_alert_key_to_lrmd_params(params, CRM_alert_kind, - crm_alert_flag2text(kind)); + params = lrmd_set_alert_key_to_lrmd_params(params, CRM_alert_kind, kind_s); params = lrmd_set_alert_key_to_lrmd_params(params, CRM_alert_version, VERSION); - for (l = g_list_first(attrd_alert_list); l; l = g_list_next(l)) { - crm_alert_entry_t *entry = (crm_alert_entry_t *)(l->data); - char *timestamp = NULL; - lrmd_key_value_t * copy_params = NULL; - lrmd_key_value_t *head, *p; + for (GList *iter = g_list_first(alert_list); iter; iter = g_list_next(iter)) { + crm_alert_entry_t *entry = (crm_alert_entry_t *)(iter->data); + lrmd_key_value_t *copy_params = NULL; + lrmd_key_value_t *head = NULL; + lrmd_t *lrmd_conn = NULL; + int rc; if (is_not_set(entry->flags, kind)) { crm_trace("Filtering unwanted %s alert to %s via %s", - crm_alert_flag2text(kind), entry->recipient, entry->id); + kind_s, entry->recipient, entry->id); continue; } if ((kind == crm_alert_attribute) - && !crm_is_target_alert(entry->select_attribute_name, attribute_name)) { + && !crm_is_target_alert(entry->select_attribute_name, attr_name)) { crm_trace("Filtering unwanted attribute '%s' alert to %s via %s", - attribute_name, entry->recipient, entry->id); + attr_name, entry->recipient, entry->id); continue; } + if (now == NULL) { + now = crm_time_hr_new(NULL); + } crm_info("Sending %s alert via %s to %s", - crm_alert_flag2text(kind), entry->id, entry->recipient); + kind_s, entry->id, entry->recipient); - /* Because there is a parameter to turn into every transmission, Copy a parameter. */ - head = params; - while (head) { - p = head->next; + /* Make a copy of the parameters, because each alert will be unique */ + for (head = params; head != NULL; head = head->next) { copy_params = lrmd_key_value_add(copy_params, head->key, head->value); - head = p; } copy_params = lrmd_set_alert_key_to_lrmd_params(copy_params, CRM_alert_recipient, entry->recipient); if (now) { - timestamp = crm_time_format_hr(entry->tstamp_format, now); + char *timestamp = crm_time_format_hr(entry->tstamp_format, now); + if (timestamp) { copy_params = lrmd_set_alert_key_to_lrmd_params(copy_params, CRM_alert_timestamp, timestamp); free(timestamp); } } copy_params = lrmd_set_alert_envvar_to_lrmd_params(copy_params, entry); - if (the_lrmd == NULL) { - the_lrmd = attrd_lrmd_connect(10, attrd_lrmd_callback); - if (the_lrmd == NULL) { - crm_warn("Cannot send alerts: LRMD connection not active"); - rc = -ENOTCONN; - goto done; - } + lrmd_conn = (*lrmd_connect_func)(); + if (lrmd_conn == NULL) { + crm_warn("Cannot send alerts: No LRMD connection"); + any_failure = TRUE; + goto done; } - rc = the_lrmd->cmds->exec_alert(the_lrmd, entry->id, entry->path, - entry->timeout, copy_params); + + rc = lrmd_conn->cmds->exec_alert(lrmd_conn, entry->id, entry->path, + entry->timeout, copy_params); if (rc < 0) { crm_err("Could not execute alert %s: %s " CRM_XS " rc=%d", entry->id, pcmk_strerror(rc), rc); + any_failure = TRUE; + } else { + any_success = TRUE; } } done: if (now) { free(now); } - return rc; + if (any_failure) { + return (any_success? -1 : -2); + } + return pcmk_ok; } +/*! + * \internal + * \brief Send an alert for a node attribute change + * + * \param[in] alert_list List of alert agents to execute + * \param[in] lrmd_connect_func Function that returns an LRMD connection + * \param[in] node Name of node with attribute change + * \param[in] nodeid Node ID of node with attribute change + * \param[in] attr_name Name of attribute that changed + * \param[in] attr_value New value of attribute that changed + * + * \retval pcmk_ok on success + * \retval -1 if some alert agents failed + * \retval -2 if all alert agents failed + */ int -attrd_send_alerts(const char *node, uint32_t nodeid, - const char *attribute_name, const char *attribute_value) +lrmd_send_attribute_alert(GList *alert_list, lrmd_t *(*lrmd_connect_func)(void), + const char *node, uint32_t nodeid, + const char *attr_name, const char *attr_value) { - int ret = pcmk_ok; + int rc = pcmk_ok; lrmd_key_value_t *params = NULL; char *nodeid_s; + if (alert_list == NULL) { + return pcmk_ok; + } params = lrmd_set_alert_key_to_lrmd_params(params, CRM_alert_node, node); nodeid_s = crm_itoa(nodeid); - params = lrmd_set_alert_key_to_lrmd_params(params, CRM_alert_nodeid, nodeid_s); + params = lrmd_set_alert_key_to_lrmd_params(params, CRM_alert_nodeid, + nodeid_s); free(nodeid_s); - params = lrmd_set_alert_key_to_lrmd_params(params, CRM_alert_attribute_name, attribute_name); + params = lrmd_set_alert_key_to_lrmd_params(params, CRM_alert_attribute_name, + attr_name); params = lrmd_set_alert_key_to_lrmd_params(params, CRM_alert_attribute_value, - attribute_value); - - ret = exec_alerts(crm_alert_attribute, attribute_name, params); + attr_value); - if (params) { - lrmd_key_value_freeall(params); - } - - return ret; + rc = exec_alert_list(alert_list, lrmd_connect_func, crm_alert_attribute, + attr_name, params); + lrmd_key_value_freeall(params); + return rc; } #if HAVE_ATOMIC_ATTRD 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); a_v->nodeid = v->nodeid; a_v->nodename = strdup(v->nodename); if (v->current != NULL) { a_v->current = strdup(v->current); } g_hash_table_replace(t, a_v->nodename, a_v); } void send_alert_attributes_value(attribute_t *a, GHashTable *t) { - int call_id = 0; + int rc = 0; attribute_value_t *at = NULL; GHashTableIter vIter; g_hash_table_iter_init(&vIter, t); while (g_hash_table_iter_next(&vIter, NULL, (gpointer *) & at)) { - call_id = attrd_send_alerts(at->nodename, at->nodeid, a->id, - at->current); - crm_trace("Sent alerts for %s[%s]=%s: call_id=%d nodeid=%d", - a->id, at->nodename, at->current, call_id, at->nodeid); + rc = lrmd_send_attribute_alert(attrd_alert_list, attrd_lrmd_connect, + at->nodename, at->nodeid, a->id, + at->current); + crm_trace("Sent alerts for %s[%s]=%s: nodeid=%d rc=%d", + a->id, at->nodename, at->current, at->nodeid, rc); } } #endif diff --git a/attrd/attrd_alerts.h b/attrd/attrd_alerts.h index bf4f485e83..ba79e1b63c 100644 --- a/attrd/attrd_alerts.h +++ b/attrd/attrd_alerts.h @@ -1,42 +1,44 @@ /* * Copyright (C) 2015 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef ATTRD_ALERT__H # define ATTRD_ALERT__H # include # include # include extern cib_t *the_cib; extern lrmd_t *the_lrmd; extern crm_trigger_t *attrd_config_read; -lrmd_t *attrd_lrmd_connect(int max_retry, void callback(lrmd_event_data_t * op)); +lrmd_t * attrd_lrmd_connect(void); void attrd_lrmd_disconnect(void); gboolean attrd_read_options(gpointer user_data); void attrd_cib_updated_cb(const char *event, xmlNode * msg); void attrd_enable_alerts(const char *script, const char *target); -int attrd_send_alerts(const char *node, uint32_t nodeid, - const char *attribute_name, const char *attribute_value); +int lrmd_send_attribute_alert(GList *alert_list, + lrmd_t *(*lrmd_connect_func)(void), + const char *node, uint32_t nodeid, + const char *attr_name, const char *attr_value); #if HAVE_ATOMIC_ATTRD void set_alert_attribute_value(GHashTable *t, attribute_value_t *v); void send_alert_attributes_value(attribute_t *a, GHashTable *t); #endif #endif diff --git a/attrd/legacy.c b/attrd/legacy.c index 285d751e0e..6a0a3b5b93 100644 --- a/attrd/legacy.c +++ b/attrd/legacy.c @@ -1,1247 +1,1245 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "attrd_alerts.h" #define OPTARGS "hV" #if SUPPORT_HEARTBEAT ll_cluster_t *attrd_cluster_conn; #endif char *attrd_uname = NULL; char *attrd_uuid = NULL; uint32_t attrd_nodeid = 0; GHashTable *attr_hash = NULL; cib_t *the_cib = NULL; lrmd_t *the_lrmd = NULL; crm_trigger_t *attrd_config_read = NULL; /* Convenience macro for registering a CIB callback. * Check the_cib != NULL before using. */ #define register_cib_callback(call_id, data, fn, free_fn) \ the_cib->cmds->register_callback_full(the_cib, call_id, 120, FALSE, \ data, #fn, fn, free_fn) typedef struct attr_hash_entry_s { char *uuid; char *id; char *set; char *section; char *value; char *stored_value; int timeout; char *dampen; guint timer_id; char *user; } attr_hash_entry_t; void attrd_local_callback(xmlNode * msg); gboolean attrd_timer_callback(void *user_data); gboolean attrd_trigger_update(attr_hash_entry_t * hash_entry); void attrd_perform_update(attr_hash_entry_t * hash_entry); static void update_local_attr(xmlNode *msg, attr_hash_entry_t *hash_entry); static void free_hash_entry(gpointer data) { attr_hash_entry_t *entry = data; if (entry == NULL) { return; } free(entry->id); free(entry->set); free(entry->dampen); free(entry->section); free(entry->uuid); free(entry->value); free(entry->stored_value); free(entry->user); free(entry); } /* Exit code means? */ static int32_t attrd_ipc_dispatch(qb_ipcs_connection_t * c, void *data, size_t size) { uint32_t id = 0; uint32_t flags = 0; crm_client_t *client = crm_client_get(c); xmlNode *msg = crm_ipcs_recv(client, data, size, &id, &flags); crm_ipcs_send_ack(client, id, flags, "ack", __FUNCTION__, __LINE__); if (msg == NULL) { crm_debug("No msg from %d (%p)", crm_ipcs_client_pid(c), c); return 0; } #if ENABLE_ACL CRM_ASSERT(client->user != NULL); crm_acl_get_set_user(msg, F_ATTRD_USER, client->user); #endif crm_trace("Processing msg from %d (%p)", crm_ipcs_client_pid(c), c); crm_log_xml_trace(msg, __FUNCTION__); attrd_local_callback(msg); free_xml(msg); return 0; } static void usage(const char *cmd, int exit_status) { FILE *stream; stream = exit_status ? stderr : stdout; fprintf(stream, "usage: %s [-srkh] [-c configure file]\n", cmd); /* fprintf(stream, "\t-d\tsets debug level\n"); */ /* fprintf(stream, "\t-s\tgets daemon status\n"); */ /* fprintf(stream, "\t-r\trestarts daemon\n"); */ /* fprintf(stream, "\t-k\tstops daemon\n"); */ /* fprintf(stream, "\t-h\thelp message\n"); */ fflush(stream); crm_exit(exit_status); } static void stop_attrd_timer(attr_hash_entry_t * hash_entry) { if (hash_entry != NULL && hash_entry->timer_id != 0) { crm_trace("Stopping %s timer", hash_entry->id); g_source_remove(hash_entry->timer_id); hash_entry->timer_id = 0; } } static void log_hash_entry(int level, attr_hash_entry_t * entry, const char *text) { do_crm_log(level, "%s: Set: %s, Name: %s, Value: %s, Timeout: %s", text, entry->section, entry->id, entry->value, entry->dampen); } static attr_hash_entry_t * find_hash_entry(xmlNode * msg) { const char *value = NULL; const char *attr = crm_element_value(msg, F_ATTRD_ATTRIBUTE); attr_hash_entry_t *hash_entry = NULL; if (attr == NULL) { crm_info("Ignoring message with no attribute name"); return NULL; } hash_entry = g_hash_table_lookup(attr_hash, attr); if (hash_entry == NULL) { /* create one and add it */ crm_info("Creating hash entry for %s", attr); hash_entry = calloc(1, sizeof(attr_hash_entry_t)); hash_entry->id = strdup(attr); g_hash_table_insert(attr_hash, hash_entry->id, hash_entry); hash_entry = g_hash_table_lookup(attr_hash, attr); CRM_CHECK(hash_entry != NULL, return NULL); } value = crm_element_value(msg, F_ATTRD_SET); if (value != NULL) { free(hash_entry->set); hash_entry->set = strdup(value); crm_debug("\t%s->set: %s", attr, value); } value = crm_element_value(msg, F_ATTRD_SECTION); if (value == NULL) { value = XML_CIB_TAG_STATUS; } free(hash_entry->section); hash_entry->section = strdup(value); crm_trace("\t%s->section: %s", attr, value); value = crm_element_value(msg, F_ATTRD_DAMPEN); if (value != NULL) { free(hash_entry->dampen); hash_entry->dampen = strdup(value); hash_entry->timeout = crm_get_msec(value); crm_trace("\t%s->timeout: %s", attr, value); } #if ENABLE_ACL free(hash_entry->user); hash_entry->user = NULL; value = crm_element_value(msg, F_ATTRD_USER); if (value != NULL) { hash_entry->user = strdup(value); crm_trace("\t%s->user: %s", attr, value); } #endif log_hash_entry(LOG_DEBUG_2, hash_entry, "Found (and updated) entry:"); return hash_entry; } /*! * \internal * \brief Clear failure-related attributes for local node * * \param[in] xml XML of ATTRD_OP_CLEAR_FAILURE request */ static void local_clear_failure(xmlNode *xml) { const char *rsc = crm_element_value(xml, F_ATTRD_RESOURCE); const char *what = rsc? rsc : "all resources"; const char *op = crm_element_value(xml, F_ATTRD_OPERATION); const char *interval_s = crm_element_value(xml, F_ATTRD_INTERVAL); int interval = crm_get_interval(interval_s); regex_t regex; GHashTableIter iter; attr_hash_entry_t *hash_entry = NULL; if (attrd_failure_regex(®ex, rsc, op, interval) != pcmk_ok) { crm_info("Ignoring invalid request to clear %s", (rsc? rsc : "all resources")); return; } crm_debug("Clearing %s locally", what); /* Make sure value is not set, so we delete */ if (crm_element_value(xml, F_ATTRD_VALUE)) { crm_xml_replace(xml, F_ATTRD_VALUE, NULL); } g_hash_table_iter_init(&iter, attr_hash); while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &hash_entry)) { if (regexec(®ex, hash_entry->id, 0, NULL, 0) == 0) { crm_trace("Matched %s when clearing %s", hash_entry->id, what); update_local_attr(xml, hash_entry); } } regfree(®ex); } static void remote_clear_callback(xmlNode *msg, int call_id, int rc, xmlNode *output, void *user_data) { if (rc == 0) { crm_debug("Successfully cleared failures using %s", (char *) user_data); } else { crm_notice("Failed to clear failures: %s " CRM_XS " call=%d xpath=%s rc=%d", pcmk_strerror(rc), call_id, (char *) user_data, rc); } } /* xpath component to match an id attribute (format takes remote node name) */ #define XPATH_ID "[@" XML_ATTR_UUID "='%s']" /* Define the start of an xpath to match a remote node transient attribute * (argument must be either an empty string to match for all remote nodes, * or XPATH_ID to match for a single remote node) */ #define XPATH_REMOTE_ATTR(x) "/" XML_TAG_CIB "/" XML_CIB_TAG_STATUS \ "/" XML_CIB_TAG_STATE "[@" XML_NODE_IS_REMOTE "='true']" x \ "/" XML_TAG_TRANSIENT_NODEATTRS "/" XML_TAG_ATTR_SETS "/" XML_CIB_TAG_NVPAIR /* xpath component to match an attribute name exactly */ #define XPATH_NAME_IS(x) "@" XML_NVPAIR_ATTR_NAME "='" x "'" /* xpath component to match an attribute name by prefix */ #define XPATH_NAME_START(x) "starts-with(@" XML_NVPAIR_ATTR_NAME ", '" x "')" /* xpath ending to clear all resources */ #define XPATH_CLEAR_ALL \ "[" XPATH_NAME_START(CRM_FAIL_COUNT_PREFIX "-") \ " or " XPATH_NAME_START(CRM_LAST_FAILURE_PREFIX "-") "]" /* xpath ending to clear all operations for one resource * (format takes resource name x 4) * * @COMPAT attributes set < 1.1.17: * also match older attributes that do not have the operation part */ #define XPATH_CLEAR_ONE \ "[" XPATH_NAME_IS(CRM_FAIL_COUNT_PREFIX "-%s") \ " or " XPATH_NAME_IS(CRM_LAST_FAILURE_PREFIX "-%s") \ " or " XPATH_NAME_START(CRM_FAIL_COUNT_PREFIX "-%s#") \ " or " XPATH_NAME_START(CRM_LAST_FAILURE_PREFIX "-%s#") "]" /* xpath ending to clear one operation for one resource * (format takes resource name x 2, resource name + operation + interval x 2) * * @COMPAT attributes set < 1.1.17: * also match older attributes that do not have the operation part */ #define XPATH_CLEAR_OP \ "[" XPATH_NAME_IS(CRM_FAIL_COUNT_PREFIX "-%s") \ " or " XPATH_NAME_IS(CRM_LAST_FAILURE_PREFIX "-%s") \ " or " XPATH_NAME_IS(CRM_FAIL_COUNT_PREFIX "-%s#%s_%d") \ " or " XPATH_NAME_IS(CRM_LAST_FAILURE_PREFIX "-%s#%s_%d") "]" /*! * \internal * \brief Clear failure-related attributes for Pacemaker Remote node(s) * * \param[in] xml XML of ATTRD_OP_CLEAR_FAILURE request */ static void remote_clear_failure(xmlNode *xml) { const char *rsc = crm_element_value(xml, F_ATTRD_RESOURCE); const char *host = crm_element_value(xml, F_ATTRD_HOST); const char *op = crm_element_value(xml, F_ATTRD_OPERATION); int rc = pcmk_ok; char *xpath; if (the_cib == NULL) { crm_info("Ignoring request to clear %s on %s because not connected to CIB", (rsc? rsc : "all resources"), (host? host: "all remote nodes")); return; } /* Build an xpath to clear appropriate attributes */ if (rsc == NULL) { /* No resource specified, clear all resources */ if (host == NULL) { xpath = crm_strdup_printf(XPATH_REMOTE_ATTR("") XPATH_CLEAR_ALL); } else { xpath = crm_strdup_printf(XPATH_REMOTE_ATTR(XPATH_ID) XPATH_CLEAR_ALL, host); } } else if (op == NULL) { /* Resource but no operation specified, clear all operations */ if (host == NULL) { xpath = crm_strdup_printf(XPATH_REMOTE_ATTR("") XPATH_CLEAR_ONE, rsc, rsc, rsc, rsc); } else { xpath = crm_strdup_printf(XPATH_REMOTE_ATTR(XPATH_ID) XPATH_CLEAR_ONE, host, rsc, rsc, rsc, rsc); } } else { /* Resource and operation specified */ const char *interval_s = crm_element_value(xml, F_ATTRD_INTERVAL); int interval = crm_get_interval(interval_s); if (host == NULL) { xpath = crm_strdup_printf(XPATH_REMOTE_ATTR("") XPATH_CLEAR_OP, rsc, rsc, rsc, op, interval, rsc, op, interval); } else { xpath = crm_strdup_printf(XPATH_REMOTE_ATTR(XPATH_ID) XPATH_CLEAR_OP, host, rsc, rsc, rsc, op, interval, rsc, op, interval); } } crm_trace("Clearing attributes matching %s", xpath); rc = the_cib->cmds->delete(the_cib, xpath, NULL, cib_xpath|cib_multiple); register_cib_callback(rc, xpath, remote_clear_callback, free); } static void process_xml_request(xmlNode *xml) { attr_hash_entry_t *hash_entry = NULL; const char *from = crm_element_value(xml, F_ORIG); const char *op = crm_element_value(xml, F_ATTRD_TASK); const char *host = crm_element_value(xml, F_ATTRD_HOST); const char *ignore = crm_element_value(xml, F_ATTRD_IGNORE_LOCALLY); if (host && safe_str_eq(host, attrd_uname)) { crm_info("%s relayed from %s", (op? op : "Request"), from); attrd_local_callback(xml); } else if (safe_str_eq(op, ATTRD_OP_PEER_REMOVE)) { CRM_CHECK(host != NULL, return); crm_debug("Removing %s from peer caches for %s", host, from); crm_remote_peer_cache_remove(host); reap_crm_member(0, host); } else if (safe_str_eq(op, ATTRD_OP_CLEAR_FAILURE)) { local_clear_failure(xml); } else if ((ignore == NULL) || safe_str_neq(from, attrd_uname)) { crm_trace("%s message from %s", op, from); hash_entry = find_hash_entry(xml); stop_attrd_timer(hash_entry); attrd_perform_update(hash_entry); } } #if SUPPORT_HEARTBEAT static void attrd_ha_connection_destroy(gpointer user_data) { crm_trace("Invoked"); if (attrd_shutting_down()) { /* we signed out, so this is expected */ crm_info("Heartbeat disconnection complete"); return; } crm_crit("Lost connection to heartbeat service!"); if (attrd_mainloop_running()) { attrd_quit_mainloop(); return; } crm_exit(pcmk_ok); } static void attrd_ha_callback(HA_Message * msg, void *private_data) { xmlNode *xml = convert_ha_message(NULL, msg, __FUNCTION__); process_xml_request(xml); free_xml(xml); } #endif #if SUPPORT_COROSYNC static void attrd_cs_dispatch(cpg_handle_t handle, const struct cpg_name *groupName, uint32_t nodeid, uint32_t pid, void *msg, size_t msg_len) { uint32_t kind = 0; xmlNode *xml = NULL; const char *from = NULL; char *data = pcmk_message_common_cs(handle, nodeid, pid, msg, &kind, &from); if(data == NULL) { return; } if (kind == crm_class_cluster) { xml = string2xml(data); if (xml == NULL) { crm_err("Bad message received: '%.120s'", data); } } if (xml != NULL) { /* crm_xml_add_int(xml, F_SEQ, wrapper->id); */ crm_xml_add(xml, F_ORIG, from); process_xml_request(xml); free_xml(xml); } free(data); } static void attrd_cs_destroy(gpointer unused) { if (attrd_shutting_down()) { /* we signed out, so this is expected */ crm_info("Corosync disconnection complete"); return; } crm_crit("Lost connection to Corosync service!"); if (attrd_mainloop_running()) { attrd_quit_mainloop(); return; } crm_exit(EINVAL); } #endif static void attrd_cib_connection_destroy(gpointer user_data) { cib_t *conn = user_data; conn->cmds->signoff(conn); /* Ensure IPC is cleaned up */ if (attrd_shutting_down()) { crm_info("Connection to the CIB terminated..."); } else { /* eventually this will trigger a reconnect, not a shutdown */ crm_err("Connection to the CIB terminated..."); crm_exit(ENOTCONN); } return; } static void update_for_hash_entry(gpointer key, gpointer value, gpointer user_data) { attr_hash_entry_t *entry = value; if (entry->value != NULL || entry->stored_value != NULL) { attrd_timer_callback(value); } } static void local_update_for_hash_entry(gpointer key, gpointer value, gpointer user_data) { attr_hash_entry_t *entry = value; if (entry->timer_id == 0) { crm_trace("Performing local-only update after replace for %s", entry->id); attrd_perform_update(entry); /* } else { * just let the timer expire and attrd_timer_callback() will do the right thing */ } } static void do_cib_replaced(const char *event, xmlNode * msg) { crm_info("Updating all attributes after %s event", event); g_hash_table_foreach(attr_hash, local_update_for_hash_entry, NULL); } static gboolean cib_connect(void *user_data) { static int attempts = 1; static int max_retry = 20; gboolean was_err = FALSE; static cib_t *local_conn = NULL; if (local_conn == NULL) { local_conn = cib_new(); } if (was_err == FALSE) { int rc = -ENOTCONN; if (attempts < max_retry) { crm_debug("CIB signon attempt %d", attempts); rc = local_conn->cmds->signon(local_conn, T_ATTRD, cib_command); } if (rc != pcmk_ok && attempts > max_retry) { crm_err("Signon to CIB failed: %s", pcmk_strerror(rc)); was_err = TRUE; } else if (rc != pcmk_ok) { attempts++; return TRUE; } } crm_info("Connected to the CIB after %d signon attempts", attempts); if (was_err == FALSE) { int rc = local_conn->cmds->set_connection_dnotify(local_conn, attrd_cib_connection_destroy); if (rc != pcmk_ok) { crm_err("Could not set dnotify callback"); was_err = TRUE; } } if (was_err == FALSE) { if (pcmk_ok != local_conn->cmds->add_notify_callback(local_conn, T_CIB_REPLACE_NOTIFY, do_cib_replaced)) { crm_err("Could not set CIB notification callback"); was_err = TRUE; } if (was_err == FALSE) { if (pcmk_ok != local_conn->cmds->add_notify_callback(local_conn, T_CIB_DIFF_NOTIFY, attrd_cib_updated_cb)) { crm_err("Could not set CIB notification callback (update)"); was_err = TRUE; } } attrd_config_read = mainloop_add_trigger(G_PRIORITY_HIGH, attrd_read_options, NULL); /* Reading of cib(Alert section) after the start */ mainloop_set_trigger(attrd_config_read); } if (was_err) { crm_err("Aborting startup"); crm_exit(DAEMON_RESPAWN_STOP); } the_cib = local_conn; crm_info("Sending full refresh now that we're connected to the cib"); g_hash_table_foreach(attr_hash, local_update_for_hash_entry, NULL); return FALSE; } int main(int argc, char **argv) { int flag = 0; int argerr = 0; crm_cluster_t cluster; gboolean was_err = FALSE; qb_ipcs_connection_t *c = NULL; qb_ipcs_service_t *ipcs = NULL; crm_log_init(T_ATTRD, LOG_NOTICE, TRUE, FALSE, argc, argv, FALSE); mainloop_add_signal(SIGTERM, attrd_shutdown); while ((flag = getopt(argc, argv, OPTARGS)) != EOF) { switch (flag) { case 'V': crm_bump_log_level(argc, argv); break; case 'h': /* Help message */ usage(T_ATTRD, EX_OK); break; default: ++argerr; break; } } if (optind > argc) { ++argerr; } if (argerr) { usage(T_ATTRD, EX_USAGE); } attr_hash = g_hash_table_new_full(crm_str_hash, g_str_equal, NULL, free_hash_entry); crm_info("Starting up"); if (was_err == FALSE) { #if SUPPORT_COROSYNC if (is_openais_cluster()) { cluster.destroy = attrd_cs_destroy; cluster.cpg.cpg_deliver_fn = attrd_cs_dispatch; cluster.cpg.cpg_confchg_fn = pcmk_cpg_membership; } #endif #if SUPPORT_HEARTBEAT if (is_heartbeat_cluster()) { cluster.hb_conn = NULL; cluster.hb_dispatch = attrd_ha_callback; cluster.destroy = attrd_ha_connection_destroy; } #endif if (FALSE == crm_cluster_connect(&cluster)) { crm_err("HA Signon failed"); was_err = TRUE; } attrd_uname = cluster.uname; attrd_uuid = cluster.uuid; attrd_nodeid = cluster.nodeid; #if SUPPORT_HEARTBEAT attrd_cluster_conn = cluster.hb_conn; #endif } crm_info("Cluster connection active"); if (was_err == FALSE) { attrd_init_ipc(&ipcs, attrd_ipc_dispatch); } crm_info("Accepting attribute updates"); attrd_init_mainloop(); if (0 == g_timeout_add_full(G_PRIORITY_LOW + 1, 5000, cib_connect, NULL, NULL)) { crm_info("Adding timer failed"); was_err = TRUE; } if (was_err) { crm_err("Aborting startup"); return 100; } crm_notice("Starting mainloop..."); attrd_run_mainloop(); crm_notice("Exiting..."); #if SUPPORT_HEARTBEAT if (is_heartbeat_cluster()) { attrd_cluster_conn->llc_ops->signoff(attrd_cluster_conn, TRUE); attrd_cluster_conn->llc_ops->delete(attrd_cluster_conn); } #endif c = qb_ipcs_connection_first_get(ipcs); while (c != NULL) { qb_ipcs_connection_t *last = c; c = qb_ipcs_connection_next_get(ipcs, last); /* There really shouldn't be anyone connected at this point */ crm_notice("Disconnecting client %p, pid=%d...", last, crm_ipcs_client_pid(last)); qb_ipcs_disconnect(last); qb_ipcs_connection_unref(last); } qb_ipcs_destroy(ipcs); attrd_lrmd_disconnect(); if (the_cib) { the_cib->cmds->signoff(the_cib); cib_delete(the_cib); } g_hash_table_destroy(attr_hash); free(attrd_uuid); return crm_exit(pcmk_ok); } struct attrd_callback_s { char *attr; char *value; }; /*! * \internal * \brief Free an attrd callback structure */ static void free_attrd_callback(void *user_data) { struct attrd_callback_s *data = user_data; free(data->attr); free(data->value); free(data); } static void attrd_cib_callback(xmlNode * msg, int call_id, int rc, xmlNode * output, void *user_data) { attr_hash_entry_t *hash_entry = NULL; struct attrd_callback_s *data = user_data; if (data->value == NULL && rc == -ENXIO) { rc = pcmk_ok; } else if (call_id < 0) { crm_warn("Update %s=%s failed: %s", data->attr, data->value, pcmk_strerror(call_id)); return; } switch (rc) { case pcmk_ok: crm_debug("Update %d for %s=%s passed", call_id, data->attr, data->value); hash_entry = g_hash_table_lookup(attr_hash, data->attr); if (hash_entry) { free(hash_entry->stored_value); hash_entry->stored_value = NULL; if (data->value != NULL) { hash_entry->stored_value = strdup(data->value); } } break; case -pcmk_err_diff_failed: /* When an attr changes while the CIB is syncing */ case -ETIME: /* When an attr changes while there is a DC election */ case -ENXIO: /* When an attr changes while the CIB is syncing a * newer config from a node that just came up */ crm_warn("Update %d for %s=%s failed: %s", call_id, data->attr, data->value, pcmk_strerror(rc)); break; default: crm_err("Update %d for %s=%s failed: %s", call_id, data->attr, data->value, pcmk_strerror(rc)); } } void attrd_perform_update(attr_hash_entry_t * hash_entry) { int rc = pcmk_ok; struct attrd_callback_s *data = NULL; const char *user_name = NULL; if (hash_entry == NULL) { return; } else if (the_cib == NULL) { crm_info("Delaying operation %s=%s: cib not connected", hash_entry->id, crm_str(hash_entry->value)); return; } #if ENABLE_ACL if (hash_entry->user) { user_name = hash_entry->user; crm_trace("Performing request from user '%s'", hash_entry->user); } #endif if (hash_entry->value == NULL) { /* delete the attr */ rc = delete_attr_delegate(the_cib, cib_none, hash_entry->section, attrd_uuid, NULL, hash_entry->set, hash_entry->uuid, hash_entry->id, NULL, FALSE, user_name); if (rc >= 0 && hash_entry->stored_value) { crm_notice("Sent delete %d: node=%s, attr=%s, id=%s, set=%s, section=%s", rc, attrd_uuid, hash_entry->id, hash_entry->uuid ? hash_entry->uuid : "", hash_entry->set, hash_entry->section); } else if (rc < 0 && rc != -ENXIO) { crm_notice ("Delete operation failed: node=%s, attr=%s, id=%s, set=%s, section=%s: %s (%d)", attrd_uuid, hash_entry->id, hash_entry->uuid ? hash_entry->uuid : "", hash_entry->set, hash_entry->section, pcmk_strerror(rc), rc); } else { crm_trace("Sent delete %d: node=%s, attr=%s, id=%s, set=%s, section=%s", rc, attrd_uuid, hash_entry->id, hash_entry->uuid ? hash_entry->uuid : "", hash_entry->set, hash_entry->section); } - attrd_send_alerts(attrd_uname, attrd_nodeid, hash_entry->id, - hash_entry->value); - } else { /* send update */ rc = update_attr_delegate(the_cib, cib_none, hash_entry->section, attrd_uuid, NULL, hash_entry->set, hash_entry->uuid, hash_entry->id, hash_entry->value, FALSE, user_name, NULL); if (rc < 0) { crm_notice("Could not update %s=%s: %s (%d)", hash_entry->id, hash_entry->value, pcmk_strerror(rc), rc); } else if (safe_str_neq(hash_entry->value, hash_entry->stored_value)) { crm_notice("Sent update %d: %s=%s", rc, hash_entry->id, hash_entry->value); } else { crm_trace("Sent update %d: %s=%s", rc, hash_entry->id, hash_entry->value); } - attrd_send_alerts(attrd_uname, attrd_nodeid, hash_entry->id, - hash_entry->value); } + lrmd_send_attribute_alert(attrd_alert_list, attrd_lrmd_connect, attrd_uname, + attrd_nodeid, hash_entry->id, hash_entry->value); data = calloc(1, sizeof(struct attrd_callback_s)); data->attr = strdup(hash_entry->id); if (hash_entry->value != NULL) { data->value = strdup(hash_entry->value); } register_cib_callback(rc, data, attrd_cib_callback, free_attrd_callback); return; } /*! * \internal * \brief Expand attribute values that use "++" or "+=" * * \param[in] value Attribute value to expand * \param[in] old_value Previous value of attribute * * \return Newly allocated string with expanded value, or NULL if not expanded */ static char * expand_attr_value(const char *value, const char *old_value) { char *expanded = NULL; if (attrd_value_needs_expansion(value)) { expanded = crm_itoa(attrd_expand_value(value, old_value)); } return expanded; } /*! * \internal * \brief Update a single node attribute for this node * * \param[in] msg XML message with update * \param[in,out] hash_entry Node attribute structure */ static void update_local_attr(xmlNode *msg, attr_hash_entry_t *hash_entry) { const char *value = crm_element_value(msg, F_ATTRD_VALUE); char *expanded = NULL; if (hash_entry->uuid == NULL) { const char *key = crm_element_value(msg, F_ATTRD_KEY); if (key) { hash_entry->uuid = strdup(key); } } crm_debug("Request to update %s (%s) to %s from %s (stored: %s)", hash_entry->id, (hash_entry->uuid? hash_entry->uuid : "no uuid"), value, hash_entry->value, hash_entry->stored_value); if (safe_str_eq(value, hash_entry->value) && safe_str_eq(value, hash_entry->stored_value)) { crm_trace("Ignoring non-change"); return; } else if (value) { expanded = expand_attr_value(value, hash_entry->value); if (expanded) { crm_info("Expanded %s=%s to %s", hash_entry->id, value, expanded); value = expanded; } } if (safe_str_eq(value, hash_entry->value) && hash_entry->timer_id) { /* We're already waiting to set this value */ free(expanded); return; } free(hash_entry->value); hash_entry->value = NULL; if (value != NULL) { hash_entry->value = (expanded? expanded : strdup(value)); crm_debug("New value of %s is %s", hash_entry->id, value); } stop_attrd_timer(hash_entry); if (hash_entry->timeout > 0) { hash_entry->timer_id = g_timeout_add(hash_entry->timeout, attrd_timer_callback, hash_entry); } else { attrd_trigger_update(hash_entry); } } /*! * \internal * \brief Log the result of a CIB operation for a remote attribute * * \param[in] msg ignored * \param[in] id CIB operation ID * \param[in] rc CIB operation result * \param[in] output ignored * \param[in] data User-friendly string describing operation */ static void remote_attr_callback(xmlNode *msg, int id, int rc, xmlNode *output, void *data) { if (rc == pcmk_ok) { crm_debug("%s succeeded " CRM_XS " call=%d", (char *) data, id); } else { crm_notice("%s failed: %s " CRM_XS " call=%d rc=%d", (char *) data, pcmk_strerror(rc), id, rc); } } /*! * \internal * \brief Update a Pacemaker Remote node attribute via CIB only * * \param[in] host Pacemaker Remote node name * \param[in] name Attribute name * \param[in] value New attribute value * \param[in] section CIB section to update (defaults to status if NULL) * \param[in] user_name User to perform operation as * * \note Legacy attrd does not track remote node attributes, so such requests * are only sent to the CIB. This means that dampening is ignored, and * updates for the same attribute submitted to different nodes cannot be * reliably ordered. This is not ideal, but allows remote nodes to * be supported, and should be acceptable in practice. */ static void update_remote_attr(const char *host, const char *name, const char *value, const char *section, const char *user_name) { int rc = pcmk_ok; char *desc; if (value == NULL) { desc = crm_strdup_printf("Delete of %s in %s for %s", name, section, host); } else { desc = crm_strdup_printf("Update of %s=%s in %s for %s", name, value, section, host); } if (name == NULL) { rc = -EINVAL; } else if (the_cib == NULL) { rc = -ENOTCONN; } if (rc != pcmk_ok) { remote_attr_callback(NULL, rc, rc, NULL, desc); free(desc); return; } if (value == NULL) { rc = delete_attr_delegate(the_cib, cib_none, section, host, NULL, NULL, NULL, name, NULL, FALSE, user_name); } else { rc = update_attr_delegate(the_cib, cib_none, section, host, NULL, NULL, NULL, name, value, FALSE, user_name, "remote"); } - attrd_send_alerts(host, 0, name, (value? value : "")); + lrmd_send_attribute_alert(attrd_alert_list, attrd_lrmd_connect, host, 0, + name, (value? value : "")); crm_trace("%s submitted as CIB call %d", desc, rc); register_cib_callback(rc, desc, remote_attr_callback, free); } /*! * \internal * \brief Handle a client request to clear failures * * \param[in] msg XML of request * * \note Handling is according to the host specified in the request: * NULL: Relay to all cluster nodes (which do local_clear_failure()) * and also handle all remote nodes here, using remote_clear_failure(); * Our uname: Handle here, using local_clear_failure(); * Known peer: Relay to that peer, which (via process_xml_message() then * attrd_local_callback()) comes back here as previous case; * Unknown peer: Handle here as remote node, using remote_clear_failure() */ static void attrd_client_clear_failure(xmlNode *msg) { const char *host = crm_element_value(msg, F_ATTRD_HOST); if (host == NULL) { /* Clear failure on all cluster nodes */ crm_notice("Broadcasting request to clear failure on all hosts"); send_cluster_message(NULL, crm_msg_attrd, msg, FALSE); /* Clear failure on all remote nodes */ remote_clear_failure(msg); } else if (safe_str_eq(host, attrd_uname)) { local_clear_failure(msg); } else { int is_remote = FALSE; crm_node_t *peer = crm_find_peer(0, host); crm_element_value_int(msg, F_ATTRD_IS_REMOTE, &is_remote); if (is_remote || (peer == NULL)) { /* If request is not for a known cluster node, assume remote */ remote_clear_failure(msg); } else { /* Relay request to proper node */ crm_notice("Relaying request to clear failure to %s", host); send_cluster_message(peer, crm_msg_attrd, msg, FALSE); } } } void attrd_local_callback(xmlNode * msg) { attr_hash_entry_t *hash_entry = NULL; const char *from = crm_element_value(msg, F_ORIG); const char *op = crm_element_value(msg, F_ATTRD_TASK); const char *attr = crm_element_value(msg, F_ATTRD_ATTRIBUTE); const char *pattern = crm_element_value(msg, F_ATTRD_REGEX); const char *value = crm_element_value(msg, F_ATTRD_VALUE); const char *host = crm_element_value(msg, F_ATTRD_HOST); int is_remote = FALSE; crm_element_value_int(msg, F_ATTRD_IS_REMOTE, &is_remote); if (safe_str_eq(op, ATTRD_OP_REFRESH)) { crm_notice("Sending full refresh (origin=%s)", from); g_hash_table_foreach(attr_hash, update_for_hash_entry, NULL); return; } else if (safe_str_eq(op, ATTRD_OP_PEER_REMOVE)) { if (host) { crm_notice("Broadcasting removal of peer %s", host); send_cluster_message(NULL, crm_msg_attrd, msg, FALSE); } return; } else if (safe_str_eq(op, ATTRD_OP_CLEAR_FAILURE)) { attrd_client_clear_failure(msg); return; } else if (op && safe_str_neq(op, ATTRD_OP_UPDATE)) { crm_notice("Ignoring unsupported %s request from %s", op, from); return; } /* Handle requests for Pacemaker Remote nodes specially */ if (host && is_remote) { const char *section = crm_element_value(msg, F_ATTRD_SECTION); const char *user_name = crm_element_value(msg, F_ATTRD_USER); if (section == NULL) { section = XML_CIB_TAG_STATUS; } if ((attr == NULL) && (pattern != NULL)) { /* Attribute(s) specified by regular expression */ /* @TODO query, iterate and update_remote_attr() for matches? */ crm_notice("Update of %s for %s failed: regular expressions " "are not supported with Pacemaker Remote nodes", pattern, host); } else { /* Single attribute specified by exact name */ update_remote_attr(host, attr, value, section, user_name); } return; } /* Redirect requests for another cluster node to that node */ if (host != NULL && safe_str_neq(host, attrd_uname)) { send_cluster_message(crm_get_peer(0, host), crm_msg_attrd, msg, FALSE); return; } if (attr != NULL) { /* Single attribute specified by exact name */ crm_debug("%s message from %s: %s=%s", op, from, attr, crm_str(value)); hash_entry = find_hash_entry(msg); if (hash_entry != NULL) { update_local_attr(msg, hash_entry); } } else if (pattern != NULL) { /* Attribute(s) specified by regular expression */ regex_t regex; GHashTableIter iter; if (regcomp(®ex, pattern, REG_EXTENDED|REG_NOSUB)) { crm_err("Update from %s failed: invalid pattern %s", from, pattern); return; } crm_debug("%s message from %s: %s=%s", op, from, pattern, crm_str(value)); g_hash_table_iter_init(&iter, attr_hash); while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &hash_entry)) { int rc = regexec(®ex, hash_entry->id, 0, NULL, 0); if (rc == 0) { crm_trace("Attribute %s matches %s", hash_entry->id, pattern); update_local_attr(msg, hash_entry); } } regfree(®ex); } else { crm_info("Ignoring message with no attribute name or expression"); } } gboolean attrd_timer_callback(void *user_data) { stop_attrd_timer(user_data); attrd_trigger_update(user_data); return TRUE; /* Always return true, removed cleanly by stop_attrd_timer() */ } gboolean attrd_trigger_update(attr_hash_entry_t * hash_entry) { xmlNode *msg = NULL; /* send HA message to everyone */ crm_notice("Sending flush op to all hosts for: %s (%s)", hash_entry->id, crm_str(hash_entry->value)); log_hash_entry(LOG_DEBUG_2, hash_entry, "Sending flush op to all hosts for:"); msg = create_xml_node(NULL, __FUNCTION__); crm_xml_add(msg, F_TYPE, T_ATTRD); crm_xml_add(msg, F_ORIG, attrd_uname); crm_xml_add(msg, F_ATTRD_TASK, "flush"); crm_xml_add(msg, F_ATTRD_ATTRIBUTE, hash_entry->id); crm_xml_add(msg, F_ATTRD_SET, hash_entry->set); crm_xml_add(msg, F_ATTRD_SECTION, hash_entry->section); crm_xml_add(msg, F_ATTRD_DAMPEN, hash_entry->dampen); crm_xml_add(msg, F_ATTRD_VALUE, hash_entry->value); #if ENABLE_ACL if (hash_entry->user) { crm_xml_add(msg, F_ATTRD_USER, hash_entry->user); } #endif if (hash_entry->timeout <= 0) { crm_xml_add(msg, F_ATTRD_IGNORE_LOCALLY, hash_entry->value); attrd_perform_update(hash_entry); } send_cluster_message(NULL, crm_msg_attrd, msg, FALSE); free_xml(msg); return TRUE; }