root/tools/attrd_updater.c

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

DEFINITIONS

This source file includes following definitions.
  1. main
  2. send_attrd_query
  3. validate_attrd_reply
  4. print_attrd_values
  5. do_query
  6. do_update

   1 /*
   2  * Copyright 2004-2020 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 General Public License version 2
   7  * or later (GPLv2+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #include <crm_internal.h>
  11 
  12 #include <stdio.h>
  13 #include <unistd.h>
  14 #include <stdlib.h>
  15 #include <libgen.h>
  16 
  17 #include <sys/param.h>
  18 #include <sys/types.h>
  19 
  20 #include <crm/crm.h>
  21 #include <crm/msg_xml.h>
  22 #include <crm/common/xml_internal.h>
  23 #include <crm/common/ipc.h>
  24 
  25 #include <crm/common/attrd_internal.h>
  26 
  27 static pcmk__cli_option_t long_options[] = {
  28     // long option, argument type, storage, short option, description, flags
  29     {
  30         "help", no_argument, NULL, '?',
  31         "\tThis text", pcmk__option_default
  32     },
  33     {
  34         "version", no_argument, NULL, '$',
  35         "\tVersion information", pcmk__option_default
  36     },
  37     {
  38         "verbose", no_argument, NULL, 'V',
  39         "\tIncrease debug output\n", pcmk__option_default
  40     },
  41     {
  42         "name", required_argument, NULL, 'n',
  43         "The attribute's name", pcmk__option_default
  44     },
  45     {
  46         "-spacer-", no_argument, NULL, '-',
  47         "\nCommands:", pcmk__option_default
  48     },
  49     {
  50         "update", required_argument, NULL, 'U',
  51         "Update attribute's value in pacemaker-attrd. If this causes the value "
  52             "to change, it will also be updated in the cluster configuration.",
  53         pcmk__option_default
  54     },
  55     {
  56         "update-both", required_argument, NULL, 'B',
  57         "Update attribute's value and time to wait (dampening) in "
  58             "pacemaker-attrd. If this causes the value or dampening to change, "
  59             "the attribute will also be written to the cluster configuration, "
  60             "so be aware that repeatedly changing the dampening reduces its "
  61             "effectiveness.",
  62         pcmk__option_default
  63     },
  64     {
  65         "update-delay", no_argument, NULL, 'Y',
  66         "Update attribute's dampening in pacemaker-attrd (requires "
  67             "-d/--delay). If this causes the dampening to change, the "
  68             "attribute will also be written to the cluster configuration, so "
  69             "be aware that repeatedly changing the dampening reduces its "
  70             "effectiveness.",
  71         pcmk__option_default
  72     },
  73     {
  74         "query", no_argument, NULL, 'Q',
  75         "\tQuery the attribute's value from pacemaker-attrd",
  76         pcmk__option_default
  77     },
  78     {
  79         "delete", no_argument, NULL, 'D',
  80         "\tDelete attribute from pacemaker-attrd. If a value was previously "
  81             "set, it will also be removed from the cluster configuration",
  82         pcmk__option_default
  83     },
  84     {
  85         "refresh", no_argument, NULL, 'R',
  86         "\t(Advanced) Force the pacemaker-attrd daemon to resend all current "
  87             "values to the CIB",
  88         pcmk__option_default
  89     },
  90 
  91     {
  92         "-spacer-", no_argument, NULL, '-',
  93         "\nAdditional options:", pcmk__option_default
  94     },
  95     {
  96         "delay", required_argument, NULL, 'd',
  97         "The time to wait (dampening) in seconds for further changes "
  98             "before writing",
  99         pcmk__option_default
 100     },
 101     {
 102         "set", required_argument, NULL, 's',
 103         "(Advanced) The attribute set in which to place the value",
 104         pcmk__option_default
 105     },
 106     {
 107         "node", required_argument, NULL, 'N',
 108         "Set the attribute for the named node (instead of the local one)",
 109         pcmk__option_default
 110     },
 111     {
 112         "all", no_argument, NULL, 'A',
 113         "Show values of the attribute for all nodes (query only)",
 114         pcmk__option_default
 115     },
 116 
 117     // @TODO Implement --lifetime
 118     {
 119         "lifetime", required_argument, NULL, 'l',
 120         "(Not yet implemented) Lifetime of the node attribute (silently "
 121             "ignored by cluster)",
 122         pcmk__option_default
 123     },
 124     {
 125         "private", no_argument, NULL, 'p',
 126         "\tIf this creates a new attribute, never write the attribute to CIB",
 127         pcmk__option_default
 128     },
 129 
 130     /* Legacy options */
 131     {
 132         "quiet", no_argument, NULL, 'q',
 133         NULL, pcmk__option_hidden
 134     },
 135     {
 136         "update", required_argument, NULL, 'v',
 137         NULL, pcmk__option_hidden
 138     },
 139     {
 140         "section", required_argument, NULL, 'S',
 141         NULL, pcmk__option_hidden
 142     },
 143     { 0, 0, 0, 0 }
 144 };
 145 
 146 static int do_query(const char *attr_name, const char *attr_node, gboolean query_all);
 147 static int do_update(char command, const char *attr_node, const char *attr_name,
 148                      const char *attr_value, const char *attr_section,
 149                      const char *attr_set, const char *attr_dampen, int attr_options);
 150 
 151 // Free memory at exit to make analyzers happy
 152 #define cleanup_memory() \
 153     free(attr_dampen); \
 154     free(attr_name); \
 155     free(attr_node); \
 156     free(attr_section); \
 157     free(attr_set);
 158 
 159 #define set_option(option_var) \
 160     if (option_var) { \
 161         free(option_var); \
 162     } \
 163     option_var = strdup(optarg);
 164 
 165 int
 166 main(int argc, char **argv)
     /* [previous][next][first][last][top][bottom][index][help] */
 167 {
 168     int index = 0;
 169     int argerr = 0;
 170     int attr_options = pcmk__node_attr_none;
 171     int flag;
 172     crm_exit_t exit_code = CRM_EX_OK;
 173     char *attr_node = NULL;
 174     char *attr_name = NULL;
 175     char *attr_set = NULL;
 176     char *attr_section = NULL;
 177     char *attr_dampen = NULL;
 178     const char *attr_value = NULL;
 179     char command = 'Q';
 180 
 181     gboolean query_all = FALSE;
 182 
 183     crm_log_cli_init("attrd_updater");
 184     pcmk__set_cli_options(NULL, "-n <attribute> <command> [options]",
 185                           long_options,
 186                           "query and update Pacemaker node attributes");
 187 
 188     if (argc < 2) {
 189         pcmk__cli_help('?', CRM_EX_USAGE);
 190     }
 191 
 192     while (1) {
 193         flag = pcmk__next_cli_option(argc, argv, &index, NULL);
 194         if (flag == -1)
 195             break;
 196 
 197         switch (flag) {
 198             case 'V':
 199                 crm_bump_log_level(argc, argv);
 200                 break;
 201             case '?':
 202             case '$':
 203                 cleanup_memory();
 204                 pcmk__cli_help(flag, CRM_EX_OK);
 205                 break;
 206             case 'n':
 207                 set_option(attr_name);
 208                 break;
 209             case 's':
 210                 set_option(attr_set);
 211                 break;
 212             case 'd':
 213                 set_option(attr_dampen);
 214                 break;
 215             case 'l':
 216             case 'S':
 217                 set_option(attr_section);
 218                 break;
 219             case 'N':
 220                 set_option(attr_node);
 221                 break;
 222             case 'A':
 223                 query_all = TRUE;
 224                 break;
 225             case 'p':
 226                 pcmk__set_node_attr_flags(attr_options, pcmk__node_attr_private);
 227                 break;
 228             case 'q':
 229                 break;
 230             case 'Y':
 231                 command = flag;
 232                 crm_log_args(argc, argv); /* Too much? */
 233                 break;
 234             case 'Q':
 235             case 'B':
 236             case 'R':
 237             case 'D':
 238             case 'U':
 239             case 'v':
 240                 command = flag;
 241                 attr_value = optarg;
 242                 crm_log_args(argc, argv); /* Too much? */
 243                 break;
 244             default:
 245                 ++argerr;
 246                 break;
 247         }
 248     }
 249 
 250     if (optind > argc) {
 251         ++argerr;
 252     }
 253 
 254     if (command != 'R' && attr_name == NULL) {
 255         ++argerr;
 256     }
 257 
 258     if (argerr) {
 259         cleanup_memory();
 260         pcmk__cli_help('?', CRM_EX_USAGE);
 261     }
 262 
 263     if (command == 'Q') {
 264         exit_code = crm_errno2exit(do_query(attr_name, attr_node, query_all));
 265     } else {
 266         /* @TODO We don't know whether the specified node is a Pacemaker Remote
 267          * node or not, so we can't set pcmk__node_attr_remote when appropriate.
 268          * However, it's not a big problem, because pacemaker-attrd will learn
 269          * and remember a node's "remoteness".
 270          */
 271         exit_code = pcmk_rc2exitc(do_update(command,
 272                                             pcmk__node_attr_target(attr_node),
 273                                             attr_name, attr_value,
 274                                             attr_section, attr_set,
 275                                             attr_dampen, attr_options));
 276     }
 277 
 278     cleanup_memory();
 279     crm_exit(exit_code);
 280 }
 281 
 282 /*!
 283  * \internal
 284  * \brief Submit a query request to pacemaker-attrd and wait for reply
 285  *
 286  * \param[in] name    Name of attribute to query
 287  * \param[in] host    Query applies to this host only (or all hosts if NULL)
 288  * \param[out] reply  On success, will be set to new XML tree with reply
 289  *
 290  * \return pcmk_ok on success, -errno on error
 291  * \note On success, caller is responsible for freeing result via free_xml(*reply)
 292  */
 293 static int
 294 send_attrd_query(const char *name, const char *host, xmlNode **reply)
     /* [previous][next][first][last][top][bottom][index][help] */
 295 {
 296     int rc;
 297     crm_ipc_t *ipc;
 298     xmlNode *query;
 299 
 300     /* Build the query XML */
 301     query = create_xml_node(NULL, __func__);
 302     if (query == NULL) {
 303         return -ENOMEM;
 304     }
 305     crm_xml_add(query, F_TYPE, T_ATTRD);
 306     crm_xml_add(query, F_ORIG, crm_system_name);
 307     crm_xml_add(query, PCMK__XA_ATTR_NODE_NAME, host);
 308     crm_xml_add(query, PCMK__XA_TASK, PCMK__ATTRD_CMD_QUERY);
 309     crm_xml_add(query, PCMK__XA_ATTR_NAME, name);
 310 
 311     /* Connect to pacemaker-attrd, send query XML and get reply */
 312     crm_debug("Sending query for value of %s on %s", name, (host? host : "all nodes"));
 313     ipc = crm_ipc_new(T_ATTRD, 0);
 314     if (crm_ipc_connect(ipc) == FALSE) {
 315         crm_perror(LOG_ERR, "Connection to cluster attribute manager failed");
 316         rc = -ENOTCONN;
 317     } else {
 318         rc = crm_ipc_send(ipc, query, crm_ipc_client_response, 0, reply);
 319         if (rc > 0) {
 320             rc = pcmk_ok;
 321         }
 322         crm_ipc_close(ipc);
 323     }
 324     crm_ipc_destroy(ipc);
 325 
 326     free_xml(query);
 327     return(rc);
 328 }
 329 
 330 /*!
 331  * \brief Validate pacemaker-attrd's XML reply to an query
 332  *
 333  * param[in] reply      Root of reply XML tree to validate
 334  * param[in] attr_name  Name of attribute that was queried
 335  *
 336  * \return pcmk_ok on success,
 337  *         -errno on error (-ENXIO = requested attribute does not exist)
 338  */
 339 static int
 340 validate_attrd_reply(xmlNode *reply, const char *attr_name)
     /* [previous][next][first][last][top][bottom][index][help] */
 341 {
 342     const char *reply_attr;
 343 
 344     if (reply == NULL) {
 345         fprintf(stderr, "Could not query value of %s: reply did not contain valid XML\n",
 346                 attr_name);
 347         return -pcmk_err_schema_validation;
 348     }
 349     crm_log_xml_trace(reply, "Reply");
 350 
 351     reply_attr = crm_element_value(reply, PCMK__XA_ATTR_NAME);
 352     if (reply_attr == NULL) {
 353         fprintf(stderr, "Could not query value of %s: attribute does not exist\n",
 354                 attr_name);
 355         return -ENXIO;
 356     }
 357 
 358     if (!pcmk__str_eq(crm_element_value(reply, F_TYPE), T_ATTRD, pcmk__str_casei)
 359         || (crm_element_value(reply, PCMK__XA_ATTR_VERSION) == NULL)
 360         || strcmp(reply_attr, attr_name)) {
 361             fprintf(stderr,
 362                     "Could not query value of %s: reply did not contain expected identification\n",
 363                     attr_name);
 364             return -pcmk_err_schema_validation;
 365     }
 366     return pcmk_ok;
 367 }
 368 
 369 /*!
 370  * \brief Print the attribute values in a pacemaker-attrd XML query reply
 371  *
 372  * \param[in] reply     Root of XML tree with query reply
 373  * \param[in] attr_name Name of attribute that was queried
 374  *
 375  * \return TRUE if any values were printed
 376  */
 377 static gboolean
 378 print_attrd_values(xmlNode *reply, const char *attr_name)
     /* [previous][next][first][last][top][bottom][index][help] */
 379 {
 380     xmlNode *child;
 381     const char *reply_host, *reply_value;
 382     gboolean have_values = FALSE;
 383 
 384     /* Iterate through reply's XML tags (a node tag for each host-value pair) */
 385     for (child = pcmk__xml_first_child(reply); child != NULL;
 386          child = pcmk__xml_next(child)) {
 387 
 388         if (!pcmk__str_eq((const char *)child->name, XML_CIB_TAG_NODE,
 389                           pcmk__str_casei)) {
 390             crm_warn("Ignoring unexpected %s tag in query reply", child->name);
 391         } else {
 392             reply_host = crm_element_value(child, PCMK__XA_ATTR_NODE_NAME);
 393             reply_value = crm_element_value(child, PCMK__XA_ATTR_VALUE);
 394 
 395             if (reply_host == NULL) {
 396                 crm_warn("Ignoring %s tag without %s attribute in query reply",
 397                          XML_CIB_TAG_NODE, PCMK__XA_ATTR_NODE_NAME);
 398             } else {
 399                 printf("name=\"%s\" host=\"%s\" value=\"%s\"\n",
 400                        attr_name, reply_host, (reply_value? reply_value : ""));
 401                 have_values = TRUE;
 402             }
 403         }
 404     }
 405     return have_values;
 406 }
 407 
 408 /*!
 409  * \brief Submit a query to pacemaker-attrd and print reply
 410  *
 411  * \param[in] attr_name  Name of attribute to be affected by request
 412  * \param[in] attr_node  Name of host to query for (or NULL for localhost)
 413  * \param[in] query_all  If TRUE, ignore attr_node and query all nodes instead
 414  *
 415  * \return pcmk_ok on success, -errno on error
 416  */
 417 static int
 418 do_query(const char *attr_name, const char *attr_node, gboolean query_all)
     /* [previous][next][first][last][top][bottom][index][help] */
 419 {
 420     xmlNode *reply = NULL;
 421     int rc;
 422 
 423     /* Decide which node(s) to query */
 424     if (query_all == TRUE) {
 425         attr_node = NULL;
 426     } else {
 427         attr_node = pcmk__node_attr_target(attr_node);
 428     }
 429 
 430     /* Build and send pacemaker-attrd request, and get XML reply */
 431     rc = send_attrd_query(attr_name, attr_node, &reply);
 432     if (rc != pcmk_ok) {
 433         fprintf(stderr, "Could not query value of %s: %s (%d)\n", attr_name, pcmk_strerror(rc), rc);
 434         return rc;
 435     }
 436 
 437     /* Validate the XML reply */
 438     rc = validate_attrd_reply(reply, attr_name);
 439     if (rc != pcmk_ok) {
 440         if (reply != NULL) {
 441             free_xml(reply);
 442         }
 443         return rc;
 444     }
 445 
 446     /* Print the values from the reply */
 447     if (print_attrd_values(reply, attr_name) == FALSE) {
 448         fprintf(stderr,
 449                 "Could not query value of %s: reply had attribute name but no host values\n",
 450                 attr_name);
 451         free_xml(reply);
 452         return -pcmk_err_schema_validation;
 453     }
 454 
 455     return pcmk_ok;
 456 }
 457 
 458 static int
 459 do_update(char command, const char *attr_node, const char *attr_name,
     /* [previous][next][first][last][top][bottom][index][help] */
 460           const char *attr_value, const char *attr_section,
 461           const char *attr_set, const char *attr_dampen, int attr_options)
 462 {
 463     int rc = pcmk__node_attr_request(NULL, command, attr_node, attr_name,
 464                                      attr_value, attr_section, attr_set,
 465                                      attr_dampen, NULL, attr_options);
 466     if (rc != pcmk_rc_ok) {
 467         fprintf(stderr, "Could not update %s=%s: %s (%d)\n",
 468                 attr_name, attr_value, pcmk_rc_str(rc), rc);
 469     }
 470     return rc;
 471 }

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