diff --git a/daemons/attrd/pacemaker-attrd.c b/daemons/attrd/pacemaker-attrd.c index 15c6a8075e..141ed8277f 100644 --- a/daemons/attrd/pacemaker-attrd.c +++ b/daemons/attrd/pacemaker-attrd.c @@ -1,441 +1,441 @@ /* * Copyright 2013-2019 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU General Public License version 2 * or later (GPLv2+) WITHOUT ANY WARRANTY. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include "pacemaker-attrd.h" lrmd_t *the_lrmd = NULL; crm_cluster_t *attrd_cluster = NULL; crm_trigger_t *attrd_config_read = NULL; static crm_exit_t attrd_exit_status = CRM_EX_OK; static void attrd_cpg_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 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); } free_xml(xml); free(data); } static void attrd_cpg_destroy(gpointer unused) { if (attrd_shutting_down()) { crm_info("Corosync disconnection complete"); } else { crm_crit("Lost connection to cluster layer, shutting down"); attrd_exit_status = CRM_EX_DISCONNECT; attrd_shutdown(0); } } static void attrd_cib_replaced_cb(const char *event, xmlNode * msg) { if (attrd_shutting_down()) { return; } if (attrd_election_won()) { crm_notice("Updating all attributes after %s event", event); write_attributes(TRUE, FALSE); } // Check for changes in alerts mainloop_set_trigger(attrd_config_read); } static void attrd_cib_destroy_cb(gpointer user_data) { cib_t *conn = user_data; conn->cmds->signoff(conn); /* Ensure IPC is cleaned up */ if (attrd_shutting_down()) { crm_info("Connection disconnection complete"); } else { /* eventually this should trigger a reconnect, not a shutdown */ crm_crit("Lost connection to the CIB manager, shutting down"); attrd_exit_status = CRM_EX_DISCONNECT; attrd_shutdown(0); } return; } static void attrd_erase_cb(xmlNode *msg, int call_id, int rc, xmlNode *output, void *user_data) { do_crm_log_unlikely((rc? LOG_NOTICE : LOG_DEBUG), "Cleared transient attributes: %s " CRM_XS " xpath=%s rc=%d", pcmk_strerror(rc), (char *) user_data, rc); } #define XPATH_TRANSIENT "//node_state[@uname='%s']/" XML_TAG_TRANSIENT_NODEATTRS /*! * \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. * * \todo If pacemaker-attrd respawns after crashing (see PCMK_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(). */ static void attrd_erase_attrs() { int call_id; char *xpath = crm_strdup_printf(XPATH_TRANSIENT, attrd_cluster->uname); crm_info("Clearing transient attributes from CIB " CRM_XS " xpath=%s", xpath); call_id = the_cib->cmds->remove(the_cib, xpath, NULL, cib_quorum_override | cib_xpath); the_cib->cmds->register_callback_full(the_cib, call_id, 120, FALSE, xpath, "attrd_erase_cb", attrd_erase_cb, free); } static int attrd_cib_connect(int max_retry) { static int attempts = 0; int rc = -ENOTCONN; the_cib = cib_new(); if (the_cib == NULL) { return -ENOTCONN; } do { if(attempts > 0) { sleep(attempts); } attempts++; crm_debug("Connection attempt %d to the CIB manager", attempts); rc = the_cib->cmds->signon(the_cib, T_ATTRD, cib_command); } while(rc != pcmk_ok && attempts < max_retry); if (rc != pcmk_ok) { crm_err("Connection to the CIB manager failed: %s " CRM_XS " rc=%d", pcmk_strerror(rc), rc); goto cleanup; } crm_debug("Connected to the CIB manager after %d attempts", attempts); rc = the_cib->cmds->set_connection_dnotify(the_cib, attrd_cib_destroy_cb); if (rc != pcmk_ok) { crm_err("Could not set disconnection callback"); goto cleanup; } rc = the_cib->cmds->add_notify_callback(the_cib, T_CIB_REPLACE_NOTIFY, attrd_cib_replaced_cb); if(rc != pcmk_ok) { crm_err("Could not set CIB notification callback"); goto cleanup; } rc = the_cib->cmds->add_notify_callback(the_cib, T_CIB_DIFF_NOTIFY, attrd_cib_updated_cb); if (rc != pcmk_ok) { crm_err("Could not set CIB notification callback (update)"); goto cleanup; } return pcmk_ok; cleanup: the_cib->cmds->signoff(the_cib); cib_delete(the_cib); the_cib = NULL; return -ENOTCONN; } /*! * \internal * \brief Prepare the CIB after cluster is connected */ static void attrd_cib_init() { // We have no attribute values in memory, wipe the CIB to match attrd_erase_attrs(); // Set a trigger for reading the CIB (for the alerts section) attrd_config_read = mainloop_add_trigger(G_PRIORITY_HIGH, attrd_read_options, NULL); // Always read the CIB at start-up mainloop_set_trigger(attrd_config_read); } static qb_ipcs_service_t *ipcs = NULL; 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 *xml = crm_ipcs_recv(client, data, size, &id, &flags); const char *op; if (xml == 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(xml, F_ATTRD_USER, client->user); #endif crm_trace("Processing msg from %d (%p)", crm_ipcs_client_pid(c), c); crm_log_xml_trace(xml, __FUNCTION__); op = crm_element_value(xml, F_ATTRD_TASK); if (client->name == NULL) { const char *value = crm_element_value(xml, F_ORIG); client->name = crm_strdup_printf("%s.%d", value?value:"unknown", client->pid); } if (safe_str_eq(op, ATTRD_OP_PEER_REMOVE)) { attrd_send_ack(client, id, flags); attrd_client_peer_remove(client->name, xml); } else if (safe_str_eq(op, ATTRD_OP_CLEAR_FAILURE)) { attrd_send_ack(client, id, flags); attrd_client_clear_failure(xml); } else if (safe_str_eq(op, ATTRD_OP_UPDATE)) { attrd_send_ack(client, id, flags); attrd_client_update(xml); } else if (safe_str_eq(op, ATTRD_OP_UPDATE_BOTH)) { attrd_send_ack(client, id, flags); attrd_client_update(xml); } else if (safe_str_eq(op, ATTRD_OP_UPDATE_DELAY)) { attrd_send_ack(client, id, flags); attrd_client_update(xml); } else if (safe_str_eq(op, ATTRD_OP_REFRESH)) { attrd_send_ack(client, id, flags); attrd_client_refresh(); } else if (safe_str_eq(op, ATTRD_OP_QUERY)) { /* queries will get reply, so no ack is necessary */ attrd_client_query(client, id, flags, xml); } else { crm_info("Ignoring request from client %s with unknown operation %s", client->name, op); } free_xml(xml); return 0; } void attrd_ipc_fini() { if (ipcs != NULL) { crm_client_disconnect_all(ipcs); qb_ipcs_destroy(ipcs); ipcs = NULL; } } static int attrd_cluster_connect() { attrd_cluster = calloc(1, sizeof(crm_cluster_t)); attrd_cluster->destroy = attrd_cpg_destroy; attrd_cluster->cpg.cpg_deliver_fn = attrd_cpg_dispatch; attrd_cluster->cpg.cpg_confchg_fn = pcmk_cpg_membership; crm_set_status_callback(&attrd_peer_change_cb); if (crm_cluster_connect(attrd_cluster) == FALSE) { crm_err("Cluster connection failed"); return -ENOTCONN; } return pcmk_ok; } /* *INDENT-OFF* */ static struct crm_option long_options[] = { /* Top-level Options */ {"help", 0, 0, '?', "\tThis text"}, {"verbose", 0, 0, 'V', "\tIncrease debug output"}, {0, 0, 0, 0} }; /* *INDENT-ON* */ int main(int argc, char **argv) { int flag = 0; int index = 0; int argerr = 0; crm_ipc_t *old_instance = NULL; attrd_init_mainloop(); crm_log_preinit(NULL, argc, argv); crm_set_options(NULL, "[options]", long_options, "Daemon for aggregating and atomically storing node attribute updates into the CIB"); mainloop_add_signal(SIGTERM, attrd_shutdown); while (1) { flag = crm_get_option(argc, argv, &index); if (flag == -1) break; switch (flag) { case 'V': crm_bump_log_level(argc, argv); break; case 'h': /* Help message */ crm_help(flag, CRM_EX_OK); break; default: ++argerr; break; } } if (optind > argc) { ++argerr; } if (argerr) { crm_help('?', CRM_EX_USAGE); } crm_log_init(T_ATTRD, LOG_INFO, TRUE, FALSE, argc, argv, FALSE); crm_notice("Starting Pacemaker node attribute manager"); old_instance = crm_ipc_new(T_ATTRD, 0); if (crm_ipc_connect(old_instance)) { /* IPC end-point already up */ crm_ipc_close(old_instance); crm_ipc_destroy(old_instance); crm_err("pacemaker-attrd is already active, aborting startup"); crm_exit(CRM_EX_OK); } else { /* not up or not authentic, we'll proceed either way */ crm_ipc_destroy(old_instance); old_instance = NULL; } attributes = g_hash_table_new_full(crm_str_hash, g_str_equal, NULL, free_attribute); /* Connect to the CIB before connecting to the cluster or listening for IPC. * This allows us to assume the CIB is connected whenever we process a * cluster or IPC message (which also avoids start-up race conditions). */ if (attrd_cib_connect(10) != pcmk_ok) { attrd_exit_status = CRM_EX_FATAL; goto done; } crm_info("CIB connection active"); if (attrd_cluster_connect() != pcmk_ok) { attrd_exit_status = CRM_EX_FATAL; goto done; } crm_info("Cluster connection active"); // Initialization that requires the cluster to be connected attrd_election_init(); attrd_cib_init(); /* Set a private attribute for ourselves with the protocol version we * support. This lets all nodes determine the minimum supported version * across all nodes. It also ensures that the writer learns our node name, * so it can send our attributes to the CIB. */ attrd_broadcast_protocol(); attrd_init_ipc(&ipcs, attrd_ipc_dispatch); crm_notice("Pacemaker node attribute manager successfully started and accepting connections"); attrd_run_mainloop(); done: crm_info("Shutting down attribute manager"); attrd_election_fini(); attrd_ipc_fini(); attrd_lrmd_disconnect(); attrd_cib_disconnect(); g_hash_table_destroy(attributes); crm_exit(attrd_exit_status); } diff --git a/daemons/controld/controld_attrd.c b/daemons/controld/controld_attrd.c index 8ae6992c22..cea914af36 100644 --- a/daemons/controld/controld_attrd.c +++ b/daemons/controld/controld_attrd.c @@ -1,184 +1,184 @@ /* * Copyright 2006-2019 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU General Public License version 2 * or later (GPLv2+) WITHOUT ANY WARRANTY. */ #include #include -#include +#include #include #include static crm_ipc_t *attrd_ipc = NULL; void controld_close_attrd_ipc() { if (attrd_ipc) { crm_trace("Closing connection to pacemaker-attrd"); crm_ipc_close(attrd_ipc); crm_ipc_destroy(attrd_ipc); attrd_ipc = NULL; } } static void log_attrd_error(const char *host, const char *name, const char *value, gboolean is_remote, char command, int rc) { const char *node_type = (is_remote? "Pacemaker Remote" : "cluster"); gboolean shutting_down = is_set(fsa_input_register, R_SHUTDOWN); const char *when = (shutting_down? " at shutdown" : ""); switch (command) { case 0: crm_err("Could not clear failure attributes for %s on %s node %s%s: %s " CRM_XS " rc=%d", (name? name : "all resources"), node_type, host, when, pcmk_strerror(rc), rc); break; case 'C': crm_err("Could not purge %s node %s in attribute manager%s: %s " CRM_XS " rc=%d", node_type, host, when, pcmk_strerror(rc), rc); break; case 'U': /* We weren't able to update an attribute after several retries, * so something is horribly wrong with the attribute manager or the * underlying system. */ do_crm_log(AM_I_DC? LOG_CRIT : LOG_ERR, "Could not update attribute %s=%s for %s node %s%s: %s " CRM_XS " rc=%d", name, value, node_type, host, when, pcmk_strerror(rc), rc); if (AM_I_DC) { /* We are unable to provide accurate information to the * scheduler, so allow another node to take over DC. * @TODO Should we do this unconditionally on any failure? */ crmd_exit(CRM_EX_FATAL); } else if (shutting_down) { // Fast-track shutdown since unable to request via attribute register_fsa_input(C_FSA_INTERNAL, I_FAIL, NULL); } break; } } static void update_attrd_helper(const char *host, const char *name, const char *value, const char *interval_spec, const char *user_name, gboolean is_remote_node, char command) { int rc; int attrd_opts = attrd_opt_none; if (is_remote_node) { attrd_opts |= attrd_opt_remote; } if (attrd_ipc == NULL) { attrd_ipc = crm_ipc_new(T_ATTRD, 0); } for (int attempt = 1; attempt <= 4; ++attempt) { rc = pcmk_ok; // If we're not already connected, try to connect if (crm_ipc_connected(attrd_ipc) == FALSE) { if (attempt == 1) { // Start with a clean slate crm_ipc_close(attrd_ipc); } if (crm_ipc_connect(attrd_ipc) == FALSE) { rc = errno; } crm_debug("Attribute manager connection attempt %d of 4: %s (%d)", attempt, pcmk_strerror(rc), rc); } if (rc == pcmk_ok) { rc = command? attrd_update_delegate(attrd_ipc, command, host, name, value, XML_CIB_TAG_STATUS, NULL, NULL, user_name, attrd_opts) /* No command means clear fail count (name/value is really * resource/operation) */ : attrd_clear_delegate(attrd_ipc, host, name, value, interval_spec, user_name, attrd_opts); crm_debug("Attribute manager request attempt %d of 4: %s (%d)", attempt, pcmk_strerror(rc), rc); } if (rc == pcmk_ok) { // Success, we're done break; } else if ((rc != EAGAIN) && (rc != EALREADY)) { /* EAGAIN or EALREADY indicates a temporary block, so just try * again. Otherwise, close the connection for a clean slate. */ crm_ipc_close(attrd_ipc); } /* @TODO If the attribute manager remains unavailable the entire time, * this function takes more than 6 seconds. Maybe set a timer for * retries, to let the main loop do other work. */ if (attempt < 4) { sleep(attempt); } } if (rc != pcmk_ok) { log_attrd_error(host, name, value, is_remote_node, command, rc); } } void update_attrd(const char *host, const char *name, const char *value, const char *user_name, gboolean is_remote_node) { update_attrd_helper(host, name, value, NULL, user_name, is_remote_node, 'U'); } void update_attrd_remote_node_removed(const char *host, const char *user_name) { crm_trace("Asking attribute manager to purge Pacemaker Remote node %s", host); update_attrd_helper(host, NULL, NULL, NULL, user_name, TRUE, 'C'); } void update_attrd_clear_failures(const char *host, const char *rsc, const char *op, const char *interval_spec, gboolean is_remote_node) { const char *op_desc = NULL; const char *interval_desc = NULL; const char *node_type = is_remote_node? "Pacemaker Remote" : "cluster"; if (op) { interval_desc = interval_spec? interval_spec : "nonrecurring"; op_desc = op; } else { interval_desc = "all"; op_desc = "operations"; } crm_info("Asking pacemaker-attrd to clear failure of %s %s for %s on %s node %s", interval_desc, op_desc, rsc, node_type, host); update_attrd_helper(host, rsc, op, interval_spec, NULL, is_remote_node, 0); } diff --git a/include/crm/Makefile.am b/include/crm/Makefile.am index 566e9feafc..8f11a56557 100644 --- a/include/crm/Makefile.am +++ b/include/crm/Makefile.am @@ -1,19 +1,19 @@ # # Copyright 2004-2019 the Pacemaker project contributors # # The version control history for this file may have further details. # # This source code is licensed under the GNU General Public License version 2 # or later (GPLv2+) WITHOUT ANY WARRANTY. # MAINTAINERCLEANFILES = Makefile.in headerdir=$(pkgincludedir)/crm -header_HEADERS = attrd.h cib.h cluster.h compatibility.h crm.h \ +header_HEADERS = cib.h cluster.h compatibility.h crm.h \ lrmd.h msg_xml.h services.h stonith-ng.h noinst_HEADERS = lrmd_internal.h SUBDIRS = common pengine cib fencing cluster diff --git a/include/crm/common/Makefile.am b/include/crm/common/Makefile.am index 7c1c090129..4e77e50c34 100644 --- a/include/crm/common/Makefile.am +++ b/include/crm/common/Makefile.am @@ -1,18 +1,19 @@ # # Copyright 2004-2019 the Pacemaker project contributors # # The version control history for this file may have further details. # # This source code is licensed under the GNU General Public License version 2 # or later (GPLv2+) WITHOUT ANY WARRANTY. # MAINTAINERCLEANFILES = Makefile.in headerdir=$(pkgincludedir)/crm/common header_HEADERS = xml.h ipc.h util.h iso8601.h mainloop.h logging.h results.h \ nvpair.h noinst_HEADERS = cib_secrets.h ipcs.h internal.h alerts_internal.h \ iso8601_internal.h remote_internal.h xml_internal.h \ - ipc_internal.h output.h cmdline_internal.h curses_internal.h + ipc_internal.h output.h cmdline_internal.h curses_internal.h \ + attrd_internal.h diff --git a/include/crm/attrd.h b/include/crm/common/attrd_internal.h similarity index 53% rename from include/crm/attrd.h rename to include/crm/common/attrd_internal.h index 5ec1948670..6d9169ac81 100644 --- a/include/crm/attrd.h +++ b/include/crm/common/attrd_internal.h @@ -1,47 +1,37 @@ -/* - * Copyright 2004-2018 the Pacemaker project contributors +/* + * Copyright 2004-2019 the Pacemaker project contributors * * The version control history for this file may have further details. - * - * 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 + * + * This source code is licensed under the GNU Lesser General Public License + * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. */ + #ifndef CRM_ATTRD__H # define CRM_ATTRD__H #ifdef __cplusplus extern "C" { #endif # include /* attribute options for clients to use with these functions */ #define attrd_opt_none 0x000 #define attrd_opt_remote 0x001 #define attrd_opt_private 0x002 const char *attrd_get_target(const char *name); int attrd_update_delegate(crm_ipc_t * ipc, char command, const char *host, const char *name, const char *value, const char *section, const char *set, const char *dampen, const char *user_name, int options); int attrd_clear_delegate(crm_ipc_t *ipc, const char *host, const char *resource, const char *operation, const char *interval_spec, const char *user_name, int options); #ifdef __cplusplus } #endif #endif diff --git a/lib/common/attrd_client.c b/lib/common/attrd_client.c index 7a3b2503de..750236a00f 100644 --- a/lib/common/attrd_client.c +++ b/lib/common/attrd_client.c @@ -1,306 +1,312 @@ /* - * Copyright 2011-2018 Andrew Beekhof + * Copyright 2011-2019 the Pacemaker project contributors + * + * The version control history for this file may have further details. * * This source code is licensed under the GNU Lesser General Public License * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. */ - #ifndef _GNU_SOURCE # define _GNU_SOURCE #endif #include #include #include #include -#include +#include /*! * \internal * \brief Create a generic pacemaker-attrd operation * * \param[in] user_name If not NULL, ACL user to set for operation * * \return XML of pacemaker-attrd operation */ static xmlNode * create_attrd_op(const char *user_name) { xmlNode *attrd_op = create_xml_node(NULL, __FUNCTION__); crm_xml_add(attrd_op, F_TYPE, T_ATTRD); crm_xml_add(attrd_op, F_ORIG, (crm_system_name? crm_system_name: "unknown")); #if ENABLE_ACL crm_xml_add(attrd_op, F_ATTRD_USER, user_name); #endif return attrd_op; } /*! * \internal * \brief Send an operation to pacemaker-attrd via IPC * * \param[in] ipc Connection to pacemaker-attrd (or NULL to use a local connection) * \param[in] attrd_op XML of pacemaker-attrd operation to send * * \return pcmk_ok on success, -errno otherwise */ static int send_attrd_op(crm_ipc_t *ipc, xmlNode *attrd_op) { int rc = -ENOTCONN; int max = 5; static gboolean connected = TRUE; static crm_ipc_t *local_ipc = NULL; static enum crm_ipc_flags flags = crm_ipc_flags_none; if (ipc == NULL && local_ipc == NULL) { local_ipc = crm_ipc_new(T_ATTRD, 0); flags |= crm_ipc_client_response; connected = FALSE; } if (ipc == NULL) { ipc = local_ipc; } while (max > 0) { if (connected == FALSE) { crm_info("Connecting to cluster... %d retries remaining", max); connected = crm_ipc_connect(ipc); } if (connected) { rc = crm_ipc_send(ipc, attrd_op, flags, 0, NULL); } else { crm_perror(LOG_INFO, "Connection to cluster attribute manager failed"); } if (ipc != local_ipc) { break; } else if (rc > 0) { break; } else if (rc == -EAGAIN || rc == -EALREADY) { sleep(5 - max); max--; } else { crm_ipc_close(ipc); connected = FALSE; sleep(5 - max); max--; } } if (rc > 0) { rc = pcmk_ok; } return rc; } /*! + * \internal * \brief Send a request to pacemaker-attrd * * \param[in] ipc Connection to pacemaker-attrd (or NULL to use a local connection) * \param[in] command A character indicating the type of pacemaker-attrd request: * U or v: update attribute (or refresh if name is NULL) * u: update attributes matching regular expression in name * D: delete attribute (value must be NULL) * R: refresh * B: update both attribute and its dampening * Y: update attribute dampening only * Q: query attribute * C: remove peer specified by host * \param[in] host Affect only this host (or NULL for all hosts) * \param[in] name Name of attribute to affect * \param[in] value Attribute value to set * \param[in] section Status or nodes * \param[in] set ID of attribute set to use (or NULL to choose first) * \param[in] dampen Attribute dampening to use with B/Y, and U/v if creating * \param[in] user_name ACL user to pass to pacemaker-attrd * \param[in] options Bitmask that may include: * attrd_opt_remote: host is a Pacemaker Remote node * attrd_opt_private: attribute is private (not kept in CIB) * * \return pcmk_ok if request was successfully submitted to pacemaker-attrd, else -errno */ int attrd_update_delegate(crm_ipc_t *ipc, char command, const char *host, const char *name, const char *value, const char *section, const char *set, const char *dampen, const char *user_name, int options) { int rc = pcmk_ok; const char *task = NULL; const char *name_as = NULL; const char *display_host = (host ? host : "localhost"); const char *display_command = NULL; /* for commands without name/value */ xmlNode *update = create_attrd_op(user_name); /* remap common aliases */ if (safe_str_eq(section, "reboot")) { section = XML_CIB_TAG_STATUS; } else if (safe_str_eq(section, "forever")) { section = XML_CIB_TAG_NODES; } if (name == NULL && command == 'U') { command = 'R'; } switch (command) { case 'u': task = ATTRD_OP_UPDATE; name_as = F_ATTRD_REGEX; break; case 'D': case 'U': case 'v': task = ATTRD_OP_UPDATE; name_as = F_ATTRD_ATTRIBUTE; break; case 'R': task = ATTRD_OP_REFRESH; display_command = "refresh"; break; case 'B': task = ATTRD_OP_UPDATE_BOTH; name_as = F_ATTRD_ATTRIBUTE; break; case 'Y': task = ATTRD_OP_UPDATE_DELAY; name_as = F_ATTRD_ATTRIBUTE; break; case 'Q': task = ATTRD_OP_QUERY; name_as = F_ATTRD_ATTRIBUTE; break; case 'C': task = ATTRD_OP_PEER_REMOVE; display_command = "purge"; break; } if (name_as != NULL) { if (name == NULL) { rc = -EINVAL; goto done; } crm_xml_add(update, name_as, name); } crm_xml_add(update, F_ATTRD_TASK, task); crm_xml_add(update, F_ATTRD_VALUE, value); crm_xml_add(update, F_ATTRD_DAMPEN, dampen); crm_xml_add(update, F_ATTRD_SECTION, section); crm_xml_add(update, F_ATTRD_HOST, host); crm_xml_add(update, F_ATTRD_SET, set); crm_xml_add_int(update, F_ATTRD_IS_REMOTE, is_set(options, attrd_opt_remote)); crm_xml_add_int(update, F_ATTRD_IS_PRIVATE, is_set(options, attrd_opt_private)); rc = send_attrd_op(ipc, update); done: free_xml(update); if (display_command) { crm_debug("Asked pacemaker-attrd to %s %s: %s (%d)", display_command, display_host, pcmk_strerror(rc), rc); } else { crm_debug("Asked pacemaker-attrd to update %s=%s for %s: %s (%d)", name, value, display_host, pcmk_strerror(rc), rc); } return rc; } /*! + * \internal * \brief Send a request to pacemaker-attrd to clear resource failure * * \param[in] ipc Connection to pacemaker-attrd (NULL to use local connection) * \param[in] host Affect only this host (or NULL for all hosts) * \param[in] resource Name of resource to clear (or NULL for all) * \param[in] operation Name of operation to clear (or NULL for all) * \param[in] interval_spec If operation is not NULL, its interval * \param[in] user_name ACL user to pass to pacemaker-attrd * \param[in] options attrd_opt_remote if host is a Pacemaker Remote node * * \return pcmk_ok if request was successfully submitted to pacemaker-attrd, else -errno */ int attrd_clear_delegate(crm_ipc_t *ipc, const char *host, const char *resource, const char *operation, const char *interval_spec, const char *user_name, int options) { int rc = pcmk_ok; xmlNode *clear_op = create_attrd_op(user_name); const char *interval_desc = NULL; const char *op_desc = NULL; crm_xml_add(clear_op, F_ATTRD_TASK, ATTRD_OP_CLEAR_FAILURE); crm_xml_add(clear_op, F_ATTRD_HOST, host); crm_xml_add(clear_op, F_ATTRD_RESOURCE, resource); crm_xml_add(clear_op, F_ATTRD_OPERATION, operation); crm_xml_add(clear_op, F_ATTRD_INTERVAL, interval_spec); crm_xml_add_int(clear_op, F_ATTRD_IS_REMOTE, is_set(options, attrd_opt_remote)); rc = send_attrd_op(ipc, clear_op); free_xml(clear_op); if (operation) { interval_desc = interval_spec? interval_spec : "nonrecurring"; op_desc = operation; } else { interval_desc = "all"; op_desc = "operations"; } crm_debug("Asked pacemaker-attrd to clear failure of %s %s for %s on %s: %s (%d)", interval_desc, op_desc, (resource? resource : "all resources"), (host? host : "all nodes"), pcmk_strerror(rc), rc); return rc; } #define LRM_TARGET_ENV "OCF_RESKEY_" CRM_META "_" XML_LRM_ATTR_TARGET +/*! + * \internal + */ const char * attrd_get_target(const char *name) { if(safe_str_eq(name, "auto") || safe_str_eq(name, "localhost")) { name = NULL; } if(name != NULL) { return name; } else { char *target_var = crm_meta_name(XML_RSC_ATTR_TARGET); char *phys_var = crm_meta_name(PCMK_ENV_PHYSICAL_HOST); const char *target = getenv(target_var); const char *host_physical = getenv(phys_var); // It is important to use the name by which the scheduler knows us if (host_physical && safe_str_eq(target, "host")) { name = host_physical; } else { const char *host_pcmk = getenv(LRM_TARGET_ENV); if (host_pcmk) { name = host_pcmk; } } free(target_var); free(phys_var); } // TODO? Call get_local_node_name() if name == NULL // (currently would require linkage against libcrmcluster) return name; } diff --git a/tools/attrd_updater.c b/tools/attrd_updater.c index 3dfec5c93b..9ffcb00126 100644 --- a/tools/attrd_updater.c +++ b/tools/attrd_updater.c @@ -1,378 +1,378 @@ /* * Copyright 2004-2019 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU General Public License version 2 * or later (GPLv2+) WITHOUT ANY WARRANTY. */ #include #include #include #include #include #include #include #include #include #include -#include +#include /* *INDENT-OFF* */ static struct crm_option long_options[] = { /* Top-level Options */ {"help", 0, 0, '?', "\tThis text"}, {"version", 0, 0, '$', "\tVersion information" }, {"verbose", 0, 0, 'V', "\tIncrease debug output\n"}, {"name", 1, 0, 'n', "The attribute's name"}, {"-spacer-",1, 0, '-', "\nCommands:"}, {"update", 1, 0, 'U', "Update the attribute's value in pacemaker-attrd. If this causes the value to change, it will also be updated in the cluster configuration"}, {"update-both", 1, 0, 'B', "Update the attribute's value and time to wait (dampening) in pacemaker-attrd. If this causes the value or dampening to change, the attribute will also be written to the cluster configuration, so be aware that repeatedly changing the dampening reduces its effectiveness."}, {"update-delay", 0, 0, 'Y', "Update the attribute's dampening in pacemaker-attrd (requires -d/--delay). If this causes the dampening to change, the attribute will also be written to the cluster configuration, so be aware that repeatedly changing the dampening reduces its effectiveness."}, {"query", 0, 0, 'Q', "\tQuery the attribute's value from pacemaker-attrd"}, {"delete", 0, 0, 'D', "\tDelete the attribute in pacemaker-attrd. If a value was previously set, it will also be removed from the cluster configuration"}, {"refresh", 0, 0, 'R', "\t(Advanced) Force the pacemaker-attrd daemon to resend all current values to the CIB\n"}, {"-spacer-",1, 0, '-', "\nAdditional options:"}, {"delay", 1, 0, 'd', "The time to wait (dampening) in seconds for further changes before writing"}, {"set", 1, 0, 's', "(Advanced) The attribute set in which to place the value"}, {"node", 1, 0, 'N', "Set the attribute for the named node (instead of the local one)"}, {"all", 0, 0, 'A', "Show values of the attribute for all nodes (query only)"}, /* lifetime could be implemented if there is sufficient user demand */ {"lifetime",1, 0, 'l', "(Deprecated) Lifetime of the node attribute (silently ignored by cluster)"}, {"private", 0, 0, 'p', "\tIf this creates a new attribute, never write the attribute to the CIB"}, /* Legacy options */ {"quiet", 0, 0, 'q', NULL, pcmk_option_hidden}, {"update", 1, 0, 'v', NULL, pcmk_option_hidden}, {"section", 1, 0, 'S', NULL, pcmk_option_hidden}, {0, 0, 0, 0} }; /* *INDENT-ON* */ static int do_query(const char *attr_name, const char *attr_node, gboolean query_all); static int do_update(char command, const char *attr_node, const char *attr_name, const char *attr_value, const char *attr_section, const char *attr_set, const char *attr_dampen, int attr_options); // Free memory at exit to make analyzers happy #define cleanup_memory() \ free(attr_dampen); \ free(attr_name); \ free(attr_node); \ free(attr_section); \ free(attr_set); #define set_option(option_var) \ if (option_var) { \ free(option_var); \ } \ option_var = strdup(optarg); int main(int argc, char **argv) { int index = 0; int argerr = 0; int attr_options = attrd_opt_none; int flag; crm_exit_t exit_code = CRM_EX_OK; char *attr_node = NULL; char *attr_name = NULL; char *attr_set = NULL; char *attr_section = NULL; char *attr_dampen = NULL; const char *attr_value = NULL; char command = 'Q'; gboolean query_all = FALSE; crm_log_cli_init("attrd_updater"); crm_set_options(NULL, "command -n attribute [options]", long_options, "Tool for updating cluster node attributes"); if (argc < 2) { crm_help('?', CRM_EX_USAGE); } while (1) { flag = crm_get_option(argc, argv, &index); if (flag == -1) break; switch (flag) { case 'V': crm_bump_log_level(argc, argv); break; case '?': case '$': cleanup_memory(); crm_help(flag, CRM_EX_OK); break; case 'n': set_option(attr_name); break; case 's': set_option(attr_set); break; case 'd': set_option(attr_dampen); break; case 'l': case 'S': set_option(attr_section); break; case 'N': set_option(attr_node); break; case 'A': query_all = TRUE; break; case 'p': set_bit(attr_options, attrd_opt_private); break; case 'q': break; case 'Y': command = flag; crm_log_args(argc, argv); /* Too much? */ break; case 'Q': case 'B': case 'R': case 'D': case 'U': case 'v': command = flag; attr_value = optarg; crm_log_args(argc, argv); /* Too much? */ break; default: ++argerr; break; } } if (optind > argc) { ++argerr; } if (command != 'R' && attr_name == NULL) { ++argerr; } if (argerr) { cleanup_memory(); crm_help('?', CRM_EX_USAGE); } if (command == 'Q') { exit_code = crm_errno2exit(do_query(attr_name, attr_node, query_all)); } else { /* @TODO We don't know whether the specified node is a Pacemaker Remote * node or not, so we can't set attrd_opt_remote when appropriate. * However, it's not a big problem, because pacemaker-attrd will learn * and remember a node's "remoteness". */ exit_code = crm_errno2exit(do_update(command, attrd_get_target(attr_node), attr_name, attr_value, attr_section, attr_set, attr_dampen, attr_options)); } cleanup_memory(); crm_exit(exit_code); } /*! * \internal * \brief Submit a query request to pacemaker-attrd and wait for reply * * \param[in] name Name of attribute to query * \param[in] host Query applies to this host only (or all hosts if NULL) * \param[out] reply On success, will be set to new XML tree with reply * * \return pcmk_ok on success, -errno on error * \note On success, caller is responsible for freeing result via free_xml(*reply) */ static int send_attrd_query(const char *name, const char *host, xmlNode **reply) { int rc; crm_ipc_t *ipc; xmlNode *query; /* Build the query XML */ query = create_xml_node(NULL, __FUNCTION__); if (query == NULL) { return -ENOMEM; } crm_xml_add(query, F_TYPE, T_ATTRD); crm_xml_add(query, F_ORIG, crm_system_name); crm_xml_add(query, F_ATTRD_HOST, host); crm_xml_add(query, F_ATTRD_TASK, ATTRD_OP_QUERY); crm_xml_add(query, F_ATTRD_ATTRIBUTE, name); /* Connect to pacemaker-attrd, send query XML and get reply */ crm_debug("Sending query for value of %s on %s", name, (host? host : "all nodes")); ipc = crm_ipc_new(T_ATTRD, 0); if (crm_ipc_connect(ipc) == FALSE) { crm_perror(LOG_ERR, "Connection to cluster attribute manager failed"); rc = -ENOTCONN; } else { rc = crm_ipc_send(ipc, query, crm_ipc_flags_none|crm_ipc_client_response, 0, reply); if (rc > 0) { rc = pcmk_ok; } crm_ipc_close(ipc); } free_xml(query); return(rc); } /*! * \brief Validate pacemaker-attrd's XML reply to an query * * param[in] reply Root of reply XML tree to validate * param[in] attr_name Name of attribute that was queried * * \return pcmk_ok on success, * -errno on error (-ENXIO = requested attribute does not exist) */ static int validate_attrd_reply(xmlNode *reply, const char *attr_name) { const char *reply_attr; if (reply == NULL) { fprintf(stderr, "Could not query value of %s: reply did not contain valid XML\n", attr_name); return -pcmk_err_schema_validation; } crm_log_xml_trace(reply, "Reply"); reply_attr = crm_element_value(reply, F_ATTRD_ATTRIBUTE); if (reply_attr == NULL) { fprintf(stderr, "Could not query value of %s: attribute does not exist\n", attr_name); return -ENXIO; } if (safe_str_neq(crm_element_value(reply, F_TYPE), T_ATTRD) || (crm_element_value(reply, F_ATTRD_VERSION) == NULL) || strcmp(reply_attr, attr_name)) { fprintf(stderr, "Could not query value of %s: reply did not contain expected identification\n", attr_name); return -pcmk_err_schema_validation; } return pcmk_ok; } /*! * \brief Print the attribute values in a pacemaker-attrd XML query reply * * \param[in] reply Root of XML tree with query reply * \param[in] attr_name Name of attribute that was queried * * \return TRUE if any values were printed */ static gboolean print_attrd_values(xmlNode *reply, const char *attr_name) { xmlNode *child; const char *reply_host, *reply_value; gboolean have_values = FALSE; /* Iterate through reply's XML tags (a node tag for each host-value pair) */ for (child = __xml_first_child(reply); child != NULL; child = __xml_next(child)) { if (safe_str_neq((const char*)child->name, XML_CIB_TAG_NODE)) { crm_warn("Ignoring unexpected %s tag in query reply", child->name); } else { reply_host = crm_element_value(child, F_ATTRD_HOST); reply_value = crm_element_value(child, F_ATTRD_VALUE); if (reply_host == NULL) { crm_warn("Ignoring %s tag without %s attribute in query reply", XML_CIB_TAG_NODE, F_ATTRD_HOST); } else { printf("name=\"%s\" host=\"%s\" value=\"%s\"\n", attr_name, reply_host, (reply_value? reply_value : "")); have_values = TRUE; } } } return have_values; } /*! * \brief Submit a query to pacemaker-attrd and print reply * * \param[in] attr_name Name of attribute to be affected by request * \param[in] attr_node Name of host to query for (or NULL for localhost) * \param[in] query_all If TRUE, ignore attr_node and query all nodes instead * * \return pcmk_ok on success, -errno on error */ static int do_query(const char *attr_name, const char *attr_node, gboolean query_all) { xmlNode *reply = NULL; int rc; /* Decide which node(s) to query */ if (query_all == TRUE) { attr_node = NULL; } else { attr_node = attrd_get_target(attr_node); } /* Build and send pacemaker-attrd request, and get XML reply */ rc = send_attrd_query(attr_name, attr_node, &reply); if (rc != pcmk_ok) { fprintf(stderr, "Could not query value of %s: %s (%d)\n", attr_name, pcmk_strerror(rc), rc); return rc; } /* Validate the XML reply */ rc = validate_attrd_reply(reply, attr_name); if (rc != pcmk_ok) { if (reply != NULL) { free_xml(reply); } return rc; } /* Print the values from the reply */ if (print_attrd_values(reply, attr_name) == FALSE) { fprintf(stderr, "Could not query value of %s: reply had attribute name but no host values\n", attr_name); free_xml(reply); return -pcmk_err_schema_validation; } return pcmk_ok; } static int do_update(char command, const char *attr_node, const char *attr_name, const char *attr_value, const char *attr_section, const char *attr_set, const char *attr_dampen, int attr_options) { int rc = attrd_update_delegate(NULL, command, attr_node, attr_name, attr_value, attr_section, attr_set, attr_dampen, NULL, attr_options); if (rc != pcmk_ok) { fprintf(stderr, "Could not update %s=%s: %s (%d)\n", attr_name, attr_value, pcmk_strerror(rc), rc); } return rc; } diff --git a/tools/crm_attribute.c b/tools/crm_attribute.c index 9892c06688..5503786741 100644 --- a/tools/crm_attribute.c +++ b/tools/crm_attribute.c @@ -1,350 +1,350 @@ /* * Copyright 2004-2019 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU General Public License version 2 * or later (GPLv2+) WITHOUT ANY WARRANTY. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include gboolean BE_QUIET = FALSE; char command = 'G'; const char *dest_uname = NULL; char *dest_node = NULL; char *set_name = NULL; char *attr_id = NULL; char *attr_name = NULL; char *attr_pattern = NULL; const char *type = NULL; const char *rsc_id = NULL; const char *attr_value = NULL; const char *attr_default = NULL; const char *set_type = NULL; /* *INDENT-OFF* */ static struct crm_option long_options[] = { /* Top-level Options */ {"help", 0, 0, '?', "\tThis text"}, {"version", 0, 0, '$', "\tVersion information" }, {"verbose", 0, 0, 'V', "\tIncrease debug output"}, {"quiet", 0, 0, 'q', "\tPrint only the value on stdout\n"}, {"name", 1, 0, 'n', "Name of the attribute/option to operate on"}, {"pattern", 1, 0, 'P', "Pattern matching names of attributes (only with -v/-D and -l reboot)"}, {"-spacer-", 0, 0, '-', "\nCommands:"}, {"query", 0, 0, 'G', "\tQuery the current value of the attribute/option"}, {"update", 1, 0, 'v', "Update the value of the attribute/option"}, {"delete", 0, 0, 'D', "\tDelete the attribute/option"}, {"-spacer-", 0, 0, '-', "\nAdditional Options:"}, {"node", 1, 0, 'N', "Set an attribute for the named node (instead of a cluster option). See also: -l"}, {"type", 1, 0, 't', "Which part of the configuration to update/delete/query the option in"}, {"-spacer-", 0, 0, '-', "\t\t\tValid values: crm_config, rsc_defaults, op_defaults, tickets"}, {"lifetime", 1, 0, 'l', "Lifetime of the node attribute"}, {"-spacer-", 0, 0, '-', "\t\t\tValid values: reboot, forever"}, {"utilization", 0, 0, 'z', "Set an utilization attribute for the node."}, {"set-name", 1, 0, 's', "(Advanced) The attribute set in which to place the value"}, {"id", 1, 0, 'i', "\t(Advanced) The ID used to identify the attribute"}, {"default", 1, 0, 'd', "(Advanced) The default value to display if none is found in the configuration"}, {"inhibit-policy-engine", 0, 0, '!', NULL, 1}, /* legacy */ {"quiet", 0, 0, 'Q', NULL, 1}, {"node-uname", 1, 0, 'U', NULL, 1}, {"get-value", 0, 0, 'G', NULL, 1}, {"delete-attr", 0, 0, 'D', NULL, 1}, {"attr-value", 1, 0, 'v', NULL, 1}, {"attr-name", 1, 0, 'n', NULL, 1}, {"attr-id", 1, 0, 'i', NULL, 1}, {"-spacer-", 1, 0, '-', "\nExamples:", pcmk_option_paragraph}, {"-spacer-", 1, 0, '-', "Add a new node attribute called 'location' with the value of 'office' for host 'myhost':", pcmk_option_paragraph}, {"-spacer-", 1, 0, '-', " crm_attribute --node myhost --name location --update office", pcmk_option_example}, {"-spacer-", 1, 0, '-', "Query the value of the 'location' node attribute for host 'myhost':", pcmk_option_paragraph}, {"-spacer-", 1, 0, '-', " crm_attribute --node myhost --name location --query", pcmk_option_example}, {"-spacer-", 1, 0, '-', "Change the value of the 'location' node attribute for host 'myhost':", pcmk_option_paragraph}, {"-spacer-", 1, 0, '-', " crm_attribute --node myhost --name location --update backoffice", pcmk_option_example}, {"-spacer-", 1, 0, '-', "Delete the 'location' node attribute for host 'myhost':", pcmk_option_paragraph}, {"-spacer-", 1, 0, '-', " crm_attribute --node myhost --name location --delete", pcmk_option_example}, {"-spacer-", 1, 0, '-', "Query the value of the cluster-delay cluster option:", pcmk_option_paragraph}, {"-spacer-", 1, 0, '-', " crm_attribute --type crm_config --name cluster-delay --query", pcmk_option_example}, {"-spacer-", 1, 0, '-', "Query the value of the cluster-delay cluster option. Only print the value:", pcmk_option_paragraph}, {"-spacer-", 1, 0, '-', " crm_attribute --type crm_config --name cluster-delay --query --quiet", pcmk_option_example}, {0, 0, 0, 0} }; /* *INDENT-ON* */ int main(int argc, char **argv) { cib_t *the_cib = NULL; int rc = pcmk_ok; int cib_opts = cib_sync_call; int argerr = 0; int flag; int option_index = 0; int is_remote_node = 0; bool try_attrd = true; crm_log_cli_init("crm_attribute"); crm_set_options(NULL, " -n [options]", long_options, "Manage node's attributes and cluster options." "\n\nAllows node attributes and cluster options to be queried, modified and deleted.\n"); if (argc < 2) { crm_help('?', CRM_EX_USAGE); } while (1) { flag = crm_get_option(argc, argv, &option_index); if (flag == -1) break; switch (flag) { case 'V': crm_bump_log_level(argc, argv); break; case '$': case '?': crm_help(flag, CRM_EX_OK); break; case 'G': command = flag; attr_value = optarg; break; case 'D': case 'v': command = flag; attr_value = optarg; crm_log_args(argc, argv); break; case 'q': case 'Q': BE_QUIET = TRUE; break; case 'U': case 'N': dest_uname = strdup(optarg); break; case 's': set_name = strdup(optarg); break; case 'l': case 't': type = optarg; break; case 'z': type = XML_CIB_TAG_NODES; set_type = XML_TAG_UTILIZATION; break; case 'n': attr_name = strdup(optarg); break; case 'P': attr_pattern = strdup(optarg); break; case 'i': attr_id = strdup(optarg); break; case 'r': rsc_id = optarg; break; case 'd': attr_default = optarg; break; case '!': crm_warn("Inhibiting notifications for this update"); cib_opts |= cib_inhibit_notify; break; default: printf("Argument code 0%o (%c) is not (?yet?) supported\n", flag, flag); ++argerr; break; } } if (optind < argc) { printf("non-option ARGV-elements: "); while (optind < argc) printf("%s ", argv[optind++]); printf("\n"); } if (optind > argc) { ++argerr; } if (argerr) { crm_help('?', CRM_EX_USAGE); } the_cib = cib_new(); rc = the_cib->cmds->signon(the_cib, crm_system_name, cib_command); if (rc != pcmk_ok) { fprintf(stderr, "Error connecting to the CIB manager: %s\n", pcmk_strerror(rc)); crm_exit(crm_errno2exit(rc)); } if (type == NULL && dest_uname != NULL) { type = "forever"; } if (safe_str_eq(type, "reboot")) { type = XML_CIB_TAG_STATUS; } else if (safe_str_eq(type, "forever")) { type = XML_CIB_TAG_NODES; } if (type == NULL && dest_uname == NULL) { /* we're updating cluster options - don't populate dest_node */ type = XML_CIB_TAG_CRMCONFIG; } else if (safe_str_eq(type, XML_CIB_TAG_CRMCONFIG)) { } else if (safe_str_neq(type, XML_CIB_TAG_TICKETS)) { /* If we are being called from a resource agent via the cluster, * the correct local node name will be passed as an environment * variable. Otherwise, we have to ask the cluster. */ dest_uname = attrd_get_target(dest_uname); if (dest_uname == NULL) { dest_uname = get_local_node_name(); } rc = query_node_uuid(the_cib, dest_uname, &dest_node, &is_remote_node); if (pcmk_ok != rc) { fprintf(stderr, "Could not map name=%s to a UUID\n", dest_uname); the_cib->cmds->signoff(the_cib); cib_delete(the_cib); crm_exit(crm_errno2exit(rc)); } } if ((command == 'D') && (attr_name == NULL) && (attr_pattern == NULL)) { fprintf(stderr, "Error: must specify attribute name or pattern to delete\n"); crm_exit(CRM_EX_USAGE); } if (attr_pattern) { if (((command != 'v') && (command != 'D')) || safe_str_neq(type, XML_CIB_TAG_STATUS)) { fprintf(stderr, "Error: pattern can only be used with till-reboot update or delete\n"); crm_exit(CRM_EX_USAGE); } command = 'u'; free(attr_name); attr_name = attr_pattern; } // Only go through attribute manager for transient attributes try_attrd = safe_str_eq(type, XML_CIB_TAG_STATUS); // Don't try to contact attribute manager if we're using a file as CIB if (getenv("CIB_file") || getenv("CIB_shadow")) { try_attrd = FALSE; } if (((command == 'v') || (command == 'D') || (command == 'u')) && try_attrd && pcmk_ok == attrd_update_delegate(NULL, command, dest_uname, attr_name, attr_value, type, set_name, NULL, NULL, is_remote_node?attrd_opt_remote:attrd_opt_none)) { crm_info("Update %s=%s sent via pacemaker-attrd", attr_name, ((command == 'D')? "" : attr_value)); } else if (command == 'D') { rc = delete_attr_delegate(the_cib, cib_opts, type, dest_node, set_type, set_name, attr_id, attr_name, attr_value, TRUE, NULL); if (rc == -ENXIO) { /* Nothing to delete... * which means it's not there... * which is what the admin wanted */ rc = pcmk_ok; } } else if (command == 'v') { CRM_LOG_ASSERT(type != NULL); CRM_LOG_ASSERT(attr_name != NULL); CRM_LOG_ASSERT(attr_value != NULL); rc = update_attr_delegate(the_cib, cib_opts, type, dest_node, set_type, set_name, attr_id, attr_name, attr_value, TRUE, NULL, is_remote_node ? "remote" : NULL); } else { /* query */ char *read_value = NULL; rc = read_attr_delegate(the_cib, type, dest_node, set_type, set_name, attr_id, attr_name, &read_value, TRUE, NULL); if (rc == -ENXIO && attr_default) { read_value = strdup(attr_default); rc = pcmk_ok; } crm_info("Read %s=%s %s%s", attr_name, crm_str(read_value), set_name ? "in " : "", set_name ? set_name : ""); if (rc == -ENOTUNIQ) { // Multiple matches (already displayed) are not error for queries rc = pcmk_ok; } else if (BE_QUIET == FALSE) { fprintf(stdout, "%s%s %s%s %s%s value=%s\n", type ? "scope=" : "", type ? type : "", attr_id ? "id=" : "", attr_id ? attr_id : "", attr_name ? "name=" : "", attr_name ? attr_name : "", read_value ? read_value : "(null)"); } else if (read_value != NULL) { fprintf(stdout, "%s\n", read_value); } free(read_value); } if (rc == -ENOTUNIQ) { printf("Please choose from one of the matches above and supply the 'id' with --attr-id\n"); } else if (rc != pcmk_ok) { fprintf(stderr, "Error performing operation: %s\n", pcmk_strerror(rc)); } the_cib->cmds->signoff(the_cib); cib_delete(the_cib); crm_exit(crm_errno2exit(rc)); } diff --git a/tools/crm_node.c b/tools/crm_node.c index ff806d8010..0214afde34 100644 --- a/tools/crm_node.c +++ b/tools/crm_node.c @@ -1,664 +1,664 @@ /* * Copyright 2004-2019 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU General Public License version 2 * or later (GPLv2+) WITHOUT ANY WARRANTY. */ #include #include #include #include #include #include #include #include #include #include -#include +#include #define SUMMARY "crm_node - Tool for displaying low-level node information" struct { gboolean corosync; gboolean dangerous_cmd; gboolean force_flag; char command; int nodeid; char *target_uname; } options = { .command = '\0', .force_flag = FALSE }; gboolean command_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error); gboolean name_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error); gboolean remove_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error); static char *pid_s = NULL; static GMainLoop *mainloop = NULL; static crm_exit_t exit_code = CRM_EX_OK; #define INDENT " " static GOptionEntry command_entries[] = { { "cluster-id", 'i', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb, "Display this node's cluster id", NULL }, { "list", 'l', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb, "Display all known members (past and present) of this cluster", NULL }, { "name", 'n', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb, "Display the name used by the cluster for this node", NULL }, { "partition", 'p', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb, "Display the members of this partition", NULL }, { "quorum", 'q', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb, "Display a 1 if our partition has quorum, 0 if not", NULL }, { "name-for-id", 'N', 0, G_OPTION_ARG_CALLBACK, name_cb, "Display the name used by the cluster for the node with the specified ID", "ID" }, { "remove", 'R', 0, G_OPTION_ARG_CALLBACK, remove_cb, "(Advanced) Remove the (stopped) node with the specified name from Pacemaker's\n" INDENT "configuration and caches (the node must already have been removed from\n" INDENT "the underlying cluster stack configuration", "NAME" }, { NULL } }; static GOptionEntry addl_entries[] = { { "force", 'f', 0, G_OPTION_ARG_NONE, &options.force_flag, NULL, NULL }, #if SUPPORT_COROSYNC /* Unused and deprecated */ { "corosync", 'C', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &options.corosync, NULL, NULL }, #endif // @TODO add timeout option for when IPC replies are needed { NULL } }; gboolean command_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) { if (safe_str_eq("-i", option_name) || safe_str_eq("--cluster-id", option_name)) { options.command = 'i'; } else if (safe_str_eq("-l", option_name) || safe_str_eq("--list", option_name)) { options.command = 'l'; } else if (safe_str_eq("-n", option_name) || safe_str_eq("--name", option_name)) { options.command = 'n'; } else if (safe_str_eq("-p", option_name) || safe_str_eq("--partition", option_name)) { options.command = 'p'; } else if (safe_str_eq("-q", option_name) || safe_str_eq("--quorum", option_name)) { options.command = 'q'; } else { g_set_error(error, G_OPTION_ERROR, CRM_EX_INVALID_PARAM, "Unknown param passed to command_cb: %s\n", option_name); return FALSE; } return TRUE; } gboolean name_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) { options.command = 'N'; options.nodeid = crm_parse_int(optarg, NULL); return TRUE; } gboolean remove_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) { if (optarg == NULL) { crm_err("-R option requires an argument"); g_set_error(error, G_OPTION_ERROR, CRM_EX_INVALID_PARAM, "-R option requires an argument"); return FALSE; } options.command = 'R'; options.dangerous_cmd = TRUE; options.target_uname = strdup(optarg); return TRUE; } /*! * \internal * \brief Exit crm_node * Clean up memory, and either quit mainloop (if running) or exit * * \param[in] value Exit status */ static void crm_node_exit(crm_exit_t value) { if (pid_s) { free(pid_s); pid_s = NULL; } exit_code = value; if (mainloop && g_main_loop_is_running(mainloop)) { g_main_loop_quit(mainloop); } else { crm_exit(exit_code); } } static void exit_disconnect(gpointer user_data) { fprintf(stderr, "error: Lost connection to cluster\n"); crm_node_exit(CRM_EX_DISCONNECT); } typedef int (*ipc_dispatch_fn) (const char *buffer, ssize_t length, gpointer userdata); static crm_ipc_t * new_mainloop_for_ipc(const char *system, ipc_dispatch_fn dispatch) { mainloop_io_t *source = NULL; crm_ipc_t *ipc = NULL; struct ipc_client_callbacks ipc_callbacks = { .dispatch = dispatch, .destroy = exit_disconnect }; mainloop = g_main_loop_new(NULL, FALSE); source = mainloop_add_ipc_client(system, G_PRIORITY_DEFAULT, 0, NULL, &ipc_callbacks); ipc = mainloop_get_ipc_client(source); if (ipc == NULL) { fprintf(stderr, "error: Could not connect to cluster (is it running?)\n"); crm_node_exit(CRM_EX_DISCONNECT); } return ipc; } static void run_mainloop_and_exit() { g_main_loop_run(mainloop); g_main_loop_unref(mainloop); mainloop = NULL; crm_node_exit(exit_code); } static int send_controller_hello(crm_ipc_t *controller) { xmlNode *hello = NULL; int rc; pid_s = crm_getpid_s(); hello = create_hello_message(pid_s, crm_system_name, "1", "0"); rc = crm_ipc_send(controller, hello, 0, 0, NULL); free_xml(hello); return (rc < 0)? rc : 0; } static int send_node_info_request(crm_ipc_t *controller, uint32_t nodeid) { xmlNode *ping = NULL; int rc; ping = create_request(CRM_OP_NODE_INFO, NULL, NULL, CRM_SYSTEM_CRMD, crm_system_name, pid_s); if (nodeid > 0) { crm_xml_add_int(ping, XML_ATTR_ID, nodeid); } rc = crm_ipc_send(controller, ping, 0, 0, NULL); free_xml(ping); return (rc < 0)? rc : 0; } static int dispatch_controller(const char *buffer, ssize_t length, gpointer userdata) { xmlNode *message = string2xml(buffer); xmlNode *data = NULL; const char *value = NULL; if (message == NULL) { fprintf(stderr, "error: Could not understand reply from controller\n"); crm_node_exit(CRM_EX_PROTOCOL); return 0; } crm_log_xml_trace(message, "controller reply"); exit_code = CRM_EX_PROTOCOL; // Validate reply value = crm_element_value(message, F_CRM_MSG_TYPE); if (safe_str_neq(value, XML_ATTR_RESPONSE)) { fprintf(stderr, "error: Message from controller was not a reply\n"); goto done; } value = crm_element_value(message, XML_ATTR_REFERENCE); if (value == NULL) { fprintf(stderr, "error: Controller reply did not specify original message\n"); goto done; } data = get_message_xml(message, F_CRM_DATA); if (data == NULL) { fprintf(stderr, "error: Controller reply did not contain any data\n"); goto done; } switch (options.command) { case 'i': value = crm_element_value(data, XML_ATTR_ID); if (value == NULL) { fprintf(stderr, "error: Controller reply did not contain node ID\n"); } else { printf("%s\n", value); exit_code = CRM_EX_OK; } break; case 'n': case 'N': value = crm_element_value(data, XML_ATTR_UNAME); if (value == NULL) { fprintf(stderr, "Node is not known to cluster\n"); exit_code = CRM_EX_NOHOST; } else { printf("%s\n", value); exit_code = CRM_EX_OK; } break; case 'q': value = crm_element_value(data, XML_ATTR_HAVE_QUORUM); if (value == NULL) { fprintf(stderr, "error: Controller reply did not contain quorum status\n"); } else { bool quorum = crm_is_true(value); printf("%d\n", quorum); exit_code = quorum? CRM_EX_OK : CRM_EX_QUORUM; } break; default: fprintf(stderr, "internal error: Controller reply not expected\n"); exit_code = CRM_EX_SOFTWARE; break; } done: free_xml(message); crm_node_exit(exit_code); return 0; } static void run_controller_mainloop(uint32_t nodeid) { crm_ipc_t *controller = NULL; int rc; controller = new_mainloop_for_ipc(CRM_SYSTEM_CRMD, dispatch_controller); rc = send_controller_hello(controller); if (rc < 0) { fprintf(stderr, "error: Could not register with controller: %s\n", pcmk_strerror(rc)); crm_node_exit(crm_errno2exit(rc)); } rc = send_node_info_request(controller, nodeid); if (rc < 0) { fprintf(stderr, "error: Could not ping controller: %s\n", pcmk_strerror(rc)); crm_node_exit(crm_errno2exit(rc)); } // Run main loop to get controller reply via dispatch_controller() run_mainloop_and_exit(); } static void print_node_name() { // Check environment first (i.e. when called by resource agent) const char *name = getenv("OCF_RESKEY_" CRM_META "_" XML_LRM_ATTR_TARGET); if (name != NULL) { printf("%s\n", name); crm_node_exit(CRM_EX_OK); } else { // Otherwise ask the controller run_controller_mainloop(0); } } static int cib_remove_node(long id, const char *name) { int rc; cib_t *cib = NULL; xmlNode *node = NULL; xmlNode *node_state = NULL; crm_trace("Removing %s from the CIB", name); if(name == NULL && id == 0) { return -ENOTUNIQ; } node = create_xml_node(NULL, XML_CIB_TAG_NODE); node_state = create_xml_node(NULL, XML_CIB_TAG_STATE); crm_xml_add(node, XML_ATTR_UNAME, name); crm_xml_add(node_state, XML_ATTR_UNAME, name); if (id > 0) { crm_xml_set_id(node, "%ld", id); crm_xml_add(node_state, XML_ATTR_ID, ID(node)); } cib = cib_new(); cib->cmds->signon(cib, crm_system_name, cib_command); rc = cib->cmds->remove(cib, XML_CIB_TAG_NODES, node, cib_sync_call); if (rc != pcmk_ok) { printf("Could not remove %s[%ld] from " XML_CIB_TAG_NODES ": %s", name, id, pcmk_strerror(rc)); } rc = cib->cmds->remove(cib, XML_CIB_TAG_STATUS, node_state, cib_sync_call); if (rc != pcmk_ok) { printf("Could not remove %s[%ld] from " XML_CIB_TAG_STATUS ": %s", name, id, pcmk_strerror(rc)); } cib->cmds->signoff(cib); cib_delete(cib); return rc; } static int tools_remove_node_cache(const char *node_name, long nodeid, const char *target) { int rc = -1; crm_ipc_t *conn = crm_ipc_new(target, 0); xmlNode *cmd = NULL; if (!conn) { return -ENOTCONN; } if (!crm_ipc_connect(conn)) { crm_perror(LOG_ERR, "Connection to %s failed", target); crm_ipc_destroy(conn); return -ENOTCONN; } if(safe_str_eq(target, CRM_SYSTEM_CRMD)) { // The controller requires a hello message before sending a request rc = send_controller_hello(conn); if (rc < 0) { fprintf(stderr, "error: Could not register with controller: %s\n", pcmk_strerror(rc)); return rc; } } crm_trace("Removing %s[%ld] from the %s membership cache", node_name, nodeid, target); if(safe_str_eq(target, T_ATTRD)) { cmd = create_xml_node(NULL, __FUNCTION__); crm_xml_add(cmd, F_TYPE, T_ATTRD); crm_xml_add(cmd, F_ORIG, crm_system_name); crm_xml_add(cmd, F_ATTRD_TASK, ATTRD_OP_PEER_REMOVE); crm_xml_add(cmd, F_ATTRD_HOST, node_name); if (nodeid > 0) { crm_xml_add_int(cmd, F_ATTRD_HOST_ID, (int) nodeid); } } else { cmd = create_request(CRM_OP_RM_NODE_CACHE, NULL, NULL, target, crm_system_name, pid_s); if (nodeid > 0) { crm_xml_set_id(cmd, "%ld", nodeid); } crm_xml_add(cmd, XML_ATTR_UNAME, node_name); } rc = crm_ipc_send(conn, cmd, 0, 0, NULL); crm_debug("%s peer cache cleanup for %s (%ld): %d", target, node_name, nodeid, rc); if (rc > 0) { rc = cib_remove_node(nodeid, node_name); } if (conn) { crm_ipc_close(conn); crm_ipc_destroy(conn); } free_xml(cmd); return rc > 0 ? 0 : rc; } static void remove_node(const char *target_uname) { int d = 0; long nodeid = 0; const char *node_name = NULL; char *endptr = NULL; const char *daemons[] = { CRM_SYSTEM_CRMD, "stonith-ng", T_ATTRD, CRM_SYSTEM_MCP, }; // Check whether node was specified by name or numeric ID errno = 0; nodeid = strtol(target_uname, &endptr, 10); if ((errno != 0) || (endptr == target_uname) || (*endptr != '\0') || (nodeid <= 0)) { // It's not a positive integer, so assume it's a node name nodeid = 0; node_name = target_uname; } for (d = 0; d < DIMOF(daemons); d++) { if (tools_remove_node_cache(node_name, nodeid, daemons[d])) { crm_err("Failed to connect to %s to remove node '%s'", daemons[d], target_uname); crm_node_exit(CRM_EX_ERROR); return; } } crm_node_exit(CRM_EX_OK); } static gint compare_node_xml(gconstpointer a, gconstpointer b) { const char *a_name = crm_element_value((xmlNode*) a, "uname"); const char *b_name = crm_element_value((xmlNode*) b, "uname"); return strcmp((a_name? a_name : ""), (b_name? b_name : "")); } static int node_mcp_dispatch(const char *buffer, ssize_t length, gpointer userdata) { GList *nodes = NULL; xmlNode *node = NULL; xmlNode *msg = string2xml(buffer); const char *uname; const char *state; if (msg == NULL) { fprintf(stderr, "error: Could not understand pacemakerd response\n"); crm_node_exit(CRM_EX_PROTOCOL); return 0; } crm_log_xml_trace(msg, "message"); for (node = __xml_first_child(msg); node != NULL; node = __xml_next(node)) { nodes = g_list_insert_sorted(nodes, node, compare_node_xml); } for (GList *iter = nodes; iter; iter = iter->next) { node = (xmlNode*) iter->data; uname = crm_element_value(node, "uname"); state = crm_element_value(node, "state"); if (options.command == 'l') { int id = 0; crm_element_value_int(node, "id", &id); printf("%d %s %s\n", id, (uname? uname : ""), (state? state : "")); // This is CRM_NODE_MEMBER but we don't want to include cluster header } else if ((options.command == 'p') && safe_str_eq(state, "member")) { printf("%s ", (uname? uname : "")); } } if (options.command == 'p') { fprintf(stdout, "\n"); } free_xml(msg); crm_node_exit(CRM_EX_OK); return 0; } static void run_pacemakerd_mainloop() { crm_ipc_t *ipc = NULL; xmlNode *poke = NULL; ipc = new_mainloop_for_ipc(CRM_SYSTEM_MCP, node_mcp_dispatch); // Sending anything will get us a list of nodes poke = create_xml_node(NULL, "poke"); crm_ipc_send(ipc, poke, 0, 0, NULL); free_xml(poke); // Handle reply via node_mcp_dispatch() run_mainloop_and_exit(); } static GOptionContext * build_arg_context(pcmk__common_args_t *args, GOptionGroup *group) { GOptionContext *context = NULL; GOptionEntry extra_prog_entries[] = { { "quiet", 'Q', 0, G_OPTION_ARG_NONE, &(args->quiet), "Be less descriptive in output.", NULL }, { NULL } }; context = pcmk__build_arg_context(args, NULL, &group); /* Add the -q option, which cannot be part of the globally supported options * because some tools use that flag for something else. */ pcmk__add_main_args(context, extra_prog_entries); pcmk__add_arg_group(context, "commands", "Commands:", "Show command help", command_entries); pcmk__add_arg_group(context, "additional", "Additional Options:", "Show additional options", addl_entries); return context; } int main(int argc, char **argv) { pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY); GError *error = NULL; GOptionContext *context = NULL; GOptionGroup *output_group = NULL; gchar **processed_args = NULL; context = build_arg_context(args, output_group); crm_log_cli_init("crm_node"); processed_args = pcmk__cmdline_preproc(argc, argv, "NR"); if (!g_option_context_parse_strv(context, &processed_args, &error)) { fprintf(stderr, "%s: %s\n", g_get_prgname(), error->message); exit_code = CRM_EX_USAGE; goto done; } for (int i = 0; i < args->verbosity; i++) { crm_bump_log_level(argc, argv); } if (args->version) { /* FIXME: When crm_node is converted to use formatted output, this can go. */ crm_help('v', CRM_EX_USAGE); } if (optind > argc || options.command == 0) { fprintf(stderr, "%s", g_option_context_get_help(context, TRUE, NULL)); exit_code = CRM_EX_USAGE; goto done; } if (options.dangerous_cmd && options.force_flag == FALSE) { fprintf(stderr, "The supplied command is considered dangerous." " To prevent accidental destruction of the cluster," " the --force flag is required in order to proceed.\n"); exit_code = CRM_EX_USAGE; goto done; } switch (options.command) { case 'n': print_node_name(); break; case 'R': remove_node(options.target_uname); break; case 'i': case 'q': case 'N': run_controller_mainloop(options.nodeid); break; case 'l': case 'p': run_pacemakerd_mainloop(); break; default: break; } done: g_strfreev(processed_args); pcmk__free_arg_context(context); crm_node_exit(exit_code); return exit_code; } diff --git a/tools/crm_resource.h b/tools/crm_resource.h index 48236b6695..84b094b1b6 100644 --- a/tools/crm_resource.h +++ b/tools/crm_resource.h @@ -1,103 +1,103 @@ /* * Copyright 2004-2019 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU Lesser General Public License * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. */ #include #include #include #include #include #include #include -#include +#include #include #include #include #include extern bool print_pending; extern bool scope_master; extern bool do_force; extern bool BE_QUIET; extern int resource_verbose; extern int cib_options; extern int crmd_replies_needed; extern char *move_lifetime; extern const char *attr_set_type; /* ban */ int cli_resource_prefer(const char *rsc_id, const char *host, cib_t * cib_conn); int cli_resource_ban(const char *rsc_id, const char *host, GListPtr allnodes, cib_t * cib_conn); int cli_resource_clear(const char *rsc_id, const char *host, GListPtr allnodes, cib_t * cib_conn, bool clear_ban_constraints); int cli_resource_clear_all_expired(xmlNode *root, cib_t *cib_conn, const char *rsc, const char *node, bool scope_master); /* print */ void cli_resource_print_cts(resource_t * rsc); void cli_resource_print_raw(resource_t * rsc); void cli_resource_print_cts_constraints(pe_working_set_t * data_set); void cli_resource_print_location(resource_t * rsc, const char *prefix); void cli_resource_print_colocation(resource_t * rsc, bool dependents, bool recursive, int offset); int cli_resource_print(resource_t *rsc, pe_working_set_t *data_set, bool expanded); int cli_resource_print_list(pe_working_set_t * data_set, bool raw); int cli_resource_print_attribute(resource_t *rsc, const char *attr, pe_working_set_t *data_set); int cli_resource_print_property(resource_t *rsc, const char *attr, pe_working_set_t *data_set); int cli_resource_print_operations(const char *rsc_id, const char *host_uname, bool active, pe_working_set_t * data_set); /* runtime */ void cli_resource_check(cib_t * cib, resource_t *rsc); int cli_resource_fail(crm_ipc_t * crmd_channel, const char *host_uname, const char *rsc_id, pe_working_set_t * data_set); int cli_resource_search(resource_t *rsc, const char *requested_name, pe_working_set_t *data_set); int cli_resource_delete(crm_ipc_t *crmd_channel, const char *host_uname, resource_t *rsc, const char *operation, const char *interval_spec, bool just_failures, pe_working_set_t *data_set); int cli_cleanup_all(crm_ipc_t *crmd_channel, const char *node_name, const char *operation, const char *interval_spec, pe_working_set_t *data_set); int cli_resource_restart(pe_resource_t *rsc, const char *host, int timeout_ms, cib_t *cib); int cli_resource_move(resource_t *rsc, const char *rsc_id, const char *host_name, cib_t *cib, pe_working_set_t *data_set); int cli_resource_execute_from_params(const char *rsc_name, const char *rsc_class, const char *rsc_prov, const char *rsc_type, const char *rsc_action, GHashTable *params, GHashTable *override_hash, int timeout_ms); int cli_resource_execute(resource_t *rsc, const char *requested_name, const char *rsc_action, GHashTable *override_hash, int timeout_ms, cib_t *cib, pe_working_set_t *data_set); int cli_resource_update_attribute(resource_t *rsc, const char *requested_name, const char *attr_set, const char *attr_id, const char *attr_name, const char *attr_value, bool recursive, cib_t *cib, pe_working_set_t *data_set); int cli_resource_delete_attribute(resource_t *rsc, const char *requested_name, const char *attr_set, const char *attr_id, const char *attr_name, cib_t *cib, pe_working_set_t *data_set); GList* subtract_lists(GList *from, GList *items, GCompareFunc cmp); int update_working_set_xml(pe_working_set_t *data_set, xmlNode **xml); int wait_till_stable(int timeout_ms, cib_t * cib); void cli_resource_why(cib_t *cib_conn, GListPtr resources, resource_t *rsc, node_t *node); diff --git a/tools/notifyServicelogEvent.c b/tools/notifyServicelogEvent.c index ed4967cad9..620a7997c8 100644 --- a/tools/notifyServicelogEvent.c +++ b/tools/notifyServicelogEvent.c @@ -1,192 +1,192 @@ /* * Original copyright 2009 International Business Machines, IBM, Mark Hamzy - * Later changes copyright 2009-2018 the Pacemaker project contributors + * Later changes copyright 2009-2019 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU General Public License version 2 * or later (GPLv2+) WITHOUT ANY WARRANTY. */ /* gcc -o notifyServicelogEvent `pkg-config --cflags servicelog-1` `pkg-config --libs servicelog-1` notifyServicelogEvent.c */ #include #include #include #include #include #include #include #include #include #include /* U64T ~ PRIu64, U64TS ~ SCNu64 */ #include #include -#include +#include typedef enum { STATUS_GREEN = 1, STATUS_YELLOW, STATUS_RED } STATUS; const char *status2char(STATUS status); STATUS event2status(struct sl_event *event); const char * status2char(STATUS status) { switch (status) { default: case STATUS_GREEN: return "green"; case STATUS_YELLOW: return "yellow"; case STATUS_RED: return "red"; } } STATUS event2status(struct sl_event * event) { STATUS status = STATUS_GREEN; crm_debug("Severity = %d, Disposition = %d", event->severity, event->disposition); /* @TBD */ if (event->severity == SL_SEV_WARNING) { status = STATUS_YELLOW; } if (event->disposition == SL_DISP_UNRECOVERABLE) { status = STATUS_RED; } return status; } static struct crm_option long_options[] = { /* Top-level Options */ {"help", 0, 0, '?', "\tThis text"}, {"version", 0, 0, '$', "\tVersion information"}, {"-spacer-", 0, 0, '-', "\nUsage: notifyServicelogEvent event_id"}, {"-spacer-", 0, 0, '-', "\nWhere event_id is unique unsigned event identifier which is then passed into servicelog"}, {0, 0, 0, 0} }; int main(int argc, char *argv[]) { int argerr = 0; int flag; int index = 0; int rc = 0; servicelog *slog = NULL; struct sl_event *event = NULL; uint64_t event_id = 0; crm_log_cli_init("notifyServicelogEvent"); crm_set_options(NULL, "event_id ", long_options, "Gets called upon events written to servicelog database"); if (argc < 2) { argerr++; } while (1) { flag = crm_get_option(argc, argv, &index); if (flag == -1) break; switch (flag) { case '?': case '$': crm_help(flag, CRM_EX_OK); break; default: ++argerr; break; } } if (argc - optind != 1) { ++argerr; } if (argerr) { crm_help('?', CRM_EX_USAGE); } openlog("notifyServicelogEvent", LOG_NDELAY, LOG_USER); if (sscanf(argv[optind], "%" U64TS, &event_id) != 1) { crm_err("Error: could not read event_id from args!"); rc = 1; goto cleanup; } if (event_id == 0) { crm_err("Error: event_id is 0!"); rc = 1; goto cleanup; } rc = servicelog_open(&slog, 0); /* flags is one of SL_FLAG_xxx */ if (!slog) { crm_err("Error: servicelog_open failed, rc = %d", rc); rc = 1; goto cleanup; } if (slog) { rc = servicelog_event_get(slog, event_id, &event); } if (rc == 0) { STATUS status = STATUS_GREEN; const char *health_component = "#health-ipmi"; const char *health_status = NULL; crm_debug("Event id = %" U64T ", Log timestamp = %s, Event timestamp = %s", event_id, ctime(&(event->time_logged)), ctime(&(event->time_event))); status = event2status(event); health_status = status2char(status); if (health_status) { gboolean rc; /* @TODO pass attrd_opt_remote when appropriate */ rc = (attrd_update_delegate(NULL, 'v', NULL, health_component, health_status, NULL, NULL, NULL, NULL, attrd_opt_none) > 0); crm_debug("Updating attribute ('%s', '%s') = %d", health_component, health_status, rc); } else { crm_err("Error: status2char failed, status = %d", status); rc = 1; } } else { crm_err("Error: servicelog_event_get failed, rc = %d", rc); } cleanup: if (event) { servicelog_event_free(event); } if (slog) { servicelog_close(slog); } closelog(); return rc; }