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