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-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 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     target = pcmk__node_attr_target(node);
 336 
 337     if (target != NULL) {
 338         node = target;
 339     }
 340 
 341     request = create_attrd_op(NULL);
 342 
 343     crm_xml_add(request, PCMK__XA_ATTR_NAME, name);
 344     crm_xml_add(request, PCMK__XA_TASK, PCMK__ATTRD_CMD_QUERY);
 345     pcmk__xe_add_node(request, node, 0);
 346 
 347     rc = send_attrd_request(api, request);
 348     free_xml(request);
 349 
 350     if (node) {
 351         crm_debug("Queried pacemaker-attrd for %s on %s: %s (%d)",
 352                   name, node, pcmk_rc_str(rc), rc);
 353     } else {
 354         crm_debug("Queried pacemaker-attrd for %s: %s (%d)",
 355                   name, pcmk_rc_str(rc), rc);
 356     }
 357 
 358     return rc;
 359 }
 360 
 361 int
 362 pcmk__attrd_api_refresh(pcmk_ipc_api_t *api, const char *node)
     /* [previous][next][first][last][top][bottom][index][help] */
 363 {
 364     int rc = pcmk_rc_ok;
 365     xmlNode *request = NULL;
 366     const char *display_host = (node ? node : "localhost");
 367     const char *target = pcmk__node_attr_target(node);
 368 
 369     if (target != NULL) {
 370         node = target;
 371     }
 372 
 373     request = create_attrd_op(NULL);
 374 
 375     crm_xml_add(request, PCMK__XA_TASK, PCMK__ATTRD_CMD_REFRESH);
 376     pcmk__xe_add_node(request, node, 0);
 377 
 378     if (api == NULL) {
 379         rc = create_api(&api);
 380         if (rc != pcmk_rc_ok) {
 381             return rc;
 382         }
 383 
 384         rc = connect_and_send_attrd_request(api, request);
 385         destroy_api(api);
 386 
 387     } else if (!pcmk_ipc_is_connected(api)) {
 388         rc = connect_and_send_attrd_request(api, request);
 389 
 390     } else {
 391         rc = send_attrd_request(api, request);
 392     }
 393 
 394     free_xml(request);
 395 
 396     crm_debug("Asked pacemaker-attrd to refresh %s: %s (%d)",
 397               display_host, pcmk_rc_str(rc), rc);
 398 
 399     return rc;
 400 }
 401 
 402 static void
 403 add_op_attr(xmlNode *op, uint32_t options)
     /* [previous][next][first][last][top][bottom][index][help] */
 404 {
 405     if (pcmk_all_flags_set(options, pcmk__node_attr_value | pcmk__node_attr_delay)) {
 406         crm_xml_add(op, PCMK__XA_TASK, PCMK__ATTRD_CMD_UPDATE_BOTH);
 407     } else if (pcmk_is_set(options, pcmk__node_attr_value)) {
 408         crm_xml_add(op, PCMK__XA_TASK, PCMK__ATTRD_CMD_UPDATE);
 409     } else if (pcmk_is_set(options, pcmk__node_attr_delay)) {
 410         crm_xml_add(op, PCMK__XA_TASK, PCMK__ATTRD_CMD_UPDATE_DELAY);
 411     }
 412 }
 413 
 414 static void
 415 populate_update_op(xmlNode *op, const char *node, const char *name, const char *value,
     /* [previous][next][first][last][top][bottom][index][help] */
 416                    const char *dampen, const char *set, uint32_t options)
 417 {
 418     if (pcmk_is_set(options, pcmk__node_attr_pattern)) {
 419         crm_xml_add(op, PCMK__XA_ATTR_PATTERN, name);
 420     } else {
 421         crm_xml_add(op, PCMK__XA_ATTR_NAME, name);
 422     }
 423 
 424     add_op_attr(op, options);
 425 
 426     crm_xml_add(op, PCMK__XA_ATTR_VALUE, value);
 427     crm_xml_add(op, PCMK__XA_ATTR_DAMPENING, dampen);
 428     pcmk__xe_add_node(op, node, 0);
 429     crm_xml_add(op, PCMK__XA_ATTR_SET, set);
 430     crm_xml_add_int(op, PCMK__XA_ATTR_IS_REMOTE,
 431                     pcmk_is_set(options, pcmk__node_attr_remote));
 432     crm_xml_add_int(op, PCMK__XA_ATTR_IS_PRIVATE,
 433                     pcmk_is_set(options, pcmk__node_attr_private));
 434 }
 435 
 436 int
 437 pcmk__attrd_api_update(pcmk_ipc_api_t *api, const char *node, const char *name,
     /* [previous][next][first][last][top][bottom][index][help] */
 438                        const char *value, const char *dampen, const char *set,
 439                        const char *user_name, uint32_t options)
 440 {
 441     int rc = pcmk_rc_ok;
 442     xmlNode *request = NULL;
 443     const char *display_host = (node ? node : "localhost");
 444     const char *target = NULL;
 445 
 446     if (name == NULL) {
 447         return EINVAL;
 448     }
 449 
 450     target = pcmk__node_attr_target(node);
 451 
 452     if (target != NULL) {
 453         node = target;
 454     }
 455 
 456     request = create_attrd_op(user_name);
 457     populate_update_op(request, node, name, value, dampen, set, options);
 458 
 459     if (api == NULL) {
 460         rc = create_api(&api);
 461         if (rc != pcmk_rc_ok) {
 462             return rc;
 463         }
 464 
 465         rc = connect_and_send_attrd_request(api, request);
 466         destroy_api(api);
 467 
 468     } else if (!pcmk_ipc_is_connected(api)) {
 469         rc = connect_and_send_attrd_request(api, request);
 470 
 471     } else {
 472         rc = send_attrd_request(api, request);
 473     }
 474 
 475     free_xml(request);
 476 
 477     crm_debug("Asked pacemaker-attrd to update %s on %s: %s (%d)",
 478               name, display_host, pcmk_rc_str(rc), rc);
 479 
 480     return rc;
 481 }
 482 
 483 int
 484 pcmk__attrd_api_update_list(pcmk_ipc_api_t *api, GList *attrs, const char *dampen,
     /* [previous][next][first][last][top][bottom][index][help] */
 485                             const char *set, const char *user_name,
 486                             uint32_t options)
 487 {
 488     int rc = pcmk_rc_ok;
 489     xmlNode *request = NULL;
 490 
 491     if (attrs == NULL) {
 492         return EINVAL;
 493     }
 494 
 495     /* There are two different ways of handling a list of attributes:
 496      *
 497      * (1) For messages originating from some command line tool, we have to send
 498      *     them one at a time.  In this loop, we just call pcmk__attrd_api_update
 499      *     for each, letting it deal with creating the API object if it doesn't
 500      *     already exist.
 501      *
 502      *     The reason we can't use a single message in this case is that we can't
 503      *     trust that the server supports it.  Remote nodes could be involved
 504      *     here, and there's no guarantee that a newer client running on a remote
 505      *     node is talking to (or proxied through) a cluster node with a newer
 506      *     attrd.  We also can't just try sending a single message and then falling
 507      *     back on multiple.  There's no handshake with the attrd server to
 508      *     determine its version.  And then we would need to do that fallback in the
 509      *     dispatch function for this to work for all connection types (mainloop in
 510      *     particular), and at that point we won't know what the original message
 511      *     was in order to break it apart and resend as individual messages.
 512      *
 513      * (2) For messages between daemons, we can be assured that the local attrd
 514      *     will support the new message and that it can send to the other attrds
 515      *     as one request or split up according to the minimum supported version.
 516      */
 517     for (GList *iter = attrs; iter != NULL; iter = iter->next) {
 518         pcmk__attrd_query_pair_t *pair = (pcmk__attrd_query_pair_t *) iter->data;
 519 
 520         if (pcmk__is_daemon) {
 521             const char *target = NULL;
 522             xmlNode *child = NULL;
 523 
 524             /* First time through this loop - create the basic request. */
 525             if (request == NULL) {
 526                 request = create_attrd_op(user_name);
 527                 add_op_attr(request, options);
 528             }
 529 
 530             /* Add a child node for this operation.  We add the task to the top
 531              * level XML node so attrd_ipc_dispatch doesn't need changes.  And
 532              * then we also add the task to each child node in populate_update_op
 533              * so attrd_client_update knows what form of update is taking place.
 534              */
 535             child = create_xml_node(request, XML_ATTR_OP);
 536             target = pcmk__node_attr_target(pair->node);
 537 
 538             if (target != NULL) {
 539                 pair->node = target;
 540             }
 541 
 542             populate_update_op(child, pair->node, pair->name, pair->value, dampen,
 543                                set, options);
 544         } else {
 545             rc = pcmk__attrd_api_update(api, pair->node, pair->name, pair->value,
 546                                         dampen, set, user_name, options);
 547         }
 548     }
 549 
 550     /* If we were doing multiple attributes at once, we still need to send the
 551      * request.  Do that now, creating and destroying the API object if needed.
 552      */
 553     if (pcmk__is_daemon) {
 554         bool created_api = false;
 555 
 556         if (api == NULL) {
 557             rc = create_api(&api);
 558             if (rc != pcmk_rc_ok) {
 559                 return rc;
 560             }
 561 
 562             created_api = true;
 563         }
 564 
 565         rc = connect_and_send_attrd_request(api, request);
 566         free_xml(request);
 567 
 568         if (created_api) {
 569             destroy_api(api);
 570         }
 571     }
 572 
 573     return rc;
 574 }

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