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

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