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
326
327
328 run_controller_mainloop(0, false);
329 }
330 }
331
332 static int
333 cib_remove_node(long id, const char *name)
334 {
335 int rc;
336 cib_t *cib = NULL;
337 xmlNode *node = NULL;
338 xmlNode *node_state = NULL;
339
340 crm_trace("Removing %s from the CIB", name);
341
342 if(name == NULL && id == 0) {
343 return -ENOTUNIQ;
344 }
345
346 node = create_xml_node(NULL, XML_CIB_TAG_NODE);
347 node_state = create_xml_node(NULL, XML_CIB_TAG_STATE);
348
349 crm_xml_add(node, XML_ATTR_UNAME, name);
350 crm_xml_add(node_state, XML_ATTR_UNAME, name);
351 if (id > 0) {
352 crm_xml_set_id(node, "%ld", id);
353 crm_xml_add(node_state, XML_ATTR_ID, ID(node));
354 }
355
356 cib = cib_new();
357 cib->cmds->signon(cib, crm_system_name, cib_command);
358
359 rc = cib->cmds->remove(cib, XML_CIB_TAG_NODES, node, cib_sync_call);
360 if (rc != pcmk_ok) {
361 printf("Could not remove %s[%ld] from " XML_CIB_TAG_NODES ": %s",
362 name, id, pcmk_strerror(rc));
363 }
364 rc = cib->cmds->remove(cib, XML_CIB_TAG_STATUS, node_state, cib_sync_call);
365 if (rc != pcmk_ok) {
366 printf("Could not remove %s[%ld] from " XML_CIB_TAG_STATUS ": %s",
367 name, id, pcmk_strerror(rc));
368 }
369
370 cib__clean_up_connection(&cib);
371 return rc;
372 }
373
374 static int
375 controller_remove_node(const char *node_name, long nodeid)
376 {
377 pcmk_ipc_api_t *controld_api = NULL;
378 int rc;
379
380
381 rc = pcmk_new_ipc_api(&controld_api, pcmk_ipc_controld);
382 if (rc != pcmk_rc_ok) {
383 fprintf(stderr, "error: Could not connect to controller: %s\n",
384 pcmk_rc_str(rc));
385 return ENOTCONN;
386 }
387
388
389 rc = pcmk_connect_ipc(controld_api, pcmk_ipc_dispatch_sync);
390 if (rc != pcmk_rc_ok) {
391 fprintf(stderr, "error: Could not connect to controller: %s\n",
392 pcmk_rc_str(rc));
393 pcmk_free_ipc_api(controld_api);
394 return rc;
395 }
396
397 rc = pcmk_ipc_purge_node(controld_api, node_name, nodeid);
398 if (rc != pcmk_rc_ok) {
399 fprintf(stderr,
400 "error: Could not clear node from controller's cache: %s\n",
401 pcmk_rc_str(rc));
402 }
403
404 pcmk_free_ipc_api(controld_api);
405 return pcmk_rc_ok;
406 }
407
408 static int
409 tools_remove_node_cache(const char *node_name, long nodeid, const char *target)
410 {
411 int rc = -1;
412 crm_ipc_t *conn = NULL;
413 xmlNode *cmd = NULL;
414
415 conn = crm_ipc_new(target, 0);
416 if (!conn) {
417 return -ENOTCONN;
418 }
419 if (!crm_ipc_connect(conn)) {
420 crm_perror(LOG_ERR, "Connection to %s failed", target);
421 crm_ipc_destroy(conn);
422 return -ENOTCONN;
423 }
424
425 crm_trace("Removing %s[%ld] from the %s membership cache",
426 node_name, nodeid, target);
427
428 if(pcmk__str_eq(target, T_ATTRD, pcmk__str_casei)) {
429 cmd = create_xml_node(NULL, __func__);
430
431 crm_xml_add(cmd, F_TYPE, T_ATTRD);
432 crm_xml_add(cmd, F_ORIG, crm_system_name);
433
434 crm_xml_add(cmd, PCMK__XA_TASK, PCMK__ATTRD_CMD_PEER_REMOVE);
435
436 pcmk__xe_add_node(cmd, node_name, nodeid);
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');
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
583
584
585 run_controller_mainloop(options.nodeid, false);
586 break;
587 case 'l':
588 case 'p':
589 run_controller_mainloop(0, true);
590 break;
591 default:
592 break;
593 }
594
595 done:
596 g_strfreev(processed_args);
597 pcmk__free_arg_context(context);
598
599 pcmk__output_and_clear_error(&error, NULL);
600 return crm_exit(exit_code);
601 }