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. build_arg_context
  11. 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_controld.h>
  36 #include <crm/common/output_internal.h>
  37 #include <sys/utsname.h>
  38 
  39 #include <pcmki/pcmki_output.h>
  40 
  41 #define SUMMARY "crm_attribute - query and update Pacemaker cluster options and node attributes"
  42 
  43 GError *error = NULL;
  44 crm_exit_t exit_code = CRM_EX_OK;
  45 uint64_t cib_opts = cib_sync_call;
  46 
  47 PCMK__OUTPUT_ARGS("attribute", "char *", "char *", "char *", "char *")
     /* [previous][next][first][last][top][bottom][index][help] */
  48 static int
  49 attribute_text(pcmk__output_t *out, va_list args)
  50 {
  51     char *scope = va_arg(args, char *);
  52     char *instance = va_arg(args, char *);
  53     char *name = va_arg(args, char *);
  54     char *value = va_arg(args, char *);
  55     char *host G_GNUC_UNUSED = va_arg(args, char *);
  56 
  57     if (out->quiet) {
  58         pcmk__formatted_printf(out, "%s\n", value);
  59     } else {
  60         out->info(out, "%s%s %s%s %s%s value=%s",
  61                   scope ? "scope=" : "", scope ? scope : "",
  62                   instance ? "id=" : "", instance ? instance : "",
  63                   name ? "name=" : "", name ? name : "",
  64                   value ? value : "(null)");
  65     }
  66 
  67     return pcmk_rc_ok;
  68 }
  69 
  70 static pcmk__supported_format_t formats[] = {
  71     PCMK__SUPPORTED_FORMAT_NONE,
  72     PCMK__SUPPORTED_FORMAT_TEXT,
  73     PCMK__SUPPORTED_FORMAT_XML,
  74     { NULL, NULL, NULL }
  75 };
  76 
  77 static pcmk__message_entry_t fmt_functions[] = {
  78     { "attribute", "text", attribute_text },
  79 
  80     { NULL, NULL, NULL }
  81 };
  82 
  83 struct {
  84     char command;
  85     gchar *attr_default;
  86     gchar *attr_id;
  87     gchar *attr_name;
  88     gchar *attr_pattern;
  89     char *attr_value;
  90     char *dest_node;
  91     gchar *dest_uname;
  92     gboolean inhibit;
  93     gchar *set_name;
  94     char *set_type;
  95     gchar *type;
  96     gboolean promotion_score;
  97 } options = {
  98     .command = 'G',
  99     .promotion_score = FALSE
 100 };
 101 
 102 #define INDENT "                               "
 103 
 104 static gboolean
 105 delete_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 106     options.command = 'D';
 107 
 108     if (options.attr_value) {
 109         free(options.attr_value);
 110     }
 111 
 112     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 = 'v';
 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 
 152     if (options.set_type) {
 153         free(options.set_type);
 154     }
 155 
 156     options.set_type = strdup(XML_TAG_UTILIZATION);
 157     return TRUE;
 158 }
 159 
 160 static gboolean
 161 value_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 162     options.command = 'G';
 163 
 164     if (options.attr_value) {
 165         free(options.attr_value);
 166     }
 167 
 168     options.attr_value = NULL;
 169     return TRUE;
 170 }
 171 
 172 static GOptionEntry selecting_entries[] = {
 173     { "id", 'i', 0, G_OPTION_ARG_STRING, &options.attr_id,
 174       "(Advanced) Operate on instance of specified attribute with this\n"
 175       INDENT "XML ID",
 176       "XML_ID"
 177     },
 178 
 179     { "name", 'n', 0, G_OPTION_ARG_STRING, &options.attr_name,
 180       "Operate on attribute or option with this name",
 181       "NAME"
 182     },
 183 
 184     { "pattern", 'P', 0, G_OPTION_ARG_STRING, &options.attr_pattern,
 185       "Operate on all attributes matching this pattern\n"
 186       INDENT "(with -v/-D and -l reboot)",
 187       "PATTERN"
 188     },
 189 
 190     { "promotion", 'p', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK, promotion_cb,
 191       "Operate on node attribute used as promotion score for specified\n"
 192       INDENT "resource, or resource given in OCF_RESOURCE_INSTANCE environment\n"
 193       INDENT "variable if none is specified; this also defaults -l/--lifetime\n"
 194       INDENT "to reboot (normally invoked from an OCF resource agent)",
 195       "RESOURCE"
 196     },
 197 
 198     { "set-name", 's', 0, G_OPTION_ARG_STRING, &options.set_name,
 199       "(Advanced) Operate on instance of specified attribute that is\n"
 200       INDENT "within set with this XML ID",
 201       "NAME"
 202     },
 203 
 204     { NULL }
 205 };
 206 
 207 static GOptionEntry command_entries[] = {
 208     { "delete", 'D', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, delete_cb,
 209       "Delete the attribute/option",
 210       NULL
 211     },
 212 
 213     { "query", 'G', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, value_cb,
 214       "Query the current value of the attribute/option",
 215       NULL
 216     },
 217 
 218     { "update", 'v', 0, G_OPTION_ARG_CALLBACK, update_cb,
 219       "Update the value of the attribute/option",
 220       "VALUE"
 221     },
 222 
 223     { NULL }
 224 };
 225 
 226 static GOptionEntry addl_entries[] = {
 227     { "default", 'd', 0, G_OPTION_ARG_STRING, &options.attr_default,
 228       "(Advanced) Default value to display if none is found in configuration",
 229       "VALUE"
 230     },
 231 
 232     { "lifetime", 'l', 0, G_OPTION_ARG_STRING, &options.type,
 233       "Lifetime of the node attribute.\n"
 234       INDENT "Valid values: reboot, forever",
 235       "LIFETIME"
 236     },
 237 
 238     { "node", 'N', 0, G_OPTION_ARG_STRING, &options.dest_uname,
 239       "Set a node attribute for named node (instead of a cluster option).\n"
 240       INDENT "See also: -l",
 241       "NODE"
 242     },
 243 
 244     { "type", 't', 0, G_OPTION_ARG_STRING, &options.type,
 245       "Which part of the configuration to update/delete/query the option in.\n"
 246       INDENT "Valid values: crm_config, rsc_defaults, op_defaults, tickets",
 247       "SECTION"
 248     },
 249 
 250     { "utilization", 'z', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, utilization_cb,
 251       "Set an utilization attribute for the node.",
 252       NULL
 253     },
 254 
 255     { "inhibit-policy-engine", '!', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &options.inhibit,
 256       NULL, NULL
 257     },
 258 
 259     { NULL }
 260 };
 261 
 262 static GOptionEntry deprecated_entries[] = {
 263     { "attr-id", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &options.attr_id,
 264       NULL, NULL
 265     },
 266 
 267     { "attr-name", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &options.attr_name,
 268       NULL, NULL
 269     },
 270 
 271     { "attr-value", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, update_cb,
 272       NULL, NULL
 273     },
 274 
 275     { "delete-attr", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, delete_cb,
 276       NULL, NULL
 277     },
 278 
 279     { "get-value", 0, G_OPTION_FLAG_HIDDEN|G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, value_cb,
 280       NULL, NULL
 281     },
 282 
 283     { "node-uname", 'U', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &options.dest_uname,
 284       NULL, NULL
 285     },
 286 
 287     { NULL }
 288 };
 289 
 290 static void
 291 controller_event_cb(pcmk_ipc_api_t *controld_api,
     /* [previous][next][first][last][top][bottom][index][help] */
 292                     enum pcmk_ipc_event event_type, crm_exit_t status,
 293                     void *event_data, void *user_data)
 294 {
 295     pcmk_controld_api_reply_t *reply = event_data;
 296 
 297     if (event_type != pcmk_ipc_event_reply) {
 298         return;
 299     }
 300 
 301     if (status != CRM_EX_OK) {
 302         exit_code = status;
 303         g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 304                     "Bad reply from controller: %s", crm_exit_str(exit_code));
 305         return;
 306     }
 307 
 308     if (reply->reply_type != pcmk_controld_reply_info) {
 309         exit_code = CRM_EX_PROTOCOL;
 310         g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 311                     "Unknown reply type %d from controller", reply->reply_type);
 312         return;
 313     }
 314 
 315     if (reply->data.node_info.uname == NULL) {
 316         exit_code = CRM_EX_NOHOST;
 317         g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 318                     "Node is not known to cluster");
 319     }
 320 
 321     exit_code = CRM_EX_OK;
 322     pcmk__str_update(&options.dest_uname, reply->data.node_info.uname);
 323 }
 324 
 325 static void
 326 get_node_name_from_local(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 327 {
 328     char *hostname = pcmk_hostname();
 329 
 330     g_free(options.dest_uname);
 331 
 332     /* This silliness is so that dest_uname is always a glib-managed
 333      * string so we know how to free it later.  pcmk_hostname returns
 334      * a newly allocated string via strdup.
 335      */
 336     options.dest_uname = g_strdup(hostname);
 337     free(hostname);
 338 }
 339 
 340 static int
 341 get_node_name_from_controller(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 342 {
 343     int rc = pcmk_rc_ok;
 344     pcmk_ipc_api_t *controld_api = NULL;
 345 
 346     rc = pcmk_new_ipc_api(&controld_api, pcmk_ipc_controld);
 347     if (controld_api == NULL) {
 348         g_set_error(&error, PCMK__RC_ERROR, rc, "Could not connect to controller: %s",
 349                     pcmk_rc_str(rc));
 350         return rc;
 351     }
 352 
 353     pcmk_register_ipc_callback(controld_api, controller_event_cb, NULL);
 354 
 355     rc = pcmk_connect_ipc(controld_api, pcmk_ipc_dispatch_sync);
 356     if (rc != pcmk_rc_ok) {
 357         g_set_error(&error, PCMK__RC_ERROR, rc, "Could not connect to controller: %s",
 358                     pcmk_rc_str(rc));
 359         pcmk_free_ipc_api(controld_api);
 360         return rc;
 361     }
 362 
 363     rc = pcmk_controld_api_node_info(controld_api, 0);
 364 
 365     if (rc != pcmk_rc_ok) {
 366         g_set_error(&error, PCMK__RC_ERROR, rc, "Could not ping controller: %s",
 367                     pcmk_rc_str(rc));
 368     }
 369 
 370     /* This is a synchronous call, so we have already received and processed
 371      * the reply, which means controller_event_cb has been called.  If
 372      * exit_code was set, return some generic error here.  The caller can
 373      * then check for that and fail with exit_code.
 374      */
 375     if (exit_code != CRM_EX_OK) {
 376         rc = pcmk_rc_error;
 377     }
 378 
 379     pcmk_disconnect_ipc(controld_api);
 380     pcmk_free_ipc_api(controld_api);
 381     return rc;
 382 }
 383 
 384 static GOptionContext *
 385 build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) {
     /* [previous][next][first][last][top][bottom][index][help] */
 386     GOptionContext *context = NULL;
 387 
 388     GOptionEntry extra_prog_entries[] = {
 389         { "quiet", 'q', 0, G_OPTION_ARG_NONE, &(args->quiet),
 390           "Print only the value on stdout",
 391           NULL },
 392 
 393         { "quiet", 'Q', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &(args->quiet),
 394           NULL, NULL
 395         },
 396 
 397         { NULL }
 398     };
 399 
 400     const char *description = "Examples:\n\n"
 401                               "Add new node attribute called 'location' with the value of 'office' for host 'myhost':\n\n"
 402                               "\tcrm_attribute --node myhost --name location --update office\n\n"
 403                               "Query the value of the 'location' node attribute for host 'myhost':\n\n"
 404                               "\tcrm_attribute --node myhost --name location --query\n\n"
 405                               "Change the value of the 'location' node attribute for host 'myhost':\n\n"
 406                               "\tcrm_attribute --node myhost --name location --update backoffice\n\n"
 407                               "Delete the 'location' node attribute for host 'myhost':\n\n"
 408                               "\tcrm_attribute --node myhost --name location --delete\n\n"
 409                               "Query the value of the 'cluster-delay' cluster option:\n\n"
 410                               "\tcrm_attribute --type crm_config --name cluster-delay --query\n\n"
 411                               "Query value of the 'cluster-delay' cluster option and print only the value:\n\n"
 412                               "\tcrm_attribute --type crm_config --name cluster-delay --query --quiet\n\n";
 413 
 414     context = pcmk__build_arg_context(args, "text (default), xml", group, NULL);
 415     pcmk__add_main_args(context, extra_prog_entries);
 416     g_option_context_set_description(context, description);
 417 
 418     pcmk__add_arg_group(context, "selections", "Selecting attributes:",
 419                         "Show selecting options", selecting_entries);
 420     pcmk__add_arg_group(context, "command", "Commands:",
 421                         "Show command options", command_entries);
 422     pcmk__add_arg_group(context, "additional", "Additional options:",
 423                         "Show additional options", addl_entries);
 424     pcmk__add_arg_group(context, "deprecated", "Deprecated Options:",
 425                         "Show deprecated options", deprecated_entries);
 426 
 427     return context;
 428 }
 429 
 430 int
 431 main(int argc, char **argv)
     /* [previous][next][first][last][top][bottom][index][help] */
 432 {
 433     cib_t *the_cib = NULL;
 434     int is_remote_node = 0;
 435     bool try_attrd = true;
 436     int attrd_opts = pcmk__node_attr_none;
 437 
 438     int rc = pcmk_rc_ok;
 439 
 440     pcmk__output_t *out = NULL;
 441 
 442     GOptionGroup *output_group = NULL;
 443     pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
 444     gchar **processed_args = pcmk__cmdline_preproc(argv, "NPUdilnpstv");
 445     GOptionContext *context = build_arg_context(args, &output_group);
 446 
 447     pcmk__register_formats(output_group, formats);
 448     if (!g_option_context_parse_strv(context, &processed_args, &error)) {
 449         exit_code = CRM_EX_USAGE;
 450         goto done;
 451     }
 452 
 453     pcmk__cli_init_logging("crm_attribute", args->verbosity);
 454 
 455     rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv);
 456     if (rc != pcmk_rc_ok) {
 457         exit_code = CRM_EX_ERROR;
 458         g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Error creating output format %s: %s",
 459                     args->output_ty, pcmk_rc_str(rc));
 460         goto done;
 461     }
 462 
 463     pcmk__register_lib_messages(out);
 464     pcmk__register_messages(out, fmt_functions);
 465 
 466     if (args->version) {
 467         out->version(out, false);
 468         goto done;
 469     }
 470 
 471     out->quiet = args->quiet;
 472 
 473     if (options.promotion_score && options.attr_name == NULL) {
 474         exit_code = CRM_EX_USAGE;
 475         g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 476                     "-p/--promotion must be called from an OCF resource agent "
 477                     "or with a resource ID specified");
 478         goto done;
 479     }
 480 
 481     if (options.inhibit) {
 482         crm_warn("Inhibiting notifications for this update");
 483         cib__set_call_options(cib_opts, crm_system_name, cib_inhibit_notify);
 484     }
 485 
 486     the_cib = cib_new();
 487     rc = the_cib->cmds->signon(the_cib, crm_system_name, cib_command);
 488     rc = pcmk_legacy2rc(rc);
 489 
 490     if (rc != pcmk_rc_ok) {
 491         exit_code = pcmk_rc2exitc(rc);
 492         g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 493                     "Could not connect to the CIB: %s", pcmk_rc_str(rc));
 494         goto done;
 495     }
 496 
 497     // Use default CIB location if not given
 498     if (options.type == NULL) {
 499         if (options.promotion_score) {
 500             // Updating a promotion score node attribute
 501             options.type = g_strdup(XML_CIB_TAG_STATUS);
 502 
 503         } else if (options.dest_uname != NULL) {
 504             // Updating some other node attribute
 505             options.type = g_strdup(XML_CIB_TAG_NODES);
 506 
 507         } else {
 508             // Updating cluster options
 509             options.type = g_strdup(XML_CIB_TAG_CRMCONFIG);
 510         }
 511     } else if (pcmk__str_eq(options.type, "reboot", pcmk__str_casei)) {
 512         options.type = g_strdup(XML_CIB_TAG_STATUS);
 513 
 514     } else if (pcmk__str_eq(options.type, "forever", pcmk__str_casei)) {
 515         options.type = g_strdup(XML_CIB_TAG_NODES);
 516     }
 517 
 518     // Use default node if not given (except for cluster options and tickets)
 519     if (!pcmk__strcase_any_of(options.type, XML_CIB_TAG_CRMCONFIG, XML_CIB_TAG_TICKETS,
 520                               NULL)) {
 521         /* If we are being called from a resource agent via the cluster,
 522          * the correct local node name will be passed as an environment
 523          * variable. Otherwise, we have to ask the cluster.
 524          */
 525         const char *target = pcmk__node_attr_target(options.dest_uname);
 526 
 527         if (target != NULL) {
 528             g_free(options.dest_uname);
 529             options.dest_uname = g_strdup(target);
 530         } else if (getenv("CIB_file") != NULL && options.dest_uname == NULL) {
 531             get_node_name_from_local();
 532         }
 533 
 534         if (options.dest_uname == NULL) {
 535             rc = get_node_name_from_controller();
 536 
 537             if (rc == pcmk_rc_error) {
 538                 /* The callback failed with some error condition that is stored in
 539                  * exit_code.
 540                  */
 541                 goto done;
 542             } else if (rc != pcmk_rc_ok) {
 543                 /* get_node_name_from_controller failed in some other way.  Convert
 544                  * the return code to an exit code.
 545                  */
 546                 exit_code = pcmk_rc2exitc(rc);
 547                 goto done;
 548             }
 549         }
 550 
 551         rc = query_node_uuid(the_cib, options.dest_uname, &options.dest_node, &is_remote_node);
 552         rc = pcmk_legacy2rc(rc);
 553 
 554         if (rc != pcmk_rc_ok) {
 555             exit_code = pcmk_rc2exitc(rc);
 556             g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 557                         "Could not map name=%s to a UUID", options.dest_uname);
 558             goto done;
 559         }
 560     }
 561 
 562     if ((options.command == 'D') && (options.attr_name == NULL) && (options.attr_pattern == NULL)) {
 563         exit_code = CRM_EX_USAGE;
 564         g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 565                     "Error: must specify attribute name or pattern to delete");
 566         goto done;
 567     }
 568 
 569     if (options.attr_pattern) {
 570         if (((options.command != 'v') && (options.command != 'D'))
 571             || !pcmk__str_eq(options.type, XML_CIB_TAG_STATUS, pcmk__str_casei)) {
 572 
 573             exit_code = CRM_EX_USAGE;
 574             g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 575                         "Error: pattern can only be used with till-reboot update or delete");
 576             goto done;
 577         }
 578         options.command = 'u';
 579         g_free(options.attr_name);
 580         options.attr_name = options.attr_pattern;
 581     }
 582 
 583     // Only go through attribute manager for transient attributes
 584     try_attrd = pcmk__str_eq(options.type, XML_CIB_TAG_STATUS, pcmk__str_casei);
 585 
 586     // Don't try to contact attribute manager if we're using a file as CIB
 587     if (getenv("CIB_file") || getenv("CIB_shadow")) {
 588         try_attrd = false;
 589     }
 590 
 591     if (is_remote_node) {
 592         attrd_opts = pcmk__node_attr_remote;
 593     }
 594     if (((options.command == 'v') || (options.command == 'D') || (options.command == 'u')) && try_attrd
 595         && (pcmk__node_attr_request(NULL, options.command, options.dest_uname, options.attr_name,
 596                                     options.attr_value, options.type, options.set_name, NULL, NULL,
 597                                     attrd_opts) == pcmk_rc_ok)) {
 598         crm_info("Update %s=%s sent via pacemaker-attrd",
 599                  options.attr_name, ((options.command == 'D')? "<none>" : options.attr_value));
 600 
 601     } else if (options.command == 'D') {
 602         rc = cib__delete_node_attr(out, the_cib, cib_opts, options.type, options.dest_node,
 603                                    options.set_type, options.set_name, options.attr_id,
 604                                    options.attr_name, options.attr_value, NULL);
 605 
 606         if (rc == ENXIO) {
 607             /* Nothing to delete...
 608              * which means it's not there...
 609              * which is what the admin wanted
 610              */
 611             rc = pcmk_rc_ok;
 612         }
 613 
 614     } else if (options.command == 'v') {
 615         CRM_LOG_ASSERT(options.type != NULL);
 616         CRM_LOG_ASSERT(options.attr_name != NULL);
 617         CRM_LOG_ASSERT(options.attr_value != NULL);
 618 
 619         rc = cib__update_node_attr(out, the_cib, cib_opts, options.type, options.dest_node,
 620                                    options.set_type, options.set_name, options.attr_id,
 621                                    options.attr_name, options.attr_value, NULL,
 622                                    is_remote_node ? "remote" : NULL);
 623 
 624     } else {                    /* query */
 625 
 626         char *read_value = NULL;
 627 
 628         if (options.attr_id == NULL && options.attr_name == NULL) {
 629             exit_code = CRM_EX_USAGE;
 630             g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 631                         "Error: must specify attribute name or pattern to query");
 632             goto done;
 633         }
 634 
 635         rc = cib__read_node_attr(out, the_cib, options.type, options.dest_node,
 636                                  options.set_type, options.set_name, options.attr_id,
 637                                  options.attr_name, &read_value, NULL);
 638 
 639         if (rc == ENXIO && options.attr_default) {
 640             read_value = strdup(options.attr_default);
 641             rc = pcmk_rc_ok;
 642         }
 643 
 644         crm_info("Read %s=%s %s%s",
 645                  options.attr_name, crm_str(read_value), options.set_name ? "in " : "", options.set_name ? options.set_name : "");
 646 
 647         if (rc == ENOTUNIQ) {
 648             // Multiple matches (already displayed) are not error for queries
 649             rc = pcmk_rc_ok;
 650         } else {
 651             out->message(out, "attribute", options.type, options.attr_id,
 652                          options.attr_name, read_value);
 653         }
 654 
 655         free(read_value);
 656     }
 657 
 658     if (rc == ENOTUNIQ) {
 659         exit_code = pcmk_rc2exitc(rc);
 660         g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 661                     "Please choose from one of the matches below and supply the 'id' with --attr-id");
 662 
 663     } else if (rc != pcmk_rc_ok) {
 664         exit_code = pcmk_rc2exitc(rc);
 665         g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 666                     "Error performing operation: %s", pcmk_strerror(rc));
 667     }
 668 
 669 done:
 670     g_strfreev(processed_args);
 671     pcmk__free_arg_context(context);
 672 
 673     free(options.attr_default);
 674     g_free(options.attr_id);
 675     g_free(options.attr_name);
 676     free(options.attr_value);
 677     free(options.dest_node);
 678     g_free(options.dest_uname);
 679     g_free(options.set_name);
 680     free(options.set_type);
 681     g_free(options.type);
 682 
 683     cib__clean_up_connection(&the_cib);
 684 
 685     pcmk__output_and_clear_error(error, out);
 686 
 687     if (out != NULL) {
 688         out->finish(out, exit_code, true, NULL);
 689         pcmk__output_free(out);
 690     }
 691 
 692     return crm_exit(exit_code);
 693 }

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