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

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