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. controller_event_cb
  8. get_node_name_from_local
  9. get_node_name_from_controller
  10. send_attrd_update
  11. delete_attr_on_node
  12. command_delete
  13. update_attr_on_node
  14. command_update
  15. output_one_attribute
  16. command_query
  17. set_type
  18. use_attrd
  19. try_ipc_update
  20. pattern_used_correctly
  21. delete_used_correctly
  22. build_arg_context
  23. main

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

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