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