This source file includes following definitions.
- command_cb
- name_cb
- remove_cb
- sort_node
- controller_event_cb
- run_controller_mainloop
- print_node_name
- cib_remove_node
- controller_remove_node
- tools_remove_node_cache
- remove_node
- build_arg_context
- main
1
2
3
4
5
6
7
8
9
10 #include <crm_internal.h>
11
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <errno.h>
15 #include <sys/types.h>
16
17 #include <crm/crm.h>
18 #include <crm/common/cmdline_internal.h>
19 #include <crm/common/output_internal.h>
20 #include <crm/common/mainloop.h>
21 #include <crm/msg_xml.h>
22 #include <crm/cib.h>
23 #include <crm/cib/internal.h>
24 #include <crm/common/ipc_controld.h>
25 #include <crm/common/attrd_internal.h>
26
27 #define SUMMARY "crm_node - Tool for displaying low-level node information"
28
29 struct {
30 gboolean corosync;
31 gboolean dangerous_cmd;
32 gboolean force_flag;
33 char command;
34 int nodeid;
35 char *target_uname;
36 } options = {
37 .command = '\0',
38 .force_flag = FALSE
39 };
40
41 gboolean command_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error);
42 gboolean name_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error);
43 gboolean remove_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error);
44
45 static GMainLoop *mainloop = NULL;
46 static crm_exit_t exit_code = CRM_EX_OK;
47
48 #define INDENT " "
49
50 static GOptionEntry command_entries[] = {
51 { "cluster-id", 'i', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
52 "Display this node's cluster id",
53 NULL },
54 { "list", 'l', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
55 "Display all known members (past and present) of this cluster",
56 NULL },
57 { "name", 'n', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
58 "Display the name used by the cluster for this node",
59 NULL },
60 { "partition", 'p', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
61 "Display the members of this partition",
62 NULL },
63 { "quorum", 'q', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
64 "Display a 1 if our partition has quorum, 0 if not",
65 NULL },
66 { "name-for-id", 'N', 0, G_OPTION_ARG_CALLBACK, name_cb,
67 "Display the name used by the cluster for the node with the specified ID",
68 "ID" },
69 { "remove", 'R', 0, G_OPTION_ARG_CALLBACK, remove_cb,
70 "(Advanced) Remove the (stopped) node with the specified name from Pacemaker's\n"
71 INDENT "configuration and caches (the node must already have been removed from\n"
72 INDENT "the underlying cluster stack configuration",
73 "NAME" },
74
75 { NULL }
76 };
77
78 static GOptionEntry addl_entries[] = {
79 { "force", 'f', 0, G_OPTION_ARG_NONE, &options.force_flag,
80 NULL,
81 NULL },
82 #if SUPPORT_COROSYNC
83
84 { "corosync", 'C', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &options.corosync,
85 NULL,
86 NULL },
87 #endif
88
89
90
91 { NULL }
92 };
93
94 gboolean
95 command_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
96 if (pcmk__str_eq("-i", option_name, pcmk__str_casei) || pcmk__str_eq("--cluster-id", option_name, pcmk__str_casei)) {
97 options.command = 'i';
98 } else if (pcmk__str_eq("-l", option_name, pcmk__str_casei) || pcmk__str_eq("--list", option_name, pcmk__str_casei)) {
99 options.command = 'l';
100 } else if (pcmk__str_eq("-n", option_name, pcmk__str_casei) || pcmk__str_eq("--name", option_name, pcmk__str_casei)) {
101 options.command = 'n';
102 } else if (pcmk__str_eq("-p", option_name, pcmk__str_casei) || pcmk__str_eq("--partition", option_name, pcmk__str_casei)) {
103 options.command = 'p';
104 } else if (pcmk__str_eq("-q", option_name, pcmk__str_casei) || pcmk__str_eq("--quorum", option_name, pcmk__str_casei)) {
105 options.command = 'q';
106 } else {
107 g_set_error(error, PCMK__EXITC_ERROR, CRM_EX_INVALID_PARAM, "Unknown param passed to command_cb: %s\n", option_name);
108 return FALSE;
109 }
110
111 return TRUE;
112 }
113
114 gboolean
115 name_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
116 options.command = 'N';
117 pcmk__scan_min_int(optarg, &(options.nodeid), 0);
118 return TRUE;
119 }
120
121 gboolean
122 remove_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
123 if (optarg == NULL) {
124 crm_err("-R option requires an argument");
125 g_set_error(error, PCMK__EXITC_ERROR, CRM_EX_INVALID_PARAM, "-R option requires an argument");
126 return FALSE;
127 }
128
129 options.command = 'R';
130 options.dangerous_cmd = TRUE;
131 pcmk__str_update(&options.target_uname, optarg);
132 return TRUE;
133 }
134
135 static gint
136 sort_node(gconstpointer a, gconstpointer b)
137 {
138 const pcmk_controld_api_node_t *node_a = a;
139 const pcmk_controld_api_node_t *node_b = b;
140
141 return pcmk__numeric_strcasecmp((node_a->uname? node_a->uname : ""),
142 (node_b->uname? node_b->uname : ""));
143 }
144
145 static void
146 controller_event_cb(pcmk_ipc_api_t *controld_api,
147 enum pcmk_ipc_event event_type, crm_exit_t status,
148 void *event_data, void *user_data)
149 {
150 pcmk_controld_api_reply_t *reply = event_data;
151
152 switch (event_type) {
153 case pcmk_ipc_event_disconnect:
154 if (exit_code == CRM_EX_DISCONNECT) {
155 fprintf(stderr, "error: Lost connection to controller\n");
156 }
157 goto done;
158 break;
159
160 case pcmk_ipc_event_reply:
161 break;
162
163 default:
164 return;
165 }
166
167 if (status != CRM_EX_OK) {
168 fprintf(stderr, "error: Bad reply from controller: %s\n",
169 crm_exit_str(status));
170 goto done;
171 }
172
173
174 switch (options.command) {
175 case 'i':
176 if (reply->reply_type != pcmk_controld_reply_info) {
177 fprintf(stderr,
178 "error: Unknown reply type %d from controller\n",
179 reply->reply_type);
180 goto done;
181 }
182 if (reply->data.node_info.id == 0) {
183 fprintf(stderr,
184 "error: Controller reply did not contain node ID\n");
185 exit_code = CRM_EX_PROTOCOL;
186 goto done;
187 }
188 printf("%d\n", reply->data.node_info.id);
189 break;
190
191 case 'n':
192 case 'N':
193 if (reply->reply_type != pcmk_controld_reply_info) {
194 fprintf(stderr,
195 "error: Unknown reply type %d from controller\n",
196 reply->reply_type);
197 goto done;
198 }
199 if (reply->data.node_info.uname == NULL) {
200 fprintf(stderr, "Node is not known to cluster\n");
201 exit_code = CRM_EX_NOHOST;
202 goto done;
203 }
204 printf("%s\n", reply->data.node_info.uname);
205 break;
206
207 case 'q':
208 if (reply->reply_type != pcmk_controld_reply_info) {
209 fprintf(stderr,
210 "error: Unknown reply type %d from controller\n",
211 reply->reply_type);
212 goto done;
213 }
214 printf("%d\n", reply->data.node_info.have_quorum);
215 if (!(reply->data.node_info.have_quorum)) {
216 exit_code = CRM_EX_QUORUM;
217 goto done;
218 }
219 break;
220
221 case 'l':
222 case 'p':
223 if (reply->reply_type != pcmk_controld_reply_nodes) {
224 fprintf(stderr,
225 "error: Unknown reply type %d from controller\n",
226 reply->reply_type);
227 goto done;
228 }
229 reply->data.nodes = g_list_sort(reply->data.nodes, sort_node);
230 for (GList *node_iter = reply->data.nodes;
231 node_iter != NULL; node_iter = node_iter->next) {
232
233 pcmk_controld_api_node_t *node = node_iter->data;
234 const char *uname = (node->uname? node->uname : "");
235 const char *state = (node->state? node->state : "");
236
237 if (options.command == 'l') {
238 printf("%lu %s %s\n",
239 (unsigned long) node->id, uname, state);
240
241
242 } else if (!strcmp(state, "member")) {
243 printf("%s ", uname);
244 }
245 }
246 if (options.command == 'p') {
247 printf("\n");
248 }
249 break;
250
251 default:
252 fprintf(stderr, "internal error: Controller reply not expected\n");
253 exit_code = CRM_EX_SOFTWARE;
254 goto done;
255 }
256
257
258 exit_code = CRM_EX_OK;
259 done:
260 pcmk_disconnect_ipc(controld_api);
261 pcmk_quit_main_loop(mainloop, 10);
262 }
263
264 static void
265 run_controller_mainloop(uint32_t nodeid, bool list_nodes)
266 {
267 pcmk_ipc_api_t *controld_api = NULL;
268 int rc;
269
270
271 exit_code = CRM_EX_DISCONNECT;
272
273
274 rc = pcmk_new_ipc_api(&controld_api, pcmk_ipc_controld);
275 if (rc != pcmk_rc_ok) {
276 fprintf(stderr, "error: Could not connect to controller: %s\n",
277 pcmk_rc_str(rc));
278 return;
279 }
280 pcmk_register_ipc_callback(controld_api, controller_event_cb, NULL);
281
282
283 rc = pcmk_connect_ipc(controld_api, pcmk_ipc_dispatch_main);
284 if (rc != pcmk_rc_ok) {
285 fprintf(stderr, "error: Could not connect to controller: %s\n",
286 pcmk_rc_str(rc));
287 exit_code = pcmk_rc2exitc(rc);
288 return;
289 }
290
291 if (list_nodes) {
292 rc = pcmk_controld_api_list_nodes(controld_api);
293 } else {
294 rc = pcmk_controld_api_node_info(controld_api, nodeid);
295 }
296 if (rc != pcmk_rc_ok) {
297 fprintf(stderr, "error: Could not ping controller: %s\n",
298 pcmk_rc_str(rc));
299 pcmk_disconnect_ipc(controld_api);
300 exit_code = pcmk_rc2exitc(rc);
301 return;
302 }
303
304
305 mainloop = g_main_loop_new(NULL, FALSE);
306 g_main_loop_run(mainloop);
307 g_main_loop_unref(mainloop);
308 mainloop = NULL;
309 pcmk_free_ipc_api(controld_api);
310 }
311
312 static void
313 print_node_name(void)
314 {
315
316 const char *name = getenv("OCF_RESKEY_" CRM_META "_" XML_LRM_ATTR_TARGET);
317
318 if (name != NULL) {
319 printf("%s\n", name);
320 exit_code = CRM_EX_OK;
321 return;
322
323 } else {
324
325 run_controller_mainloop(0, false);
326 }
327 }
328
329 static int
330 cib_remove_node(long id, const char *name)
331 {
332 int rc;
333 cib_t *cib = NULL;
334 xmlNode *node = NULL;
335 xmlNode *node_state = NULL;
336
337 crm_trace("Removing %s from the CIB", name);
338
339 if(name == NULL && id == 0) {
340 return -ENOTUNIQ;
341 }
342
343 node = create_xml_node(NULL, XML_CIB_TAG_NODE);
344 node_state = create_xml_node(NULL, XML_CIB_TAG_STATE);
345
346 crm_xml_add(node, XML_ATTR_UNAME, name);
347 crm_xml_add(node_state, XML_ATTR_UNAME, name);
348 if (id > 0) {
349 crm_xml_set_id(node, "%ld", id);
350 crm_xml_add(node_state, XML_ATTR_ID, ID(node));
351 }
352
353 cib = cib_new();
354 cib->cmds->signon(cib, crm_system_name, cib_command);
355
356 rc = cib->cmds->remove(cib, XML_CIB_TAG_NODES, node, cib_sync_call);
357 if (rc != pcmk_ok) {
358 printf("Could not remove %s[%ld] from " XML_CIB_TAG_NODES ": %s",
359 name, id, pcmk_strerror(rc));
360 }
361 rc = cib->cmds->remove(cib, XML_CIB_TAG_STATUS, node_state, cib_sync_call);
362 if (rc != pcmk_ok) {
363 printf("Could not remove %s[%ld] from " XML_CIB_TAG_STATUS ": %s",
364 name, id, pcmk_strerror(rc));
365 }
366
367 cib__clean_up_connection(&cib);
368 return rc;
369 }
370
371 static int
372 controller_remove_node(const char *node_name, long nodeid)
373 {
374 pcmk_ipc_api_t *controld_api = NULL;
375 int rc;
376
377
378 rc = pcmk_new_ipc_api(&controld_api, pcmk_ipc_controld);
379 if (rc != pcmk_rc_ok) {
380 fprintf(stderr, "error: Could not connect to controller: %s\n",
381 pcmk_rc_str(rc));
382 return ENOTCONN;
383 }
384
385
386 rc = pcmk_connect_ipc(controld_api, pcmk_ipc_dispatch_sync);
387 if (rc != pcmk_rc_ok) {
388 fprintf(stderr, "error: Could not connect to controller: %s\n",
389 pcmk_rc_str(rc));
390 pcmk_free_ipc_api(controld_api);
391 return rc;
392 }
393
394 rc = pcmk_ipc_purge_node(controld_api, node_name, nodeid);
395 if (rc != pcmk_rc_ok) {
396 fprintf(stderr,
397 "error: Could not clear node from controller's cache: %s\n",
398 pcmk_rc_str(rc));
399 }
400
401 pcmk_free_ipc_api(controld_api);
402 return pcmk_rc_ok;
403 }
404
405 static int
406 tools_remove_node_cache(const char *node_name, long nodeid, const char *target)
407 {
408 int rc = -1;
409 crm_ipc_t *conn = NULL;
410 xmlNode *cmd = NULL;
411
412 conn = crm_ipc_new(target, 0);
413 if (!conn) {
414 return -ENOTCONN;
415 }
416 if (!crm_ipc_connect(conn)) {
417 crm_perror(LOG_ERR, "Connection to %s failed", target);
418 crm_ipc_destroy(conn);
419 return -ENOTCONN;
420 }
421
422 crm_trace("Removing %s[%ld] from the %s membership cache",
423 node_name, nodeid, target);
424
425 if(pcmk__str_eq(target, T_ATTRD, pcmk__str_casei)) {
426 cmd = create_xml_node(NULL, __func__);
427
428 crm_xml_add(cmd, F_TYPE, T_ATTRD);
429 crm_xml_add(cmd, F_ORIG, crm_system_name);
430
431 crm_xml_add(cmd, PCMK__XA_TASK, PCMK__ATTRD_CMD_PEER_REMOVE);
432
433 pcmk__xe_add_node(cmd, node_name, nodeid);
434
435 } else {
436 cmd = create_request(CRM_OP_RM_NODE_CACHE, NULL, NULL, target,
437 crm_system_name, NULL);
438 if (nodeid > 0) {
439 crm_xml_set_id(cmd, "%ld", nodeid);
440 }
441 crm_xml_add(cmd, XML_ATTR_UNAME, node_name);
442 }
443
444 rc = crm_ipc_send(conn, cmd, 0, 0, NULL);
445 crm_debug("%s peer cache cleanup for %s (%ld): %d",
446 target, node_name, nodeid, rc);
447
448 if (rc > 0) {
449
450 rc = cib_remove_node(nodeid, node_name);
451 }
452
453 if (conn) {
454 crm_ipc_close(conn);
455 crm_ipc_destroy(conn);
456 }
457 free_xml(cmd);
458 return rc > 0 ? 0 : rc;
459 }
460
461 static void
462 remove_node(const char *target_uname)
463 {
464 int rc;
465 int d = 0;
466 long nodeid = 0;
467 const char *node_name = NULL;
468 char *endptr = NULL;
469 const char *daemons[] = {
470 "stonith-ng",
471 T_ATTRD,
472 CRM_SYSTEM_MCP,
473 };
474
475
476 errno = 0;
477 nodeid = strtol(target_uname, &endptr, 10);
478 if ((errno != 0) || (endptr == target_uname) || (*endptr != '\0')
479 || (nodeid <= 0)) {
480
481 nodeid = 0;
482 node_name = target_uname;
483 }
484
485 rc = controller_remove_node(node_name, nodeid);
486 if (rc != pcmk_rc_ok) {
487 exit_code = pcmk_rc2exitc(rc);
488 return;
489 }
490
491 for (d = 0; d < PCMK__NELEM(daemons); d++) {
492 if (tools_remove_node_cache(node_name, nodeid, daemons[d])) {
493 crm_err("Failed to connect to %s to remove node '%s'",
494 daemons[d], target_uname);
495 exit_code = CRM_EX_ERROR;
496 return;
497 }
498 }
499 exit_code = CRM_EX_OK;
500 }
501
502 static GOptionContext *
503 build_arg_context(pcmk__common_args_t *args, GOptionGroup *group) {
504 GOptionContext *context = NULL;
505
506 GOptionEntry extra_prog_entries[] = {
507 { "quiet", 'Q', 0, G_OPTION_ARG_NONE, &(args->quiet),
508 "Be less descriptive in output.",
509 NULL },
510
511 { NULL }
512 };
513
514 context = pcmk__build_arg_context(args, NULL, &group, NULL);
515
516
517
518
519 pcmk__add_main_args(context, extra_prog_entries);
520
521 pcmk__add_arg_group(context, "commands", "Commands:",
522 "Show command help", command_entries);
523 pcmk__add_arg_group(context, "additional", "Additional Options:",
524 "Show additional options", addl_entries);
525 return context;
526 }
527
528 int
529 main(int argc, char **argv)
530 {
531 GError *error = NULL;
532
533 GOptionGroup *output_group = NULL;
534 pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
535 gchar **processed_args = pcmk__cmdline_preproc(argv, "NR");
536 GOptionContext *context = build_arg_context(args, output_group);
537
538 if (!g_option_context_parse_strv(context, &processed_args, &error)) {
539 exit_code = CRM_EX_USAGE;
540 goto done;
541 }
542
543 pcmk__cli_init_logging("crm_node", args->verbosity);
544
545 if (args->version) {
546 g_strfreev(processed_args);
547 pcmk__free_arg_context(context);
548
549 pcmk__cli_help('v', CRM_EX_OK);
550 }
551
552 if (options.command == 0) {
553 char *help = g_option_context_get_help(context, TRUE, NULL);
554
555 fprintf(stderr, "%s", help);
556 g_free(help);
557 exit_code = CRM_EX_USAGE;
558 goto done;
559 }
560
561 if (options.dangerous_cmd && options.force_flag == FALSE) {
562 fprintf(stderr, "The supplied command is considered dangerous."
563 " To prevent accidental destruction of the cluster,"
564 " the --force flag is required in order to proceed.\n");
565 exit_code = CRM_EX_USAGE;
566 goto done;
567 }
568
569 switch (options.command) {
570 case 'n':
571 print_node_name();
572 break;
573 case 'R':
574 remove_node(options.target_uname);
575 break;
576 case 'i':
577 case 'q':
578 case 'N':
579 run_controller_mainloop(options.nodeid, false);
580 break;
581 case 'l':
582 case 'p':
583 run_controller_mainloop(0, true);
584 break;
585 default:
586 break;
587 }
588
589 done:
590 g_strfreev(processed_args);
591 pcmk__free_arg_context(context);
592
593 pcmk__output_and_clear_error(error, NULL);
594 return crm_exit(exit_code);
595 }