root/lib/common/operations.c

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

DEFINITIONS

This source file includes following definitions.
  1. generate_op_key
  2. parse_op_key
  3. generate_notify_key
  4. generate_transition_magic_v202
  5. generate_transition_magic
  6. decode_transition_magic
  7. generate_transition_key
  8. decode_transition_key
  9. filter_action_parameters
  10. append_digest
  11. rsc_op_expected_rc
  12. did_rsc_op_fail
  13. crm_create_op_xml
  14. create_operation_update
  15. crm_op_needs_metadata

   1 /*
   2  * Copyright (C) 2004-2017 Andrew Beekhof <andrew@beekhof.net>
   3  *
   4  * This source code is licensed under the GNU Lesser General Public License
   5  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
   6  */
   7 
   8 #include <crm_internal.h>
   9 
  10 #ifndef _GNU_SOURCE
  11 #  define _GNU_SOURCE
  12 #endif
  13 
  14 #include <stdio.h>
  15 #include <string.h>
  16 #include <stdlib.h>
  17 #include <ctype.h>
  18 
  19 #include <crm/crm.h>
  20 #include <crm/lrmd.h>
  21 #include <crm/msg_xml.h>
  22 #include <crm/common/xml.h>
  23 #include <crm/common/util.h>
  24 
  25 /*!
  26  * \brief Generate an operation key
  27  *
  28  * \param[in] rsc_id    ID of resource being operated on
  29  * \param[in] op_type   Operation name
  30  * \param[in] interval  Operation interval
  31  *
  32  * \return Newly allocated memory containing operation key as string
  33  *
  34  * \note It is the caller's responsibility to free() the result.
  35  */
  36 char *
  37 generate_op_key(const char *rsc_id, const char *op_type, int interval)
     /* [previous][next][first][last][top][bottom][index][help] */
  38 {
  39     CRM_ASSERT(rsc_id != NULL);
  40     CRM_ASSERT(op_type != NULL);
  41     CRM_ASSERT(interval >= 0);
  42     return crm_strdup_printf("%s_%s_%d", rsc_id, op_type, interval);
  43 }
  44 
  45 gboolean
  46 parse_op_key(const char *key, char **rsc_id, char **op_type, int *interval)
     /* [previous][next][first][last][top][bottom][index][help] */
  47 {
  48     char *notify = NULL;
  49     char *mutable_key = NULL;
  50     char *mutable_key_ptr = NULL;
  51     int len = 0, offset = 0, ch = 0;
  52 
  53     CRM_CHECK(key != NULL, return FALSE);
  54 
  55     *interval = 0;
  56     len = strlen(key);
  57     offset = len - 1;
  58 
  59     crm_trace("Source: %s", key);
  60 
  61     while (offset > 0 && isdigit(key[offset])) {
  62         int digits = len - offset;
  63 
  64         ch = key[offset] - '0';
  65         CRM_CHECK(ch < 10, return FALSE);
  66         CRM_CHECK(ch >= 0, return FALSE);
  67         while (digits > 1) {
  68             digits--;
  69             ch = ch * 10;
  70         }
  71         *interval += ch;
  72         offset--;
  73     }
  74 
  75     crm_trace("  Interval: %d", *interval);
  76     CRM_CHECK(key[offset] == '_', return FALSE);
  77 
  78     mutable_key = strdup(key);
  79     mutable_key[offset] = 0;
  80     offset--;
  81 
  82     while (offset > 0 && key[offset] != '_') {
  83         offset--;
  84     }
  85 
  86     CRM_CHECK(key[offset] == '_', free(mutable_key);
  87               return FALSE);
  88 
  89     mutable_key_ptr = mutable_key + offset + 1;
  90 
  91     crm_trace("  Action: %s", mutable_key_ptr);
  92 
  93     *op_type = strdup(mutable_key_ptr);
  94 
  95     mutable_key[offset] = 0;
  96     offset--;
  97 
  98     CRM_CHECK(mutable_key != mutable_key_ptr, free(mutable_key);
  99               return FALSE);
 100 
 101     notify = strstr(mutable_key, "_post_notify");
 102     if (notify && safe_str_eq(notify, "_post_notify")) {
 103         notify[0] = 0;
 104     }
 105 
 106     notify = strstr(mutable_key, "_pre_notify");
 107     if (notify && safe_str_eq(notify, "_pre_notify")) {
 108         notify[0] = 0;
 109     }
 110 
 111     crm_trace("  Resource: %s", mutable_key);
 112     *rsc_id = mutable_key;
 113 
 114     return TRUE;
 115 }
 116 
 117 char *
 118 generate_notify_key(const char *rsc_id, const char *notify_type, const char *op_type)
     /* [previous][next][first][last][top][bottom][index][help] */
 119 {
 120     int len = 12;
 121     char *op_id = NULL;
 122 
 123     CRM_CHECK(rsc_id != NULL, return NULL);
 124     CRM_CHECK(op_type != NULL, return NULL);
 125     CRM_CHECK(notify_type != NULL, return NULL);
 126 
 127     len += strlen(op_type);
 128     len += strlen(rsc_id);
 129     len += strlen(notify_type);
 130     if(len > 0) {
 131         op_id = malloc(len);
 132     }
 133     if (op_id != NULL) {
 134         sprintf(op_id, "%s_%s_notify_%s_0", rsc_id, notify_type, op_type);
 135     }
 136     return op_id;
 137 }
 138 
 139 char *
 140 generate_transition_magic_v202(const char *transition_key, int op_status)
     /* [previous][next][first][last][top][bottom][index][help] */
 141 {
 142     int len = 80;
 143     char *fail_state = NULL;
 144 
 145     CRM_CHECK(transition_key != NULL, return NULL);
 146 
 147     len += strlen(transition_key);
 148 
 149     fail_state = malloc(len);
 150     if (fail_state != NULL) {
 151         snprintf(fail_state, len, "%d:%s", op_status, transition_key);
 152     }
 153     return fail_state;
 154 }
 155 
 156 char *
 157 generate_transition_magic(const char *transition_key, int op_status, int op_rc)
     /* [previous][next][first][last][top][bottom][index][help] */
 158 {
 159     int len = 80;
 160     char *fail_state = NULL;
 161 
 162     CRM_CHECK(transition_key != NULL, return NULL);
 163 
 164     len += strlen(transition_key);
 165 
 166     fail_state = malloc(len);
 167     if (fail_state != NULL) {
 168         snprintf(fail_state, len, "%d:%d;%s", op_status, op_rc, transition_key);
 169     }
 170     return fail_state;
 171 }
 172 
 173 gboolean
 174 decode_transition_magic(const char *magic, char **uuid, int *transition_id, int *action_id,
     /* [previous][next][first][last][top][bottom][index][help] */
 175                         int *op_status, int *op_rc, int *target_rc)
 176 {
 177     int res = 0;
 178     char *key = NULL;
 179     gboolean result = TRUE;
 180 
 181     CRM_CHECK(magic != NULL, return FALSE);
 182     CRM_CHECK(op_rc != NULL, return FALSE);
 183     CRM_CHECK(op_status != NULL, return FALSE);
 184 
 185     key = calloc(1, strlen(magic) + 1);
 186     res = sscanf(magic, "%d:%d;%s", op_status, op_rc, key);
 187     if (res != 3) {
 188         crm_warn("Only found %d items in: '%s'", res, magic);
 189         free(key);
 190         return FALSE;
 191     }
 192 
 193     CRM_CHECK(decode_transition_key(key, uuid, transition_id, action_id, target_rc), result = FALSE);
 194 
 195     free(key);
 196     return result;
 197 }
 198 
 199 char *
 200 generate_transition_key(int transition_id, int action_id, int target_rc, const char *node)
     /* [previous][next][first][last][top][bottom][index][help] */
 201 {
 202     int len = 40;
 203     char *fail_state = NULL;
 204 
 205     CRM_CHECK(node != NULL, return NULL);
 206 
 207     len += strlen(node);
 208 
 209     fail_state = malloc(len);
 210     if (fail_state != NULL) {
 211         snprintf(fail_state, len, "%d:%d:%d:%-*s", action_id, transition_id, target_rc, 36, node);
 212     }
 213     return fail_state;
 214 }
 215 
 216 gboolean
 217 decode_transition_key(const char *key, char **uuid, int *transition_id, int *action_id,
     /* [previous][next][first][last][top][bottom][index][help] */
 218                       int *target_rc)
 219 {
 220     int res = 0;
 221     gboolean done = FALSE;
 222 
 223     CRM_CHECK(uuid != NULL, return FALSE);
 224     CRM_CHECK(target_rc != NULL, return FALSE);
 225     CRM_CHECK(action_id != NULL, return FALSE);
 226     CRM_CHECK(transition_id != NULL, return FALSE);
 227 
 228     *uuid = calloc(1, 37);
 229     res = sscanf(key, "%d:%d:%d:%36s", action_id, transition_id, target_rc, *uuid);
 230     switch (res) {
 231         case 4:
 232             /* Post Pacemaker 0.6 */
 233             done = TRUE;
 234             break;
 235         case 3:
 236         case 2:
 237             /* this can be tricky - the UUID might start with an integer */
 238 
 239             /* Until Pacemaker 0.6 */
 240             done = TRUE;
 241             *target_rc = -1;
 242             res = sscanf(key, "%d:%d:%36s", action_id, transition_id, *uuid);
 243             if (res == 2) {
 244                 *action_id = -1;
 245                 res = sscanf(key, "%d:%36s", transition_id, *uuid);
 246                 CRM_CHECK(res == 2, done = FALSE);
 247 
 248             } else if (res != 3) {
 249                 CRM_CHECK(res == 3, done = FALSE);
 250             }
 251             break;
 252 
 253         case 1:
 254             /* Prior to Heartbeat 2.0.8 */
 255             done = TRUE;
 256             *action_id = -1;
 257             *target_rc = -1;
 258             res = sscanf(key, "%d:%36s", transition_id, *uuid);
 259             CRM_CHECK(res == 2, done = FALSE);
 260             break;
 261         default:
 262             crm_crit("Unhandled sscanf result (%d) for %s", res, key);
 263     }
 264 
 265     if (strlen(*uuid) != 36) {
 266         crm_warn("Bad UUID (%s) in sscanf result (%d) for %s", *uuid, res, key);
 267     }
 268 
 269     if (done == FALSE) {
 270         crm_err("Cannot decode '%s' rc=%d", key, res);
 271 
 272         free(*uuid);
 273         *uuid = NULL;
 274         *target_rc = -1;
 275         *action_id = -1;
 276         *transition_id = -1;
 277     }
 278 
 279     return done;
 280 }
 281 
 282 void
 283 filter_action_parameters(xmlNode * param_set, const char *version)
     /* [previous][next][first][last][top][bottom][index][help] */
 284 {
 285     char *key = NULL;
 286     char *timeout = NULL;
 287     char *interval = NULL;
 288 
 289     const char *attr_filter[] = {
 290         XML_ATTR_ID,
 291         XML_ATTR_CRM_VERSION,
 292         XML_LRM_ATTR_OP_DIGEST,
 293         XML_LRM_ATTR_TARGET,
 294         XML_LRM_ATTR_TARGET_UUID,
 295         "pcmk_external_ip"
 296     };
 297 
 298     gboolean do_delete = FALSE;
 299     int lpc = 0;
 300     static int meta_len = 0;
 301 
 302     if (meta_len == 0) {
 303         meta_len = strlen(CRM_META);
 304     }
 305 
 306     if (param_set == NULL) {
 307         return;
 308     }
 309 
 310     for (lpc = 0; lpc < DIMOF(attr_filter); lpc++) {
 311         xml_remove_prop(param_set, attr_filter[lpc]);
 312     }
 313 
 314     key = crm_meta_name(XML_LRM_ATTR_INTERVAL);
 315     interval = crm_element_value_copy(param_set, key);
 316     free(key);
 317 
 318     key = crm_meta_name(XML_ATTR_TIMEOUT);
 319     timeout = crm_element_value_copy(param_set, key);
 320 
 321     if (param_set) {
 322         xmlAttrPtr xIter = param_set->properties;
 323 
 324         while (xIter) {
 325             const char *prop_name = (const char *)xIter->name;
 326 
 327             xIter = xIter->next;
 328             do_delete = FALSE;
 329             if (strncasecmp(prop_name, CRM_META, meta_len) == 0) {
 330                 do_delete = TRUE;
 331             }
 332 
 333             if (do_delete) {
 334                 xml_remove_prop(param_set, prop_name);
 335             }
 336         }
 337     }
 338 
 339     if (crm_get_msec(interval) > 0 && compare_version(version, "1.0.8") > 0) {
 340         /* Re-instate the operation's timeout value */
 341         if (timeout != NULL) {
 342             crm_xml_add(param_set, key, timeout);
 343         }
 344     }
 345 
 346     free(interval);
 347     free(timeout);
 348     free(key);
 349 }
 350 
 351 #define FAKE_TE_ID      "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
 352 static void
 353 append_digest(lrmd_event_data_t * op, xmlNode * update, const char *version, const char *magic,
     /* [previous][next][first][last][top][bottom][index][help] */
 354               int level)
 355 {
 356     /* this will enable us to later determine that the
 357      *   resource's parameters have changed and we should force
 358      *   a restart
 359      */
 360     char *digest = NULL;
 361     xmlNode *args_xml = NULL;
 362 
 363     if (op->params == NULL) {
 364         return;
 365     }
 366 
 367     args_xml = create_xml_node(NULL, XML_TAG_PARAMS);
 368     g_hash_table_foreach(op->params, hash2field, args_xml);
 369     filter_action_parameters(args_xml, version);
 370     digest = calculate_operation_digest(args_xml, version);
 371 
 372 #if 0
 373     if (level < get_crm_log_level()
 374         && op->interval == 0 && crm_str_eq(op->op_type, CRMD_ACTION_START, TRUE)) {
 375         char *digest_source = dump_xml_unformatted(args_xml);
 376 
 377         do_crm_log(level, "Calculated digest %s for %s (%s). Source: %s\n",
 378                    digest, ID(update), magic, digest_source);
 379         free(digest_source);
 380     }
 381 #endif
 382     crm_xml_add(update, XML_LRM_ATTR_OP_DIGEST, digest);
 383 
 384     free_xml(args_xml);
 385     free(digest);
 386 }
 387 
 388 int
 389 rsc_op_expected_rc(lrmd_event_data_t * op)
     /* [previous][next][first][last][top][bottom][index][help] */
 390 {
 391     int rc = 0;
 392 
 393     if (op && op->user_data) {
 394         int dummy = 0;
 395         char *uuid = NULL;
 396 
 397         decode_transition_key(op->user_data, &uuid, &dummy, &dummy, &rc);
 398         free(uuid);
 399     }
 400     return rc;
 401 }
 402 
 403 gboolean
 404 did_rsc_op_fail(lrmd_event_data_t * op, int target_rc)
     /* [previous][next][first][last][top][bottom][index][help] */
 405 {
 406     switch (op->op_status) {
 407         case PCMK_LRM_OP_CANCELLED:
 408         case PCMK_LRM_OP_PENDING:
 409             return FALSE;
 410             break;
 411 
 412         case PCMK_LRM_OP_NOTSUPPORTED:
 413         case PCMK_LRM_OP_TIMEOUT:
 414         case PCMK_LRM_OP_ERROR:
 415             return TRUE;
 416             break;
 417 
 418         default:
 419             if (target_rc != op->rc) {
 420                 return TRUE;
 421             }
 422     }
 423 
 424     return FALSE;
 425 }
 426 
 427 /*!
 428  * \brief Create a CIB XML element for an operation
 429  *
 430  * \param[in] parent    If not NULL, make new XML node a child of this one
 431  * \param[in] prefix    Generate an ID using this prefix
 432  * \param[in] task      Operation task to set
 433  * \param[in] interval  Operation interval to set
 434  * \param[in] timeout   If not NULL, operation timeout to set
 435  *
 436  * \return New XML object on success, NULL otherwise
 437  */
 438 xmlNode *
 439 crm_create_op_xml(xmlNode *parent, const char *prefix, const char *task,
     /* [previous][next][first][last][top][bottom][index][help] */
 440                   const char *interval, const char *timeout)
 441 {
 442     xmlNode *xml_op;
 443 
 444     CRM_CHECK(prefix && task && interval, return NULL);
 445 
 446     xml_op = create_xml_node(parent, XML_ATTR_OP);
 447     crm_xml_set_id(xml_op, "%s-%s-%s", prefix, task, interval);
 448     crm_xml_add(xml_op, XML_LRM_ATTR_INTERVAL, interval);
 449     crm_xml_add(xml_op, "name", task);
 450     if (timeout) {
 451         crm_xml_add(xml_op, XML_ATTR_TIMEOUT, timeout);
 452     }
 453     return xml_op;
 454 }
 455 
 456 xmlNode *
 457 create_operation_update(xmlNode * parent, lrmd_event_data_t * op, const char * caller_version,
     /* [previous][next][first][last][top][bottom][index][help] */
 458                         int target_rc, const char * node, const char * origin, int level)
 459 {
 460     char *key = NULL;
 461     char *magic = NULL;
 462     char *op_id = NULL;
 463     char *op_id_additional = NULL;
 464     char *local_user_data = NULL;
 465     const char *exit_reason = NULL;
 466 
 467     xmlNode *xml_op = NULL;
 468     const char *task = NULL;
 469     gboolean dc_munges_migrate_ops = (compare_version(caller_version, "3.0.3") < 0);
 470     gboolean dc_needs_unique_ops = (compare_version(caller_version, "3.0.6") < 0);
 471 
 472     CRM_CHECK(op != NULL, return NULL);
 473     do_crm_log(level, "%s: Updating resource %s after %s op %s (interval=%d)",
 474                origin, op->rsc_id, op->op_type, services_lrm_status_str(op->op_status),
 475                op->interval);
 476 
 477     crm_trace("DC version: %s", caller_version);
 478 
 479     task = op->op_type;
 480     /* remap the task name under various scenarios
 481      * this makes life easier for the PE when trying determine the current state
 482      */
 483     if (crm_str_eq(task, "reload", TRUE)) {
 484         if (op->op_status == PCMK_LRM_OP_DONE) {
 485             task = CRMD_ACTION_START;
 486         } else {
 487             task = CRMD_ACTION_STATUS;
 488         }
 489 
 490     } else if (dc_munges_migrate_ops && crm_str_eq(task, CRMD_ACTION_MIGRATE, TRUE)) {
 491         /* if the migrate_from fails it will have enough info to do the right thing */
 492         if (op->op_status == PCMK_LRM_OP_DONE) {
 493             task = CRMD_ACTION_STOP;
 494         } else {
 495             task = CRMD_ACTION_STATUS;
 496         }
 497 
 498     } else if (dc_munges_migrate_ops
 499                && op->op_status == PCMK_LRM_OP_DONE
 500                && crm_str_eq(task, CRMD_ACTION_MIGRATED, TRUE)) {
 501         task = CRMD_ACTION_START;
 502     }
 503 
 504     key = generate_op_key(op->rsc_id, task, op->interval);
 505     if (dc_needs_unique_ops && op->interval > 0) {
 506         op_id = strdup(key);
 507 
 508     } else if (crm_str_eq(task, CRMD_ACTION_NOTIFY, TRUE)) {
 509         const char *n_type = crm_meta_value(op->params, "notify_type");
 510         const char *n_task = crm_meta_value(op->params, "notify_operation");
 511 
 512         CRM_LOG_ASSERT(n_type != NULL);
 513         CRM_LOG_ASSERT(n_task != NULL);
 514         op_id = generate_notify_key(op->rsc_id, n_type, n_task);
 515 
 516         /* these are not yet allowed to fail */
 517         op->op_status = PCMK_LRM_OP_DONE;
 518         op->rc = 0;
 519 
 520     } else if (did_rsc_op_fail(op, target_rc)) {
 521         op_id = generate_op_key(op->rsc_id, "last_failure", 0);
 522         if (op->interval == 0) {
 523             /* Ensure 'last' gets updated too in case recording-pending="true" */
 524             op_id_additional = generate_op_key(op->rsc_id, "last", 0);
 525         }
 526         exit_reason = op->exit_reason;
 527 
 528     } else if (op->interval > 0) {
 529         op_id = strdup(key);
 530 
 531     } else {
 532         op_id = generate_op_key(op->rsc_id, "last", 0);
 533     }
 534 
 535   again:
 536     xml_op = find_entity(parent, XML_LRM_TAG_RSC_OP, op_id);
 537     if (xml_op == NULL) {
 538         xml_op = create_xml_node(parent, XML_LRM_TAG_RSC_OP);
 539     }
 540 
 541     if (op->user_data == NULL) {
 542         crm_debug("Generating fake transition key for:"
 543                   " %s_%s_%d %d from %s",
 544                   op->rsc_id, op->op_type, op->interval, op->call_id, origin);
 545         local_user_data = generate_transition_key(-1, op->call_id, target_rc, FAKE_TE_ID);
 546         op->user_data = local_user_data;
 547     }
 548 
 549     if(magic == NULL) {
 550         magic = generate_transition_magic(op->user_data, op->op_status, op->rc);
 551     }
 552 
 553     crm_xml_add(xml_op, XML_ATTR_ID, op_id);
 554     crm_xml_add(xml_op, XML_LRM_ATTR_TASK_KEY, key);
 555     crm_xml_add(xml_op, XML_LRM_ATTR_TASK, task);
 556     crm_xml_add(xml_op, XML_ATTR_ORIGIN, origin);
 557     crm_xml_add(xml_op, XML_ATTR_CRM_VERSION, caller_version);
 558     crm_xml_add(xml_op, XML_ATTR_TRANSITION_KEY, op->user_data);
 559     crm_xml_add(xml_op, XML_ATTR_TRANSITION_MAGIC, magic);
 560     crm_xml_add(xml_op, XML_LRM_ATTR_EXIT_REASON, exit_reason == NULL ? "" : exit_reason);
 561     crm_xml_add(xml_op, XML_LRM_ATTR_TARGET, node); /* For context during triage */
 562 
 563     crm_xml_add_int(xml_op, XML_LRM_ATTR_CALLID, op->call_id);
 564     crm_xml_add_int(xml_op, XML_LRM_ATTR_RC, op->rc);
 565     crm_xml_add_int(xml_op, XML_LRM_ATTR_OPSTATUS, op->op_status);
 566     crm_xml_add_int(xml_op, XML_LRM_ATTR_INTERVAL, op->interval);
 567 
 568     if (compare_version("2.1", caller_version) <= 0) {
 569         if (op->t_run || op->t_rcchange || op->exec_time || op->queue_time) {
 570             crm_trace("Timing data (%s_%s_%d): last=%u change=%u exec=%u queue=%u",
 571                       op->rsc_id, op->op_type, op->interval,
 572                       op->t_run, op->t_rcchange, op->exec_time, op->queue_time);
 573 
 574             if (op->interval == 0) {
 575                 /* The values are the same for non-recurring ops */
 576                 crm_xml_add_int(xml_op, XML_RSC_OP_LAST_RUN, op->t_run);
 577                 crm_xml_add_int(xml_op, XML_RSC_OP_LAST_CHANGE, op->t_run);
 578 
 579             } else if(op->t_rcchange) {
 580                 /* last-run is not accurate for recurring ops */
 581                 crm_xml_add_int(xml_op, XML_RSC_OP_LAST_CHANGE, op->t_rcchange);
 582 
 583             } else {
 584                 /* ...but is better than nothing otherwise */
 585                 crm_xml_add_int(xml_op, XML_RSC_OP_LAST_CHANGE, op->t_run);
 586             }
 587 
 588             crm_xml_add_int(xml_op, XML_RSC_OP_T_EXEC, op->exec_time);
 589             crm_xml_add_int(xml_op, XML_RSC_OP_T_QUEUE, op->queue_time);
 590         }
 591     }
 592 
 593     if (crm_str_eq(op->op_type, CRMD_ACTION_MIGRATE, TRUE)
 594         || crm_str_eq(op->op_type, CRMD_ACTION_MIGRATED, TRUE)) {
 595         /*
 596          * Record migrate_source and migrate_target always for migrate ops.
 597          */
 598         const char *name = XML_LRM_ATTR_MIGRATE_SOURCE;
 599 
 600         crm_xml_add(xml_op, name, crm_meta_value(op->params, name));
 601 
 602         name = XML_LRM_ATTR_MIGRATE_TARGET;
 603         crm_xml_add(xml_op, name, crm_meta_value(op->params, name));
 604     }
 605 
 606     append_digest(op, xml_op, caller_version, magic, LOG_DEBUG);
 607 
 608     if (op_id_additional) {
 609         free(op_id);
 610         op_id = op_id_additional;
 611         op_id_additional = NULL;
 612         goto again;
 613     }
 614 
 615     if (local_user_data) {
 616         free(local_user_data);
 617         op->user_data = NULL;
 618     }
 619     free(magic);
 620     free(op_id);
 621     free(key);
 622     return xml_op;
 623 }
 624 
 625 /*!
 626  * \brief Check whether an operation requires resource agent meta-data
 627  *
 628  * \param[in] rsc_class  Resource agent class (or NULL to skip class check)
 629  * \param[in] op         Operation action (or NULL to skip op check)
 630  *
 631  * \return TRUE if operation needs meta-data, FALSE otherwise
 632  * \note At least one of rsc_class and op must be specified.
 633  */
 634 bool
 635 crm_op_needs_metadata(const char *rsc_class, const char *op)
     /* [previous][next][first][last][top][bottom][index][help] */
 636 {
 637     /* Agent meta-data is used to determine whether a reload is possible, and to
 638      * evaluate versioned parameters -- so if this op is not relevant to those
 639      * features, we don't need the meta-data.
 640      */
 641 
 642     CRM_CHECK(rsc_class || op, return FALSE);
 643 
 644     if (rsc_class
 645         && strcmp(rsc_class, PCMK_RESOURCE_CLASS_OCF)
 646         && strcmp(rsc_class, PCMK_RESOURCE_CLASS_STONITH)) {
 647 
 648         /* Meta-data is only needed for resource classes that use parameters */
 649         return FALSE;
 650     }
 651 
 652     /* Meta-data is only needed for these actions */
 653     if (op
 654         && strcmp(op, CRMD_ACTION_START)
 655         && strcmp(op, CRMD_ACTION_STATUS)
 656         && strcmp(op, CRMD_ACTION_PROMOTE)
 657         && strcmp(op, CRMD_ACTION_DEMOTE)
 658         && strcmp(op, CRMD_ACTION_RELOAD)
 659         && strcmp(op, CRMD_ACTION_MIGRATE)
 660         && strcmp(op, CRMD_ACTION_MIGRATED)
 661         && strcmp(op, CRMD_ACTION_NOTIFY)) {
 662         return FALSE;
 663     }
 664 
 665     return TRUE;
 666 }

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