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, const 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, const xmlNode *request)
     /* [previous][next][first][last][top][bottom][index][help] */
 173 {
 174     int rc = pcmk_rc_ok;
 175 
 176     rc = pcmk__connect_ipc(api, pcmk_ipc_dispatch_sync, 5);
 177     if (rc != pcmk_rc_ok) {
 178         crm_err("Could not connect to %s: %s",
 179                 pcmk_ipc_name(api, true), pcmk_rc_str(rc));
 180         return rc;
 181     }
 182 
 183     rc = pcmk__send_ipc_request(api, request);
 184     if (rc != pcmk_rc_ok) {
 185         crm_err("Could not send request to %s: %s",
 186                 pcmk_ipc_name(api, true), pcmk_rc_str(rc));
 187         return rc;
 188     }
 189 
 190     return pcmk_rc_ok;
 191 }
 192 
 193 static int
 194 send_attrd_request(pcmk_ipc_api_t *api, const xmlNode *request)
     /* [previous][next][first][last][top][bottom][index][help] */
 195 {
 196     return pcmk__send_ipc_request(api, request);
 197 }
 198 
 199 int
 200 pcmk__attrd_api_clear_failures(pcmk_ipc_api_t *api, const char *node,
     /* [previous][next][first][last][top][bottom][index][help] */
 201                                const char *resource, const char *operation,
 202                                const char *interval_spec, const char *user_name,
 203                                uint32_t options)
 204 {
 205     int rc = pcmk_rc_ok;
 206     xmlNode *request = create_attrd_op(user_name);
 207     const char *interval_desc = NULL;
 208     const char *op_desc = NULL;
 209     const char *target = pcmk__node_attr_target(node);
 210 
 211     if (target != NULL) {
 212         node = target;
 213     }
 214 
 215     crm_xml_add(request, PCMK__XA_TASK, PCMK__ATTRD_CMD_CLEAR_FAILURE);
 216     pcmk__xe_add_node(request, node, 0);
 217     crm_xml_add(request, PCMK__XA_ATTR_RESOURCE, resource);
 218     crm_xml_add(request, PCMK__XA_ATTR_OPERATION, operation);
 219     crm_xml_add(request, PCMK__XA_ATTR_INTERVAL, interval_spec);
 220     crm_xml_add_int(request, PCMK__XA_ATTR_IS_REMOTE,
 221                     pcmk_is_set(options, pcmk__node_attr_remote));
 222 
 223     if (api == NULL) {
 224         rc = create_api(&api);
 225         if (rc != pcmk_rc_ok) {
 226             return rc;
 227         }
 228 
 229         rc = connect_and_send_attrd_request(api, request);
 230         destroy_api(api);
 231 
 232     } else if (!pcmk_ipc_is_connected(api)) {
 233         rc = connect_and_send_attrd_request(api, request);
 234 
 235     } else {
 236         rc = send_attrd_request(api, request);
 237     }
 238 
 239     free_xml(request);
 240 
 241     if (operation) {
 242         interval_desc = interval_spec? interval_spec : "nonrecurring";
 243         op_desc = operation;
 244     } else {
 245         interval_desc = "all";
 246         op_desc = "operations";
 247     }
 248 
 249     crm_debug("Asked pacemaker-attrd to clear failure of %s %s for %s on %s: %s (%d)",
 250               interval_desc, op_desc, (resource? resource : "all resources"),
 251               (node? node : "all nodes"), pcmk_rc_str(rc), rc);
 252 
 253     return rc;
 254 }
 255 
 256 int
 257 pcmk__attrd_api_delete(pcmk_ipc_api_t *api, const char *node, const char *name,
     /* [previous][next][first][last][top][bottom][index][help] */
 258                        uint32_t options)
 259 {
 260     const char *target = NULL;
 261 
 262     if (name == NULL) {
 263         return EINVAL;
 264     }
 265 
 266     target = pcmk__node_attr_target(node);
 267 
 268     if (target != NULL) {
 269         node = target;
 270     }
 271 
 272     /* Make sure the right update option is set. */
 273     options &= ~pcmk__node_attr_delay;
 274     options |= pcmk__node_attr_value;
 275 
 276     return pcmk__attrd_api_update(api, node, name, NULL, NULL, NULL, NULL, options);
 277 }
 278 
 279 int
 280 pcmk__attrd_api_purge(pcmk_ipc_api_t *api, const char *node)
     /* [previous][next][first][last][top][bottom][index][help] */
 281 {
 282     int rc = pcmk_rc_ok;
 283     xmlNode *request = NULL;
 284     const char *display_host = (node ? node : "localhost");
 285     const char *target = pcmk__node_attr_target(node);
 286 
 287     if (target != NULL) {
 288         node = target;
 289     }
 290 
 291     request = create_attrd_op(NULL);
 292 
 293     crm_xml_add(request, PCMK__XA_TASK, PCMK__ATTRD_CMD_PEER_REMOVE);
 294     pcmk__xe_add_node(request, node, 0);
 295 
 296     if (api == NULL) {
 297         rc = create_api(&api);
 298         if (rc != pcmk_rc_ok) {
 299             return rc;
 300         }
 301 
 302         rc = connect_and_send_attrd_request(api, request);
 303         destroy_api(api);
 304 
 305     } else if (!pcmk_ipc_is_connected(api)) {
 306         rc = connect_and_send_attrd_request(api, request);
 307 
 308     } else {
 309         rc = send_attrd_request(api, request);
 310     }
 311 
 312     free_xml(request);
 313 
 314     crm_debug("Asked pacemaker-attrd to purge %s: %s (%d)",
 315               display_host, pcmk_rc_str(rc), rc);
 316 
 317     return rc;
 318 }
 319 
 320 int
 321 pcmk__attrd_api_query(pcmk_ipc_api_t *api, const char *node, const char *name,
     /* [previous][next][first][last][top][bottom][index][help] */
 322                       uint32_t options)
 323 {
 324     int rc = pcmk_rc_ok;
 325     xmlNode *request = NULL;
 326     const char *target = NULL;
 327 
 328     if (name == NULL) {
 329         return EINVAL;
 330     }
 331 
 332     if (pcmk_is_set(options, pcmk__node_attr_query_all)) {
 333         node = NULL;
 334     } else {
 335         target = pcmk__node_attr_target(node);
 336 
 337         if (target != NULL) {
 338             node = target;
 339         }
 340     }
 341 
 342     request = create_attrd_op(NULL);
 343 
 344     crm_xml_add(request, PCMK__XA_ATTR_NAME, name);
 345     crm_xml_add(request, PCMK__XA_TASK, PCMK__ATTRD_CMD_QUERY);
 346     pcmk__xe_add_node(request, node, 0);
 347 
 348     rc = send_attrd_request(api, request);
 349     free_xml(request);
 350 
 351     if (node) {
 352         crm_debug("Queried pacemaker-attrd for %s on %s: %s (%d)",
 353                   name, node, pcmk_rc_str(rc), rc);
 354     } else {
 355         crm_debug("Queried pacemaker-attrd for %s: %s (%d)",
 356                   name, pcmk_rc_str(rc), rc);
 357     }
 358 
 359     return rc;
 360 }
 361 
 362 int
 363 pcmk__attrd_api_refresh(pcmk_ipc_api_t *api, const char *node)
     /* [previous][next][first][last][top][bottom][index][help] */
 364 {
 365     int rc = pcmk_rc_ok;
 366     xmlNode *request = NULL;
 367     const char *display_host = (node ? node : "localhost");
 368     const char *target = pcmk__node_attr_target(node);
 369 
 370     if (target != NULL) {
 371         node = target;
 372     }
 373 
 374     request = create_attrd_op(NULL);
 375 
 376     crm_xml_add(request, PCMK__XA_TASK, PCMK__ATTRD_CMD_REFRESH);
 377     pcmk__xe_add_node(request, node, 0);
 378 
 379     if (api == NULL) {
 380         rc = create_api(&api);
 381         if (rc != pcmk_rc_ok) {
 382             return rc;
 383         }
 384 
 385         rc = connect_and_send_attrd_request(api, request);
 386         destroy_api(api);
 387 
 388     } else if (!pcmk_ipc_is_connected(api)) {
 389         rc = connect_and_send_attrd_request(api, request);
 390 
 391     } else {
 392         rc = send_attrd_request(api, request);
 393     }
 394 
 395     free_xml(request);
 396 
 397     crm_debug("Asked pacemaker-attrd to refresh %s: %s (%d)",
 398               display_host, pcmk_rc_str(rc), rc);
 399 
 400     return rc;
 401 }
 402 
 403 static void
 404 add_op_attr(xmlNode *op, uint32_t options)
     /* [previous][next][first][last][top][bottom][index][help] */
 405 {
 406     if (pcmk_all_flags_set(options, pcmk__node_attr_value | pcmk__node_attr_delay)) {
 407         crm_xml_add(op, PCMK__XA_TASK, PCMK__ATTRD_CMD_UPDATE_BOTH);
 408     } else if (pcmk_is_set(options, pcmk__node_attr_value)) {
 409         crm_xml_add(op, PCMK__XA_TASK, PCMK__ATTRD_CMD_UPDATE);
 410     } else if (pcmk_is_set(options, pcmk__node_attr_delay)) {
 411         crm_xml_add(op, PCMK__XA_TASK, PCMK__ATTRD_CMD_UPDATE_DELAY);
 412     }
 413 }
 414 
 415 static void
 416 populate_update_op(xmlNode *op, const char *node, const char *name, const char *value,
     /* [previous][next][first][last][top][bottom][index][help] */
 417                    const char *dampen, const char *set, uint32_t options)
 418 {
 419     if (pcmk_is_set(options, pcmk__node_attr_pattern)) {
 420         crm_xml_add(op, PCMK__XA_ATTR_PATTERN, name);
 421     } else {
 422         crm_xml_add(op, PCMK__XA_ATTR_NAME, name);
 423     }
 424 
 425     if (pcmk_is_set(options, pcmk__node_attr_utilization)) {
 426         crm_xml_add(op, PCMK__XA_ATTR_SET_TYPE, XML_TAG_UTILIZATION);
 427     } else {
 428         crm_xml_add(op, PCMK__XA_ATTR_SET_TYPE, XML_TAG_ATTR_SETS);
 429     }
 430 
 431     add_op_attr(op, options);
 432 
 433     crm_xml_add(op, PCMK__XA_ATTR_VALUE, value);
 434     crm_xml_add(op, PCMK__XA_ATTR_DAMPENING, dampen);
 435     pcmk__xe_add_node(op, node, 0);
 436     crm_xml_add(op, PCMK__XA_ATTR_SET, set);
 437     crm_xml_add_int(op, PCMK__XA_ATTR_IS_REMOTE,
 438                     pcmk_is_set(options, pcmk__node_attr_remote));
 439     crm_xml_add_int(op, PCMK__XA_ATTR_IS_PRIVATE,
 440                     pcmk_is_set(options, pcmk__node_attr_private));
 441 
 442     if (pcmk_is_set(options, pcmk__node_attr_sync_local)) {
 443         crm_xml_add(op, PCMK__XA_ATTR_SYNC_POINT, PCMK__VALUE_LOCAL);
 444     } else if (pcmk_is_set(options, pcmk__node_attr_sync_cluster)) {
 445         crm_xml_add(op, PCMK__XA_ATTR_SYNC_POINT, PCMK__VALUE_CLUSTER);
 446     }
 447 }
 448 
 449 int
 450 pcmk__attrd_api_update(pcmk_ipc_api_t *api, const char *node, const char *name,
     /* [previous][next][first][last][top][bottom][index][help] */
 451                        const char *value, const char *dampen, const char *set,
 452                        const char *user_name, uint32_t options)
 453 {
 454     int rc = pcmk_rc_ok;
 455     xmlNode *request = NULL;
 456     const char *display_host = (node ? node : "localhost");
 457     const char *target = NULL;
 458 
 459     if (name == NULL) {
 460         return EINVAL;
 461     }
 462 
 463     target = pcmk__node_attr_target(node);
 464 
 465     if (target != NULL) {
 466         node = target;
 467     }
 468 
 469     request = create_attrd_op(user_name);
 470     populate_update_op(request, node, name, value, dampen, set, options);
 471 
 472     if (api == NULL) {
 473         rc = create_api(&api);
 474         if (rc != pcmk_rc_ok) {
 475             return rc;
 476         }
 477 
 478         rc = connect_and_send_attrd_request(api, request);
 479         destroy_api(api);
 480 
 481     } else if (!pcmk_ipc_is_connected(api)) {
 482         rc = connect_and_send_attrd_request(api, request);
 483 
 484     } else {
 485         rc = send_attrd_request(api, request);
 486     }
 487 
 488     free_xml(request);
 489 
 490     crm_debug("Asked pacemaker-attrd to update %s on %s: %s (%d)",
 491               name, display_host, pcmk_rc_str(rc), rc);
 492 
 493     return rc;
 494 }
 495 
 496 int
 497 pcmk__attrd_api_update_list(pcmk_ipc_api_t *api, GList *attrs, const char *dampen,
     /* [previous][next][first][last][top][bottom][index][help] */
 498                             const char *set, const char *user_name,
 499                             uint32_t options)
 500 {
 501     int rc = pcmk_rc_ok;
 502     xmlNode *request = NULL;
 503 
 504     if (attrs == NULL) {
 505         return EINVAL;
 506     }
 507 
 508     /* There are two different ways of handling a list of attributes:
 509      *
 510      * (1) For messages originating from some command line tool, we have to send
 511      *     them one at a time.  In this loop, we just call pcmk__attrd_api_update
 512      *     for each, letting it deal with creating the API object if it doesn't
 513      *     already exist.
 514      *
 515      *     The reason we can't use a single message in this case is that we can't
 516      *     trust that the server supports it.  Remote nodes could be involved
 517      *     here, and there's no guarantee that a newer client running on a remote
 518      *     node is talking to (or proxied through) a cluster node with a newer
 519      *     attrd.  We also can't just try sending a single message and then falling
 520      *     back on multiple.  There's no handshake with the attrd server to
 521      *     determine its version.  And then we would need to do that fallback in the
 522      *     dispatch function for this to work for all connection types (mainloop in
 523      *     particular), and at that point we won't know what the original message
 524      *     was in order to break it apart and resend as individual messages.
 525      *
 526      * (2) For messages between daemons, we can be assured that the local attrd
 527      *     will support the new message and that it can send to the other attrds
 528      *     as one request or split up according to the minimum supported version.
 529      */
 530     for (GList *iter = attrs; iter != NULL; iter = iter->next) {
 531         pcmk__attrd_query_pair_t *pair = (pcmk__attrd_query_pair_t *) iter->data;
 532 
 533         if (pcmk__is_daemon) {
 534             const char *target = NULL;
 535             xmlNode *child = NULL;
 536 
 537             /* First time through this loop - create the basic request. */
 538             if (request == NULL) {
 539                 request = create_attrd_op(user_name);
 540                 add_op_attr(request, options);
 541             }
 542 
 543             /* Add a child node for this operation.  We add the task to the top
 544              * level XML node so attrd_ipc_dispatch doesn't need changes.  And
 545              * then we also add the task to each child node in populate_update_op
 546              * so attrd_client_update knows what form of update is taking place.
 547              */
 548             child = create_xml_node(request, XML_ATTR_OP);
 549             target = pcmk__node_attr_target(pair->node);
 550 
 551             if (target != NULL) {
 552                 pair->node = target;
 553             }
 554 
 555             populate_update_op(child, pair->node, pair->name, pair->value, dampen,
 556                                set, options);
 557         } else {
 558             rc = pcmk__attrd_api_update(api, pair->node, pair->name, pair->value,
 559                                         dampen, set, user_name, options);
 560         }
 561     }
 562 
 563     /* If we were doing multiple attributes at once, we still need to send the
 564      * request.  Do that now, creating and destroying the API object if needed.
 565      */
 566     if (pcmk__is_daemon) {
 567         bool created_api = false;
 568 
 569         if (api == NULL) {
 570             rc = create_api(&api);
 571             if (rc != pcmk_rc_ok) {
 572                 return rc;
 573             }
 574 
 575             created_api = true;
 576         }
 577 
 578         rc = connect_and_send_attrd_request(api, request);
 579         free_xml(request);
 580 
 581         if (created_api) {
 582             destroy_api(api);
 583         }
 584     }
 585 
 586     return rc;
 587 }

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