Page Menu
Home
ClusterLabs Projects
Search
Configure Global Search
Log In
Files
F3871558
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
18 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/tools/crm_node.c b/tools/crm_node.c
index 5f328e5931..9e357c4aee 100644
--- a/tools/crm_node.c
+++ b/tools/crm_node.c
@@ -1,544 +1,650 @@
/*
* Copyright 2004-2018 Andrew Beekhof <andrew@beekhof.net>
*
* This source code is licensed under the GNU General Public License version 2
* or later (GPLv2+) WITHOUT ANY WARRANTY.
*/
#include <crm_internal.h>
#include <sys/param.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <libgen.h> /* for basename() */
#include <crm/crm.h>
#include <crm/cluster/internal.h>
#include <crm/common/mainloop.h>
#include <crm/msg_xml.h>
#include <crm/cib.h>
#include <crm/attrd.h>
static int command = 0;
static char *pid_s = NULL;
static const char *target_uname = NULL;
static GMainLoop *mainloop = NULL;
static crm_exit_t exit_code = CRM_EX_OK;
/* *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', "\tEssential output only"},
{"-spacer-", 1, 0, '-', "\nStack:"},
#if SUPPORT_COROSYNC
{"corosync", 0, 0, 'C', "\tOnly try connecting to an Corosync-based cluster"},
#endif
{"-spacer-", 1, 0, '-', "\nCommands:"},
{"name", 0, 0, 'n', "\tDisplay the name used by the cluster for this node"},
{"name-for-id", 1, 0, 'N', "\tDisplay the name used by the cluster for the node with the specified id"},
{"quorum", 0, 0, 'q', "\tDisplay a 1 if our partition has quorum, 0 if not"},
{"list", 0, 0, 'l', "\tDisplay all known members (past and present) of this cluster"},
{"partition", 0, 0, 'p', "Display the members of this partition"},
{"cluster-id", 0, 0, 'i', "Display this node's cluster id"},
{"remove", 1, 0, 'R', "(Advanced) Remove the (stopped) node with the specified name from Pacemaker's configuration and caches"},
{"-spacer-", 1, 0, '-', "(the node must already have been removed from the underlying cluster stack configuration)"},
{"-spacer-", 1, 0, '-', "\nAdditional Options:"},
{"force", 0, 0, 'f'},
// @TODO add timeout option for when IPC replies are needed
{0, 0, 0, 0}
};
/* *INDENT-ON* */
/*!
* \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)
+{
+ xmlNode *ping = NULL;
+ int rc;
+
+ ping = create_request(CRM_OP_NODE_INFO, NULL, NULL, CRM_SYSTEM_CRMD,
+ crm_system_name, pid_s);
+ 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 (command) {
+ 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;
+ 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()
+{
+ 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);
+ 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();
+ }
+}
+
static int
cib_remove_node(uint32_t 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) {
crm_xml_set_id(node, "%u", 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/%u 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/%u 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, const char *target)
{
int n = 0;
int rc = -1;
char *name = NULL;
crm_ipc_t *conn = crm_ipc_new(target, 0);
xmlNode *cmd = NULL;
char *endptr = 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;
}
}
errno = 0;
n = strtol(node, &endptr, 10);
if (errno != 0 || endptr == node || *endptr != '\0') {
/* Argument was not a nodeid */
n = 0;
name = strdup(node);
} else {
name = get_node_name(n);
}
crm_trace("Removing %s aka. %s (%u) from the membership cache", name, node, n);
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, name);
if (n) {
char buffer[64];
if(snprintf(buffer, 63, "%d", n) > 0) {
crm_xml_add(cmd, F_ATTRD_HOST_ID, buffer);
}
}
} else {
cmd = create_request(CRM_OP_RM_NODE_CACHE,
NULL, NULL, target, crm_system_name, pid_s);
if (n) {
crm_xml_set_id(cmd, "%u", n);
}
crm_xml_add(cmd, XML_ATTR_UNAME, name);
}
rc = crm_ipc_send(conn, cmd, 0, 0, NULL);
crm_debug("%s peer cache cleanup for %s (%u): %d", target, name, n, rc);
if (rc > 0) {
rc = cib_remove_node(n, name);
}
if (conn) {
crm_ipc_close(conn);
crm_ipc_destroy(conn);
}
free_xml(cmd);
free(name);
return rc > 0 ? 0 : rc;
}
static gint
compare_node_uname(gconstpointer a, gconstpointer b)
{
const crm_node_t *a_node = a;
const crm_node_t *b_node = b;
return strcmp(a_node->uname?a_node->uname:"", b_node->uname?b_node->uname:"");
}
static int
node_mcp_dispatch(const char *buffer, ssize_t length, gpointer userdata)
{
xmlNode *msg = string2xml(buffer);
if (msg) {
xmlNode *node = NULL;
GListPtr nodes = NULL;
GListPtr iter = NULL;
const char *quorate = crm_element_value(msg, "quorate");
crm_log_xml_trace(msg, "message");
if (command == 'q' && quorate != NULL) {
fprintf(stdout, "%s\n", quorate);
crm_node_exit(CRM_EX_OK);
} else if(command == 'q') {
crm_node_exit(CRM_EX_ERROR);
}
for (node = __xml_first_child(msg); node != NULL; node = __xml_next(node)) {
crm_node_t *peer = calloc(1, sizeof(crm_node_t));
nodes = g_list_insert_sorted(nodes, peer, compare_node_uname);
peer->uname = (char*)crm_element_value_copy(node, "uname");
peer->state = (char*)crm_element_value_copy(node, "state");
crm_element_value_int(node, "id", (int*)&peer->id);
}
for(iter = nodes; iter; iter = iter->next) {
crm_node_t *peer = iter->data;
if (command == 'l') {
fprintf(stdout, "%u %s %s\n",
peer->id, peer->uname, (peer->state? peer->state : ""));
} else if (command == 'p') {
if(safe_str_eq(peer->state, CRM_NODE_MEMBER)) {
fprintf(stdout, "%s ", peer->uname);
}
} else if (command == 'i') {
if(safe_str_eq(peer->state, CRM_NODE_MEMBER)) {
fprintf(stdout, "%u ", peer->id);
}
}
}
g_list_free_full(nodes, free);
free_xml(msg);
if (command == 'p') {
fprintf(stdout, "\n");
}
crm_node_exit(CRM_EX_OK);
}
return 0;
}
static void
try_pacemaker(int command, enum cluster_type_e stack)
{
switch (command) {
case 'R':
{
int lpc = 0;
const char *daemons[] = {
CRM_SYSTEM_CRMD,
"stonith-ng",
T_ATTRD,
CRM_SYSTEM_MCP,
};
for(lpc = 0; lpc < DIMOF(daemons); lpc++) {
if (tools_remove_node_cache(target_uname, daemons[lpc])) {
crm_err("Failed to connect to %s to remove node '%s'", daemons[lpc], target_uname);
crm_node_exit(CRM_EX_ERROR);
}
}
crm_node_exit(CRM_EX_OK);
}
break;
case 'i':
case 'l':
case 'q':
case 'p':
/* Go to pacemakerd */
{
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();
}
break;
}
}
#if SUPPORT_COROSYNC
# include <corosync/quorum.h>
# include <corosync/cpg.h>
static void
try_corosync(int command, enum cluster_type_e stack)
{
int rc = 0;
int quorate = 0;
uint32_t quorum_type = 0;
unsigned int nodeid = 0;
cpg_handle_t c_handle = 0;
quorum_handle_t q_handle = 0;
switch (command) {
case 'q':
/* Go direct to the Quorum API */
rc = quorum_initialize(&q_handle, NULL, &quorum_type);
if (rc != CS_OK) {
crm_err("Could not connect to the Quorum API: %d", rc);
return;
}
rc = quorum_getquorate(q_handle, &quorate);
if (rc != CS_OK) {
crm_err("Could not obtain the current Quorum API state: %d", rc);
return;
}
printf("%d\n", quorate);
quorum_finalize(q_handle);
crm_node_exit(CRM_EX_OK);
case 'i':
/* Go direct to the CPG API */
rc = cpg_initialize(&c_handle, NULL);
if (rc != CS_OK) {
crm_err("Could not connect to the Cluster Process Group API: %d", rc);
return;
}
rc = cpg_local_get(c_handle, &nodeid);
if (rc != CS_OK) {
crm_err("Could not get local node id from the CPG API");
return;
}
printf("%u\n", nodeid);
cpg_finalize(c_handle);
crm_node_exit(CRM_EX_OK);
default:
break;
}
}
#endif
int set_cluster_type(enum cluster_type_e type);
int
main(int argc, char **argv)
{
int flag = 0;
int argerr = 0;
uint32_t nodeid = 0;
gboolean force_flag = FALSE;
gboolean dangerous_cmd = FALSE;
enum cluster_type_e try_stack = pcmk_cluster_unknown;
int option_index = 0;
crm_peer_init();
crm_log_cli_init("crm_node");
crm_set_options(NULL, "command [options]", long_options,
"Tool for displaying low-level node information");
while (flag >= 0) {
flag = crm_get_option(argc, argv, &option_index);
switch (flag) {
case -1:
break;
case 'V':
crm_bump_log_level(argc, argv);
break;
case '$':
case '?':
crm_help(flag, CRM_EX_OK);
break;
case 'Q':
// currently unused
break;
case 'C':
set_cluster_type(pcmk_cluster_corosync);
break;
case 'f':
force_flag = TRUE;
break;
case 'R':
command = flag;
dangerous_cmd = TRUE;
target_uname = optarg;
break;
case 'N':
command = flag;
nodeid = crm_parse_int(optarg, NULL);
break;
case 'p':
case 'q':
case 'i':
case 'l':
case 'n':
command = flag;
break;
default:
++argerr;
break;
}
}
if (optind > argc) {
++argerr;
}
if (argerr) {
crm_help('?', CRM_EX_USAGE);
}
if (dangerous_cmd && 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");
crm_node_exit(CRM_EX_USAGE);
}
if (command == 'n') {
- const char *name = getenv("OCF_RESKEY_" CRM_META "_" XML_LRM_ATTR_TARGET);
- if(name == NULL) {
- name = get_local_node_name();
- }
- fprintf(stdout, "%s\n", name);
- crm_node_exit(CRM_EX_OK);
+ print_node_name();
} else if (command == 'N') {
fprintf(stdout, "%s\n", get_node_name(nodeid));
crm_node_exit(CRM_EX_OK);
}
try_stack = get_cluster_type();
crm_debug("Attempting to process -%c command for cluster type: %s", command,
name_for_cluster_type(try_stack));
#if SUPPORT_COROSYNC
if (try_stack == pcmk_cluster_corosync) {
try_corosync(command, try_stack);
}
#endif
try_pacemaker(command, try_stack);
// We only get here if command hasn't been handled
crm_node_exit(CRM_EX_ERROR);
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Mon, May 12, 5:05 PM (22 h, 5 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1753932
Default Alt Text
(18 KB)
Attached To
Mode
rP Pacemaker
Attached
Detach File
Event Timeline
Log In to Comment