root/tools/attrd_updater.c

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

DEFINITIONS

This source file includes following definitions.
  1. command_cb
  2. private_cb
  3. section_cb
  4. build_arg_context
  5. main
  6. print_attrd_values
  7. attrd_event_cb
  8. send_attrd_query
  9. send_attrd_update

   1 /*
   2  * Copyright 2004-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 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/ipc_attrd_internal.h>
  23 #include <crm/common/cmdline_internal.h>
  24 #include <crm/common/output_internal.h>
  25 #include <crm/common/xml_internal.h>
  26 
  27 #include <crm/common/attrd_internal.h>
  28 
  29 #include <pcmki/pcmki_output.h>
  30 
  31 #define SUMMARY "query and update Pacemaker node attributes"
  32 
  33 static pcmk__supported_format_t formats[] = {
  34     PCMK__SUPPORTED_FORMAT_NONE,
  35     PCMK__SUPPORTED_FORMAT_TEXT,
  36     PCMK__SUPPORTED_FORMAT_XML,
  37     { NULL, NULL, NULL }
  38 };
  39 
  40 GError *error = NULL;
  41 bool printed_values = false;
  42 
  43 struct {
  44     char command;
  45     gchar *attr_dampen;
  46     gchar *attr_name;
  47     gchar *attr_node;
  48     gchar *attr_set;
  49     char *attr_value;
  50     int attr_options;
  51     gboolean query_all;
  52     gboolean quiet;
  53 } options = {
  54     .attr_options = pcmk__node_attr_none,
  55     .command = 'Q',
  56 };
  57 
  58 static gboolean
  59 command_cb (const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
     /* [previous][next][first][last][top][bottom][index][help] */
  60     pcmk__str_update(&options.attr_value, optarg);
  61 
  62     if (pcmk__str_any_of(option_name, "--update-both", "-B", NULL)) {
  63         options.command = 'B';
  64     } else if (pcmk__str_any_of(option_name, "--delete", "-D", NULL)) {
  65         options.command = 'D';
  66     } else if (pcmk__str_any_of(option_name, "--query", "-Q", NULL)) {
  67         options.command = 'Q';
  68     } else if (pcmk__str_any_of(option_name, "--refresh", "-R", NULL)) {
  69         options.command = 'R';
  70     } else if (pcmk__str_any_of(option_name, "--update", "-U", "-v", NULL)) {
  71         options.command = 'U';
  72     } else if (pcmk__str_any_of(option_name, "--update-delay", "-Y", NULL)) {
  73         options.command = 'Y';
  74     }
  75 
  76     return TRUE;
  77 }
  78 
  79 static gboolean
  80 private_cb (const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
     /* [previous][next][first][last][top][bottom][index][help] */
  81     pcmk__set_node_attr_flags(options.attr_options, pcmk__node_attr_private);
  82     return TRUE;
  83 }
  84 
  85 static gboolean
  86 section_cb (const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
     /* [previous][next][first][last][top][bottom][index][help] */
  87     if (pcmk__str_any_of(optarg, "nodes", "forever", NULL)) {
  88         pcmk__set_node_attr_flags(options.attr_options, pcmk__node_attr_perm);
  89     } else if (pcmk__str_any_of(optarg, "status", "reboot", NULL)) {
  90         pcmk__clear_node_attr_flags(options.attr_options, pcmk__node_attr_perm);
  91     } else {
  92         g_set_error(err, PCMK__EXITC_ERROR, CRM_EX_USAGE, "Unknown value for --lifetime: %s",
  93                     optarg);
  94         return FALSE;
  95     }
  96 
  97     return TRUE;
  98 }
  99 
 100 #define INDENT "                              "
 101 
 102 static GOptionEntry required_entries[] = {
 103     { "name", 'n', 0, G_OPTION_ARG_STRING, &options.attr_name,
 104       "The attribute's name",
 105       "NAME" },
 106 
 107     { NULL }
 108 };
 109 
 110 static GOptionEntry command_entries[] = {
 111     { "update", 'U', 0, G_OPTION_ARG_CALLBACK, command_cb,
 112       "Update attribute's value in pacemaker-attrd. If this causes the value\n"
 113       INDENT "to change, it will also be updated in the cluster configuration.",
 114       "VALUE" },
 115 
 116     { "update-both", 'B', 0, G_OPTION_ARG_CALLBACK, command_cb,
 117       "Update attribute's value and time to wait (dampening) in\n"
 118       INDENT "pacemaker-attrd. If this causes the value or dampening to change,\n"
 119       INDENT "the attribute will also be written to the cluster configuration,\n"
 120       INDENT "so be aware that repeatedly changing the dampening reduces its\n"
 121       INDENT "effectiveness.\n"
 122       INDENT "Requires -d/--delay",
 123       "VALUE" },
 124 
 125     { "update-delay", 'Y', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
 126       "Update attribute's dampening in pacemaker-attrd. If this causes\n"
 127       INDENT "the dampening to change, the attribute will also be written\n"
 128       INDENT "to the cluster configuration, so be aware that repeatedly\n"
 129       INDENT "changing the dampening reduces its effectiveness.\n"
 130       INDENT "Requires -d/--delay",
 131       NULL },
 132 
 133     { "query", 'Q', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
 134       "Query the attribute's value from pacemaker-attrd",
 135       NULL },
 136 
 137     { "delete", 'D', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
 138       "Unset attribute from pacemaker-attrd. At the moment, there is no way\n"
 139       INDENT "to remove an attribute. This option will instead set its value\n"
 140       INDENT "to the empty string.",
 141       NULL },
 142 
 143     { "refresh", 'R', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
 144       "(Advanced) Force the pacemaker-attrd daemon to resend all current\n"
 145       INDENT "values to the CIB",
 146       NULL },
 147 
 148     { NULL }
 149 };
 150 
 151 static GOptionEntry addl_entries[] = {
 152     { "delay", 'd', 0, G_OPTION_ARG_STRING, &options.attr_dampen,
 153       "The time to wait (dampening) in seconds for further changes\n"
 154       INDENT "before sending to the CIB",
 155       "SECONDS" },
 156 
 157     { "set", 's', 0, G_OPTION_ARG_STRING, &options.attr_set,
 158       "(Advanced) The attribute set in which to place the value",
 159       "SET" },
 160 
 161     { "node", 'N', 0, G_OPTION_ARG_STRING, &options.attr_node,
 162       "Set the attribute for the named node (instead of the local one)",
 163       "NODE" },
 164 
 165     { "all", 'A', 0, G_OPTION_ARG_NONE, &options.query_all,
 166       "Show values of the attribute for all nodes (query only)",
 167       NULL },
 168 
 169     { "lifetime", 'l', 0, G_OPTION_ARG_CALLBACK, section_cb,
 170       "(Not yet implemented) Lifetime of the node attribute (silently\n"
 171       INDENT "ignored by cluster)",
 172       "SECTION" },
 173 
 174     { "private", 'p', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, private_cb,
 175       "If this creates a new attribute, never write the attribute to CIB",
 176       NULL },
 177 
 178     { NULL }
 179 };
 180 
 181 static GOptionEntry deprecated_entries[] = {
 182     { "quiet", 'q', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &options.quiet,
 183       NULL,
 184       NULL },
 185 
 186     { "update", 'v', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, command_cb,
 187       NULL,
 188       NULL },
 189 
 190     { "section", 'S', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, section_cb,
 191       NULL,
 192       NULL },
 193 
 194     { NULL }
 195 };
 196 
 197 static int send_attrd_query(pcmk__output_t *out, const char *attr_name, const char *attr_node,
 198                     gboolean query_all);
 199 static int send_attrd_update(char command, const char *attr_node, const char *attr_name,
 200                              const char *attr_value, const char *attr_set,
 201                              const char *attr_dampen, uint32_t attr_options);
 202 
 203 static GOptionContext *
 204 build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) {
     /* [previous][next][first][last][top][bottom][index][help] */
 205     GOptionContext *context = NULL;
 206 
 207     context = pcmk__build_arg_context(args, "text (default), xml", group, NULL);
 208 
 209     pcmk__add_arg_group(context, "required", "Required Arguments:",
 210                         "Show required arguments", required_entries);
 211     pcmk__add_arg_group(context, "command", "Command:",
 212                         "Show command options (mutually exclusive)", command_entries);
 213     pcmk__add_arg_group(context, "additional", "Additional Options:",
 214                         "Show additional options", addl_entries);
 215     pcmk__add_arg_group(context, "deprecated", "Deprecated Options:",
 216                         "Show deprecated options", deprecated_entries);
 217 
 218     return context;
 219 }
 220 
 221 int
 222 main(int argc, char **argv)
     /* [previous][next][first][last][top][bottom][index][help] */
 223 {
 224     int rc = pcmk_rc_ok;
 225     crm_exit_t exit_code = CRM_EX_OK;
 226 
 227     pcmk__output_t *out = NULL;
 228 
 229     GOptionGroup *output_group = NULL;
 230     pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
 231     GOptionContext *context = build_arg_context(args, &output_group);
 232     gchar **processed_args = pcmk__cmdline_preproc(argv, "dlnsvBNUS");
 233 
 234     pcmk__register_formats(output_group, formats);
 235     if (!g_option_context_parse_strv(context, &processed_args, &error)) {
 236         exit_code = CRM_EX_USAGE;
 237         goto done;
 238     }
 239 
 240     pcmk__cli_init_logging("attrd_updater", args->verbosity);
 241 
 242     rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv);
 243     if (rc != pcmk_rc_ok) {
 244         exit_code = CRM_EX_ERROR;
 245         g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Error creating output format %s: %s",
 246                     args->output_ty, pcmk_rc_str(rc));
 247         goto done;
 248     }
 249 
 250     if (args->version) {
 251         out->version(out, false);
 252         goto done;
 253     }
 254 
 255     if (options.command != 'R' && options.attr_name == NULL) {
 256         exit_code = CRM_EX_USAGE;
 257         g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Command requires --name argument");
 258         goto done;
 259     } else if ((options.command == 'B'|| options.command == 'Y') && options.attr_dampen == NULL) {
 260         out->info(out, "Warning: '%c' command given without required --delay", options.command);
 261     }
 262 
 263     pcmk__register_lib_messages(out);
 264 
 265     if (options.command == 'Q') {
 266         int rc = send_attrd_query(out, options.attr_name, options.attr_node, options.query_all);
 267         exit_code = pcmk_rc2exitc(rc);
 268     } else {
 269         /* @TODO We don't know whether the specified node is a Pacemaker Remote
 270          * node or not, so we can't set pcmk__node_attr_remote when appropriate.
 271          * However, it's not a big problem, because pacemaker-attrd will learn
 272          * and remember a node's "remoteness".
 273          */
 274         int rc = send_attrd_update(options.command, options.attr_node,
 275                                    options.attr_name, options.attr_value,
 276                                    options.attr_set, options.attr_dampen,
 277                                    options.attr_options);
 278         exit_code = pcmk_rc2exitc(rc);
 279     }
 280 
 281 done:
 282     g_strfreev(processed_args);
 283     pcmk__free_arg_context(context);
 284     g_free(options.attr_dampen);
 285     g_free(options.attr_name);
 286     g_free(options.attr_node);
 287     g_free(options.attr_set);
 288     free(options.attr_value);
 289 
 290     pcmk__output_and_clear_error(error, out);
 291 
 292     if (out != NULL) {
 293         out->finish(out, exit_code, true, NULL);
 294         pcmk__output_free(out);
 295     }
 296 
 297     crm_exit(exit_code);
 298 }
 299 
 300 /*!
 301  * \brief Print the attribute values in a pacemaker-attrd XML query reply
 302  *
 303  * \param[in] reply     List of attribute name/value pairs
 304  * \param[in] attr_name Name of attribute that was queried
 305  *
 306  * \return true if any values were printed
 307  */
 308 static void
 309 print_attrd_values(pcmk__output_t *out, GList *reply)
     /* [previous][next][first][last][top][bottom][index][help] */
 310 {
 311     for (GList *iter = reply; iter != NULL; iter = iter->next) {
 312         pcmk__attrd_query_pair_t *pair = (pcmk__attrd_query_pair_t *) iter->data;
 313 
 314         out->message(out, "attribute", NULL, NULL, pair->name, pair->value,
 315                      pair->node);
 316         printed_values = true;
 317     }
 318 }
 319 
 320 static void
 321 attrd_event_cb(pcmk_ipc_api_t *attrd_api, enum pcmk_ipc_event event_type,
     /* [previous][next][first][last][top][bottom][index][help] */
 322                crm_exit_t status, void *event_data, void *user_data)
 323 {
 324     pcmk__output_t *out = (pcmk__output_t *) user_data;
 325     pcmk__attrd_api_reply_t *reply = event_data;
 326 
 327     if (event_type != pcmk_ipc_event_reply || status != CRM_EX_OK) {
 328         return;
 329     }
 330 
 331     /* Print the values from the reply. */
 332     if (reply->reply_type == pcmk__attrd_reply_query) {
 333         print_attrd_values(out, reply->data.pairs);
 334     }
 335 }
 336 
 337 /*!
 338  * \brief Submit a query to pacemaker-attrd and print reply
 339  *
 340  * \param[in] attr_name  Name of attribute to be affected by request
 341  * \param[in] attr_node  Name of host to query for (or NULL for localhost)
 342  * \param[in] query_all  If TRUE, ignore attr_node and query all nodes instead
 343  *
 344  * \return Standard Pacemaker return code
 345  */
 346 static int
 347 send_attrd_query(pcmk__output_t *out, const char *attr_name, const char *attr_node, gboolean query_all)
     /* [previous][next][first][last][top][bottom][index][help] */
 348 {
 349     pcmk_ipc_api_t *attrd_api = NULL;
 350     int rc = pcmk_rc_ok;
 351 
 352     // Create attrd IPC object
 353     rc = pcmk_new_ipc_api(&attrd_api, pcmk_ipc_attrd);
 354     if (rc != pcmk_rc_ok) {
 355         g_set_error(&error, PCMK__RC_ERROR, rc,
 356                     "Could not connect to attrd: %s", pcmk_rc_str(rc));
 357         return ENOTCONN;
 358     }
 359 
 360     pcmk_register_ipc_callback(attrd_api, attrd_event_cb, out);
 361 
 362     // Connect to attrd (without main loop)
 363     rc = pcmk_connect_ipc(attrd_api, pcmk_ipc_dispatch_sync);
 364     if (rc != pcmk_rc_ok) {
 365         g_set_error(&error, PCMK__RC_ERROR, rc,
 366                     "Could not connect to attrd: %s", pcmk_rc_str(rc));
 367         pcmk_free_ipc_api(attrd_api);
 368         return rc;
 369     }
 370 
 371     /* Decide which node(s) to query */
 372     if (query_all == TRUE) {
 373         attr_node = NULL;
 374     }
 375 
 376     rc = pcmk__attrd_api_query(attrd_api, attr_node, attr_name, 0);
 377 
 378     if (rc != pcmk_rc_ok) {
 379         g_set_error(&error, PCMK__RC_ERROR, rc, "Could not query value of %s: %s (%d)",
 380                     attr_name, pcmk_strerror(rc), rc);
 381     } else if (!printed_values) {
 382         rc = pcmk_rc_schema_validation;
 383         g_set_error(&error, PCMK__RC_ERROR, rc,
 384                     "Could not query value of %s: attribute does not exist", attr_name);
 385     }
 386 
 387     pcmk_disconnect_ipc(attrd_api);
 388     pcmk_free_ipc_api(attrd_api);
 389 
 390     return rc;
 391 }
 392 
 393 static int
 394 send_attrd_update(char command, const char *attr_node, const char *attr_name,
     /* [previous][next][first][last][top][bottom][index][help] */
 395                   const char *attr_value, const char *attr_set,
 396                   const char *attr_dampen, uint32_t attr_options)
 397 {
 398     int rc = pcmk_rc_ok;
 399 
 400     switch (command) {
 401         case 'B':
 402             rc = pcmk__attrd_api_update(NULL, attr_node, attr_name, attr_value,
 403                                         attr_dampen, attr_set, NULL,
 404                                         attr_options | pcmk__node_attr_value | pcmk__node_attr_delay);
 405             break;
 406 
 407         case 'D':
 408             rc = pcmk__attrd_api_delete(NULL, attr_node, attr_name, attr_options);
 409             break;
 410 
 411         case 'R':
 412             rc = pcmk__attrd_api_refresh(NULL, attr_node);
 413             break;
 414 
 415         case 'U':
 416             rc = pcmk__attrd_api_update(NULL, attr_node, attr_name, attr_value,
 417                                         NULL, attr_set, NULL,
 418                                         attr_options | pcmk__node_attr_value);
 419             break;
 420 
 421         case 'Y':
 422             rc = pcmk__attrd_api_update(NULL, attr_node, attr_name, NULL,
 423                                         attr_dampen, attr_set, NULL,
 424                                         attr_options | pcmk__node_attr_delay);
 425             break;
 426     }
 427 
 428     if (rc != pcmk_rc_ok) {
 429         g_set_error(&error, PCMK__RC_ERROR, rc, "Could not update %s=%s: %s (%d)",
 430                     attr_name, attr_value, pcmk_rc_str(rc), rc);
 431     }
 432 
 433     return rc;
 434 }

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