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. connect_and_send_attrd_request
  7. pcmk__attrd_api_clear_failures
  8. pcmk__attrd_api_delete
  9. pcmk__attrd_api_purge
  10. pcmk__attrd_api_query
  11. pcmk__attrd_api_refresh
  12. add_op_attr
  13. populate_update_op
  14. pcmk__attrd_api_update
  15. pcmk__attrd_api_update_list

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

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