root/tools/crm_node.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. command_cb
  2. name_cb
  3. remove_cb
  4. PCMK__OUTPUT_ARGS
  5. PCMK__OUTPUT_ARGS
  6. PCMK__OUTPUT_ARGS
  7. PCMK__OUTPUT_ARGS
  8. PCMK__OUTPUT_ARGS
  9. PCMK__OUTPUT_ARGS
  10. PCMK__OUTPUT_ARGS
  11. PCMK__OUTPUT_ARGS
  12. PCMK__OUTPUT_ARGS
  13. PCMK__OUTPUT_ARGS
  14. sort_node
  15. controller_event_cb
  16. run_controller_mainloop
  17. print_node_id
  18. print_node_name
  19. print_quorum
  20. remove_from_section
  21. purge_node_from_cib
  22. purge_node_from
  23. purge_node_from_fencer
  24. remove_node
  25. build_arg_context
  26. main

   1 /*
   2  * Copyright 2004-2024 the Pacemaker project contributors
   3  *
   4  * The version control history for this file may have further details.
   5  *
   6  * This source code is licensed under the GNU General Public License version 2
   7  * or later (GPLv2+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #include <crm_internal.h>
  11 
  12 #include <inttypes.h>
  13 #include <stdio.h>
  14 #include <stdlib.h>
  15 #include <errno.h>
  16 #include <sys/types.h>
  17 
  18 #include <crm/crm.h>
  19 #include <crm/common/cmdline_internal.h>
  20 #include <crm/common/output_internal.h>
  21 #include <crm/common/mainloop.h>
  22 #include <crm/common/xml.h>
  23 #include <crm/cib.h>
  24 #include <crm/cib/internal.h>
  25 #include <crm/common/ipc_controld.h>
  26 #include <crm/common/attrs_internal.h>
  27 
  28 #include <pacemaker-internal.h>
  29 
  30 #define SUMMARY "crm_node - Tool for displaying low-level node information"
  31 
  32 struct {
  33     gboolean corosync;
  34     gboolean dangerous_cmd;
  35     gboolean force_flag;
  36     char command;
  37     int nodeid;
  38     char *target_uname;
  39 } options = {
  40     .command = '\0',
  41     .force_flag = FALSE
  42 };
  43 
  44 gboolean command_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error);
  45 gboolean name_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error);
  46 gboolean remove_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error);
  47 
  48 static GError *error = NULL;
  49 static GMainLoop *mainloop = NULL;
  50 static crm_exit_t exit_code = CRM_EX_OK;
  51 static pcmk__output_t *out = NULL;
  52 
  53 #define INDENT "                           "
  54 
  55 static GOptionEntry command_entries[] = {
  56     { "cluster-id", 'i', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
  57       "Display this node's cluster id",
  58       NULL },
  59     { "list", 'l', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
  60       "Display all known members (past and present) of this cluster",
  61       NULL },
  62     { "name", 'n', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
  63       "Display the name used by the cluster for this node",
  64       NULL },
  65     { "partition", 'p', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
  66       "Display the members of this partition",
  67       NULL },
  68     { "quorum", 'q', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
  69       "Display a 1 if our partition has quorum, 0 if not",
  70       NULL },
  71     { "name-for-id", 'N', 0, G_OPTION_ARG_CALLBACK, name_cb,
  72       "Display the name used by the cluster for the node with the specified ID",
  73       "ID" },
  74     { "remove", 'R', 0, G_OPTION_ARG_CALLBACK, remove_cb,
  75       "(Advanced) Remove the (stopped) node with the specified name from Pacemaker's\n"
  76       INDENT "configuration and caches (the node must already have been removed from\n"
  77       INDENT "the underlying cluster stack configuration",
  78       "NAME" },
  79 
  80     { NULL }
  81 };
  82 
  83 static GOptionEntry addl_entries[] = {
  84     { "force", 'f', 0, G_OPTION_ARG_NONE, &options.force_flag,
  85       NULL,
  86       NULL },
  87 #if SUPPORT_COROSYNC
  88     /* Unused and deprecated */
  89     { "corosync", 'C', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &options.corosync,
  90       NULL,
  91       NULL },
  92 #endif
  93 
  94     // @TODO add timeout option for when IPC replies are needed
  95 
  96     { NULL }
  97 };
  98 
  99 static pcmk__supported_format_t formats[] = {
 100     PCMK__SUPPORTED_FORMAT_NONE,
 101     PCMK__SUPPORTED_FORMAT_TEXT,
 102     PCMK__SUPPORTED_FORMAT_XML,
 103     { NULL, NULL, NULL }
 104 };
 105 
 106 gboolean
 107 command_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 108     if (pcmk__str_eq("-i", option_name, pcmk__str_casei) || pcmk__str_eq("--cluster-id", option_name, pcmk__str_casei)) {
 109         options.command = 'i';
 110     } else if (pcmk__str_eq("-l", option_name, pcmk__str_casei) || pcmk__str_eq("--list", option_name, pcmk__str_casei)) {
 111         options.command = 'l';
 112     } else if (pcmk__str_eq("-n", option_name, pcmk__str_casei) || pcmk__str_eq("--name", option_name, pcmk__str_casei)) {
 113         options.command = 'n';
 114     } else if (pcmk__str_eq("-p", option_name, pcmk__str_casei) || pcmk__str_eq("--partition", option_name, pcmk__str_casei)) {
 115         options.command = 'p';
 116     } else if (pcmk__str_eq("-q", option_name, pcmk__str_casei) || pcmk__str_eq("--quorum", option_name, pcmk__str_casei)) {
 117         options.command = 'q';
 118     } else {
 119         g_set_error(error, PCMK__EXITC_ERROR, CRM_EX_INVALID_PARAM, "Unknown param passed to command_cb: %s", option_name);
 120         return FALSE;
 121     }
 122 
 123     return TRUE;
 124 }
 125 
 126 gboolean
 127 name_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 128     options.command = 'N';
 129     pcmk__scan_min_int(optarg, &(options.nodeid), 0);
 130     return TRUE;
 131 }
 132 
 133 gboolean
 134 remove_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 135     if (optarg == NULL) {
 136         g_set_error(error, PCMK__EXITC_ERROR, CRM_EX_INVALID_PARAM, "-R option requires an argument");
 137         return FALSE;
 138     }
 139 
 140     options.command = 'R';
 141     options.dangerous_cmd = TRUE;
 142     pcmk__str_update(&options.target_uname, optarg);
 143     return TRUE;
 144 }
 145 
 146 PCMK__OUTPUT_ARGS("node-id", "uint32_t")
     /* [previous][next][first][last][top][bottom][index][help] */
 147 static int
 148 node_id_default(pcmk__output_t *out, va_list args) {
 149     uint32_t node_id = va_arg(args, uint32_t);
 150 
 151     out->info(out, "%" PRIu32, node_id);
 152     return pcmk_rc_ok;
 153 }
 154 
 155 PCMK__OUTPUT_ARGS("node-id", "uint32_t")
     /* [previous][next][first][last][top][bottom][index][help] */
 156 static int
 157 node_id_xml(pcmk__output_t *out, va_list args) {
 158     uint32_t node_id = va_arg(args, uint32_t);
 159 
 160     char *id_s = crm_strdup_printf("%" PRIu32, node_id);
 161 
 162     pcmk__output_create_xml_node(out, PCMK_XE_NODE_INFO,
 163                                  PCMK_XA_NODEID, id_s,
 164                                  NULL);
 165 
 166     free(id_s);
 167     return pcmk_rc_ok;
 168 }
 169 
 170 PCMK__OUTPUT_ARGS("simple-node-list", "GList *")
     /* [previous][next][first][last][top][bottom][index][help] */
 171 static int
 172 simple_node_list_default(pcmk__output_t *out, va_list args)
 173 {
 174     GList *nodes = va_arg(args, GList *);
 175 
 176     for (GList *node_iter = nodes; node_iter != NULL; node_iter = node_iter->next) {
 177         pcmk_controld_api_node_t *node = node_iter->data;
 178         out->info(out, "%" PRIu32 " %s %s", node->id, pcmk__s(node->uname, ""),
 179                   pcmk__s(node->state, ""));
 180     }
 181 
 182     return pcmk_rc_ok;
 183 }
 184 
 185 PCMK__OUTPUT_ARGS("simple-node-list", "GList *")
     /* [previous][next][first][last][top][bottom][index][help] */
 186 static int
 187 simple_node_list_xml(pcmk__output_t *out, va_list args)
 188 {
 189     GList *nodes = va_arg(args, GList *);
 190 
 191     out->begin_list(out, NULL, NULL, PCMK_XE_NODES);
 192 
 193     for (GList *node_iter = nodes; node_iter != NULL; node_iter = node_iter->next) {
 194         pcmk_controld_api_node_t *node = node_iter->data;
 195         char *id_s = crm_strdup_printf("%" PRIu32, node->id);
 196 
 197         pcmk__output_create_xml_node(out, PCMK_XE_NODE,
 198                                      PCMK_XA_ID, id_s,
 199                                      PCMK_XA_NAME, node->uname,
 200                                      PCMK_XA_STATE, node->state,
 201                                      NULL);
 202 
 203         free(id_s);
 204     }
 205 
 206     out->end_list(out);
 207 
 208     return pcmk_rc_ok;
 209 }
 210 
 211 PCMK__OUTPUT_ARGS("node-name", "uint32_t", "const char *")
     /* [previous][next][first][last][top][bottom][index][help] */
 212 static int
 213 node_name_default(pcmk__output_t *out, va_list args) {
 214     uint32_t node_id G_GNUC_UNUSED = va_arg(args, uint32_t);
 215     const char *node_name = va_arg(args, const char *);
 216 
 217     out->info(out, "%s", node_name);
 218     return pcmk_rc_ok;
 219 }
 220 
 221 PCMK__OUTPUT_ARGS("node-name", "uint32_t", "const char *")
     /* [previous][next][first][last][top][bottom][index][help] */
 222 static int
 223 node_name_xml(pcmk__output_t *out, va_list args) {
 224     uint32_t node_id = va_arg(args, uint32_t);
 225     const char *node_name = va_arg(args, const char *);
 226 
 227     char *id_s = crm_strdup_printf("%" PRIu32, node_id);
 228 
 229     pcmk__output_create_xml_node(out, PCMK_XE_NODE_INFO,
 230                                  PCMK_XA_NODEID, id_s,
 231                                  PCMK_XA_UNAME, node_name,
 232                                  NULL);
 233 
 234     free(id_s);
 235     return pcmk_rc_ok;
 236 }
 237 
 238 PCMK__OUTPUT_ARGS("partition-list", "GList *")
     /* [previous][next][first][last][top][bottom][index][help] */
 239 static int
 240 partition_list_default(pcmk__output_t *out, va_list args)
 241 {
 242     GList *nodes = va_arg(args, GList *);
 243 
 244     GString *buffer = NULL;
 245 
 246     for (GList *node_iter = nodes; node_iter != NULL; node_iter = node_iter->next) {
 247         pcmk_controld_api_node_t *node = node_iter->data;
 248         if (pcmk__str_eq(node->state, "member", pcmk__str_none)) {
 249             pcmk__add_separated_word(&buffer, 128, pcmk__s(node->uname, ""), " ");
 250         }
 251     }
 252 
 253     if (buffer != NULL) {
 254         out->info(out, "%s", buffer->str);
 255         g_string_free(buffer, TRUE);
 256         return pcmk_rc_ok;
 257     }
 258 
 259     return pcmk_rc_no_output;
 260 }
 261 
 262 PCMK__OUTPUT_ARGS("partition-list", "GList *")
     /* [previous][next][first][last][top][bottom][index][help] */
 263 static int
 264 partition_list_xml(pcmk__output_t *out, va_list args)
 265 {
 266     GList *nodes = va_arg(args, GList *);
 267 
 268     out->begin_list(out, NULL, NULL, PCMK_XE_NODES);
 269 
 270     for (GList *node_iter = nodes; node_iter != NULL; node_iter = node_iter->next) {
 271         pcmk_controld_api_node_t *node = node_iter->data;
 272 
 273         if (pcmk__str_eq(node->state, "member", pcmk__str_none)) {
 274             char *id_s = crm_strdup_printf("%" PRIu32, node->id);
 275 
 276             pcmk__output_create_xml_node(out, PCMK_XE_NODE,
 277                                          PCMK_XA_ID, id_s,
 278                                          PCMK_XA_NAME, node->uname,
 279                                          PCMK_XA_STATE, node->state,
 280                                          NULL);
 281             free(id_s);
 282         }
 283     }
 284 
 285     out->end_list(out);
 286     return pcmk_rc_ok;
 287 }
 288 
 289 PCMK__OUTPUT_ARGS("quorum", "bool")
     /* [previous][next][first][last][top][bottom][index][help] */
 290 static int
 291 quorum_default(pcmk__output_t *out, va_list args) {
 292     bool have_quorum = va_arg(args, int);
 293 
 294     out->info(out, "%d", have_quorum);
 295     return pcmk_rc_ok;
 296 }
 297 
 298 PCMK__OUTPUT_ARGS("quorum", "bool")
     /* [previous][next][first][last][top][bottom][index][help] */
 299 static int
 300 quorum_xml(pcmk__output_t *out, va_list args) {
 301     bool have_quorum = va_arg(args, int);
 302 
 303     pcmk__output_create_xml_node(out, PCMK_XE_CLUSTER_INFO,
 304                                  PCMK_XA_QUORUM, pcmk__btoa(have_quorum),
 305                                  NULL);
 306     return pcmk_rc_ok;
 307 }
 308 
 309 static pcmk__message_entry_t fmt_functions[] = {
 310     { "node-id", "default", node_id_default },
 311     { "node-id", "xml", node_id_xml },
 312     { "node-name", "default", node_name_default },
 313     { "node-name", "xml", node_name_xml },
 314     { "partition-list", "default", partition_list_default },
 315     { "partition-list", "xml", partition_list_xml },
 316     { "quorum", "default", quorum_default },
 317     { "quorum", "xml", quorum_xml },
 318     { "simple-node-list", "default", simple_node_list_default },
 319     { "simple-node-list", "xml", simple_node_list_xml },
 320 
 321     { NULL, NULL, NULL }
 322 };
 323 
 324 static gint
 325 sort_node(gconstpointer a, gconstpointer b)
     /* [previous][next][first][last][top][bottom][index][help] */
 326 {
 327     const pcmk_controld_api_node_t *node_a = a;
 328     const pcmk_controld_api_node_t *node_b = b;
 329 
 330     return pcmk__numeric_strcasecmp((node_a->uname? node_a->uname : ""),
 331                                     (node_b->uname? node_b->uname : ""));
 332 }
 333 
 334 static void
 335 controller_event_cb(pcmk_ipc_api_t *controld_api,
     /* [previous][next][first][last][top][bottom][index][help] */
 336                     enum pcmk_ipc_event event_type, crm_exit_t status,
 337                     void *event_data, void *user_data)
 338 {
 339     pcmk_controld_api_reply_t *reply = event_data;
 340 
 341     switch (event_type) {
 342         case pcmk_ipc_event_disconnect:
 343             if (exit_code == CRM_EX_DISCONNECT) { // Unexpected
 344                 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 345                             "Lost connection to controller");
 346             }
 347             goto done;
 348             break;
 349 
 350         case pcmk_ipc_event_reply:
 351             break;
 352 
 353         default:
 354             return;
 355     }
 356 
 357     if (status != CRM_EX_OK) {
 358         exit_code = status;
 359         g_set_error(&error, PCMK__EXITC_ERROR, status,
 360                     "Bad reply from controller: %s",
 361                     crm_exit_str(status));
 362         goto done;
 363     }
 364 
 365     if (reply->reply_type != pcmk_controld_reply_nodes) {
 366         g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_INDETERMINATE,
 367                     "Unknown reply type %d from controller",
 368                     reply->reply_type);
 369         goto done;
 370     }
 371 
 372     reply->data.nodes = g_list_sort(reply->data.nodes, sort_node);
 373 
 374     if (options.command == 'p') {
 375         out->message(out, "partition-list", reply->data.nodes);
 376     } else if (options.command == 'l') {
 377         out->message(out, "simple-node-list", reply->data.nodes);
 378     }
 379 
 380     // Success
 381     exit_code = CRM_EX_OK;
 382 done:
 383     pcmk_disconnect_ipc(controld_api);
 384     pcmk_quit_main_loop(mainloop, 10);
 385 }
 386 
 387 static void
 388 run_controller_mainloop(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 389 {
 390     pcmk_ipc_api_t *controld_api = NULL;
 391     int rc;
 392 
 393     // Set disconnect exit code to handle unexpected disconnects
 394     exit_code = CRM_EX_DISCONNECT;
 395 
 396     // Create controller IPC object
 397     rc = pcmk_new_ipc_api(&controld_api, pcmk_ipc_controld);
 398     if (rc != pcmk_rc_ok) {
 399         g_set_error(&error, PCMK__RC_ERROR, rc,
 400                     "Could not connect to controller: %s",
 401                     pcmk_rc_str(rc));
 402         return;
 403     }
 404     pcmk_register_ipc_callback(controld_api, controller_event_cb, NULL);
 405 
 406     // Connect to controller
 407     rc = pcmk__connect_ipc(controld_api, pcmk_ipc_dispatch_main, 5);
 408     if (rc != pcmk_rc_ok) {
 409         exit_code = pcmk_rc2exitc(rc);
 410         g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 411                     "Could not connect to %s: %s",
 412                     pcmk_ipc_name(controld_api, true), pcmk_rc_str(rc));
 413         return;
 414     }
 415 
 416     rc = pcmk_controld_api_list_nodes(controld_api);
 417 
 418     if (rc != pcmk_rc_ok) {
 419         pcmk_disconnect_ipc(controld_api);
 420         exit_code = pcmk_rc2exitc(rc);
 421         g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 422                     "Could not ping controller: %s", pcmk_rc_str(rc));
 423         return;
 424     }
 425 
 426     // Run main loop to get controller reply via controller_event_cb()
 427     mainloop = g_main_loop_new(NULL, FALSE);
 428     g_main_loop_run(mainloop);
 429     g_main_loop_unref(mainloop);
 430     mainloop = NULL;
 431     pcmk_free_ipc_api(controld_api);
 432 }
 433 
 434 static void
 435 print_node_id(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 436 {
 437     uint32_t nodeid = 0;
 438     int rc = pcmk__query_node_info(out, &nodeid, NULL, NULL, NULL, NULL, NULL,
 439                                    false, 0);
 440 
 441     if (rc != pcmk_rc_ok) {
 442         /* pcmk__query_node_info already sets an error message on the output object,
 443          * so there's no need to call g_set_error here.  That would just create a
 444          * duplicate error message in the output.
 445          */
 446         exit_code = pcmk_rc2exitc(rc);
 447         return;
 448     }
 449 
 450     rc = out->message(out, "node-id", nodeid);
 451 
 452     if (rc != pcmk_rc_ok) {
 453         g_set_error(&error, PCMK__RC_ERROR, rc, "Could not print node ID: %s",
 454                     pcmk_rc_str(rc));
 455     }
 456 
 457     exit_code = pcmk_rc2exitc(rc);
 458 }
 459 
 460 static void
 461 print_node_name(uint32_t nodeid)
     /* [previous][next][first][last][top][bottom][index][help] */
 462 {
 463     int rc = pcmk_rc_ok;
 464     char *node_name = NULL;
 465 
 466     if (nodeid == 0) {
 467         // Check environment first (i.e. when called by resource agent)
 468         const char *name = getenv("OCF_RESKEY_" CRM_META "_"
 469                                   PCMK__META_ON_NODE);
 470 
 471         if (name != NULL) {
 472             rc = out->message(out, "node-name", 0UL, name);
 473             goto done;
 474         }
 475     }
 476 
 477     // Otherwise ask the controller
 478 
 479     /* pcmk__query_node_name already sets an error message on the output object,
 480      * so there's no need to call g_set_error here.  That would just create a
 481      * duplicate error message in the output.
 482      */
 483     rc = pcmk__query_node_name(out, nodeid, &node_name, 0);
 484     if (rc != pcmk_rc_ok) {
 485         exit_code = pcmk_rc2exitc(rc);
 486         return;
 487     }
 488 
 489     rc = out->message(out, "node-name", 0UL, node_name);
 490 
 491 done:
 492     if (node_name != NULL) {
 493         free(node_name);
 494     }
 495 
 496     if (rc != pcmk_rc_ok) {
 497         g_set_error(&error, PCMK__RC_ERROR, rc, "Could not print node name: %s",
 498                     pcmk_rc_str(rc));
 499     }
 500 
 501     exit_code = pcmk_rc2exitc(rc);
 502 }
 503 
 504 static void
 505 print_quorum(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 506 {
 507     bool quorum;
 508     int rc = pcmk__query_node_info(out, NULL, NULL, NULL, NULL, &quorum, NULL,
 509                                    false, 0);
 510 
 511     if (rc != pcmk_rc_ok) {
 512         /* pcmk__query_node_info already sets an error message on the output object,
 513          * so there's no need to call g_set_error here.  That would just create a
 514          * duplicate error message in the output.
 515          */
 516         exit_code = pcmk_rc2exitc(rc);
 517         return;
 518     }
 519 
 520     rc = out->message(out, "quorum", quorum);
 521 
 522     if (rc != pcmk_rc_ok) {
 523         g_set_error(&error, PCMK__RC_ERROR, rc, "Could not print quorum status: %s",
 524                     pcmk_rc_str(rc));
 525     }
 526 
 527     exit_code = pcmk_rc2exitc(rc);
 528 }
 529 
 530 /*!
 531  * \internal
 532  * \brief Extend a transaction by removing a node from a CIB section
 533  *
 534  * \param[in,out] cib        Active CIB connection
 535  * \param[in]     element    CIB element containing node name and/or ID
 536  * \param[in]     section    CIB section that \p element is in
 537  * \param[in]     node_name  Name of node to purge (NULL to leave unspecified)
 538  * \param[in]     node_id    Node ID of node to purge (0 to leave unspecified)
 539  *
 540  * \note At least one of node_name and node_id must be specified.
 541  * \return Standard Pacemaker return code
 542  */
 543 static int
 544 remove_from_section(cib_t *cib, const char *element, const char *section,
     /* [previous][next][first][last][top][bottom][index][help] */
 545                     const char *node_name, long node_id)
 546 {
 547     int rc = pcmk_rc_ok;
 548     xmlNode *xml = pcmk__xe_create(NULL, element);
 549 
 550     crm_xml_add(xml, PCMK_XA_UNAME, node_name);
 551     if (node_id > 0) {
 552         crm_xml_add_ll(xml, PCMK_XA_ID, node_id);
 553     }
 554 
 555     rc = cib->cmds->remove(cib, section, xml, cib_transaction);
 556     free_xml(xml);
 557     return (rc >= 0)? pcmk_rc_ok : pcmk_legacy2rc(rc);
 558 }
 559 
 560 /*!
 561  * \internal
 562  * \brief Purge a node from CIB
 563  *
 564  * \param[in] node_name  Name of node to purge (or NULL to leave unspecified)
 565  * \param[in] node_id    Node ID of node to purge (or 0 to leave unspecified)
 566  *
 567  * \note At least one of node_name and node_id must be specified.
 568  * \return Standard Pacemaker return code
 569  */
 570 static int
 571 purge_node_from_cib(const char *node_name, long node_id)
     /* [previous][next][first][last][top][bottom][index][help] */
 572 {
 573     int rc = pcmk_rc_ok;
 574     int commit_rc = pcmk_rc_ok;
 575     cib_t *cib = NULL;
 576 
 577     // Connect to CIB and start a transaction
 578     cib = cib_new();
 579     if (cib == NULL) {
 580         return ENOTCONN;
 581     }
 582     rc = cib__signon_attempts(cib, crm_system_name, cib_command, 5);
 583     if (rc == pcmk_ok) {
 584         rc = cib->cmds->init_transaction(cib);
 585     }
 586     if (rc != pcmk_ok) {
 587         rc = pcmk_legacy2rc(rc);
 588         cib__clean_up_connection(&cib);
 589         return rc;
 590     }
 591 
 592     // Remove from configuration and status
 593     rc = remove_from_section(cib, PCMK_XE_NODE, PCMK_XE_NODES, node_name,
 594                              node_id);
 595     if (rc == pcmk_rc_ok) {
 596         rc = remove_from_section(cib, PCMK__XE_NODE_STATE, PCMK_XE_STATUS,
 597                                  node_name, node_id);
 598     }
 599 
 600     // Commit the transaction
 601     commit_rc = cib->cmds->end_transaction(cib, (rc == pcmk_rc_ok),
 602                                            cib_sync_call);
 603     cib__clean_up_connection(&cib);
 604 
 605     if ((rc == pcmk_rc_ok) && (commit_rc == pcmk_ok)) {
 606         crm_debug("Purged node %s (%ld) from CIB",
 607                   pcmk__s(node_name, "by ID"), node_id);
 608     }
 609     return rc;
 610 }
 611 
 612 /*!
 613  * \internal
 614  * \brief Purge a node from a single server's peer cache
 615  *
 616  * \param[in] server     IPC server to send request to
 617  * \param[in] node_name  Name of node to purge (or NULL to leave unspecified)
 618  * \param[in] node_id    Node ID of node to purge (or 0 to leave unspecified)
 619  *
 620  * \note At least one of node_name and node_id must be specified.
 621  * \return Standard Pacemaker return code
 622  */
 623 static int
 624 purge_node_from(enum pcmk_ipc_server server, const char *node_name,
     /* [previous][next][first][last][top][bottom][index][help] */
 625                 long node_id)
 626 {
 627     pcmk_ipc_api_t *api = NULL;
 628     int rc;
 629 
 630     rc = pcmk_new_ipc_api(&api, server);
 631     if (rc != pcmk_rc_ok) {
 632         goto done;
 633     }
 634 
 635     rc = pcmk__connect_ipc(api, pcmk_ipc_dispatch_sync, 5);
 636     if (rc != pcmk_rc_ok) {
 637         goto done;
 638     }
 639 
 640     rc = pcmk_ipc_purge_node(api, node_name, node_id);
 641 done:
 642     if (rc != pcmk_rc_ok) { // Debug message already logged on success
 643         g_set_error(&error, PCMK__RC_ERROR, rc,
 644                     "Could not purge node %s from %s: %s",
 645                     pcmk__s(node_name, "by ID"), pcmk_ipc_name(api, true),
 646                     pcmk_rc_str(rc));
 647     }
 648     pcmk_free_ipc_api(api);
 649     return rc;
 650 }
 651 
 652 /*!
 653  * \internal
 654  * \brief Purge a node from the fencer's peer cache
 655  *
 656  * \param[in] node_name  Name of node to purge (or NULL to leave unspecified)
 657  * \param[in] node_id    Node ID of node to purge (or 0 to leave unspecified)
 658  *
 659  * \note At least one of node_name and node_id must be specified.
 660  * \return Standard Pacemaker return code
 661  */
 662 static int
 663 purge_node_from_fencer(const char *node_name, long node_id)
     /* [previous][next][first][last][top][bottom][index][help] */
 664 {
 665     int rc = pcmk_rc_ok;
 666     crm_ipc_t *conn = NULL;
 667     xmlNode *cmd = NULL;
 668 
 669     conn = crm_ipc_new("stonith-ng", 0);
 670     if (conn == NULL) {
 671         rc = ENOTCONN;
 672         exit_code = pcmk_rc2exitc(rc);
 673         g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 674                     "Could not connect to fencer to purge node %s",
 675                     pcmk__s(node_name, "by ID"));
 676         return rc;
 677     }
 678 
 679     rc = pcmk__connect_generic_ipc(conn);
 680     if (rc != pcmk_rc_ok) {
 681         exit_code = pcmk_rc2exitc(rc);
 682         g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 683                     "Could not connect to fencer to purge node %s: %s",
 684                     pcmk__s(node_name, "by ID"), pcmk_rc_str(rc));
 685         crm_ipc_destroy(conn);
 686         return rc;
 687     }
 688 
 689     cmd = create_request(CRM_OP_RM_NODE_CACHE, NULL, NULL, "stonith-ng",
 690                          crm_system_name, NULL);
 691     if (node_id > 0) {
 692         crm_xml_add_ll(cmd, PCMK_XA_ID, node_id);
 693     }
 694     crm_xml_add(cmd, PCMK_XA_UNAME, node_name);
 695 
 696     rc = crm_ipc_send(conn, cmd, 0, 0, NULL);
 697     if (rc >= 0) {
 698         rc = pcmk_rc_ok;
 699         crm_debug("Purged node %s (%ld) from fencer",
 700                   pcmk__s(node_name, "by ID"), node_id);
 701     } else {
 702         rc = pcmk_legacy2rc(rc);
 703         fprintf(stderr, "Could not purge node %s from fencer: %s\n",
 704                 pcmk__s(node_name, "by ID"), pcmk_rc_str(rc));
 705     }
 706     free_xml(cmd);
 707     crm_ipc_close(conn);
 708     crm_ipc_destroy(conn);
 709     return rc;
 710 }
 711 
 712 static void
 713 remove_node(const char *target_uname)
     /* [previous][next][first][last][top][bottom][index][help] */
 714 {
 715     int rc = pcmk_rc_ok;
 716     long nodeid = 0;
 717     const char *node_name = NULL;
 718     char *endptr = NULL;
 719     const enum pcmk_ipc_server servers[] = {
 720         pcmk_ipc_controld,
 721         pcmk_ipc_attrd,
 722     };
 723 
 724     // Check whether node was specified by name or numeric ID
 725     errno = 0;
 726     nodeid = strtol(target_uname, &endptr, 10);
 727     if ((errno != 0) || (endptr == target_uname) || (*endptr != '\0')
 728         || (nodeid <= 0)) {
 729         // It's not a positive integer, so assume it's a node name
 730         nodeid = 0;
 731         node_name = target_uname;
 732     }
 733 
 734     for (int i = 0; i < PCMK__NELEM(servers); ++i) {
 735         rc = purge_node_from(servers[i], node_name, nodeid);
 736         if (rc != pcmk_rc_ok) {
 737             exit_code = pcmk_rc2exitc(rc);
 738             return;
 739         }
 740     }
 741 
 742     // The fencer hasn't been converted to pcmk_ipc_api_t yet
 743     rc = purge_node_from_fencer(node_name, nodeid);
 744     if (rc != pcmk_rc_ok) {
 745         exit_code = pcmk_rc2exitc(rc);
 746         return;
 747     }
 748 
 749     // Lastly, purge the node from the CIB itself
 750     rc = purge_node_from_cib(node_name, nodeid);
 751     exit_code = pcmk_rc2exitc(rc);
 752 }
 753 
 754 static GOptionContext *
 755 build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) {
     /* [previous][next][first][last][top][bottom][index][help] */
 756     GOptionContext *context = NULL;
 757 
 758     GOptionEntry extra_prog_entries[] = {
 759         { "quiet", 'Q', 0, G_OPTION_ARG_NONE, &(args->quiet),
 760           "Be less descriptive in output.",
 761           NULL },
 762 
 763         { NULL }
 764     };
 765 
 766     context = pcmk__build_arg_context(args, "text (default), xml", group, NULL);
 767 
 768     /* Add the -q option, which cannot be part of the globally supported options
 769      * because some tools use that flag for something else.
 770      */
 771     pcmk__add_main_args(context, extra_prog_entries);
 772 
 773     pcmk__add_arg_group(context, "commands", "Commands:",
 774                         "Show command help", command_entries);
 775     pcmk__add_arg_group(context, "additional", "Additional Options:",
 776                         "Show additional options", addl_entries);
 777     return context;
 778 }
 779 
 780 int
 781 main(int argc, char **argv)
     /* [previous][next][first][last][top][bottom][index][help] */
 782 {
 783     int rc = pcmk_rc_ok;
 784 
 785     GOptionGroup *output_group = NULL;
 786     pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
 787     gchar **processed_args = pcmk__cmdline_preproc(argv, "NR");
 788     GOptionContext *context = build_arg_context(args, &output_group);
 789 
 790     pcmk__register_formats(output_group, formats);
 791     if (!g_option_context_parse_strv(context, &processed_args, &error)) {
 792         exit_code = CRM_EX_USAGE;
 793         goto done;
 794     }
 795 
 796     pcmk__cli_init_logging("crm_node", args->verbosity);
 797 
 798     rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv);
 799     if (rc != pcmk_rc_ok) {
 800         exit_code = pcmk_rc2exitc(rc);
 801         g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 802                     "Error creating output format %s: %s", args->output_ty,
 803                     pcmk_rc_str(rc));
 804         goto done;
 805     }
 806 
 807     if (args->version) {
 808         out->version(out, false);
 809         goto done;
 810     }
 811 
 812     if (options.command == 0) {
 813         char *help = g_option_context_get_help(context, TRUE, NULL);
 814 
 815         out->err(out, "%s", help);
 816         g_free(help);
 817         exit_code = CRM_EX_USAGE;
 818         goto done;
 819     }
 820 
 821     if (options.dangerous_cmd && options.force_flag == FALSE) {
 822         exit_code = CRM_EX_USAGE;
 823         g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 824                     "The supplied command is considered dangerous."
 825                     "  To prevent accidental destruction of the cluster,"
 826                     " the --force flag is required in order to proceed.");
 827         goto done;
 828     }
 829 
 830     pcmk__register_lib_messages(out);
 831     pcmk__register_messages(out, fmt_functions);
 832 
 833     switch (options.command) {
 834         case 'i':
 835             print_node_id();
 836             break;
 837 
 838         case 'n':
 839             print_node_name(0);
 840             break;
 841 
 842         case 'q':
 843             print_quorum();
 844             break;
 845 
 846         case 'N':
 847             print_node_name(options.nodeid);
 848             break;
 849 
 850         case 'R':
 851             remove_node(options.target_uname);
 852             break;
 853 
 854         case 'l':
 855         case 'p':
 856             run_controller_mainloop();
 857             break;
 858 
 859         default:
 860             break;
 861     }
 862 
 863 done:
 864     g_strfreev(processed_args);
 865     pcmk__free_arg_context(context);
 866 
 867     pcmk__output_and_clear_error(&error, out);
 868 
 869     if (out != NULL) {
 870         out->finish(out, exit_code, true, NULL);
 871         pcmk__output_free(out);
 872     }
 873     pcmk__unregister_formats();
 874     return crm_exit(exit_code);
 875 }

/* [previous][next][first][last][top][bottom][index][help] */