root/daemons/attrd/attrd_corosync.c

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

DEFINITIONS

This source file includes following definitions.
  1. attrd_peer_message
  2. attrd_cpg_dispatch
  3. attrd_cpg_destroy
  4. broadcast_local_value
  5. cache_remote_node
  6. attrd_lookup_or_create_value
  7. attrd_peer_change_cb
  8. record_peer_nodeid
  9. update_attr_on_host
  10. attrd_peer_update_one
  11. broadcast_unseen_local_values
  12. attrd_cluster_connect
  13. attrd_peer_clear_failure
  14. attrd_peer_sync_response
  15. attrd_peer_remove
  16. attrd_peer_sync
  17. copy_attrs
  18. attrd_peer_update

   1 /*
   2  * Copyright 2013-2022 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 <errno.h>
  13 #include <stdbool.h>
  14 #include <stdint.h>
  15 #include <stdlib.h>
  16 
  17 #include <crm/cluster.h>
  18 #include <crm/cluster/internal.h>
  19 #include <crm/common/logging.h>
  20 #include <crm/common/results.h>
  21 #include <crm/common/strings_internal.h>
  22 #include <crm/msg_xml.h>
  23 
  24 #include "pacemaker-attrd.h"
  25 
  26 extern crm_exit_t attrd_exit_status;
  27 
  28 static void
  29 attrd_peer_message(crm_node_t *peer, xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
  30 {
  31     const char *election_op = crm_element_value(xml, F_CRM_TASK);
  32 
  33     if (election_op) {
  34         attrd_handle_election_op(peer, xml);
  35         return;
  36     }
  37 
  38     if (attrd_shutting_down()) {
  39         /* If we're shutting down, we want to continue responding to election
  40          * ops as long as we're a cluster member (because our vote may be
  41          * needed). Ignore all other messages.
  42          */
  43         return;
  44 
  45     } else {
  46         pcmk__request_t request = {
  47             .ipc_client     = NULL,
  48             .ipc_id         = 0,
  49             .ipc_flags      = 0,
  50             .peer           = peer->uname,
  51             .xml            = xml,
  52             .call_options   = 0,
  53             .result         = PCMK__UNKNOWN_RESULT,
  54         };
  55 
  56         request.op = crm_element_value_copy(request.xml, PCMK__XA_TASK);
  57         CRM_CHECK(request.op != NULL, return);
  58 
  59         attrd_handle_request(&request);
  60         pcmk__reset_request(&request);
  61     }
  62 }
  63 
  64 static void
  65 attrd_cpg_dispatch(cpg_handle_t handle,
     /* [previous][next][first][last][top][bottom][index][help] */
  66                  const struct cpg_name *groupName,
  67                  uint32_t nodeid, uint32_t pid, void *msg, size_t msg_len)
  68 {
  69     uint32_t kind = 0;
  70     xmlNode *xml = NULL;
  71     const char *from = NULL;
  72     char *data = pcmk_message_common_cs(handle, nodeid, pid, msg, &kind, &from);
  73 
  74     if(data == NULL) {
  75         return;
  76     }
  77 
  78     if (kind == crm_class_cluster) {
  79         xml = string2xml(data);
  80     }
  81 
  82     if (xml == NULL) {
  83         crm_err("Bad message of class %d received from %s[%u]: '%.120s'", kind, from, nodeid, data);
  84     } else {
  85         crm_node_t *peer = crm_get_peer(nodeid, from);
  86 
  87         attrd_peer_message(peer, xml);
  88     }
  89 
  90     free_xml(xml);
  91     free(data);
  92 }
  93 
  94 static void
  95 attrd_cpg_destroy(gpointer unused)
     /* [previous][next][first][last][top][bottom][index][help] */
  96 {
  97     if (attrd_shutting_down()) {
  98         crm_info("Corosync disconnection complete");
  99 
 100     } else {
 101         crm_crit("Lost connection to cluster layer, shutting down");
 102         attrd_exit_status = CRM_EX_DISCONNECT;
 103         attrd_shutdown(0);
 104     }
 105 }
 106 
 107 /*!
 108  * \internal
 109  * \brief Override an attribute sync with a local value
 110  *
 111  * Broadcast the local node's value for an attribute that's different from the
 112  * value provided in a peer's attribute synchronization response. This ensures a
 113  * node's values for itself take precedence and all peers are kept in sync.
 114  *
 115  * \param[in] a          Attribute entry to override
 116  *
 117  * \return Local instance of attribute value
 118  */
 119 static attribute_value_t *
 120 broadcast_local_value(const attribute_t *a)
     /* [previous][next][first][last][top][bottom][index][help] */
 121 {
 122     attribute_value_t *v = g_hash_table_lookup(a->values, attrd_cluster->uname);
 123     xmlNode *sync = create_xml_node(NULL, __func__);
 124 
 125     crm_xml_add(sync, PCMK__XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE);
 126     attrd_add_value_xml(sync, a, v, false);
 127     attrd_send_message(NULL, sync);
 128     free_xml(sync);
 129     return v;
 130 }
 131 
 132 /*!
 133  * \internal
 134  * \brief Ensure a Pacemaker Remote node is in the correct peer cache
 135  *
 136  * \param[in] node_name  Name of Pacemaker Remote node to check
 137  */
 138 static void
 139 cache_remote_node(const char *node_name)
     /* [previous][next][first][last][top][bottom][index][help] */
 140 {
 141     /* If we previously assumed this node was an unseen cluster node,
 142      * remove its entry from the cluster peer cache.
 143      */
 144     crm_node_t *dup = pcmk__search_cluster_node_cache(0, node_name);
 145 
 146     if (dup && (dup->uuid == NULL)) {
 147         reap_crm_member(0, node_name);
 148     }
 149 
 150     // Ensure node is in the remote peer cache
 151     CRM_ASSERT(crm_remote_peer_get(node_name) != NULL);
 152 }
 153 
 154 #define state_text(state) pcmk__s((state), "in unknown state")
 155 
 156 /*!
 157  * \internal
 158  * \brief Return host's hash table entry (creating one if needed)
 159  *
 160  * \param[in,out] values Hash table of values
 161  * \param[in]     host   Name of peer to look up
 162  * \param[in]     xml    XML describing the attribute
 163  *
 164  * \return Pointer to new or existing hash table entry
 165  */
 166 static attribute_value_t *
 167 attrd_lookup_or_create_value(GHashTable *values, const char *host,
     /* [previous][next][first][last][top][bottom][index][help] */
 168                              const xmlNode *xml)
 169 {
 170     attribute_value_t *v = g_hash_table_lookup(values, host);
 171     int is_remote = 0;
 172 
 173     crm_element_value_int(xml, PCMK__XA_ATTR_IS_REMOTE, &is_remote);
 174     if (is_remote) {
 175         cache_remote_node(host);
 176     }
 177 
 178     if (v == NULL) {
 179         v = calloc(1, sizeof(attribute_value_t));
 180         CRM_ASSERT(v != NULL);
 181 
 182         pcmk__str_update(&v->nodename, host);
 183         v->is_remote = is_remote;
 184         g_hash_table_replace(values, v->nodename, v);
 185     }
 186     return(v);
 187 }
 188 
 189 static void
 190 attrd_peer_change_cb(enum crm_status_type kind, crm_node_t *peer, const void *data)
     /* [previous][next][first][last][top][bottom][index][help] */
 191 {
 192     bool gone = false;
 193     bool is_remote = pcmk_is_set(peer->flags, crm_remote_node);
 194 
 195     switch (kind) {
 196         case crm_status_uname:
 197             crm_debug("%s node %s is now %s",
 198                       (is_remote? "Remote" : "Cluster"),
 199                       peer->uname, state_text(peer->state));
 200             break;
 201 
 202         case crm_status_processes:
 203             if (!pcmk_is_set(peer->processes, crm_get_cluster_proc())) {
 204                 gone = true;
 205             }
 206             crm_debug("Node %s is %s a peer",
 207                       peer->uname, (gone? "no longer" : "now"));
 208             break;
 209 
 210         case crm_status_nstate:
 211             crm_debug("%s node %s is now %s (was %s)",
 212                       (is_remote? "Remote" : "Cluster"),
 213                       peer->uname, state_text(peer->state), state_text(data));
 214             if (pcmk__str_eq(peer->state, CRM_NODE_MEMBER, pcmk__str_casei)) {
 215                 /* If we're the writer, send new peers a list of all attributes
 216                  * (unless it's a remote node, which doesn't run its own attrd)
 217                  */
 218                 if (attrd_election_won()
 219                     && !pcmk_is_set(peer->flags, crm_remote_node)) {
 220                     attrd_peer_sync(peer, NULL);
 221                 }
 222             } else {
 223                 // Remove all attribute values associated with lost nodes
 224                 attrd_peer_remove(peer->uname, false, "loss");
 225                 gone = true;
 226             }
 227             break;
 228     }
 229 
 230     // Remove votes from cluster nodes that leave, in case election in progress
 231     if (gone && !is_remote) {
 232         attrd_remove_voter(peer);
 233 
 234     // Ensure remote nodes that come up are in the remote node cache
 235     } else if (!gone && is_remote) {
 236         cache_remote_node(peer->uname);
 237     }
 238 }
 239 
 240 static void
 241 record_peer_nodeid(attribute_value_t *v, const char *host)
     /* [previous][next][first][last][top][bottom][index][help] */
 242 {
 243     crm_node_t *known_peer = crm_get_peer(v->nodeid, host);
 244 
 245     crm_trace("Learned %s has node id %s", known_peer->uname, known_peer->uuid);
 246     if (attrd_election_won()) {
 247         attrd_write_attributes(false, false);
 248     }
 249 }
 250 
 251 static void
 252 update_attr_on_host(attribute_t *a, const crm_node_t *peer, const xmlNode *xml,
     /* [previous][next][first][last][top][bottom][index][help] */
 253                     const char *attr, const char *value, const char *host,
 254                     bool filter, int is_force_write)
 255 {
 256     attribute_value_t *v = NULL;
 257 
 258     v = attrd_lookup_or_create_value(a->values, host, xml);
 259 
 260     if (filter && !pcmk__str_eq(v->current, value, pcmk__str_casei)
 261         && pcmk__str_eq(host, attrd_cluster->uname, pcmk__str_casei)) {
 262 
 263         crm_notice("%s[%s]: local value '%s' takes priority over '%s' from %s",
 264                    attr, host, v->current, value, peer->uname);
 265         v = broadcast_local_value(a);
 266 
 267     } else if (!pcmk__str_eq(v->current, value, pcmk__str_casei)) {
 268         crm_notice("Setting %s[%s]: %s -> %s "
 269                    CRM_XS " from %s with %s write delay",
 270                    attr, host, pcmk__s(v->current, "(unset)"),
 271                    pcmk__s(value, "(unset)"), peer->uname,
 272                    (a->timeout_ms == 0)? "no" : pcmk__readable_interval(a->timeout_ms));
 273         pcmk__str_update(&v->current, value);
 274         a->changed = true;
 275 
 276         if (pcmk__str_eq(host, attrd_cluster->uname, pcmk__str_casei)
 277             && pcmk__str_eq(attr, XML_CIB_ATTR_SHUTDOWN, pcmk__str_none)) {
 278 
 279             if (!pcmk__str_eq(value, "0", pcmk__str_null_matches)) {
 280                 attrd_set_requesting_shutdown();
 281 
 282             } else {
 283                 attrd_clear_requesting_shutdown();
 284             }
 285         }
 286 
 287         // Write out new value or start dampening timer
 288         if (a->timeout_ms && a->timer) {
 289             crm_trace("Delayed write out (%dms) for %s", a->timeout_ms, attr);
 290             mainloop_timer_start(a->timer);
 291         } else {
 292             attrd_write_or_elect_attribute(a);
 293         }
 294 
 295     } else {
 296         if (is_force_write == 1 && a->timeout_ms && a->timer) {
 297             /* Save forced writing and set change flag. */
 298             /* The actual attribute is written by Writer after election. */
 299             crm_trace("Unchanged %s[%s] from %s is %s(Set the forced write flag)",
 300                       attr, host, peer->uname, value);
 301             a->force_write = TRUE;
 302         } else {
 303             crm_trace("Unchanged %s[%s] from %s is %s", attr, host, peer->uname, value);
 304         }
 305     }
 306 
 307     /* Set the seen flag for attribute processing held only in the own node. */
 308     v->seen = TRUE;
 309 
 310     /* If this is a cluster node whose node ID we are learning, remember it */
 311     if ((v->nodeid == 0) && (v->is_remote == FALSE)
 312         && (crm_element_value_int(xml, PCMK__XA_ATTR_NODE_ID,
 313                                   (int*)&v->nodeid) == 0) && (v->nodeid > 0)) {
 314         record_peer_nodeid(v, host);
 315     }
 316 }
 317 
 318 static void
 319 attrd_peer_update_one(const crm_node_t *peer, xmlNode *xml, bool filter)
     /* [previous][next][first][last][top][bottom][index][help] */
 320 {
 321     attribute_t *a = NULL;
 322     const char *attr = crm_element_value(xml, PCMK__XA_ATTR_NAME);
 323     const char *value = crm_element_value(xml, PCMK__XA_ATTR_VALUE);
 324     const char *host = crm_element_value(xml, PCMK__XA_ATTR_NODE_NAME);
 325     int is_force_write = 0;
 326 
 327     if (attr == NULL) {
 328         crm_warn("Could not update attribute: peer did not specify name");
 329         return;
 330     }
 331 
 332     crm_element_value_int(xml, PCMK__XA_ATTR_FORCE, &is_force_write);
 333 
 334     a = attrd_populate_attribute(xml, attr);
 335     if (a == NULL) {
 336         return;
 337     }
 338 
 339     if (host == NULL) {
 340         // If no host was specified, update all hosts
 341         GHashTableIter vIter;
 342 
 343         crm_debug("Setting %s for all hosts to %s", attr, value);
 344         xml_remove_prop(xml, PCMK__XA_ATTR_NODE_ID);
 345         g_hash_table_iter_init(&vIter, a->values);
 346 
 347         while (g_hash_table_iter_next(&vIter, (gpointer *) & host, NULL)) {
 348             update_attr_on_host(a, peer, xml, attr, value, host, filter, is_force_write);
 349         }
 350 
 351     } else {
 352         // Update attribute value for the given host
 353         update_attr_on_host(a, peer, xml, attr, value, host, filter, is_force_write);
 354     }
 355 
 356     /* If this is a message from some attrd instance broadcasting its protocol
 357      * version, check to see if it's a new minimum version.
 358      */
 359     if (pcmk__str_eq(attr, CRM_ATTR_PROTOCOL, pcmk__str_none)) {
 360         attrd_update_minimum_protocol_ver(value);
 361     }
 362 }
 363 
 364 static void
 365 broadcast_unseen_local_values(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 366 {
 367     GHashTableIter aIter;
 368     GHashTableIter vIter;
 369     attribute_t *a = NULL;
 370     attribute_value_t *v = NULL;
 371     xmlNode *sync = NULL;
 372 
 373     g_hash_table_iter_init(&aIter, attributes);
 374     while (g_hash_table_iter_next(&aIter, NULL, (gpointer *) & a)) {
 375         g_hash_table_iter_init(&vIter, a->values);
 376         while (g_hash_table_iter_next(&vIter, NULL, (gpointer *) & v)) {
 377             if (!(v->seen) && pcmk__str_eq(v->nodename, attrd_cluster->uname,
 378                                            pcmk__str_casei)) {
 379                 if (sync == NULL) {
 380                     sync = create_xml_node(NULL, __func__);
 381                     crm_xml_add(sync, PCMK__XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE);
 382                 }
 383                 attrd_add_value_xml(sync, a, v, a->timeout_ms && a->timer);
 384             }
 385         }
 386     }
 387 
 388     if (sync != NULL) {
 389         crm_debug("Broadcasting local-only values");
 390         attrd_send_message(NULL, sync);
 391         free_xml(sync);
 392     }
 393 }
 394 
 395 int
 396 attrd_cluster_connect(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 397 {
 398     attrd_cluster = calloc(1, sizeof(crm_cluster_t));
 399 
 400     attrd_cluster->destroy = attrd_cpg_destroy;
 401     attrd_cluster->cpg.cpg_deliver_fn = attrd_cpg_dispatch;
 402     attrd_cluster->cpg.cpg_confchg_fn = pcmk_cpg_membership;
 403 
 404     crm_set_status_callback(&attrd_peer_change_cb);
 405 
 406     if (crm_cluster_connect(attrd_cluster) == FALSE) {
 407         crm_err("Cluster connection failed");
 408         return -ENOTCONN;
 409     }
 410     return pcmk_ok;
 411 }
 412 
 413 void
 414 attrd_peer_clear_failure(pcmk__request_t *request)
     /* [previous][next][first][last][top][bottom][index][help] */
 415 {
 416     xmlNode *xml = request->xml;
 417     const char *rsc = crm_element_value(xml, PCMK__XA_ATTR_RESOURCE);
 418     const char *host = crm_element_value(xml, PCMK__XA_ATTR_NODE_NAME);
 419     const char *op = crm_element_value(xml, PCMK__XA_ATTR_OPERATION);
 420     const char *interval_spec = crm_element_value(xml, PCMK__XA_ATTR_INTERVAL);
 421     guint interval_ms = crm_parse_interval_spec(interval_spec);
 422     char *attr = NULL;
 423     GHashTableIter iter;
 424     regex_t regex;
 425 
 426     crm_node_t *peer = crm_get_peer(0, request->peer);
 427 
 428     if (attrd_failure_regex(&regex, rsc, op, interval_ms) != pcmk_ok) {
 429         crm_info("Ignoring invalid request to clear failures for %s",
 430                  pcmk__s(rsc, "all resources"));
 431         return;
 432     }
 433 
 434     crm_xml_add(xml, PCMK__XA_TASK, PCMK__ATTRD_CMD_UPDATE);
 435 
 436     /* Make sure value is not set, so we delete */
 437     if (crm_element_value(xml, PCMK__XA_ATTR_VALUE)) {
 438         crm_xml_replace(xml, PCMK__XA_ATTR_VALUE, NULL);
 439     }
 440 
 441     g_hash_table_iter_init(&iter, attributes);
 442     while (g_hash_table_iter_next(&iter, (gpointer *) &attr, NULL)) {
 443         if (regexec(&regex, attr, 0, NULL, 0) == 0) {
 444             crm_trace("Matched %s when clearing %s",
 445                       attr, pcmk__s(rsc, "all resources"));
 446             crm_xml_add(xml, PCMK__XA_ATTR_NAME, attr);
 447             attrd_peer_update(peer, xml, host, false);
 448         }
 449     }
 450     regfree(&regex);
 451 }
 452 
 453 /*!
 454  * \internal
 455  * \brief Load attributes from a peer sync response
 456  *
 457  * \param[in]     peer      Peer that sent clear request
 458  * \param[in]     peer_won  Whether peer is the attribute writer
 459  * \param[in,out] xml       Request XML
 460  */
 461 void
 462 attrd_peer_sync_response(const crm_node_t *peer, bool peer_won, xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 463 {
 464     crm_info("Processing " PCMK__ATTRD_CMD_SYNC_RESPONSE " from %s",
 465              peer->uname);
 466 
 467     if (peer_won) {
 468         /* Initialize the "seen" flag for all attributes to cleared, so we can
 469          * detect attributes that local node has but the writer doesn't.
 470          */
 471         attrd_clear_value_seen();
 472     }
 473 
 474     // Process each attribute update in the sync response
 475     for (xmlNode *child = pcmk__xml_first_child(xml); child != NULL;
 476          child = pcmk__xml_next(child)) {
 477         attrd_peer_update(peer, child,
 478                           crm_element_value(child, PCMK__XA_ATTR_NODE_NAME),
 479                           true);
 480     }
 481 
 482     if (peer_won) {
 483         /* If any attributes are still not marked as seen, the writer doesn't
 484          * know about them, so send all peers an update with them.
 485          */
 486         broadcast_unseen_local_values();
 487     }
 488 }
 489 
 490 /*!
 491  * \internal
 492  * \brief Remove all attributes and optionally peer cache entries for a node
 493  *
 494  * \param[in] host     Name of node to purge
 495  * \param[in] uncache  If true, remove node from peer caches
 496  * \param[in] source   Who requested removal (only used for logging)
 497  */
 498 void
 499 attrd_peer_remove(const char *host, bool uncache, const char *source)
     /* [previous][next][first][last][top][bottom][index][help] */
 500 {
 501     attribute_t *a = NULL;
 502     GHashTableIter aIter;
 503 
 504     CRM_CHECK(host != NULL, return);
 505     crm_notice("Removing all %s attributes for peer %s", host, source);
 506 
 507     g_hash_table_iter_init(&aIter, attributes);
 508     while (g_hash_table_iter_next(&aIter, NULL, (gpointer *) & a)) {
 509         if(g_hash_table_remove(a->values, host)) {
 510             crm_debug("Removed %s[%s] for peer %s", a->id, host, source);
 511         }
 512     }
 513 
 514     if (uncache) {
 515         crm_remote_peer_cache_remove(host);
 516         reap_crm_member(0, host);
 517     }
 518 }
 519 
 520 void
 521 attrd_peer_sync(crm_node_t *peer, xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 522 {
 523     GHashTableIter aIter;
 524     GHashTableIter vIter;
 525 
 526     attribute_t *a = NULL;
 527     attribute_value_t *v = NULL;
 528     xmlNode *sync = create_xml_node(NULL, __func__);
 529 
 530     crm_xml_add(sync, PCMK__XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE);
 531 
 532     g_hash_table_iter_init(&aIter, attributes);
 533     while (g_hash_table_iter_next(&aIter, NULL, (gpointer *) & a)) {
 534         g_hash_table_iter_init(&vIter, a->values);
 535         while (g_hash_table_iter_next(&vIter, NULL, (gpointer *) & v)) {
 536             crm_debug("Syncing %s[%s] = %s to %s", a->id, v->nodename, v->current, peer?peer->uname:"everyone");
 537             attrd_add_value_xml(sync, a, v, false);
 538         }
 539     }
 540 
 541     crm_debug("Syncing values to %s", peer?peer->uname:"everyone");
 542     attrd_send_message(peer, sync);
 543     free_xml(sync);
 544 }
 545 
 546 static void
 547 copy_attrs(xmlNode *src, xmlNode *dest)
     /* [previous][next][first][last][top][bottom][index][help] */
 548 {
 549     /* Copy attributes from the wrapper parent node into the child node.
 550      * We can't just use copy_in_properties because we want to skip any
 551      * attributes that are already set on the child.  For instance, if
 552      * we were told to use a specific node, there will already be a node
 553      * attribute on the child.  Copying the parent's node attribute over
 554      * could result in the wrong value.
 555      */
 556     for (xmlAttrPtr a = pcmk__xe_first_attr(src); a != NULL; a = a->next) {
 557         const char *p_name = (const char *) a->name;
 558         const char *p_value = ((a == NULL) || (a->children == NULL)) ? NULL :
 559                               (const char *) a->children->content;
 560 
 561         if (crm_element_value(dest, p_name) == NULL) {
 562             crm_xml_add(dest, p_name, p_value);
 563         }
 564     }
 565 }
 566 
 567 void
 568 attrd_peer_update(const crm_node_t *peer, xmlNode *xml, const char *host,
     /* [previous][next][first][last][top][bottom][index][help] */
 569                   bool filter)
 570 {
 571     if (xml_has_children(xml)) {
 572         for (xmlNode *child = first_named_child(xml, XML_ATTR_OP); child != NULL;
 573              child = crm_next_same_xml(child)) {
 574             copy_attrs(xml, child);
 575             attrd_peer_update_one(peer, child, filter);
 576         }
 577 
 578     } else {
 579         attrd_peer_update_one(peer, xml, filter);
 580     }
 581 }

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