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