root/tools/crm_attribute.c

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

DEFINITIONS

This source file includes following definitions.
  1. PCMK__OUTPUT_ARGS
  2. delete_cb
  3. promotion_cb
  4. update_cb
  5. utilization_cb
  6. value_cb
  7. wait_cb
  8. get_node_name_from_local
  9. send_attrd_update
  10. delete_attr_on_node
  11. command_delete
  12. update_attr_on_node
  13. command_update
  14. output_one_attribute
  15. command_query
  16. set_type
  17. use_attrd
  18. try_ipc_update
  19. pattern_used_correctly
  20. delete_used_correctly
  21. build_arg_context
  22. main

   1 /*
   2  * Copyright 2004-2023 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 <crm/common/ipc_attrd_internal.h>
  36 #include <crm/common/ipc_controld.h>
  37 #include <crm/common/output_internal.h>
  38 #include <sys/utsname.h>
  39 
  40 #include <pacemaker-internal.h>
  41 
  42 #define SUMMARY "crm_attribute - query and update Pacemaker cluster options and node attributes"
  43 
  44 GError *error = NULL;
  45 crm_exit_t exit_code = CRM_EX_OK;
  46 uint64_t cib_opts = cib_sync_call;
  47 
  48 PCMK__OUTPUT_ARGS("attribute", "const char *", "const char *", "const char *",
     /* [previous][next][first][last][top][bottom][index][help] */
  49                   "const char *", "const char *")
  50 static int
  51 attribute_text(pcmk__output_t *out, va_list args)
  52 {
  53     const char *scope = va_arg(args, const char *);
  54     const char *instance = va_arg(args, const char *);
  55     const char *name = va_arg(args, const char *);
  56     const char *value = va_arg(args, const char *);
  57     const char *host G_GNUC_UNUSED = va_arg(args, const char *);
  58 
  59     if (out->quiet) {
  60         if (value != NULL) {
  61             pcmk__formatted_printf(out, "%s\n", value);
  62         }
  63     } else {
  64         out->info(out, "%s%s %s%s %s%s value=%s",
  65                   scope ? "scope=" : "", scope ? scope : "",
  66                   instance ? "id=" : "", instance ? instance : "",
  67                   name ? "name=" : "", name ? name : "",
  68                   value ? value : "(null)");
  69     }
  70 
  71     return pcmk_rc_ok;
  72 }
  73 
  74 static pcmk__supported_format_t formats[] = {
  75     PCMK__SUPPORTED_FORMAT_NONE,
  76     PCMK__SUPPORTED_FORMAT_TEXT,
  77     PCMK__SUPPORTED_FORMAT_XML,
  78     { NULL, NULL, NULL }
  79 };
  80 
  81 static pcmk__message_entry_t fmt_functions[] = {
  82     { "attribute", "text", attribute_text },
  83 
  84     { NULL, NULL, NULL }
  85 };
  86 
  87 struct {
  88     char command;
  89     gchar *attr_default;
  90     gchar *attr_id;
  91     gchar *attr_name;
  92     uint32_t attr_options;
  93     gchar *attr_pattern;
  94     char *attr_value;
  95     char *dest_node;
  96     gchar *dest_uname;
  97     gboolean inhibit;
  98     gchar *set_name;
  99     char *set_type;
 100     gchar *type;
 101     gboolean promotion_score;
 102 } options = {
 103     .command = 'G',
 104     .promotion_score = FALSE
 105 };
 106 
 107 #define INDENT "                               "
 108 
 109 static gboolean
 110 delete_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 111     options.command = 'D';
 112     pcmk__str_update(&options.attr_value, NULL);
 113     return TRUE;
 114 }
 115 
 116 static gboolean
 117 promotion_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 118     char *score_name = NULL;
 119 
 120     options.promotion_score = TRUE;
 121 
 122     if (options.attr_name) {
 123         g_free(options.attr_name);
 124     }
 125 
 126     score_name = pcmk_promotion_score_name(optarg);
 127     if (score_name != NULL) {
 128         options.attr_name = g_strdup(score_name);
 129         free(score_name);
 130     } else {
 131         options.attr_name = NULL;
 132     }
 133 
 134     return TRUE;
 135 }
 136 
 137 static gboolean
 138 update_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 139     options.command = 'u';
 140     pcmk__str_update(&options.attr_value, optarg);
 141     return TRUE;
 142 }
 143 
 144 static gboolean
 145 utilization_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 146     if (options.type) {
 147         g_free(options.type);
 148     }
 149 
 150     options.type = g_strdup(XML_CIB_TAG_NODES);
 151     pcmk__str_update(&options.set_type, XML_TAG_UTILIZATION);
 152     return TRUE;
 153 }
 154 
 155 static gboolean
 156 value_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 157     options.command = 'G';
 158     pcmk__str_update(&options.attr_value, NULL);
 159     return TRUE;
 160 }
 161 
 162 static gboolean
 163 wait_cb (const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
     /* [previous][next][first][last][top][bottom][index][help] */
 164     if (pcmk__str_eq(optarg, "no", pcmk__str_none)) {
 165         pcmk__clear_node_attr_flags(options.attr_options, pcmk__node_attr_sync_local | pcmk__node_attr_sync_cluster);
 166         return TRUE;
 167     } else if (pcmk__str_eq(optarg, PCMK__VALUE_LOCAL, pcmk__str_none)) {
 168         pcmk__clear_node_attr_flags(options.attr_options, pcmk__node_attr_sync_local | pcmk__node_attr_sync_cluster);
 169         pcmk__set_node_attr_flags(options.attr_options, pcmk__node_attr_sync_local);
 170         return TRUE;
 171     } else if (pcmk__str_eq(optarg, PCMK__VALUE_CLUSTER, pcmk__str_none)) {
 172         pcmk__clear_node_attr_flags(options.attr_options, pcmk__node_attr_sync_local | pcmk__node_attr_sync_cluster);
 173         pcmk__set_node_attr_flags(options.attr_options, pcmk__node_attr_sync_cluster);
 174         return TRUE;
 175     } else {
 176         g_set_error(err, PCMK__EXITC_ERROR, CRM_EX_USAGE,
 177                     "--wait= must be one of 'no', 'local', 'cluster'");
 178         return FALSE;
 179     }
 180 }
 181 
 182 static GOptionEntry selecting_entries[] = {
 183     { "id", 'i', 0, G_OPTION_ARG_STRING, &options.attr_id,
 184       "(Advanced) Operate on instance of specified attribute with this\n"
 185       INDENT "XML ID",
 186       "XML_ID"
 187     },
 188 
 189     { "name", 'n', 0, G_OPTION_ARG_STRING, &options.attr_name,
 190       "Operate on attribute or option with this name.  For queries, this\n"
 191       INDENT "is optional, in which case all matching attributes will be\n"
 192       INDENT "returned.",
 193       "NAME"
 194     },
 195 
 196     { "pattern", 'P', 0, G_OPTION_ARG_STRING, &options.attr_pattern,
 197       "Operate on all attributes matching this pattern\n"
 198       INDENT "(with -v, -D, or -G)",
 199       "PATTERN"
 200     },
 201 
 202     { "promotion", 'p', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK, promotion_cb,
 203       "Operate on node attribute used as promotion score for specified\n"
 204       INDENT "resource, or resource given in OCF_RESOURCE_INSTANCE environment\n"
 205       INDENT "variable if none is specified; this also defaults -l/--lifetime\n"
 206       INDENT "to reboot (normally invoked from an OCF resource agent)",
 207       "RESOURCE"
 208     },
 209 
 210     { "set-name", 's', 0, G_OPTION_ARG_STRING, &options.set_name,
 211       "(Advanced) Operate on instance of specified attribute that is\n"
 212       INDENT "within set with this XML ID",
 213       "NAME"
 214     },
 215 
 216     { NULL }
 217 };
 218 
 219 static GOptionEntry command_entries[] = {
 220     { "delete", 'D', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, delete_cb,
 221       "Delete the attribute/option",
 222       NULL
 223     },
 224 
 225     { "query", 'G', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, value_cb,
 226       "Query the current value of the attribute/option.\n"
 227       INDENT "See also: -n, -P",
 228       NULL
 229     },
 230 
 231     { "update", 'v', 0, G_OPTION_ARG_CALLBACK, update_cb,
 232       "Update the value of the attribute/option",
 233       "VALUE"
 234     },
 235 
 236     { NULL }
 237 };
 238 
 239 static GOptionEntry addl_entries[] = {
 240     { "default", 'd', 0, G_OPTION_ARG_STRING, &options.attr_default,
 241       "(Advanced) Default value to display if none is found in configuration",
 242       "VALUE"
 243     },
 244 
 245     { "lifetime", 'l', 0, G_OPTION_ARG_STRING, &options.type,
 246       "Lifetime of the node attribute.\n"
 247       INDENT "Valid values: reboot, forever",
 248       "LIFETIME"
 249     },
 250 
 251     { "node", 'N', 0, G_OPTION_ARG_STRING, &options.dest_uname,
 252       "Set a node attribute for named node (instead of a cluster option).\n"
 253       INDENT "See also: -l",
 254       "NODE"
 255     },
 256 
 257     { "type", 't', 0, G_OPTION_ARG_STRING, &options.type,
 258       "Which part of the configuration to update/delete/query the option in.\n"
 259       INDENT "Valid values: crm_config, rsc_defaults, op_defaults, tickets",
 260       "SECTION"
 261     },
 262 
 263     { "wait", 'W', 0, G_OPTION_ARG_CALLBACK, wait_cb,
 264       "Wait for some event to occur before returning.  Values are 'no' (wait\n"
 265       INDENT "only for the attribute daemon to acknowledge the request),\n"
 266       INDENT "'local' (wait until the change has propagated to where a local\n"
 267       INDENT "query will return the request value, or the value set by a\n"
 268       INDENT "later request), or 'cluster' (wait until the change has propagated\n"
 269       INDENT "to where a query anywhere on the cluster will return the requested\n"
 270       INDENT "value, or the value set by a later request).  Default is 'no'.\n"
 271       INDENT "(with -N, and one of -D or -u)",
 272       "UNTIL" },
 273 
 274     { "utilization", 'z', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, utilization_cb,
 275       "Set an utilization attribute for the node.",
 276       NULL
 277     },
 278 
 279     { "inhibit-policy-engine", '!', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &options.inhibit,
 280       NULL, NULL
 281     },
 282 
 283     { NULL }
 284 };
 285 
 286 static GOptionEntry deprecated_entries[] = {
 287     { "attr-id", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &options.attr_id,
 288       NULL, NULL
 289     },
 290 
 291     { "attr-name", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &options.attr_name,
 292       NULL, NULL
 293     },
 294 
 295     { "attr-value", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, update_cb,
 296       NULL, NULL
 297     },
 298 
 299     { "delete-attr", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, delete_cb,
 300       NULL, NULL
 301     },
 302 
 303     { "get-value", 0, G_OPTION_FLAG_HIDDEN|G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, value_cb,
 304       NULL, NULL
 305     },
 306 
 307     { "node-uname", 'U', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &options.dest_uname,
 308       NULL, NULL
 309     },
 310 
 311     { NULL }
 312 };
 313 
 314 static void
 315 get_node_name_from_local(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 316 {
 317     char *hostname = pcmk_hostname();
 318 
 319     g_free(options.dest_uname);
 320 
 321     /* This silliness is so that dest_uname is always a glib-managed
 322      * string so we know how to free it later.  pcmk_hostname returns
 323      * a newly allocated string via strdup.
 324      */
 325     options.dest_uname = g_strdup(hostname);
 326     free(hostname);
 327 }
 328 
 329 static int
 330 send_attrd_update(char command, const char *attr_node, const char *attr_name,
     /* [previous][next][first][last][top][bottom][index][help] */
 331                   const char *attr_value, const char *attr_set,
 332                   const char *attr_dampen, uint32_t attr_options)
 333 {
 334     int rc = pcmk_rc_ok;
 335     uint32_t opts = attr_options;
 336 
 337     switch (command) {
 338         case 'D':
 339             rc = pcmk__attrd_api_delete(NULL, attr_node, attr_name, opts);
 340             break;
 341 
 342         case 'u':
 343             rc = pcmk__attrd_api_update(NULL, attr_node, attr_name,
 344                                         attr_value, NULL, attr_set, NULL,
 345                                         opts | pcmk__node_attr_value);
 346             break;
 347     }
 348 
 349     if (rc != pcmk_rc_ok) {
 350         g_set_error(&error, PCMK__RC_ERROR, rc, "Could not update %s=%s: %s (%d)",
 351                     attr_name, attr_value, pcmk_rc_str(rc), rc);
 352     }
 353 
 354     return rc;
 355 }
 356 
 357 struct delete_data_s {
 358     pcmk__output_t *out;
 359     cib_t *cib;
 360 };
 361 
 362 static int
 363 delete_attr_on_node(xmlNode *child, void *userdata)
     /* [previous][next][first][last][top][bottom][index][help] */
 364 {
 365     struct delete_data_s *dd = (struct delete_data_s *) userdata;
 366 
 367     const char *attr_name = crm_element_value(child, XML_NVPAIR_ATTR_NAME);
 368     int rc = pcmk_rc_ok;
 369 
 370     if (!pcmk__str_eq(attr_name, options.attr_pattern, pcmk__str_regex)) {
 371         return pcmk_rc_ok;
 372     }
 373 
 374     rc = cib__delete_node_attr(dd->out, dd->cib, cib_opts, options.type,
 375                                options.dest_node, options.set_type,
 376                                options.set_name, options.attr_id,
 377                                attr_name, options.attr_value, NULL);
 378 
 379     if (rc == ENXIO) {
 380         rc = pcmk_rc_ok;
 381     }
 382 
 383     return rc;
 384 }
 385 
 386 static int
 387 command_delete(pcmk__output_t *out, cib_t *cib)
     /* [previous][next][first][last][top][bottom][index][help] */
 388 {
 389     int rc = pcmk_rc_ok;
 390 
 391     xmlNode *result = NULL;
 392     bool use_pattern = options.attr_pattern != NULL;
 393 
 394     /* See the comment in command_query regarding xpath and regular expressions. */
 395     if (use_pattern) {
 396         struct delete_data_s dd = { out, cib };
 397 
 398         rc = cib__get_node_attrs(out, cib, options.type, options.dest_node,
 399                                  options.set_type, options.set_name, NULL, NULL,
 400                                  NULL, &result);
 401 
 402         if (rc != pcmk_rc_ok) {
 403             goto done_deleting;
 404         }
 405 
 406         rc = pcmk__xe_foreach_child(result, NULL, delete_attr_on_node, &dd);
 407 
 408         if (rc != pcmk_rc_ok) {
 409             goto done_deleting;
 410         }
 411 
 412     } else {
 413         rc = cib__delete_node_attr(out, cib, cib_opts, options.type, options.dest_node,
 414                                    options.set_type, options.set_name, options.attr_id,
 415                                    options.attr_name, options.attr_value, NULL);
 416     }
 417 
 418 done_deleting:
 419     free_xml(result);
 420 
 421     if (rc == ENXIO) {
 422         /* Nothing to delete...
 423          * which means it's not there...
 424          * which is what the admin wanted
 425          */
 426         rc = pcmk_rc_ok;
 427     }
 428 
 429     return rc;
 430 }
 431 
 432 struct update_data_s {
 433     pcmk__output_t *out;
 434     cib_t *cib;
 435     int is_remote_node;
 436 };
 437 
 438 static int
 439 update_attr_on_node(xmlNode *child, void *userdata)
     /* [previous][next][first][last][top][bottom][index][help] */
 440 {
 441     struct update_data_s *ud = (struct update_data_s *) userdata;
 442 
 443     const char *attr_name = crm_element_value(child, XML_NVPAIR_ATTR_NAME);
 444 
 445     if (!pcmk__str_eq(attr_name, options.attr_pattern, pcmk__str_regex)) {
 446         return pcmk_rc_ok;
 447     }
 448 
 449     return cib__update_node_attr(ud->out, ud->cib, cib_opts, options.type,
 450                                  options.dest_node, options.set_type,
 451                                  options.set_name, options.attr_id,
 452                                  attr_name, options.attr_value, NULL,
 453                                  ud->is_remote_node ? "remote" : NULL);
 454 }
 455 
 456 static int
 457 command_update(pcmk__output_t *out, cib_t *cib, int is_remote_node)
     /* [previous][next][first][last][top][bottom][index][help] */
 458 {
 459     int rc = pcmk_rc_ok;
 460 
 461     xmlNode *result = NULL;
 462     bool use_pattern = options.attr_pattern != NULL;
 463 
 464     CRM_LOG_ASSERT(options.type != NULL);
 465     CRM_LOG_ASSERT(options.attr_name != NULL);
 466     CRM_LOG_ASSERT(options.attr_value != NULL);
 467 
 468     /* See the comment in command_query regarding xpath and regular expressions. */
 469     if (use_pattern) {
 470         struct update_data_s ud = { out, cib, is_remote_node };
 471 
 472         rc = cib__get_node_attrs(out, cib, options.type, options.dest_node,
 473                                  options.set_type, options.set_name, NULL, NULL,
 474                                  NULL, &result);
 475 
 476         if (rc != pcmk_rc_ok) {
 477             goto done_updating;
 478         }
 479 
 480         rc = pcmk__xe_foreach_child(result, NULL, update_attr_on_node, &ud);
 481 
 482         if (rc != pcmk_rc_ok) {
 483             goto done_updating;
 484         }
 485 
 486     } else {
 487         rc = cib__update_node_attr(out, cib, cib_opts, options.type,
 488                                    options.dest_node, options.set_type,
 489                                    options.set_name, options.attr_id,
 490                                    options.attr_name, options.attr_value,
 491                                    NULL, is_remote_node ? "remote" : NULL);
 492     }
 493 
 494 done_updating:
 495     free_xml(result);
 496     return rc;
 497 }
 498 
 499 struct output_data_s {
 500     pcmk__output_t *out;
 501     bool use_pattern;
 502     bool did_output;
 503 };
 504 
 505 static int
 506 output_one_attribute(xmlNode *node, void *userdata)
     /* [previous][next][first][last][top][bottom][index][help] */
 507 {
 508     struct output_data_s *od = (struct output_data_s *) userdata;
 509 
 510     const char *name = crm_element_value(node, XML_NVPAIR_ATTR_NAME);
 511     const char *value = crm_element_value(node, XML_NVPAIR_ATTR_VALUE);
 512     const char *host = crm_element_value(node, PCMK__XA_ATTR_NODE_NAME);
 513 
 514     const char *type = options.type;
 515     const char *attr_id = options.attr_id;
 516 
 517     if (od->use_pattern && !pcmk__str_eq(name, options.attr_pattern, pcmk__str_regex)) {
 518         return pcmk_rc_ok;
 519     }
 520 
 521     od->out->message(od->out, "attribute", type, attr_id, name, value, host);
 522     od->did_output = true;
 523     crm_info("Read %s='%s' %s%s",
 524              pcmk__s(name, "<null>"), pcmk__s(value, ""),
 525              options.set_name ? "in " : "", options.set_name ? options.set_name : "");
 526 
 527     return pcmk_rc_ok;
 528 }
 529 
 530 static int
 531 command_query(pcmk__output_t *out, cib_t *cib)
     /* [previous][next][first][last][top][bottom][index][help] */
 532 {
 533     int rc = pcmk_rc_ok;
 534 
 535     xmlNode *result = NULL;
 536     bool use_pattern = options.attr_pattern != NULL;
 537 
 538     /* libxml2 doesn't support regular expressions in xpath queries (which is how
 539      * cib__get_node_attrs -> find_attr finds attributes).  So instead, we'll just
 540      * find all the attributes for a given node here by passing NULL for attr_id
 541      * and attr_name, and then later see if they match the given pattern.
 542      */
 543     if (use_pattern) {
 544         rc = cib__get_node_attrs(out, cib, options.type, options.dest_node,
 545                                  options.set_type, options.set_name, NULL,
 546                                  NULL, NULL, &result);
 547     } else {
 548         rc = cib__get_node_attrs(out, cib, options.type, options.dest_node,
 549                                  options.set_type, options.set_name, options.attr_id,
 550                                  options.attr_name, NULL, &result);
 551     }
 552 
 553     if (rc == ENXIO && options.attr_default) {
 554         /* Make static analysis happy */
 555         const char *type = options.type;
 556         const char *attr_id = options.attr_id;
 557         const char *attr_name = options.attr_name;
 558         const char *attr_default = options.attr_default;
 559         const char *dest_uname = options.dest_uname;
 560 
 561         out->message(out, "attribute", type, attr_id, attr_name, attr_default,
 562                      dest_uname);
 563         rc = pcmk_rc_ok;
 564 
 565     } else if (rc != pcmk_rc_ok) {
 566         // Don't do anything.
 567 
 568     } else if (xml_has_children(result)) {
 569         struct output_data_s od = { out, use_pattern, false };
 570 
 571         pcmk__xe_foreach_child(result, NULL, output_one_attribute, &od);
 572 
 573         if (!od.did_output) {
 574             rc = ENXIO;
 575         }
 576 
 577     } else {
 578         struct output_data_s od = { out, use_pattern, false };
 579         output_one_attribute(result, &od);
 580     }
 581 
 582     free_xml(result);
 583     return rc;
 584 }
 585 
 586 static void
 587 set_type(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 588 {
 589     if (options.type == NULL) {
 590         if (options.promotion_score) {
 591             // Updating a promotion score node attribute
 592             options.type = g_strdup(XML_CIB_TAG_STATUS);
 593 
 594         } else if (options.dest_uname != NULL) {
 595             // Updating some other node attribute
 596             options.type = g_strdup(XML_CIB_TAG_NODES);
 597 
 598         } else {
 599             // Updating cluster options
 600             options.type = g_strdup(XML_CIB_TAG_CRMCONFIG);
 601         }
 602 
 603     } else if (pcmk__str_eq(options.type, "reboot", pcmk__str_casei)) {
 604         options.type = g_strdup(XML_CIB_TAG_STATUS);
 605 
 606     } else if (pcmk__str_eq(options.type, "forever", pcmk__str_casei)) {
 607         options.type = g_strdup(XML_CIB_TAG_NODES);
 608     }
 609 }
 610 
 611 static bool
 612 use_attrd(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 613 {
 614     /* Only go through the attribute manager for transient attributes, and
 615      * then only if we're not using a file as the CIB.
 616      */
 617     return pcmk__str_eq(options.type, XML_CIB_TAG_STATUS, pcmk__str_casei) &&
 618            getenv("CIB_file") == NULL && getenv("CIB_shadow") == NULL;
 619 }
 620 
 621 static bool
 622 try_ipc_update(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 623 {
 624     return use_attrd() && (options.command == 'D' || options.command == 'u');
 625 }
 626 
 627 static bool
 628 pattern_used_correctly(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 629 {
 630     /* --pattern can only be used with:
 631      * -G (query), -v (update), or -D (delete)
 632      */
 633     return options.command == 'G' || options.command == 'u' || options.command == 'D';
 634 }
 635 
 636 static bool
 637 delete_used_correctly(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 638 {
 639     return options.command != 'D' || options.attr_name != NULL || options.attr_pattern != NULL;
 640 }
 641 
 642 static GOptionContext *
 643 build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) {
     /* [previous][next][first][last][top][bottom][index][help] */
 644     GOptionContext *context = NULL;
 645 
 646     GOptionEntry extra_prog_entries[] = {
 647         { "quiet", 'q', 0, G_OPTION_ARG_NONE, &(args->quiet),
 648           "Print only the value on stdout",
 649           NULL },
 650 
 651         { "quiet", 'Q', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &(args->quiet),
 652           NULL, NULL
 653         },
 654 
 655         { NULL }
 656     };
 657 
 658     const char *description = "Examples:\n\n"
 659                               "Add new node attribute called 'location' with the value of 'office' for host 'myhost':\n\n"
 660                               "\tcrm_attribute --node myhost --name location --update office\n\n"
 661                               "Query the value of the 'location' node attribute for host 'myhost':\n\n"
 662                               "\tcrm_attribute --node myhost --name location --query\n\n"
 663                               "Change the value of the 'location' node attribute for host 'myhost':\n\n"
 664                               "\tcrm_attribute --node myhost --name location --update backoffice\n\n"
 665                               "Delete the 'location' node attribute for host 'myhost':\n\n"
 666                               "\tcrm_attribute --node myhost --name location --delete\n\n"
 667                               "Query the value of the 'cluster-delay' cluster option:\n\n"
 668                               "\tcrm_attribute --type crm_config --name cluster-delay --query\n\n"
 669                               "Query value of the 'cluster-delay' cluster option and print only the value:\n\n"
 670                               "\tcrm_attribute --type crm_config --name cluster-delay --query --quiet\n\n";
 671 
 672     context = pcmk__build_arg_context(args, "text (default), xml", group, NULL);
 673     pcmk__add_main_args(context, extra_prog_entries);
 674     g_option_context_set_description(context, description);
 675 
 676     pcmk__add_arg_group(context, "selections", "Selecting attributes:",
 677                         "Show selecting options", selecting_entries);
 678     pcmk__add_arg_group(context, "command", "Commands:",
 679                         "Show command options", command_entries);
 680     pcmk__add_arg_group(context, "additional", "Additional options:",
 681                         "Show additional options", addl_entries);
 682     pcmk__add_arg_group(context, "deprecated", "Deprecated Options:",
 683                         "Show deprecated options", deprecated_entries);
 684 
 685     return context;
 686 }
 687 
 688 int
 689 main(int argc, char **argv)
     /* [previous][next][first][last][top][bottom][index][help] */
 690 {
 691     cib_t *the_cib = NULL;
 692     int is_remote_node = 0;
 693 
 694     int rc = pcmk_rc_ok;
 695 
 696     pcmk__output_t *out = NULL;
 697 
 698     GOptionGroup *output_group = NULL;
 699     pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
 700     gchar **processed_args = pcmk__cmdline_preproc(argv, "NPUdilnpstv");
 701     GOptionContext *context = build_arg_context(args, &output_group);
 702 
 703     pcmk__register_formats(output_group, formats);
 704     if (!g_option_context_parse_strv(context, &processed_args, &error)) {
 705         exit_code = CRM_EX_USAGE;
 706         goto done;
 707     }
 708 
 709     pcmk__cli_init_logging("crm_attribute", args->verbosity);
 710 
 711     rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv);
 712     if (rc != pcmk_rc_ok) {
 713         exit_code = CRM_EX_ERROR;
 714         g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Error creating output format %s: %s",
 715                     args->output_ty, pcmk_rc_str(rc));
 716         goto done;
 717     }
 718 
 719     pcmk__register_lib_messages(out);
 720     pcmk__register_messages(out, fmt_functions);
 721 
 722     if (args->version) {
 723         out->version(out, false);
 724         goto done;
 725     }
 726 
 727     out->quiet = args->quiet;
 728 
 729     if (options.promotion_score && options.attr_name == NULL) {
 730         exit_code = CRM_EX_USAGE;
 731         g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 732                     "-p/--promotion must be called from an OCF resource agent "
 733                     "or with a resource ID specified");
 734         goto done;
 735     }
 736 
 737     if (options.inhibit) {
 738         crm_warn("Inhibiting notifications for this update");
 739         cib__set_call_options(cib_opts, crm_system_name, cib_inhibit_notify);
 740     }
 741 
 742     the_cib = cib_new();
 743     rc = the_cib->cmds->signon(the_cib, crm_system_name, cib_command);
 744     rc = pcmk_legacy2rc(rc);
 745 
 746     if (rc != pcmk_rc_ok) {
 747         exit_code = pcmk_rc2exitc(rc);
 748         g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 749                     "Could not connect to the CIB: %s", pcmk_rc_str(rc));
 750         goto done;
 751     }
 752 
 753     set_type();
 754 
 755     // Use default node if not given (except for cluster options and tickets)
 756     if (!pcmk__strcase_any_of(options.type, XML_CIB_TAG_CRMCONFIG, XML_CIB_TAG_TICKETS,
 757                               NULL)) {
 758         /* If we are being called from a resource agent via the cluster,
 759          * the correct local node name will be passed as an environment
 760          * variable. Otherwise, we have to ask the cluster.
 761          */
 762         const char *target = pcmk__node_attr_target(options.dest_uname);
 763 
 764         if (target != NULL) {
 765             g_free(options.dest_uname);
 766             options.dest_uname = g_strdup(target);
 767         } else if (getenv("CIB_file") != NULL && options.dest_uname == NULL) {
 768             get_node_name_from_local();
 769         }
 770 
 771         if (options.dest_uname == NULL) {
 772             char *node_name = NULL;
 773 
 774             rc = pcmk__query_node_name(out, 0, &node_name, 0);
 775 
 776             if (rc != pcmk_rc_ok) {
 777                 exit_code = pcmk_rc2exitc(rc);
 778                 free(node_name);
 779                 goto done;
 780             }
 781             options.dest_uname = g_strdup(node_name);
 782             free(node_name);
 783         }
 784 
 785         rc = query_node_uuid(the_cib, options.dest_uname, &options.dest_node, &is_remote_node);
 786         rc = pcmk_legacy2rc(rc);
 787 
 788         if (rc != pcmk_rc_ok) {
 789             exit_code = pcmk_rc2exitc(rc);
 790             g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 791                         "Could not map name=%s to a UUID", options.dest_uname);
 792             goto done;
 793         }
 794     }
 795 
 796     if (!delete_used_correctly()) {
 797         exit_code = CRM_EX_USAGE;
 798         g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 799                     "Error: must specify attribute name or pattern to delete");
 800         goto done;
 801     }
 802 
 803     if (options.attr_pattern) {
 804         if (options.attr_name) {
 805             exit_code = CRM_EX_USAGE;
 806             g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 807                         "Error: --name and --pattern cannot be used at the same time");
 808             goto done;
 809         }
 810 
 811         if (!pattern_used_correctly()) {
 812             exit_code = CRM_EX_USAGE;
 813             g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 814                         "Error: pattern can only be used with delete, query, or update");
 815             goto done;
 816         }
 817 
 818         g_free(options.attr_name);
 819         options.attr_name = options.attr_pattern;
 820         options.attr_options |= pcmk__node_attr_pattern;
 821     }
 822 
 823     if (is_remote_node) {
 824         options.attr_options |= pcmk__node_attr_remote;
 825     }
 826 
 827     if (pcmk__str_eq(options.set_type, XML_TAG_UTILIZATION, pcmk__str_none)) {
 828         options.attr_options |= pcmk__node_attr_utilization;
 829     }
 830 
 831     if (try_ipc_update() &&
 832         (send_attrd_update(options.command, options.dest_uname, options.attr_name,
 833                            options.attr_value, options.set_name, NULL, options.attr_options) == pcmk_rc_ok)) {
 834         crm_info("Update %s=%s sent via pacemaker-attrd",
 835                  options.attr_name, ((options.command == 'D')? "<none>" : options.attr_value));
 836 
 837     } else if (options.command == 'D') {
 838         rc = command_delete(out, the_cib);
 839 
 840     } else if (options.command == 'u') {
 841         rc = command_update(out, the_cib, is_remote_node);
 842 
 843     } else {
 844         rc = command_query(out, the_cib);
 845     }
 846 
 847     if (rc == ENOTUNIQ) {
 848         exit_code = pcmk_rc2exitc(rc);
 849         g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 850                     "Please choose from one of the matches below and supply the 'id' with --attr-id");
 851 
 852     } else if (rc != pcmk_rc_ok) {
 853         exit_code = pcmk_rc2exitc(rc);
 854         g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 855                     "Error performing operation: %s", pcmk_strerror(rc));
 856     }
 857 
 858 done:
 859     g_strfreev(processed_args);
 860     pcmk__free_arg_context(context);
 861 
 862     free(options.attr_default);
 863     g_free(options.attr_id);
 864     g_free(options.attr_name);
 865     free(options.attr_value);
 866     free(options.dest_node);
 867     g_free(options.dest_uname);
 868     g_free(options.set_name);
 869     free(options.set_type);
 870     g_free(options.type);
 871 
 872     cib__clean_up_connection(&the_cib);
 873 
 874     pcmk__output_and_clear_error(&error, out);
 875 
 876     if (out != NULL) {
 877         out->finish(out, exit_code, true, NULL);
 878         pcmk__output_free(out);
 879     }
 880 
 881     pcmk__unregister_formats();
 882     return crm_exit(exit_code);
 883 }

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