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     pcmk__cli_init_logging("attrd_updater", 0);
 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         const char *target = pcmk__node_attr_target(attr_node);
 272 
 273         exit_code = pcmk_rc2exitc(do_update(command,
 274                                             target == NULL ? attr_node : target,
 275                                             attr_name, attr_value,
 276                                             attr_section, attr_set,
 277                                             attr_dampen, attr_options));
 278     }
 279 
 280     cleanup_memory();
 281     crm_exit(exit_code);
 282 }
 283 
 284 /*!
 285  * \internal
 286  * \brief Submit a query request to pacemaker-attrd and wait for reply
 287  *
 288  * \param[in] name    Name of attribute to query
 289  * \param[in] host    Query applies to this host only (or all hosts if NULL)
 290  * \param[out] reply  On success, will be set to new XML tree with reply
 291  *
 292  * \return pcmk_ok on success, -errno on error
 293  * \note On success, caller is responsible for freeing result via free_xml(*reply)
 294  */
 295 static int
 296 send_attrd_query(const char *name, const char *host, xmlNode **reply)
     /* [previous][next][first][last][top][bottom][index][help] */
 297 {
 298     int rc;
 299     crm_ipc_t *ipc;
 300     xmlNode *query;
 301 
 302     /* Build the query XML */
 303     query = create_xml_node(NULL, __func__);
 304     if (query == NULL) {
 305         return -ENOMEM;
 306     }
 307     crm_xml_add(query, F_TYPE, T_ATTRD);
 308     crm_xml_add(query, F_ORIG, crm_system_name);
 309     crm_xml_add(query, PCMK__XA_ATTR_NODE_NAME, host);
 310     crm_xml_add(query, PCMK__XA_TASK, PCMK__ATTRD_CMD_QUERY);
 311     crm_xml_add(query, PCMK__XA_ATTR_NAME, name);
 312 
 313     /* Connect to pacemaker-attrd, send query XML and get reply */
 314     crm_debug("Sending query for value of %s on %s", name, (host? host : "all nodes"));
 315     ipc = crm_ipc_new(T_ATTRD, 0);
 316     if (crm_ipc_connect(ipc) == FALSE) {
 317         crm_perror(LOG_ERR, "Connection to cluster attribute manager failed");
 318         rc = -ENOTCONN;
 319     } else {
 320         rc = crm_ipc_send(ipc, query, crm_ipc_client_response, 0, reply);
 321         if (rc > 0) {
 322             rc = pcmk_ok;
 323         }
 324         crm_ipc_close(ipc);
 325     }
 326     crm_ipc_destroy(ipc);
 327 
 328     free_xml(query);
 329     return(rc);
 330 }
 331 
 332 /*!
 333  * \brief Validate pacemaker-attrd's XML reply to an query
 334  *
 335  * param[in] reply      Root of reply XML tree to validate
 336  * param[in] attr_name  Name of attribute that was queried
 337  *
 338  * \return pcmk_ok on success,
 339  *         -errno on error (-ENXIO = requested attribute does not exist)
 340  */
 341 static int
 342 validate_attrd_reply(xmlNode *reply, const char *attr_name)
     /* [previous][next][first][last][top][bottom][index][help] */
 343 {
 344     const char *reply_attr;
 345 
 346     if (reply == NULL) {
 347         fprintf(stderr, "Could not query value of %s: reply did not contain valid XML\n",
 348                 attr_name);
 349         return -pcmk_err_schema_validation;
 350     }
 351     crm_log_xml_trace(reply, "Reply");
 352 
 353     reply_attr = crm_element_value(reply, PCMK__XA_ATTR_NAME);
 354     if (reply_attr == NULL) {
 355         fprintf(stderr, "Could not query value of %s: attribute does not exist\n",
 356                 attr_name);
 357         return -ENXIO;
 358     }
 359 
 360     if (!pcmk__str_eq(crm_element_value(reply, F_TYPE), T_ATTRD, pcmk__str_casei)
 361         || (crm_element_value(reply, PCMK__XA_ATTR_VERSION) == NULL)
 362         || strcmp(reply_attr, attr_name)) {
 363             fprintf(stderr,
 364                     "Could not query value of %s: reply did not contain expected identification\n",
 365                     attr_name);
 366             return -pcmk_err_schema_validation;
 367     }
 368     return pcmk_ok;
 369 }
 370 
 371 /*!
 372  * \brief Print the attribute values in a pacemaker-attrd XML query reply
 373  *
 374  * \param[in] reply     Root of XML tree with query reply
 375  * \param[in] attr_name Name of attribute that was queried
 376  *
 377  * \return TRUE if any values were printed
 378  */
 379 static gboolean
 380 print_attrd_values(xmlNode *reply, const char *attr_name)
     /* [previous][next][first][last][top][bottom][index][help] */
 381 {
 382     xmlNode *child;
 383     const char *reply_host, *reply_value;
 384     gboolean have_values = FALSE;
 385 
 386     /* Iterate through reply's XML tags (a node tag for each host-value pair) */
 387     for (child = pcmk__xml_first_child(reply); child != NULL;
 388          child = pcmk__xml_next(child)) {
 389 
 390         if (!pcmk__str_eq((const char *)child->name, XML_CIB_TAG_NODE,
 391                           pcmk__str_casei)) {
 392             crm_warn("Ignoring unexpected %s tag in query reply", child->name);
 393         } else {
 394             reply_host = crm_element_value(child, PCMK__XA_ATTR_NODE_NAME);
 395             reply_value = crm_element_value(child, PCMK__XA_ATTR_VALUE);
 396 
 397             if (reply_host == NULL) {
 398                 crm_warn("Ignoring %s tag without %s attribute in query reply",
 399                          XML_CIB_TAG_NODE, PCMK__XA_ATTR_NODE_NAME);
 400             } else {
 401                 printf("name=\"%s\" host=\"%s\" value=\"%s\"\n",
 402                        attr_name, reply_host, (reply_value? reply_value : ""));
 403                 have_values = TRUE;
 404             }
 405         }
 406     }
 407     return have_values;
 408 }
 409 
 410 /*!
 411  * \brief Submit a query to pacemaker-attrd and print reply
 412  *
 413  * \param[in] attr_name  Name of attribute to be affected by request
 414  * \param[in] attr_node  Name of host to query for (or NULL for localhost)
 415  * \param[in] query_all  If TRUE, ignore attr_node and query all nodes instead
 416  *
 417  * \return pcmk_ok on success, -errno on error
 418  */
 419 static int
 420 do_query(const char *attr_name, const char *attr_node, gboolean query_all)
     /* [previous][next][first][last][top][bottom][index][help] */
 421 {
 422     xmlNode *reply = NULL;
 423     int rc;
 424 
 425     /* Decide which node(s) to query */
 426     if (query_all == TRUE) {
 427         attr_node = NULL;
 428     } else {
 429         const char *target = pcmk__node_attr_target(attr_node);
 430         if (target != NULL) {
 431             attr_node = target;
 432         }
 433     }
 434 
 435     /* Build and send pacemaker-attrd request, and get XML reply */
 436     rc = send_attrd_query(attr_name, attr_node, &reply);
 437     if (rc != pcmk_ok) {
 438         fprintf(stderr, "Could not query value of %s: %s (%d)\n", attr_name, pcmk_strerror(rc), rc);
 439         return rc;
 440     }
 441 
 442     /* Validate the XML reply */
 443     rc = validate_attrd_reply(reply, attr_name);
 444     if (rc != pcmk_ok) {
 445         if (reply != NULL) {
 446             free_xml(reply);
 447         }
 448         return rc;
 449     }
 450 
 451     /* Print the values from the reply */
 452     if (print_attrd_values(reply, attr_name) == FALSE) {
 453         fprintf(stderr,
 454                 "Could not query value of %s: reply had attribute name but no host values\n",
 455                 attr_name);
 456         free_xml(reply);
 457         return -pcmk_err_schema_validation;
 458     }
 459 
 460     return pcmk_ok;
 461 }
 462 
 463 static int
 464 do_update(char command, const char *attr_node, const char *attr_name,
     /* [previous][next][first][last][top][bottom][index][help] */
 465           const char *attr_value, const char *attr_section,
 466           const char *attr_set, const char *attr_dampen, int attr_options)
 467 {
 468     int rc = pcmk__node_attr_request(NULL, command, attr_node, attr_name,
 469                                      attr_value, attr_section, attr_set,
 470                                      attr_dampen, NULL, attr_options);
 471     if (rc != pcmk_rc_ok) {
 472         fprintf(stderr, "Could not update %s=%s: %s (%d)\n",
 473                 attr_name, attr_value, pcmk_rc_str(rc), rc);
 474     }
 475     return rc;
 476 }

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