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     // @COMPAT Pacemaker Remote clients <3.0.0 may send this
  87     if (pcmk__str_eq(op, PCMK__CIB_REQUEST_IS_PRIMARY, pcmk__str_none)) {
  88         if (based_is_primary) {
  89             result = pcmk_ok;
  90         } else {
  91             result = -EPERM;
  92         }
  93         return result;
  94     }
  95 
  96     if (pcmk__str_eq(op, PCMK__CIB_REQUEST_PRIMARY, pcmk__str_none)) {
  97         if (!based_is_primary) {
  98             crm_info("We are now in R/W mode");
  99             based_is_primary = true;
 100         } else {
 101             crm_debug("We are still in R/W mode");
 102         }
 103 
 104     } else if (based_is_primary) {
 105         crm_info("We are now in R/O mode");
 106         based_is_primary = false;
 107     }
 108 
 109     return result;
 110 }
 111 
 112 /* Set to 1 when a sync is requested, incremented when a diff is ignored,
 113  * reset to 0 when a sync is received
 114  */
 115 static int sync_in_progress = 0;
 116 
 117 void
 118 send_sync_request(const char *host)
     /* [previous][next][first][last][top][bottom][index][help] */
 119 {
 120     xmlNode *sync_me = pcmk__xe_create(NULL, "sync-me");
 121     pcmk__node_status_t *peer = NULL;
 122 
 123     crm_info("Requesting re-sync from %s", (host? host : "all peers"));
 124     sync_in_progress = 1;
 125 
 126     crm_xml_add(sync_me, PCMK__XA_T, PCMK__VALUE_CIB);
 127     crm_xml_add(sync_me, PCMK__XA_CIB_OP, PCMK__CIB_REQUEST_SYNC_TO_ONE);
 128     crm_xml_add(sync_me, PCMK__XA_CIB_DELEGATED_FROM, OUR_NODENAME);
 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, pcmk_ipc_based, sync_me);
 134     pcmk__xml_free(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 = pcmk__digest_xml(the_cib, true);
 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         if (original_schema == NULL) {
 222             crm_info("Rejecting upgrade request from %s: No "
 223                      PCMK_XA_VALIDATE_WITH, host);
 224             return -pcmk_err_cib_corrupt;
 225         }
 226 
 227         rc = pcmk__update_schema(&scratch, NULL, true, true);
 228         rc = pcmk_rc2legacy(rc);
 229         new_schema = crm_element_value(scratch, PCMK_XA_VALIDATE_WITH);
 230 
 231         if (pcmk__cmp_schemas_by_name(new_schema, original_schema) > 0) {
 232             xmlNode *up = pcmk__xe_create(NULL, __func__);
 233 
 234             rc = pcmk_ok;
 235             crm_notice("Upgrade request from %s verified", host);
 236 
 237             crm_xml_add(up, PCMK__XA_T, PCMK__VALUE_CIB);
 238             crm_xml_add(up, PCMK__XA_CIB_OP, PCMK__CIB_REQUEST_UPGRADE);
 239             crm_xml_add(up, PCMK__XA_CIB_SCHEMA_MAX, new_schema);
 240             crm_xml_add(up, PCMK__XA_CIB_DELEGATED_FROM, host);
 241             crm_xml_add(up, PCMK__XA_CIB_CLIENTID, client_id);
 242             crm_xml_add(up, PCMK__XA_CIB_CALLOPT, call_opts);
 243             crm_xml_add(up, PCMK__XA_CIB_CALLID, call_id);
 244 
 245             pcmk__cluster_send_message(NULL, pcmk_ipc_based, up);
 246 
 247             pcmk__xml_free(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             pcmk__node_status_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                      QB_XS " rc=%d peer=%s", host, pcmk_strerror(rc), rc,
 262                      (origin? origin->name : "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, pcmk_ipc_based, up)) {
 276                     crm_warn("Could not send CIB upgrade result to %s", host);
 277                 }
 278                 pcmk__xml_free(up);
 279             }
 280         }
 281         pcmk__xml_free(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         pcmk__xml_free(*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 
 345     return rc;
 346 }
 347 
 348 int
 349 cib_process_replace_svr(const char *op, int options, const char *section, xmlNode * req,
     /* [previous][next][first][last][top][bottom][index][help] */
 350                         xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib,
 351                         xmlNode ** answer)
 352 {
 353     int rc =
 354         cib_process_replace(op, options, section, req, input, existing_cib, result_cib, answer);
 355 
 356     if ((rc == pcmk_ok) && pcmk__xe_is(input, PCMK_XE_CIB)) {
 357         sync_in_progress = 0;
 358     }
 359     return rc;
 360 }
 361 
 362 /* @COMPAT: Remove when PCMK__CIB_REQUEST_ABS_DELETE is removed
 363  * (At least external client code <3.0.0 can send it)
 364  */
 365 int
 366 cib_process_delete_absolute(const char *op, int options, const char *section, xmlNode * req,
     /* [previous][next][first][last][top][bottom][index][help] */
 367                             xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib,
 368                             xmlNode ** answer)
 369 {
 370     return -EINVAL;
 371 }
 372 
 373 static xmlNode *
 374 cib_msg_copy(xmlNode *msg)
     /* [previous][next][first][last][top][bottom][index][help] */
 375 {
 376     static const char *field_list[] = {
 377         PCMK__XA_T,
 378         PCMK__XA_CIB_CLIENTID,
 379         PCMK__XA_CIB_CALLOPT,
 380         PCMK__XA_CIB_CALLID,
 381         PCMK__XA_CIB_OP,
 382         PCMK__XA_CIB_ISREPLYTO,
 383         PCMK__XA_CIB_SECTION,
 384         PCMK__XA_CIB_HOST,
 385         PCMK__XA_CIB_RC,
 386         PCMK__XA_CIB_DELEGATED_FROM,
 387         PCMK__XA_CIB_UPDATE,
 388         PCMK__XA_CIB_CLIENTNAME,
 389         PCMK__XA_CIB_USER,
 390         PCMK__XA_CIB_NOTIFY_TYPE,
 391         PCMK__XA_CIB_NOTIFY_ACTIVATE,
 392     };
 393 
 394     xmlNode *copy = pcmk__xe_create(NULL, PCMK__XE_COPY);
 395 
 396     for (int lpc = 0; lpc < PCMK__NELEM(field_list); lpc++) {
 397         const char *field = field_list[lpc];
 398         const char *value = crm_element_value(msg, field);
 399 
 400         if (value != NULL) {
 401             crm_xml_add(copy, field, value);
 402         }
 403     }
 404 
 405     return copy;
 406 }
 407 
 408 int
 409 sync_our_cib(xmlNode * request, gboolean all)
     /* [previous][next][first][last][top][bottom][index][help] */
 410 {
 411     int result = pcmk_ok;
 412     char *digest = NULL;
 413     const char *host = crm_element_value(request, PCMK__XA_SRC);
 414     const char *op = crm_element_value(request, PCMK__XA_CIB_OP);
 415     pcmk__node_status_t *peer = NULL;
 416     xmlNode *replace_request = NULL;
 417     xmlNode *wrapper = NULL;
 418 
 419     CRM_CHECK(the_cib != NULL, return -EINVAL);
 420     CRM_CHECK(all || (host != NULL), return -EINVAL);
 421 
 422     crm_debug("Syncing CIB to %s", all ? "all peers" : host);
 423 
 424     replace_request = cib_msg_copy(request);
 425 
 426     if (host != NULL) {
 427         crm_xml_add(replace_request, PCMK__XA_CIB_ISREPLYTO, host);
 428     }
 429     if (all) {
 430         pcmk__xe_remove_attr(replace_request, PCMK__XA_CIB_HOST);
 431     }
 432 
 433     crm_xml_add(replace_request, PCMK__XA_CIB_OP, PCMK__CIB_REQUEST_REPLACE);
 434 
 435     // @TODO Keep for tracing, or drop?
 436     crm_xml_add(replace_request, PCMK__XA_ORIGINAL_CIB_OP, op);
 437 
 438     pcmk__xe_set_bool_attr(replace_request, PCMK__XA_CIB_UPDATE, true);
 439 
 440     crm_xml_add(replace_request, PCMK_XA_CRM_FEATURE_SET, CRM_FEATURE_SET);
 441     digest = pcmk__digest_xml(the_cib, true);
 442     crm_xml_add(replace_request, PCMK__XA_DIGEST, digest);
 443 
 444     wrapper = pcmk__xe_create(replace_request, PCMK__XE_CIB_CALLDATA);
 445     pcmk__xml_copy(wrapper, the_cib);
 446 
 447     if (!all) {
 448         peer = pcmk__get_node(0, host, NULL, pcmk__node_search_cluster_member);
 449     }
 450     if (!pcmk__cluster_send_message(peer, pcmk_ipc_based, replace_request)) {
 451         result = -ENOTCONN;
 452     }
 453     pcmk__xml_free(replace_request);
 454     free(digest);
 455     return result;
 456 }
 457 
 458 int
 459 cib_process_commit_transaction(const char *op, int options, const char *section,
     /* [previous][next][first][last][top][bottom][index][help] */
 460                                xmlNode *req, xmlNode *input,
 461                                xmlNode *existing_cib, xmlNode **result_cib,
 462                                xmlNode **answer)
 463 {
 464     /* On success, our caller will activate *result_cib locally, trigger a
 465      * replace notification if appropriate, and sync *result_cib to all nodes.
 466      * On failure, our caller will free *result_cib.
 467      */
 468     int rc = pcmk_rc_ok;
 469     const char *client_id = crm_element_value(req, PCMK__XA_CIB_CLIENTID);
 470     const char *origin = crm_element_value(req, PCMK__XA_SRC);
 471     pcmk__client_t *client = pcmk__find_client_by_id(client_id);
 472 
 473     rc = based_commit_transaction(input, client, origin, result_cib);
 474 
 475     if (rc != pcmk_rc_ok) {
 476         char *source = based_transaction_source_str(client, origin);
 477 
 478         crm_err("Could not commit transaction for %s: %s",
 479                 source, pcmk_rc_str(rc));
 480         free(source);
 481     }
 482     return pcmk_rc2legacy(rc);
 483 }
 484 
 485 int
 486 cib_process_schemas(const char *op, int options, const char *section, xmlNode *req,
     /* [previous][next][first][last][top][bottom][index][help] */
 487                     xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib,
 488                     xmlNode **answer)
 489 {
 490     xmlNode *wrapper = NULL;
 491     xmlNode *data = NULL;
 492 
 493     const char *after_ver = NULL;
 494     GList *schemas = NULL;
 495     GList *already_included = NULL;
 496 
 497     *answer = pcmk__xe_create(NULL, PCMK__XA_SCHEMAS);
 498 
 499     wrapper = pcmk__xe_first_child(req, PCMK__XE_CIB_CALLDATA, NULL, NULL);
 500     data = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
 501     if (data == NULL) {
 502         crm_warn("No data specified in request");
 503         return -EPROTO;
 504     }
 505 
 506     after_ver = crm_element_value(data, PCMK_XA_VERSION);
 507     if (after_ver == NULL) {
 508         crm_warn("No version specified in request");
 509         return -EPROTO;
 510     }
 511 
 512     /* The client requested all schemas after the latest one we know about, which
 513      * means the client is fully up-to-date.  Return a properly formatted reply
 514      * with no schemas.
 515      */
 516     if (pcmk__str_eq(after_ver, pcmk__highest_schema_name(), pcmk__str_none)) {
 517         return pcmk_ok;
 518     }
 519 
 520     schemas = pcmk__schema_files_later_than(after_ver);
 521 
 522     for (GList *iter = schemas; iter != NULL; iter = iter->next) {
 523         pcmk__build_schema_xml_node(*answer, iter->data, &already_included);
 524     }
 525 
 526     g_list_free_full(schemas, free);
 527     g_list_free_full(already_included, free);
 528     return pcmk_ok;
 529 }

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