static void
process_op_deletion(const char *xpath, xmlNode *change)
{
    char *mutable_key = strdup(xpath);
    char *key;
    char *node_uuid;
    crm_action_t *cancel = NULL;

    // Extract the part of xpath between last pair of single quotes
    key = strrchr(mutable_key, '\'');
    if (key != NULL) {
        *key = '\0';
        key = strrchr(mutable_key, '\'');
    }
    if (key == NULL) {
        crm_warn("Ignoring malformed CIB update (resource deletion of %s)",
                 xpath);
        free(mutable_key);
        return;
    }
    ++key;

    node_uuid = extract_node_uuid(xpath);
    cancel = get_cancel_action(key, node_uuid);
    if (cancel) {
        confirm_cancel_action(cancel);

    } else {
        abort_transition(INFINITY, tg_restart, "Resource operation removal",
                         change);
    }
    free(mutable_key);
    free(node_uuid);
}
void
te_update_diff(const char *event, xmlNode * msg)
{
    int rc = -1;
    const char *op = NULL;

    xmlNode *diff = NULL;
    xmlXPathObject *xpathObj = NULL;

    int diff_add_updates = 0;
    int diff_add_epoch = 0;
    int diff_add_admin_epoch = 0;

    int diff_del_updates = 0;
    int diff_del_epoch = 0;
    int diff_del_admin_epoch = 0;

    CRM_CHECK(msg != NULL, return);
    crm_element_value_int(msg, F_CIB_RC, &rc);

    if (transition_graph == NULL) {
        crm_trace("No graph");
        return;

    } else if (rc < pcmk_ok) {
        crm_trace("Filter rc=%d (%s)", rc, pcmk_strerror(rc));
        return;

    } else if (transition_graph->complete == TRUE
               && fsa_state != S_IDLE
               && fsa_state != S_TRANSITION_ENGINE && fsa_state != S_POLICY_ENGINE) {
        crm_trace("Filter state=%s, complete=%d", fsa_state2string(fsa_state),
                  transition_graph->complete);
        return;
    }

    op = crm_element_value(msg, F_CIB_OPERATION);
    diff = get_message_xml(msg, F_CIB_UPDATE_RESULT);

    cib_diff_version_details(diff,
                             &diff_add_admin_epoch, &diff_add_epoch, &diff_add_updates,
                             &diff_del_admin_epoch, &diff_del_epoch, &diff_del_updates);

    crm_debug("Processing diff (%s): %d.%d.%d -> %d.%d.%d (%s)", op,
              diff_del_admin_epoch, diff_del_epoch, diff_del_updates,
              diff_add_admin_epoch, diff_add_epoch, diff_add_updates, fsa_state2string(fsa_state));
    log_cib_diff(LOG_DEBUG_2, diff, op);

    if (cib_config_changed(NULL, NULL, &diff)) {
        abort_transition(INFINITY, tg_restart, "Non-status change", diff);
        goto bail;              /* configuration changed */
    }

    /* Tickets Attributes - Added/Updated */
    xpathObj =
        xpath_search(diff,
                     "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_ADDED "//" XML_CIB_TAG_TICKETS);
    if (xpathObj && xpathObj->nodesetval->nodeNr > 0) {
        xmlNode *aborted = getXpathResult(xpathObj, 0);

        abort_transition(INFINITY, tg_restart, "Ticket attribute: update", aborted);
        goto bail;

    } else if (xpathObj) {
        xmlXPathFreeObject(xpathObj);
    }

    /* Tickets Attributes - Removed */
    xpathObj =
        xpath_search(diff,
                     "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_REMOVED "//" XML_CIB_TAG_TICKETS);
    if (xpathObj && xpathObj->nodesetval->nodeNr > 0) {
        xmlNode *aborted = getXpathResult(xpathObj, 0);

        abort_transition(INFINITY, tg_restart, "Ticket attribute: removal", aborted);
        goto bail;

    } else if (xpathObj) {
        xmlXPathFreeObject(xpathObj);
    }

    /* Transient Attributes - Added/Updated */
    xpathObj =
        xpath_search(diff,
                     "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_ADDED "//"
                     XML_TAG_TRANSIENT_NODEATTRS "//" XML_CIB_TAG_NVPAIR);
    if (xpathObj && xpathObj->nodesetval->nodeNr > 0) {
        int lpc;

        for (lpc = 0; lpc < xpathObj->nodesetval->nodeNr; lpc++) {
            xmlNode *attr = getXpathResult(xpathObj, lpc);
            const char *name = crm_element_value(attr, XML_NVPAIR_ATTR_NAME);
            const char *value = NULL;

            if (safe_str_eq(CRM_OP_PROBED, name)) {
                value = crm_element_value(attr, XML_NVPAIR_ATTR_VALUE);
            }

            if (crm_is_true(value) == FALSE) {
                abort_transition(INFINITY, tg_restart, "Transient attribute: update", attr);
                crm_log_xml_trace(attr, "Abort");
                goto bail;
            }
        }

    } else if (xpathObj) {
        xmlXPathFreeObject(xpathObj);
    }

    /* Transient Attributes - Removed */
    xpathObj =
        xpath_search(diff,
                     "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_REMOVED "//"
                     XML_TAG_TRANSIENT_NODEATTRS);
    if (xpathObj && xpathObj->nodesetval->nodeNr > 0) {
        xmlNode *aborted = getXpathResult(xpathObj, 0);

        abort_transition(INFINITY, tg_restart, "Transient attribute: removal", aborted);
        goto bail;

    } else if (xpathObj) {
        xmlXPathFreeObject(xpathObj);
    }

    /*
     * Check for and fast-track the processing of LRM refreshes
     * In large clusters this can result in _huge_ speedups
     *
     * Unfortunately we can only do so when there are no pending actions
     * Otherwise we could miss updates we're waiting for and stall 
     *
     */
    xpathObj = NULL;
    if (transition_graph->pending == 0) {
        xpathObj =
            xpath_search(diff,
                         "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_ADDED "//"
                         XML_LRM_TAG_RESOURCE);
    }

    if (xpathObj) {
        int updates = xpathObj->nodesetval->nodeNr;

        if (updates > 1) {
            /* Updates by, or in response to, TE actions will never contain updates
             * for more than one resource at a time
             */
            crm_debug("Detected LRM refresh - %d resources updated: Skipping all resource events",
                      updates);
            crm_log_xml_trace(diff, "lrm-refresh");
            abort_transition(INFINITY, tg_restart, "LRM Refresh", NULL);
            goto bail;
        }
        xmlXPathFreeObject(xpathObj);
    }

    /* Process operation updates */
    xpathObj =
        xpath_search(diff,
                     "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_ADDED "//" XML_LRM_TAG_RSC_OP);
    if (xpathObj) {
        process_resource_updates(xpathObj);
        xmlXPathFreeObject(xpathObj);
    }

    /* Detect deleted (as opposed to replaced or added) actions - eg. crm_resource -C */
    xpathObj = xpath_search(diff, "//" XML_TAG_DIFF_REMOVED "//" XML_LRM_TAG_RSC_OP);
    if (xpathObj) {
        int lpc = 0, max = xpathObj->nodesetval->nodeNr;

        for (lpc = 0; lpc < max; lpc++) {
            int max = 0;
            const char *op_id = NULL;
            char *rsc_op_xpath = NULL;
            xmlXPathObject *op_match = NULL;
            xmlNode *match = getXpathResult(xpathObj, lpc);

            CRM_CHECK(match != NULL, continue);

            op_id = ID(match);

            max = strlen(rsc_op_template) + strlen(op_id) + 1;
            rsc_op_xpath = calloc(1, max);
            snprintf(rsc_op_xpath, max, rsc_op_template, op_id);

            op_match = xpath_search(diff, rsc_op_xpath);
            if (op_match == NULL || op_match->nodesetval->nodeNr == 0) {
                /* Prevent false positives by matching cancelations too */
                const char *node = get_node_id(match);
                crm_action_t *cancelled = get_cancel_action(op_id, node);

                if (cancelled == NULL) {
                    crm_debug("No match for deleted action %s (%s on %s)", rsc_op_xpath, op_id,
                              node);
                    abort_transition(INFINITY, tg_restart, "Resource op removal", match);
                    if (op_match) {
                        xmlXPathFreeObject(op_match);
                    }
                    free(rsc_op_xpath);
                    goto bail;

                } else {
                    crm_debug("Deleted lrm_rsc_op %s on %s was for graph event %d",
                              op_id, node, cancelled->id);
                }
            }

            if (op_match) {
                xmlXPathFreeObject(op_match);
            }
            free(rsc_op_xpath);
        }
    }

  bail:
    if (xpathObj) {
        xmlXPathFreeObject(xpathObj);
    }
}
void
te_update_diff(const char *event, xmlNode *msg)
{
	int rc = -1;
	const char *op = NULL;

	xmlNode *diff = NULL;
	xmlNode *cib_top = NULL;
	xmlXPathObject *xpathObj = NULL;

	int diff_add_updates     = 0;
	int diff_add_epoch       = 0;
	int diff_add_admin_epoch = 0;

	int diff_del_updates     = 0;
	int diff_del_epoch       = 0;
	int diff_del_admin_epoch = 0;
	
	CRM_CHECK(msg != NULL, return);
	crm_element_value_int(msg, F_CIB_RC, &rc);	

	if(transition_graph == NULL) {
	    crm_debug_3("No graph");
	    return;

	} else if(rc < cib_ok) {
	    crm_debug_3("Filter rc=%d (%s)", rc, cib_error2string(rc));
	    return;

	} else if(transition_graph->complete == TRUE
		  && fsa_state != S_IDLE
		  && fsa_state != S_TRANSITION_ENGINE
		  && fsa_state != S_POLICY_ENGINE) {
	    crm_debug_2("Filter state=%s, complete=%d", fsa_state2string(fsa_state), transition_graph->complete);
	    return;
	} 	

	op = crm_element_value(msg, F_CIB_OPERATION);
	diff = get_message_xml(msg, F_CIB_UPDATE_RESULT);

	cib_diff_version_details(
		diff,
		&diff_add_admin_epoch, &diff_add_epoch, &diff_add_updates, 
		&diff_del_admin_epoch, &diff_del_epoch, &diff_del_updates);
	
	crm_debug("Processing diff (%s): %d.%d.%d -> %d.%d.%d (%s)", op,
		  diff_del_admin_epoch,diff_del_epoch,diff_del_updates,
		  diff_add_admin_epoch,diff_add_epoch,diff_add_updates,
		  fsa_state2string(fsa_state));
	log_cib_diff(LOG_DEBUG_2, diff, op);

	/* Process anything that was added */
	cib_top = get_xpath_object("//"F_CIB_UPDATE_RESULT"//"XML_TAG_DIFF_ADDED"//"XML_TAG_CIB, diff, LOG_ERR);
	if(need_abort(cib_top)) {
	    goto bail; /* configuration changed */
	}

	/* Process anything that was removed */
	cib_top = get_xpath_object("//"F_CIB_UPDATE_RESULT"//"XML_TAG_DIFF_REMOVED"//"XML_TAG_CIB, diff, LOG_ERR);
	if(need_abort(cib_top)) {
	    goto bail; /* configuration changed */
	}

	/* Transient Attributes - Added/Updated */
	xpathObj = xpath_search(diff,"//"F_CIB_UPDATE_RESULT"//"XML_TAG_DIFF_ADDED"//"XML_TAG_TRANSIENT_NODEATTRS"//"XML_CIB_TAG_NVPAIR);
	if(xpathObj && xpathObj->nodesetval->nodeNr > 0) {
	    int lpc;
	    for(lpc = 0; lpc < xpathObj->nodesetval->nodeNr; lpc++) {
		xmlNode *attr = getXpathResult(xpathObj, lpc);
		const char *name = crm_element_value(attr, XML_NVPAIR_ATTR_NAME);
		const char *value = NULL;
		
		if(safe_str_eq(CRM_OP_PROBED, name)) {
		    value = crm_element_value(attr, XML_NVPAIR_ATTR_VALUE);
		}

		if(crm_is_true(value) == FALSE) {
		    abort_transition(INFINITY, tg_restart, "Transient attribute: update", attr);
		    crm_log_xml_debug_2(attr, "Abort");
		    goto bail;
		}
	    }

	} else if(xpathObj) {
	    xmlXPathFreeObject(xpathObj);
	}
	
	/* Transient Attributes - Removed */
	xpathObj = xpath_search(diff,"//"F_CIB_UPDATE_RESULT"//"XML_TAG_DIFF_REMOVED"//"XML_TAG_TRANSIENT_NODEATTRS);
	if(xpathObj && xpathObj->nodesetval->nodeNr > 0) {
	    xmlNode *aborted = getXpathResult(xpathObj, 0);
	    abort_transition(INFINITY, tg_restart, "Transient attribute: removal", aborted);
	    goto bail;

	} else if(xpathObj) {
	    xmlXPathFreeObject(xpathObj);
	}

	/* Check for node state updates... possibly from a shutdown we requested */
	xpathObj = xpath_search(diff, "//"F_CIB_UPDATE_RESULT"//"XML_TAG_DIFF_ADDED"//"XML_CIB_TAG_STATE);
	if(xpathObj) {
	    int lpc = 0, max = xpathObj->nodesetval->nodeNr;
	    for(lpc = 0; lpc < max; lpc++) {
		xmlNode *node = getXpathResult(xpathObj, lpc);
		const char *event_node = crm_element_value(node, XML_ATTR_ID);
		const char *ccm_state  = crm_element_value(node, XML_CIB_ATTR_INCCM);
		const char *ha_state   = crm_element_value(node, XML_CIB_ATTR_HASTATE);
		const char *shutdown_s = crm_element_value(node, XML_CIB_ATTR_SHUTDOWN);
		const char *crmd_state = crm_element_value(node, XML_CIB_ATTR_CRMDSTATE);

		if(safe_str_eq(ccm_state, XML_BOOLEAN_FALSE)
		   || safe_str_eq(ha_state, DEADSTATUS)
		   || safe_str_eq(crmd_state, CRMD_JOINSTATE_DOWN)) {
		    crm_action_t *shutdown = match_down_event(0, event_node, NULL);
		    
		    if(shutdown != NULL) {
			const char *task = crm_element_value(shutdown->xml, XML_LRM_ATTR_TASK);
			if(safe_str_neq(task, CRM_OP_FENCE)) {
			    /* Wait for stonithd to tell us it is complete via tengine_stonith_callback() */
			    update_graph(transition_graph, shutdown);
			    trigger_graph();
			}
			
		    } else {
			crm_info("Stonith/shutdown of %s not matched", event_node);
			abort_transition(INFINITY, tg_restart, "Node failure", node);
		    }			
		    fail_incompletable_actions(transition_graph, event_node);
		}
	 
		if(shutdown_s) {
		    int shutdown = crm_parse_int(shutdown_s, NULL);
		    if(shutdown > 0) {
			crm_info("Aborting on "XML_CIB_ATTR_SHUTDOWN" attribute for %s", event_node);
			abort_transition(INFINITY, tg_restart, "Shutdown request", node);
		    }
		}
	    }
	    xmlXPathFreeObject(xpathObj);
	}

	/*
	 * Check for and fast-track the processing of LRM refreshes
	 * In large clusters this can result in _huge_ speedups
	 *
	 * Unfortunately we can only do so when there are no pending actions
	 * Otherwise we could miss updates we're waiting for and stall 
	 *
	 */
	xpathObj = NULL;
	if(transition_graph->pending == 0) {
	    xpathObj = xpath_search(diff, "//"F_CIB_UPDATE_RESULT"//"XML_TAG_DIFF_ADDED"//"XML_LRM_TAG_RESOURCE);
	}
	
	if(xpathObj) {
	    int updates = xpathObj->nodesetval->nodeNr;
	    if(updates > 1) {
		/* Updates by, or in response to, TE actions will never contain updates
		 * for more than one resource at a time
		 */
		crm_info("Detected LRM refresh - %d resources updated: Skipping all resource events", updates);
		abort_transition(INFINITY, tg_restart, "LRM Refresh", diff);
		goto bail;
	    }
	    xmlXPathFreeObject(xpathObj);
	}

	/* Process operation updates */
	xpathObj = xpath_search(diff, "//"F_CIB_UPDATE_RESULT"//"XML_TAG_DIFF_ADDED"//"XML_LRM_TAG_RSC_OP);
	if(xpathObj) {
	    process_resource_updates(xpathObj);
	    xmlXPathFreeObject(xpathObj);
	}
	
	/* Detect deleted (as opposed to replaced or added) actions - eg. crm_resource -C */ 
	xpathObj = xpath_search(diff, "//"XML_TAG_DIFF_REMOVED"//"XML_LRM_TAG_RSC_OP);
	if(xpathObj) {
	    int lpc = 0, max = xpathObj->nodesetval->nodeNr;
	    
	    for(lpc = 0; lpc < max; lpc++) {
		int max = 0;
		const char *op_id = NULL;
		char *rsc_op_xpath = NULL;
		xmlXPathObject *op_match = NULL;
		xmlNode *match = getXpathResult(xpathObj, lpc);
		CRM_CHECK(match != NULL, continue);

		op_id = ID(match);

		max = strlen(rsc_op_template) + strlen(op_id) + 1;
		crm_malloc0(rsc_op_xpath, max);
		snprintf(rsc_op_xpath, max, rsc_op_template, op_id);
		
		op_match = xpath_search(diff, rsc_op_xpath);
		if(op_match == NULL || op_match->nodesetval->nodeNr == 0) {
		    /* Prevent false positives by matching cancelations too */
		    const char *node = get_node_id(match);
		    crm_action_t *cancelled = get_cancel_action(op_id, node);

		    if(cancelled == NULL) {
			crm_debug("No match for deleted action %s (%s on %s)", rsc_op_xpath, op_id, node);
			abort_transition(INFINITY, tg_restart, "Resource op removal", match);
			if(op_match) {
			    xmlXPathFreeObject(op_match);
			}
			crm_free(rsc_op_xpath);
			goto bail;

		    } else {
			crm_debug("Deleted lrm_rsc_op %s on %s was for graph event %d",
				  op_id, node, cancelled->id);
		    }
		}

		if(op_match) {
		    xmlXPathFreeObject(op_match);
		}
		crm_free(rsc_op_xpath);
	    }
	}

  bail:
	if(xpathObj) {
	    xmlXPathFreeObject(xpathObj);
	}
}
static void
te_update_diff_v1(const char *event, xmlNode *diff)
{
    int lpc, max;
    xmlXPathObject *xpathObj = NULL;

    CRM_CHECK(diff != NULL, return);

    xml_log_patchset(LOG_TRACE, __FUNCTION__, diff);
    if (cib_config_changed(NULL, NULL, &diff)) {
        abort_transition(INFINITY, tg_restart, "Non-status change", diff);
        goto bail;              /* configuration changed */
    }

    /* Tickets Attributes - Added/Updated */
    xpathObj =
        xpath_search(diff,
                     "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_ADDED "//" XML_CIB_TAG_TICKETS);
    if (numXpathResults(xpathObj) > 0) {
        xmlNode *aborted = getXpathResult(xpathObj, 0);

        abort_transition(INFINITY, tg_restart, "Ticket attribute: update", aborted);
        goto bail;

    }
    freeXpathObject(xpathObj);

    /* Tickets Attributes - Removed */
    xpathObj =
        xpath_search(diff,
                     "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_REMOVED "//" XML_CIB_TAG_TICKETS);
    if (numXpathResults(xpathObj) > 0) {
        xmlNode *aborted = getXpathResult(xpathObj, 0);

        abort_transition(INFINITY, tg_restart, "Ticket attribute: removal", aborted);
        goto bail;
    }
    freeXpathObject(xpathObj);

    /* Transient Attributes - Added/Updated */
    xpathObj =
        xpath_search(diff,
                     "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_ADDED "//"
                     XML_TAG_TRANSIENT_NODEATTRS "//" XML_CIB_TAG_NVPAIR);
    max = numXpathResults(xpathObj);

    for (lpc = 0; lpc < max; lpc++) {
        xmlNode *attr = getXpathResult(xpathObj, lpc);
        const char *name = crm_element_value(attr, XML_NVPAIR_ATTR_NAME);
        const char *value = NULL;

        if (safe_str_eq(CRM_OP_PROBED, name)) {
            value = crm_element_value(attr, XML_NVPAIR_ATTR_VALUE);
        }

        if (crm_is_true(value) == FALSE) {
            abort_transition(INFINITY, tg_restart, "Transient attribute: update", attr);
            crm_log_xml_trace(attr, "Abort");
            goto bail;
        }
    }

    freeXpathObject(xpathObj);

    /* Transient Attributes - Removed */
    xpathObj =
        xpath_search(diff,
                     "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_REMOVED "//"
                     XML_TAG_TRANSIENT_NODEATTRS);
    if (numXpathResults(xpathObj) > 0) {
        xmlNode *aborted = getXpathResult(xpathObj, 0);

        abort_transition(INFINITY, tg_restart, "Transient attribute: removal", aborted);
        goto bail;

    }
    freeXpathObject(xpathObj);

    /*
     * Updates by, or in response to, TE actions will never contain updates
     * for more than one resource at a time, so such updates indicate an
     * LRM refresh.
     *
     * In that case, start a new transition rather than check each result
     * individually, which can result in _huge_ speedups in large clusters.
     *
     * Unfortunately, we can only do so when there are no pending actions.
     * Otherwise, we could mistakenly throw away those results here, and
     * the cluster will stall waiting for them and time out the operation.
     */
    if (transition_graph->pending == 0) {
        xpathObj = xpath_search(diff,
                                "//" F_CIB_UPDATE_RESULT
                                "//" XML_TAG_DIFF_ADDED
                                "//" XML_LRM_TAG_RESOURCE);
        max = numXpathResults(xpathObj);
        if (max > 1) {
            crm_debug("Ignoring resource operation updates due to history refresh of %d resources",
                      max);
            crm_log_xml_trace(diff, "lrm-refresh");
            abort_transition(INFINITY, tg_restart, "History refresh", NULL);
            goto bail;
        }
        freeXpathObject(xpathObj);
    }

    /* Process operation updates */
    xpathObj =
        xpath_search(diff,
                     "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_ADDED "//" XML_LRM_TAG_RSC_OP);
    max = numXpathResults(xpathObj);
    if (max > 0) {
        int lpc = 0;

        for (lpc = 0; lpc < max; lpc++) {
            xmlNode *rsc_op = getXpathResult(xpathObj, lpc);
            const char *node = get_node_id(rsc_op);

            process_graph_event(rsc_op, node);
        }
    }
    freeXpathObject(xpathObj);

    /* Detect deleted (as opposed to replaced or added) actions - eg. crm_resource -C */
    xpathObj = xpath_search(diff, "//" XML_TAG_DIFF_REMOVED "//" XML_LRM_TAG_RSC_OP);
    max = numXpathResults(xpathObj);
    for (lpc = 0; lpc < max; lpc++) {
        int path_max = 0;
        const char *op_id = NULL;
        char *rsc_op_xpath = NULL;
        xmlXPathObject *op_match = NULL;
        xmlNode *match = getXpathResult(xpathObj, lpc);

        CRM_LOG_ASSERT(match != NULL);
        if(match == NULL) { continue; };

        op_id = ID(match);

        path_max = strlen(RSC_OP_TEMPLATE) + strlen(op_id) + 1;
        rsc_op_xpath = calloc(1, path_max);
        snprintf(rsc_op_xpath, path_max, RSC_OP_TEMPLATE, op_id);

        op_match = xpath_search(diff, rsc_op_xpath);
        if (numXpathResults(op_match) == 0) {
            /* Prevent false positives by matching cancelations too */
            const char *node = get_node_id(match);
            crm_action_t *cancelled = get_cancel_action(op_id, node);

            if (cancelled == NULL) {
                crm_debug("No match for deleted action %s (%s on %s)", rsc_op_xpath, op_id,
                          node);
                abort_transition(INFINITY, tg_restart, "Resource op removal", match);
                freeXpathObject(op_match);
                free(rsc_op_xpath);
                goto bail;

            } else {
                crm_debug("Deleted lrm_rsc_op %s on %s was for graph event %d",
                          op_id, node, cancelled->id);
            }
        }

        freeXpathObject(op_match);
        free(rsc_op_xpath);
    }

  bail:
    freeXpathObject(xpathObj);
}
Exemple #5
0
static void
te_legacy_update_diff(const char *event, xmlNode * diff)
{
    int lpc, max;
    xmlXPathObject *xpathObj = NULL;

    CRM_CHECK(diff != NULL, return);

    xml_log_patchset(LOG_TRACE, __FUNCTION__, diff);
    if (cib_config_changed(NULL, NULL, &diff)) {
        abort_transition(INFINITY, tg_restart, "Non-status change", diff);
        goto bail;              /* configuration changed */
    }

    /* Tickets Attributes - Added/Updated */
    xpathObj =
        xpath_search(diff,
                     "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_ADDED "//" XML_CIB_TAG_TICKETS);
    if (numXpathResults(xpathObj) > 0) {
        xmlNode *aborted = getXpathResult(xpathObj, 0);

        abort_transition(INFINITY, tg_restart, "Ticket attribute: update", aborted);
        goto bail;

    }
    freeXpathObject(xpathObj);

    /* Tickets Attributes - Removed */
    xpathObj =
        xpath_search(diff,
                     "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_REMOVED "//" XML_CIB_TAG_TICKETS);
    if (numXpathResults(xpathObj) > 0) {
        xmlNode *aborted = getXpathResult(xpathObj, 0);

        abort_transition(INFINITY, tg_restart, "Ticket attribute: removal", aborted);
        goto bail;
    }
    freeXpathObject(xpathObj);

    /* Transient Attributes - Added/Updated */
    xpathObj =
        xpath_search(diff,
                     "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_ADDED "//"
                     XML_TAG_TRANSIENT_NODEATTRS "//" XML_CIB_TAG_NVPAIR);
    max = numXpathResults(xpathObj);

    for (lpc = 0; lpc < max; lpc++) {
        xmlNode *attr = getXpathResult(xpathObj, lpc);
        const char *name = crm_element_value(attr, XML_NVPAIR_ATTR_NAME);
        const char *value = NULL;

        if (safe_str_eq(CRM_OP_PROBED, name)) {
            value = crm_element_value(attr, XML_NVPAIR_ATTR_VALUE);
        }

        if (crm_is_true(value) == FALSE) {
            abort_transition(INFINITY, tg_restart, "Transient attribute: update", attr);
            crm_log_xml_trace(attr, "Abort");
            goto bail;
        }
    }

    freeXpathObject(xpathObj);

    /* Transient Attributes - Removed */
    xpathObj =
        xpath_search(diff,
                     "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_REMOVED "//"
                     XML_TAG_TRANSIENT_NODEATTRS);
    if (numXpathResults(xpathObj) > 0) {
        xmlNode *aborted = getXpathResult(xpathObj, 0);

        abort_transition(INFINITY, tg_restart, "Transient attribute: removal", aborted);
        goto bail;

    }
    freeXpathObject(xpathObj);

    /*
     * Check for and fast-track the processing of LRM refreshes
     * In large clusters this can result in _huge_ speedups
     *
     * Unfortunately we can only do so when there are no pending actions
     * Otherwise we could miss updates we're waiting for and stall
     *
     */
    xpathObj = NULL;
    if (transition_graph->pending == 0) {
        xpathObj =
            xpath_search(diff,
                         "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_ADDED "//"
                         XML_LRM_TAG_RESOURCE);
    }

    max = numXpathResults(xpathObj);
    if (max > 1) {
        /* Updates by, or in response to, TE actions will never contain updates
         * for more than one resource at a time
         */
        crm_debug("Detected LRM refresh - %d resources updated: Skipping all resource events", max);
        crm_log_xml_trace(diff, "lrm-refresh");
        abort_transition(INFINITY, tg_restart, "LRM Refresh", NULL);
        goto bail;
    }
    freeXpathObject(xpathObj);

    /* Process operation updates */
    xpathObj =
        xpath_search(diff,
                     "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_ADDED "//" XML_LRM_TAG_RSC_OP);
    if (numXpathResults(xpathObj)) {
/*
    <status>
       <node_state id="node1" state=CRMD_JOINSTATE_MEMBER exp_state="active">
          <lrm>
             <lrm_resources>
        	<rsc_state id="" rsc_id="rsc4" node_id="node1" rsc_state="stopped"/>
*/
        int lpc = 0, max = numXpathResults(xpathObj);

        for (lpc = 0; lpc < max; lpc++) {
            xmlNode *rsc_op = getXpathResult(xpathObj, lpc);
            const char *node = get_node_id(rsc_op);

            process_graph_event(rsc_op, node);
        }
    }
    freeXpathObject(xpathObj);

    /* Detect deleted (as opposed to replaced or added) actions - eg. crm_resource -C */
    xpathObj = xpath_search(diff, "//" XML_TAG_DIFF_REMOVED "//" XML_LRM_TAG_RSC_OP);
    max = numXpathResults(xpathObj);
    for (lpc = 0; lpc < max; lpc++) {
        int path_max = 0;
        const char *op_id = NULL;
        char *rsc_op_xpath = NULL;
        xmlXPathObject *op_match = NULL;
        xmlNode *match = getXpathResult(xpathObj, lpc);

        CRM_LOG_ASSERT(match != NULL);
        if(match == NULL) { continue; };

        op_id = ID(match);

        path_max = strlen(rsc_op_template) + strlen(op_id) + 1;
        rsc_op_xpath = calloc(1, path_max);
        snprintf(rsc_op_xpath, path_max, rsc_op_template, op_id);

        op_match = xpath_search(diff, rsc_op_xpath);
        if (numXpathResults(op_match) == 0) {
            /* Prevent false positives by matching cancelations too */
            const char *node = get_node_id(match);
            crm_action_t *cancelled = get_cancel_action(op_id, node);

            if (cancelled == NULL) {
                crm_debug("No match for deleted action %s (%s on %s)", rsc_op_xpath, op_id,
                          node);
                abort_transition(INFINITY, tg_restart, "Resource op removal", match);
                freeXpathObject(op_match);
                free(rsc_op_xpath);
                goto bail;

            } else {
                crm_debug("Deleted lrm_rsc_op %s on %s was for graph event %d",
                          op_id, node, cancelled->id);
            }
        }

        freeXpathObject(op_match);
        free(rsc_op_xpath);
    }

  bail:
    freeXpathObject(xpathObj);
}
Exemple #6
0
void
te_update_diff(const char *event, xmlNode * msg)
{
    int rc = -EINVAL;
    int format = 1;
    xmlNode *change = NULL;
    const char *op = NULL;

    xmlNode *diff = NULL;

    int p_add[] = { 0, 0, 0 };
    int p_del[] = { 0, 0, 0 };

    CRM_CHECK(msg != NULL, return);
    crm_element_value_int(msg, F_CIB_RC, &rc);

    if (transition_graph == NULL) {
        crm_trace("No graph");
        return;

    } else if (rc < pcmk_ok) {
        crm_trace("Filter rc=%d (%s)", rc, pcmk_strerror(rc));
        return;

    } else if (transition_graph->complete == TRUE
               && fsa_state != S_IDLE
               && fsa_state != S_TRANSITION_ENGINE && fsa_state != S_POLICY_ENGINE) {
        crm_trace("Filter state=%s, complete=%d", fsa_state2string(fsa_state),
                  transition_graph->complete);
        return;
    }

    op = crm_element_value(msg, F_CIB_OPERATION);
    diff = get_message_xml(msg, F_CIB_UPDATE_RESULT);

    xml_patch_versions(diff, p_add, p_del);
    crm_debug("Processing (%s) diff: %d.%d.%d -> %d.%d.%d (%s)", op,
              p_del[0], p_del[1], p_del[2], p_add[0], p_add[1], p_add[2],
              fsa_state2string(fsa_state));

    crm_element_value_int(diff, "format", &format);
    switch(format) {
        case 1:
            te_legacy_update_diff(event, diff);
            return;
        case 2:
            /* Cool, we know what to do here */
            crm_log_xml_trace(diff, "Patch:Raw");
            break;
        default:
            crm_warn("Unknown patch format: %d", format);
            return;
    }

    for (change = __xml_first_child(diff); change != NULL; change = __xml_next(change)) {
        const char *name = NULL;
        const char *op = crm_element_value(change, XML_DIFF_OP);
        const char *xpath = crm_element_value(change, XML_DIFF_PATH);
        xmlNode *match = NULL;
        const char *node = NULL;

        if(op == NULL) {
            continue;

        } else if(strcmp(op, "create") == 0) {
            match = change->children;

        } else if(strcmp(op, "move") == 0) {
            continue;

        } else if(strcmp(op, "modify") == 0) {
            match = first_named_child(change, XML_DIFF_RESULT);
            if(match) {
                match = match->children;
            }
        }

        if(match) {
            name = (const char *)match->name;
        }

        crm_trace("Handling %s operation for %s %p, %s", op, xpath, match, name);
        if(xpath == NULL) {
            /* Version field, ignore */

        } else if(strstr(xpath, "/cib/configuration")) {
            abort_transition(INFINITY, tg_restart, "Non-status change", change);
            break; /* Wont be packaged with any resource operations we may be waiting for */

        } else if(strstr(xpath, "/"XML_CIB_TAG_TICKETS) || safe_str_eq(name, XML_CIB_TAG_TICKETS)) {
            abort_transition(INFINITY, tg_restart, "Ticket attribute change", change);
            break; /* Wont be packaged with any resource operations we may be waiting for */

        } else if(strstr(xpath, "/"XML_TAG_TRANSIENT_NODEATTRS"[") || safe_str_eq(name, XML_TAG_TRANSIENT_NODEATTRS)) {
            abort_transition(INFINITY, tg_restart, "Transient attribute change", change);
            break; /* Wont be packaged with any resource operations we may be waiting for */

        } else if(strstr(xpath, "/"XML_LRM_TAG_RSC_OP"[") && safe_str_eq(op, "delete")) {
            crm_action_t *cancel = NULL;
            char *mutable_key = strdup(xpath);
            char *mutable_node = strdup(xpath);
            char *search = NULL;

            const char *key = NULL;
            const char *node_uuid = NULL;

            search = strrchr(mutable_key, '\'');
            search[0] = 0;

            key = strrchr(mutable_key, '\'') + 1;

            node_uuid = strstr(mutable_node, "node_state[@id=\'") + strlen("node_state[@id=\'");
            search = strchr(node_uuid, '\'');
            search[0] = 0;

            cancel = get_cancel_action(key, node_uuid);
            if (cancel == NULL) {
                abort_transition(INFINITY, tg_restart, "Resource operation removal", change);

            } else {
                crm_info("Cancellation of %s on %s confirmed (%d)", key, node_uuid, cancel->id);
                stop_te_timer(cancel->timer);
                te_action_confirmed(cancel);

                update_graph(transition_graph, cancel);
                trigger_graph();

            }
            free(mutable_node);
            free(mutable_key);

        } else if(strstr(xpath, "/"XML_CIB_TAG_LRM"[") && safe_str_eq(op, "delete")) {
            abort_transition(INFINITY, tg_restart, "Resource state removal", change);

        } else if(strstr(xpath, "/"XML_CIB_TAG_STATE"[") && safe_str_eq(op, "delete")) {
            abort_transition(INFINITY, tg_restart, "Node state removal", change);

        } else if(name == NULL) {
            crm_debug("No result for %s operation to %s", op, xpath);
            CRM_ASSERT(strcmp(op, "delete") == 0 || strcmp(op, "move") == 0);

        } else if(strcmp(name, XML_TAG_CIB) == 0) {
            xmlNode *state = NULL;
            xmlNode *status = first_named_child(match, XML_CIB_TAG_STATUS);
            xmlNode *config = first_named_child(match, XML_CIB_TAG_CONFIGURATION);

            for (state = __xml_first_child(status); state != NULL; state = __xml_next(state)) {
                xmlNode *lrm = first_named_child(state, XML_CIB_TAG_LRM);

                node = ID(state);
                process_resource_updates(node, lrm, change, op, xpath);
            }

            if(config) {
                abort_transition(INFINITY, tg_restart, "Non-status change", change);
            }

        } else if(strcmp(name, XML_CIB_TAG_STATUS) == 0) {
            xmlNode *state = NULL;

            for (state = __xml_first_child(match); state != NULL; state = __xml_next(state)) {
                xmlNode *lrm = first_named_child(state, XML_CIB_TAG_LRM);

                node = ID(state);
                process_resource_updates(node, lrm, change, op, xpath);
            }

        } else if(strcmp(name, XML_CIB_TAG_STATE) == 0) {
            xmlNode *lrm = first_named_child(match, XML_CIB_TAG_LRM);

            node = ID(match);
            process_resource_updates(node, lrm, change, op, xpath);

        } else if(strcmp(name, XML_CIB_TAG_LRM) == 0) {
            node = ID(match);
            process_resource_updates(node, match, change, op, xpath);

        } else if(strcmp(name, XML_LRM_TAG_RESOURCES) == 0) {
            char *local_node = get_node_from_xpath(xpath);

            process_resource_updates(local_node, match, change, op, xpath);
            free(local_node);

        } else if(strcmp(name, XML_LRM_TAG_RESOURCE) == 0) {

            xmlNode *rsc_op;
            char *local_node = get_node_from_xpath(xpath);

            for (rsc_op = __xml_first_child(match); rsc_op != NULL; rsc_op = __xml_next(rsc_op)) {
                process_graph_event(rsc_op, local_node);
            }
            free(local_node);

        } else if(strcmp(name, XML_LRM_TAG_RSC_OP) == 0) {
            char *local_node = get_node_from_xpath(xpath);

            process_graph_event(match, local_node);
            free(local_node);

        } else {
            crm_err("Ignoring %s operation for %s %p, %s", op, xpath, match, name);
        }
    }
}