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-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 
  29 //! Object to store node info from the controller API
  30 typedef struct {
  31     /* Adapted from pcmk_controld_api_reply_t:data:node_info.
  32      * (char **) are convenient here for use within callbacks: we can skip
  33      * copying strings unless the caller passes a non-NULL value.
  34      */
  35     uint32_t id;
  36     char **node_name;
  37     char **uuid;
  38     char **state;
  39     bool have_quorum;
  40     bool is_remote;
  41 } node_info_t;
  42 
  43 //! Object to store API results, a timeout, and an output object
  44 typedef struct {
  45     pcmk__output_t *out;
  46     bool show_output;
  47     int rc;
  48     unsigned int message_timeout_ms;
  49     enum pcmk_pacemakerd_state pcmkd_state;
  50     node_info_t node_info;
  51 } data_t;
  52 
  53 /*!
  54  * \internal
  55  * \brief Validate that an IPC API event is a good reply
  56  *
  57  * \param[in,out] data        API results and options
  58  * \param[in]     api         IPC API connection
  59  * \param[in]     event_type  Type of event that occurred
  60  * \param[in]     status      Event status
  61  *
  62  * \return Standard Pacemaker return code
  63  */
  64 static int
  65 validate_reply_event(data_t *data, const pcmk_ipc_api_t *api,
     /* [previous][next][first][last][top][bottom][index][help] */
  66                      enum pcmk_ipc_event event_type, crm_exit_t status)
  67 {
  68     pcmk__output_t *out = data->out;
  69 
  70     switch (event_type) {
  71         case pcmk_ipc_event_reply:
  72             break;
  73 
  74         case pcmk_ipc_event_disconnect:
  75             if (data->rc == ECONNRESET) { // Unexpected
  76                 out->err(out, "error: Lost connection to %s",
  77                          pcmk_ipc_name(api, true));
  78             }
  79             // Nothing bad but not the reply we're looking for
  80             return ENOTSUP;
  81 
  82         default:
  83             // Ditto
  84             return ENOTSUP;
  85     }
  86 
  87     if (status != CRM_EX_OK) {
  88         out->err(out, "error: Bad reply from %s: %s",
  89                  pcmk_ipc_name(api, true), crm_exit_str(status));
  90         data->rc = EBADMSG;
  91         return data->rc;
  92     }
  93     return pcmk_rc_ok;
  94 }
  95 
  96 /*!
  97  * \internal
  98  * \brief Validate that a controller API event is a good reply of expected type
  99  *
 100  * \param[in,out] data           API results and options
 101  * \param[in]     api            Controller connection
 102  * \param[in]     event_type     Type of event that occurred
 103  * \param[in]     status         Event status
 104  * \param[in]     event_data     Event-specific data
 105  * \param[in]     expected_type  Expected reply type
 106  *
 107  * \return Standard Pacemaker return code
 108  */
 109 static int
 110 validate_controld_reply(data_t *data, const pcmk_ipc_api_t *api,
     /* [previous][next][first][last][top][bottom][index][help] */
 111                         enum pcmk_ipc_event event_type, crm_exit_t status,
 112                         const void *event_data,
 113                         enum pcmk_controld_api_reply expected_type)
 114 {
 115     pcmk__output_t *out = data->out;
 116     int rc = pcmk_rc_ok;
 117     const pcmk_controld_api_reply_t *reply = NULL;
 118 
 119     rc = validate_reply_event(data, api, event_type, status);
 120     if (rc != pcmk_rc_ok) {
 121         return rc;
 122     }
 123 
 124     reply = (const pcmk_controld_api_reply_t *) event_data;
 125 
 126     if (reply->reply_type != expected_type) {
 127         out->err(out, "error: Unexpected reply type '%s' from controller",
 128                  pcmk__controld_api_reply2str(reply->reply_type));
 129         data->rc = EBADMSG;
 130         return data->rc;
 131     }
 132 
 133     return pcmk_rc_ok;
 134 }
 135 
 136 /*!
 137  * \internal
 138  * \brief Validate that a \p pacemakerd API event is a good reply of expected
 139  *        type
 140  *
 141  * \param[in,out] data           API results and options
 142  * \param[in]     api            \p pacemakerd connection
 143  * \param[in]     event_type     Type of event that occurred
 144  * \param[in]     status         Event status
 145  * \param[in]     event_data     Event-specific data
 146  * \param[in]     expected_type  Expected reply type
 147  *
 148  * \return Standard Pacemaker return code
 149  */
 150 static int
 151 validate_pcmkd_reply(data_t *data, const pcmk_ipc_api_t *api,
     /* [previous][next][first][last][top][bottom][index][help] */
 152                      enum pcmk_ipc_event event_type, crm_exit_t status,
 153                      const void *event_data,
 154                      enum pcmk_pacemakerd_api_reply expected_type)
 155 {
 156     pcmk__output_t *out = data->out;
 157     const pcmk_pacemakerd_api_reply_t *reply = NULL;
 158     int rc = validate_reply_event(data, api, event_type, status);
 159 
 160     if (rc != pcmk_rc_ok) {
 161         return rc;
 162     }
 163 
 164     reply = (const pcmk_pacemakerd_api_reply_t *) event_data;
 165 
 166     if (reply->reply_type != expected_type) {
 167         out->err(out, "error: Unexpected reply type '%s' from pacemakerd",
 168                  pcmk__pcmkd_api_reply2str(reply->reply_type));
 169         data->rc = EBADMSG;
 170         return data->rc;
 171     }
 172 
 173     return pcmk_rc_ok;
 174 }
 175 
 176 /*!
 177  * \internal
 178  * \brief Process a controller status IPC event
 179  *
 180  * \param[in,out] controld_api  Controller connection
 181  * \param[in]     event_type    Type of event that occurred
 182  * \param[in]     status        Event status
 183  * \param[in,out] event_data    \p pcmk_controld_api_reply_t object containing
 184  *                              event-specific data
 185  * \param[in,out] user_data     \p data_t object for API results and options
 186  */
 187 static void
 188 controller_status_event_cb(pcmk_ipc_api_t *controld_api,
     /* [previous][next][first][last][top][bottom][index][help] */
 189                            enum pcmk_ipc_event event_type, crm_exit_t status,
 190                            void *event_data, void *user_data)
 191 {
 192     data_t *data = (data_t *) user_data;
 193     pcmk__output_t *out = data->out;
 194     const pcmk_controld_api_reply_t *reply = NULL;
 195 
 196     int rc = validate_controld_reply(data, controld_api, event_type, status,
 197                                      event_data, pcmk_controld_reply_ping);
 198 
 199     if (rc != pcmk_rc_ok) {
 200         return;
 201     }
 202 
 203     reply = (const pcmk_controld_api_reply_t *) event_data;
 204     out->message(out, "health",
 205                  reply->data.ping.sys_from, reply->host_from,
 206                  reply->data.ping.fsa_state, reply->data.ping.result);
 207     data->rc = pcmk_rc_ok;
 208 }
 209 
 210 /*!
 211  * \internal
 212  * \brief Process a designated controller IPC event
 213  *
 214  * \param[in,out] controld_api  Controller connection
 215  * \param[in]     event_type    Type of event that occurred
 216  * \param[in]     status        Event status
 217  * \param[in,out] event_data    \p pcmk_controld_api_reply_t object containing
 218  *                              event-specific data
 219  * \param[in,out] user_data     \p data_t object for API results and options
 220  */
 221 static void
 222 designated_controller_event_cb(pcmk_ipc_api_t *controld_api,
     /* [previous][next][first][last][top][bottom][index][help] */
 223                                enum pcmk_ipc_event event_type,
 224                                crm_exit_t status, void *event_data,
 225                                void *user_data)
 226 {
 227     data_t *data = (data_t *) user_data;
 228     pcmk__output_t *out = data->out;
 229     const pcmk_controld_api_reply_t *reply = NULL;
 230 
 231     int rc = validate_controld_reply(data, controld_api, event_type, status,
 232                                      event_data, pcmk_controld_reply_ping);
 233 
 234     if (rc != pcmk_rc_ok) {
 235         return;
 236     }
 237 
 238     reply = (const pcmk_controld_api_reply_t *) event_data;
 239     out->message(out, "dc", reply->host_from);
 240     data->rc = pcmk_rc_ok;
 241 }
 242 
 243 /*!
 244  * \internal
 245  * \brief Process a node info IPC event
 246  *
 247  * \param[in,out] controld_api  Controller connection
 248  * \param[in]     event_type    Type of event that occurred
 249  * \param[in]     status        Event status
 250  * \param[in,out] event_data    \p pcmk_controld_api_reply_t object containing
 251  *                              event-specific data
 252  * \param[in,out] user_data     \p data_t object for API results and options
 253  */
 254 static void
 255 node_info_event_cb(pcmk_ipc_api_t *controld_api, enum pcmk_ipc_event event_type,
     /* [previous][next][first][last][top][bottom][index][help] */
 256                    crm_exit_t status, void *event_data, void *user_data)
 257 {
 258     data_t *data = (data_t *) user_data;
 259     pcmk__output_t *out = data->out;
 260 
 261     const pcmk_controld_api_reply_t *reply = NULL;
 262 
 263     int rc = validate_controld_reply(data, controld_api, event_type, status,
 264                                      event_data, pcmk_controld_reply_info);
 265 
 266     if (rc != pcmk_rc_ok) {
 267         return;
 268     }
 269 
 270     reply = (const pcmk_controld_api_reply_t *) event_data;
 271 
 272     if (reply->data.node_info.uname == NULL) {
 273         out->err(out, "Node is not known to cluster");
 274         data->rc = pcmk_rc_node_unknown;
 275         return;
 276     }
 277 
 278     data->node_info.have_quorum = reply->data.node_info.have_quorum;
 279     data->node_info.is_remote = reply->data.node_info.is_remote;
 280     data->node_info.id = (uint32_t) reply->data.node_info.id;
 281 
 282     pcmk__str_update(data->node_info.node_name, reply->data.node_info.uname);
 283     pcmk__str_update(data->node_info.uuid, reply->data.node_info.uuid);
 284     pcmk__str_update(data->node_info.state, reply->data.node_info.state);
 285 
 286     if (data->show_output) {
 287         out->message(out, "node-info",
 288                      reply->data.node_info.id, reply->data.node_info.uname,
 289                      reply->data.node_info.uuid, reply->data.node_info.state,
 290                      reply->data.node_info.have_quorum,
 291                      reply->data.node_info.is_remote);
 292     }
 293 
 294     data->rc = pcmk_rc_ok;
 295 }
 296 
 297 /*!
 298  * \internal
 299  * \brief Process a \p pacemakerd status IPC event
 300  *
 301  * \param[in,out] pacemakerd_api  \p pacemakerd connection
 302  * \param[in]     event_type      Type of event that occurred
 303  * \param[in]     status          Event status
 304  * \param[in,out] event_data      \p pcmk_pacemakerd_api_reply_t object
 305  *                                containing event-specific data
 306  * \param[in,out] user_data       \p data_t object for API results and options
 307  */
 308 static void
 309 pacemakerd_event_cb(pcmk_ipc_api_t *pacemakerd_api,
     /* [previous][next][first][last][top][bottom][index][help] */
 310                     enum pcmk_ipc_event event_type, crm_exit_t status,
 311                     void *event_data, void *user_data)
 312 {
 313     data_t *data = user_data;
 314     pcmk__output_t *out = data->out;
 315     const pcmk_pacemakerd_api_reply_t *reply = NULL;
 316 
 317     int rc = validate_pcmkd_reply(data, pacemakerd_api, event_type, status,
 318                                   event_data, pcmk_pacemakerd_reply_ping);
 319 
 320     if (rc != pcmk_rc_ok) {
 321         return;
 322     }
 323 
 324     // Parse desired information from reply
 325     reply = (const pcmk_pacemakerd_api_reply_t *) event_data;
 326 
 327     data->pcmkd_state = reply->data.ping.state;
 328     data->rc = pcmk_rc_ok;
 329 
 330     if (!data->show_output) {
 331         return;
 332     }
 333 
 334     if (reply->data.ping.status == pcmk_rc_ok) {
 335         out->message(out, "pacemakerd-health",
 336                      reply->data.ping.sys_from, reply->data.ping.state, NULL,
 337                      reply->data.ping.last_good);
 338     } else {
 339         out->message(out, "pacemakerd-health",
 340                      reply->data.ping.sys_from, reply->data.ping.state,
 341                      "query failed", time(NULL));
 342     }
 343 }
 344 
 345 static pcmk_ipc_api_t *
 346 ipc_connect(data_t *data, enum pcmk_ipc_server server, pcmk_ipc_callback_t cb,
     /* [previous][next][first][last][top][bottom][index][help] */
 347             enum pcmk_ipc_dispatch dispatch_type, bool eremoteio_ok)
 348 {
 349     int rc;
 350     pcmk__output_t *out = data->out;
 351     pcmk_ipc_api_t *api = NULL;
 352 
 353     rc = pcmk_new_ipc_api(&api, server);
 354     if (api == NULL) {
 355         out->err(out, "error: Could not connect to %s: %s",
 356                 pcmk_ipc_name(api, true),
 357                 pcmk_rc_str(rc));
 358         data->rc = rc;
 359         return NULL;
 360     }
 361     if (cb != NULL) {
 362         pcmk_register_ipc_callback(api, cb, data);
 363     }
 364 
 365     rc = pcmk_connect_ipc(api, dispatch_type);
 366 
 367     if (rc != pcmk_rc_ok) {
 368         if (rc == EREMOTEIO) {
 369             data->pcmkd_state = pcmk_pacemakerd_state_remote;
 370             if (eremoteio_ok) {
 371                 /* EREMOTEIO may be expected and acceptable for some callers
 372                  * on a Pacemaker Remote node
 373                  */
 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 = start_nsec;
 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, 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, 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 name to get. If \p NULL
 590  *                                    or 0, get the local node name. If not
 591  *                                    \p NULL, store the true node ID here on
 592  *                                    success.
 593  * \param[out]    node_name           If not \p NULL, where to store the node
 594  *                                    name
 595  * \param[out]    uuid                If not \p NULL, where to store the node
 596  *                                    UUID
 597  * \param[out]    state               If not \p NULL, where to store the
 598  *                                    membership state
 599  * \param[out]    is_remote           If not \p NULL, where to store whether the
 600  *                                    node is a Pacemaker Remote node
 601  * \param[out]    have_quorum         If not \p 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  *                                    \p pacemaker-controld API. If 0,
 606  *                                    \p pcmk_ipc_dispatch_sync will be used.
 607  *                                    Otherwise, \p 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, 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, 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     gboolean 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, XML_ATTR_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                  name ? name : id,
 823                  id,
 824                  data->bash_export);
 825     data->found++;
 826 }
 827 
 828 // \return Standard Pacemaker return code
 829 int
 830 pcmk__list_nodes(pcmk__output_t *out, const char *node_types,
     /* [previous][next][first][last][top][bottom][index][help] */
 831                  gboolean bash_export)
 832 {
 833     xmlNode *xml_node = NULL;
 834     int rc;
 835 
 836     rc = cib__signon_query(out, NULL, &xml_node);
 837 
 838     if (rc == pcmk_rc_ok) {
 839         struct node_data data = {
 840             .out = out,
 841             .found = 0,
 842             .bash_export = bash_export
 843         };
 844 
 845         out->begin_list(out, NULL, NULL, "nodes");
 846 
 847         if (!pcmk__str_empty(node_types) && strstr(node_types, "all")) {
 848             node_types = NULL;
 849         }
 850 
 851         if (pcmk__str_empty(node_types) || strstr(node_types, "cluster")) {
 852             data.field = "id";
 853             data.type = "cluster";
 854             crm_foreach_xpath_result(xml_node, PCMK__XP_MEMBER_NODE_CONFIG,
 855                                      remote_node_print_helper, &data);
 856         }
 857 
 858         if (pcmk__str_empty(node_types) || strstr(node_types, "guest")) {
 859             data.field = "value";
 860             data.type = "guest";
 861             crm_foreach_xpath_result(xml_node, PCMK__XP_GUEST_NODE_CONFIG,
 862                                      remote_node_print_helper, &data);
 863         }
 864 
 865         if (pcmk__str_empty(node_types) || !pcmk__strcmp(node_types, ",|^remote", pcmk__str_regex)) {
 866             data.field = "id";
 867             data.type = "remote";
 868             crm_foreach_xpath_result(xml_node, PCMK__XP_REMOTE_NODE_CONFIG,
 869                                      remote_node_print_helper, &data);
 870         }
 871 
 872         out->end_list(out);
 873 
 874         if (data.found == 0) {
 875             out->info(out, "No nodes configured");
 876         }
 877 
 878         free_xml(xml_node);
 879     }
 880 
 881     return rc;
 882 }
 883 
 884 int
 885 pcmk_list_nodes(xmlNodePtr *xml, const char *node_types)
     /* [previous][next][first][last][top][bottom][index][help] */
 886 {
 887     pcmk__output_t *out = NULL;
 888     int rc = pcmk_rc_ok;
 889 
 890     rc = pcmk__xml_output_new(&out, xml);
 891     if (rc != pcmk_rc_ok) {
 892         return rc;
 893     }
 894 
 895     pcmk__register_lib_messages(out);
 896 
 897     rc = pcmk__list_nodes(out, node_types, FALSE);
 898     pcmk__xml_output_finish(out, xml);
 899     return rc;
 900 }

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