diff --git a/include/crm/crm.h b/include/crm/crm.h
index 282bb065c3..05ec555f49 100644
--- a/include/crm/crm.h
+++ b/include/crm/crm.h
@@ -1,230 +1,231 @@
 /*
  * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation; either
  * version 2 of the License, or (at your option) any later version.
  *
  * This software is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 #ifndef CRM__H
 #  define CRM__H
 
 /**
  * \file
  * \brief A dumping ground
  * \ingroup core
  */
 
 #  include <crm_config.h>
 #  include <stdlib.h>
 #  include <glib.h>
 #  include <stdbool.h>
 
 #  include <string.h>
 
 #  include <libxml/tree.h>
 
 #  define CRM_FEATURE_SET		"3.0.14"
 
 #  define EOS		'\0'
 #  define DIMOF(a)	((int) (sizeof(a)/sizeof(a[0])) )
 
 #  ifndef MAX_NAME
 #    define MAX_NAME	256
 #  endif
 
 #  ifndef __GNUC__
 #    define __builtin_expect(expr, result) (expr)
 #  endif
 
 /* Some handy macros used by the Linux kernel */
 #  define __likely(expr) __builtin_expect(expr, 1)
 #  define __unlikely(expr) __builtin_expect(expr, 0)
 
 #  define CRM_META			"CRM_meta"
 
 extern char *crm_system_name;
 
 /* *INDENT-OFF* */
 
 /* Clean these up at some point, some probably should be runtime options */
 #  define SOCKET_LEN	1024
 #  define APPNAME_LEN	256
 #  define MAX_IPC_FAIL	5
 #  define MAX_IPC_DELAY   120
 
 #  define DAEMON_RESPAWN_STOP 100
 
 #  define MSG_LOG			1
 #  define DOT_FSA_ACTIONS		1
 #  define DOT_ALL_FSA_INPUTS	1
 /* #define FSA_TRACE		1 */
 
 /* This header defines INFINITY, but it might be defined elsewhere as well
  * (e.g. math.h), so undefine it first. This, of course, complicates any attempt
  * to use the other definition in any code that includes this header.
  *
  * @TODO: Rename our constant (which will break API backward compatibility).
  */
 #  undef INFINITY
 
 #  define INFINITY_S        "INFINITY"
 #  define MINUS_INFINITY_S "-INFINITY"
 
 #  define INFINITY        1000000
 
 /* Sub-systems */
 #  define CRM_SYSTEM_DC		"dc"
 #  define CRM_SYSTEM_DCIB		"dcib"
                                         /*  The master CIB */
 #  define CRM_SYSTEM_CIB		"cib"
 #  define CRM_SYSTEM_CRMD		"crmd"
 #  define CRM_SYSTEM_LRMD		"lrmd"
 #  define CRM_SYSTEM_PENGINE	"pengine"
 #  define CRM_SYSTEM_TENGINE	"tengine"
 #  define CRM_SYSTEM_STONITHD	"stonithd"
 #  define CRM_SYSTEM_MCP	"pacemakerd"
 
 // Names of internally generated node attributes
 #  define CRM_ATTR_UNAME            "#uname"
 #  define CRM_ATTR_ID               "#id"
 #  define CRM_ATTR_KIND             "#kind"
 #  define CRM_ATTR_ROLE             "#role"
 #  define CRM_ATTR_IS_DC            "#is_dc"
 #  define CRM_ATTR_CLUSTER_NAME     "#cluster-name"
 #  define CRM_ATTR_SITE_NAME        "#site-name"
 #  define CRM_ATTR_UNFENCED         "#node-unfenced"
 #  define CRM_ATTR_DIGESTS_ALL      "#digests-all"
 #  define CRM_ATTR_DIGESTS_SECURE   "#digests-secure"
+#  define CRM_ATTR_RA_VERSION       "#ra-version"
 
 /* Valid operations */
 #  define CRM_OP_NOOP		"noop"
 #  define CRM_OP_JOIN_ANNOUNCE	"join_announce"
 #  define CRM_OP_JOIN_OFFER	"join_offer"
 #  define CRM_OP_JOIN_REQUEST	"join_request"
 #  define CRM_OP_JOIN_ACKNAK	"join_ack_nack"
 #  define CRM_OP_JOIN_CONFIRM	"join_confirm"
 #  define CRM_OP_PING		"ping"
 #  define CRM_OP_THROTTLE	"throttle"
 #  define CRM_OP_VOTE		"vote"
 #  define CRM_OP_NOVOTE		"no-vote"
 #  define CRM_OP_HELLO		"hello"
 #  define CRM_OP_PECALC		"pe_calc"
 #  define CRM_OP_QUIT		"quit"
 #  define CRM_OP_LOCAL_SHUTDOWN 	"start_shutdown"
 #  define CRM_OP_SHUTDOWN_REQ	"req_shutdown"
 #  define CRM_OP_SHUTDOWN 	"do_shutdown"
 #  define CRM_OP_FENCE	 	"stonith"
 #  define CRM_OP_REGISTER		"register"
 #  define CRM_OP_IPC_FWD		"ipc_fwd"
 #  define CRM_OP_INVOKE_LRM	"lrm_invoke"
 #  define CRM_OP_LRM_REFRESH	"lrm_refresh" /* Deprecated */
 #  define CRM_OP_LRM_QUERY	"lrm_query"
 #  define CRM_OP_LRM_DELETE	"lrm_delete"
 #  define CRM_OP_LRM_FAIL		"lrm_fail"
 #  define CRM_OP_PROBED		"probe_complete"
 #  define CRM_OP_REPROBE		"probe_again"
 #  define CRM_OP_CLEAR_FAILCOUNT  "clear_failcount"
 #  define CRM_OP_REMOTE_STATE     "remote_state"
 #  define CRM_OP_RELAXED_SET  "one-or-more"
 #  define CRM_OP_RELAXED_CLONE  "clone-one-or-more"
 #  define CRM_OP_RM_NODE_CACHE "rm_node_cache"
 #  define CRM_OP_MAINTENANCE_NODES "maintenance_nodes"
 
 /* @COMPAT: These symbols are deprecated and not used by Pacemaker,
  * but they are kept for public API backward compatibility.
  */
 #  define CRM_OP_DIE		    "die_no_respawn"
 #  define CRM_OP_RETRIVE_CIB	"retrieve_cib"
 #  define CRM_OP_HBEAT		    "dc_beat"
 #  define CRM_OP_ABORT		    "abort"
 #  define CRM_OP_EVENTCC		"event_cc"
 #  define CRM_OP_TEABORT		"te_abort"
 #  define CRM_OP_TEABORTED	    "te_abort_confirmed"
 #  define CRM_OP_TE_HALT		"te_halt"
 #  define CRM_OP_TECOMPLETE	    "te_complete"
 #  define CRM_OP_TETIMEOUT	    "te_timeout"
 #  define CRM_OP_TRANSITION	    "transition"
 #  define CRM_OP_DEBUG_UP		"debug_inc" /* unused since 1.1.8 */
 #  define CRM_OP_DEBUG_DOWN     "debug_dec" /* unused since 1.1.8 */
 #  define CRM_OP_NODES_PROBED	"probe_nodes_complete"
 
 /* Possible cluster membership states */
 #  define CRMD_JOINSTATE_DOWN           "down"
 #  define CRMD_JOINSTATE_PENDING        "pending"
 #  define CRMD_JOINSTATE_MEMBER         "member"
 #  define CRMD_JOINSTATE_NACK           "banned"
 
 #  define CRMD_ACTION_DELETE		"delete"
 #  define CRMD_ACTION_CANCEL		"cancel"
 
 #  define CRMD_ACTION_RELOAD		"reload"
 #  define CRMD_ACTION_MIGRATE		"migrate_to"
 #  define CRMD_ACTION_MIGRATED		"migrate_from"
 
 #  define CRMD_ACTION_START		"start"
 #  define CRMD_ACTION_STARTED		"running"
 
 #  define CRMD_ACTION_STOP		"stop"
 #  define CRMD_ACTION_STOPPED		"stopped"
 
 #  define CRMD_ACTION_PROMOTE		"promote"
 #  define CRMD_ACTION_PROMOTED		"promoted"
 #  define CRMD_ACTION_DEMOTE		"demote"
 #  define CRMD_ACTION_DEMOTED		"demoted"
 
 #  define CRMD_ACTION_NOTIFY		"notify"
 #  define CRMD_ACTION_NOTIFIED		"notified"
 
 #  define CRMD_ACTION_STATUS		"monitor"
 #  define CRMD_ACTION_METADATA		"meta-data"
 #  define CRMD_METADATA_CALL_TIMEOUT   30000
 
 /* short names */
 #  define RSC_DELETE	CRMD_ACTION_DELETE
 #  define RSC_CANCEL	CRMD_ACTION_CANCEL
 
 #  define RSC_MIGRATE	CRMD_ACTION_MIGRATE
 #  define RSC_MIGRATED	CRMD_ACTION_MIGRATED
 
 #  define RSC_START	CRMD_ACTION_START
 #  define RSC_STARTED	CRMD_ACTION_STARTED
 
 #  define RSC_STOP	CRMD_ACTION_STOP
 #  define RSC_STOPPED	CRMD_ACTION_STOPPED
 
 #  define RSC_PROMOTE	CRMD_ACTION_PROMOTE
 #  define RSC_PROMOTED	CRMD_ACTION_PROMOTED
 #  define RSC_DEMOTE	CRMD_ACTION_DEMOTE
 #  define RSC_DEMOTED	CRMD_ACTION_DEMOTED
 
 #  define RSC_NOTIFY	CRMD_ACTION_NOTIFY
 #  define RSC_NOTIFIED	CRMD_ACTION_NOTIFIED
 
 #  define RSC_STATUS	CRMD_ACTION_STATUS
 #  define RSC_METADATA	CRMD_ACTION_METADATA
 /* *INDENT-ON* */
 
 typedef GList *GListPtr;
 
 #  include <crm/common/logging.h>
 #  include <crm/common/util.h>
 #  include <crm/error.h>
 
 static inline const char *crm_action_str(const char *task, int interval) {
     if(safe_str_eq(task, RSC_STATUS) && !interval) {
         return "probe";
     }
     return task;
 }
 
 #endif
diff --git a/lib/pengine/rules.c b/lib/pengine/rules.c
index daa3cd58b7..117bcb7d6b 100644
--- a/lib/pengine/rules.c
+++ b/lib/pengine/rules.c
@@ -1,1011 +1,1014 @@
 /* 
  * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
  * 
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation; either
  * version 2.1 of the License, or (at your option) any later version.
  * 
  * This library is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * Lesser General Public License for more details.
  * 
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
 #include <crm_internal.h>
 #include <crm/crm.h>
 #include <crm/msg_xml.h>
 #include <crm/common/xml.h>
 
 #include <glib.h>
 
 #include <crm/pengine/rules.h>
 #include <crm/pengine/internal.h>
 
 #include <sys/types.h>
 #include <regex.h>
 #include <ctype.h>
 
 CRM_TRACE_INIT_DATA(pe_rules);
 
 crm_time_t *parse_xml_duration(crm_time_t * start, xmlNode * duration_spec);
 
 gboolean test_date_expression(xmlNode * time_expr, crm_time_t * now);
 gboolean cron_range_satisfied(crm_time_t * now, xmlNode * cron_spec);
 gboolean test_attr_expression(xmlNode * expr, GHashTable * hash, crm_time_t * now);
 gboolean pe_test_attr_expression_full(xmlNode * expr, GHashTable * hash, crm_time_t * now, pe_match_data_t * match_data);
 gboolean test_role_expression(xmlNode * expr, enum rsc_role_e role, crm_time_t * now);
 
 gboolean
 test_ruleset(xmlNode * ruleset, GHashTable * node_hash, crm_time_t * now)
 {
     gboolean ruleset_default = TRUE;
     xmlNode *rule = NULL;
 
     for (rule = __xml_first_child(ruleset); rule != NULL; rule = __xml_next_element(rule)) {
         if (crm_str_eq((const char *)rule->name, XML_TAG_RULE, TRUE)) {
             ruleset_default = FALSE;
             if (test_rule(rule, node_hash, RSC_ROLE_UNKNOWN, now)) {
                 return TRUE;
             }
         }
     }
 
     return ruleset_default;
 }
 
 gboolean
 test_rule(xmlNode * rule, GHashTable * node_hash, enum rsc_role_e role, crm_time_t * now)
 {
     return pe_test_rule_full(rule, node_hash, role, now, NULL);
 }
 
 gboolean
 pe_test_rule_re(xmlNode * rule, GHashTable * node_hash, enum rsc_role_e role, crm_time_t * now, pe_re_match_data_t * re_match_data)
 {
     pe_match_data_t match_data = {
                                     .re = re_match_data,
                                     .params = NULL,
                                     .meta = NULL,
                                  };
     return pe_test_rule_full(rule, node_hash, role, now, &match_data);
 }
 
 gboolean
 pe_test_rule_full(xmlNode * rule, GHashTable * node_hash, enum rsc_role_e role, crm_time_t * now, pe_match_data_t * match_data)
 {
     xmlNode *expr = NULL;
     gboolean test = TRUE;
     gboolean empty = TRUE;
     gboolean passed = TRUE;
     gboolean do_and = TRUE;
     const char *value = NULL;
 
     rule = expand_idref(rule, NULL);
     value = crm_element_value(rule, XML_RULE_ATTR_BOOLEAN_OP);
     if (safe_str_eq(value, "or")) {
         do_and = FALSE;
         passed = FALSE;
     }
 
     crm_trace("Testing rule %s", ID(rule));
     for (expr = __xml_first_child(rule); expr != NULL; expr = __xml_next_element(expr)) {
         test = pe_test_expression_full(expr, node_hash, role, now, match_data);
         empty = FALSE;
 
         if (test && do_and == FALSE) {
             crm_trace("Expression %s/%s passed", ID(rule), ID(expr));
             return TRUE;
 
         } else if (test == FALSE && do_and) {
             crm_trace("Expression %s/%s failed", ID(rule), ID(expr));
             return FALSE;
         }
     }
 
     if (empty) {
         crm_err("Invalid Rule %s: rules must contain at least one expression", ID(rule));
     }
 
     crm_trace("Rule %s %s", ID(rule), passed ? "passed" : "failed");
     return passed;
 }
 
 gboolean
 test_expression(xmlNode * expr, GHashTable * node_hash, enum rsc_role_e role, crm_time_t * now)
 {
     return pe_test_expression_full(expr, node_hash, role, now, NULL);
 }
 
 gboolean
 pe_test_expression_re(xmlNode * expr, GHashTable * node_hash, enum rsc_role_e role, crm_time_t * now, pe_re_match_data_t * re_match_data)
 {
     pe_match_data_t match_data = {
                                     .re = re_match_data,
                                     .params = NULL,
                                     .meta = NULL,
                                  };
     return pe_test_expression_full(expr, node_hash, role, now, &match_data);
 }
 
 gboolean
 pe_test_expression_full(xmlNode * expr, GHashTable * node_hash, enum rsc_role_e role, crm_time_t * now, pe_match_data_t * match_data)
 {
     gboolean accept = FALSE;
     const char *uname = NULL;
 
     switch (find_expression_type(expr)) {
         case nested_rule:
             accept = pe_test_rule_full(expr, node_hash, role, now, match_data);
             break;
         case attr_expr:
         case loc_expr:
             /* these expressions can never succeed if there is
              * no node to compare with
              */
             if (node_hash != NULL) {
                 accept = pe_test_attr_expression_full(expr, node_hash, now, match_data);
             }
             break;
 
         case time_expr:
             accept = test_date_expression(expr, now);
             break;
 
         case role_expr:
             accept = test_role_expression(expr, role, now);
             break;
 
         case version_expr:
-            if (node_hash &&
-                g_hash_table_lookup_extended(node_hash, "#ra-version", NULL, NULL)) {
+            if (node_hash && g_hash_table_lookup_extended(node_hash,
+                                                          CRM_ATTR_RA_VERSION,
+                                                          NULL, NULL)) {
                 accept = test_attr_expression(expr, node_hash, now);
             } else {
                 // we are going to test it when we have ra-version
                 accept = TRUE;
             }
             break;
 
         default:
             CRM_CHECK(FALSE /* bad type */ , return FALSE);
             accept = FALSE;
     }
     if (node_hash) {
         uname = g_hash_table_lookup(node_hash, CRM_ATTR_UNAME);
     }
 
     crm_trace("Expression %s %s on %s",
               ID(expr), accept ? "passed" : "failed", uname ? uname : "all nodes");
     return accept;
 }
 
 enum expression_type
 find_expression_type(xmlNode * expr)
 {
     const char *tag = NULL;
     const char *attr = NULL;
 
     attr = crm_element_value(expr, XML_EXPR_ATTR_ATTRIBUTE);
     tag = crm_element_name(expr);
 
     if (safe_str_eq(tag, "date_expression")) {
         return time_expr;
 
     } else if (safe_str_eq(tag, XML_TAG_RULE)) {
         return nested_rule;
 
     } else if (safe_str_neq(tag, "expression")) {
         return not_expr;
 
     } else if (safe_str_eq(attr, CRM_ATTR_UNAME)
                || safe_str_eq(attr, CRM_ATTR_KIND)
                || safe_str_eq(attr, CRM_ATTR_ID)) {
         return loc_expr;
 
     } else if (safe_str_eq(attr, CRM_ATTR_ROLE)) {
         return role_expr;
 
-    } else if (safe_str_eq(attr, "#ra-version")) {
+    } else if (safe_str_eq(attr, CRM_ATTR_RA_VERSION)) {
         return version_expr;
     }
 
     return attr_expr;
 }
 
 gboolean
 test_role_expression(xmlNode * expr, enum rsc_role_e role, crm_time_t * now)
 {
     gboolean accept = FALSE;
     const char *op = NULL;
     const char *value = NULL;
 
     if (role == RSC_ROLE_UNKNOWN) {
         return accept;
     }
 
     value = crm_element_value(expr, XML_EXPR_ATTR_VALUE);
     op = crm_element_value(expr, XML_EXPR_ATTR_OPERATION);
 
     if (safe_str_eq(op, "defined")) {
         if (role > RSC_ROLE_STARTED) {
             accept = TRUE;
         }
 
     } else if (safe_str_eq(op, "not_defined")) {
         if (role < RSC_ROLE_SLAVE && role > RSC_ROLE_UNKNOWN) {
             accept = TRUE;
         }
 
     } else if (safe_str_eq(op, "eq")) {
         if (text2role(value) == role) {
             accept = TRUE;
         }
 
     } else if (safe_str_eq(op, "ne")) {
         /* we will only test "ne" wtih master/slave roles style */
         if (role < RSC_ROLE_SLAVE && role > RSC_ROLE_UNKNOWN) {
             accept = FALSE;
 
         } else if (text2role(value) != role) {
             accept = TRUE;
         }
     }
     return accept;
 }
 
 gboolean
 test_attr_expression(xmlNode * expr, GHashTable * hash, crm_time_t * now)
 {
     return pe_test_attr_expression_full(expr, hash, now, NULL);
 }
 
 gboolean
 pe_test_attr_expression_full(xmlNode * expr, GHashTable * hash, crm_time_t * now, pe_match_data_t * match_data)
 {
     gboolean accept = FALSE;
     gboolean attr_allocated = FALSE;
     int cmp = 0;
     const char *h_val = NULL;
     GHashTable *table = NULL;
 
     const char *op = NULL;
     const char *type = NULL;
     const char *attr = NULL;
     const char *value = NULL;
     const char *value_source = NULL;
 
     attr = crm_element_value(expr, XML_EXPR_ATTR_ATTRIBUTE);
     op = crm_element_value(expr, XML_EXPR_ATTR_OPERATION);
     value = crm_element_value(expr, XML_EXPR_ATTR_VALUE);
     type = crm_element_value(expr, XML_EXPR_ATTR_TYPE);
     value_source = crm_element_value(expr, XML_EXPR_ATTR_VALUE_SOURCE);
 
     if (attr == NULL || op == NULL) {
         pe_err("Invalid attribute or operation in expression"
                " (\'%s\' \'%s\' \'%s\')", crm_str(attr), crm_str(op), crm_str(value));
         return FALSE;
     }
 
     if (match_data) {
         if (match_data->re) {
             char *resolved_attr = pe_expand_re_matches(attr, match_data->re);
 
             if (resolved_attr) {
                 attr = (const char *) resolved_attr;
                 attr_allocated = TRUE;
             }
         }
 
         if (safe_str_eq(value_source, "param")) {
             table = match_data->params;
         } else if (safe_str_eq(value_source, "meta")) {
             table = match_data->meta;
         }
     }
 
     if (table) {
         const char *param_name = value;
         const char *param_value = NULL;
 
         if (param_name && param_name[0]) {
             if ((param_value = (const char *)g_hash_table_lookup(table, param_name))) {
                 value = param_value;
             }
         }
     }
 
     if (hash != NULL) {
         h_val = (const char *)g_hash_table_lookup(hash, attr);
     }
 
     if (attr_allocated) {
         free((char *)attr);
         attr = NULL;
     }
 
     if (value != NULL && h_val != NULL) {
         if (type == NULL) {
             if (safe_str_eq(op, "lt")
                 || safe_str_eq(op, "lte")
                 || safe_str_eq(op, "gt")
                 || safe_str_eq(op, "gte")) {
                 type = "number";
 
             } else {
                 type = "string";
             }
             crm_trace("Defaulting to %s based comparison for '%s' op", type, op);
         }
 
         if (safe_str_eq(type, "string")) {
             cmp = strcasecmp(h_val, value);
 
         } else if (safe_str_eq(type, "number")) {
             int h_val_f = crm_parse_int(h_val, NULL);
             int value_f = crm_parse_int(value, NULL);
 
             if (h_val_f < value_f) {
                 cmp = -1;
             } else if (h_val_f > value_f) {
                 cmp = 1;
             } else {
                 cmp = 0;
             }
 
         } else if (safe_str_eq(type, "version")) {
             cmp = compare_version(h_val, value);
 
         }
 
     } else if (value == NULL && h_val == NULL) {
         cmp = 0;
     } else if (value == NULL) {
         cmp = 1;
     } else {
         cmp = -1;
     }
 
     if (safe_str_eq(op, "defined")) {
         if (h_val != NULL) {
             accept = TRUE;
         }
 
     } else if (safe_str_eq(op, "not_defined")) {
         if (h_val == NULL) {
             accept = TRUE;
         }
 
     } else if (safe_str_eq(op, "eq")) {
         if ((h_val == value) || cmp == 0) {
             accept = TRUE;
         }
 
     } else if (safe_str_eq(op, "ne")) {
         if ((h_val == NULL && value != NULL)
             || (h_val != NULL && value == NULL)
             || cmp != 0) {
             accept = TRUE;
         }
 
     } else if (value == NULL || h_val == NULL) {
         /* the comparision is meaningless from this point on */
         accept = FALSE;
 
     } else if (safe_str_eq(op, "lt")) {
         if (cmp < 0) {
             accept = TRUE;
         }
 
     } else if (safe_str_eq(op, "lte")) {
         if (cmp <= 0) {
             accept = TRUE;
         }
 
     } else if (safe_str_eq(op, "gt")) {
         if (cmp > 0) {
             accept = TRUE;
         }
 
     } else if (safe_str_eq(op, "gte")) {
         if (cmp >= 0) {
             accept = TRUE;
         }
     }
 
     return accept;
 }
 
 /* As per the nethack rules:
  *
  * moon period = 29.53058 days ~= 30, year = 365.2422 days
  * days moon phase advances on first day of year compared to preceding year
  *      = 365.2422 - 12*29.53058 ~= 11
  * years in Metonic cycle (time until same phases fall on the same days of
  *      the month) = 18.6 ~= 19
  * moon phase on first day of year (epact) ~= (11*(year%19) + 29) % 30
  *      (29 as initial condition)
  * current phase in days = first day phase + days elapsed in year
  * 6 moons ~= 177 days
  * 177 ~= 8 reported phases * 22
  * + 11/22 for rounding
  *
  * 0-7, with 0: new, 4: full
  */
 
 static int
 phase_of_the_moon(crm_time_t * now)
 {
     uint32_t epact, diy, goldn;
     uint32_t y;
 
     crm_time_get_ordinal(now, &y, &diy);
 
     goldn = (y % 19) + 1;
     epact = (11 * goldn + 18) % 30;
     if ((epact == 25 && goldn > 11) || epact == 24)
         epact++;
 
     return ((((((diy + epact) * 6) + 11) % 177) / 22) & 7);
 }
 
 static gboolean
 decodeNVpair(const char *srcstring, char separator, char **name, char **value)
 {
     int lpc = 0;
     int len = 0;
     const char *temp = NULL;
 
     CRM_ASSERT(name != NULL && value != NULL);
     *name = NULL;
     *value = NULL;
 
     crm_trace("Attempting to decode: [%s]", srcstring);
     if (srcstring != NULL) {
         len = strlen(srcstring);
         while (lpc <= len) {
             if (srcstring[lpc] == separator) {
                 *name = calloc(1, lpc + 1);
                 if (*name == NULL) {
                     break;      /* and return FALSE */
                 }
                 memcpy(*name, srcstring, lpc);
                 (*name)[lpc] = '\0';
 
 /* this sucks but as the strtok manpage says..
  * it *is* a bug
  */
                 len = len - lpc;
                 len--;
                 if (len <= 0) {
                     *value = NULL;
                 } else {
 
                     *value = calloc(1, len + 1);
                     if (*value == NULL) {
                         break;  /* and return FALSE */
                     }
                     temp = srcstring + lpc + 1;
                     memcpy(*value, temp, len);
                     (*value)[len] = '\0';
                 }
                 return TRUE;
             }
             lpc++;
         }
     }
 
     if (*name != NULL) {
         free(*name);
         *name = NULL;
     }
     *name = NULL;
     *value = NULL;
 
     return FALSE;
 }
 
 #define cron_check(xml_field, time_field)				\
     value = crm_element_value(cron_spec, xml_field);			\
     if(value != NULL) {							\
 	gboolean pass = TRUE;						\
 	decodeNVpair(value, '-', &value_low, &value_high);		\
 	if(value_low == NULL) {						\
 	    value_low = strdup(value);				\
 	}								\
 	value_low_i = crm_parse_int(value_low, "0");			\
 	value_high_i = crm_parse_int(value_high, "-1");			\
 	if(value_high_i < 0) {						\
 	    if(value_low_i != time_field) {				\
 		pass = FALSE;						\
 	    }								\
 	} else if(value_low_i > time_field) {				\
 	    pass = FALSE;						\
 	} else if(value_high_i < time_field) {				\
 	    pass = FALSE;						\
 	}								\
 	free(value_low);						\
 	free(value_high);						\
 	if(pass == FALSE) {						\
 	    crm_debug("Condition '%s' in %s: failed", value, xml_field); \
 	    return pass;						\
 	}								\
 	crm_debug("Condition '%s' in %s: passed", value, xml_field);	\
     }
 
 gboolean
 cron_range_satisfied(crm_time_t * now, xmlNode * cron_spec)
 {
     const char *value = NULL;
     char *value_low = NULL;
     char *value_high = NULL;
 
     int value_low_i = 0;
     int value_high_i = 0;
 
     uint32_t h, m, s, y, d, w;
 
     CRM_CHECK(now != NULL, return FALSE);
 
     crm_time_get_timeofday(now, &h, &m, &s);
 
     cron_check("seconds", s);
     cron_check("minutes", m);
     cron_check("hours", h);
 
     crm_time_get_gregorian(now, &y, &m, &d);
 
     cron_check("monthdays", d);
     cron_check("months", m);
     cron_check("years", y);
 
     crm_time_get_ordinal(now, &y, &d);
 
     cron_check("yeardays", d);
 
     crm_time_get_isoweek(now, &y, &w, &d);
 
     cron_check("weekyears", y);
     cron_check("weeks", w);
     cron_check("weekdays", d);
 
     cron_check("moon", phase_of_the_moon(now));
 
     return TRUE;
 }
 
 #define update_field(xml_field, time_fn)			\
     value = crm_element_value(duration_spec, xml_field);	\
     if(value != NULL) {						\
 	int value_i = crm_parse_int(value, "0");		\
 	time_fn(end, value_i);					\
     }
 
 crm_time_t *
 parse_xml_duration(crm_time_t * start, xmlNode * duration_spec)
 {
     crm_time_t *end = NULL;
     const char *value = NULL;
 
     end = crm_time_new(NULL);
     crm_time_set(end, start);
 
     update_field("years", crm_time_add_years);
     update_field("months", crm_time_add_months);
     update_field("weeks", crm_time_add_weeks);
     update_field("days", crm_time_add_days);
     update_field("hours", crm_time_add_hours);
     update_field("minutes", crm_time_add_minutes);
     update_field("seconds", crm_time_add_seconds);
 
     return end;
 }
 
 gboolean
 test_date_expression(xmlNode * time_expr, crm_time_t * now)
 {
     crm_time_t *start = NULL;
     crm_time_t *end = NULL;
     const char *value = NULL;
     const char *op = crm_element_value(time_expr, "operation");
 
     xmlNode *duration_spec = NULL;
     xmlNode *date_spec = NULL;
 
     gboolean passed = FALSE;
 
     crm_trace("Testing expression: %s", ID(time_expr));
 
     duration_spec = first_named_child(time_expr, "duration");
     date_spec = first_named_child(time_expr, "date_spec");
 
     value = crm_element_value(time_expr, "start");
     if (value != NULL) {
         start = crm_time_new(value);
     }
     value = crm_element_value(time_expr, "end");
     if (value != NULL) {
         end = crm_time_new(value);
     }
 
     if (start != NULL && end == NULL && duration_spec != NULL) {
         end = parse_xml_duration(start, duration_spec);
     }
     if (op == NULL) {
         op = "in_range";
     }
 
     if (safe_str_eq(op, "date_spec") || safe_str_eq(op, "in_range")) {
         if (start != NULL && crm_time_compare(start, now) > 0) {
             passed = FALSE;
         } else if (end != NULL && crm_time_compare(end, now) < 0) {
             passed = FALSE;
         } else if (safe_str_eq(op, "in_range")) {
             passed = TRUE;
         } else {
             passed = cron_range_satisfied(now, date_spec);
         }
 
     } else if (safe_str_eq(op, "gt") && crm_time_compare(start, now) < 0) {
         passed = TRUE;
 
     } else if (safe_str_eq(op, "lt") && crm_time_compare(end, now) > 0) {
         passed = TRUE;
 
     } else if (safe_str_eq(op, "eq") && crm_time_compare(start, now) == 0) {
         passed = TRUE;
 
     } else if (safe_str_eq(op, "neq") && crm_time_compare(start, now) != 0) {
         passed = TRUE;
     }
 
     crm_time_free(start);
     crm_time_free(end);
     return passed;
 }
 
 typedef struct sorted_set_s {
     int score;
     const char *name;
     const char *special_name;
     xmlNode *attr_set;
 } sorted_set_t;
 
 static gint
 sort_pairs(gconstpointer a, gconstpointer b)
 {
     const sorted_set_t *pair_a = a;
     const sorted_set_t *pair_b = b;
 
     if (a == NULL && b == NULL) {
         return 0;
     } else if (a == NULL) {
         return 1;
     } else if (b == NULL) {
         return -1;
     }
 
     if (safe_str_eq(pair_a->name, pair_a->special_name)) {
         return -1;
 
     } else if (safe_str_eq(pair_b->name, pair_a->special_name)) {
         return 1;
     }
 
     if (pair_a->score < pair_b->score) {
         return 1;
     } else if (pair_a->score > pair_b->score) {
         return -1;
     }
     return 0;
 }
 
 static void
 populate_hash(xmlNode * nvpair_list, GHashTable * hash, gboolean overwrite, xmlNode * top)
 {
     const char *name = NULL;
     const char *value = NULL;
     const char *old_value = NULL;
     xmlNode *list = nvpair_list;
     xmlNode *an_attr = NULL;
 
     name = crm_element_name(list->children);
     if (safe_str_eq(XML_TAG_ATTRS, name)) {
         list = list->children;
     }
 
     for (an_attr = __xml_first_child(list); an_attr != NULL; an_attr = __xml_next_element(an_attr)) {
         if (crm_str_eq((const char *)an_attr->name, XML_CIB_TAG_NVPAIR, TRUE)) {
             xmlNode *ref_nvpair = expand_idref(an_attr, top);
 
             name = crm_element_value(an_attr, XML_NVPAIR_ATTR_NAME);
             if (name == NULL) {
                 name = crm_element_value(ref_nvpair, XML_NVPAIR_ATTR_NAME);
             }
 
             crm_trace("Setting attribute: %s", name);
             value = crm_element_value(an_attr, XML_NVPAIR_ATTR_VALUE);
             if (value == NULL) {
                 value = crm_element_value(ref_nvpair, XML_NVPAIR_ATTR_VALUE);
             }
 
             if (name == NULL || value == NULL) {
                 continue;
 
             }
 
             old_value = g_hash_table_lookup(hash, name);
 
             if (safe_str_eq(value, "#default")) {
                 if (old_value) {
                     crm_trace("Removing value for %s (%s)", name, value);
                     g_hash_table_remove(hash, name);
                 }
                 continue;
 
             } else if (old_value == NULL) {
                 g_hash_table_insert(hash, strdup(name), strdup(value));
 
             } else if (overwrite) {
                 crm_debug("Overwriting value of %s: %s -> %s", name, old_value, value);
                 g_hash_table_replace(hash, strdup(name), strdup(value));
             }
         }
     }
 }
 
 static xmlNode*
 get_versioned_rule(xmlNode * attr_set)
 {
     xmlNode * rule = NULL;
     xmlNode * expr = NULL;
 
     for (rule = __xml_first_child(attr_set); rule != NULL; rule = __xml_next_element(rule)) {
         if (crm_str_eq((const char *)rule->name, XML_TAG_RULE, TRUE)) {
             for (expr = __xml_first_child(rule); expr != NULL; expr = __xml_next_element(expr)) {
                 if (find_expression_type(expr) == version_expr) {
                     return rule;
                 }
             }
         }
     }
 
     return NULL;
 }
 
 static void
 add_versioned_attributes(xmlNode * attr_set, xmlNode * versioned_attrs)
 {
     xmlNode *attr_set_copy = NULL;
     xmlNode *rule = NULL;
     xmlNode *expr = NULL;
 
     if (!attr_set || !versioned_attrs) {
         return;
     }
 
     attr_set_copy = copy_xml(attr_set);
 
     rule = get_versioned_rule(attr_set_copy);
     if (!rule) {
         free_xml(attr_set_copy);
         return;
     }
 
     expr = __xml_first_child(rule);
     while (expr != NULL) {
         if (find_expression_type(expr) != version_expr) {
             xmlNode *node = expr;
 
             expr = __xml_next_element(expr);
             free_xml(node);
         } else {
             expr = __xml_next_element(expr);
         }
     }
 
     add_node_nocopy(versioned_attrs, NULL, attr_set_copy);
 }
 
 typedef struct unpack_data_s {
     gboolean overwrite;
     GHashTable *node_hash;
     void *hash;
     crm_time_t *now;
     xmlNode *top;
 } unpack_data_t;
 
 static void
 unpack_attr_set(gpointer data, gpointer user_data)
 {
     sorted_set_t *pair = data;
     unpack_data_t *unpack_data = user_data;
 
     if (test_ruleset(pair->attr_set, unpack_data->node_hash, unpack_data->now) == FALSE) {
         return;
     }
 
     if (get_versioned_rule(pair->attr_set) && !(unpack_data->node_hash &&
-        g_hash_table_lookup_extended(unpack_data->node_hash, "#ra-version", NULL, NULL))) {
+        g_hash_table_lookup_extended(unpack_data->node_hash,
+                                     CRM_ATTR_RA_VERSION, NULL, NULL))) {
         // we haven't actually tested versioned expressions yet
         return;
     }
 
     crm_trace("Adding attributes from %s", pair->name);
     populate_hash(pair->attr_set, unpack_data->hash, unpack_data->overwrite, unpack_data->top);
 }
 
 static void
 unpack_versioned_attr_set(gpointer data, gpointer user_data)
 {
     sorted_set_t *pair = data;
     unpack_data_t *unpack_data = user_data;
 
     if (test_ruleset(pair->attr_set, unpack_data->node_hash, unpack_data->now) == FALSE) {
         return;
     }
 
     add_versioned_attributes(pair->attr_set, unpack_data->hash);
 }
 
 static GListPtr
 make_pairs_and_populate_data(xmlNode * top, xmlNode * xml_obj, const char *set_name,
                              GHashTable * node_hash, void * hash, const char *always_first,
                              gboolean overwrite, crm_time_t * now, unpack_data_t * data)
 {
     GListPtr unsorted = NULL;
     const char *score = NULL;
     sorted_set_t *pair = NULL;
     xmlNode *attr_set = NULL;
 
     if (xml_obj == NULL) {
         crm_trace("No instance attributes");
         return NULL;
     }
 
     crm_trace("Checking for attributes");
     for (attr_set = __xml_first_child(xml_obj); attr_set != NULL; attr_set = __xml_next_element(attr_set)) {
         /* Uncertain if set_name == NULL check is strictly necessary here */
         if (set_name == NULL || crm_str_eq((const char *)attr_set->name, set_name, TRUE)) {
             pair = NULL;
             attr_set = expand_idref(attr_set, top);
             if (attr_set == NULL) {
                 continue;
             }
 
             pair = calloc(1, sizeof(sorted_set_t));
             pair->name = ID(attr_set);
             pair->special_name = always_first;
             pair->attr_set = attr_set;
 
             score = crm_element_value(attr_set, XML_RULE_ATTR_SCORE);
             pair->score = char2score(score);
 
             unsorted = g_list_prepend(unsorted, pair);
         }
     }
 
     if (pair != NULL) {
         data->hash = hash;
         data->node_hash = node_hash;
         data->now = now;
         data->overwrite = overwrite;
         data->top = top;
     }
 
     if (unsorted) {
         return g_list_sort(unsorted, sort_pairs);
     }
 
     return NULL;
 }
 
 void
 unpack_instance_attributes(xmlNode * top, xmlNode * xml_obj, const char *set_name,
                            GHashTable * node_hash, GHashTable * hash, const char *always_first,
                            gboolean overwrite, crm_time_t * now)
 {
     unpack_data_t data;
     GListPtr pairs = make_pairs_and_populate_data(top, xml_obj, set_name, node_hash, hash,
                                                   always_first, overwrite, now, &data);
 
     if (pairs) {
         g_list_foreach(pairs, unpack_attr_set, &data);
         g_list_free_full(pairs, free);
     }
 }
 
 void
 pe_unpack_versioned_attributes(xmlNode * top, xmlNode * xml_obj, const char *set_name,
                                GHashTable * node_hash, xmlNode * hash, crm_time_t * now)
 {
     unpack_data_t data;
     GListPtr pairs = make_pairs_and_populate_data(top, xml_obj, set_name, node_hash, hash,
                                                   NULL, FALSE, now, &data);
 
     if (pairs) {
         g_list_foreach(pairs, unpack_versioned_attr_set, &data);
         g_list_free_full(pairs, free);
     }
 }
 
 char *
 pe_expand_re_matches(const char *string, pe_re_match_data_t *match_data)
 {
     size_t len = 0;
     int i;
     const char *p, *last_match_index;
     char *p_dst, *result = NULL;
 
     if (!string || string[0] == '\0' || !match_data) {
         return NULL;
     }
 
     p = last_match_index = string;
 
     while (*p) {
         if (*p == '%' && *(p + 1) && isdigit(*(p + 1))) {
             i = *(p + 1) - '0';
             if (match_data->nregs >= i && match_data->pmatch[i].rm_so != -1 &&
                 match_data->pmatch[i].rm_eo > match_data->pmatch[i].rm_so) {
                 len += p - last_match_index + (match_data->pmatch[i].rm_eo - match_data->pmatch[i].rm_so);
                 last_match_index = p + 2;
             }
             p++;
         }
         p++;
     }
     len += p - last_match_index + 1;
 
     /* FIXME: Excessive? */
     if (len - 1 <= 0) {
         return NULL;
     }
 
     p_dst = result = calloc(1, len);
     p = string;
 
     while (*p) {
         if (*p == '%' && *(p + 1) && isdigit(*(p + 1))) {
             i = *(p + 1) - '0';
             if (match_data->nregs >= i && match_data->pmatch[i].rm_so != -1 &&
                 match_data->pmatch[i].rm_eo > match_data->pmatch[i].rm_so) {
                 /* rm_eo can be equal to rm_so, but then there is nothing to do */
                 int match_len = match_data->pmatch[i].rm_eo - match_data->pmatch[i].rm_so;
                 memcpy(p_dst, match_data->string + match_data->pmatch[i].rm_so, match_len);
                 p_dst += match_len;
             }
             p++;
         } else {
             *(p_dst) = *(p);
             p_dst++;
         }
         p++;
     }
 
     return result;
 }
 
 GHashTable*
 pe_unpack_versioned_parameters(xmlNode *versioned_params, const char *ra_version)
 {
     GHashTable *hash = crm_str_table_new();
 
     if (versioned_params && ra_version) {
         GHashTable *node_hash = crm_str_table_new();
         xmlNode *attr_set = __xml_first_child(versioned_params);
 
         if (attr_set) {
-            g_hash_table_insert(node_hash, strdup("#" XML_ATTR_RA_VERSION), strdup(ra_version));
+            g_hash_table_insert(node_hash, strdup(CRM_ATTR_RA_VERSION),
+                                strdup(ra_version));
             unpack_instance_attributes(NULL, versioned_params, crm_element_name(attr_set),
                                        node_hash, hash, NULL, FALSE, NULL);
         }
 
         g_hash_table_destroy(node_hash);
     }
 
     return hash;
 }