root/lib/pacemaker/pcmk_cluster_queries.c

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

DEFINITIONS

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

   1 /*
   2  * Copyright 2020-2021 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/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 #include <crm/common/mainloop.h>
  28 
  29 #define DEFAULT_MESSAGE_TIMEOUT_MS 30000
  30 
  31 
  32 typedef struct {
  33     pcmk__output_t *out;
  34     GMainLoop *mainloop;
  35     int rc;
  36     guint message_timer_id;
  37     guint message_timeout_ms;
  38 } data_t;
  39 
  40 static void
  41 quit_main_loop(data_t *data)
     /* [previous][next][first][last][top][bottom][index][help] */
  42 {
  43     if (data->mainloop != NULL) {
  44         GMainLoop *mloop = data->mainloop;
  45 
  46         data->mainloop = NULL; // Don't re-enter this block
  47         pcmk_quit_main_loop(mloop, 10);
  48         g_main_loop_unref(mloop);
  49     }
  50 }
  51 
  52 static gboolean
  53 admin_message_timeout(gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
  54 {
  55     data_t *data = user_data;
  56     pcmk__output_t *out = data->out;
  57 
  58     out->err(out, "error: No reply received from controller before timeout (%dms)",
  59             data->message_timeout_ms);
  60     data->message_timer_id = 0;
  61     data->rc = ETIMEDOUT;
  62     quit_main_loop(data);
  63     return FALSE; // Tells glib to remove source
  64 }
  65 
  66 static void
  67 start_main_loop(data_t *data)
     /* [previous][next][first][last][top][bottom][index][help] */
  68 {
  69     if (data->message_timeout_ms < 1) {
  70         data->message_timeout_ms = DEFAULT_MESSAGE_TIMEOUT_MS;
  71     }
  72 
  73     data->rc = ECONNRESET; // For unexpected disconnects
  74     data->mainloop = g_main_loop_new(NULL, FALSE);
  75     data->message_timer_id = g_timeout_add(data->message_timeout_ms,
  76                                      admin_message_timeout,
  77                                      data);
  78     g_main_loop_run(data->mainloop);
  79 }
  80 
  81 static void
  82 event_done(data_t *data, pcmk_ipc_api_t *api)
     /* [previous][next][first][last][top][bottom][index][help] */
  83 {
  84     pcmk_disconnect_ipc(api);
  85     quit_main_loop(data);
  86 }
  87 
  88 static pcmk_controld_api_reply_t *
  89 controld_event_reply(data_t *data, pcmk_ipc_api_t *controld_api, enum pcmk_ipc_event event_type, crm_exit_t status, void *event_data)
     /* [previous][next][first][last][top][bottom][index][help] */
  90 {
  91     pcmk__output_t *out = data->out;
  92     pcmk_controld_api_reply_t *reply = event_data;
  93 
  94     switch (event_type) {
  95         case pcmk_ipc_event_disconnect:
  96             if (data->rc == ECONNRESET) { // Unexpected
  97                 out->err(out, "error: Lost connection to controller");
  98             }
  99             event_done(data, controld_api);
 100             return NULL;
 101 
 102         case pcmk_ipc_event_reply:
 103             break;
 104 
 105         default:
 106             return NULL;
 107     }
 108 
 109     if (data->message_timer_id != 0) {
 110         g_source_remove(data->message_timer_id);
 111         data->message_timer_id = 0;
 112     }
 113 
 114     if (status != CRM_EX_OK) {
 115         out->err(out, "error: Bad reply from controller: %s",
 116                 crm_exit_str(status));
 117         data->rc = EBADMSG;
 118         event_done(data, controld_api);
 119         return NULL;
 120     }
 121 
 122     if (reply->reply_type != pcmk_controld_reply_ping) {
 123         out->err(out, "error: Unknown reply type %d from controller",
 124                 reply->reply_type);
 125         data->rc = EBADMSG;
 126         event_done(data, controld_api);
 127         return NULL;
 128     }
 129 
 130     return reply;
 131 }
 132 
 133 static void
 134 controller_status_event_cb(pcmk_ipc_api_t *controld_api,
     /* [previous][next][first][last][top][bottom][index][help] */
 135                     enum pcmk_ipc_event event_type, crm_exit_t status,
 136                     void *event_data, void *user_data)
 137 {
 138     data_t *data = user_data;
 139     pcmk__output_t *out = data->out;
 140     pcmk_controld_api_reply_t *reply = controld_event_reply(data, controld_api,
 141         event_type, status, event_data);
 142 
 143     if (reply != NULL) {
 144         out->message(out, "health",
 145                reply->data.ping.sys_from,
 146                reply->host_from,
 147                reply->data.ping.fsa_state,
 148                reply->data.ping.result);
 149         data->rc = pcmk_rc_ok;
 150     }
 151 
 152     event_done(data, controld_api);
 153 }
 154 
 155 static void
 156 designated_controller_event_cb(pcmk_ipc_api_t *controld_api,
     /* [previous][next][first][last][top][bottom][index][help] */
 157                     enum pcmk_ipc_event event_type, crm_exit_t status,
 158                     void *event_data, void *user_data)
 159 {
 160     data_t *data = user_data;
 161     pcmk__output_t *out = data->out;
 162     pcmk_controld_api_reply_t *reply = controld_event_reply(data, controld_api,
 163         event_type, status, event_data);
 164 
 165     if (reply != NULL) {
 166         out->message(out, "dc", reply->host_from);
 167         data->rc = pcmk_rc_ok;
 168     }
 169 
 170     event_done(data, controld_api);
 171 }
 172 
 173 static void
 174 pacemakerd_event_cb(pcmk_ipc_api_t *pacemakerd_api,
     /* [previous][next][first][last][top][bottom][index][help] */
 175                     enum pcmk_ipc_event event_type, crm_exit_t status,
 176                     void *event_data, void *user_data)
 177 {
 178     data_t *data = user_data;
 179     pcmk__output_t *out = data->out;
 180     pcmk_pacemakerd_api_reply_t *reply = event_data;
 181 
 182     crm_time_t *crm_when;
 183     char *pinged_buf = NULL;
 184 
 185     switch (event_type) {
 186         case pcmk_ipc_event_disconnect:
 187             if (data->rc == ECONNRESET) { // Unexpected
 188                 out->err(out, "error: Lost connection to pacemakerd");
 189             }
 190             event_done(data, pacemakerd_api);
 191             return;
 192 
 193         case pcmk_ipc_event_reply:
 194             break;
 195 
 196         default:
 197             return;
 198     }
 199 
 200     if (data->message_timer_id != 0) {
 201         g_source_remove(data->message_timer_id);
 202         data->message_timer_id = 0;
 203     }
 204 
 205     if (status != CRM_EX_OK) {
 206         out->err(out, "error: Bad reply from pacemakerd: %s",
 207                 crm_exit_str(status));
 208         event_done(data, pacemakerd_api);
 209         return;
 210     }
 211 
 212     if (reply->reply_type != pcmk_pacemakerd_reply_ping) {
 213         out->err(out, "error: Unknown reply type %d from pacemakerd",
 214                 reply->reply_type);
 215         event_done(data, pacemakerd_api);
 216         return;
 217     }
 218 
 219     // Parse desired information from reply
 220     crm_when = crm_time_new(NULL);
 221     crm_time_set_timet(crm_when, &reply->data.ping.last_good);
 222     pinged_buf = crm_time_as_string(crm_when,
 223         crm_time_log_date | crm_time_log_timeofday |
 224             crm_time_log_with_timezone);
 225 
 226     out->message(out, "pacemakerd-health",
 227         reply->data.ping.sys_from,
 228         (reply->data.ping.status == pcmk_rc_ok)?
 229             pcmk_pacemakerd_api_daemon_state_enum2text(
 230                 reply->data.ping.state):"query failed",
 231         (reply->data.ping.status == pcmk_rc_ok)?pinged_buf:"");
 232     data->rc = pcmk_rc_ok;
 233     crm_time_free(crm_when);
 234     free(pinged_buf);
 235 
 236     event_done(data, pacemakerd_api);
 237 }
 238 
 239 static pcmk_ipc_api_t *
 240 ipc_connect(data_t *data, enum pcmk_ipc_server server, pcmk_ipc_callback_t cb)
     /* [previous][next][first][last][top][bottom][index][help] */
 241 {
 242     int rc;
 243     pcmk__output_t *out = data->out;
 244     pcmk_ipc_api_t *api = NULL;
 245 
 246 
 247     rc = pcmk_new_ipc_api(&api, server);
 248     if (api == NULL) {
 249         out->err(out, "error: Could not connect to %s: %s",
 250                 pcmk_ipc_name(api, true),
 251                 pcmk_rc_str(rc));
 252         data->rc = rc;
 253         return NULL;
 254     }
 255     if (cb != NULL) {
 256         pcmk_register_ipc_callback(api, cb, data);
 257     }
 258     rc = pcmk_connect_ipc(api, pcmk_ipc_dispatch_main);
 259     if (rc != pcmk_rc_ok) {
 260         out->err(out, "error: Could not connect to %s: %s",
 261                 pcmk_ipc_name(api, true),
 262                 pcmk_rc_str(rc));
 263         data->rc = rc;
 264         return NULL;
 265     }
 266 
 267     return api;
 268 }
 269 
 270 int
 271 pcmk__controller_status(pcmk__output_t *out, char *dest_node, guint message_timeout_ms)
     /* [previous][next][first][last][top][bottom][index][help] */
 272 {
 273     data_t data = {
 274         .out = out,
 275         .mainloop = NULL,
 276         .rc = pcmk_rc_ok,
 277         .message_timer_id = 0,
 278         .message_timeout_ms = message_timeout_ms
 279     };
 280     pcmk_ipc_api_t *controld_api = ipc_connect(&data, pcmk_ipc_controld, controller_status_event_cb);
 281 
 282     if (controld_api != NULL) {
 283         int rc = pcmk_controld_api_ping(controld_api, dest_node);
 284         if (rc != pcmk_rc_ok) {
 285             out->err(out, "error: Command failed: %s", pcmk_rc_str(rc));
 286             data.rc = rc;
 287         }
 288 
 289         start_main_loop(&data);
 290 
 291         pcmk_free_ipc_api(controld_api);
 292     }
 293 
 294     return data.rc;
 295 }
 296 
 297 int
 298 pcmk_controller_status(xmlNodePtr *xml, char *dest_node, unsigned int message_timeout_ms)
     /* [previous][next][first][last][top][bottom][index][help] */
 299 {
 300     pcmk__output_t *out = NULL;
 301     int rc = pcmk_rc_ok;
 302 
 303     rc = pcmk__out_prologue(&out, xml);
 304     if (rc != pcmk_rc_ok) {
 305         return rc;
 306     }
 307 
 308     pcmk__register_lib_messages(out);
 309 
 310     rc = pcmk__controller_status(out, dest_node, (guint) message_timeout_ms);
 311     pcmk__out_epilogue(out, xml, rc);
 312     return rc;
 313 }
 314 
 315 int
 316 pcmk__designated_controller(pcmk__output_t *out, guint message_timeout_ms)
     /* [previous][next][first][last][top][bottom][index][help] */
 317 {
 318     data_t data = {
 319         .out = out,
 320         .mainloop = NULL,
 321         .rc = pcmk_rc_ok,
 322         .message_timer_id = 0,
 323         .message_timeout_ms = message_timeout_ms
 324     };
 325     pcmk_ipc_api_t *controld_api = ipc_connect(&data, pcmk_ipc_controld, designated_controller_event_cb);
 326 
 327     if (controld_api != NULL) {
 328         int rc = pcmk_controld_api_ping(controld_api, NULL);
 329         if (rc != pcmk_rc_ok) {
 330             out->err(out, "error: Command failed: %s", pcmk_rc_str(rc));
 331             data.rc = rc;
 332         }
 333 
 334         start_main_loop(&data);
 335 
 336         pcmk_free_ipc_api(controld_api);
 337     }
 338 
 339     return data.rc;
 340 }
 341 
 342 int
 343 pcmk_designated_controller(xmlNodePtr *xml, unsigned int message_timeout_ms)
     /* [previous][next][first][last][top][bottom][index][help] */
 344 {
 345     pcmk__output_t *out = NULL;
 346     int rc = pcmk_rc_ok;
 347 
 348     rc = pcmk__out_prologue(&out, xml);
 349     if (rc != pcmk_rc_ok) {
 350         return rc;
 351     }
 352 
 353     pcmk__register_lib_messages(out);
 354 
 355     rc = pcmk__designated_controller(out, (guint) message_timeout_ms);
 356     pcmk__out_epilogue(out, xml, rc);
 357     return rc;
 358 }
 359 
 360 int
 361 pcmk__pacemakerd_status(pcmk__output_t *out, char *ipc_name, guint message_timeout_ms)
     /* [previous][next][first][last][top][bottom][index][help] */
 362 {
 363     data_t data = {
 364         .out = out,
 365         .mainloop = NULL,
 366         .rc = pcmk_rc_ok,
 367         .message_timer_id = 0,
 368         .message_timeout_ms = message_timeout_ms
 369     };
 370     pcmk_ipc_api_t *pacemakerd_api = ipc_connect(&data, pcmk_ipc_pacemakerd, pacemakerd_event_cb);
 371 
 372     if (pacemakerd_api != NULL) {
 373         int rc = pcmk_pacemakerd_api_ping(pacemakerd_api, ipc_name);
 374         if (rc != pcmk_rc_ok) {
 375             out->err(out, "error: Command failed: %s", pcmk_rc_str(rc));
 376             data.rc = rc;
 377         }
 378 
 379         start_main_loop(&data);
 380 
 381         pcmk_free_ipc_api(pacemakerd_api);
 382     }
 383 
 384     return data.rc;
 385 }
 386 
 387 int
 388 pcmk_pacemakerd_status(xmlNodePtr *xml, char *ipc_name, unsigned int message_timeout_ms)
     /* [previous][next][first][last][top][bottom][index][help] */
 389 {
 390     pcmk__output_t *out = NULL;
 391     int rc = pcmk_rc_ok;
 392 
 393     rc = pcmk__out_prologue(&out, xml);
 394     if (rc != pcmk_rc_ok) {
 395         return rc;
 396     }
 397 
 398     pcmk__register_lib_messages(out);
 399 
 400     rc = pcmk__pacemakerd_status(out, ipc_name, (guint) message_timeout_ms);
 401     pcmk__out_epilogue(out, xml, rc);
 402     return rc;
 403 }
 404 
 405 /* user data for looping through remote node xpath searches */
 406 struct node_data {
 407     pcmk__output_t *out;
 408     int found;
 409     const char *field;  /* XML attribute to check for node name */
 410     const char *type;
 411     gboolean BASH_EXPORT;
 412 };
 413 
 414 static void
 415 remote_node_print_helper(xmlNode *result, void *user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 416 {
 417     struct node_data *data = user_data;
 418     pcmk__output_t *out = data->out;
 419     const char *name = crm_element_value(result, XML_ATTR_UNAME);
 420     const char *id = crm_element_value(result, data->field);
 421 
 422     // node name and node id are the same for remote/guest nodes
 423     out->message(out, "crmadmin-node", data->type,
 424                  name ? name : id,
 425                  id,
 426                  data->BASH_EXPORT);
 427     data->found++;
 428 }
 429 
 430 // \return Standard Pacemaker return code
 431 int
 432 pcmk__list_nodes(pcmk__output_t *out, char *node_types, gboolean BASH_EXPORT)
     /* [previous][next][first][last][top][bottom][index][help] */
 433 {
 434     cib_t *the_cib = cib_new();
 435     xmlNode *xml_node = NULL;
 436     int rc;
 437 
 438     if (the_cib == NULL) {
 439         return ENOMEM;
 440     }
 441     rc = the_cib->cmds->signon(the_cib, crm_system_name, cib_command);
 442     if (rc != pcmk_ok) {
 443         return pcmk_legacy2rc(rc);
 444     }
 445 
 446     rc = the_cib->cmds->query(the_cib, NULL, &xml_node,
 447                               cib_scope_local | cib_sync_call);
 448     if (rc == pcmk_ok) {
 449         struct node_data data = {
 450             .out = out,
 451             .found = 0,
 452             .BASH_EXPORT = BASH_EXPORT
 453         };
 454 
 455         out->begin_list(out, NULL, NULL, "nodes");
 456 
 457         if (!pcmk__str_empty(node_types) && strstr(node_types, "all")) {
 458             node_types = NULL;
 459         }
 460 
 461         if (pcmk__str_empty(node_types) || strstr(node_types, "cluster")) {
 462             data.field = "id";
 463             data.type = "cluster";
 464             crm_foreach_xpath_result(xml_node, PCMK__XP_MEMBER_NODE_CONFIG,
 465                                      remote_node_print_helper, &data);
 466         }
 467 
 468         if (pcmk__str_empty(node_types) || strstr(node_types, "guest")) {
 469             data.field = "value";
 470             data.type = "guest";
 471             crm_foreach_xpath_result(xml_node, PCMK__XP_GUEST_NODE_CONFIG,
 472                                      remote_node_print_helper, &data);
 473         }
 474 
 475         if (pcmk__str_empty(node_types) || !pcmk__strcmp(node_types, ",|^remote", pcmk__str_regex)) {
 476             data.field = "id";
 477             data.type = "remote";
 478             crm_foreach_xpath_result(xml_node, PCMK__XP_REMOTE_NODE_CONFIG,
 479                                      remote_node_print_helper, &data);
 480         }
 481 
 482         out->end_list(out);
 483 
 484         if (data.found == 0) {
 485             out->info(out, "No nodes configured");
 486         }
 487 
 488         free_xml(xml_node);
 489     }
 490     the_cib->cmds->signoff(the_cib);
 491     return pcmk_legacy2rc(rc);
 492 }
 493 
 494 int
 495 pcmk_list_nodes(xmlNodePtr *xml, char *node_types)
     /* [previous][next][first][last][top][bottom][index][help] */
 496 {
 497     pcmk__output_t *out = NULL;
 498     int rc = pcmk_rc_ok;
 499 
 500     rc = pcmk__out_prologue(&out, xml);
 501     if (rc != pcmk_rc_ok) {
 502         return rc;
 503     }
 504 
 505     pcmk__register_lib_messages(out);
 506 
 507     rc = pcmk__list_nodes(out, node_types, FALSE);
 508     pcmk__out_epilogue(out, xml, rc);
 509     return rc;
 510 }

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