LCOV - code coverage report
Current view: top level - common - ipc_attrd.c (source / functions) Hit Total Coverage
Test: Pacemaker code coverage Lines: 0 217 0.0 %
Date: 2024-05-07 11:09:47 Functions: 0 15 0.0 %

          Line data    Source code
       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           0 : set_pairs_data(pcmk__attrd_api_reply_t *data, xmlNode *msg_data)
      27             : {
      28           0 :     const char *name = NULL;
      29             :     pcmk__attrd_query_pair_t *pair;
      30             : 
      31           0 :     name = crm_element_value(msg_data, PCMK__XA_ATTR_NAME);
      32             : 
      33           0 :     for (xmlNode *node = pcmk__xe_first_child(msg_data, PCMK_XE_NODE, NULL,
      34             :                                               NULL);
      35           0 :          node != NULL; node = pcmk__xe_next_same(node)) {
      36             : 
      37           0 :         pair = pcmk__assert_alloc(1, sizeof(pcmk__attrd_query_pair_t));
      38             : 
      39           0 :         pair->node = crm_element_value(node, PCMK__XA_ATTR_HOST);
      40           0 :         pair->name = name;
      41           0 :         pair->value = crm_element_value(node, PCMK__XA_ATTR_VALUE);
      42           0 :         data->data.pairs = g_list_prepend(data->data.pairs, pair);
      43             :     }
      44           0 : }
      45             : 
      46             : static bool
      47           0 : reply_expected(pcmk_ipc_api_t *api, const xmlNode *request)
      48             : {
      49           0 :     const char *command = crm_element_value(request, PCMK_XA_TASK);
      50             : 
      51           0 :     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           0 : dispatch(pcmk_ipc_api_t *api, xmlNode *reply)
      63             : {
      64           0 :     const char *value = NULL;
      65           0 :     crm_exit_t status = CRM_EX_OK;
      66             : 
      67           0 :     pcmk__attrd_api_reply_t reply_data = {
      68             :         pcmk__attrd_reply_unknown
      69             :     };
      70             : 
      71           0 :     if (pcmk__xe_is(reply, PCMK__XE_ACK)) {
      72           0 :         return false;
      73             :     }
      74             : 
      75             :     /* Do some basic validation of the reply */
      76           0 :     value = crm_element_value(reply, PCMK__XA_T);
      77           0 :     if (pcmk__str_empty(value)
      78           0 :         || !pcmk__str_eq(value, PCMK__VALUE_ATTRD, pcmk__str_none)) {
      79           0 :         crm_info("Unrecognizable message from attribute manager: "
      80             :                  "message type '%s' not '" PCMK__VALUE_ATTRD "'",
      81             :                  pcmk__s(value, ""));
      82           0 :         status = CRM_EX_PROTOCOL;
      83           0 :         goto done;
      84             :     }
      85             : 
      86           0 :     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           0 :     if (pcmk__str_eq(value, PCMK__ATTRD_CMD_QUERY, pcmk__str_null_matches)) {
      92           0 :         if (!xmlHasProp(reply, (pcmkXmlStr) PCMK__XA_ATTR_NAME)) {
      93           0 :             status = ENXIO; // Most likely, the attribute doesn't exist
      94           0 :             goto done;
      95             :         }
      96           0 :         reply_data.reply_type = pcmk__attrd_reply_query;
      97           0 :         set_pairs_data(&reply_data, reply);
      98             : 
      99             :     } else {
     100           0 :         crm_info("Unrecognizable message from attribute manager: "
     101             :                  "message subtype '%s' unknown", pcmk__s(value, ""));
     102           0 :         status = CRM_EX_PROTOCOL;
     103           0 :         goto done;
     104             :     }
     105             : 
     106           0 : done:
     107           0 :     pcmk__call_ipc_callback(api, pcmk_ipc_event_reply, status, &reply_data);
     108             : 
     109             :     /* Free any reply data that was allocated */
     110           0 :     if (reply_data.data.pairs) {
     111           0 :         g_list_free_full(reply_data.data.pairs, free);
     112             :     }
     113             : 
     114           0 :     return false;
     115             : }
     116             : 
     117             : pcmk__ipc_methods_t *
     118           0 : pcmk__attrd_api_methods(void)
     119             : {
     120           0 :     pcmk__ipc_methods_t *cmds = calloc(1, sizeof(pcmk__ipc_methods_t));
     121             : 
     122           0 :     if (cmds != NULL) {
     123           0 :         cmds->new_data = NULL;
     124           0 :         cmds->free_data = NULL;
     125           0 :         cmds->post_connect = NULL;
     126           0 :         cmds->reply_expected = reply_expected;
     127           0 :         cmds->dispatch = dispatch;
     128             :     }
     129           0 :     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           0 : create_attrd_op(const char *user_name)
     142             : {
     143           0 :     xmlNode *attrd_op = pcmk__xe_create(NULL, __func__);
     144             : 
     145           0 :     crm_xml_add(attrd_op, PCMK__XA_T, PCMK__VALUE_ATTRD);
     146           0 :     crm_xml_add(attrd_op, PCMK__XA_SRC, pcmk__s(crm_system_name, "unknown"));
     147           0 :     crm_xml_add(attrd_op, PCMK__XA_ATTR_USER, user_name);
     148             : 
     149           0 :     return attrd_op;
     150             : }
     151             : 
     152             : static int
     153           0 : connect_and_send_attrd_request(pcmk_ipc_api_t *api, const xmlNode *request)
     154             : {
     155           0 :     int rc = pcmk_rc_ok;
     156           0 :     bool created_api = false;
     157             : 
     158           0 :     if (api == NULL) {
     159           0 :         rc = pcmk_new_ipc_api(&api, pcmk_ipc_attrd);
     160           0 :         if (rc != pcmk_rc_ok) {
     161           0 :             return rc;
     162             :         }
     163           0 :         created_api = true;
     164             :     }
     165             : 
     166           0 :     rc = pcmk__connect_ipc(api, pcmk_ipc_dispatch_sync, 5);
     167           0 :     if (rc == pcmk_rc_ok) {
     168           0 :         rc = pcmk__send_ipc_request(api, request);
     169             :     }
     170             : 
     171           0 :     if (created_api) {
     172           0 :         pcmk_free_ipc_api(api);
     173             :     }
     174           0 :     return rc;
     175             : }
     176             : 
     177             : int
     178           0 : pcmk__attrd_api_clear_failures(pcmk_ipc_api_t *api, const char *node,
     179             :                                const char *resource, const char *operation,
     180             :                                const char *interval_spec, const char *user_name,
     181             :                                uint32_t options)
     182             : {
     183           0 :     int rc = pcmk_rc_ok;
     184           0 :     xmlNode *request = create_attrd_op(user_name);
     185           0 :     const char *interval_desc = NULL;
     186           0 :     const char *op_desc = NULL;
     187           0 :     const char *target = pcmk__node_attr_target(node);
     188             : 
     189           0 :     if (target != NULL) {
     190           0 :         node = target;
     191             :     }
     192             : 
     193           0 :     if (operation) {
     194           0 :         interval_desc = pcmk__s(interval_spec, "nonrecurring");
     195           0 :         op_desc = operation;
     196             :     } else {
     197           0 :         interval_desc = "all";
     198           0 :         op_desc = "operations";
     199             :     }
     200           0 :     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           0 :     crm_xml_add(request, PCMK_XA_TASK, PCMK__ATTRD_CMD_CLEAR_FAILURE);
     205           0 :     pcmk__xe_add_node(request, node, 0);
     206           0 :     crm_xml_add(request, PCMK__XA_ATTR_RESOURCE, resource);
     207           0 :     crm_xml_add(request, PCMK__XA_ATTR_CLEAR_OPERATION, operation);
     208           0 :     crm_xml_add(request, PCMK__XA_ATTR_CLEAR_INTERVAL, interval_spec);
     209           0 :     crm_xml_add_int(request, PCMK__XA_ATTR_IS_REMOTE,
     210           0 :                     pcmk_is_set(options, pcmk__node_attr_remote));
     211             : 
     212           0 :     rc = connect_and_send_attrd_request(api, request);
     213             : 
     214           0 :     free_xml(request);
     215           0 :     return rc;
     216             : }
     217             : 
     218             : int
     219           0 : pcmk__attrd_api_delete(pcmk_ipc_api_t *api, const char *node, const char *name,
     220             :                        uint32_t options)
     221             : {
     222           0 :     const char *target = NULL;
     223             : 
     224           0 :     if (name == NULL) {
     225           0 :         return EINVAL;
     226             :     }
     227             : 
     228           0 :     target = pcmk__node_attr_target(node);
     229             : 
     230           0 :     if (target != NULL) {
     231           0 :         node = target;
     232             :     }
     233             : 
     234             :     /* Make sure the right update option is set. */
     235           0 :     options &= ~pcmk__node_attr_delay;
     236           0 :     options |= pcmk__node_attr_value;
     237             : 
     238           0 :     return pcmk__attrd_api_update(api, node, name, NULL, NULL, NULL, NULL, options);
     239             : }
     240             : 
     241             : int
     242           0 : pcmk__attrd_api_purge(pcmk_ipc_api_t *api, const char *node, bool reap)
     243             : {
     244           0 :     int rc = pcmk_rc_ok;
     245           0 :     xmlNode *request = NULL;
     246           0 :     const char *target = pcmk__node_attr_target(node);
     247             : 
     248           0 :     if (target != NULL) {
     249           0 :         node = target;
     250             :     }
     251             : 
     252           0 :     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           0 :     request = create_attrd_op(NULL);
     258             : 
     259           0 :     crm_xml_add(request, PCMK_XA_TASK, PCMK__ATTRD_CMD_PEER_REMOVE);
     260           0 :     pcmk__xe_set_bool_attr(request, PCMK__XA_REAP, reap);
     261           0 :     pcmk__xe_add_node(request, node, 0);
     262             : 
     263           0 :     rc = connect_and_send_attrd_request(api, request);
     264             : 
     265           0 :     free_xml(request);
     266           0 :     return rc;
     267             : }
     268             : 
     269             : int
     270           0 : pcmk__attrd_api_query(pcmk_ipc_api_t *api, const char *node, const char *name,
     271             :                       uint32_t options)
     272             : {
     273           0 :     int rc = pcmk_rc_ok;
     274           0 :     xmlNode *request = NULL;
     275           0 :     const char *target = NULL;
     276             : 
     277           0 :     if (name == NULL) {
     278           0 :         return EINVAL;
     279             :     }
     280             : 
     281           0 :     if (pcmk_is_set(options, pcmk__node_attr_query_all)) {
     282           0 :         node = NULL;
     283             :     } else {
     284           0 :         target = pcmk__node_attr_target(node);
     285             : 
     286           0 :         if (target != NULL) {
     287           0 :             node = target;
     288             :         }
     289             :     }
     290             : 
     291           0 :     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           0 :     request = create_attrd_op(NULL);
     296             : 
     297           0 :     crm_xml_add(request, PCMK__XA_ATTR_NAME, name);
     298           0 :     crm_xml_add(request, PCMK_XA_TASK, PCMK__ATTRD_CMD_QUERY);
     299           0 :     pcmk__xe_add_node(request, node, 0);
     300             : 
     301           0 :     rc = connect_and_send_attrd_request(api, request);
     302           0 :     free_xml(request);
     303           0 :     return rc;
     304             : }
     305             : 
     306             : int
     307           0 : pcmk__attrd_api_refresh(pcmk_ipc_api_t *api, const char *node)
     308             : {
     309           0 :     int rc = pcmk_rc_ok;
     310           0 :     xmlNode *request = NULL;
     311           0 :     const char *target = pcmk__node_attr_target(node);
     312             : 
     313           0 :     if (target != NULL) {
     314           0 :         node = target;
     315             :     }
     316             : 
     317           0 :     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           0 :     request = create_attrd_op(NULL);
     321             : 
     322           0 :     crm_xml_add(request, PCMK_XA_TASK, PCMK__ATTRD_CMD_REFRESH);
     323           0 :     pcmk__xe_add_node(request, node, 0);
     324             : 
     325           0 :     rc = connect_and_send_attrd_request(api, request);
     326             : 
     327           0 :     free_xml(request);
     328           0 :     return rc;
     329             : }
     330             : 
     331             : static void
     332           0 : add_op_attr(xmlNode *op, uint32_t options)
     333             : {
     334           0 :     if (pcmk_all_flags_set(options, pcmk__node_attr_value | pcmk__node_attr_delay)) {
     335           0 :         crm_xml_add(op, PCMK_XA_TASK, PCMK__ATTRD_CMD_UPDATE_BOTH);
     336           0 :     } else if (pcmk_is_set(options, pcmk__node_attr_value)) {
     337           0 :         crm_xml_add(op, PCMK_XA_TASK, PCMK__ATTRD_CMD_UPDATE);
     338           0 :     } else if (pcmk_is_set(options, pcmk__node_attr_delay)) {
     339           0 :         crm_xml_add(op, PCMK_XA_TASK, PCMK__ATTRD_CMD_UPDATE_DELAY);
     340             :     }
     341           0 : }
     342             : 
     343             : static void
     344           0 : populate_update_op(xmlNode *op, const char *node, const char *name, const char *value,
     345             :                    const char *dampen, const char *set, uint32_t options)
     346             : {
     347           0 :     if (pcmk_is_set(options, pcmk__node_attr_pattern)) {
     348           0 :         crm_xml_add(op, PCMK__XA_ATTR_REGEX, name);
     349             :     } else {
     350           0 :         crm_xml_add(op, PCMK__XA_ATTR_NAME, name);
     351             :     }
     352             : 
     353           0 :     if (pcmk_is_set(options, pcmk__node_attr_utilization)) {
     354           0 :         crm_xml_add(op, PCMK__XA_ATTR_SET_TYPE, PCMK_XE_UTILIZATION);
     355             :     } else {
     356           0 :         crm_xml_add(op, PCMK__XA_ATTR_SET_TYPE, PCMK_XE_INSTANCE_ATTRIBUTES);
     357             :     }
     358             : 
     359           0 :     add_op_attr(op, options);
     360             : 
     361           0 :     crm_xml_add(op, PCMK__XA_ATTR_VALUE, value);
     362           0 :     crm_xml_add(op, PCMK__XA_ATTR_DAMPENING, dampen);
     363           0 :     pcmk__xe_add_node(op, node, 0);
     364           0 :     crm_xml_add(op, PCMK__XA_ATTR_SET, set);
     365           0 :     crm_xml_add_int(op, PCMK__XA_ATTR_IS_REMOTE,
     366           0 :                     pcmk_is_set(options, pcmk__node_attr_remote));
     367           0 :     crm_xml_add_int(op, PCMK__XA_ATTR_IS_PRIVATE,
     368           0 :                     pcmk_is_set(options, pcmk__node_attr_private));
     369             : 
     370           0 :     if (pcmk_is_set(options, pcmk__node_attr_sync_local)) {
     371           0 :         crm_xml_add(op, PCMK__XA_ATTR_SYNC_POINT, PCMK__VALUE_LOCAL);
     372           0 :     } else if (pcmk_is_set(options, pcmk__node_attr_sync_cluster)) {
     373           0 :         crm_xml_add(op, PCMK__XA_ATTR_SYNC_POINT, PCMK__VALUE_CLUSTER);
     374             :     }
     375           0 : }
     376             : 
     377             : int
     378           0 : pcmk__attrd_api_update(pcmk_ipc_api_t *api, const char *node, const char *name,
     379             :                        const char *value, const char *dampen, const char *set,
     380             :                        const char *user_name, uint32_t options)
     381             : {
     382           0 :     int rc = pcmk_rc_ok;
     383           0 :     xmlNode *request = NULL;
     384           0 :     const char *target = NULL;
     385             : 
     386           0 :     if (name == NULL) {
     387           0 :         return EINVAL;
     388             :     }
     389             : 
     390           0 :     target = pcmk__node_attr_target(node);
     391             : 
     392           0 :     if (target != NULL) {
     393           0 :         node = target;
     394             :     }
     395             : 
     396           0 :     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           0 :     request = create_attrd_op(user_name);
     401           0 :     populate_update_op(request, node, name, value, dampen, set, options);
     402             : 
     403           0 :     rc = connect_and_send_attrd_request(api, request);
     404             : 
     405           0 :     free_xml(request);
     406           0 :     return rc;
     407             : }
     408             : 
     409             : int
     410           0 : pcmk__attrd_api_update_list(pcmk_ipc_api_t *api, GList *attrs, const char *dampen,
     411             :                             const char *set, const char *user_name,
     412             :                             uint32_t options)
     413             : {
     414           0 :     int rc = pcmk_rc_ok;
     415           0 :     xmlNode *request = NULL;
     416             : 
     417           0 :     if (attrs == NULL) {
     418           0 :         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           0 :     for (GList *iter = attrs; iter != NULL; iter = iter->next) {
     444           0 :         pcmk__attrd_query_pair_t *pair = (pcmk__attrd_query_pair_t *) iter->data;
     445             : 
     446           0 :         if (pcmk__is_daemon) {
     447           0 :             const char *target = NULL;
     448           0 :             xmlNode *child = NULL;
     449             : 
     450             :             /* First time through this loop - create the basic request. */
     451           0 :             if (request == NULL) {
     452           0 :                 request = create_attrd_op(user_name);
     453           0 :                 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           0 :             child = pcmk__xe_create(request, PCMK_XE_OP);
     462           0 :             target = pcmk__node_attr_target(pair->node);
     463             : 
     464           0 :             if (target != NULL) {
     465           0 :                 pair->node = target;
     466             :             }
     467             : 
     468           0 :             populate_update_op(child, pair->node, pair->name, pair->value, dampen,
     469             :                                set, options);
     470             :         } else {
     471           0 :             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           0 :     if (pcmk__is_daemon) {
     480           0 :         rc = connect_and_send_attrd_request(api, request);
     481           0 :         free_xml(request);
     482             :     }
     483             : 
     484           0 :     return rc;
     485             : }

Generated by: LCOV version 1.14