root/tools/attrd_updater.c

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

DEFINITIONS

This source file includes following definitions.
  1. main
  2. send_attrd_query
  3. validate_attrd_reply
  4. print_attrd_values
  5. do_query
  6. do_update

   1 
   2 /* 
   3  * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
   4  * 
   5  * This program is free software; you can redistribute it and/or
   6  * modify it under the terms of the GNU General Public
   7  * License as published by the Free Software Foundation; either
   8  * version 2 of the License, or (at your option) any later version.
   9  * 
  10  * This software is distributed in the hope that it will be useful,
  11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  13  * General Public License for more details.
  14  * 
  15  * You should have received a copy of the GNU General Public
  16  * License along with this library; if not, write to the Free Software
  17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  18  */
  19 
  20 #include <crm_internal.h>
  21 
  22 #include <stdio.h>
  23 #include <unistd.h>
  24 #include <stdlib.h>
  25 #include <libgen.h>
  26 
  27 #include <sys/param.h>
  28 #include <sys/types.h>
  29 
  30 #include <crm/crm.h>
  31 #include <crm/msg_xml.h>
  32 #include <crm/common/ipc.h>
  33 
  34 #include <crm/attrd.h>
  35 
  36 /* *INDENT-OFF* */
  37 static struct crm_option long_options[] = {
  38     /* Top-level Options */
  39     {"help",    0, 0, '?', "\tThis text"},
  40     {"version", 0, 0, '$', "\tVersion information"  },
  41     {"verbose", 0, 0, 'V', "\tIncrease debug output\n"},
  42 
  43     {"name",    1, 0, 'n', "The attribute's name"},
  44 
  45     {"-spacer-",1, 0, '-', "\nCommands:"},
  46     {"update",  1, 0, 'U', "Update the attribute's value in attrd.  If this causes the value to change, it will also be updated in the cluster configuration"},
  47     {"update-both", 1, 0, 'B', "Update the attribute's value and time to wait (dampening) in attrd. If this causes the value or dampening to change, the attribute will also be written to the cluster configuration, so be aware that repeatedly changing the dampening reduces its effectiveness."},
  48 #if HAVE_ATOMIC_ATTRD
  49     {"update-delay", 0, 0, 'Y', "Update the attribute's dampening in attrd (requires -d/--delay). If this causes the dampening to change, the attribute will also be written to the cluster configuration, so be aware that repeatedly changing the dampening reduces its effectiveness."},
  50     {"query",   0, 0, 'Q', "\tQuery the attribute's value from attrd"},
  51 #endif
  52     {"delete",  0, 0, 'D', "\tDelete the attribute in attrd.  If a value was previously set, it will also be removed from the cluster configuration"},
  53     {"refresh", 0, 0, 'R', "\t(Advanced) Force the attrd daemon to resend all current values to the CIB\n"},    
  54     
  55     {"-spacer-",1, 0, '-', "\nAdditional options:"},
  56     {"delay",   1, 0, 'd', "The time to wait (dampening) in seconds for further changes before writing"},
  57     {"set",     1, 0, 's', "(Advanced) The attribute set in which to place the value"},
  58     {"node",    1, 0, 'N', "Set the attribute for the named node (instead of the local one)"},
  59 #if HAVE_ATOMIC_ATTRD
  60     {"all",     0, 0, 'A', "Show values of the attribute for all nodes (query only)"},
  61     /* lifetime could be implemented for atomic attrd if there is sufficient user demand */
  62     {"lifetime",1, 0, 'l', "(Deprecated) Lifetime of the node attribute (silently ignored by cluster)"},
  63     {"private", 0, 0, 'p', "\tIf this creates a new attribute, never write the attribute to the CIB"},
  64 #else
  65     {"lifetime",1, 0, 'l', "Lifetime of the node attribute.  Allowed values: forever, reboot"},
  66 #endif
  67 
  68     /* Legacy options */
  69     {"quiet",   0, 0, 'q', NULL, pcmk_option_hidden},
  70     {"update",  1, 0, 'v', NULL, pcmk_option_hidden},
  71     {"section", 1, 0, 'S', NULL, pcmk_option_hidden},
  72     {0, 0, 0, 0}
  73 };
  74 /* *INDENT-ON* */
  75 
  76 #if HAVE_ATOMIC_ATTRD
  77 static int do_query(const char *attr_name, const char *attr_node, gboolean query_all);
  78 #endif
  79 static int do_update(char command, const char *attr_node, const char *attr_name,
  80                      const char *attr_value, const char *attr_section,
  81                      const char *attr_set, const char *attr_dampen, int attr_options);
  82 
  83 int
  84 main(int argc, char **argv)
     /* [previous][next][first][last][top][bottom][index][help] */
  85 {
  86     int index = 0;
  87     int argerr = 0;
  88     int attr_options = attrd_opt_none;
  89     int flag;
  90     const char *attr_node = NULL;
  91     const char *attr_name = NULL;
  92     const char *attr_value = NULL;
  93     const char *attr_set = NULL;
  94     const char *attr_section = NULL;
  95     const char *attr_dampen = NULL;
  96     char command = 'Q';
  97 
  98 #if HAVE_ATOMIC_ATTRD
  99     gboolean query_all = FALSE;
 100 #endif
 101 
 102     crm_log_cli_init("attrd_updater");
 103     crm_set_options(NULL, "command -n attribute [options]", long_options,
 104                     "Tool for updating cluster node attributes");
 105 
 106     if (argc < 2) {
 107         crm_help('?', EX_USAGE);
 108     }
 109 
 110     while (1) {
 111         flag = crm_get_option(argc, argv, &index);
 112         if (flag == -1)
 113             break;
 114 
 115         switch (flag) {
 116             case 'V':
 117                 crm_bump_log_level(argc, argv);
 118                 break;
 119             case '?':
 120             case '$':
 121                 crm_help(flag, EX_OK);
 122                 break;
 123             case 'n':
 124                 attr_name = strdup(optarg);
 125                 break;
 126             case 's':
 127                 attr_set = strdup(optarg);
 128                 break;
 129             case 'd':
 130                 attr_dampen = strdup(optarg);
 131                 break;
 132             case 'l':
 133             case 'S':
 134                 attr_section = strdup(optarg);
 135                 break;
 136             case 'N':
 137                 attr_node = strdup(optarg);
 138                 break;
 139 #if HAVE_ATOMIC_ATTRD
 140             case 'A':
 141                 query_all = TRUE;
 142                 break;
 143             case 'p':
 144                 set_bit(attr_options, attrd_opt_private);
 145                 break;
 146 #endif
 147             case 'q':
 148                 break;
 149 #if HAVE_ATOMIC_ATTRD
 150             case 'Y':
 151                 command = flag;
 152                 crm_log_args(argc, argv); /* Too much? */
 153                 break;
 154             case 'Q':
 155 #endif
 156             case 'B':
 157             case 'R':
 158             case 'D':
 159             case 'U':
 160             case 'v':
 161                 command = flag;
 162                 attr_value = optarg;
 163                 crm_log_args(argc, argv); /* Too much? */
 164                 break;
 165             default:
 166                 ++argerr;
 167                 break;
 168         }
 169     }
 170 
 171     if (optind > argc) {
 172         ++argerr;
 173     }
 174 
 175     if (command != 'R' && attr_name == NULL) {
 176         ++argerr;
 177     }
 178 
 179     if (argerr) {
 180         crm_help('?', EX_USAGE);
 181     }
 182 
 183     if (command == 'Q') {
 184 #if HAVE_ATOMIC_ATTRD
 185         crm_exit(do_query(attr_name, attr_node, query_all));
 186 #else
 187         crm_help('?', EX_USAGE);
 188 #endif
 189     } else {
 190         /* @TODO We don't know whether the specified node is a Pacemaker Remote
 191          * node or not, so we can't set attrd_opt_remote when appropriate.
 192          * That's OK with atomic attrd, because it will learn and remember a
 193          * node's "remoteness".
 194          *
 195          * Legacy attrd will simply ignore the request if it's a remote node. A
 196          * possible solution would be to call query_node_uuid() (which is the
 197          * approach crm_attribute takes), but that would require linking against
 198          * libcluster, and would add the overhead of a synchronous CIB call to
 199          * every update, even in clusters with no remote nodes. Since we haven't
 200          * had user requests for this support, we'll leave it as it is for now.
 201          */
 202 
 203         attr_node = attrd_get_target(attr_node);
 204         crm_exit(do_update(command, attr_node, attr_name, attr_value,
 205                            attr_section, attr_set, attr_dampen, attr_options));
 206     }
 207     return crm_exit(pcmk_ok);
 208 }
 209 
 210 #if HAVE_ATOMIC_ATTRD
 211 
 212 /*!
 213  * \internal
 214  * \brief Submit a query request to attrd and wait for reply
 215  *
 216  * \param[in] name    Name of attribute to query
 217  * \param[in] host    Query applies to this host only (or all hosts if NULL)
 218  * \param[out] reply  On success, will be set to new XML tree with reply
 219  *
 220  * \return pcmk_ok on success, -errno on error
 221  * \note On success, caller is responsible for freeing result via free_xml(*reply)
 222  */
 223 static int
 224 send_attrd_query(const char *name, const char *host, xmlNode **reply)
     /* [previous][next][first][last][top][bottom][index][help] */
 225 {
 226     int rc;
 227     crm_ipc_t *ipc;
 228     xmlNode *query;
 229 
 230     /* Build the query XML */
 231     query = create_xml_node(NULL, __FUNCTION__);
 232     if (query == NULL) {
 233         return -ENOMEM;
 234     }
 235     crm_xml_add(query, F_TYPE, T_ATTRD);
 236     crm_xml_add(query, F_ORIG, crm_system_name);
 237     crm_xml_add(query, F_ATTRD_HOST, host);
 238     crm_xml_add(query, F_ATTRD_TASK, ATTRD_OP_QUERY);
 239     crm_xml_add(query, F_ATTRD_ATTRIBUTE, name);
 240 
 241     /* Connect to attrd, send query XML and get reply */
 242     crm_debug("Sending query for value of %s on %s", name, (host? host : "all nodes"));
 243     ipc = crm_ipc_new(T_ATTRD, 0);
 244     if (crm_ipc_connect(ipc) == FALSE) {
 245         crm_perror(LOG_ERR, "Connection to cluster attribute manager failed");
 246         rc = -ENOTCONN;
 247     } else {
 248         rc = crm_ipc_send(ipc, query, crm_ipc_flags_none|crm_ipc_client_response, 0, reply);
 249         if (rc > 0) {
 250             rc = pcmk_ok;
 251         }
 252         crm_ipc_close(ipc);
 253     }
 254 
 255     free_xml(query);
 256     return(rc);
 257 }
 258 
 259 /*!
 260  * \brief Validate attrd's XML reply to an query
 261  *
 262  * param[in] reply      Root of reply XML tree to validate
 263  * param[in] attr_name  Name of attribute that was queried
 264  *
 265  * \return pcmk_ok on success,
 266  *         -errno on error (-ENXIO = requested attribute does not exist)
 267  */
 268 static int
 269 validate_attrd_reply(xmlNode *reply, const char *attr_name)
     /* [previous][next][first][last][top][bottom][index][help] */
 270 {
 271     const char *reply_attr;
 272 
 273     if (reply == NULL) {
 274         fprintf(stderr, "Could not query value of %s: reply did not contain valid XML\n",
 275                 attr_name);
 276         return -pcmk_err_schema_validation;
 277     }
 278     crm_log_xml_trace(reply, "Reply");
 279 
 280     reply_attr = crm_element_value(reply, F_ATTRD_ATTRIBUTE);
 281     if (reply_attr == NULL) {
 282         fprintf(stderr, "Could not query value of %s: attribute does not exist\n",
 283                 attr_name);
 284         return -ENXIO;
 285     }
 286 
 287     if (safe_str_neq(crm_element_value(reply, F_TYPE), T_ATTRD)
 288         || (crm_element_value(reply, F_ATTRD_VERSION) == NULL)
 289         || strcmp(reply_attr, attr_name)) {
 290             fprintf(stderr,
 291                     "Could not query value of %s: reply did not contain expected identification\n",
 292                     attr_name);
 293             return -pcmk_err_schema_validation;
 294     }
 295     return pcmk_ok;
 296 }
 297 
 298 /*!
 299  * \brief Print the attribute values in an attrd XML query reply
 300  *
 301  * \param[in] reply     Root of XML tree with query reply
 302  * \param[in] attr_name Name of attribute that was queried
 303  *
 304  * \return TRUE if any values were printed
 305  */
 306 static gboolean
 307 print_attrd_values(xmlNode *reply, const char *attr_name)
     /* [previous][next][first][last][top][bottom][index][help] */
 308 {
 309     xmlNode *child;
 310     const char *reply_host, *reply_value;
 311     gboolean have_values = FALSE;
 312 
 313     /* Iterate through reply's XML tags (a node tag for each host-value pair) */
 314     for (child = __xml_first_child(reply); child != NULL; child = __xml_next(child)) {
 315         if (safe_str_neq((const char*)child->name, XML_CIB_TAG_NODE)) {
 316             crm_warn("Ignoring unexpected %s tag in query reply", child->name);
 317         } else {
 318             reply_host = crm_element_value(child, F_ATTRD_HOST);
 319             reply_value = crm_element_value(child, F_ATTRD_VALUE);
 320 
 321             if (reply_host == NULL) {
 322                 crm_warn("Ignoring %s tag without %s attribute in query reply",
 323                          XML_CIB_TAG_NODE, F_ATTRD_HOST);
 324             } else {
 325                 printf("name=\"%s\" host=\"%s\" value=\"%s\"\n",
 326                        attr_name, reply_host, (reply_value? reply_value : ""));
 327                 have_values = TRUE;
 328             }
 329         }
 330     }
 331     return have_values;
 332 }
 333 
 334 /*!
 335  * \brief Submit a query to attrd and print reply
 336  *
 337  * \param[in] attr_name  Name of attribute to be affected by request
 338  * \param[in] attr_node  Name of host to query for (or NULL for localhost)
 339  * \param[in] query_all  If TRUE, ignore attr_node and query all nodes instead
 340  *
 341  * \return pcmk_ok on success, -errno on error
 342  */
 343 static int
 344 do_query(const char *attr_name, const char *attr_node, gboolean query_all)
     /* [previous][next][first][last][top][bottom][index][help] */
 345 {
 346     xmlNode *reply = NULL;
 347     int rc;
 348 
 349     /* Decide which node(s) to query */
 350     if (query_all == TRUE) {
 351         attr_node = NULL;
 352     } else {
 353         attr_node = attrd_get_target(attr_node);
 354     }
 355 
 356     /* Build and send attrd request, and get XML reply */
 357     rc = send_attrd_query(attr_name, attr_node, &reply);
 358     if (rc != pcmk_ok) {
 359         fprintf(stderr, "Could not query value of %s: %s (%d)\n", attr_name, pcmk_strerror(rc), rc);
 360         return rc;
 361     }
 362 
 363     /* Validate the XML reply */
 364     rc = validate_attrd_reply(reply, attr_name);
 365     if (rc != pcmk_ok) {
 366         if (reply != NULL) {
 367             free_xml(reply);
 368         }
 369         return rc;
 370     }
 371 
 372     /* Print the values from the reply */
 373     if (print_attrd_values(reply, attr_name) == FALSE) {
 374         fprintf(stderr,
 375                 "Could not query value of %s: reply had attribute name but no host values\n",
 376                 attr_name);
 377         free_xml(reply);
 378         return -pcmk_err_schema_validation;
 379     }
 380 
 381     return pcmk_ok;
 382 }
 383 
 384 #endif
 385 
 386 static int
 387 do_update(char command, const char *attr_node, const char *attr_name,
     /* [previous][next][first][last][top][bottom][index][help] */
 388           const char *attr_value, const char *attr_section,
 389           const char *attr_set, const char *attr_dampen, int attr_options)
 390 {
 391     int rc = attrd_update_delegate(NULL, command, attr_node, attr_name,
 392                                    attr_value, attr_section, attr_set,
 393                                    attr_dampen, NULL, attr_options);
 394     if (rc != pcmk_ok) {
 395         fprintf(stderr, "Could not update %s=%s: %s (%d)\n", attr_name, attr_value, pcmk_strerror(rc), rc);
 396     }
 397     return rc;
 398 }

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