root/daemons/based/based_messages.c

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

DEFINITIONS

This source file includes following definitions.
  1. cib_process_shutdown_req
  2. cib_process_noop
  3. cib_process_readwrite
  4. send_sync_request
  5. cib_process_ping
  6. cib_process_sync
  7. cib_process_upgrade_server
  8. cib_process_sync_one
  9. cib_server_process_diff
  10. cib_process_replace_svr
  11. cib_process_delete_absolute
  12. cib_msg_copy
  13. sync_our_cib
  14. cib_process_commit_transaction
  15. cib_process_schemas

   1 /*
   2  * Copyright 2004-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 General Public License version 2
   7  * or later (GPLv2+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #include <crm_internal.h>
  11 
  12 #include <stdio.h>
  13 #include <unistd.h>
  14 #include <stdlib.h>
  15 #include <errno.h>
  16 #include <fcntl.h>
  17 #include <time.h>
  18 
  19 #include <sys/param.h>
  20 #include <sys/types.h>
  21 
  22 #include <glib.h>
  23 #include <libxml/tree.h>
  24 
  25 #include <crm/crm.h>
  26 #include <crm/cib/internal.h>
  27 
  28 #include <crm/common/xml.h>
  29 #include <crm/common/ipc_internal.h>
  30 #include <crm/common/xml_internal.h>
  31 #include <crm/cluster/internal.h>
  32 
  33 #include <pacemaker-based.h>
  34 
  35 /* Maximum number of diffs to ignore while waiting for a resync */
  36 #define MAX_DIFF_RETRY 5
  37 
  38 bool based_is_primary = false;
  39 
  40 xmlNode *the_cib = NULL;
  41 
  42 int
  43 cib_process_shutdown_req(const char *op, int options, const char *section, xmlNode * req,
     /* [previous][next][first][last][top][bottom][index][help] */
  44                          xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib,
  45                          xmlNode ** answer)
  46 {
  47     const char *host = crm_element_value(req, PCMK__XA_SRC);
  48 
  49     *answer = NULL;
  50 
  51     if (crm_element_value(req, PCMK__XA_CIB_ISREPLYTO) == NULL) {
  52         crm_info("Peer %s is requesting to shut down", host);
  53         return pcmk_ok;
  54     }
  55 
  56     if (cib_shutdown_flag == FALSE) {
  57         crm_err("Peer %s mistakenly thinks we wanted to shut down", host);
  58         return -EINVAL;
  59     }
  60 
  61     crm_info("Peer %s has acknowledged our shutdown request", host);
  62     terminate_cib(__func__, 0);
  63     return pcmk_ok;
  64 }
  65 
  66 // @COMPAT: Remove when PCMK__CIB_REQUEST_NOOP is removed
  67 int
  68 cib_process_noop(const char *op, int options, const char *section, xmlNode *req,
     /* [previous][next][first][last][top][bottom][index][help] */
  69                  xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib,
  70                  xmlNode **answer)
  71 {
  72     crm_trace("Processing \"%s\" event", op);
  73     *answer = NULL;
  74     return pcmk_ok;
  75 }
  76 
  77 int
  78 cib_process_readwrite(const char *op, int options, const char *section, xmlNode * req,
     /* [previous][next][first][last][top][bottom][index][help] */
  79                       xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib,
  80                       xmlNode ** answer)
  81 {
  82     int result = pcmk_ok;
  83 
  84     crm_trace("Processing \"%s\" event", op);
  85 
  86     if (pcmk__str_eq(op, PCMK__CIB_REQUEST_IS_PRIMARY, pcmk__str_none)) {
  87         if (based_is_primary) {
  88             result = pcmk_ok;
  89         } else {
  90             result = -EPERM;
  91         }
  92         return result;
  93     }
  94 
  95     if (pcmk__str_eq(op, PCMK__CIB_REQUEST_PRIMARY, pcmk__str_none)) {
  96         if (!based_is_primary) {
  97             crm_info("We are now in R/W mode");
  98             based_is_primary = true;
  99         } else {
 100             crm_debug("We are still in R/W mode");
 101         }
 102 
 103     } else if (based_is_primary) {
 104         crm_info("We are now in R/O mode");
 105         based_is_primary = false;
 106     }
 107 
 108     return result;
 109 }
 110 
 111 /* Set to 1 when a sync is requested, incremented when a diff is ignored,
 112  * reset to 0 when a sync is received
 113  */
 114 static int sync_in_progress = 0;
 115 
 116 void
 117 send_sync_request(const char *host)
     /* [previous][next][first][last][top][bottom][index][help] */
 118 {
 119     xmlNode *sync_me = pcmk__xe_create(NULL, "sync-me");
 120     crm_node_t *peer = NULL;
 121 
 122     crm_info("Requesting re-sync from %s", (host? host : "all peers"));
 123     sync_in_progress = 1;
 124 
 125     crm_xml_add(sync_me, PCMK__XA_T, PCMK__VALUE_CIB);
 126     crm_xml_add(sync_me, PCMK__XA_CIB_OP, PCMK__CIB_REQUEST_SYNC_TO_ONE);
 127     crm_xml_add(sync_me, PCMK__XA_CIB_DELEGATED_FROM,
 128                 stand_alone? "localhost" : crm_cluster->uname);
 129 
 130     if (host != NULL) {
 131         peer = pcmk__get_node(0, host, NULL, pcmk__node_search_cluster_member);
 132     }
 133     pcmk__cluster_send_message(peer, crm_msg_cib, sync_me);
 134     free_xml(sync_me);
 135 }
 136 
 137 int
 138 cib_process_ping(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
     /* [previous][next][first][last][top][bottom][index][help] */
 139                  xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
 140 {
 141     const char *host = crm_element_value(req, PCMK__XA_SRC);
 142     const char *seq = crm_element_value(req, PCMK__XA_CIB_PING_ID);
 143     char *digest = calculate_xml_versioned_digest(the_cib, FALSE, TRUE, CRM_FEATURE_SET);
 144 
 145     xmlNode *wrapper = NULL;
 146 
 147     crm_trace("Processing \"%s\" event %s from %s", op, seq, host);
 148     *answer = pcmk__xe_create(NULL, PCMK__XE_PING_RESPONSE);
 149 
 150     crm_xml_add(*answer, PCMK_XA_CRM_FEATURE_SET, CRM_FEATURE_SET);
 151     crm_xml_add(*answer, PCMK__XA_DIGEST, digest);
 152     crm_xml_add(*answer, PCMK__XA_CIB_PING_ID, seq);
 153 
 154     wrapper = pcmk__xe_create(*answer, PCMK__XE_CIB_CALLDATA);
 155 
 156     if (the_cib != NULL) {
 157         pcmk__if_tracing(
 158             {
 159                 /* Append additional detail so the receiver can log the
 160                  * differences
 161                  */
 162                 pcmk__xml_copy(wrapper, the_cib);
 163             },
 164             {
 165                 // Always include at least the version details
 166                 const char *name = (const char *) the_cib->name;
 167                 xmlNode *shallow = pcmk__xe_create(wrapper, name);
 168 
 169                 pcmk__xe_copy_attrs(shallow, the_cib, pcmk__xaf_none);
 170             }
 171         );
 172     }
 173 
 174     crm_info("Reporting our current digest to %s: %s for %s.%s.%s",
 175              host, digest,
 176              crm_element_value(existing_cib, PCMK_XA_ADMIN_EPOCH),
 177              crm_element_value(existing_cib, PCMK_XA_EPOCH),
 178              crm_element_value(existing_cib, PCMK_XA_NUM_UPDATES));
 179 
 180     free(digest);
 181 
 182     return pcmk_ok;
 183 }
 184 
 185 int
 186 cib_process_sync(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
     /* [previous][next][first][last][top][bottom][index][help] */
 187                  xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
 188 {
 189     return sync_our_cib(req, TRUE);
 190 }
 191 
 192 int
 193 cib_process_upgrade_server(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
     /* [previous][next][first][last][top][bottom][index][help] */
 194                            xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
 195 {
 196     int rc = pcmk_ok;
 197 
 198     *answer = NULL;
 199 
 200     if (crm_element_value(req, PCMK__XA_CIB_SCHEMA_MAX) != NULL) {
 201         /* The originator of an upgrade request sends it to the DC, without
 202          * PCMK__XA_CIB_SCHEMA_MAX. If an upgrade is needed, the DC
 203          * re-broadcasts the request with PCMK__XA_CIB_SCHEMA_MAX, and each node
 204          * performs the upgrade (and notifies its local clients) here.
 205          */
 206         return cib_process_upgrade(
 207             op, options, section, req, input, existing_cib, result_cib, answer);
 208 
 209     } else {
 210         xmlNode *scratch = pcmk__xml_copy(NULL, existing_cib);
 211         const char *host = crm_element_value(req, PCMK__XA_SRC);
 212         const char *original_schema = NULL;
 213         const char *new_schema = NULL;
 214         const char *client_id = crm_element_value(req, PCMK__XA_CIB_CLIENTID);
 215         const char *call_opts = crm_element_value(req, PCMK__XA_CIB_CALLOPT);
 216         const char *call_id = crm_element_value(req, PCMK__XA_CIB_CALLID);
 217 
 218         crm_trace("Processing \"%s\" event", op);
 219         original_schema = crm_element_value(existing_cib,
 220                                             PCMK_XA_VALIDATE_WITH);
 221         rc = pcmk__update_schema(&scratch, NULL, true, true);
 222         rc = pcmk_rc2legacy(rc);
 223         new_schema = crm_element_value(scratch, PCMK_XA_VALIDATE_WITH);
 224 
 225         if (pcmk__cmp_schemas_by_name(new_schema, original_schema) > 0) {
 226             xmlNode *up = pcmk__xe_create(NULL, __func__);
 227 
 228             rc = pcmk_ok;
 229             crm_notice("Upgrade request from %s verified", host);
 230 
 231             crm_xml_add(up, PCMK__XA_T, PCMK__VALUE_CIB);
 232             crm_xml_add(up, PCMK__XA_CIB_OP, PCMK__CIB_REQUEST_UPGRADE);
 233             crm_xml_add(up, PCMK__XA_CIB_SCHEMA_MAX, new_schema);
 234             crm_xml_add(up, PCMK__XA_CIB_DELEGATED_FROM, host);
 235             crm_xml_add(up, PCMK__XA_CIB_CLIENTID, client_id);
 236             crm_xml_add(up, PCMK__XA_CIB_CALLOPT, call_opts);
 237             crm_xml_add(up, PCMK__XA_CIB_CALLID, call_id);
 238 
 239             if (cib_legacy_mode() && based_is_primary) {
 240                 rc = cib_process_upgrade(
 241                     op, options, section, up, input, existing_cib, result_cib, answer);
 242 
 243             } else {
 244                 pcmk__cluster_send_message(NULL, crm_msg_cib, up);
 245             }
 246 
 247             free_xml(up);
 248 
 249         } else if(rc == pcmk_ok) {
 250             rc = -pcmk_err_schema_unchanged;
 251         }
 252 
 253         if (rc != pcmk_ok) {
 254             // Notify originating peer so it can notify its local clients
 255             crm_node_t *origin = NULL;
 256 
 257             origin = pcmk__search_node_caches(0, host,
 258                                               pcmk__node_search_cluster_member);
 259 
 260             crm_info("Rejecting upgrade request from %s: %s "
 261                      CRM_XS " rc=%d peer=%s", host, pcmk_strerror(rc), rc,
 262                      (origin? origin->uname : "lost"));
 263 
 264             if (origin) {
 265                 xmlNode *up = pcmk__xe_create(NULL, __func__);
 266 
 267                 crm_xml_add(up, PCMK__XA_T, PCMK__VALUE_CIB);
 268                 crm_xml_add(up, PCMK__XA_CIB_OP, PCMK__CIB_REQUEST_UPGRADE);
 269                 crm_xml_add(up, PCMK__XA_CIB_DELEGATED_FROM, host);
 270                 crm_xml_add(up, PCMK__XA_CIB_ISREPLYTO, host);
 271                 crm_xml_add(up, PCMK__XA_CIB_CLIENTID, client_id);
 272                 crm_xml_add(up, PCMK__XA_CIB_CALLOPT, call_opts);
 273                 crm_xml_add(up, PCMK__XA_CIB_CALLID, call_id);
 274                 crm_xml_add_int(up, PCMK__XA_CIB_UPGRADE_RC, rc);
 275                 if (!pcmk__cluster_send_message(origin, crm_msg_cib, up)) {
 276                     crm_warn("Could not send CIB upgrade result to %s", host);
 277                 }
 278                 free_xml(up);
 279             }
 280         }
 281         free_xml(scratch);
 282     }
 283     return rc;
 284 }
 285 
 286 int
 287 cib_process_sync_one(const char *op, int options, const char *section, xmlNode * req,
     /* [previous][next][first][last][top][bottom][index][help] */
 288                      xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib,
 289                      xmlNode ** answer)
 290 {
 291     return sync_our_cib(req, FALSE);
 292 }
 293 
 294 int
 295 cib_server_process_diff(const char *op, int options, const char *section, xmlNode * req,
     /* [previous][next][first][last][top][bottom][index][help] */
 296                         xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib,
 297                         xmlNode ** answer)
 298 {
 299     int rc = pcmk_ok;
 300 
 301     if (sync_in_progress > MAX_DIFF_RETRY) {
 302         /* Don't ignore diffs forever; the last request may have been lost.
 303          * If the diff fails, we'll ask for another full resync.
 304          */
 305         sync_in_progress = 0;
 306     }
 307 
 308     // The primary instance should never ignore a diff
 309     if (sync_in_progress && !based_is_primary) {
 310         int diff_add_updates = 0;
 311         int diff_add_epoch = 0;
 312         int diff_add_admin_epoch = 0;
 313 
 314         int diff_del_updates = 0;
 315         int diff_del_epoch = 0;
 316         int diff_del_admin_epoch = 0;
 317 
 318         cib_diff_version_details(input,
 319                                  &diff_add_admin_epoch, &diff_add_epoch, &diff_add_updates,
 320                                  &diff_del_admin_epoch, &diff_del_epoch, &diff_del_updates);
 321 
 322         sync_in_progress++;
 323         crm_notice("Not applying diff %d.%d.%d -> %d.%d.%d (sync in progress)",
 324                    diff_del_admin_epoch, diff_del_epoch, diff_del_updates,
 325                    diff_add_admin_epoch, diff_add_epoch, diff_add_updates);
 326         return -pcmk_err_diff_resync;
 327     }
 328 
 329     rc = cib_process_diff(op, options, section, req, input, existing_cib, result_cib, answer);
 330     crm_trace("result: %s (%d), %s", pcmk_strerror(rc), rc,
 331               (based_is_primary? "primary": "secondary"));
 332 
 333     if ((rc == -pcmk_err_diff_resync) && !based_is_primary) {
 334         free_xml(*result_cib);
 335         *result_cib = NULL;
 336         send_sync_request(NULL);
 337 
 338     } else if (rc == -pcmk_err_diff_resync) {
 339         rc = -pcmk_err_diff_failed;
 340         if (options & cib_force_diff) {
 341             crm_warn("Not requesting full refresh in R/W mode");
 342         }
 343 
 344     } else if ((rc != pcmk_ok) && !based_is_primary && cib_legacy_mode()) {
 345         crm_warn("Requesting full CIB refresh because update failed: %s"
 346                  CRM_XS " rc=%d", pcmk_strerror(rc), rc);
 347 
 348         pcmk__log_xml_patchset(LOG_INFO, input);
 349         free_xml(*result_cib);
 350         *result_cib = NULL;
 351         send_sync_request(NULL);
 352     }
 353 
 354     return rc;
 355 }
 356 
 357 int
 358 cib_process_replace_svr(const char *op, int options, const char *section, xmlNode * req,
     /* [previous][next][first][last][top][bottom][index][help] */
 359                         xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib,
 360                         xmlNode ** answer)
 361 {
 362     int rc =
 363         cib_process_replace(op, options, section, req, input, existing_cib, result_cib, answer);
 364 
 365     if ((rc == pcmk_ok) && pcmk__xe_is(input, PCMK_XE_CIB)) {
 366         sync_in_progress = 0;
 367     }
 368     return rc;
 369 }
 370 
 371 // @COMPAT: Remove when PCMK__CIB_REQUEST_ABS_DELETE is removed
 372 int
 373 cib_process_delete_absolute(const char *op, int options, const char *section, xmlNode * req,
     /* [previous][next][first][last][top][bottom][index][help] */
 374                             xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib,
 375                             xmlNode ** answer)
 376 {
 377     return -EINVAL;
 378 }
 379 
 380 static xmlNode *
 381 cib_msg_copy(xmlNode *msg)
     /* [previous][next][first][last][top][bottom][index][help] */
 382 {
 383     static const char *field_list[] = {
 384         PCMK__XA_T,
 385         PCMK__XA_CIB_CLIENTID,
 386         PCMK__XA_CIB_CALLOPT,
 387         PCMK__XA_CIB_CALLID,
 388         PCMK__XA_CIB_OP,
 389         PCMK__XA_CIB_ISREPLYTO,
 390         PCMK__XA_CIB_SECTION,
 391         PCMK__XA_CIB_HOST,
 392         PCMK__XA_CIB_RC,
 393         PCMK__XA_CIB_DELEGATED_FROM,
 394         PCMK__XA_CIB_OBJECT,
 395         PCMK__XA_CIB_OBJECT_TYPE,
 396         PCMK__XA_CIB_UPDATE,
 397         PCMK__XA_CIB_CLIENTNAME,
 398         PCMK__XA_CIB_USER,
 399         PCMK__XA_CIB_NOTIFY_TYPE,
 400         PCMK__XA_CIB_NOTIFY_ACTIVATE,
 401     };
 402 
 403     xmlNode *copy = pcmk__xe_create(NULL, PCMK__XE_COPY);
 404 
 405     for (int lpc = 0; lpc < PCMK__NELEM(field_list); lpc++) {
 406         const char *field = field_list[lpc];
 407         const char *value = crm_element_value(msg, field);
 408 
 409         if (value != NULL) {
 410             crm_xml_add(copy, field, value);
 411         }
 412     }
 413 
 414     return copy;
 415 }
 416 
 417 int
 418 sync_our_cib(xmlNode * request, gboolean all)
     /* [previous][next][first][last][top][bottom][index][help] */
 419 {
 420     int result = pcmk_ok;
 421     char *digest = NULL;
 422     const char *host = crm_element_value(request, PCMK__XA_SRC);
 423     const char *op = crm_element_value(request, PCMK__XA_CIB_OP);
 424     crm_node_t *peer = NULL;
 425     xmlNode *replace_request = NULL;
 426     xmlNode *wrapper = NULL;
 427 
 428     CRM_CHECK(the_cib != NULL, return -EINVAL);
 429     CRM_CHECK(all || (host != NULL), return -EINVAL);
 430 
 431     crm_debug("Syncing CIB to %s", all ? "all peers" : host);
 432 
 433     replace_request = cib_msg_copy(request);
 434 
 435     if (host != NULL) {
 436         crm_xml_add(replace_request, PCMK__XA_CIB_ISREPLYTO, host);
 437     }
 438     if (all) {
 439         pcmk__xe_remove_attr(replace_request, PCMK__XA_CIB_HOST);
 440     }
 441 
 442     crm_xml_add(replace_request, PCMK__XA_CIB_OP, PCMK__CIB_REQUEST_REPLACE);
 443 
 444     // @TODO Keep for tracing, or drop?
 445     crm_xml_add(replace_request, PCMK__XA_ORIGINAL_CIB_OP, op);
 446 
 447     pcmk__xe_set_bool_attr(replace_request, PCMK__XA_CIB_UPDATE, true);
 448 
 449     crm_xml_add(replace_request, PCMK_XA_CRM_FEATURE_SET, CRM_FEATURE_SET);
 450     digest = calculate_xml_versioned_digest(the_cib, FALSE, TRUE, CRM_FEATURE_SET);
 451     crm_xml_add(replace_request, PCMK__XA_DIGEST, digest);
 452 
 453     wrapper = pcmk__xe_create(replace_request, PCMK__XE_CIB_CALLDATA);
 454     pcmk__xml_copy(wrapper, the_cib);
 455 
 456     if (!all) {
 457         peer = pcmk__get_node(0, host, NULL, pcmk__node_search_cluster_member);
 458     }
 459     if (!pcmk__cluster_send_message(peer, crm_msg_cib, replace_request)) {
 460         result = -ENOTCONN;
 461     }
 462     free_xml(replace_request);
 463     free(digest);
 464     return result;
 465 }
 466 
 467 int
 468 cib_process_commit_transaction(const char *op, int options, const char *section,
     /* [previous][next][first][last][top][bottom][index][help] */
 469                                xmlNode *req, xmlNode *input,
 470                                xmlNode *existing_cib, xmlNode **result_cib,
 471                                xmlNode **answer)
 472 {
 473     /* On success, our caller will activate *result_cib locally, trigger a
 474      * replace notification if appropriate, and sync *result_cib to all nodes.
 475      * On failure, our caller will free *result_cib.
 476      */
 477     int rc = pcmk_rc_ok;
 478     const char *client_id = crm_element_value(req, PCMK__XA_CIB_CLIENTID);
 479     const char *origin = crm_element_value(req, PCMK__XA_SRC);
 480     pcmk__client_t *client = pcmk__find_client_by_id(client_id);
 481 
 482     rc = based_commit_transaction(input, client, origin, result_cib);
 483 
 484     if (rc != pcmk_rc_ok) {
 485         char *source = based_transaction_source_str(client, origin);
 486 
 487         crm_err("Could not commit transaction for %s: %s",
 488                 source, pcmk_rc_str(rc));
 489         free(source);
 490     }
 491     return pcmk_rc2legacy(rc);
 492 }
 493 
 494 int
 495 cib_process_schemas(const char *op, int options, const char *section, xmlNode *req,
     /* [previous][next][first][last][top][bottom][index][help] */
 496                     xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib,
 497                     xmlNode **answer)
 498 {
 499     xmlNode *wrapper = NULL;
 500     xmlNode *data = NULL;
 501 
 502     const char *after_ver = NULL;
 503     GList *schemas = NULL;
 504     GList *already_included = NULL;
 505 
 506     *answer = pcmk__xe_create(NULL, PCMK__XA_SCHEMAS);
 507 
 508     wrapper = pcmk__xe_first_child(req, PCMK__XE_CIB_CALLDATA, NULL, NULL);
 509     data = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
 510     if (data == NULL) {
 511         crm_warn("No data specified in request");
 512         return -EPROTO;
 513     }
 514 
 515     after_ver = crm_element_value(data, PCMK_XA_VERSION);
 516     if (after_ver == NULL) {
 517         crm_warn("No version specified in request");
 518         return -EPROTO;
 519     }
 520 
 521     /* The client requested all schemas after the latest one we know about, which
 522      * means the client is fully up-to-date.  Return a properly formatted reply
 523      * with no schemas.
 524      */
 525     if (pcmk__str_eq(after_ver, pcmk__highest_schema_name(), pcmk__str_none)) {
 526         return pcmk_ok;
 527     }
 528 
 529     schemas = pcmk__schema_files_later_than(after_ver);
 530 
 531     for (GList *iter = schemas; iter != NULL; iter = iter->next) {
 532         pcmk__build_schema_xml_node(*answer, iter->data, &already_included);
 533     }
 534 
 535     g_list_free_full(schemas, free);
 536     g_list_free_full(already_included, free);
 537     return pcmk_ok;
 538 }

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