From 3df6aff2d72d508c7c3eeb5ccc48856d624e86a6 Mon Sep 17 00:00:00 2001
From: Andrew Beekhof <andrew@beekhof.net>
Date: Fri, 25 Jul 2014 15:01:45 +1000
Subject: [PATCH] Fix: crm_mon: Repair notification delivery when the v2 patch
 format is in use

---
 tools/crm_mon.c | 183 ++++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 161 insertions(+), 22 deletions(-)

diff --git a/tools/crm_mon.c b/tools/crm_mon.c
index 3f79c46d7e..79b2ef2f23 100644
--- a/tools/crm_mon.c
+++ b/tools/crm_mon.c
@@ -2384,7 +2384,7 @@ send_smtp_trap(const char *node, const char *rsc, const char *task, int target_r
 }
 
 static void
-handle_rsc_op(xmlNode * rsc_op)
+handle_rsc_op(xmlNode * xml, const char *node)
 {
     int rc = -1;
     int status = -1;
@@ -2397,13 +2397,24 @@ handle_rsc_op(xmlNode * rsc_op)
     char *rsc = NULL;
     char *task = NULL;
     const char *desc = NULL;
-    const char *node = NULL;
     const char *magic = NULL;
-    const char *id = crm_element_value(rsc_op, XML_LRM_ATTR_TASK_KEY);
+    const char *id = NULL;
     char *update_te_uuid = NULL;
 
-    xmlNode *n = rsc_op;
+    xmlNode *n = xml;
+    xmlNode * rsc_op = xml;
 
+    if(strcmp((const char*)xml->name, XML_LRM_TAG_RSC_OP) != 0) {
+        xmlNode *cIter;
+
+        for(cIter = xml->children; cIter; cIter = cIter->next) {
+            handle_rsc_op(cIter, node);
+        }
+
+        return;
+    }
+
+    id = crm_element_value(rsc_op, XML_LRM_ATTR_TASK_KEY);
     if (id == NULL) {
         /* Compatability with <= 1.1.5 */
         id = ID(rsc_op);
@@ -2430,10 +2441,14 @@ handle_rsc_op(xmlNode * rsc_op)
         n = n->parent;
     }
 
-    node = crm_element_value(n, XML_ATTR_UNAME);
+    if(node == NULL) {
+        node = crm_element_value(n, XML_ATTR_UNAME);
+    }
+
     if (node == NULL) {
         node = ID(n);
     }
+
     if (node == NULL) {
         crm_err("No node detected for event %s (%s)", magic, id);
         goto bail;
@@ -2478,6 +2493,132 @@ mon_trigger_refresh(gpointer user_data)
     return FALSE;
 }
 
+#define NODE_PATT "/lrm[@id="
+static char *get_node_from_xpath(const char *xpath) 
+{
+    char *nodeid = NULL;
+    char *tmp = strstr(xpath, NODE_PATT);
+
+    if(tmp) {
+        tmp += strlen(NODE_PATT);
+        tmp += 1;
+
+        nodeid = strdup(tmp);
+        tmp = strstr(nodeid, "\'");
+        CRM_ASSERT(tmp);
+        tmp[0] = 0;
+    }
+    return nodeid;
+}
+
+static void crm_diff_update_v2(const char *event, xmlNode * msg) 
+{
+    xmlNode *change = NULL;
+    xmlNode *diff = get_message_xml(msg, F_CIB_UPDATE_RESULT);
+
+    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, "delete") == 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(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);
+
+            for (state = __xml_first_child(status); state != NULL; state = __xml_next(state)) {
+                node = ID(state);
+                handle_rsc_op(state, node);
+            }
+
+        } else if(strcmp(name, XML_CIB_TAG_STATUS) == 0) {
+            xmlNode *state = NULL;
+
+            for (state = __xml_first_child(match); state != NULL; state = __xml_next(state)) {
+                node = ID(state);
+                handle_rsc_op(state, node);
+            }
+
+        } else if(strcmp(name, XML_CIB_TAG_STATE) == 0) {
+            node = ID(match);
+            handle_rsc_op(match, node);
+
+        } else if(strcmp(name, XML_CIB_TAG_LRM) == 0) {
+            node = ID(match);
+            handle_rsc_op(match, node);
+
+        } else if(strcmp(name, XML_LRM_TAG_RESOURCES) == 0) {
+            char *local_node = get_node_from_xpath(xpath);
+
+            handle_rsc_op(match, local_node);
+            free(local_node);
+
+        } else if(strcmp(name, XML_LRM_TAG_RESOURCE) == 0) {
+            char *local_node = get_node_from_xpath(xpath);
+
+            handle_rsc_op(match, local_node);
+            free(local_node);
+
+        } else if(strcmp(name, XML_LRM_TAG_RSC_OP) == 0) {
+            char *local_node = get_node_from_xpath(xpath);
+
+            handle_rsc_op(match, local_node);
+            free(local_node);
+
+        } else {
+            crm_err("Ignoring %s operation for %s %p, %s", op, xpath, match, name);
+        }
+    }
+}
+
+static void crm_diff_update_v1(const char *event, xmlNode * msg) 
+{
+    /* Process operation updates */
+    xmlXPathObject *xpathObj = xpath_search(msg,
+                                            "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_ADDED
+                                            "//" XML_LRM_TAG_RSC_OP);
+    int lpc = 0, max = numXpathResults(xpathObj);
+
+    for (lpc = 0; lpc < max; lpc++) {
+        xmlNode *rsc_op = getXpathResult(xpathObj, lpc);
+
+        handle_rsc_op(rsc_op, NULL);
+    }
+    freeXpathObject(xpathObj);
+}
+
 void
 crm_diff_update(const char *event, xmlNode * msg)
 {
@@ -2486,6 +2627,7 @@ crm_diff_update(const char *event, xmlNode * msg)
     static bool stale = FALSE;
     static int updates = 0;
     static mainloop_timer_t *refresh_timer = NULL;
+    xmlNode *diff = get_message_xml(msg, F_CIB_UPDATE_RESULT);
 
     print_dot();
 
@@ -2494,23 +2636,20 @@ crm_diff_update(const char *event, xmlNode * msg)
     }
 
     if (current_cib != NULL) {
-        xmlNode *cib_last = current_cib;
-
-        current_cib = NULL;
-
-        rc = cib_apply_patch_event(msg, cib_last, &current_cib, LOG_DEBUG);
-        free_xml(cib_last);
+        rc = xml_apply_patchset(current_cib, diff, TRUE);
 
         switch (rc) {
             case -pcmk_err_diff_resync:
             case -pcmk_err_diff_failed:
                 crm_notice("[%s] Patch aborted: %s (%d)", event, pcmk_strerror(rc), rc);
+                free_xml(current_cib); current_cib = NULL;
                 break;
             case pcmk_ok:
                 updates++;
                 break;
             default:
                 crm_notice("[%s] ABORTED: %s (%d)", event, pcmk_strerror(rc), rc);
+                free_xml(current_cib); current_cib = NULL;
         }
     }
 
@@ -2520,18 +2659,18 @@ crm_diff_update(const char *event, xmlNode * msg)
     }
 
     if (crm_mail_to || snmp_target || external_agent) {
-        /* Process operation updates */
-        xmlXPathObject *xpathObj = xpath_search(msg,
-                                                "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_ADDED
-                                                "//" XML_LRM_TAG_RSC_OP);
-        int lpc = 0, max = numXpathResults(xpathObj);
-
-        for (lpc = 0; lpc < max; lpc++) {
-            xmlNode *rsc_op = getXpathResult(xpathObj, lpc);
-
-            handle_rsc_op(rsc_op);
+        int format = 0;
+        crm_element_value_int(diff, "format", &format);
+        switch(format) {
+            case 1:
+                crm_diff_update_v1(event, msg);
+                break;
+            case 2:
+                crm_diff_update_v2(event, msg);
+                break;
+            default:
+                crm_err("Unknown patch format: %d", format);
         }
-        freeXpathObject(xpathObj);
     }
 
     if (current_cib == NULL) {
