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

   1 /*
   2  * Copyright 2004-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 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 #include <crm/msg_xml.h>
  28 
  29 #include <crm/common/xml.h>
  30 #include <crm/common/ipc_internal.h>
  31 #include <crm/common/xml_internal.h>
  32 #include <crm/cluster/internal.h>
  33 
  34 #include <pacemaker-based.h>
  35 
  36 /* Maximum number of diffs to ignore while waiting for a resync */
  37 #define MAX_DIFF_RETRY 5
  38 
  39 bool based_is_primary = false;
  40 
  41 xmlNode *the_cib = NULL;
  42 
  43 int
  44 cib_process_shutdown_req(const char *op, int options, const char *section, xmlNode * req,
     /* [previous][next][first][last][top][bottom][index][help] */
  45                          xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib,
  46                          xmlNode ** answer)
  47 {
  48     const char *host = crm_element_value(req, F_ORIG);
  49 
  50     *answer = NULL;
  51 
  52     if (crm_element_value(req, F_CIB_ISREPLY) == NULL) {
  53         crm_info("Peer %s is requesting to shut down", host);
  54         return pcmk_ok;
  55     }
  56 
  57     if (cib_shutdown_flag == FALSE) {
  58         crm_err("Peer %s mistakenly thinks we wanted to shut down", host);
  59         return -EINVAL;
  60     }
  61 
  62     crm_info("Peer %s has acknowledged our shutdown request", host);
  63     terminate_cib(__func__, 0);
  64     return pcmk_ok;
  65 }
  66 
  67 // @COMPAT: Remove when PCMK__CIB_REQUEST_NOOP is removed
  68 int
  69 cib_process_noop(const char *op, int options, const char *section, xmlNode *req,
     /* [previous][next][first][last][top][bottom][index][help] */
  70                  xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib,
  71                  xmlNode **answer)
  72 {
  73     crm_trace("Processing \"%s\" event", op);
  74     *answer = NULL;
  75     return pcmk_ok;
  76 }
  77 
  78 int
  79 cib_process_readwrite(const char *op, int options, const char *section, xmlNode * req,
     /* [previous][next][first][last][top][bottom][index][help] */
  80                       xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib,
  81                       xmlNode ** answer)
  82 {
  83     int result = pcmk_ok;
  84 
  85     crm_trace("Processing \"%s\" event", op);
  86 
  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 = create_xml_node(NULL, "sync-me");
 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, F_TYPE, "cib");
 126     crm_xml_add(sync_me, F_CIB_OPERATION, PCMK__CIB_REQUEST_SYNC_TO_ONE);
 127     crm_xml_add(sync_me, F_CIB_DELEGATED,
 128                 stand_alone? "localhost" : crm_cluster->uname);
 129 
 130     send_cluster_message(host ? crm_get_peer(0, host) : NULL, crm_msg_cib, sync_me, FALSE);
 131     free_xml(sync_me);
 132 }
 133 
 134 int
 135 cib_process_ping(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
     /* [previous][next][first][last][top][bottom][index][help] */
 136                  xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
 137 {
 138     const char *host = crm_element_value(req, F_ORIG);
 139     const char *seq = crm_element_value(req, F_CIB_PING_ID);
 140     char *digest = calculate_xml_versioned_digest(the_cib, FALSE, TRUE, CRM_FEATURE_SET);
 141 
 142     crm_trace("Processing \"%s\" event %s from %s", op, seq, host);
 143     *answer = create_xml_node(NULL, XML_CRM_TAG_PING);
 144 
 145     crm_xml_add(*answer, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET);
 146     crm_xml_add(*answer, XML_ATTR_DIGEST, digest);
 147     crm_xml_add(*answer, F_CIB_PING_ID, seq);
 148 
 149     pcmk__if_tracing(
 150         {
 151             // Append additional detail so the receiver can log the differences
 152             add_message_xml(*answer, F_CIB_CALLDATA, the_cib);
 153         },
 154         if (the_cib != NULL) {
 155             // Always include at least the version details
 156             xmlNode *shallow = create_xml_node(NULL,
 157                                                (const char *) the_cib->name);
 158 
 159             copy_in_properties(shallow, the_cib);
 160             add_message_xml(*answer, F_CIB_CALLDATA, shallow);
 161             free_xml(shallow);
 162         }
 163     );
 164 
 165     crm_info("Reporting our current digest to %s: %s for %s.%s.%s",
 166              host, digest,
 167              crm_element_value(existing_cib, XML_ATTR_GENERATION_ADMIN),
 168              crm_element_value(existing_cib, XML_ATTR_GENERATION),
 169              crm_element_value(existing_cib, XML_ATTR_NUMUPDATES));
 170 
 171     free(digest);
 172 
 173     return pcmk_ok;
 174 }
 175 
 176 int
 177 cib_process_sync(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
     /* [previous][next][first][last][top][bottom][index][help] */
 178                  xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
 179 {
 180     return sync_our_cib(req, TRUE);
 181 }
 182 
 183 int
 184 cib_process_upgrade_server(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
     /* [previous][next][first][last][top][bottom][index][help] */
 185                            xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
 186 {
 187     int rc = pcmk_ok;
 188 
 189     *answer = NULL;
 190 
 191     if(crm_element_value(req, F_CIB_SCHEMA_MAX)) {
 192         /* The originator of an upgrade request sends it to the DC, without
 193          * F_CIB_SCHEMA_MAX. If an upgrade is needed, the DC re-broadcasts the
 194          * request with F_CIB_SCHEMA_MAX, and each node performs the upgrade
 195          * (and notifies its local clients) here.
 196          */
 197         return cib_process_upgrade(
 198             op, options, section, req, input, existing_cib, result_cib, answer);
 199 
 200     } else {
 201         int new_version = 0;
 202         int current_version = 0;
 203         xmlNode *scratch = copy_xml(existing_cib);
 204         const char *host = crm_element_value(req, F_ORIG);
 205         const char *value = crm_element_value(existing_cib, XML_ATTR_VALIDATION);
 206         const char *client_id = crm_element_value(req, F_CIB_CLIENTID);
 207         const char *call_opts = crm_element_value(req, F_CIB_CALLOPTS);
 208         const char *call_id = crm_element_value(req, F_CIB_CALLID);
 209 
 210         crm_trace("Processing \"%s\" event", op);
 211         if (value != NULL) {
 212             current_version = get_schema_version(value);
 213         }
 214 
 215         rc = update_validation(&scratch, &new_version, 0, TRUE, TRUE);
 216         if (new_version > current_version) {
 217             xmlNode *up = create_xml_node(NULL, __func__);
 218 
 219             rc = pcmk_ok;
 220             crm_notice("Upgrade request from %s verified", host);
 221 
 222             crm_xml_add(up, F_TYPE, "cib");
 223             crm_xml_add(up, F_CIB_OPERATION, PCMK__CIB_REQUEST_UPGRADE);
 224             crm_xml_add(up, F_CIB_SCHEMA_MAX, get_schema_name(new_version));
 225             crm_xml_add(up, F_CIB_DELEGATED, host);
 226             crm_xml_add(up, F_CIB_CLIENTID, client_id);
 227             crm_xml_add(up, F_CIB_CALLOPTS, call_opts);
 228             crm_xml_add(up, F_CIB_CALLID, call_id);
 229 
 230             if (cib_legacy_mode() && based_is_primary) {
 231                 rc = cib_process_upgrade(
 232                     op, options, section, up, input, existing_cib, result_cib, answer);
 233 
 234             } else {
 235                 send_cluster_message(NULL, crm_msg_cib, up, FALSE);
 236             }
 237 
 238             free_xml(up);
 239 
 240         } else if(rc == pcmk_ok) {
 241             rc = -pcmk_err_schema_unchanged;
 242         }
 243 
 244         if (rc != pcmk_ok) {
 245             // Notify originating peer so it can notify its local clients
 246             crm_node_t *origin = pcmk__search_cluster_node_cache(0, host, NULL);
 247 
 248             crm_info("Rejecting upgrade request from %s: %s "
 249                      CRM_XS " rc=%d peer=%s", host, pcmk_strerror(rc), rc,
 250                      (origin? origin->uname : "lost"));
 251 
 252             if (origin) {
 253                 xmlNode *up = create_xml_node(NULL, __func__);
 254 
 255                 crm_xml_add(up, F_TYPE, "cib");
 256                 crm_xml_add(up, F_CIB_OPERATION, PCMK__CIB_REQUEST_UPGRADE);
 257                 crm_xml_add(up, F_CIB_DELEGATED, host);
 258                 crm_xml_add(up, F_CIB_ISREPLY, host);
 259                 crm_xml_add(up, F_CIB_CLIENTID, client_id);
 260                 crm_xml_add(up, F_CIB_CALLOPTS, call_opts);
 261                 crm_xml_add(up, F_CIB_CALLID, call_id);
 262                 crm_xml_add_int(up, F_CIB_UPGRADE_RC, rc);
 263                 if (send_cluster_message(origin, crm_msg_cib, up, TRUE)
 264                     == FALSE) {
 265                     crm_warn("Could not send CIB upgrade result to %s", host);
 266                 }
 267                 free_xml(up);
 268             }
 269         }
 270         free_xml(scratch);
 271     }
 272     return rc;
 273 }
 274 
 275 int
 276 cib_process_sync_one(const char *op, int options, const char *section, xmlNode * req,
     /* [previous][next][first][last][top][bottom][index][help] */
 277                      xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib,
 278                      xmlNode ** answer)
 279 {
 280     return sync_our_cib(req, FALSE);
 281 }
 282 
 283 int
 284 cib_server_process_diff(const char *op, int options, const char *section, xmlNode * req,
     /* [previous][next][first][last][top][bottom][index][help] */
 285                         xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib,
 286                         xmlNode ** answer)
 287 {
 288     int rc = pcmk_ok;
 289 
 290     if (sync_in_progress > MAX_DIFF_RETRY) {
 291         /* Don't ignore diffs forever; the last request may have been lost.
 292          * If the diff fails, we'll ask for another full resync.
 293          */
 294         sync_in_progress = 0;
 295     }
 296 
 297     // The primary instance should never ignore a diff
 298     if (sync_in_progress && !based_is_primary) {
 299         int diff_add_updates = 0;
 300         int diff_add_epoch = 0;
 301         int diff_add_admin_epoch = 0;
 302 
 303         int diff_del_updates = 0;
 304         int diff_del_epoch = 0;
 305         int diff_del_admin_epoch = 0;
 306 
 307         cib_diff_version_details(input,
 308                                  &diff_add_admin_epoch, &diff_add_epoch, &diff_add_updates,
 309                                  &diff_del_admin_epoch, &diff_del_epoch, &diff_del_updates);
 310 
 311         sync_in_progress++;
 312         crm_notice("Not applying diff %d.%d.%d -> %d.%d.%d (sync in progress)",
 313                    diff_del_admin_epoch, diff_del_epoch, diff_del_updates,
 314                    diff_add_admin_epoch, diff_add_epoch, diff_add_updates);
 315         return -pcmk_err_diff_resync;
 316     }
 317 
 318     rc = cib_process_diff(op, options, section, req, input, existing_cib, result_cib, answer);
 319     crm_trace("result: %s (%d), %s", pcmk_strerror(rc), rc,
 320               (based_is_primary? "primary": "secondary"));
 321 
 322     if ((rc == -pcmk_err_diff_resync) && !based_is_primary) {
 323         free_xml(*result_cib);
 324         *result_cib = NULL;
 325         send_sync_request(NULL);
 326 
 327     } else if (rc == -pcmk_err_diff_resync) {
 328         rc = -pcmk_err_diff_failed;
 329         if (options & cib_force_diff) {
 330             crm_warn("Not requesting full refresh in R/W mode");
 331         }
 332 
 333     } else if ((rc != pcmk_ok) && !based_is_primary && cib_legacy_mode()) {
 334         crm_warn("Requesting full CIB refresh because update failed: %s"
 335                  CRM_XS " rc=%d", pcmk_strerror(rc), rc);
 336 
 337         pcmk__log_xml_patchset(LOG_INFO, input);
 338         free_xml(*result_cib);
 339         *result_cib = NULL;
 340         send_sync_request(NULL);
 341     }
 342 
 343     return rc;
 344 }
 345 
 346 int
 347 cib_process_replace_svr(const char *op, int options, const char *section, xmlNode * req,
     /* [previous][next][first][last][top][bottom][index][help] */
 348                         xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib,
 349                         xmlNode ** answer)
 350 {
 351     int rc =
 352         cib_process_replace(op, options, section, req, input, existing_cib, result_cib, answer);
 353 
 354     if ((rc == pcmk_ok) && pcmk__xe_is(input, XML_TAG_CIB)) {
 355         sync_in_progress = 0;
 356     }
 357     return rc;
 358 }
 359 
 360 // @COMPAT: Remove when PCMK__CIB_REQUEST_ABS_DELETE is removed
 361 int
 362 cib_process_delete_absolute(const char *op, int options, const char *section, xmlNode * req,
     /* [previous][next][first][last][top][bottom][index][help] */
 363                             xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib,
 364                             xmlNode ** answer)
 365 {
 366     return -EINVAL;
 367 }
 368 
 369 static xmlNode *
 370 cib_msg_copy(xmlNode *msg)
     /* [previous][next][first][last][top][bottom][index][help] */
 371 {
 372     static const char *field_list[] = {
 373         F_XML_TAGNAME,
 374         F_TYPE,
 375         F_CIB_CLIENTID,
 376         F_CIB_CALLOPTS,
 377         F_CIB_CALLID,
 378         F_CIB_OPERATION,
 379         F_CIB_ISREPLY,
 380         F_CIB_SECTION,
 381         F_CIB_HOST,
 382         F_CIB_RC,
 383         F_CIB_DELEGATED,
 384         F_CIB_OBJID,
 385         F_CIB_OBJTYPE,
 386         F_CIB_EXISTING,
 387         F_CIB_SEENCOUNT,
 388         F_CIB_TIMEOUT,
 389         F_CIB_GLOBAL_UPDATE,
 390         F_CIB_CLIENTNAME,
 391         F_CIB_USER,
 392         F_CIB_NOTIFY_TYPE,
 393         F_CIB_NOTIFY_ACTIVATE
 394     };
 395 
 396     xmlNode *copy = create_xml_node(NULL, "copy");
 397 
 398     CRM_ASSERT(copy != NULL);
 399 
 400     for (int lpc = 0; lpc < PCMK__NELEM(field_list); lpc++) {
 401         const char *field = field_list[lpc];
 402         const char *value = crm_element_value(msg, field);
 403 
 404         if (value != NULL) {
 405             crm_xml_add(copy, field, value);
 406         }
 407     }
 408 
 409     return copy;
 410 }
 411 
 412 int
 413 sync_our_cib(xmlNode * request, gboolean all)
     /* [previous][next][first][last][top][bottom][index][help] */
 414 {
 415     int result = pcmk_ok;
 416     char *digest = NULL;
 417     const char *host = crm_element_value(request, F_ORIG);
 418     const char *op = crm_element_value(request, F_CIB_OPERATION);
 419 
 420     xmlNode *replace_request = NULL;
 421 
 422     CRM_CHECK(the_cib != NULL, return -EINVAL);
 423     CRM_CHECK(all || (host != NULL), return -EINVAL);
 424 
 425     crm_debug("Syncing CIB to %s", all ? "all peers" : host);
 426 
 427     replace_request = cib_msg_copy(request);
 428 
 429     if (host != NULL) {
 430         crm_xml_add(replace_request, F_CIB_ISREPLY, host);
 431     }
 432     if (all) {
 433         xml_remove_prop(replace_request, F_CIB_HOST);
 434     }
 435 
 436     crm_xml_add(replace_request, F_CIB_OPERATION, PCMK__CIB_REQUEST_REPLACE);
 437     crm_xml_add(replace_request, "original_" F_CIB_OPERATION, op);
 438     pcmk__xe_set_bool_attr(replace_request, F_CIB_GLOBAL_UPDATE, true);
 439 
 440     crm_xml_add(replace_request, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET);
 441     digest = calculate_xml_versioned_digest(the_cib, FALSE, TRUE, CRM_FEATURE_SET);
 442     crm_xml_add(replace_request, XML_ATTR_DIGEST, digest);
 443 
 444     add_message_xml(replace_request, F_CIB_CALLDATA, the_cib);
 445 
 446     if (send_cluster_message
 447         (all ? NULL : crm_get_peer(0, host), crm_msg_cib, replace_request, FALSE) == FALSE) {
 448         result = -ENOTCONN;
 449     }
 450     free_xml(replace_request);
 451     free(digest);
 452     return result;
 453 }
 454 
 455 int
 456 cib_process_commit_transaction(const char *op, int options, const char *section,
     /* [previous][next][first][last][top][bottom][index][help] */
 457                                xmlNode *req, xmlNode *input,
 458                                xmlNode *existing_cib, xmlNode **result_cib,
 459                                xmlNode **answer)
 460 {
 461     /* On success, our caller will activate *result_cib locally, trigger a
 462      * replace notification if appropriate, and sync *result_cib to all nodes.
 463      * On failure, our caller will free *result_cib.
 464      */
 465     int rc = pcmk_rc_ok;
 466     const char *client_id = crm_element_value(req, F_CIB_CLIENTID);
 467     const char *origin = crm_element_value(req, F_ORIG);
 468     pcmk__client_t *client = pcmk__find_client_by_id(client_id);
 469 
 470     rc = based_commit_transaction(input, client, origin, result_cib);
 471 
 472     if (rc != pcmk_rc_ok) {
 473         char *source = based_transaction_source_str(client, origin);
 474 
 475         crm_err("Could not commit transaction for %s: %s",
 476                 source, pcmk_rc_str(rc));
 477         free(source);
 478     }
 479     return pcmk_rc2legacy(rc);
 480 }

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