root/lib/pacemaker/pcmk_cluster_queries.c

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

DEFINITIONS

This source file includes following definitions.
  1. quit_main_loop
  2. admin_message_timeout
  3. start_main_loop
  4. event_done
  5. controld_event_reply
  6. controller_status_event_cb
  7. designated_controller_event_cb
  8. pacemakerd_event_cb
  9. ipc_connect
  10. pcmk__controller_status
  11. pcmk_controller_status
  12. pcmk__designated_controller
  13. pcmk_designated_controller
  14. pcmk__pacemakerd_status
  15. pcmk_pacemakerd_status
  16. remote_node_print_helper
  17. pcmk__list_nodes
  18. pcmk_list_nodes

   1 /*
   2  * Copyright 2020-2022 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 Lesser General Public License
   7  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #include <crm_internal.h>
  11 
  12 #include <glib.h>               // gboolean, GMainLoop, etc.
  13 #include <libxml/tree.h>        // xmlNode
  14 
  15 #include <pacemaker.h>
  16 #include <pacemaker-internal.h>
  17 
  18 #include <crm/crm.h>
  19 #include <crm/cib.h>
  20 #include <crm/cib/internal.h>
  21 #include <crm/msg_xml.h>
  22 #include <crm/common/output_internal.h>
  23 #include <crm/common/xml.h>
  24 #include <crm/common/xml_internal.h>
  25 #include <crm/common/iso8601.h>
  26 #include <crm/common/ipc_controld.h>
  27 #include <crm/common/ipc_pacemakerd.h>
  28 #include <crm/common/mainloop.h>
  29 
  30 #define DEFAULT_MESSAGE_TIMEOUT_MS 30000
  31 
  32 
  33 typedef struct {
  34     pcmk__output_t *out;
  35     GMainLoop *mainloop;
  36     int rc;
  37     guint message_timer_id;
  38     guint message_timeout_ms;
  39     enum pcmk_pacemakerd_state pcmkd_state;
  40 } data_t;
  41 
  42 static void
  43 quit_main_loop(data_t *data)
     /* [previous][next][first][last][top][bottom][index][help] */
  44 {
  45     if (data->mainloop != NULL) {
  46         GMainLoop *mloop = data->mainloop;
  47 
  48         data->mainloop = NULL; // Don't re-enter this block
  49         pcmk_quit_main_loop(mloop, 10);
  50         g_main_loop_unref(mloop);
  51     }
  52 }
  53 
  54 static gboolean
  55 admin_message_timeout(gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
  56 {
  57     data_t *data = user_data;
  58     pcmk__output_t *out = data->out;
  59 
  60     out->err(out, "error: No reply received from controller before timeout (%dms)",
  61             data->message_timeout_ms);
  62     data->message_timer_id = 0;
  63     data->rc = ETIMEDOUT;
  64     quit_main_loop(data);
  65     return FALSE; // Tells glib to remove source
  66 }
  67 
  68 static void
  69 start_main_loop(data_t *data)
     /* [previous][next][first][last][top][bottom][index][help] */
  70 {
  71     if (data->message_timeout_ms < 1) {
  72         data->message_timeout_ms = DEFAULT_MESSAGE_TIMEOUT_MS;
  73     }
  74 
  75     data->rc = ECONNRESET; // For unexpected disconnects
  76     data->mainloop = g_main_loop_new(NULL, FALSE);
  77     data->message_timer_id = g_timeout_add(data->message_timeout_ms,
  78                                      admin_message_timeout,
  79                                      data);
  80     g_main_loop_run(data->mainloop);
  81 }
  82 
  83 static void
  84 event_done(data_t *data, pcmk_ipc_api_t *api)
     /* [previous][next][first][last][top][bottom][index][help] */
  85 {
  86     pcmk_disconnect_ipc(api);
  87     quit_main_loop(data);
  88 }
  89 
  90 static pcmk_controld_api_reply_t *
  91 controld_event_reply(data_t *data, pcmk_ipc_api_t *controld_api, enum pcmk_ipc_event event_type, crm_exit_t status, void *event_data)
     /* [previous][next][first][last][top][bottom][index][help] */
  92 {
  93     pcmk__output_t *out = data->out;
  94     pcmk_controld_api_reply_t *reply = event_data;
  95 
  96     switch (event_type) {
  97         case pcmk_ipc_event_disconnect:
  98             if (data->rc == ECONNRESET) { // Unexpected
  99                 out->err(out, "error: Lost connection to controller");
 100             }
 101             event_done(data, controld_api);
 102             return NULL;
 103 
 104         case pcmk_ipc_event_reply:
 105             break;
 106 
 107         default:
 108             return NULL;
 109     }
 110 
 111     if (data->message_timer_id != 0) {
 112         g_source_remove(data->message_timer_id);
 113         data->message_timer_id = 0;
 114     }
 115 
 116     if (status != CRM_EX_OK) {
 117         out->err(out, "error: Bad reply from controller: %s",
 118                 crm_exit_str(status));
 119         data->rc = EBADMSG;
 120         event_done(data, controld_api);
 121         return NULL;
 122     }
 123 
 124     if (reply->reply_type != pcmk_controld_reply_ping) {
 125         out->err(out, "error: Unknown reply type %d from controller",
 126                 reply->reply_type);
 127         data->rc = EBADMSG;
 128         event_done(data, controld_api);
 129         return NULL;
 130     }
 131 
 132     return reply;
 133 }
 134 
 135 static void
 136 controller_status_event_cb(pcmk_ipc_api_t *controld_api,
     /* [previous][next][first][last][top][bottom][index][help] */
 137                     enum pcmk_ipc_event event_type, crm_exit_t status,
 138                     void *event_data, void *user_data)
 139 {
 140     data_t *data = user_data;
 141     pcmk__output_t *out = data->out;
 142     pcmk_controld_api_reply_t *reply = controld_event_reply(data, controld_api,
 143         event_type, status, event_data);
 144 
 145     if (reply != NULL) {
 146         out->message(out, "health",
 147                reply->data.ping.sys_from,
 148                reply->host_from,
 149                reply->data.ping.fsa_state,
 150                reply->data.ping.result);
 151         data->rc = pcmk_rc_ok;
 152     }
 153 
 154     event_done(data, controld_api);
 155 }
 156 
 157 static void
 158 designated_controller_event_cb(pcmk_ipc_api_t *controld_api,
     /* [previous][next][first][last][top][bottom][index][help] */
 159                     enum pcmk_ipc_event event_type, crm_exit_t status,
 160                     void *event_data, void *user_data)
 161 {
 162     data_t *data = user_data;
 163     pcmk__output_t *out = data->out;
 164     pcmk_controld_api_reply_t *reply = controld_event_reply(data, controld_api,
 165         event_type, status, event_data);
 166 
 167     if (reply != NULL) {
 168         out->message(out, "dc", reply->host_from);
 169         data->rc = pcmk_rc_ok;
 170     }
 171 
 172     event_done(data, controld_api);
 173 }
 174 
 175 static void
 176 pacemakerd_event_cb(pcmk_ipc_api_t *pacemakerd_api,
     /* [previous][next][first][last][top][bottom][index][help] */
 177                     enum pcmk_ipc_event event_type, crm_exit_t status,
 178                     void *event_data, void *user_data)
 179 {
 180     data_t *data = user_data;
 181     pcmk__output_t *out = data->out;
 182     pcmk_pacemakerd_api_reply_t *reply = event_data;
 183 
 184     switch (event_type) {
 185         case pcmk_ipc_event_disconnect:
 186             if (data->rc == ECONNRESET) { // Unexpected
 187                 out->err(out, "error: Lost connection to pacemakerd");
 188             }
 189             event_done(data, pacemakerd_api);
 190             return;
 191 
 192         case pcmk_ipc_event_reply:
 193             break;
 194 
 195         default:
 196             return;
 197     }
 198 
 199     if (data->message_timer_id != 0) {
 200         g_source_remove(data->message_timer_id);
 201         data->message_timer_id = 0;
 202     }
 203 
 204     if (status != CRM_EX_OK) {
 205         out->err(out, "error: Bad reply from pacemakerd: %s",
 206                 crm_exit_str(status));
 207         event_done(data, pacemakerd_api);
 208         data->rc = EBADMSG;
 209         return;
 210     }
 211 
 212     if (reply->reply_type != pcmk_pacemakerd_reply_ping) {
 213         out->err(out, "error: Unknown reply type %d from pacemakerd",
 214                 reply->reply_type);
 215         event_done(data, pacemakerd_api);
 216         data->rc = EBADMSG;
 217         return;
 218     }
 219 
 220     // Parse desired information from reply
 221     data->pcmkd_state = reply->data.ping.state;
 222     if (reply->data.ping.status == pcmk_rc_ok) {
 223         crm_time_t *when = crm_time_new(NULL);
 224         char *when_s = NULL;
 225 
 226         crm_time_set_timet(when, &reply->data.ping.last_good);
 227         when_s = crm_time_as_string(when,
 228                                     crm_time_log_date
 229                                     |crm_time_log_timeofday
 230                                     |crm_time_log_with_timezone);
 231 
 232         out->message(out, "pacemakerd-health",
 233                      reply->data.ping.sys_from, reply->data.ping.state, NULL,
 234                      when_s);
 235 
 236         crm_time_free(when);
 237         free(when_s);
 238 
 239     } else {
 240         out->message(out, "pacemakerd-health",
 241                      reply->data.ping.sys_from, reply->data.ping.state,
 242                      "query failed", NULL);
 243     }
 244     data->rc = pcmk_rc_ok;
 245     event_done(data, pacemakerd_api);
 246 }
 247 
 248 static pcmk_ipc_api_t *
 249 ipc_connect(data_t *data, enum pcmk_ipc_server server, pcmk_ipc_callback_t cb,
     /* [previous][next][first][last][top][bottom][index][help] */
 250             enum pcmk_ipc_dispatch dispatch_type, bool eremoteio_ok)
 251 {
 252     int rc;
 253     pcmk__output_t *out = data->out;
 254     pcmk_ipc_api_t *api = NULL;
 255 
 256     rc = pcmk_new_ipc_api(&api, server);
 257     if (api == NULL) {
 258         out->err(out, "error: Could not connect to %s: %s",
 259                 pcmk_ipc_name(api, true),
 260                 pcmk_rc_str(rc));
 261         data->rc = rc;
 262         return NULL;
 263     }
 264     if (cb != NULL) {
 265         pcmk_register_ipc_callback(api, cb, data);
 266     }
 267 
 268     rc = pcmk_connect_ipc(api, dispatch_type);
 269     if (rc != pcmk_rc_ok) {
 270         if ((rc == EREMOTEIO) && eremoteio_ok) {
 271             /* EREMOTEIO may be expected and acceptable for some callers.
 272              * Preserve the return code in case callers need to handle it
 273              * specially.
 274              */
 275         } else {
 276             out->err(out, "error: Could not connect to %s: %s",
 277                      pcmk_ipc_name(api, true), pcmk_rc_str(rc));
 278         }
 279         data->rc = rc;
 280         pcmk_free_ipc_api(api);
 281         return NULL;
 282     }
 283 
 284     return api;
 285 }
 286 
 287 int
 288 pcmk__controller_status(pcmk__output_t *out, char *dest_node, guint message_timeout_ms)
     /* [previous][next][first][last][top][bottom][index][help] */
 289 {
 290     data_t data = {
 291         .out = out,
 292         .mainloop = NULL,
 293         .rc = pcmk_rc_ok,
 294         .message_timer_id = 0,
 295         .message_timeout_ms = message_timeout_ms,
 296         .pcmkd_state = pcmk_pacemakerd_state_invalid,
 297     };
 298     enum pcmk_ipc_dispatch dispatch_type = pcmk_ipc_dispatch_main;
 299     pcmk_ipc_api_t *controld_api = NULL;
 300 
 301     if (message_timeout_ms == 0) {
 302         dispatch_type = pcmk_ipc_dispatch_sync;
 303     }
 304     controld_api = ipc_connect(&data, pcmk_ipc_controld,
 305                                controller_status_event_cb, dispatch_type,
 306                                false);
 307 
 308     if (controld_api != NULL) {
 309         int rc = pcmk_controld_api_ping(controld_api, dest_node);
 310         if (rc != pcmk_rc_ok) {
 311             out->err(out, "error: Could not ping controller API: %s",
 312                      pcmk_rc_str(rc));
 313             data.rc = rc;
 314         }
 315 
 316         if (dispatch_type == pcmk_ipc_dispatch_main) {
 317             start_main_loop(&data);
 318         }
 319 
 320         pcmk_free_ipc_api(controld_api);
 321     }
 322 
 323     return data.rc;
 324 }
 325 
 326 int
 327 pcmk_controller_status(xmlNodePtr *xml, char *dest_node, unsigned int message_timeout_ms)
     /* [previous][next][first][last][top][bottom][index][help] */
 328 {
 329     pcmk__output_t *out = NULL;
 330     int rc = pcmk_rc_ok;
 331 
 332     rc = pcmk__xml_output_new(&out, xml);
 333     if (rc != pcmk_rc_ok) {
 334         return rc;
 335     }
 336 
 337     pcmk__register_lib_messages(out);
 338 
 339     rc = pcmk__controller_status(out, dest_node, (guint) message_timeout_ms);
 340     pcmk__xml_output_finish(out, xml);
 341     return rc;
 342 }
 343 
 344 int
 345 pcmk__designated_controller(pcmk__output_t *out, guint message_timeout_ms)
     /* [previous][next][first][last][top][bottom][index][help] */
 346 {
 347     data_t data = {
 348         .out = out,
 349         .mainloop = NULL,
 350         .rc = pcmk_rc_ok,
 351         .message_timer_id = 0,
 352         .message_timeout_ms = message_timeout_ms,
 353         .pcmkd_state = pcmk_pacemakerd_state_invalid,
 354     };
 355     enum pcmk_ipc_dispatch dispatch_type = pcmk_ipc_dispatch_main;
 356     pcmk_ipc_api_t *controld_api = NULL;
 357 
 358     if (message_timeout_ms == 0) {
 359         dispatch_type = pcmk_ipc_dispatch_sync;
 360     }
 361     controld_api = ipc_connect(&data, pcmk_ipc_controld,
 362                                designated_controller_event_cb, dispatch_type,
 363                                false);
 364 
 365     if (controld_api != NULL) {
 366         int rc = pcmk_controld_api_ping(controld_api, NULL);
 367         if (rc != pcmk_rc_ok) {
 368             out->err(out, "error: Could not ping controller API: %s",
 369                      pcmk_rc_str(rc));
 370             data.rc = rc;
 371         }
 372 
 373         if (dispatch_type == pcmk_ipc_dispatch_main) {
 374             start_main_loop(&data);
 375         }
 376 
 377         pcmk_free_ipc_api(controld_api);
 378     }
 379 
 380     return data.rc;
 381 }
 382 
 383 int
 384 pcmk_designated_controller(xmlNodePtr *xml, unsigned int message_timeout_ms)
     /* [previous][next][first][last][top][bottom][index][help] */
 385 {
 386     pcmk__output_t *out = NULL;
 387     int rc = pcmk_rc_ok;
 388 
 389     rc = pcmk__xml_output_new(&out, xml);
 390     if (rc != pcmk_rc_ok) {
 391         return rc;
 392     }
 393 
 394     pcmk__register_lib_messages(out);
 395 
 396     rc = pcmk__designated_controller(out, (guint) message_timeout_ms);
 397     pcmk__xml_output_finish(out, xml);
 398     return rc;
 399 }
 400 
 401 /*!
 402  * \internal
 403  * \brief Get and output \p pacemakerd status
 404  *
 405  * \param[in,out] out                 Output object
 406  * \param[in]     ipc_name            IPC name for request
 407  * \param[in]     message_timeout_ms  How long to wait for a reply from the
 408  *                                    \p pacemakerd API. If 0,
 409  *                                    \p pcmk_ipc_dispatch_sync will be used.
 410  *                                    If positive, \p pcmk_ipc_dispatch_main
 411  *                                    will be used, and a new mainloop will be
 412  *                                    created for this purpose (freed before
 413  *                                    return).
 414  * \param[out]    state               Where to store the \p pacemakerd state, if
 415  *                                    not \p NULL
 416  *
 417  * \return Standard Pacemaker return code
 418  *
 419  * \note This function returns \p EREMOTEIO if run on a Pacemaker Remote node
 420  *       with \p pacemaker-remoted running, since \p pacemakerd is not proxied
 421  *       to remote nodes. The fencer and CIB may still be accessible, but
 422  *       \p state will be \p pcmk_pacemakerd_state_invalid.
 423  */
 424 int
 425 pcmk__pacemakerd_status(pcmk__output_t *out, const char *ipc_name,
     /* [previous][next][first][last][top][bottom][index][help] */
 426                         guint message_timeout_ms,
 427                         enum pcmk_pacemakerd_state *state)
 428 {
 429     data_t data = {
 430         .out = out,
 431         .mainloop = NULL,
 432         .rc = pcmk_rc_ipc_unresponsive,
 433         .message_timer_id = 0,
 434         .message_timeout_ms = message_timeout_ms,
 435         .pcmkd_state = pcmk_pacemakerd_state_invalid,
 436     };
 437     enum pcmk_ipc_dispatch dispatch_type = pcmk_ipc_dispatch_main;
 438     pcmk_ipc_api_t *pacemakerd_api = NULL;
 439 
 440     if (message_timeout_ms == 0) {
 441         dispatch_type = pcmk_ipc_dispatch_sync;
 442     }
 443     pacemakerd_api = ipc_connect(&data, pcmk_ipc_pacemakerd,
 444                                  pacemakerd_event_cb, dispatch_type, true);
 445 
 446     if (pacemakerd_api != NULL) {
 447         int rc = pcmk_pacemakerd_api_ping(pacemakerd_api, ipc_name);
 448         if (rc != pcmk_rc_ok) {
 449             out->err(out, "error: Could not ping launcher API: %s",
 450                      pcmk_rc_str(rc));
 451             data.rc = rc;
 452         }
 453 
 454         if (dispatch_type == pcmk_ipc_dispatch_main) {
 455             start_main_loop(&data);
 456         }
 457         pcmk_free_ipc_api(pacemakerd_api);
 458     }
 459 
 460     if (state != NULL) {
 461         *state = data.pcmkd_state;
 462     }
 463     return data.rc;
 464 }
 465 
 466 // Documented in header
 467 int
 468 pcmk_pacemakerd_status(xmlNodePtr *xml, const char *ipc_name,
     /* [previous][next][first][last][top][bottom][index][help] */
 469                        unsigned int message_timeout_ms)
 470 {
 471     pcmk__output_t *out = NULL;
 472     int rc = pcmk_rc_ok;
 473 
 474     rc = pcmk__xml_output_new(&out, xml);
 475     if (rc != pcmk_rc_ok) {
 476         return rc;
 477     }
 478 
 479     pcmk__register_lib_messages(out);
 480 
 481     rc = pcmk__pacemakerd_status(out, ipc_name, (guint) message_timeout_ms,
 482                                  NULL);
 483     pcmk__xml_output_finish(out, xml);
 484     return rc;
 485 }
 486 
 487 /* user data for looping through remote node xpath searches */
 488 struct node_data {
 489     pcmk__output_t *out;
 490     int found;
 491     const char *field;  /* XML attribute to check for node name */
 492     const char *type;
 493     gboolean bash_export;
 494 };
 495 
 496 static void
 497 remote_node_print_helper(xmlNode *result, void *user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 498 {
 499     struct node_data *data = user_data;
 500     pcmk__output_t *out = data->out;
 501     const char *name = crm_element_value(result, XML_ATTR_UNAME);
 502     const char *id = crm_element_value(result, data->field);
 503 
 504     // node name and node id are the same for remote/guest nodes
 505     out->message(out, "crmadmin-node", data->type,
 506                  name ? name : id,
 507                  id,
 508                  data->bash_export);
 509     data->found++;
 510 }
 511 
 512 // \return Standard Pacemaker return code
 513 int
 514 pcmk__list_nodes(pcmk__output_t *out, char *node_types, gboolean bash_export)
     /* [previous][next][first][last][top][bottom][index][help] */
 515 {
 516     xmlNode *xml_node = NULL;
 517     int rc;
 518 
 519     rc = cib__signon_query(NULL, &xml_node);
 520 
 521     if (rc == pcmk_rc_ok) {
 522         struct node_data data = {
 523             .out = out,
 524             .found = 0,
 525             .bash_export = bash_export
 526         };
 527 
 528         out->begin_list(out, NULL, NULL, "nodes");
 529 
 530         if (!pcmk__str_empty(node_types) && strstr(node_types, "all")) {
 531             node_types = NULL;
 532         }
 533 
 534         if (pcmk__str_empty(node_types) || strstr(node_types, "cluster")) {
 535             data.field = "id";
 536             data.type = "cluster";
 537             crm_foreach_xpath_result(xml_node, PCMK__XP_MEMBER_NODE_CONFIG,
 538                                      remote_node_print_helper, &data);
 539         }
 540 
 541         if (pcmk__str_empty(node_types) || strstr(node_types, "guest")) {
 542             data.field = "value";
 543             data.type = "guest";
 544             crm_foreach_xpath_result(xml_node, PCMK__XP_GUEST_NODE_CONFIG,
 545                                      remote_node_print_helper, &data);
 546         }
 547 
 548         if (pcmk__str_empty(node_types) || !pcmk__strcmp(node_types, ",|^remote", pcmk__str_regex)) {
 549             data.field = "id";
 550             data.type = "remote";
 551             crm_foreach_xpath_result(xml_node, PCMK__XP_REMOTE_NODE_CONFIG,
 552                                      remote_node_print_helper, &data);
 553         }
 554 
 555         out->end_list(out);
 556 
 557         if (data.found == 0) {
 558             out->info(out, "No nodes configured");
 559         }
 560 
 561         free_xml(xml_node);
 562     }
 563 
 564     return rc;
 565 }
 566 
 567 int
 568 pcmk_list_nodes(xmlNodePtr *xml, char *node_types)
     /* [previous][next][first][last][top][bottom][index][help] */
 569 {
 570     pcmk__output_t *out = NULL;
 571     int rc = pcmk_rc_ok;
 572 
 573     rc = pcmk__xml_output_new(&out, xml);
 574     if (rc != pcmk_rc_ok) {
 575         return rc;
 576     }
 577 
 578     pcmk__register_lib_messages(out);
 579 
 580     rc = pcmk__list_nodes(out, node_types, FALSE);
 581     pcmk__xml_output_finish(out, xml);
 582     return rc;
 583 }

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