root/lib/common/ipc_attrd.c

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

DEFINITIONS

This source file includes following definitions.
  1. set_pairs_data
  2. reply_expected
  3. dispatch
  4. pcmk__attrd_api_methods
  5. create_attrd_op
  6. create_api
  7. destroy_api
  8. connect_and_send_attrd_request
  9. send_attrd_request
  10. pcmk__attrd_api_clear_failures
  11. pcmk__attrd_api_delete
  12. pcmk__attrd_api_purge
  13. pcmk__attrd_api_query
  14. pcmk__attrd_api_refresh
  15. add_op_attr
  16. populate_update_op
  17. pcmk__attrd_api_update
  18. pcmk__attrd_api_update_list

   1 /*
   2  * Copyright 2011-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 Lesser General Public License
   7  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #ifndef _GNU_SOURCE
  11 #  define _GNU_SOURCE
  12 #endif
  13 
  14 #include <crm_internal.h>
  15 
  16 #include <stdio.h>
  17 
  18 #include <crm/crm.h>
  19 #include <crm/common/ipc.h>
  20 #include <crm/common/ipc_attrd_internal.h>
  21 #include <crm/common/attrd_internal.h>
  22 #include <crm/msg_xml.h>
  23 #include "crmcommon_private.h"
  24 
  25 static void
  26 set_pairs_data(pcmk__attrd_api_reply_t *data, xmlNode *msg_data)
     /* [previous][next][first][last][top][bottom][index][help] */
  27 {
  28     const char *name = NULL;
  29     pcmk__attrd_query_pair_t *pair;
  30 
  31     name = crm_element_value(msg_data, PCMK__XA_ATTR_NAME);
  32 
  33     for (xmlNode *node = first_named_child(msg_data, XML_CIB_TAG_NODE);
  34          node != NULL; node = crm_next_same_xml(node)) {
  35         pair = calloc(1, sizeof(pcmk__attrd_query_pair_t));
  36 
  37         CRM_ASSERT(pair != NULL);
  38 
  39         pair->node = crm_element_value(node, PCMK__XA_ATTR_NODE_NAME);
  40         pair->name = name;
  41         pair->value = crm_element_value(node, PCMK__XA_ATTR_VALUE);
  42         data->data.pairs = g_list_prepend(data->data.pairs, pair);
  43     }
  44 }
  45 
  46 static bool
  47 reply_expected(pcmk_ipc_api_t *api, xmlNode *request)
     /* [previous][next][first][last][top][bottom][index][help] */
  48 {
  49     const char *command = crm_element_value(request, PCMK__XA_TASK);
  50 
  51     return pcmk__str_any_of(command,
  52                             PCMK__ATTRD_CMD_CLEAR_FAILURE,
  53                             PCMK__ATTRD_CMD_QUERY,
  54                             PCMK__ATTRD_CMD_REFRESH,
  55                             PCMK__ATTRD_CMD_UPDATE,
  56                             PCMK__ATTRD_CMD_UPDATE_BOTH,
  57                             PCMK__ATTRD_CMD_UPDATE_DELAY,
  58                             NULL);
  59 }
  60 
  61 static bool
  62 dispatch(pcmk_ipc_api_t *api, xmlNode *reply)
     /* [previous][next][first][last][top][bottom][index][help] */
  63 {
  64     const char *value = NULL;
  65     crm_exit_t status = CRM_EX_OK;
  66 
  67     pcmk__attrd_api_reply_t reply_data = {
  68         pcmk__attrd_reply_unknown
  69     };
  70 
  71     if (pcmk__str_eq((const char *) reply->name, "ack", pcmk__str_none)) {
  72         return false;
  73     }
  74 
  75     /* Do some basic validation of the reply */
  76     value = crm_element_value(reply, F_TYPE);
  77     if (pcmk__str_empty(value)
  78         || !pcmk__str_eq(value, T_ATTRD, pcmk__str_none)) {
  79         crm_info("Unrecognizable message from attribute manager: "
  80                  "message type '%s' not '" T_ATTRD "'", pcmk__s(value, ""));
  81         status = CRM_EX_PROTOCOL;
  82         goto done;
  83     }
  84 
  85     value = crm_element_value(reply, F_SUBTYPE);
  86 
  87     /* Only the query command gets a reply for now. NULL counts as query for
  88      * backward compatibility with attribute managers <2.1.3 that didn't set it.
  89      */
  90     if (pcmk__str_eq(value, PCMK__ATTRD_CMD_QUERY, pcmk__str_null_matches)) {
  91         if (!xmlHasProp(reply, (pcmkXmlStr) PCMK__XA_ATTR_NAME)) {
  92             status = ENXIO; // Most likely, the attribute doesn't exist
  93             goto done;
  94         }
  95         reply_data.reply_type = pcmk__attrd_reply_query;
  96         set_pairs_data(&reply_data, reply);
  97 
  98     } else {
  99         crm_info("Unrecognizable message from attribute manager: "
 100                  "message subtype '%s' unknown", pcmk__s(value, ""));
 101         status = CRM_EX_PROTOCOL;
 102         goto done;
 103     }
 104 
 105 done:
 106     pcmk__call_ipc_callback(api, pcmk_ipc_event_reply, status, &reply_data);
 107 
 108     /* Free any reply data that was allocated */
 109     if (reply_data.data.pairs) {
 110         g_list_free_full(reply_data.data.pairs, free);
 111     }
 112 
 113     return false;
 114 }
 115 
 116 pcmk__ipc_methods_t *
 117 pcmk__attrd_api_methods(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 118 {
 119     pcmk__ipc_methods_t *cmds = calloc(1, sizeof(pcmk__ipc_methods_t));
 120 
 121     if (cmds != NULL) {
 122         cmds->new_data = NULL;
 123         cmds->free_data = NULL;
 124         cmds->post_connect = NULL;
 125         cmds->reply_expected = reply_expected;
 126         cmds->dispatch = dispatch;
 127     }
 128     return cmds;
 129 }
 130 
 131 /*!
 132  * \internal
 133  * \brief Create a generic pacemaker-attrd operation
 134  *
 135  * \param[in] user_name  If not NULL, ACL user to set for operation
 136  *
 137  * \return XML of pacemaker-attrd operation
 138  */
 139 static xmlNode *
 140 create_attrd_op(const char *user_name)
     /* [previous][next][first][last][top][bottom][index][help] */
 141 {
 142     xmlNode *attrd_op = create_xml_node(NULL, __func__);
 143 
 144     crm_xml_add(attrd_op, F_TYPE, T_ATTRD);
 145     crm_xml_add(attrd_op, F_ORIG, (crm_system_name? crm_system_name: "unknown"));
 146     crm_xml_add(attrd_op, PCMK__XA_ATTR_USER, user_name);
 147 
 148     return attrd_op;
 149 }
 150 
 151 static int
 152 create_api(pcmk_ipc_api_t **api)
     /* [previous][next][first][last][top][bottom][index][help] */
 153 {
 154     int rc = pcmk_new_ipc_api(api, pcmk_ipc_attrd);
 155 
 156     if (rc != pcmk_rc_ok) {
 157         crm_err("Could not connect to attrd: %s", pcmk_rc_str(rc));
 158     }
 159 
 160     return rc;
 161 }
 162 
 163 static void
 164 destroy_api(pcmk_ipc_api_t *api)
     /* [previous][next][first][last][top][bottom][index][help] */
 165 {
 166     pcmk_disconnect_ipc(api);
 167     pcmk_free_ipc_api(api);
 168     api = NULL;
 169 }
 170 
 171 static int
 172 connect_and_send_attrd_request(pcmk_ipc_api_t *api, xmlNode *request)
     /* [previous][next][first][last][top][bottom][index][help] */
 173 {
 174     int rc = pcmk_rc_ok;
 175     int max = 5;
 176 
 177     while (max > 0) {
 178         crm_info("Connecting to cluster... %d retries remaining", max);
 179         rc = pcmk_connect_ipc(api, pcmk_ipc_dispatch_sync);
 180 
 181         if (rc == pcmk_rc_ok) {
 182             rc = pcmk__send_ipc_request(api, request);
 183             break;
 184         } else if (rc == EAGAIN || rc == EALREADY) {
 185             sleep(5 - max);
 186             max--;
 187         } else {
 188             crm_err("Could not connect to attrd: %s", pcmk_rc_str(rc));
 189             break;
 190         }
 191     }
 192 
 193     return rc;
 194 }
 195 
 196 static int
 197 send_attrd_request(pcmk_ipc_api_t *api, xmlNode *request)
     /* [previous][next][first][last][top][bottom][index][help] */
 198 {
 199     return pcmk__send_ipc_request(api, request);
 200 }
 201 
 202 int
 203 pcmk__attrd_api_clear_failures(pcmk_ipc_api_t *api, const char *node,
     /* [previous][next][first][last][top][bottom][index][help] */
 204                                const char *resource, const char *operation,
 205                                const char *interval_spec, const char *user_name,
 206                                uint32_t options)
 207 {
 208     int rc = pcmk_rc_ok;
 209     xmlNode *request = create_attrd_op(user_name);
 210     const char *interval_desc = NULL;
 211     const char *op_desc = NULL;
 212     const char *target = pcmk__node_attr_target(node);
 213 
 214     if (target != NULL) {
 215         node = target;
 216     }
 217 
 218     crm_xml_add(request, PCMK__XA_TASK, PCMK__ATTRD_CMD_CLEAR_FAILURE);
 219     pcmk__xe_add_node(request, node, 0);
 220     crm_xml_add(request, PCMK__XA_ATTR_RESOURCE, resource);
 221     crm_xml_add(request, PCMK__XA_ATTR_OPERATION, operation);
 222     crm_xml_add(request, PCMK__XA_ATTR_INTERVAL, interval_spec);
 223     crm_xml_add_int(request, PCMK__XA_ATTR_IS_REMOTE,
 224                     pcmk_is_set(options, pcmk__node_attr_remote));
 225 
 226     if (api == NULL) {
 227         rc = create_api(&api);
 228         if (rc != pcmk_rc_ok) {
 229             return rc;
 230         }
 231 
 232         rc = connect_and_send_attrd_request(api, request);
 233         destroy_api(api);
 234 
 235     } else if (!pcmk_ipc_is_connected(api)) {
 236         rc = connect_and_send_attrd_request(api, request);
 237 
 238     } else {
 239         rc = send_attrd_request(api, request);
 240     }
 241 
 242     free_xml(request);
 243 
 244     if (operation) {
 245         interval_desc = interval_spec? interval_spec : "nonrecurring";
 246         op_desc = operation;
 247     } else {
 248         interval_desc = "all";
 249         op_desc = "operations";
 250     }
 251 
 252     crm_debug("Asked pacemaker-attrd to clear failure of %s %s for %s on %s: %s (%d)",
 253               interval_desc, op_desc, (resource? resource : "all resources"),
 254               (node? node : "all nodes"), pcmk_rc_str(rc), rc);
 255 
 256     return rc;
 257 }
 258 
 259 int
 260 pcmk__attrd_api_delete(pcmk_ipc_api_t *api, const char *node, const char *name,
     /* [previous][next][first][last][top][bottom][index][help] */
 261                        uint32_t options)
 262 {
 263     const char *target = NULL;
 264 
 265     if (name == NULL) {
 266         return EINVAL;
 267     }
 268 
 269     target = pcmk__node_attr_target(node);
 270 
 271     if (target != NULL) {
 272         node = target;
 273     }
 274 
 275     /* Make sure the right update option is set. */
 276     options &= ~pcmk__node_attr_delay;
 277     options |= pcmk__node_attr_value;
 278 
 279     return pcmk__attrd_api_update(api, node, name, NULL, NULL, NULL, NULL, options);
 280 }
 281 
 282 int
 283 pcmk__attrd_api_purge(pcmk_ipc_api_t *api, const char *node)
     /* [previous][next][first][last][top][bottom][index][help] */
 284 {
 285     int rc = pcmk_rc_ok;
 286     xmlNode *request = NULL;
 287     const char *display_host = (node ? node : "localhost");
 288     const char *target = pcmk__node_attr_target(node);
 289 
 290     if (target != NULL) {
 291         node = target;
 292     }
 293 
 294     request = create_attrd_op(NULL);
 295 
 296     crm_xml_add(request, PCMK__XA_TASK, PCMK__ATTRD_CMD_PEER_REMOVE);
 297     pcmk__xe_add_node(request, node, 0);
 298 
 299     if (api == NULL) {
 300         rc = create_api(&api);
 301         if (rc != pcmk_rc_ok) {
 302             return rc;
 303         }
 304 
 305         rc = connect_and_send_attrd_request(api, request);
 306         destroy_api(api);
 307 
 308     } else if (!pcmk_ipc_is_connected(api)) {
 309         rc = connect_and_send_attrd_request(api, request);
 310 
 311     } else {
 312         rc = send_attrd_request(api, request);
 313     }
 314 
 315     free_xml(request);
 316 
 317     crm_debug("Asked pacemaker-attrd to purge %s: %s (%d)",
 318               display_host, pcmk_rc_str(rc), rc);
 319 
 320     return rc;
 321 }
 322 
 323 int
 324 pcmk__attrd_api_query(pcmk_ipc_api_t *api, const char *node, const char *name,
     /* [previous][next][first][last][top][bottom][index][help] */
 325                       uint32_t options)
 326 {
 327     int rc = pcmk_rc_ok;
 328     xmlNode *request = NULL;
 329     const char *target = NULL;
 330 
 331     if (name == NULL) {
 332         return EINVAL;
 333     }
 334 
 335     if (pcmk_is_set(options, pcmk__node_attr_query_all)) {
 336         node = NULL;
 337     } else {
 338         target = pcmk__node_attr_target(node);
 339 
 340         if (target != NULL) {
 341             node = target;
 342         }
 343     }
 344 
 345     request = create_attrd_op(NULL);
 346 
 347     crm_xml_add(request, PCMK__XA_ATTR_NAME, name);
 348     crm_xml_add(request, PCMK__XA_TASK, PCMK__ATTRD_CMD_QUERY);
 349     pcmk__xe_add_node(request, node, 0);
 350 
 351     rc = send_attrd_request(api, request);
 352     free_xml(request);
 353 
 354     if (node) {
 355         crm_debug("Queried pacemaker-attrd for %s on %s: %s (%d)",
 356                   name, node, pcmk_rc_str(rc), rc);
 357     } else {
 358         crm_debug("Queried pacemaker-attrd for %s: %s (%d)",
 359                   name, pcmk_rc_str(rc), rc);
 360     }
 361 
 362     return rc;
 363 }
 364 
 365 int
 366 pcmk__attrd_api_refresh(pcmk_ipc_api_t *api, const char *node)
     /* [previous][next][first][last][top][bottom][index][help] */
 367 {
 368     int rc = pcmk_rc_ok;
 369     xmlNode *request = NULL;
 370     const char *display_host = (node ? node : "localhost");
 371     const char *target = pcmk__node_attr_target(node);
 372 
 373     if (target != NULL) {
 374         node = target;
 375     }
 376 
 377     request = create_attrd_op(NULL);
 378 
 379     crm_xml_add(request, PCMK__XA_TASK, PCMK__ATTRD_CMD_REFRESH);
 380     pcmk__xe_add_node(request, node, 0);
 381 
 382     if (api == NULL) {
 383         rc = create_api(&api);
 384         if (rc != pcmk_rc_ok) {
 385             return rc;
 386         }
 387 
 388         rc = connect_and_send_attrd_request(api, request);
 389         destroy_api(api);
 390 
 391     } else if (!pcmk_ipc_is_connected(api)) {
 392         rc = connect_and_send_attrd_request(api, request);
 393 
 394     } else {
 395         rc = send_attrd_request(api, request);
 396     }
 397 
 398     free_xml(request);
 399 
 400     crm_debug("Asked pacemaker-attrd to refresh %s: %s (%d)",
 401               display_host, pcmk_rc_str(rc), rc);
 402 
 403     return rc;
 404 }
 405 
 406 static void
 407 add_op_attr(xmlNode *op, uint32_t options)
     /* [previous][next][first][last][top][bottom][index][help] */
 408 {
 409     if (pcmk_all_flags_set(options, pcmk__node_attr_value | pcmk__node_attr_delay)) {
 410         crm_xml_add(op, PCMK__XA_TASK, PCMK__ATTRD_CMD_UPDATE_BOTH);
 411     } else if (pcmk_is_set(options, pcmk__node_attr_value)) {
 412         crm_xml_add(op, PCMK__XA_TASK, PCMK__ATTRD_CMD_UPDATE);
 413     } else if (pcmk_is_set(options, pcmk__node_attr_delay)) {
 414         crm_xml_add(op, PCMK__XA_TASK, PCMK__ATTRD_CMD_UPDATE_DELAY);
 415     }
 416 }
 417 
 418 static void
 419 populate_update_op(xmlNode *op, const char *node, const char *name, const char *value,
     /* [previous][next][first][last][top][bottom][index][help] */
 420                    const char *dampen, const char *set, uint32_t options)
 421 {
 422     if (pcmk_is_set(options, pcmk__node_attr_pattern)) {
 423         crm_xml_add(op, PCMK__XA_ATTR_PATTERN, name);
 424     } else {
 425         crm_xml_add(op, PCMK__XA_ATTR_NAME, name);
 426     }
 427 
 428     if (pcmk_is_set(options, pcmk__node_attr_utilization)) {
 429         crm_xml_add(op, PCMK__XA_ATTR_SET_TYPE, XML_TAG_UTILIZATION);
 430     } else {
 431         crm_xml_add(op, PCMK__XA_ATTR_SET_TYPE, XML_TAG_ATTR_SETS);
 432     }
 433 
 434     add_op_attr(op, options);
 435 
 436     crm_xml_add(op, PCMK__XA_ATTR_VALUE, value);
 437     crm_xml_add(op, PCMK__XA_ATTR_DAMPENING, dampen);
 438     pcmk__xe_add_node(op, node, 0);
 439     crm_xml_add(op, PCMK__XA_ATTR_SET, set);
 440     crm_xml_add_int(op, PCMK__XA_ATTR_IS_REMOTE,
 441                     pcmk_is_set(options, pcmk__node_attr_remote));
 442     crm_xml_add_int(op, PCMK__XA_ATTR_IS_PRIVATE,
 443                     pcmk_is_set(options, pcmk__node_attr_private));
 444 
 445     if (pcmk_is_set(options, pcmk__node_attr_sync_local)) {
 446         crm_xml_add(op, PCMK__XA_ATTR_SYNC_POINT, PCMK__VALUE_LOCAL);
 447     } else if (pcmk_is_set(options, pcmk__node_attr_sync_cluster)) {
 448         crm_xml_add(op, PCMK__XA_ATTR_SYNC_POINT, PCMK__VALUE_CLUSTER);
 449     }
 450 }
 451 
 452 int
 453 pcmk__attrd_api_update(pcmk_ipc_api_t *api, const char *node, const char *name,
     /* [previous][next][first][last][top][bottom][index][help] */
 454                        const char *value, const char *dampen, const char *set,
 455                        const char *user_name, uint32_t options)
 456 {
 457     int rc = pcmk_rc_ok;
 458     xmlNode *request = NULL;
 459     const char *display_host = (node ? node : "localhost");
 460     const char *target = NULL;
 461 
 462     if (name == NULL) {
 463         return EINVAL;
 464     }
 465 
 466     target = pcmk__node_attr_target(node);
 467 
 468     if (target != NULL) {
 469         node = target;
 470     }
 471 
 472     request = create_attrd_op(user_name);
 473     populate_update_op(request, node, name, value, dampen, set, options);
 474 
 475     if (api == NULL) {
 476         rc = create_api(&api);
 477         if (rc != pcmk_rc_ok) {
 478             return rc;
 479         }
 480 
 481         rc = connect_and_send_attrd_request(api, request);
 482         destroy_api(api);
 483 
 484     } else if (!pcmk_ipc_is_connected(api)) {
 485         rc = connect_and_send_attrd_request(api, request);
 486 
 487     } else {
 488         rc = send_attrd_request(api, request);
 489     }
 490 
 491     free_xml(request);
 492 
 493     crm_debug("Asked pacemaker-attrd to update %s on %s: %s (%d)",
 494               name, display_host, pcmk_rc_str(rc), rc);
 495 
 496     return rc;
 497 }
 498 
 499 int
 500 pcmk__attrd_api_update_list(pcmk_ipc_api_t *api, GList *attrs, const char *dampen,
     /* [previous][next][first][last][top][bottom][index][help] */
 501                             const char *set, const char *user_name,
 502                             uint32_t options)
 503 {
 504     int rc = pcmk_rc_ok;
 505     xmlNode *request = NULL;
 506 
 507     if (attrs == NULL) {
 508         return EINVAL;
 509     }
 510 
 511     /* There are two different ways of handling a list of attributes:
 512      *
 513      * (1) For messages originating from some command line tool, we have to send
 514      *     them one at a time.  In this loop, we just call pcmk__attrd_api_update
 515      *     for each, letting it deal with creating the API object if it doesn't
 516      *     already exist.
 517      *
 518      *     The reason we can't use a single message in this case is that we can't
 519      *     trust that the server supports it.  Remote nodes could be involved
 520      *     here, and there's no guarantee that a newer client running on a remote
 521      *     node is talking to (or proxied through) a cluster node with a newer
 522      *     attrd.  We also can't just try sending a single message and then falling
 523      *     back on multiple.  There's no handshake with the attrd server to
 524      *     determine its version.  And then we would need to do that fallback in the
 525      *     dispatch function for this to work for all connection types (mainloop in
 526      *     particular), and at that point we won't know what the original message
 527      *     was in order to break it apart and resend as individual messages.
 528      *
 529      * (2) For messages between daemons, we can be assured that the local attrd
 530      *     will support the new message and that it can send to the other attrds
 531      *     as one request or split up according to the minimum supported version.
 532      */
 533     for (GList *iter = attrs; iter != NULL; iter = iter->next) {
 534         pcmk__attrd_query_pair_t *pair = (pcmk__attrd_query_pair_t *) iter->data;
 535 
 536         if (pcmk__is_daemon) {
 537             const char *target = NULL;
 538             xmlNode *child = NULL;
 539 
 540             /* First time through this loop - create the basic request. */
 541             if (request == NULL) {
 542                 request = create_attrd_op(user_name);
 543                 add_op_attr(request, options);
 544             }
 545 
 546             /* Add a child node for this operation.  We add the task to the top
 547              * level XML node so attrd_ipc_dispatch doesn't need changes.  And
 548              * then we also add the task to each child node in populate_update_op
 549              * so attrd_client_update knows what form of update is taking place.
 550              */
 551             child = create_xml_node(request, XML_ATTR_OP);
 552             target = pcmk__node_attr_target(pair->node);
 553 
 554             if (target != NULL) {
 555                 pair->node = target;
 556             }
 557 
 558             populate_update_op(child, pair->node, pair->name, pair->value, dampen,
 559                                set, options);
 560         } else {
 561             rc = pcmk__attrd_api_update(api, pair->node, pair->name, pair->value,
 562                                         dampen, set, user_name, options);
 563         }
 564     }
 565 
 566     /* If we were doing multiple attributes at once, we still need to send the
 567      * request.  Do that now, creating and destroying the API object if needed.
 568      */
 569     if (pcmk__is_daemon) {
 570         bool created_api = false;
 571 
 572         if (api == NULL) {
 573             rc = create_api(&api);
 574             if (rc != pcmk_rc_ok) {
 575                 return rc;
 576             }
 577 
 578             created_api = true;
 579         }
 580 
 581         rc = connect_and_send_attrd_request(api, request);
 582         free_xml(request);
 583 
 584         if (created_api) {
 585             destroy_api(api);
 586         }
 587     }
 588 
 589     return rc;
 590 }

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