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. attr_set_type_cb
  5. wait_cb
  6. pattern_used_correctly
  7. build_arg_context
  8. main
  9. print_attrd_values
  10. attrd_event_cb
  11. send_attrd_query
  12. send_attrd_update

   1 /*
   2  * Copyright 2004-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 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/common/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/attrs_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_pattern;
  48     gchar *attr_node;
  49     gchar *attr_set;
  50     char *attr_value;
  51     uint32_t attr_options;
  52     gboolean query_all;
  53     gboolean quiet;
  54 } options = {
  55     .attr_options = pcmk__node_attr_none,
  56     .command = 'Q',
  57 };
  58 
  59 static gboolean
  60 command_cb (const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
     /* [previous][next][first][last][top][bottom][index][help] */
  61     pcmk__str_update(&options.attr_value, optarg);
  62 
  63     if (pcmk__str_any_of(option_name, "--update-both", "-B", NULL)) {
  64         options.command = 'B';
  65     } else if (pcmk__str_any_of(option_name, "--delete", "-D", NULL)) {
  66         options.command = 'D';
  67     } else if (pcmk__str_any_of(option_name, "--query", "-Q", NULL)) {
  68         options.command = 'Q';
  69     } else if (pcmk__str_any_of(option_name, "--refresh", "-R", NULL)) {
  70         options.command = 'R';
  71     } else if (pcmk__str_any_of(option_name, "--update", "-U", "-v", NULL)) {
  72         options.command = 'U';
  73     } else if (pcmk__str_any_of(option_name, "--update-delay", "-Y", NULL)) {
  74         options.command = 'Y';
  75     }
  76 
  77     return TRUE;
  78 }
  79 
  80 static gboolean
  81 private_cb (const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
     /* [previous][next][first][last][top][bottom][index][help] */
  82     pcmk__set_node_attr_flags(options.attr_options, pcmk__node_attr_private);
  83     return TRUE;
  84 }
  85 
  86 static gboolean
  87 section_cb (const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
     /* [previous][next][first][last][top][bottom][index][help] */
  88     if (pcmk__str_any_of(optarg, PCMK_XE_NODES, "forever", NULL)) {
  89         pcmk__set_node_attr_flags(options.attr_options, pcmk__node_attr_perm);
  90     } else if (pcmk__str_any_of(optarg, PCMK_XE_STATUS, PCMK_VALUE_REBOOT,
  91                                 NULL)) {
  92         pcmk__clear_node_attr_flags(options.attr_options, pcmk__node_attr_perm);
  93     } else {
  94         g_set_error(err, PCMK__EXITC_ERROR, CRM_EX_USAGE, "Unknown value for --lifetime: %s",
  95                     optarg);
  96         return FALSE;
  97     }
  98 
  99     return TRUE;
 100 }
 101 
 102 static gboolean
 103 attr_set_type_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 104     if (pcmk__str_any_of(option_name, "-z", "--utilization", NULL)) {
 105         pcmk__set_node_attr_flags(options.attr_options, pcmk__node_attr_utilization);
 106     }
 107 
 108     return TRUE;
 109 }
 110 
 111 static gboolean
 112 wait_cb (const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
     /* [previous][next][first][last][top][bottom][index][help] */
 113     if (pcmk__str_eq(optarg, "no", pcmk__str_none)) {
 114         pcmk__clear_node_attr_flags(options.attr_options, pcmk__node_attr_sync_local | pcmk__node_attr_sync_cluster);
 115         return TRUE;
 116     } else if (pcmk__str_eq(optarg, PCMK__VALUE_LOCAL, pcmk__str_none)) {
 117         pcmk__clear_node_attr_flags(options.attr_options, pcmk__node_attr_sync_local | pcmk__node_attr_sync_cluster);
 118         pcmk__set_node_attr_flags(options.attr_options, pcmk__node_attr_sync_local);
 119         return TRUE;
 120     } else if (pcmk__str_eq(optarg, PCMK__VALUE_CLUSTER, pcmk__str_none)) {
 121         pcmk__clear_node_attr_flags(options.attr_options, pcmk__node_attr_sync_local | pcmk__node_attr_sync_cluster);
 122         pcmk__set_node_attr_flags(options.attr_options, pcmk__node_attr_sync_cluster);
 123         return TRUE;
 124     } else {
 125         g_set_error(err, PCMK__EXITC_ERROR, CRM_EX_USAGE,
 126                     "--wait= must be one of 'no', 'local', 'cluster'");
 127         return FALSE;
 128     }
 129 }
 130 
 131 #define INDENT "                              "
 132 
 133 static GOptionEntry required_entries[] = {
 134     { "name", 'n', 0, G_OPTION_ARG_STRING, &options.attr_name,
 135       "The attribute's name",
 136       "NAME" },
 137 
 138     { "pattern", 'P', 0, G_OPTION_ARG_STRING, &options.attr_pattern,
 139       "Operate on all attributes matching this pattern\n"
 140       INDENT "(with -B, -D, -U, or -Y)",
 141       "PATTERN"
 142     },
 143 
 144     { NULL }
 145 };
 146 
 147 static GOptionEntry command_entries[] = {
 148     { "update", 'U', 0, G_OPTION_ARG_CALLBACK, command_cb,
 149       "Update attribute's value. Required: -n/--name or -P/--pattern.\n"
 150       INDENT "Optional: -d/--delay (if specified, the delay will be used if\n"
 151       INDENT "the attribute needs to be created, but ignored if the\n"
 152       INDENT "attribute already exists), -s/--set, -p/--private, -W/--wait,\n"
 153       INDENT "-z/--utilization.",
 154       "VALUE" },
 155 
 156     { "update-both", 'B', 0, G_OPTION_ARG_CALLBACK, command_cb,
 157       "Update attribute's value and time to wait (dampening) in the\n"
 158       INDENT "attribute manager. If this changes the value or dampening,\n"
 159       INDENT "the attribute will also be written to the cluster configuration,\n"
 160       INDENT "so be aware that repeatedly changing the dampening reduces its\n"
 161       INDENT "effectiveness.\n"
 162       INDENT "Requires -d/--delay",
 163       "VALUE" },
 164 
 165     { "update-delay", 'Y', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
 166       "Update attribute's dampening in the attribute manager. If this\n"
 167       INDENT "changes the dampening, the attribute will also be written\n"
 168       INDENT "to the cluster configuration, so be aware that repeatedly\n"
 169       INDENT "changing the dampening reduces its effectiveness.\n"
 170       INDENT "Requires -d/--delay",
 171       NULL },
 172 
 173     { "query", 'Q', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
 174       "Query the attribute's value from the attribute manager. By default\n"
 175       INDENT "this will query the value of the attribute on the local node.\n"
 176       INDENT "Use -N/--node for the value on a given node, or -A/--all for the\n"
 177       INDENT "value on all nodes.",
 178       NULL },
 179 
 180     { "delete", 'D', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
 181       "Unset attribute from the attribute manager. At the moment, there is no\n"
 182       INDENT "way to remove an attribute. This option will instead set its\n"
 183       INDENT "value to the empty string.",
 184       NULL },
 185 
 186     { "refresh", 'R', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
 187       "(Advanced) Force the attribute manager to resend all current\n"
 188       INDENT "values to the CIB",
 189       NULL },
 190 
 191     { NULL }
 192 };
 193 
 194 static GOptionEntry addl_entries[] = {
 195     { "delay", 'd', 0, G_OPTION_ARG_STRING, &options.attr_dampen,
 196       "The time to wait (dampening) in seconds for further changes\n"
 197       INDENT "before sending to the CIB",
 198       "SECONDS" },
 199 
 200     { "set", 's', 0, G_OPTION_ARG_STRING, &options.attr_set,
 201       "(Advanced) The attribute set in which to place the value",
 202       "SET" },
 203 
 204     { "node", 'N', 0, G_OPTION_ARG_STRING, &options.attr_node,
 205       "Use the named node for setting and querying the attribute (instead\n"
 206       INDENT "of the local one)",
 207       "NODE" },
 208 
 209     { "all", 'A', 0, G_OPTION_ARG_NONE, &options.query_all,
 210       "Show values of the attribute for all nodes (query only)",
 211       NULL },
 212 
 213     { "lifetime", 'l', 0, G_OPTION_ARG_CALLBACK, section_cb,
 214       "(Not yet implemented) Lifetime of the node attribute (silently\n"
 215       INDENT "ignored by cluster)",
 216       "SECTION" },
 217 
 218     { "private", 'p', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, private_cb,
 219       "If this creates a new attribute, never write the attribute to CIB",
 220       NULL },
 221 
 222     { "wait", 'W', 0, G_OPTION_ARG_CALLBACK, wait_cb,
 223       "Wait for some event to occur before returning.  Values are 'no' (wait\n"
 224       INDENT "only for the attribute daemon to acknowledge the request),\n"
 225       INDENT "'local' (wait until the change has propagated to where a local\n"
 226       INDENT "query will return the request value, or the value set by a\n"
 227       INDENT "later request), or 'cluster' (wait until the change has propagated\n"
 228       INDENT "to where a query anywhere on the cluster will return the requested\n"
 229       INDENT "value, or the value set by a later request).  Default is 'no'.",
 230       "UNTIL" },
 231 
 232     { "utilization", 'z', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, attr_set_type_cb,
 233       "When creating a new attribute, create it as a node utilization attribute\n"
 234       INDENT "instead of an instance attribute.  If the attribute already exists,\n"
 235       INDENT "its existing type (utilization vs. instance) will be used regardless.\n"
 236       INDENT "(with -B, -U, -Y)",
 237       NULL },
 238 
 239     { NULL }
 240 };
 241 
 242 static GOptionEntry deprecated_entries[] = {
 243     { "quiet", 'q', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &options.quiet,
 244       NULL,
 245       NULL },
 246 
 247     { "update", 'v', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, command_cb,
 248       NULL,
 249       NULL },
 250 
 251     { "section", 'S', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, section_cb,
 252       NULL,
 253       NULL },
 254 
 255     { NULL }
 256 };
 257 
 258 static int send_attrd_query(pcmk__output_t *out, const char *attr_name, const char *attr_node,
 259                     gboolean query_all);
 260 static int send_attrd_update(char command, const char *attr_node, const char *attr_name,
 261                              const char *attr_value, const char *attr_set,
 262                              const char *attr_dampen, uint32_t attr_options);
 263 
 264 static bool
 265 pattern_used_correctly(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 266 {
 267     /* --pattern can only be used with:
 268      * -B (update-both), -D (delete), -U (update), or -Y (update-delay)
 269      */
 270     return options.command == 'B' || options.command == 'D' || options.command == 'U' || options.command == 'Y';
 271 }
 272 
 273 static GOptionContext *
 274 build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) {
     /* [previous][next][first][last][top][bottom][index][help] */
 275     GOptionContext *context = NULL;
 276 
 277     context = pcmk__build_arg_context(args, "text (default), xml", group, NULL);
 278 
 279     pcmk__add_arg_group(context, "required", "Required Arguments:",
 280                         "Show required arguments", required_entries);
 281     pcmk__add_arg_group(context, "command", "Command:",
 282                         "Show command options (mutually exclusive)", command_entries);
 283     pcmk__add_arg_group(context, "additional", "Additional Options:",
 284                         "Show additional options", addl_entries);
 285     pcmk__add_arg_group(context, "deprecated", "Deprecated Options:",
 286                         "Show deprecated options", deprecated_entries);
 287 
 288     return context;
 289 }
 290 
 291 int
 292 main(int argc, char **argv)
     /* [previous][next][first][last][top][bottom][index][help] */
 293 {
 294     int rc = pcmk_rc_ok;
 295     crm_exit_t exit_code = CRM_EX_OK;
 296 
 297     pcmk__output_t *out = NULL;
 298 
 299     GOptionGroup *output_group = NULL;
 300     pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
 301     GOptionContext *context = build_arg_context(args, &output_group);
 302     gchar **processed_args = pcmk__cmdline_preproc(argv, "dlnsvBNUS");
 303 
 304     pcmk__register_formats(output_group, formats);
 305     if (!g_option_context_parse_strv(context, &processed_args, &error)) {
 306         exit_code = CRM_EX_USAGE;
 307         goto done;
 308     }
 309 
 310     pcmk__cli_init_logging("attrd_updater", args->verbosity);
 311 
 312     rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv);
 313     if (rc != pcmk_rc_ok) {
 314         exit_code = CRM_EX_ERROR;
 315         g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Error creating output format %s: %s",
 316                     args->output_ty, pcmk_rc_str(rc));
 317         goto done;
 318     }
 319 
 320     if (args->version) {
 321         out->version(out, false);
 322         goto done;
 323     }
 324 
 325     if (options.attr_pattern) {
 326         if (options.attr_name) {
 327             exit_code = CRM_EX_USAGE;
 328             g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 329                         "Error: --name and --pattern cannot be used at the same time");
 330             goto done;
 331         }
 332 
 333         if (!pattern_used_correctly()) {
 334             exit_code = CRM_EX_USAGE;
 335             g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 336                         "Error: pattern can only be used with delete or update");
 337             goto done;
 338         }
 339 
 340         g_free(options.attr_name);
 341         options.attr_name = options.attr_pattern;
 342         options.attr_options |= pcmk__node_attr_pattern;
 343     }
 344 
 345     if (options.command != 'R' && options.attr_name == NULL) {
 346         exit_code = CRM_EX_USAGE;
 347         g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Command requires --name or --pattern argument");
 348         goto done;
 349     } else if ((options.command == 'B'|| options.command == 'Y') && options.attr_dampen == NULL) {
 350         out->info(out, "Warning: '%c' command given without required --delay", options.command);
 351     }
 352 
 353     pcmk__register_lib_messages(out);
 354 
 355     if (options.command == 'Q') {
 356         int rc = send_attrd_query(out, options.attr_name, options.attr_node, options.query_all);
 357         exit_code = pcmk_rc2exitc(rc);
 358     } else {
 359         /* @TODO We don't know whether the specified node is a Pacemaker Remote
 360          * node or not, so we can't set pcmk__node_attr_remote when appropriate.
 361          * However, it's not a big problem, because the attribute manager will
 362          * learn and remember a node's "remoteness".
 363          */
 364         int rc = send_attrd_update(options.command, options.attr_node,
 365                                    options.attr_name, options.attr_value,
 366                                    options.attr_set, options.attr_dampen,
 367                                    options.attr_options);
 368         exit_code = pcmk_rc2exitc(rc);
 369     }
 370 
 371 done:
 372     g_strfreev(processed_args);
 373     pcmk__free_arg_context(context);
 374     g_free(options.attr_dampen);
 375     g_free(options.attr_name);
 376     g_free(options.attr_node);
 377     g_free(options.attr_set);
 378     free(options.attr_value);
 379 
 380     pcmk__output_and_clear_error(&error, out);
 381 
 382     if (out != NULL) {
 383         out->finish(out, exit_code, true, NULL);
 384         pcmk__output_free(out);
 385     }
 386 
 387     pcmk__unregister_formats();
 388     crm_exit(exit_code);
 389 }
 390 
 391 /*!
 392  * \brief Print the attribute values in an attribute manager XML query reply
 393  *
 394  * \param[in,out] out    Output object
 395  * \param[in]     reply  List of attribute name/value pairs
 396  *
 397  * \return true if any values were printed
 398  */
 399 static void
 400 print_attrd_values(pcmk__output_t *out, const GList *reply)
     /* [previous][next][first][last][top][bottom][index][help] */
 401 {
 402     for (const GList *iter = reply; iter != NULL; iter = iter->next) {
 403         const pcmk__attrd_query_pair_t *pair = iter->data;
 404 
 405         out->message(out, "attribute", NULL, NULL, pair->name, pair->value,
 406                      pair->node, false, false);
 407         printed_values = true;
 408     }
 409 }
 410 
 411 static void
 412 attrd_event_cb(pcmk_ipc_api_t *attrd_api, enum pcmk_ipc_event event_type,
     /* [previous][next][first][last][top][bottom][index][help] */
 413                crm_exit_t status, void *event_data, void *user_data)
 414 {
 415     pcmk__output_t *out = (pcmk__output_t *) user_data;
 416     pcmk__attrd_api_reply_t *reply = event_data;
 417 
 418     if (event_type != pcmk_ipc_event_reply || status != CRM_EX_OK) {
 419         return;
 420     }
 421 
 422     /* Print the values from the reply. */
 423     if (reply->reply_type == pcmk__attrd_reply_query) {
 424         print_attrd_values(out, reply->data.pairs);
 425     }
 426 }
 427 
 428 /*!
 429  * \brief Submit a query to the attribute manager and print reply
 430  *
 431  * \param[in,out] out  Output object
 432  * \param[in]     attr_name  Name of attribute to be affected by request
 433  * \param[in]     attr_node  Name of host to query for (or NULL for localhost)
 434  * \param[in]     query_all  If TRUE, ignore attr_node and query all nodes
 435  *
 436  * \return Standard Pacemaker return code
 437  */
 438 static int
 439 send_attrd_query(pcmk__output_t *out, const char *attr_name,
     /* [previous][next][first][last][top][bottom][index][help] */
 440                  const char *attr_node, gboolean query_all)
 441 {
 442     uint32_t options = pcmk__node_attr_none;
 443     pcmk_ipc_api_t *attrd_api = NULL;
 444     int rc = pcmk_rc_ok;
 445 
 446     // Create attrd IPC object
 447     rc = pcmk_new_ipc_api(&attrd_api, pcmk_ipc_attrd);
 448     if (rc != pcmk_rc_ok) {
 449         g_set_error(&error, PCMK__RC_ERROR, rc,
 450                     "Could not connect to attrd: %s", pcmk_rc_str(rc));
 451         return ENOTCONN;
 452     }
 453 
 454     pcmk_register_ipc_callback(attrd_api, attrd_event_cb, out);
 455 
 456     // Connect to attrd (without main loop)
 457     rc = pcmk__connect_ipc(attrd_api, pcmk_ipc_dispatch_sync, 5);
 458     if (rc != pcmk_rc_ok) {
 459         g_set_error(&error, PCMK__RC_ERROR, rc,
 460                     "Could not connect to %s: %s",
 461                     pcmk_ipc_name(attrd_api, true), pcmk_rc_str(rc));
 462         pcmk_free_ipc_api(attrd_api);
 463         return rc;
 464     }
 465 
 466     /* Decide which node(s) to query */
 467     if (query_all == TRUE) {
 468         options |= pcmk__node_attr_query_all;
 469     }
 470 
 471     rc = pcmk__attrd_api_query(attrd_api, attr_node, attr_name, options);
 472 
 473     if (rc != pcmk_rc_ok) {
 474         g_set_error(&error, PCMK__RC_ERROR, rc, "Could not query value of %s: %s (%d)",
 475                     attr_name, pcmk_rc_str(rc), rc);
 476     } else if (!printed_values) {
 477         rc = pcmk_rc_schema_validation;
 478         g_set_error(&error, PCMK__RC_ERROR, rc,
 479                     "Could not query value of %s: attribute does not exist", attr_name);
 480     }
 481 
 482     pcmk_disconnect_ipc(attrd_api);
 483     pcmk_free_ipc_api(attrd_api);
 484 
 485     return rc;
 486 }
 487 
 488 static int
 489 send_attrd_update(char command, const char *attr_node, const char *attr_name,
     /* [previous][next][first][last][top][bottom][index][help] */
 490                   const char *attr_value, const char *attr_set,
 491                   const char *attr_dampen, uint32_t attr_options)
 492 {
 493     int rc = pcmk_rc_ok;
 494 
 495     switch (command) {
 496         case 'B':
 497             rc = pcmk__attrd_api_update(NULL, attr_node, attr_name, attr_value,
 498                                         attr_dampen, attr_set, NULL,
 499                                         attr_options | pcmk__node_attr_value | pcmk__node_attr_delay);
 500             break;
 501 
 502         case 'D':
 503             rc = pcmk__attrd_api_delete(NULL, attr_node, attr_name, attr_options);
 504             break;
 505 
 506         case 'R':
 507             rc = pcmk__attrd_api_refresh(NULL, attr_node);
 508             break;
 509 
 510         case 'U':
 511             rc = pcmk__attrd_api_update(NULL, attr_node, attr_name, attr_value,
 512                                         attr_dampen, attr_set, NULL,
 513                                         attr_options | pcmk__node_attr_value);
 514             break;
 515 
 516         case 'Y':
 517             rc = pcmk__attrd_api_update(NULL, attr_node, attr_name, NULL,
 518                                         attr_dampen, attr_set, NULL,
 519                                         attr_options | pcmk__node_attr_delay);
 520             break;
 521     }
 522 
 523     if (rc != pcmk_rc_ok) {
 524         g_set_error(&error, PCMK__RC_ERROR, rc, "Could not update %s=%s: %s (%d)",
 525                     attr_name, attr_value, pcmk_rc_str(rc), rc);
 526     }
 527 
 528     return rc;
 529 }

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