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

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