Blame SOURCES/001-status-deletion.patch

ed4e54
From 6c529bb624ad548f66ce6ef1fa80b77c688918f4 Mon Sep 17 00:00:00 2001
ed4e54
From: Ken Gaillot <kgaillot@redhat.com>
ed4e54
Date: Fri, 22 Nov 2019 16:39:54 -0600
ed4e54
Subject: [PATCH 1/4] Refactor: controller: rename struct recurring_op_s to
ed4e54
 active_op_t
ed4e54
ed4e54
... because it holds both recurring and pending non-recurring actions,
ed4e54
and the name was confusing
ed4e54
---
ed4e54
 daemons/controld/controld_execd.c       | 18 +++++++++---------
ed4e54
 daemons/controld/controld_execd_state.c |  4 ++--
ed4e54
 daemons/controld/controld_lrm.h         |  8 ++++----
ed4e54
 3 files changed, 15 insertions(+), 15 deletions(-)
ed4e54
ed4e54
diff --git a/daemons/controld/controld_execd.c b/daemons/controld/controld_execd.c
ed4e54
index 9e8dd36..48f35dd 100644
ed4e54
--- a/daemons/controld/controld_execd.c
ed4e54
+++ b/daemons/controld/controld_execd.c
ed4e54
@@ -403,7 +403,7 @@ lrm_state_verify_stopped(lrm_state_t * lrm_state, enum crmd_fsa_state cur_state,
ed4e54
     GHashTableIter gIter;
ed4e54
     const char *key = NULL;
ed4e54
     rsc_history_t *entry = NULL;
ed4e54
-    struct recurring_op_s *pending = NULL;
ed4e54
+    active_op_t *pending = NULL;
ed4e54
 
ed4e54
     crm_debug("Checking for active resources before exit");
ed4e54
 
ed4e54
@@ -909,7 +909,7 @@ static gboolean
ed4e54
 lrm_remove_deleted_op(gpointer key, gpointer value, gpointer user_data)
ed4e54
 {
ed4e54
     const char *rsc = user_data;
ed4e54
-    struct recurring_op_s *pending = value;
ed4e54
+    active_op_t *pending = value;
ed4e54
 
ed4e54
     if (crm_str_eq(rsc, pending->rsc_id, TRUE)) {
ed4e54
         crm_info("Removing op %s:%d for deleted resource %s",
ed4e54
@@ -1137,7 +1137,7 @@ cancel_op(lrm_state_t * lrm_state, const char *rsc_id, const char *key, int op,
ed4e54
 {
ed4e54
     int rc = pcmk_ok;
ed4e54
     char *local_key = NULL;
ed4e54
-    struct recurring_op_s *pending = NULL;
ed4e54
+    active_op_t *pending = NULL;
ed4e54
 
ed4e54
     CRM_CHECK(op != 0, return FALSE);
ed4e54
     CRM_CHECK(rsc_id != NULL, return FALSE);
ed4e54
@@ -1203,7 +1203,7 @@ cancel_action_by_key(gpointer key, gpointer value, gpointer user_data)
ed4e54
 {
ed4e54
     gboolean remove = FALSE;
ed4e54
     struct cancel_data *data = user_data;
ed4e54
-    struct recurring_op_s *op = (struct recurring_op_s *)value;
ed4e54
+    active_op_t *op = value;
ed4e54
 
ed4e54
     if (crm_str_eq(op->op_key, data->key, TRUE)) {
ed4e54
         data->done = TRUE;
ed4e54
@@ -2107,7 +2107,7 @@ stop_recurring_action_by_rsc(gpointer key, gpointer value, gpointer user_data)
ed4e54
 {
ed4e54
     gboolean remove = FALSE;
ed4e54
     struct stop_recurring_action_s *event = user_data;
ed4e54
-    struct recurring_op_s *op = (struct recurring_op_s *)value;
ed4e54
+    active_op_t *op = value;
ed4e54
 
ed4e54
     if ((op->interval_ms != 0)
ed4e54
         && crm_str_eq(op->rsc_id, event->rsc->id, TRUE)) {
ed4e54
@@ -2124,7 +2124,7 @@ stop_recurring_actions(gpointer key, gpointer value, gpointer user_data)
ed4e54
 {
ed4e54
     gboolean remove = FALSE;
ed4e54
     lrm_state_t *lrm_state = user_data;
ed4e54
-    struct recurring_op_s *op = (struct recurring_op_s *)value;
ed4e54
+    active_op_t *op = value;
ed4e54
 
ed4e54
     if (op->interval_ms != 0) {
ed4e54
         crm_info("Cancelling op %d for %s (%s)", op->call_id, op->rsc_id,
ed4e54
@@ -2297,9 +2297,9 @@ do_lrm_rsc_op(lrm_state_t * lrm_state, lrmd_rsc_info_t * rsc, const char *operat
ed4e54
          * for them to complete during shutdown
ed4e54
          */
ed4e54
         char *call_id_s = make_stop_id(rsc->id, call_id);
ed4e54
-        struct recurring_op_s *pending = NULL;
ed4e54
+        active_op_t *pending = NULL;
ed4e54
 
ed4e54
-        pending = calloc(1, sizeof(struct recurring_op_s));
ed4e54
+        pending = calloc(1, sizeof(active_op_t));
ed4e54
         crm_trace("Recording pending op: %d - %s %s", call_id, op_id, call_id_s);
ed4e54
 
ed4e54
         pending->call_id = call_id;
ed4e54
@@ -2517,7 +2517,7 @@ did_lrm_rsc_op_fail(lrm_state_t *lrm_state, const char * rsc_id,
ed4e54
 
ed4e54
 void
ed4e54
 process_lrm_event(lrm_state_t *lrm_state, lrmd_event_data_t *op,
ed4e54
-                  struct recurring_op_s *pending, xmlNode *action_xml)
ed4e54
+                  active_op_t *pending, xmlNode *action_xml)
ed4e54
 {
ed4e54
     char *op_id = NULL;
ed4e54
     char *op_key = NULL;
ed4e54
diff --git a/daemons/controld/controld_execd_state.c b/daemons/controld/controld_execd_state.c
ed4e54
index 0e21d18..473da97 100644
ed4e54
--- a/daemons/controld/controld_execd_state.c
ed4e54
+++ b/daemons/controld/controld_execd_state.c
ed4e54
@@ -44,7 +44,7 @@ free_deletion_op(gpointer value)
ed4e54
 static void
ed4e54
 free_recurring_op(gpointer value)
ed4e54
 {
ed4e54
-    struct recurring_op_s *op = (struct recurring_op_s *)value;
ed4e54
+    active_op_t *op = value;
ed4e54
 
ed4e54
     free(op->user_data);
ed4e54
     free(op->rsc_id);
ed4e54
@@ -61,7 +61,7 @@ fail_pending_op(gpointer key, gpointer value, gpointer user_data)
ed4e54
 {
ed4e54
     lrmd_event_data_t event = { 0, };
ed4e54
     lrm_state_t *lrm_state = user_data;
ed4e54
-    struct recurring_op_s *op = (struct recurring_op_s *)value;
ed4e54
+    active_op_t *op = value;
ed4e54
 
ed4e54
     crm_trace("Pre-emptively failing " CRM_OP_FMT " on %s (call=%s, %s)",
ed4e54
               op->rsc_id, op->op_type, op->interval_ms,
ed4e54
diff --git a/daemons/controld/controld_lrm.h b/daemons/controld/controld_lrm.h
ed4e54
index 598682b..27df5d7 100644
ed4e54
--- a/daemons/controld/controld_lrm.h
ed4e54
+++ b/daemons/controld/controld_lrm.h
ed4e54
@@ -33,8 +33,8 @@ typedef struct resource_history_s {
ed4e54
 
ed4e54
 void history_free(gpointer data);
ed4e54
 
ed4e54
-/* TODO - Replace this with lrmd_event_data_t */
ed4e54
-struct recurring_op_s {
ed4e54
+// In-flight action (recurring or pending)
ed4e54
+typedef struct active_op_s {
ed4e54
     guint interval_ms;
ed4e54
     int call_id;
ed4e54
     gboolean remove;
ed4e54
@@ -45,7 +45,7 @@ struct recurring_op_s {
ed4e54
     char *op_key;
ed4e54
     char *user_data;
ed4e54
     GHashTable *params;
ed4e54
-};
ed4e54
+} active_op_t;
ed4e54
 
ed4e54
 typedef struct lrm_state_s {
ed4e54
     const char *node_name;
ed4e54
@@ -164,4 +164,4 @@ void remote_ra_process_maintenance_nodes(xmlNode *xml);
ed4e54
 gboolean remote_ra_controlling_guest(lrm_state_t * lrm_state);
ed4e54
 
ed4e54
 void process_lrm_event(lrm_state_t *lrm_state, lrmd_event_data_t *op,
ed4e54
-                       struct recurring_op_s *pending, xmlNode *action_xml);
ed4e54
+                       active_op_t *pending, xmlNode *action_xml);
ed4e54
-- 
ed4e54
1.8.3.1
ed4e54
ed4e54
ed4e54
From 93a59f1df8fe11d365032d75f10cb4189ad2f1f8 Mon Sep 17 00:00:00 2001
ed4e54
From: Ken Gaillot <kgaillot@redhat.com>
ed4e54
Date: Fri, 22 Nov 2019 16:45:31 -0600
ed4e54
Subject: [PATCH 2/4] Refactor: controller: convert active_op_t booleans to
ed4e54
 bitmask
ed4e54
ed4e54
---
ed4e54
 daemons/controld/controld_execd.c | 11 +++++------
ed4e54
 daemons/controld/controld_lrm.h   |  8 ++++++--
ed4e54
 2 files changed, 11 insertions(+), 8 deletions(-)
ed4e54
ed4e54
diff --git a/daemons/controld/controld_execd.c b/daemons/controld/controld_execd.c
ed4e54
index 48f35dd..2c9d9c0 100644
ed4e54
--- a/daemons/controld/controld_execd.c
ed4e54
+++ b/daemons/controld/controld_execd.c
ed4e54
@@ -1148,18 +1148,17 @@ cancel_op(lrm_state_t * lrm_state, const char *rsc_id, const char *key, int op,
ed4e54
     pending = g_hash_table_lookup(lrm_state->pending_ops, key);
ed4e54
 
ed4e54
     if (pending) {
ed4e54
-        if (remove && pending->remove == FALSE) {
ed4e54
-            pending->remove = TRUE;
ed4e54
+        if (remove && is_not_set(pending->flags, active_op_remove)) {
ed4e54
+            set_bit(pending->flags, active_op_remove);
ed4e54
             crm_debug("Scheduling %s for removal", key);
ed4e54
         }
ed4e54
 
ed4e54
-        if (pending->cancelled) {
ed4e54
+        if (is_set(pending->flags, active_op_cancelled)) {
ed4e54
             crm_debug("Operation %s already cancelled", key);
ed4e54
             free(local_key);
ed4e54
             return FALSE;
ed4e54
         }
ed4e54
-
ed4e54
-        pending->cancelled = TRUE;
ed4e54
+        set_bit(pending->flags, active_op_cancelled);
ed4e54
 
ed4e54
     } else {
ed4e54
         crm_info("No pending op found for %s", key);
ed4e54
@@ -2652,7 +2651,7 @@ process_lrm_event(lrm_state_t *lrm_state, lrmd_event_data_t *op,
ed4e54
         crm_err("Recurring operation %s was cancelled without transition information",
ed4e54
                 op_key);
ed4e54
 
ed4e54
-    } else if (pending->remove) {
ed4e54
+    } else if (is_set(pending->flags, active_op_remove)) {
ed4e54
         /* This recurring operation was cancelled (by us) and pending, and we
ed4e54
          * have been waiting for it to finish.
ed4e54
          */
ed4e54
diff --git a/daemons/controld/controld_lrm.h b/daemons/controld/controld_lrm.h
ed4e54
index 27df5d7..3ab7048 100644
ed4e54
--- a/daemons/controld/controld_lrm.h
ed4e54
+++ b/daemons/controld/controld_lrm.h
ed4e54
@@ -33,12 +33,16 @@ typedef struct resource_history_s {
ed4e54
 
ed4e54
 void history_free(gpointer data);
ed4e54
 
ed4e54
+enum active_op_e {
ed4e54
+    active_op_remove    = (1 << 0),
ed4e54
+    active_op_cancelled = (1 << 1),
ed4e54
+};
ed4e54
+
ed4e54
 // In-flight action (recurring or pending)
ed4e54
 typedef struct active_op_s {
ed4e54
     guint interval_ms;
ed4e54
     int call_id;
ed4e54
-    gboolean remove;
ed4e54
-    gboolean cancelled;
ed4e54
+    uint32_t flags; // bitmask of active_op_e
ed4e54
     time_t start_time;
ed4e54
     char *rsc_id;
ed4e54
     char *op_type;
ed4e54
-- 
ed4e54
1.8.3.1
ed4e54
ed4e54
ed4e54
From 4d087d021d325e26b41a9b36b5b190dc7b25334c Mon Sep 17 00:00:00 2001
ed4e54
From: Ken Gaillot <kgaillot@redhat.com>
ed4e54
Date: Fri, 22 Nov 2019 16:58:25 -0600
ed4e54
Subject: [PATCH 3/4] Refactor: controller: remove unused argument
ed4e54
ed4e54
---
ed4e54
 daemons/controld/controld_execd.c | 10 +++++-----
ed4e54
 1 file changed, 5 insertions(+), 5 deletions(-)
ed4e54
ed4e54
diff --git a/daemons/controld/controld_execd.c b/daemons/controld/controld_execd.c
ed4e54
index 2c9d9c0..46c1958 100644
ed4e54
--- a/daemons/controld/controld_execd.c
ed4e54
+++ b/daemons/controld/controld_execd.c
ed4e54
@@ -43,8 +43,8 @@ static int delete_rsc_status(lrm_state_t * lrm_state, const char *rsc_id, int ca
ed4e54
 
ed4e54
 static lrmd_event_data_t *construct_op(lrm_state_t * lrm_state, xmlNode * rsc_op,
ed4e54
                                        const char *rsc_id, const char *operation);
ed4e54
-static void do_lrm_rsc_op(lrm_state_t * lrm_state, lrmd_rsc_info_t * rsc, const char *operation,
ed4e54
-                          xmlNode * msg, xmlNode * request);
ed4e54
+static void do_lrm_rsc_op(lrm_state_t *lrm_state, lrmd_rsc_info_t *rsc,
ed4e54
+                          const char *operation, xmlNode *msg);
ed4e54
 
ed4e54
 void send_direct_ack(const char *to_host, const char *to_sys,
ed4e54
                      lrmd_rsc_info_t * rsc, lrmd_event_data_t * op, const char *rsc_id);
ed4e54
@@ -1858,7 +1858,7 @@ do_lrm_invoke(long long action,
ed4e54
                           crm_rsc_delete, user_name);
ed4e54
 
ed4e54
         } else {
ed4e54
-            do_lrm_rsc_op(lrm_state, rsc, operation, input->xml, input->msg);
ed4e54
+            do_lrm_rsc_op(lrm_state, rsc, operation, input->xml);
ed4e54
         }
ed4e54
 
ed4e54
         lrmd_free_rsc_info(rsc);
ed4e54
@@ -2170,8 +2170,8 @@ record_pending_op(const char *node_name, lrmd_rsc_info_t *rsc, lrmd_event_data_t
ed4e54
 }
ed4e54
 
ed4e54
 static void
ed4e54
-do_lrm_rsc_op(lrm_state_t * lrm_state, lrmd_rsc_info_t * rsc, const char *operation, xmlNode * msg,
ed4e54
-              xmlNode * request)
ed4e54
+do_lrm_rsc_op(lrm_state_t *lrm_state, lrmd_rsc_info_t *rsc,
ed4e54
+              const char *operation, xmlNode *msg)
ed4e54
 {
ed4e54
     int call_id = 0;
ed4e54
     char *op_id = NULL;
ed4e54
-- 
ed4e54
1.8.3.1
ed4e54
ed4e54
ed4e54
From 356b417274918b7da6cdd9c72c036c923160b318 Mon Sep 17 00:00:00 2001
ed4e54
From: Ken Gaillot <kgaillot@redhat.com>
ed4e54
Date: Fri, 6 Dec 2019 12:15:05 -0600
ed4e54
Subject: [PATCH 4/4] Refactor: scheduler: combine two "if" statements
ed4e54
ed4e54
... for readability, and ease of adding another block later
ed4e54
---
ed4e54
 lib/pacemaker/pcmk_sched_graph.c | 120 +++++++++++++++++++--------------------
ed4e54
 1 file changed, 60 insertions(+), 60 deletions(-)
ed4e54
ed4e54
diff --git a/lib/pacemaker/pcmk_sched_graph.c b/lib/pacemaker/pcmk_sched_graph.c
ed4e54
index e5a8a01..a6967fe 100644
ed4e54
--- a/lib/pacemaker/pcmk_sched_graph.c
ed4e54
+++ b/lib/pacemaker/pcmk_sched_graph.c
ed4e54
@@ -1088,71 +1088,71 @@ action2xml(action_t * action, gboolean as_input, pe_working_set_t *data_set)
ed4e54
         return action_xml;
ed4e54
     }
ed4e54
 
ed4e54
-    /* List affected resource */
ed4e54
-    if (action->rsc) {
ed4e54
-        if (is_set(action->flags, pe_action_pseudo) == FALSE) {
ed4e54
-            int lpc = 0;
ed4e54
-
ed4e54
-            xmlNode *rsc_xml = create_xml_node(action_xml, crm_element_name(action->rsc->xml));
ed4e54
-
ed4e54
-            const char *attr_list[] = {
ed4e54
-                XML_AGENT_ATTR_CLASS,
ed4e54
-                XML_AGENT_ATTR_PROVIDER,
ed4e54
-                XML_ATTR_TYPE
ed4e54
-            };
ed4e54
-
ed4e54
-            if (is_set(action->rsc->flags, pe_rsc_orphan) && action->rsc->clone_name) {
ed4e54
-                /* Do not use the 'instance free' name here as that
ed4e54
-                 * might interfere with the instance we plan to keep.
ed4e54
-                 * Ie. if there are more than two named /anonymous/
ed4e54
-                 * instances on a given node, we need to make sure the
ed4e54
-                 * command goes to the right one.
ed4e54
-                 *
ed4e54
-                 * Keep this block, even when everyone is using
ed4e54
-                 * 'instance free' anonymous clone names - it means
ed4e54
-                 * we'll do the right thing if anyone toggles the
ed4e54
-                 * unique flag to 'off'
ed4e54
-                 */
ed4e54
-                crm_debug("Using orphan clone name %s instead of %s", action->rsc->id,
ed4e54
-                          action->rsc->clone_name);
ed4e54
-                crm_xml_add(rsc_xml, XML_ATTR_ID, action->rsc->clone_name);
ed4e54
-                crm_xml_add(rsc_xml, XML_ATTR_ID_LONG, action->rsc->id);
ed4e54
+    if (action->rsc && is_not_set(action->flags, pe_action_pseudo)) {
ed4e54
+        int lpc = 0;
ed4e54
+        xmlNode *rsc_xml = NULL;
ed4e54
+        const char *attr_list[] = {
ed4e54
+            XML_AGENT_ATTR_CLASS,
ed4e54
+            XML_AGENT_ATTR_PROVIDER,
ed4e54
+            XML_ATTR_TYPE
ed4e54
+        };
ed4e54
+
ed4e54
+        // List affected resource
ed4e54
+
ed4e54
+        rsc_xml = create_xml_node(action_xml,
ed4e54
+                                  crm_element_name(action->rsc->xml));
ed4e54
+        if (is_set(action->rsc->flags, pe_rsc_orphan)
ed4e54
+            && action->rsc->clone_name) {
ed4e54
+            /* Do not use the 'instance free' name here as that
ed4e54
+             * might interfere with the instance we plan to keep.
ed4e54
+             * Ie. if there are more than two named /anonymous/
ed4e54
+             * instances on a given node, we need to make sure the
ed4e54
+             * command goes to the right one.
ed4e54
+             *
ed4e54
+             * Keep this block, even when everyone is using
ed4e54
+             * 'instance free' anonymous clone names - it means
ed4e54
+             * we'll do the right thing if anyone toggles the
ed4e54
+             * unique flag to 'off'
ed4e54
+             */
ed4e54
+            crm_debug("Using orphan clone name %s instead of %s", action->rsc->id,
ed4e54
+                      action->rsc->clone_name);
ed4e54
+            crm_xml_add(rsc_xml, XML_ATTR_ID, action->rsc->clone_name);
ed4e54
+            crm_xml_add(rsc_xml, XML_ATTR_ID_LONG, action->rsc->id);
ed4e54
 
ed4e54
-            } else if (is_not_set(action->rsc->flags, pe_rsc_unique)) {
ed4e54
-                const char *xml_id = ID(action->rsc->xml);
ed4e54
-
ed4e54
-                crm_debug("Using anonymous clone name %s for %s (aka. %s)", xml_id, action->rsc->id,
ed4e54
-                          action->rsc->clone_name);
ed4e54
-
ed4e54
-                /* ID is what we'd like client to use
ed4e54
-                 * ID_LONG is what they might know it as instead
ed4e54
-                 *
ed4e54
-                 * ID_LONG is only strictly needed /here/ during the
ed4e54
-                 * transition period until all nodes in the cluster
ed4e54
-                 * are running the new software /and/ have rebooted
ed4e54
-                 * once (meaning that they've only ever spoken to a DC
ed4e54
-                 * supporting this feature).
ed4e54
-                 *
ed4e54
-                 * If anyone toggles the unique flag to 'on', the
ed4e54
-                 * 'instance free' name will correspond to an orphan
ed4e54
-                 * and fall into the clause above instead
ed4e54
-                 */
ed4e54
-                crm_xml_add(rsc_xml, XML_ATTR_ID, xml_id);
ed4e54
-                if (action->rsc->clone_name && safe_str_neq(xml_id, action->rsc->clone_name)) {
ed4e54
-                    crm_xml_add(rsc_xml, XML_ATTR_ID_LONG, action->rsc->clone_name);
ed4e54
-                } else {
ed4e54
-                    crm_xml_add(rsc_xml, XML_ATTR_ID_LONG, action->rsc->id);
ed4e54
-                }
ed4e54
+        } else if (is_not_set(action->rsc->flags, pe_rsc_unique)) {
ed4e54
+            const char *xml_id = ID(action->rsc->xml);
ed4e54
+
ed4e54
+            crm_debug("Using anonymous clone name %s for %s (aka. %s)", xml_id, action->rsc->id,
ed4e54
+                      action->rsc->clone_name);
ed4e54
 
ed4e54
+            /* ID is what we'd like client to use
ed4e54
+             * ID_LONG is what they might know it as instead
ed4e54
+             *
ed4e54
+             * ID_LONG is only strictly needed /here/ during the
ed4e54
+             * transition period until all nodes in the cluster
ed4e54
+             * are running the new software /and/ have rebooted
ed4e54
+             * once (meaning that they've only ever spoken to a DC
ed4e54
+             * supporting this feature).
ed4e54
+             *
ed4e54
+             * If anyone toggles the unique flag to 'on', the
ed4e54
+             * 'instance free' name will correspond to an orphan
ed4e54
+             * and fall into the clause above instead
ed4e54
+             */
ed4e54
+            crm_xml_add(rsc_xml, XML_ATTR_ID, xml_id);
ed4e54
+            if (action->rsc->clone_name && safe_str_neq(xml_id, action->rsc->clone_name)) {
ed4e54
+                crm_xml_add(rsc_xml, XML_ATTR_ID_LONG, action->rsc->clone_name);
ed4e54
             } else {
ed4e54
-                CRM_ASSERT(action->rsc->clone_name == NULL);
ed4e54
-                crm_xml_add(rsc_xml, XML_ATTR_ID, action->rsc->id);
ed4e54
+                crm_xml_add(rsc_xml, XML_ATTR_ID_LONG, action->rsc->id);
ed4e54
             }
ed4e54
 
ed4e54
-            for (lpc = 0; lpc < DIMOF(attr_list); lpc++) {
ed4e54
-                crm_xml_add(rsc_xml, attr_list[lpc],
ed4e54
-                            g_hash_table_lookup(action->rsc->meta, attr_list[lpc]));
ed4e54
-            }
ed4e54
+        } else {
ed4e54
+            CRM_ASSERT(action->rsc->clone_name == NULL);
ed4e54
+            crm_xml_add(rsc_xml, XML_ATTR_ID, action->rsc->id);
ed4e54
+        }
ed4e54
+
ed4e54
+        for (lpc = 0; lpc < DIMOF(attr_list); lpc++) {
ed4e54
+            crm_xml_add(rsc_xml, attr_list[lpc],
ed4e54
+                        g_hash_table_lookup(action->rsc->meta, attr_list[lpc]));
ed4e54
         }
ed4e54
     }
ed4e54
 
ed4e54
-- 
ed4e54
1.8.3.1
ed4e54