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 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_OK);
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 }