root/lib/common/ipc_controld.c

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

DEFINITIONS

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

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

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