root/tools/crm_attribute.c

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

DEFINITIONS

This source file includes following definitions.
  1. delete_cb
  2. promotion_cb
  3. update_cb
  4. utilization_cb
  5. value_cb
  6. build_arg_context
  7. main

   1 /*
   2  * Copyright 2004-2021 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 <stdint.h>
  13 #include <stdio.h>
  14 #include <unistd.h>
  15 #include <stdlib.h>
  16 #include <errno.h>
  17 #include <fcntl.h>
  18 #include <libgen.h>
  19 #include <time.h>
  20 
  21 #include <sys/param.h>
  22 #include <sys/types.h>
  23 
  24 #include <crm/crm.h>
  25 #include <crm/msg_xml.h>
  26 #include <crm/common/xml.h>
  27 #include <crm/common/ipc.h>
  28 #include <crm/common/util.h>
  29 #include <crm/cluster.h>
  30 
  31 #include <crm/cib.h>
  32 #include <crm/cib/internal.h>
  33 #include <crm/common/attrd_internal.h>
  34 #include <crm/common/cmdline_internal.h>
  35 #include <sys/utsname.h>
  36 
  37 #define SUMMARY "crm_attribute - query and update Pacemaker cluster options and node attributes"
  38 
  39 crm_exit_t exit_code = CRM_EX_OK;
  40 uint64_t cib_opts = cib_sync_call;
  41 
  42 struct {
  43     char command;
  44     gchar *attr_default;
  45     gchar *attr_id;
  46     gchar *attr_name;
  47     gchar *attr_pattern;
  48     char *attr_value;
  49     char *dest_node;
  50     gchar *dest_uname;
  51     gboolean inhibit;
  52     gchar *set_name;
  53     char *set_type;
  54     gchar *type;
  55     gboolean promotion_score;
  56 } options = {
  57     .command = 'G',
  58     .promotion_score = FALSE
  59 };
  60 
  61 gboolean BE_QUIET = FALSE;
  62 
  63 #define INDENT "                              "
  64 
  65 static gboolean
  66 delete_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
  67     options.command = 'D';
  68 
  69     if (options.attr_value) {
  70         free(options.attr_value);
  71     }
  72 
  73     options.attr_value = NULL;
  74     return TRUE;
  75 }
  76 
  77 static gboolean
  78 promotion_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
  79     char *score_name = NULL;
  80 
  81     options.promotion_score = TRUE;
  82 
  83     if (options.attr_name) {
  84         g_free(options.attr_name);
  85     }
  86 
  87     score_name = pcmk_promotion_score_name(optarg);
  88     if (score_name != NULL) {
  89         options.attr_name = g_strdup(score_name);
  90         free(score_name);
  91     } else {
  92         options.attr_name = NULL;
  93     }
  94 
  95     return TRUE;
  96 }
  97 
  98 static gboolean
  99 update_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 100     options.command = 'v';
 101 
 102     if (options.attr_value) {
 103         free(options.attr_value);
 104     }
 105 
 106     options.attr_value = strdup(optarg);
 107     return TRUE;
 108 }
 109 
 110 static gboolean
 111 utilization_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 112     if (options.type) {
 113         g_free(options.type);
 114     }
 115 
 116     options.type = g_strdup(XML_CIB_TAG_NODES);
 117 
 118     if (options.set_type) {
 119         free(options.set_type);
 120     }
 121 
 122     options.set_type = strdup(XML_TAG_UTILIZATION);
 123     return TRUE;
 124 }
 125 
 126 static gboolean
 127 value_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 128     options.command = 'G';
 129 
 130     if (options.attr_value) {
 131         free(options.attr_value);
 132     }
 133 
 134     options.attr_value = NULL;
 135     return TRUE;
 136 }
 137 
 138 static GOptionEntry selecting_entries[] = {
 139     { "id", 'i', 0, G_OPTION_ARG_STRING, &options.attr_id,
 140       "(Advanced) Operate on instance of specified attribute with this\n"
 141       INDENT "XML ID",
 142       "XML_ID"
 143     },
 144 
 145     { "name", 'n', 0, G_OPTION_ARG_STRING, &options.attr_name,
 146       "Operate on attribute or option with this name",
 147       "NAME"
 148     },
 149 
 150     { "pattern", 'P', 0, G_OPTION_ARG_STRING, &options.attr_pattern,
 151       "Operate on all attributes matching this pattern\n"
 152       INDENT "(with -v/-D and -l reboot)",
 153       "PATTERN"
 154     },
 155 
 156     { "promotion", 'p', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK, promotion_cb,
 157       "Operate on node attribute used as promotion score for specified\n"
 158       INDENT "resource, or resource given in OCF_RESOURCE_INSTANCE environment\n"
 159       INDENT "variable if none is specified; this also defaults -l/--lifetime\n"
 160       INDENT "to reboot (normally invoked from an OCF resource agent)",
 161       "RESOURCE"
 162     },
 163 
 164     { "set-name", 's', 0, G_OPTION_ARG_STRING, &options.set_name,
 165       "(Advanced) Operate on instance of specified attribute that is\n"
 166       INDENT "within set with this XML ID",
 167       "NAME"
 168     },
 169 
 170     { NULL }
 171 };
 172 
 173 static GOptionEntry command_entries[] = {
 174     { "delete", 'D', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, delete_cb,
 175       "Delete the attribute/option",
 176       NULL
 177     },
 178 
 179     { "query", 'G', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, value_cb,
 180       "Query the current value of the attribute/option",
 181       NULL
 182     },
 183 
 184     { "update", 'v', 0, G_OPTION_ARG_CALLBACK, update_cb,
 185       "Update the value of the attribute/option",
 186       "VALUE"
 187     },
 188 
 189     { NULL }
 190 };
 191 
 192 static GOptionEntry addl_entries[] = {
 193     { "default", 'd', 0, G_OPTION_ARG_STRING, &options.attr_default,
 194       "(Advanced) Default value to display if none is found in configuration",
 195       "VALUE"
 196     },
 197 
 198     { "lifetime", 'l', 0, G_OPTION_ARG_STRING, &options.type,
 199       "Lifetime of the node attribute.\n"
 200       INDENT "Valid values: reboot, forever",
 201       "LIFETIME"
 202     },
 203 
 204     { "node", 'N', 0, G_OPTION_ARG_STRING, &options.dest_uname,
 205       "Set a node attribute for named node (instead of a cluster option).\n"
 206       INDENT "See also: -l",
 207       "NODE"
 208     },
 209 
 210     { "type", 't', 0, G_OPTION_ARG_STRING, &options.type,
 211       "Which part of the configuration to update/delete/query the option in.\n"
 212       INDENT "Valid values: crm_config, rsc_defaults, op_defaults, tickets",
 213       "SECTION"
 214     },
 215 
 216     { "utilization", 'z', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, utilization_cb,
 217       "Set an utilization attribute for the node.",
 218       NULL
 219     },
 220 
 221     { "inhibit-policy-engine", '!', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &options.inhibit,
 222       NULL, NULL
 223     },
 224 
 225     { NULL }
 226 };
 227 
 228 static GOptionEntry deprecated_entries[] = {
 229     { "attr-id", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &options.attr_id,
 230       NULL, NULL
 231     },
 232 
 233     { "attr-name", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &options.attr_name,
 234       NULL, NULL
 235     },
 236 
 237     { "attr-value", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, update_cb,
 238       NULL, NULL
 239     },
 240 
 241     { "delete-attr", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, delete_cb,
 242       NULL, NULL
 243     },
 244 
 245     { "get-value", 0, G_OPTION_FLAG_HIDDEN|G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, value_cb,
 246       NULL, NULL
 247     },
 248 
 249     { "node-uname", 'U', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &options.dest_uname,
 250       NULL, NULL
 251     },
 252 
 253     { NULL }
 254 };
 255 
 256 static GOptionContext *
 257 build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) {
     /* [previous][next][first][last][top][bottom][index][help] */
 258     GOptionContext *context = NULL;
 259 
 260     GOptionEntry extra_prog_entries[] = {
 261         { "quiet", 'q', 0, G_OPTION_ARG_NONE, &(args->quiet),
 262           "Print only the value on stdout",
 263           NULL },
 264 
 265         { "quiet", 'Q', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &(args->quiet),
 266           NULL, NULL
 267         },
 268 
 269         { NULL }
 270     };
 271 
 272     const char *description = "Examples:\n\n"
 273                               "Add new node attribute called 'location' with the value of 'office' for host 'myhost':\n\n"
 274                               "\tcrm_attribute --node myhost --name location --update office\n\n"
 275                               "Query the value of the 'location' node attribute for host 'myhost':\n\n"
 276                               "\tcrm_attribute --node myhost --name location --query\n\n"
 277                               "Change the value of the 'location' node attribute for host 'myhost':\n\n"
 278                               "\tcrm_attribute --node myhost --name location --update backoffice\n\n"
 279                               "Delete the 'location' node attribute for host 'myhost':\n\n"
 280                               "\tcrm_attribute --node myhost --name location --delete\n\n"
 281                               "Query the value of the 'cluster-delay' cluster option:\n\n"
 282                               "\tcrm_attribute --type crm_config --name cluster-delay --query\n\n"
 283                               "Query value of the 'cluster-delay' cluster option and print only the value:\n\n"
 284                               "\tcrm_attribute --type crm_config --name cluster-delay --query --quiet\n\n";
 285 
 286     context = pcmk__build_arg_context(args, NULL, group, NULL);
 287     pcmk__add_main_args(context, extra_prog_entries);
 288     g_option_context_set_description(context, description);
 289 
 290     pcmk__add_arg_group(context, "selections", "Selecting attributes:",
 291                         "Show selecting options", selecting_entries);
 292     pcmk__add_arg_group(context, "command", "Commands:",
 293                         "Show command options", command_entries);
 294     pcmk__add_arg_group(context, "additional", "Additional options:",
 295                         "Show additional options", addl_entries);
 296     pcmk__add_arg_group(context, "deprecated", "Deprecated Options:",
 297                         "Show deprecated options", deprecated_entries);
 298 
 299     return context;
 300 }
 301 
 302 int
 303 main(int argc, char **argv)
     /* [previous][next][first][last][top][bottom][index][help] */
 304 {
 305     cib_t *the_cib = NULL;
 306     int is_remote_node = 0;
 307     bool try_attrd = true;
 308     int attrd_opts = pcmk__node_attr_none;
 309 
 310     int rc = pcmk_ok;
 311     GError *error = NULL;
 312 
 313     GOptionGroup *output_group = NULL;
 314     pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
 315     gchar **processed_args = pcmk__cmdline_preproc(argv, "NPUdilnpstv");
 316     GOptionContext *context = build_arg_context(args, &output_group);
 317 
 318     if (!g_option_context_parse_strv(context, &processed_args, &error)) {
 319         exit_code = CRM_EX_USAGE;
 320         goto done;
 321     }
 322 
 323     pcmk__cli_init_logging("crm_attribute", 0);
 324 
 325     if (args->version) {
 326         g_strfreev(processed_args);
 327         pcmk__free_arg_context(context);
 328         /* FIXME:  When crm_attribute is converted to use formatted output, this can go. */
 329         pcmk__cli_help('v', CRM_EX_OK);
 330     }
 331 
 332     if (options.promotion_score && options.attr_name == NULL) {
 333         fprintf(stderr, "-p/--promotion must be called from an "
 334                         " OCF resource agent or with a resource ID "
 335                         " specified\n\n");
 336         exit_code = CRM_EX_USAGE;
 337         goto done;
 338     }
 339 
 340     if (options.inhibit) {
 341         crm_warn("Inhibiting notifications for this update");
 342         cib__set_call_options(cib_opts, crm_system_name, cib_inhibit_notify);
 343     }
 344 
 345     if (args->quiet) {
 346         BE_QUIET = TRUE;
 347     }
 348 
 349     the_cib = cib_new();
 350     rc = the_cib->cmds->signon(the_cib, crm_system_name, cib_command);
 351 
 352     if (rc != pcmk_ok) {
 353         fprintf(stderr, "Could not connect to the CIB: %s\n",
 354                 pcmk_strerror(rc));
 355         exit_code = crm_errno2exit(rc);
 356         goto done;
 357     }
 358 
 359     // Use default CIB location if not given
 360     if (options.type == NULL) {
 361         if (options.promotion_score) {
 362             // Updating a promotion score node attribute
 363             options.type = g_strdup(XML_CIB_TAG_STATUS);
 364 
 365         } else if (options.dest_uname != NULL) {
 366             // Updating some other node attribute
 367             options.type = g_strdup(XML_CIB_TAG_NODES);
 368 
 369         } else {
 370             // Updating cluster options
 371             options.type = g_strdup(XML_CIB_TAG_CRMCONFIG);
 372         }
 373     } else if (pcmk__str_eq(options.type, "reboot", pcmk__str_casei)) {
 374         options.type = g_strdup(XML_CIB_TAG_STATUS);
 375 
 376     } else if (pcmk__str_eq(options.type, "forever", pcmk__str_casei)) {
 377         options.type = g_strdup(XML_CIB_TAG_NODES);
 378     }
 379 
 380     // Use default node if not given (except for cluster options and tickets)
 381     if (!pcmk__strcase_any_of(options.type, XML_CIB_TAG_CRMCONFIG, XML_CIB_TAG_TICKETS,
 382                               NULL)) {
 383         /* If we are being called from a resource agent via the cluster,
 384          * the correct local node name will be passed as an environment
 385          * variable. Otherwise, we have to ask the cluster.
 386          */
 387         const char *target = pcmk__node_attr_target(options.dest_uname);
 388 
 389         if (target != NULL) {
 390             g_free(options.dest_uname);
 391             options.dest_uname = g_strdup(target);
 392         }
 393 
 394         if (options.dest_uname == NULL) {
 395             options.dest_uname = g_strdup(get_local_node_name());
 396         }
 397 
 398         rc = query_node_uuid(the_cib, options.dest_uname, &options.dest_node, &is_remote_node);
 399         if (pcmk_ok != rc) {
 400             fprintf(stderr, "Could not map name=%s to a UUID\n", options.dest_uname);
 401             exit_code = crm_errno2exit(rc);
 402             goto done;
 403         }
 404     }
 405 
 406     if ((options.command == 'D') && (options.attr_name == NULL) && (options.attr_pattern == NULL)) {
 407         fprintf(stderr, "Error: must specify attribute name or pattern to delete\n");
 408         exit_code = CRM_EX_USAGE;
 409         goto done;
 410     }
 411 
 412     if (options.attr_pattern) {
 413         if (((options.command != 'v') && (options.command != 'D'))
 414             || !pcmk__str_eq(options.type, XML_CIB_TAG_STATUS, pcmk__str_casei)) {
 415 
 416             fprintf(stderr, "Error: pattern can only be used with till-reboot update or delete\n");
 417             exit_code = CRM_EX_USAGE;
 418             goto done;
 419         }
 420         options.command = 'u';
 421         g_free(options.attr_name);
 422         options.attr_name = options.attr_pattern;
 423     }
 424 
 425     // Only go through attribute manager for transient attributes
 426     try_attrd = pcmk__str_eq(options.type, XML_CIB_TAG_STATUS, pcmk__str_casei);
 427 
 428     // Don't try to contact attribute manager if we're using a file as CIB
 429     if (getenv("CIB_file") || getenv("CIB_shadow")) {
 430         try_attrd = FALSE;
 431     }
 432 
 433     if (is_remote_node) {
 434         attrd_opts = pcmk__node_attr_remote;
 435     }
 436     if (((options.command == 'v') || (options.command == 'D') || (options.command == 'u')) && try_attrd
 437         && (pcmk__node_attr_request(NULL, options.command, options.dest_uname, options.attr_name,
 438                                     options.attr_value, options.type, options.set_name, NULL, NULL,
 439                                     attrd_opts) == pcmk_rc_ok)) {
 440         crm_info("Update %s=%s sent via pacemaker-attrd",
 441                  options.attr_name, ((options.command == 'D')? "<none>" : options.attr_value));
 442 
 443     } else if (options.command == 'D') {
 444         rc = delete_attr_delegate(the_cib, cib_opts, options.type, options.dest_node, options.set_type, options.set_name,
 445                                   options.attr_id, options.attr_name, options.attr_value, TRUE, NULL);
 446 
 447         if (rc == -ENXIO) {
 448             /* Nothing to delete...
 449              * which means it's not there...
 450              * which is what the admin wanted
 451              */
 452             rc = pcmk_ok;
 453         }
 454 
 455     } else if (options.command == 'v') {
 456         CRM_LOG_ASSERT(options.type != NULL);
 457         CRM_LOG_ASSERT(options.attr_name != NULL);
 458         CRM_LOG_ASSERT(options.attr_value != NULL);
 459 
 460         rc = update_attr_delegate(the_cib, cib_opts, options.type, options.dest_node, options.set_type, options.set_name,
 461                                   options.attr_id, options.attr_name, options.attr_value, TRUE, NULL, is_remote_node ? "remote" : NULL);
 462 
 463     } else {                    /* query */
 464 
 465         char *read_value = NULL;
 466 
 467         rc = read_attr_delegate(the_cib, options.type, options.dest_node, options.set_type, options.set_name,
 468                                 options.attr_id, options.attr_name, &read_value, TRUE, NULL);
 469 
 470         if (rc == -ENXIO && options.attr_default) {
 471             read_value = strdup(options.attr_default);
 472             rc = pcmk_ok;
 473         }
 474 
 475         crm_info("Read %s=%s %s%s",
 476                  options.attr_name, crm_str(read_value), options.set_name ? "in " : "", options.set_name ? options.set_name : "");
 477 
 478         if (rc == -ENOTUNIQ) {
 479             // Multiple matches (already displayed) are not error for queries
 480             rc = pcmk_ok;
 481 
 482         } else if (BE_QUIET == FALSE) {
 483             fprintf(stdout, "%s%s %s%s %s%s value=%s\n",
 484                     options.type ? "scope=" : "", options.type ? options.type : "",
 485                     options.attr_id ? "id=" : "", options.attr_id ? options.attr_id : "",
 486                     options.attr_name ? "name=" : "", options.attr_name ? options.attr_name : "",
 487                     read_value ? read_value : "(null)");
 488 
 489         } else if (read_value != NULL) {
 490             fprintf(stdout, "%s\n", read_value);
 491         }
 492         free(read_value);
 493     }
 494 
 495     if (rc == -ENOTUNIQ) {
 496         printf("Please choose from one of the matches above and supply the 'id' with --attr-id\n");
 497         exit_code = crm_errno2exit(rc);
 498 
 499     } else if (rc != pcmk_ok) {
 500         fprintf(stderr, "Error performing operation: %s\n", pcmk_strerror(rc));
 501         exit_code = crm_errno2exit(rc);
 502     }
 503 
 504 done:
 505     g_strfreev(processed_args);
 506     pcmk__free_arg_context(context);
 507 
 508     free(options.attr_default);
 509     g_free(options.attr_id);
 510     g_free(options.attr_name);
 511     free(options.attr_value);
 512     free(options.dest_node);
 513     g_free(options.dest_uname);
 514     g_free(options.set_name);
 515     free(options.set_type);
 516     g_free(options.type);
 517 
 518     if (the_cib) {
 519         the_cib->cmds->signoff(the_cib);
 520         cib_delete(the_cib);
 521     }
 522 
 523     pcmk__output_and_clear_error(error, NULL);
 524     return crm_exit(exit_code);
 525 }

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