root/lib/pacemaker/pcmk_cluster_queries.c

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

DEFINITIONS

This source file includes following definitions.
  1. validate_reply_event
  2. validate_controld_reply
  3. validate_pcmkd_reply
  4. controller_status_event_cb
  5. designated_controller_event_cb
  6. node_info_event_cb
  7. pacemakerd_event_cb
  8. ipc_connect
  9. poll_until_reply
  10. pcmk__controller_status
  11. pcmk_controller_status
  12. pcmk__designated_controller
  13. pcmk_designated_controller
  14. pcmk__query_node_info
  15. pcmk_query_node_info
  16. pcmk__pacemakerd_status
  17. pcmk_pacemakerd_status
  18. remote_node_print_helper
  19. pcmk__list_nodes
  20. pcmk_list_nodes

   1 /*
   2  * Copyright 2020-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 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 <libxml/tree.h>        // xmlNode
  13 
  14 #include <pacemaker.h>
  15 #include <pacemaker-internal.h>
  16 
  17 #include <crm/crm.h>
  18 #include <crm/cib.h>
  19 #include <crm/cib/internal.h>
  20 #include <crm/common/output_internal.h>
  21 #include <crm/common/xml.h>
  22 #include <crm/common/xml_internal.h>
  23 #include <crm/common/iso8601.h>
  24 #include <crm/common/ipc_controld.h>
  25 #include <crm/common/ipc_pacemakerd.h>
  26 
  27 //! Object to store node info from the controller API
  28 typedef struct {
  29     /* Adapted from pcmk_controld_api_reply_t:data:node_info.
  30      * (char **) are convenient here for use within callbacks: we can skip
  31      * copying strings unless the caller passes a non-NULL value.
  32      */
  33     uint32_t id;
  34     char **node_name;
  35     char **uuid;
  36     char **state;
  37     bool have_quorum;
  38     bool is_remote;
  39 } node_info_t;
  40 
  41 //! Object to store API results, a timeout, and an output object
  42 typedef struct {
  43     pcmk__output_t *out;
  44     bool show_output;
  45     int rc;
  46     unsigned int message_timeout_ms;
  47     enum pcmk_pacemakerd_state pcmkd_state;
  48     node_info_t node_info;
  49 } data_t;
  50 
  51 /*!
  52  * \internal
  53  * \brief Validate that an IPC API event is a good reply
  54  *
  55  * \param[in,out] data        API results and options
  56  * \param[in]     api         IPC API connection
  57  * \param[in]     event_type  Type of event that occurred
  58  * \param[in]     status      Event status
  59  *
  60  * \return Standard Pacemaker return code
  61  */
  62 static int
  63 validate_reply_event(data_t *data, const pcmk_ipc_api_t *api,
     /* [previous][next][first][last][top][bottom][index][help] */
  64                      enum pcmk_ipc_event event_type, crm_exit_t status)
  65 {
  66     pcmk__output_t *out = data->out;
  67 
  68     switch (event_type) {
  69         case pcmk_ipc_event_reply:
  70             break;
  71 
  72         case pcmk_ipc_event_disconnect:
  73             if (data->rc == ECONNRESET) { // Unexpected
  74                 out->err(out, "error: Lost connection to %s",
  75                          pcmk_ipc_name(api, true));
  76             }
  77             // Nothing bad but not the reply we're looking for
  78             return ENOTSUP;
  79 
  80         default:
  81             // Ditto
  82             return ENOTSUP;
  83     }
  84 
  85     if (status != CRM_EX_OK) {
  86         out->err(out, "error: Bad reply from %s: %s",
  87                  pcmk_ipc_name(api, true), crm_exit_str(status));
  88         data->rc = EBADMSG;
  89         return data->rc;
  90     }
  91     return pcmk_rc_ok;
  92 }
  93 
  94 /*!
  95  * \internal
  96  * \brief Validate that a controller API event is a good reply of expected type
  97  *
  98  * \param[in,out] data           API results and options
  99  * \param[in]     api            Controller connection
 100  * \param[in]     event_type     Type of event that occurred
 101  * \param[in]     status         Event status
 102  * \param[in]     event_data     Event-specific data
 103  * \param[in]     expected_type  Expected reply type
 104  *
 105  * \return Standard Pacemaker return code
 106  */
 107 static int
 108 validate_controld_reply(data_t *data, const pcmk_ipc_api_t *api,
     /* [previous][next][first][last][top][bottom][index][help] */
 109                         enum pcmk_ipc_event event_type, crm_exit_t status,
 110                         const void *event_data,
 111                         enum pcmk_controld_api_reply expected_type)
 112 {
 113     pcmk__output_t *out = data->out;
 114     int rc = pcmk_rc_ok;
 115     const pcmk_controld_api_reply_t *reply = NULL;
 116 
 117     rc = validate_reply_event(data, api, event_type, status);
 118     if (rc != pcmk_rc_ok) {
 119         return rc;
 120     }
 121 
 122     reply = (const pcmk_controld_api_reply_t *) event_data;
 123 
 124     if (reply->reply_type != expected_type) {
 125         out->err(out, "error: Unexpected reply type '%s' from controller",
 126                  pcmk__controld_api_reply2str(reply->reply_type));
 127         data->rc = EBADMSG;
 128         return data->rc;
 129     }
 130 
 131     return pcmk_rc_ok;
 132 }
 133 
 134 /*!
 135  * \internal
 136  * \brief Validate that a \p pacemakerd API event is a good reply of expected
 137  *        type
 138  *
 139  * \param[in,out] data           API results and options
 140  * \param[in]     api            \p pacemakerd connection
 141  * \param[in]     event_type     Type of event that occurred
 142  * \param[in]     status         Event status
 143  * \param[in]     event_data     Event-specific data
 144  * \param[in]     expected_type  Expected reply type
 145  *
 146  * \return Standard Pacemaker return code
 147  */
 148 static int
 149 validate_pcmkd_reply(data_t *data, const pcmk_ipc_api_t *api,
     /* [previous][next][first][last][top][bottom][index][help] */
 150                      enum pcmk_ipc_event event_type, crm_exit_t status,
 151                      const void *event_data,
 152                      enum pcmk_pacemakerd_api_reply expected_type)
 153 {
 154     pcmk__output_t *out = data->out;
 155     const pcmk_pacemakerd_api_reply_t *reply = NULL;
 156     int rc = validate_reply_event(data, api, event_type, status);
 157 
 158     if (rc != pcmk_rc_ok) {
 159         return rc;
 160     }
 161 
 162     reply = (const pcmk_pacemakerd_api_reply_t *) event_data;
 163 
 164     if (reply->reply_type != expected_type) {
 165         out->err(out, "error: Unexpected reply type '%s' from pacemakerd",
 166                  pcmk__pcmkd_api_reply2str(reply->reply_type));
 167         data->rc = EBADMSG;
 168         return data->rc;
 169     }
 170 
 171     return pcmk_rc_ok;
 172 }
 173 
 174 /*!
 175  * \internal
 176  * \brief Process a controller status IPC event
 177  *
 178  * \param[in,out] controld_api  Controller connection
 179  * \param[in]     event_type    Type of event that occurred
 180  * \param[in]     status        Event status
 181  * \param[in,out] event_data    \p pcmk_controld_api_reply_t object containing
 182  *                              event-specific data
 183  * \param[in,out] user_data     \p data_t object for API results and options
 184  */
 185 static void
 186 controller_status_event_cb(pcmk_ipc_api_t *controld_api,
     /* [previous][next][first][last][top][bottom][index][help] */
 187                            enum pcmk_ipc_event event_type, crm_exit_t status,
 188                            void *event_data, void *user_data)
 189 {
 190     data_t *data = (data_t *) user_data;
 191     pcmk__output_t *out = data->out;
 192     const pcmk_controld_api_reply_t *reply = NULL;
 193 
 194     int rc = validate_controld_reply(data, controld_api, event_type, status,
 195                                      event_data, pcmk_controld_reply_ping);
 196 
 197     if (rc != pcmk_rc_ok) {
 198         return;
 199     }
 200 
 201     reply = (const pcmk_controld_api_reply_t *) event_data;
 202     out->message(out, "health",
 203                  reply->data.ping.sys_from, reply->host_from,
 204                  reply->data.ping.fsa_state, reply->data.ping.result);
 205     data->rc = pcmk_rc_ok;
 206 }
 207 
 208 /*!
 209  * \internal
 210  * \brief Process a designated controller IPC event
 211  *
 212  * \param[in,out] controld_api  Controller connection
 213  * \param[in]     event_type    Type of event that occurred
 214  * \param[in]     status        Event status
 215  * \param[in,out] event_data    \p pcmk_controld_api_reply_t object containing
 216  *                              event-specific data
 217  * \param[in,out] user_data     \p data_t object for API results and options
 218  */
 219 static void
 220 designated_controller_event_cb(pcmk_ipc_api_t *controld_api,
     /* [previous][next][first][last][top][bottom][index][help] */
 221                                enum pcmk_ipc_event event_type,
 222                                crm_exit_t status, void *event_data,
 223                                void *user_data)
 224 {
 225     data_t *data = (data_t *) user_data;
 226     pcmk__output_t *out = data->out;
 227     const pcmk_controld_api_reply_t *reply = NULL;
 228 
 229     int rc = validate_controld_reply(data, controld_api, event_type, status,
 230                                      event_data, pcmk_controld_reply_ping);
 231 
 232     if (rc != pcmk_rc_ok) {
 233         return;
 234     }
 235 
 236     reply = (const pcmk_controld_api_reply_t *) event_data;
 237     out->message(out, "dc", reply->host_from);
 238     data->rc = pcmk_rc_ok;
 239 }
 240 
 241 /*!
 242  * \internal
 243  * \brief Process a node info IPC event
 244  *
 245  * \param[in,out] controld_api  Controller connection
 246  * \param[in]     event_type    Type of event that occurred
 247  * \param[in]     status        Event status
 248  * \param[in,out] event_data    \p pcmk_controld_api_reply_t object containing
 249  *                              event-specific data
 250  * \param[in,out] user_data     \p data_t object for API results and options
 251  */
 252 static void
 253 node_info_event_cb(pcmk_ipc_api_t *controld_api, enum pcmk_ipc_event event_type,
     /* [previous][next][first][last][top][bottom][index][help] */
 254                    crm_exit_t status, void *event_data, void *user_data)
 255 {
 256     data_t *data = (data_t *) user_data;
 257     pcmk__output_t *out = data->out;
 258 
 259     const pcmk_controld_api_reply_t *reply = NULL;
 260 
 261     int rc = validate_controld_reply(data, controld_api, event_type, status,
 262                                      event_data, pcmk_controld_reply_info);
 263 
 264     if (rc != pcmk_rc_ok) {
 265         return;
 266     }
 267 
 268     reply = (const pcmk_controld_api_reply_t *) event_data;
 269 
 270     if (reply->data.node_info.uname == NULL) {
 271         out->err(out, "Node is not known to cluster");
 272         data->rc = pcmk_rc_node_unknown;
 273         return;
 274     }
 275 
 276     data->node_info.have_quorum = reply->data.node_info.have_quorum;
 277     data->node_info.is_remote = reply->data.node_info.is_remote;
 278     data->node_info.id = (uint32_t) reply->data.node_info.id;
 279 
 280     pcmk__str_update(data->node_info.node_name, reply->data.node_info.uname);
 281     pcmk__str_update(data->node_info.uuid, reply->data.node_info.uuid);
 282     pcmk__str_update(data->node_info.state, reply->data.node_info.state);
 283 
 284     if (data->show_output) {
 285         out->message(out, "node-info",
 286                      (uint32_t) reply->data.node_info.id, reply->data.node_info.uname,
 287                      reply->data.node_info.uuid, reply->data.node_info.state,
 288                      reply->data.node_info.have_quorum,
 289                      reply->data.node_info.is_remote);
 290     }
 291 
 292     data->rc = pcmk_rc_ok;
 293 }
 294 
 295 /*!
 296  * \internal
 297  * \brief Process a \p pacemakerd status IPC event
 298  *
 299  * \param[in,out] pacemakerd_api  \p pacemakerd connection
 300  * \param[in]     event_type      Type of event that occurred
 301  * \param[in]     status          Event status
 302  * \param[in,out] event_data      \p pcmk_pacemakerd_api_reply_t object
 303  *                                containing event-specific data
 304  * \param[in,out] user_data       \p data_t object for API results and options
 305  */
 306 static void
 307 pacemakerd_event_cb(pcmk_ipc_api_t *pacemakerd_api,
     /* [previous][next][first][last][top][bottom][index][help] */
 308                     enum pcmk_ipc_event event_type, crm_exit_t status,
 309                     void *event_data, void *user_data)
 310 {
 311     data_t *data = user_data;
 312     pcmk__output_t *out = data->out;
 313     const pcmk_pacemakerd_api_reply_t *reply = NULL;
 314 
 315     int rc = validate_pcmkd_reply(data, pacemakerd_api, event_type, status,
 316                                   event_data, pcmk_pacemakerd_reply_ping);
 317 
 318     if (rc != pcmk_rc_ok) {
 319         return;
 320     }
 321 
 322     // Parse desired information from reply
 323     reply = (const pcmk_pacemakerd_api_reply_t *) event_data;
 324 
 325     data->pcmkd_state = reply->data.ping.state;
 326     data->rc = pcmk_rc_ok;
 327 
 328     if (!data->show_output) {
 329         return;
 330     }
 331 
 332     if (reply->data.ping.status == pcmk_rc_ok) {
 333         out->message(out, "pacemakerd-health",
 334                      reply->data.ping.sys_from, reply->data.ping.state, NULL,
 335                      reply->data.ping.last_good);
 336     } else {
 337         out->message(out, "pacemakerd-health",
 338                      reply->data.ping.sys_from, reply->data.ping.state,
 339                      "query failed", time(NULL));
 340     }
 341 }
 342 
 343 static pcmk_ipc_api_t *
 344 ipc_connect(data_t *data, enum pcmk_ipc_server server, pcmk_ipc_callback_t cb,
     /* [previous][next][first][last][top][bottom][index][help] */
 345             enum pcmk_ipc_dispatch dispatch_type, bool eremoteio_ok)
 346 {
 347     int rc;
 348     pcmk__output_t *out = data->out;
 349     pcmk_ipc_api_t *api = NULL;
 350 
 351     rc = pcmk_new_ipc_api(&api, server);
 352     if (api == NULL) {
 353         out->err(out, "error: Could not connect to %s: %s",
 354                 pcmk_ipc_name(api, true),
 355                 pcmk_rc_str(rc));
 356         data->rc = rc;
 357         return NULL;
 358     }
 359     if (cb != NULL) {
 360         pcmk_register_ipc_callback(api, cb, data);
 361     }
 362 
 363     rc = pcmk__connect_ipc(api, dispatch_type, 5);
 364     if (rc != pcmk_rc_ok) {
 365         if (rc == EREMOTEIO) {
 366             data->pcmkd_state = pcmk_pacemakerd_state_remote;
 367             if (eremoteio_ok) {
 368                 /* EREMOTEIO may be expected and acceptable for some callers
 369                  * on a Pacemaker Remote node
 370                  */
 371                 crm_debug("Ignoring %s connection failure: No "
 372                           "Pacemaker Remote connection",
 373                           pcmk_ipc_name(api, true));
 374                 rc = pcmk_rc_ok;
 375             } else {
 376                 out->err(out, "error: Could not connect to %s: %s",
 377                          pcmk_ipc_name(api, true), pcmk_rc_str(rc));
 378             }
 379         }
 380         data->rc = rc;
 381         pcmk_free_ipc_api(api);
 382         return NULL;
 383     }
 384 
 385     return api;
 386 }
 387 
 388 /*!
 389  * \internal
 390  * \brief Poll an IPC API connection until timeout or a reply is received
 391  *
 392  * \param[in,out] data     API results and options
 393  * \param[in,out] api      IPC API connection
 394  * \param[in]     on_node  If not \p NULL, name of the node to poll (used only
 395  *                         for logging)
 396  *
 397  * \note Sets the \p rc member of \p data on error
 398  */
 399 static void
 400 poll_until_reply(data_t *data, pcmk_ipc_api_t *api, const char *on_node)
     /* [previous][next][first][last][top][bottom][index][help] */
 401 {
 402     pcmk__output_t *out = data->out;
 403 
 404     uint64_t start_nsec = qb_util_nano_current_get();
 405     uint64_t end_nsec = 0;
 406     uint64_t elapsed_ms = 0;
 407     uint64_t remaining_ms = data->message_timeout_ms;
 408 
 409     while (remaining_ms > 0) {
 410         int rc = pcmk_poll_ipc(api, remaining_ms);
 411 
 412         if (rc == EAGAIN) {
 413             // Poll timed out
 414             break;
 415         }
 416 
 417         if (rc != pcmk_rc_ok) {
 418             out->err(out, "error: Failed to poll %s API%s%s: %s",
 419                      pcmk_ipc_name(api, true), (on_node != NULL)? " on " : "",
 420                      pcmk__s(on_node, ""), pcmk_rc_str(rc));
 421             data->rc = rc;
 422             return;
 423         }
 424 
 425         pcmk_dispatch_ipc(api);
 426 
 427         if (data->rc != EAGAIN) {
 428             // Received a reply
 429             return;
 430         }
 431         end_nsec = qb_util_nano_current_get();
 432         elapsed_ms = (end_nsec - start_nsec) / QB_TIME_NS_IN_MSEC;
 433         remaining_ms = data->message_timeout_ms - elapsed_ms;
 434     }
 435 
 436     out->err(out,
 437              "error: Timed out after %ums waiting for reply from %s API%s%s",
 438              data->message_timeout_ms, pcmk_ipc_name(api, true),
 439              (on_node != NULL)? " on " : "", pcmk__s(on_node, ""));
 440     data->rc = EAGAIN;
 441 }
 442 
 443 /*!
 444  * \internal
 445  * \brief Get and output controller status
 446  *
 447  * \param[in,out] out                 Output object
 448  * \param[in]     node_name           Name of node whose status is desired
 449  *                                    (\p NULL for DC)
 450  * \param[in]     message_timeout_ms  How long to wait for a reply from the
 451  *                                    \p pacemaker-controld API. If 0,
 452  *                                    \p pcmk_ipc_dispatch_sync will be used.
 453  *                                    Otherwise, \p pcmk_ipc_dispatch_poll will
 454  *                                    be used.
 455  *
 456  * \return Standard Pacemaker return code
 457  */
 458 int
 459 pcmk__controller_status(pcmk__output_t *out, const char *node_name,
     /* [previous][next][first][last][top][bottom][index][help] */
 460                         unsigned int message_timeout_ms)
 461 {
 462     data_t data = {
 463         .out = out,
 464         .rc = EAGAIN,
 465         .message_timeout_ms = message_timeout_ms,
 466     };
 467     enum pcmk_ipc_dispatch dispatch_type = pcmk_ipc_dispatch_poll;
 468     pcmk_ipc_api_t *controld_api = NULL;
 469 
 470     if (message_timeout_ms == 0) {
 471         dispatch_type = pcmk_ipc_dispatch_sync;
 472     }
 473     controld_api = ipc_connect(&data, pcmk_ipc_controld,
 474                                controller_status_event_cb, dispatch_type,
 475                                false);
 476 
 477     if (controld_api != NULL) {
 478         int rc = pcmk_controld_api_ping(controld_api, node_name);
 479         if (rc != pcmk_rc_ok) {
 480             out->err(out, "error: Could not ping controller API on %s: %s",
 481                      pcmk__s(node_name, "DC"), pcmk_rc_str(rc));
 482             data.rc = rc;
 483         }
 484 
 485         if (dispatch_type == pcmk_ipc_dispatch_poll) {
 486             poll_until_reply(&data, controld_api, pcmk__s(node_name, "DC"));
 487         }
 488         pcmk_free_ipc_api(controld_api);
 489     }
 490 
 491     return data.rc;
 492 }
 493 
 494 
 495 // Documented in header
 496 int
 497 pcmk_controller_status(xmlNodePtr *xml, const char *node_name,
     /* [previous][next][first][last][top][bottom][index][help] */
 498                        unsigned int message_timeout_ms)
 499 {
 500     pcmk__output_t *out = NULL;
 501     int rc = pcmk_rc_ok;
 502 
 503     rc = pcmk__xml_output_new(&out, xml);
 504     if (rc != pcmk_rc_ok) {
 505         return rc;
 506     }
 507 
 508     pcmk__register_lib_messages(out);
 509 
 510     rc = pcmk__controller_status(out, node_name, message_timeout_ms);
 511     pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
 512     return rc;
 513 }
 514 
 515 /*!
 516  * \internal
 517  * \brief Get and output designated controller node name
 518  *
 519  * \param[in,out] out                 Output object
 520  * \param[in]     message_timeout_ms  How long to wait for a reply from the
 521  *                                    \p pacemaker-controld API. If 0,
 522  *                                    \p pcmk_ipc_dispatch_sync will be used.
 523  *                                    Otherwise, \p pcmk_ipc_dispatch_poll will
 524  *                                    be used.
 525  *
 526  * \return Standard Pacemaker return code
 527  */
 528 int
 529 pcmk__designated_controller(pcmk__output_t *out,
     /* [previous][next][first][last][top][bottom][index][help] */
 530                             unsigned int message_timeout_ms)
 531 {
 532     data_t data = {
 533         .out = out,
 534         .rc = EAGAIN,
 535         .message_timeout_ms = message_timeout_ms,
 536     };
 537     enum pcmk_ipc_dispatch dispatch_type = pcmk_ipc_dispatch_poll;
 538     pcmk_ipc_api_t *controld_api = NULL;
 539 
 540     if (message_timeout_ms == 0) {
 541         dispatch_type = pcmk_ipc_dispatch_sync;
 542     }
 543     controld_api = ipc_connect(&data, pcmk_ipc_controld,
 544                                designated_controller_event_cb, dispatch_type,
 545                                false);
 546 
 547     if (controld_api != NULL) {
 548         int rc = pcmk_controld_api_ping(controld_api, NULL);
 549         if (rc != pcmk_rc_ok) {
 550             out->err(out, "error: Could not ping controller API on DC: %s",
 551                      pcmk_rc_str(rc));
 552             data.rc = rc;
 553         }
 554 
 555         if (dispatch_type == pcmk_ipc_dispatch_poll) {
 556             poll_until_reply(&data, controld_api, "DC");
 557         }
 558         pcmk_free_ipc_api(controld_api);
 559     }
 560 
 561     return data.rc;
 562 }
 563 
 564 // Documented in header
 565 int
 566 pcmk_designated_controller(xmlNodePtr *xml, unsigned int message_timeout_ms)
     /* [previous][next][first][last][top][bottom][index][help] */
 567 {
 568     pcmk__output_t *out = NULL;
 569     int rc = pcmk_rc_ok;
 570 
 571     rc = pcmk__xml_output_new(&out, xml);
 572     if (rc != pcmk_rc_ok) {
 573         return rc;
 574     }
 575 
 576     pcmk__register_lib_messages(out);
 577 
 578     rc = pcmk__designated_controller(out, message_timeout_ms);
 579     pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
 580     return rc;
 581 }
 582 
 583 /*!
 584  * \internal
 585  * \brief Get and optionally output node info corresponding to a node ID from
 586  *        the controller
 587  *
 588  * \param[in,out] out                 Output object
 589  * \param[in,out] node_id             ID of node whose info to get. If \p NULL
 590  *                                    or 0, get the local node's info. If not
 591  *                                    \c NULL, store the true node ID here on
 592  *                                    success.
 593  * \param[out]    node_name           If not \c NULL, where to store the node
 594  *                                    name
 595  * \param[out]    uuid                If not \c NULL, where to store the node
 596  *                                    UUID
 597  * \param[out]    state               If not \c NULL, where to store the
 598  *                                    membership state
 599  * \param[out]    is_remote           If not \c NULL, where to store whether the
 600  *                                    node is a Pacemaker Remote node
 601  * \param[out]    have_quorum         If not \c NULL, where to store whether the
 602  *                                    node has quorum
 603  * \param[in]     show_output         Whether to show the node info
 604  * \param[in]     message_timeout_ms  How long to wait for a reply from the
 605  *                                    \c pacemaker-controld API. If 0,
 606  *                                    \c pcmk_ipc_dispatch_sync will be used.
 607  *                                    Otherwise, \c pcmk_ipc_dispatch_poll will
 608  *                                    be used.
 609  *
 610  * \return Standard Pacemaker return code
 611  *
 612  * \note The caller is responsible for freeing \p *node_name, \p *uuid, and
 613  *       \p *state using \p free().
 614  */
 615 int
 616 pcmk__query_node_info(pcmk__output_t *out, uint32_t *node_id, char **node_name,
     /* [previous][next][first][last][top][bottom][index][help] */
 617                       char **uuid, char **state, bool *have_quorum,
 618                       bool *is_remote, bool show_output,
 619                       unsigned int message_timeout_ms)
 620 {
 621     data_t data = {
 622         .out = out,
 623         .show_output = show_output,
 624         .rc = EAGAIN,
 625         .message_timeout_ms = message_timeout_ms,
 626         .node_info = {
 627             .id = (node_id == NULL)? 0 : *node_id,
 628             .node_name = node_name,
 629             .uuid = uuid,
 630             .state = state,
 631         },
 632     };
 633     enum pcmk_ipc_dispatch dispatch_type = pcmk_ipc_dispatch_poll;
 634     pcmk_ipc_api_t *controld_api = NULL;
 635 
 636     if (node_name != NULL) {
 637         *node_name = NULL;
 638     }
 639     if (uuid != NULL) {
 640         *uuid = NULL;
 641     }
 642     if (state != NULL) {
 643         *state = NULL;
 644     }
 645 
 646     if (message_timeout_ms == 0) {
 647         dispatch_type = pcmk_ipc_dispatch_sync;
 648     }
 649     controld_api = ipc_connect(&data, pcmk_ipc_controld, node_info_event_cb,
 650                                dispatch_type, false);
 651 
 652     if (controld_api != NULL) {
 653         int rc = pcmk_controld_api_node_info(controld_api,
 654                                              (node_id != NULL)? *node_id : 0);
 655 
 656         if (rc != pcmk_rc_ok) {
 657             out->err(out,
 658                      "error: Could not send request to controller API on local "
 659                      "node: %s", pcmk_rc_str(rc));
 660             data.rc = rc;
 661         }
 662 
 663         if (dispatch_type == pcmk_ipc_dispatch_poll) {
 664             poll_until_reply(&data, controld_api, "local node");
 665         }
 666         pcmk_free_ipc_api(controld_api);
 667     }
 668 
 669     if (data.rc != pcmk_rc_ok) {
 670         return data.rc;
 671     }
 672 
 673     // String outputs are set in callback
 674     if (node_id != NULL) {
 675         *node_id = data.node_info.id;
 676     }
 677     if (have_quorum != NULL) {
 678         *have_quorum = data.node_info.have_quorum;
 679     }
 680     if (is_remote != NULL) {
 681         *is_remote = data.node_info.is_remote;
 682     }
 683 
 684     return data.rc;
 685 }
 686 
 687 // Documented in header
 688 int
 689 pcmk_query_node_info(xmlNodePtr *xml, uint32_t *node_id, char **node_name,
     /* [previous][next][first][last][top][bottom][index][help] */
 690                      char **uuid, char **state, bool *have_quorum,
 691                      bool *is_remote, bool show_output,
 692                      unsigned int message_timeout_ms)
 693 {
 694     pcmk__output_t *out = NULL;
 695     int rc = pcmk_rc_ok;
 696 
 697     CRM_ASSERT(node_name != NULL);
 698 
 699     rc = pcmk__xml_output_new(&out, xml);
 700     if (rc != pcmk_rc_ok) {
 701         return rc;
 702     }
 703 
 704     pcmk__register_lib_messages(out);
 705 
 706     rc = pcmk__query_node_info(out, node_id, node_name, uuid, state,
 707                                have_quorum, is_remote, show_output,
 708                                message_timeout_ms);
 709     pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
 710     return rc;
 711 }
 712 
 713 /*!
 714  * \internal
 715  * \brief Get and optionally output \p pacemakerd status
 716  *
 717  * \param[in,out] out                 Output object
 718  * \param[in]     ipc_name            IPC name for request
 719  * \param[in]     message_timeout_ms  How long to wait for a reply from the
 720  *                                    \p pacemakerd API. If 0,
 721  *                                    \p pcmk_ipc_dispatch_sync will be used.
 722  *                                    Otherwise, \p pcmk_ipc_dispatch_poll will
 723  *                                    be used.
 724  * \param[in]     show_output         Whether to output the \p pacemakerd state
 725  * \param[out]    state               Where to store the \p pacemakerd state, if
 726  *                                    not \p NULL
 727  *
 728  * \return Standard Pacemaker return code
 729  *
 730  * \note This function sets \p state to \p pcmk_pacemakerd_state_remote and
 731  *       returns \p pcmk_rc_ok if the IPC connection attempt returns
 732  *       \p EREMOTEIO. That code indicates that this is a Pacemaker Remote node
 733  *       with \p pacemaker-remoted running. The node may be connected to the
 734  *       cluster.
 735  */
 736 int
 737 pcmk__pacemakerd_status(pcmk__output_t *out, const char *ipc_name,
     /* [previous][next][first][last][top][bottom][index][help] */
 738                         unsigned int message_timeout_ms, bool show_output,
 739                         enum pcmk_pacemakerd_state *state)
 740 {
 741     data_t data = {
 742         .out = out,
 743         .show_output = show_output,
 744         .rc = EAGAIN,
 745         .message_timeout_ms = message_timeout_ms,
 746         .pcmkd_state = pcmk_pacemakerd_state_invalid,
 747     };
 748     enum pcmk_ipc_dispatch dispatch_type = pcmk_ipc_dispatch_poll;
 749     pcmk_ipc_api_t *pacemakerd_api = NULL;
 750 
 751     if (message_timeout_ms == 0) {
 752         dispatch_type = pcmk_ipc_dispatch_sync;
 753     }
 754     pacemakerd_api = ipc_connect(&data, pcmk_ipc_pacemakerd,
 755                                  pacemakerd_event_cb, dispatch_type, true);
 756 
 757     if (pacemakerd_api != NULL) {
 758         int rc = pcmk_pacemakerd_api_ping(pacemakerd_api, ipc_name);
 759         if (rc != pcmk_rc_ok) {
 760             out->err(out, "error: Could not ping launcher API: %s",
 761                      pcmk_rc_str(rc));
 762             data.rc = rc;
 763         }
 764 
 765         if (dispatch_type == pcmk_ipc_dispatch_poll) {
 766             poll_until_reply(&data, pacemakerd_api, NULL);
 767         }
 768         pcmk_free_ipc_api(pacemakerd_api);
 769 
 770     } else if ((data.pcmkd_state == pcmk_pacemakerd_state_remote)
 771                && show_output) {
 772         // No API connection so the callback wasn't run
 773         out->message(out, "pacemakerd-health",
 774                      NULL, data.pcmkd_state, NULL, time(NULL));
 775     }
 776 
 777     if (state != NULL) {
 778         *state = data.pcmkd_state;
 779     }
 780     return data.rc;
 781 }
 782 
 783 // Documented in header
 784 int
 785 pcmk_pacemakerd_status(xmlNodePtr *xml, const char *ipc_name,
     /* [previous][next][first][last][top][bottom][index][help] */
 786                        unsigned int message_timeout_ms)
 787 {
 788     pcmk__output_t *out = NULL;
 789     int rc = pcmk_rc_ok;
 790 
 791     rc = pcmk__xml_output_new(&out, xml);
 792     if (rc != pcmk_rc_ok) {
 793         return rc;
 794     }
 795 
 796     pcmk__register_lib_messages(out);
 797 
 798     rc = pcmk__pacemakerd_status(out, ipc_name, message_timeout_ms, true, NULL);
 799     pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
 800     return rc;
 801 }
 802 
 803 /* user data for looping through remote node xpath searches */
 804 struct node_data {
 805     pcmk__output_t *out;
 806     int found;
 807     const char *field;  /* XML attribute to check for node name */
 808     const char *type;
 809     bool bash_export;
 810 };
 811 
 812 static void
 813 remote_node_print_helper(xmlNode *result, void *user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 814 {
 815     struct node_data *data = user_data;
 816     pcmk__output_t *out = data->out;
 817     const char *name = crm_element_value(result, PCMK_XA_UNAME);
 818     const char *id = crm_element_value(result, data->field);
 819 
 820     // node name and node id are the same for remote/guest nodes
 821     out->message(out, "crmadmin-node", data->type,
 822                  pcmk__s(name, id), id, data->bash_export);
 823     data->found++;
 824 }
 825 
 826 // \return Standard Pacemaker return code
 827 int
 828 pcmk__list_nodes(pcmk__output_t *out, const char *node_types, bool bash_export)
     /* [previous][next][first][last][top][bottom][index][help] */
 829 {
 830     xmlNode *xml_node = NULL;
 831     int rc;
 832 
 833     rc = cib__signon_query(out, NULL, &xml_node);
 834 
 835     if (rc == pcmk_rc_ok) {
 836         struct node_data data = {
 837             .out = out,
 838             .found = 0,
 839             .bash_export = bash_export
 840         };
 841 
 842         /* PCMK_XE_NODES acts as the list's element name for CLI tools that
 843          * use pcmk__output_enable_list_element.  Otherwise PCMK_XE_NODES is
 844          * the value of the list's PCMK_XA_NAME attribute.
 845          */
 846         out->begin_list(out, NULL, NULL, PCMK_XE_NODES);
 847 
 848         if (!pcmk__str_empty(node_types) && strstr(node_types, "all")) {
 849             node_types = NULL;
 850         }
 851 
 852         if (pcmk__str_empty(node_types) || strstr(node_types, "cluster")) {
 853             data.field = PCMK_XA_ID;
 854             data.type = "cluster";
 855             crm_foreach_xpath_result(xml_node, PCMK__XP_MEMBER_NODE_CONFIG,
 856                                      remote_node_print_helper, &data);
 857         }
 858 
 859         if (pcmk__str_empty(node_types) || strstr(node_types, "guest")) {
 860             data.field = PCMK_XA_VALUE;
 861             data.type = "guest";
 862             crm_foreach_xpath_result(xml_node, PCMK__XP_GUEST_NODE_CONFIG,
 863                                      remote_node_print_helper, &data);
 864         }
 865 
 866         if (pcmk__str_empty(node_types)
 867             || pcmk__str_eq(node_types, ",|^remote", pcmk__str_regex)) {
 868             data.field = PCMK_XA_ID;
 869             data.type = "remote";
 870             crm_foreach_xpath_result(xml_node, PCMK__XP_REMOTE_NODE_CONFIG,
 871                                      remote_node_print_helper, &data);
 872         }
 873 
 874         out->end_list(out);
 875 
 876         if (data.found == 0) {
 877             out->info(out, "No nodes configured");
 878         }
 879 
 880         free_xml(xml_node);
 881     }
 882 
 883     return rc;
 884 }
 885 
 886 int
 887 pcmk_list_nodes(xmlNodePtr *xml, const char *node_types)
     /* [previous][next][first][last][top][bottom][index][help] */
 888 {
 889     pcmk__output_t *out = NULL;
 890     int rc = pcmk_rc_ok;
 891 
 892     rc = pcmk__xml_output_new(&out, xml);
 893     if (rc != pcmk_rc_ok) {
 894         return rc;
 895     }
 896 
 897     pcmk__register_lib_messages(out);
 898 
 899     rc = pcmk__list_nodes(out, node_types, FALSE);
 900     pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
 901     return rc;
 902 }

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