diff --git a/tools/crm_resource_ban.c b/tools/crm_resource_ban.c
index bf4f458162..0aca8e9757 100644
--- a/tools/crm_resource_ban.c
+++ b/tools/crm_resource_ban.c
@@ -1,465 +1,479 @@
 /*
  * Copyright 2004-2021 the Pacemaker project contributors
  *
  * The version control history for this file may have further details.
  *
  * This source code is licensed under the GNU General Public License version 2
  * or later (GPLv2+) WITHOUT ANY WARRANTY.
  */
 
 #include <crm_resource.h>
 
 #define XPATH_MAX 1024
 
 static char *
 parse_cli_lifetime(pcmk__output_t *out, const char *move_lifetime)
 {
     char *later_s = NULL;
     crm_time_t *now = NULL;
     crm_time_t *later = NULL;
     crm_time_t *duration = NULL;
 
     if (move_lifetime == NULL) {
         return NULL;
     }
 
     duration = crm_time_parse_duration(move_lifetime);
     if (duration == NULL) {
         out->err(out, "Invalid duration specified: %s\n"
                       "Please refer to https://en.wikipedia.org/wiki/ISO_8601#Durations "
                       "for examples of valid durations", move_lifetime);
         return NULL;
     }
 
     now = crm_time_new(NULL);
     later = crm_time_add(now, duration);
     if (later == NULL) {
         out->err(out, "Unable to add %s to current time\n"
                       "Please report to " PACKAGE_BUGREPORT " as possible bug",
                       move_lifetime);
         crm_time_free(now);
         crm_time_free(duration);
         return NULL;
     }
 
     crm_time_log(LOG_INFO, "now     ", now,
                  crm_time_log_date | crm_time_log_timeofday | crm_time_log_with_timezone);
     crm_time_log(LOG_INFO, "later   ", later,
                  crm_time_log_date | crm_time_log_timeofday | crm_time_log_with_timezone);
     crm_time_log(LOG_INFO, "duration", duration, crm_time_log_date | crm_time_log_timeofday);
     later_s = crm_time_as_string(later, crm_time_log_date | crm_time_log_timeofday | crm_time_log_with_timezone);
     out->info(out, "Migration will take effect until: %s", later_s);
 
     crm_time_free(duration);
     crm_time_free(later);
     crm_time_free(now);
     return later_s;
 }
 
+static const char *
+promoted_role_name(void)
+{
+    /* This is a judgment call for what string to use. @TODO Ideally we'd
+     * use the legacy string if the DC only supports that, and the new one
+     * otherwise. Basing it on --enable-compat-2.0 is a decent guess.
+     */
+#ifdef PCMK__COMPAT_2_0
+        return RSC_ROLE_PROMOTED_LEGACY_S;
+#else
+        return RSC_ROLE_PROMOTED_S;
+#endif
+}
+
 // \return Standard Pacemaker return code
 int
 cli_resource_ban(pcmk__output_t *out, const char *rsc_id, const char *host,
                  const char *move_lifetime, GList *allnodes, cib_t * cib_conn,
                  int cib_options, gboolean promoted_role_only)
 {
     char *later_s = NULL;
     int rc = pcmk_rc_ok;
     xmlNode *fragment = NULL;
     xmlNode *location = NULL;
 
     if(host == NULL) {
         GList *n = allnodes;
         for(; n && rc == pcmk_rc_ok; n = n->next) {
             pe_node_t *target = n->data;
 
             rc = cli_resource_ban(out, rsc_id, target->details->uname, move_lifetime,
                                   NULL, cib_conn, cib_options, promoted_role_only);
         }
         return rc;
     }
 
     later_s = parse_cli_lifetime(out, move_lifetime);
     if(move_lifetime && later_s == NULL) {
         return EINVAL;
     }
 
     fragment = create_xml_node(NULL, XML_CIB_TAG_CONSTRAINTS);
 
     location = create_xml_node(fragment, XML_CONS_TAG_RSC_LOCATION);
     crm_xml_set_id(location, "cli-ban-%s-on-%s", rsc_id, host);
 
     out->info(out, "WARNING: Creating rsc_location constraint '%s' with a "
                    "score of -INFINITY for resource %s on %s.\n\tThis will "
                    "prevent %s from %s on %s until the constraint is removed "
                    "using the clear option or by editing the CIB with an "
                    "appropriate tool\n\tThis will be the case even if %s "
                    "is the last node in the cluster",
                    ID(location), rsc_id, host, rsc_id,
                    (promoted_role_only? "being promoted" : "running"),
                    host, host);
 
     crm_xml_add(location, XML_LOC_ATTR_SOURCE, rsc_id);
     if(promoted_role_only) {
-        crm_xml_add(location, XML_RULE_ATTR_ROLE, RSC_ROLE_PROMOTED_LEGACY_S);
+        crm_xml_add(location, XML_RULE_ATTR_ROLE, promoted_role_name());
     } else {
         crm_xml_add(location, XML_RULE_ATTR_ROLE, RSC_ROLE_STARTED_S);
     }
 
     if (later_s == NULL) {
         /* Short form */
         crm_xml_add(location, XML_CIB_TAG_NODE, host);
         crm_xml_add(location, XML_RULE_ATTR_SCORE, CRM_MINUS_INFINITY_S);
 
     } else {
         xmlNode *rule = create_xml_node(location, XML_TAG_RULE);
         xmlNode *expr = create_xml_node(rule, XML_TAG_EXPRESSION);
 
         crm_xml_set_id(rule, "cli-ban-%s-on-%s-rule", rsc_id, host);
         crm_xml_add(rule, XML_RULE_ATTR_SCORE, CRM_MINUS_INFINITY_S);
         crm_xml_add(rule, XML_RULE_ATTR_BOOLEAN_OP, "and");
 
         crm_xml_set_id(expr, "cli-ban-%s-on-%s-expr", rsc_id, host);
         crm_xml_add(expr, XML_EXPR_ATTR_ATTRIBUTE, CRM_ATTR_UNAME);
         crm_xml_add(expr, XML_EXPR_ATTR_OPERATION, "eq");
         crm_xml_add(expr, XML_EXPR_ATTR_VALUE, host);
         crm_xml_add(expr, XML_EXPR_ATTR_TYPE, "string");
 
         expr = create_xml_node(rule, "date_expression");
         crm_xml_set_id(expr, "cli-ban-%s-on-%s-lifetime", rsc_id, host);
         crm_xml_add(expr, "operation", "lt");
         crm_xml_add(expr, "end", later_s);
     }
 
     crm_log_xml_notice(fragment, "Modify");
     rc = cib_conn->cmds->update(cib_conn, XML_CIB_TAG_CONSTRAINTS, fragment, cib_options);
     rc = pcmk_legacy2rc(rc);
 
     free_xml(fragment);
     free(later_s);
     return rc;
 }
 
 // \return Standard Pacemaker return code
 int
 cli_resource_prefer(pcmk__output_t *out,const char *rsc_id, const char *host,
                     const char *move_lifetime, cib_t * cib_conn, int cib_options,
                     gboolean promoted_role_only)
 {
     char *later_s = parse_cli_lifetime(out, move_lifetime);
     int rc = pcmk_rc_ok;
     xmlNode *location = NULL;
     xmlNode *fragment = NULL;
 
     if(move_lifetime && later_s == NULL) {
         return EINVAL;
     }
 
     if(cib_conn == NULL) {
         free(later_s);
         return ENOTCONN;
     }
 
     fragment = create_xml_node(NULL, XML_CIB_TAG_CONSTRAINTS);
 
     location = create_xml_node(fragment, XML_CONS_TAG_RSC_LOCATION);
     crm_xml_set_id(location, "cli-prefer-%s", rsc_id);
 
     crm_xml_add(location, XML_LOC_ATTR_SOURCE, rsc_id);
     if(promoted_role_only) {
-        crm_xml_add(location, XML_RULE_ATTR_ROLE, RSC_ROLE_PROMOTED_LEGACY_S);
+        crm_xml_add(location, XML_RULE_ATTR_ROLE, promoted_role_name());
     } else {
         crm_xml_add(location, XML_RULE_ATTR_ROLE, RSC_ROLE_STARTED_S);
     }
 
     if (later_s == NULL) {
         /* Short form */
         crm_xml_add(location, XML_CIB_TAG_NODE, host);
         crm_xml_add(location, XML_RULE_ATTR_SCORE, CRM_INFINITY_S);
 
     } else {
         xmlNode *rule = create_xml_node(location, XML_TAG_RULE);
         xmlNode *expr = create_xml_node(rule, XML_TAG_EXPRESSION);
 
         crm_xml_set_id(rule, "cli-prefer-rule-%s", rsc_id);
         crm_xml_add(rule, XML_RULE_ATTR_SCORE, CRM_INFINITY_S);
         crm_xml_add(rule, XML_RULE_ATTR_BOOLEAN_OP, "and");
 
         crm_xml_set_id(expr, "cli-prefer-expr-%s", rsc_id);
         crm_xml_add(expr, XML_EXPR_ATTR_ATTRIBUTE, CRM_ATTR_UNAME);
         crm_xml_add(expr, XML_EXPR_ATTR_OPERATION, "eq");
         crm_xml_add(expr, XML_EXPR_ATTR_VALUE, host);
         crm_xml_add(expr, XML_EXPR_ATTR_TYPE, "string");
 
         expr = create_xml_node(rule, "date_expression");
         crm_xml_set_id(expr, "cli-prefer-lifetime-end-%s", rsc_id);
         crm_xml_add(expr, "operation", "lt");
         crm_xml_add(expr, "end", later_s);
     }
 
     crm_log_xml_info(fragment, "Modify");
     rc = cib_conn->cmds->update(cib_conn, XML_CIB_TAG_CONSTRAINTS, fragment, cib_options);
     rc = pcmk_legacy2rc(rc);
 
     free_xml(fragment);
     free(later_s);
     return rc;
 }
 
 /* Nodes can be specified two different ways in the CIB, so we have two different
  * functions to try clearing out any constraints on them:
  *
  * (1) The node could be given by attribute=/value= in an expression XML node.
  * That's what resource_clear_node_in_expr handles.  That XML looks like this:
  *
  * <rsc_location id="cli-prefer-dummy" rsc="dummy" role="Started">
  *   <rule id="cli-prefer-rule-dummy" score="INFINITY" boolean-op="and">
  *     <expression id="cli-prefer-expr-dummy" attribute="#uname" operation="eq" value="test02" type="string"/>
  *     <date_expression id="cli-prefer-lifetime-end-dummy" operation="lt" end="2018-12-12 14:05:37 -05:00"/>
  *   </rule>
  * </rsc_location>
  *
  * (2) The mode could be given by node= in an rsc_location XML node.  That's
  * what resource_clear_node_in_location handles.  That XML looks like this:
  *
  * <rsc_location id="cli-prefer-dummy" rsc="dummy" role="Started" node="node1" score="INFINITY"/>
  *
  * \return Standard Pacemaker return code
  */
 static int
 resource_clear_node_in_expr(const char *rsc_id, const char *host, cib_t * cib_conn,
                             int cib_options)
 {
     int rc = pcmk_rc_ok;
     char *xpath_string = NULL;
 
     xpath_string = crm_strdup_printf("//rsc_location[@id='cli-prefer-%s'][rule[@id='cli-prefer-rule-%s']/expression[@attribute='#uname' and @value='%s']]",
                                      rsc_id, rsc_id, host);
 
     rc = cib_conn->cmds->remove(cib_conn, xpath_string, NULL, cib_xpath | cib_options);
     if (rc == -ENXIO) {
         rc = pcmk_rc_ok;
     } else {
         rc = pcmk_legacy2rc(rc);
     }
 
     free(xpath_string);
     return rc;
 }
 
 // \return Standard Pacemaker return code
 static int
 resource_clear_node_in_location(const char *rsc_id, const char *host, cib_t * cib_conn,
                                 int cib_options, bool clear_ban_constraints, gboolean force)
 {
     int rc = pcmk_rc_ok;
     xmlNode *fragment = NULL;
     xmlNode *location = NULL;
 
     fragment = create_xml_node(NULL, XML_CIB_TAG_CONSTRAINTS);
 
     if (clear_ban_constraints == TRUE) {
         location = create_xml_node(fragment, XML_CONS_TAG_RSC_LOCATION);
         crm_xml_set_id(location, "cli-ban-%s-on-%s", rsc_id, host);
     }
 
     location = create_xml_node(fragment, XML_CONS_TAG_RSC_LOCATION);
     crm_xml_set_id(location, "cli-prefer-%s", rsc_id);
     if (force == FALSE) {
         crm_xml_add(location, XML_CIB_TAG_NODE, host);
     }
 
     crm_log_xml_info(fragment, "Delete");
     rc = cib_conn->cmds->remove(cib_conn, XML_CIB_TAG_CONSTRAINTS, fragment, cib_options);
     if (rc == -ENXIO) {
         rc = pcmk_rc_ok;
     } else {
         rc = pcmk_legacy2rc(rc);
     }
 
     free(fragment);
     return rc;
 }
 
 // \return Standard Pacemaker return code
 int
 cli_resource_clear(const char *rsc_id, const char *host, GList *allnodes, cib_t * cib_conn,
                    int cib_options, bool clear_ban_constraints, gboolean force)
 {
     int rc = pcmk_rc_ok;
 
     if(cib_conn == NULL) {
         return ENOTCONN;
     }
 
     if (host) {
         rc = resource_clear_node_in_expr(rsc_id, host, cib_conn, cib_options);
 
         /* rc does not tell us whether the previous operation did anything, only
          * whether it failed or not.  Thus, as long as it did not fail, we need
          * to try the second clear method.
          */
         if (rc == pcmk_rc_ok) {
             rc = resource_clear_node_in_location(rsc_id, host, cib_conn,
                                                  cib_options, clear_ban_constraints,
                                                  force);
         }
 
     } else {
         GList *n = allnodes;
 
         /* Iterate over all nodes, attempting to clear the constraint from each.
          * On the first error, abort.
          */
         for(; n; n = n->next) {
             pe_node_t *target = n->data;
 
             rc = cli_resource_clear(rsc_id, target->details->uname, NULL,
                                     cib_conn, cib_options, clear_ban_constraints,
                                     force);
             if (rc != pcmk_rc_ok) {
                 break;
             }
         }
     }
 
     return rc;
 }
 
 static char *
 build_clear_xpath_string(xmlNode *constraint_node, const char *rsc, const char *node, gboolean promoted_role_only)
 {
     int offset = 0;
     char *xpath_string = NULL;
     char *first_half = NULL;
     char *rsc_role_substr = NULL;
     char *date_substr = NULL;
 
     if (pcmk__starts_with(ID(constraint_node), "cli-ban-")) {
         date_substr = crm_strdup_printf("//date_expression[@id='%s-lifetime']",
                                         ID(constraint_node));
 
     } else if (pcmk__starts_with(ID(constraint_node), "cli-prefer-")) {
         date_substr = crm_strdup_printf("//date_expression[@id='cli-prefer-lifetime-end-%s']",
                                         crm_element_value(constraint_node, "rsc"));
     } else {
         return NULL;
     }
 
     first_half = calloc(1, XPATH_MAX);
     offset += snprintf(first_half + offset, XPATH_MAX - offset, "//rsc_location");
 
     if (node != NULL || rsc != NULL || promoted_role_only == TRUE) {
         offset += snprintf(first_half + offset, XPATH_MAX - offset, "[");
 
         if (node != NULL) {
             if (rsc != NULL || promoted_role_only == TRUE) {
                 offset += snprintf(first_half + offset, XPATH_MAX - offset, "@node='%s' and ", node);
             } else {
                 offset += snprintf(first_half + offset, XPATH_MAX - offset, "@node='%s'", node);
             }
         }
 
         if (rsc != NULL && promoted_role_only == TRUE) {
             rsc_role_substr = crm_strdup_printf("@rsc='%s' and @role='%s'",
-                                                rsc, RSC_ROLE_PROMOTED_LEGACY_S);
+                                                rsc, promoted_role_name());
             offset += snprintf(first_half + offset, XPATH_MAX - offset,
                                "@rsc='%s' and @role='%s']",
-                               rsc, RSC_ROLE_PROMOTED_LEGACY_S);
+                               rsc, promoted_role_name());
         } else if (rsc != NULL) {
             rsc_role_substr = crm_strdup_printf("@rsc='%s'", rsc);
             offset += snprintf(first_half + offset, XPATH_MAX - offset, "@rsc='%s']", rsc);
         } else if (promoted_role_only == TRUE) {
             rsc_role_substr = crm_strdup_printf("@role='%s'",
-                                                RSC_ROLE_PROMOTED_LEGACY_S);
+                                                promoted_role_name());
             offset += snprintf(first_half + offset, XPATH_MAX - offset,
-                               "@role='%s']", RSC_ROLE_PROMOTED_LEGACY_S);
+                               "@role='%s']", promoted_role_name());
         } else {
             offset += snprintf(first_half + offset, XPATH_MAX - offset, "]");
         }
     }
 
     if (node != NULL) {
         if (rsc_role_substr != NULL) {
             xpath_string = crm_strdup_printf("%s|//rsc_location[%s]/rule[expression[@attribute='#uname' and @value='%s']]%s",
                                              first_half, rsc_role_substr, node, date_substr);
         } else {
             xpath_string = crm_strdup_printf("%s|//rsc_location/rule[expression[@attribute='#uname' and @value='%s']]%s",
                                              first_half, node, date_substr);
         }
     } else {
         xpath_string = crm_strdup_printf("%s%s", first_half, date_substr);
     }
 
     free(first_half);
     free(date_substr);
     free(rsc_role_substr);
 
     return xpath_string;
 }
 
 // \return Standard Pacemaker return code
 int
 cli_resource_clear_all_expired(xmlNode *root, cib_t *cib_conn, int cib_options,
                                const char *rsc, const char *node, gboolean promoted_role_only)
 {
     xmlXPathObject *xpathObj = NULL;
     xmlNode *cib_constraints = NULL;
     crm_time_t *now = crm_time_new(NULL);
     int i;
     int rc = pcmk_rc_ok;
 
     cib_constraints = get_object_root(XML_CIB_TAG_CONSTRAINTS, root);
     xpathObj = xpath_search(cib_constraints, "//" XML_CONS_TAG_RSC_LOCATION);
 
     for (i = 0; i < numXpathResults(xpathObj); i++) {
         xmlNode *constraint_node = getXpathResult(xpathObj, i);
         xmlNode *date_expr_node = NULL;
         crm_time_t *end = NULL;
         char *xpath_string = NULL;
 
         xpath_string = build_clear_xpath_string(constraint_node, rsc, node, promoted_role_only);
         if (xpath_string == NULL) {
             continue;
         }
 
         date_expr_node = get_xpath_object(xpath_string, constraint_node, LOG_DEBUG);
         if (date_expr_node == NULL) {
             free(xpath_string);
             continue;
         }
 
         /* And then finally, see if the date expression is expired.  If so,
          * clear the constraint.
          */
         end = crm_time_new(crm_element_value(date_expr_node, "end"));
 
         if (crm_time_compare(now, end) == 1) {
             xmlNode *fragment = NULL;
             xmlNode *location = NULL;
 
             fragment = create_xml_node(NULL, XML_CIB_TAG_CONSTRAINTS);
             location = create_xml_node(fragment, XML_CONS_TAG_RSC_LOCATION);
             crm_xml_set_id(location, "%s", ID(constraint_node));
             crm_log_xml_info(fragment, "Delete");
 
             rc = cib_conn->cmds->remove(cib_conn, XML_CIB_TAG_CONSTRAINTS,
                                         fragment, cib_options);
             rc = pcmk_legacy2rc(rc);
 
             if (rc != pcmk_rc_ok) {
                 free(xpath_string);
                 goto done;
             }
 
             free_xml(fragment);
         }
 
         crm_time_free(end);
         free(xpath_string);
     }
 
 done:
     freeXpathObject(xpathObj);
     crm_time_free(now);
     return rc;
 }