root/lib/common/ipc_controld.c

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

DEFINITIONS

This source file includes following definitions.
  1. pcmk__controld_api_reply2str
  2. new_data
  3. free_data
  4. post_connect
  5. set_node_info_data
  6. set_ping_data
  7. set_nodes_data
  8. reply_expected
  9. dispatch
  10. pcmk__controld_api_methods
  11. create_controller_request
  12. send_controller_request
  13. create_reprobe_message_data
  14. pcmk_controld_api_reprobe
  15. pcmk_controld_api_node_info
  16. pcmk_controld_api_ping
  17. pcmk_controld_api_list_nodes
  18. controller_resource_op
  19. pcmk_controld_api_fail
  20. pcmk_controld_api_refresh
  21. pcmk_controld_api_replies_expected
  22. create_hello_message

   1 /*
   2  * Copyright 2020-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 #include <crm_internal.h>
  11 
  12 #include <stdio.h>
  13 #include <stdbool.h>
  14 #include <errno.h>
  15 #include <libxml/tree.h>
  16 
  17 #include <crm/crm.h>
  18 #include <crm/msg_xml.h>
  19 #include <crm/common/xml.h>
  20 #include <crm/common/ipc.h>
  21 #include <crm/common/ipc_internal.h>
  22 #include <crm/common/ipc_controld.h>
  23 #include "crmcommon_private.h"
  24 
  25 struct controld_api_private_s {
  26     char *client_uuid;
  27     unsigned int replies_expected;
  28 };
  29 
  30 /*!
  31  * \internal
  32  * \brief Get a string representation of a controller API reply type
  33  *
  34  * \param[in] reply  Controller API reply type
  35  *
  36  * \return String representation of a controller API reply type
  37  */
  38 const char *
  39 pcmk__controld_api_reply2str(enum pcmk_controld_api_reply reply)
     /* [previous][next][first][last][top][bottom][index][help] */
  40 {
  41     switch (reply) {
  42         case pcmk_controld_reply_reprobe:
  43             return "reprobe";
  44         case pcmk_controld_reply_info:
  45             return "info";
  46         case pcmk_controld_reply_resource:
  47             return "resource";
  48         case pcmk_controld_reply_ping:
  49             return "ping";
  50         case pcmk_controld_reply_nodes:
  51             return "nodes";
  52         default:
  53             return "unknown";
  54     }
  55 }
  56 
  57 // \return Standard Pacemaker return code
  58 static int
  59 new_data(pcmk_ipc_api_t *api)
     /* [previous][next][first][last][top][bottom][index][help] */
  60 {
  61     struct controld_api_private_s *private = NULL;
  62 
  63     api->api_data = calloc(1, sizeof(struct controld_api_private_s));
  64 
  65     if (api->api_data == NULL) {
  66         return errno;
  67     }
  68 
  69     private = api->api_data;
  70 
  71     /* This is set to the PID because that's how it was always done, but PIDs
  72      * are not unique because clients can be remote. The value appears to be
  73      * unused other than as part of F_CRM_SYS_FROM in IPC requests, which is
  74      * only compared against the internal system names (CRM_SYSTEM_TENGINE,
  75      * etc.), so it shouldn't be a problem.
  76      */
  77     private->client_uuid = pcmk__getpid_s();
  78 
  79     /* @TODO Implement a call ID model similar to the CIB, executor, and fencer
  80      *       IPC APIs, so that requests and replies can be matched, and
  81      *       duplicate replies can be discarded.
  82      */
  83     return pcmk_rc_ok;
  84 }
  85 
  86 static void
  87 free_data(void *data)
     /* [previous][next][first][last][top][bottom][index][help] */
  88 {
  89     free(((struct controld_api_private_s *) data)->client_uuid);
  90     free(data);
  91 }
  92 
  93 // \return Standard Pacemaker return code
  94 static int
  95 post_connect(pcmk_ipc_api_t *api)
     /* [previous][next][first][last][top][bottom][index][help] */
  96 {
  97     /* The controller currently requires clients to register via a hello
  98      * request, but does not reply back.
  99      */
 100     struct controld_api_private_s *private = api->api_data;
 101     const char *client_name = crm_system_name? crm_system_name : "client";
 102     xmlNode *hello;
 103     int rc;
 104 
 105     hello = create_hello_message(private->client_uuid, client_name,
 106                                  PCMK__CONTROLD_API_MAJOR,
 107                                  PCMK__CONTROLD_API_MINOR);
 108     rc = pcmk__send_ipc_request(api, hello);
 109     free_xml(hello);
 110     if (rc != pcmk_rc_ok) {
 111         crm_info("Could not send IPC hello to %s: %s " CRM_XS " rc=%s",
 112                  pcmk_ipc_name(api, true), pcmk_rc_str(rc), rc);
 113     } else {
 114         crm_debug("Sent IPC hello to %s", pcmk_ipc_name(api, true));
 115     }
 116     return rc;
 117 }
 118 
 119 static void
 120 set_node_info_data(pcmk_controld_api_reply_t *data, xmlNode *msg_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 121 {
 122     data->reply_type = pcmk_controld_reply_info;
 123     if (msg_data == NULL) {
 124         return;
 125     }
 126     data->data.node_info.have_quorum = pcmk__xe_attr_is_true(msg_data, XML_ATTR_HAVE_QUORUM);
 127     data->data.node_info.is_remote = pcmk__xe_attr_is_true(msg_data, XML_NODE_IS_REMOTE);
 128 
 129     /* Integer node_info.id is currently valid only for Corosync nodes.
 130      *
 131      * @TODO: Improve handling after crm_node_t is refactored to handle layer-
 132      * specific data better.
 133      */
 134     crm_element_value_int(msg_data, XML_ATTR_ID, &(data->data.node_info.id));
 135 
 136     data->data.node_info.uuid = crm_element_value(msg_data, XML_ATTR_ID);
 137     data->data.node_info.uname = crm_element_value(msg_data, XML_ATTR_UNAME);
 138     data->data.node_info.state = crm_element_value(msg_data, XML_NODE_IS_PEER);
 139 }
 140 
 141 static void
 142 set_ping_data(pcmk_controld_api_reply_t *data, xmlNode *msg_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 143 {
 144     data->reply_type = pcmk_controld_reply_ping;
 145     if (msg_data == NULL) {
 146         return;
 147     }
 148     data->data.ping.sys_from = crm_element_value(msg_data,
 149                                                  XML_PING_ATTR_SYSFROM);
 150     data->data.ping.fsa_state = crm_element_value(msg_data,
 151                                                   XML_PING_ATTR_CRMDSTATE);
 152     data->data.ping.result = crm_element_value(msg_data, XML_PING_ATTR_STATUS);
 153 }
 154 
 155 static void
 156 set_nodes_data(pcmk_controld_api_reply_t *data, xmlNode *msg_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 157 {
 158     pcmk_controld_api_node_t *node_info;
 159 
 160     data->reply_type = pcmk_controld_reply_nodes;
 161     for (xmlNode *node = first_named_child(msg_data, XML_CIB_TAG_NODE);
 162          node != NULL; node = crm_next_same_xml(node)) {
 163 
 164         long long id_ll = 0;
 165 
 166         node_info = calloc(1, sizeof(pcmk_controld_api_node_t));
 167         crm_element_value_ll(node, XML_ATTR_ID, &id_ll);
 168         if (id_ll > 0) {
 169             node_info->id = id_ll;
 170         }
 171         node_info->uname = crm_element_value(node, XML_ATTR_UNAME);
 172         node_info->state = crm_element_value(node, XML_NODE_IN_CLUSTER);
 173         data->data.nodes = g_list_prepend(data->data.nodes, node_info);
 174     }
 175 }
 176 
 177 static bool
 178 reply_expected(pcmk_ipc_api_t *api, xmlNode *request)
     /* [previous][next][first][last][top][bottom][index][help] */
 179 {
 180     const char *command = crm_element_value(request, F_CRM_TASK);
 181 
 182     if (command == NULL) {
 183         return false;
 184     }
 185 
 186     // We only need to handle commands that functions in this file can send
 187     return !strcmp(command, CRM_OP_REPROBE)
 188            || !strcmp(command, CRM_OP_NODE_INFO)
 189            || !strcmp(command, CRM_OP_PING)
 190            || !strcmp(command, CRM_OP_LRM_FAIL)
 191            || !strcmp(command, CRM_OP_LRM_DELETE);
 192 }
 193 
 194 static bool
 195 dispatch(pcmk_ipc_api_t *api, xmlNode *reply)
     /* [previous][next][first][last][top][bottom][index][help] */
 196 {
 197     struct controld_api_private_s *private = api->api_data;
 198     crm_exit_t status = CRM_EX_OK;
 199     xmlNode *msg_data = NULL;
 200     const char *value = NULL;
 201     pcmk_controld_api_reply_t reply_data = {
 202         pcmk_controld_reply_unknown, NULL, NULL,
 203     };
 204 
 205     /* If we got an ACK, return true so the caller knows to expect more responses
 206      * from the IPC server.  We do this before decrementing replies_expected because
 207      * ACKs are not going to be included in that value.
 208      *
 209      * Note that we cannot do the same kind of status checking here that we do in
 210      * ipc_pacemakerd.c.  The ACK message we receive does not necessarily contain
 211      * a status attribute.  That is, we may receive this:
 212      *
 213      * <ack function="crmd_remote_proxy_cb" line="556"/>
 214      *
 215      * Instead of this:
 216      *
 217      * <ack function="dispatch_controller_ipc" line="391" status="112"/>
 218      */
 219     if (pcmk__str_eq(crm_element_name(reply), "ack", pcmk__str_none)) {
 220         return true; // More replies needed
 221     }
 222 
 223     if (private->replies_expected > 0) {
 224         private->replies_expected--;
 225     }
 226 
 227     // Do some basic validation of the reply
 228 
 229     /* @TODO We should be able to verify that value is always a response, but
 230      *       currently the controller doesn't always properly set the type. Even
 231      *       if we fix the controller, we'll still need to handle replies from
 232      *       old versions (feature set could be used to differentiate).
 233      */
 234     value = crm_element_value(reply, F_CRM_MSG_TYPE);
 235     if (pcmk__str_empty(value)
 236         || !pcmk__str_any_of(value, XML_ATTR_REQUEST, XML_ATTR_RESPONSE, NULL)) {
 237         crm_info("Unrecognizable message from controller: "
 238                  "invalid message type '%s'", pcmk__s(value, ""));
 239         status = CRM_EX_PROTOCOL;
 240         goto done;
 241     }
 242 
 243     if (pcmk__str_empty(crm_element_value(reply, XML_ATTR_REFERENCE))) {
 244         crm_info("Unrecognizable message from controller: no reference");
 245         status = CRM_EX_PROTOCOL;
 246         goto done;
 247     }
 248 
 249     value = crm_element_value(reply, F_CRM_TASK);
 250     if (pcmk__str_empty(value)) {
 251         crm_info("Unrecognizable message from controller: no command name");
 252         status = CRM_EX_PROTOCOL;
 253         goto done;
 254     }
 255 
 256     // Parse useful info from reply
 257 
 258     reply_data.feature_set = crm_element_value(reply, XML_ATTR_VERSION);
 259     reply_data.host_from = crm_element_value(reply, F_CRM_HOST_FROM);
 260     msg_data = get_message_xml(reply, F_CRM_DATA);
 261 
 262     if (!strcmp(value, CRM_OP_REPROBE)) {
 263         reply_data.reply_type = pcmk_controld_reply_reprobe;
 264 
 265     } else if (!strcmp(value, CRM_OP_NODE_INFO)) {
 266         set_node_info_data(&reply_data, msg_data);
 267 
 268     } else if (!strcmp(value, CRM_OP_INVOKE_LRM)) {
 269         reply_data.reply_type = pcmk_controld_reply_resource;
 270         reply_data.data.resource.node_state = msg_data;
 271 
 272     } else if (!strcmp(value, CRM_OP_PING)) {
 273         set_ping_data(&reply_data, msg_data);
 274 
 275     } else if (!strcmp(value, PCMK__CONTROLD_CMD_NODES)) {
 276         set_nodes_data(&reply_data, msg_data);
 277 
 278     } else {
 279         crm_info("Unrecognizable message from controller: unknown command '%s'",
 280                  value);
 281         status = CRM_EX_PROTOCOL;
 282     }
 283 
 284 done:
 285     pcmk__call_ipc_callback(api, pcmk_ipc_event_reply, status, &reply_data);
 286 
 287     // Free any reply data that was allocated
 288     if (pcmk__str_eq(value, PCMK__CONTROLD_CMD_NODES, pcmk__str_casei)) {
 289         g_list_free_full(reply_data.data.nodes, free);
 290     }
 291 
 292     return false; // No further replies needed
 293 }
 294 
 295 pcmk__ipc_methods_t *
 296 pcmk__controld_api_methods(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 297 {
 298     pcmk__ipc_methods_t *cmds = calloc(1, sizeof(pcmk__ipc_methods_t));
 299 
 300     if (cmds != NULL) {
 301         cmds->new_data = new_data;
 302         cmds->free_data = free_data;
 303         cmds->post_connect = post_connect;
 304         cmds->reply_expected = reply_expected;
 305         cmds->dispatch = dispatch;
 306     }
 307     return cmds;
 308 }
 309 
 310 /*!
 311  * \internal
 312  * \brief Create XML for a controller IPC request
 313  *
 314  * \param[in] api       Controller connection
 315  * \param[in] op        Controller IPC command name
 316  * \param[in] node      Node name to set as destination host
 317  * \param[in] msg_data  XML to attach to request as message data
 318  *
 319  * \return Newly allocated XML for request
 320  */
 321 static xmlNode *
 322 create_controller_request(const pcmk_ipc_api_t *api, const char *op,
     /* [previous][next][first][last][top][bottom][index][help] */
 323                           const char *node, xmlNode *msg_data)
 324 {
 325     struct controld_api_private_s *private = NULL;
 326     const char *sys_to = NULL;
 327 
 328     if (api == NULL) {
 329         return NULL;
 330     }
 331     private = api->api_data;
 332     if ((node == NULL) && !strcmp(op, CRM_OP_PING)) {
 333         sys_to = CRM_SYSTEM_DC;
 334     } else {
 335         sys_to = CRM_SYSTEM_CRMD;
 336     }
 337     return create_request(op, msg_data, node, sys_to,
 338                           (crm_system_name? crm_system_name : "client"),
 339                           private->client_uuid);
 340 }
 341 
 342 // \return Standard Pacemaker return code
 343 static int
 344 send_controller_request(pcmk_ipc_api_t *api, xmlNode *request,
     /* [previous][next][first][last][top][bottom][index][help] */
 345                         bool reply_is_expected)
 346 {
 347     int rc;
 348 
 349     if (crm_element_value(request, XML_ATTR_REFERENCE) == NULL) {
 350         return EINVAL;
 351     }
 352     rc = pcmk__send_ipc_request(api, request);
 353     if ((rc == pcmk_rc_ok) && reply_is_expected) {
 354         struct controld_api_private_s *private = api->api_data;
 355 
 356         private->replies_expected++;
 357     }
 358     return rc;
 359 }
 360 
 361 static xmlNode *
 362 create_reprobe_message_data(const char *target_node, const char *router_node)
     /* [previous][next][first][last][top][bottom][index][help] */
 363 {
 364     xmlNode *msg_data;
 365 
 366     msg_data = create_xml_node(NULL, "data_for_" CRM_OP_REPROBE);
 367     crm_xml_add(msg_data, XML_LRM_ATTR_TARGET, target_node);
 368     if ((router_node != NULL) && !pcmk__str_eq(router_node, target_node, pcmk__str_casei)) {
 369         crm_xml_add(msg_data, XML_LRM_ATTR_ROUTER_NODE, router_node);
 370     }
 371     return msg_data;
 372 }
 373 
 374 /*!
 375  * \brief Send a reprobe controller operation
 376  *
 377  * \param[in,out] api          Controller connection
 378  * \param[in]     target_node  Name of node to reprobe
 379  * \param[in]     router_node  Router node for host
 380  *
 381  * \return Standard Pacemaker return code
 382  * \note Event callback will get a reply of type pcmk_controld_reply_reprobe.
 383  */
 384 int
 385 pcmk_controld_api_reprobe(pcmk_ipc_api_t *api, const char *target_node,
     /* [previous][next][first][last][top][bottom][index][help] */
 386                           const char *router_node)
 387 {
 388     xmlNode *request;
 389     xmlNode *msg_data;
 390     int rc = pcmk_rc_ok;
 391 
 392     if (api == NULL) {
 393         return EINVAL;
 394     }
 395     if (router_node == NULL) {
 396         router_node = target_node;
 397     }
 398     crm_debug("Sending %s IPC request to reprobe %s via %s",
 399               pcmk_ipc_name(api, true), pcmk__s(target_node, "local node"),
 400               pcmk__s(router_node, "local node"));
 401     msg_data = create_reprobe_message_data(target_node, router_node);
 402     request = create_controller_request(api, CRM_OP_REPROBE, router_node,
 403                                         msg_data);
 404     rc = send_controller_request(api, request, true);
 405     free_xml(msg_data);
 406     free_xml(request);
 407     return rc;
 408 }
 409 
 410 /*!
 411  * \brief Send a "node info" controller operation
 412  *
 413  * \param[in,out] api     Controller connection
 414  * \param[in]     nodeid  ID of node to get info for (or 0 for local node)
 415  *
 416  * \return Standard Pacemaker return code
 417  * \note Event callback will get a reply of type pcmk_controld_reply_info.
 418  */
 419 int
 420 pcmk_controld_api_node_info(pcmk_ipc_api_t *api, uint32_t nodeid)
     /* [previous][next][first][last][top][bottom][index][help] */
 421 {
 422     xmlNode *request;
 423     int rc = pcmk_rc_ok;
 424 
 425     request = create_controller_request(api, CRM_OP_NODE_INFO, NULL, NULL);
 426     if (request == NULL) {
 427         return EINVAL;
 428     }
 429     if (nodeid > 0) {
 430         crm_xml_set_id(request, "%lu", (unsigned long) nodeid);
 431     }
 432 
 433     rc = send_controller_request(api, request, true);
 434     free_xml(request);
 435     return rc;
 436 }
 437 
 438 /*!
 439  * \brief Ask the controller for status
 440  *
 441  * \param[in,out] api        Controller connection
 442  * \param[in]     node_name  Name of node whose status is desired (NULL for DC)
 443  *
 444  * \return Standard Pacemaker return code
 445  * \note Event callback will get a reply of type pcmk_controld_reply_ping.
 446  */
 447 int
 448 pcmk_controld_api_ping(pcmk_ipc_api_t *api, const char *node_name)
     /* [previous][next][first][last][top][bottom][index][help] */
 449 {
 450     xmlNode *request;
 451     int rc = pcmk_rc_ok;
 452 
 453     request = create_controller_request(api, CRM_OP_PING, node_name, NULL);
 454     if (request == NULL) {
 455         return EINVAL;
 456     }
 457     rc = send_controller_request(api, request, true);
 458     free_xml(request);
 459     return rc;
 460 }
 461 
 462 /*!
 463  * \brief Ask the controller for cluster information
 464  *
 465  * \param[in,out] api  Controller connection
 466  *
 467  * \return Standard Pacemaker return code
 468  * \note Event callback will get a reply of type pcmk_controld_reply_nodes.
 469  */
 470 int
 471 pcmk_controld_api_list_nodes(pcmk_ipc_api_t *api)
     /* [previous][next][first][last][top][bottom][index][help] */
 472 {
 473     xmlNode *request;
 474     int rc = EINVAL;
 475 
 476     request = create_controller_request(api, PCMK__CONTROLD_CMD_NODES, NULL,
 477                                         NULL);
 478     if (request != NULL) {
 479         rc = send_controller_request(api, request, true);
 480         free_xml(request);
 481     }
 482     return rc;
 483 }
 484 
 485 // \return Standard Pacemaker return code
 486 static int
 487 controller_resource_op(pcmk_ipc_api_t *api, const char *op,
     /* [previous][next][first][last][top][bottom][index][help] */
 488                        const char *target_node, const char *router_node,
 489                        bool cib_only, const char *rsc_id,
 490                        const char *rsc_long_id, const char *standard,
 491                        const char *provider, const char *type)
 492 {
 493     int rc = pcmk_rc_ok;
 494     char *key;
 495     xmlNode *request, *msg_data, *xml_rsc, *params;
 496 
 497     if (api == NULL) {
 498         return EINVAL;
 499     }
 500     if (router_node == NULL) {
 501         router_node = target_node;
 502     }
 503 
 504     msg_data = create_xml_node(NULL, XML_GRAPH_TAG_RSC_OP);
 505 
 506     /* The controller logs the transition key from resource op requests, so we
 507      * need to have *something* for it.
 508      * @TODO don't use "crm-resource"
 509      */
 510     key = pcmk__transition_key(0, getpid(), 0,
 511                                "xxxxxxxx-xrsc-opxx-xcrm-resourcexxxx");
 512     crm_xml_add(msg_data, XML_ATTR_TRANSITION_KEY, key);
 513     free(key);
 514 
 515     crm_xml_add(msg_data, XML_LRM_ATTR_TARGET, target_node);
 516     if (!pcmk__str_eq(router_node, target_node, pcmk__str_casei)) {
 517         crm_xml_add(msg_data, XML_LRM_ATTR_ROUTER_NODE, router_node);
 518     }
 519 
 520     if (cib_only) {
 521         // Indicate that only the CIB needs to be cleaned
 522         crm_xml_add(msg_data, PCMK__XA_MODE, XML_TAG_CIB);
 523     }
 524 
 525     xml_rsc = create_xml_node(msg_data, XML_CIB_TAG_RESOURCE);
 526     crm_xml_add(xml_rsc, XML_ATTR_ID, rsc_id);
 527     crm_xml_add(xml_rsc, XML_ATTR_ID_LONG, rsc_long_id);
 528     crm_xml_add(xml_rsc, XML_AGENT_ATTR_CLASS, standard);
 529     crm_xml_add(xml_rsc, XML_AGENT_ATTR_PROVIDER, provider);
 530     crm_xml_add(xml_rsc, XML_ATTR_TYPE, type);
 531 
 532     params = create_xml_node(msg_data, XML_TAG_ATTRS);
 533     crm_xml_add(params, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET);
 534 
 535     // The controller parses the timeout from the request
 536     key = crm_meta_name(XML_ATTR_TIMEOUT);
 537     crm_xml_add(params, key, "60000");  /* 1 minute */ //@TODO pass as arg
 538     free(key);
 539 
 540     request = create_controller_request(api, op, router_node, msg_data);
 541     rc = send_controller_request(api, request, true);
 542     free_xml(msg_data);
 543     free_xml(request);
 544     return rc;
 545 }
 546 
 547 /*!
 548  * \brief Ask the controller to fail a resource
 549  *
 550  * \param[in,out] api          Controller connection
 551  * \param[in]     target_node  Name of node resource is on
 552  * \param[in]     router_node  Router node for target
 553  * \param[in]     rsc_id       ID of resource to fail
 554  * \param[in]     rsc_long_id  Long ID of resource (if any)
 555  * \param[in]     standard     Standard of resource
 556  * \param[in]     provider     Provider of resource (if any)
 557  * \param[in]     type         Type of resource to fail
 558  *
 559  * \return Standard Pacemaker return code
 560  * \note Event callback will get a reply of type pcmk_controld_reply_resource.
 561  */
 562 int
 563 pcmk_controld_api_fail(pcmk_ipc_api_t *api,
     /* [previous][next][first][last][top][bottom][index][help] */
 564                        const char *target_node, const char *router_node,
 565                        const char *rsc_id, const char *rsc_long_id,
 566                        const char *standard, const char *provider,
 567                        const char *type)
 568 {
 569     crm_debug("Sending %s IPC request to fail %s (a.k.a. %s) on %s via %s",
 570               pcmk_ipc_name(api, true), pcmk__s(rsc_id, "unknown resource"),
 571               pcmk__s(rsc_long_id, "no other names"),
 572               pcmk__s(target_node, "unspecified node"),
 573               pcmk__s(router_node, "unspecified node"));
 574     return controller_resource_op(api, CRM_OP_LRM_FAIL, target_node,
 575                                   router_node, false, rsc_id, rsc_long_id,
 576                                   standard, provider, type);
 577 }
 578 
 579 /*!
 580  * \brief Ask the controller to refresh a resource
 581  *
 582  * \param[in,out] api          Controller connection
 583  * \param[in]     target_node  Name of node resource is on
 584  * \param[in]     router_node  Router node for target
 585  * \param[in]     rsc_id       ID of resource to refresh
 586  * \param[in]     rsc_long_id  Long ID of resource (if any)
 587  * \param[in]     standard     Standard of resource
 588  * \param[in]     provider     Provider of resource (if any)
 589  * \param[in]     type         Type of resource
 590  * \param[in]     cib_only     If true, clean resource from CIB only
 591  *
 592  * \return Standard Pacemaker return code
 593  * \note Event callback will get a reply of type pcmk_controld_reply_resource.
 594  */
 595 int
 596 pcmk_controld_api_refresh(pcmk_ipc_api_t *api, const char *target_node,
     /* [previous][next][first][last][top][bottom][index][help] */
 597                           const char *router_node,
 598                           const char *rsc_id, const char *rsc_long_id,
 599                           const char *standard, const char *provider,
 600                           const char *type, bool cib_only)
 601 {
 602     crm_debug("Sending %s IPC request to refresh %s (a.k.a. %s) on %s via %s",
 603               pcmk_ipc_name(api, true), pcmk__s(rsc_id, "unknown resource"),
 604               pcmk__s(rsc_long_id, "no other names"),
 605               pcmk__s(target_node, "unspecified node"),
 606               pcmk__s(router_node, "unspecified node"));
 607     return controller_resource_op(api, CRM_OP_LRM_DELETE, target_node,
 608                                   router_node, cib_only, rsc_id, rsc_long_id,
 609                                   standard, provider, type);
 610 }
 611 
 612 /*!
 613  * \brief Get the number of IPC replies currently expected from the controller
 614  *
 615  * \param[in] api  Controller IPC API connection
 616  *
 617  * \return Number of replies expected
 618  */
 619 unsigned int
 620 pcmk_controld_api_replies_expected(const pcmk_ipc_api_t *api)
     /* [previous][next][first][last][top][bottom][index][help] */
 621 {
 622     struct controld_api_private_s *private = api->api_data;
 623 
 624     return private->replies_expected;
 625 }
 626 
 627 /*!
 628  * \brief Create XML for a controller IPC "hello" message
 629  *
 630  * \deprecated This function is deprecated as part of the public C API.
 631  */
 632 // \todo make this static to this file when breaking API backward compatibility
 633 xmlNode *
 634 create_hello_message(const char *uuid, const char *client_name,
     /* [previous][next][first][last][top][bottom][index][help] */
 635                      const char *major_version, const char *minor_version)
 636 {
 637     xmlNode *hello_node = NULL;
 638     xmlNode *hello = NULL;
 639 
 640     if (pcmk__str_empty(uuid) || pcmk__str_empty(client_name)
 641         || pcmk__str_empty(major_version) || pcmk__str_empty(minor_version)) {
 642         crm_err("Could not create IPC hello message from %s (UUID %s): "
 643                 "missing information",
 644                 client_name? client_name : "unknown client",
 645                 uuid? uuid : "unknown");
 646         return NULL;
 647     }
 648 
 649     hello_node = create_xml_node(NULL, XML_TAG_OPTIONS);
 650     if (hello_node == NULL) {
 651         crm_err("Could not create IPC hello message from %s (UUID %s): "
 652                 "Message data creation failed", client_name, uuid);
 653         return NULL;
 654     }
 655 
 656     crm_xml_add(hello_node, "major_version", major_version);
 657     crm_xml_add(hello_node, "minor_version", minor_version);
 658     crm_xml_add(hello_node, "client_name", client_name);
 659     crm_xml_add(hello_node, "client_uuid", uuid);
 660 
 661     hello = create_request(CRM_OP_HELLO, hello_node, NULL, NULL, client_name, uuid);
 662     if (hello == NULL) {
 663         crm_err("Could not create IPC hello message from %s (UUID %s): "
 664                 "Request creation failed", client_name, uuid);
 665         return NULL;
 666     }
 667     free_xml(hello_node);
 668 
 669     crm_trace("Created hello message from %s (UUID %s)", client_name, uuid);
 670     return hello;
 671 }

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