root/tools/crm_resource_ban.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. parse_cli_lifetime
  2. promoted_role_name
  3. cli_resource_ban
  4. cli_resource_prefer
  5. resource_clear_node_in_expr
  6. resource_clear_node_in_location
  7. cli_resource_clear
  8. build_clear_xpath_string
  9. cli_resource_clear_all_expired

   1 /*
   2  * Copyright 2004-2021 the Pacemaker project contributors
   3  *
   4  * The version control history for this file may have further details.
   5  *
   6  * This source code is licensed under the GNU General Public License version 2
   7  * or later (GPLv2+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #include <crm_internal.h>
  11 
  12 #include <crm_resource.h>
  13 
  14 #define XPATH_MAX 1024
  15 
  16 static char *
  17 parse_cli_lifetime(pcmk__output_t *out, const char *move_lifetime)
     /* [previous][next][first][last][top][bottom][index][help] */
  18 {
  19     char *later_s = NULL;
  20     crm_time_t *now = NULL;
  21     crm_time_t *later = NULL;
  22     crm_time_t *duration = NULL;
  23 
  24     if (move_lifetime == NULL) {
  25         return NULL;
  26     }
  27 
  28     duration = crm_time_parse_duration(move_lifetime);
  29     if (duration == NULL) {
  30         out->err(out, "Invalid duration specified: %s\n"
  31                       "Please refer to https://en.wikipedia.org/wiki/ISO_8601#Durations "
  32                       "for examples of valid durations", move_lifetime);
  33         return NULL;
  34     }
  35 
  36     now = crm_time_new(NULL);
  37     later = crm_time_add(now, duration);
  38     if (later == NULL) {
  39         out->err(out, "Unable to add %s to current time\n"
  40                       "Please report to " PACKAGE_BUGREPORT " as possible bug",
  41                       move_lifetime);
  42         crm_time_free(now);
  43         crm_time_free(duration);
  44         return NULL;
  45     }
  46 
  47     crm_time_log(LOG_INFO, "now     ", now,
  48                  crm_time_log_date | crm_time_log_timeofday | crm_time_log_with_timezone);
  49     crm_time_log(LOG_INFO, "later   ", later,
  50                  crm_time_log_date | crm_time_log_timeofday | crm_time_log_with_timezone);
  51     crm_time_log(LOG_INFO, "duration", duration, crm_time_log_date | crm_time_log_timeofday);
  52     later_s = crm_time_as_string(later, crm_time_log_date | crm_time_log_timeofday | crm_time_log_with_timezone);
  53     out->info(out, "Migration will take effect until: %s", later_s);
  54 
  55     crm_time_free(duration);
  56     crm_time_free(later);
  57     crm_time_free(now);
  58     return later_s;
  59 }
  60 
  61 static const char *
  62 promoted_role_name(void)
     /* [previous][next][first][last][top][bottom][index][help] */
  63 {
  64     /* This is a judgment call for what string to use. @TODO Ideally we'd
  65      * use the legacy string if the DC only supports that, and the new one
  66      * otherwise. Basing it on --enable-compat-2.0 is a decent guess.
  67      */
  68 #ifdef PCMK__COMPAT_2_0
  69         return RSC_ROLE_PROMOTED_LEGACY_S;
  70 #else
  71         return RSC_ROLE_PROMOTED_S;
  72 #endif
  73 }
  74 
  75 // \return Standard Pacemaker return code
  76 int
  77 cli_resource_ban(pcmk__output_t *out, const char *rsc_id, const char *host,
     /* [previous][next][first][last][top][bottom][index][help] */
  78                  const char *move_lifetime, GList *allnodes, cib_t * cib_conn,
  79                  int cib_options, gboolean promoted_role_only)
  80 {
  81     char *later_s = NULL;
  82     int rc = pcmk_rc_ok;
  83     xmlNode *fragment = NULL;
  84     xmlNode *location = NULL;
  85 
  86     if(host == NULL) {
  87         GList *n = allnodes;
  88         for(; n && rc == pcmk_rc_ok; n = n->next) {
  89             pe_node_t *target = n->data;
  90 
  91             rc = cli_resource_ban(out, rsc_id, target->details->uname, move_lifetime,
  92                                   NULL, cib_conn, cib_options, promoted_role_only);
  93         }
  94         return rc;
  95     }
  96 
  97     later_s = parse_cli_lifetime(out, move_lifetime);
  98     if(move_lifetime && later_s == NULL) {
  99         return EINVAL;
 100     }
 101 
 102     fragment = create_xml_node(NULL, XML_CIB_TAG_CONSTRAINTS);
 103 
 104     location = create_xml_node(fragment, XML_CONS_TAG_RSC_LOCATION);
 105     crm_xml_set_id(location, "cli-ban-%s-on-%s", rsc_id, host);
 106 
 107     out->info(out, "WARNING: Creating rsc_location constraint '%s' with a "
 108                    "score of -INFINITY for resource %s on %s.\n\tThis will "
 109                    "prevent %s from %s on %s until the constraint is removed "
 110                    "using the clear option or by editing the CIB with an "
 111                    "appropriate tool\n\tThis will be the case even if %s "
 112                    "is the last node in the cluster",
 113                    ID(location), rsc_id, host, rsc_id,
 114                    (promoted_role_only? "being promoted" : "running"),
 115                    host, host);
 116 
 117     crm_xml_add(location, XML_LOC_ATTR_SOURCE, rsc_id);
 118     if(promoted_role_only) {
 119         crm_xml_add(location, XML_RULE_ATTR_ROLE, promoted_role_name());
 120     } else {
 121         crm_xml_add(location, XML_RULE_ATTR_ROLE, RSC_ROLE_STARTED_S);
 122     }
 123 
 124     if (later_s == NULL) {
 125         /* Short form */
 126         crm_xml_add(location, XML_CIB_TAG_NODE, host);
 127         crm_xml_add(location, XML_RULE_ATTR_SCORE, CRM_MINUS_INFINITY_S);
 128 
 129     } else {
 130         xmlNode *rule = create_xml_node(location, XML_TAG_RULE);
 131         xmlNode *expr = create_xml_node(rule, XML_TAG_EXPRESSION);
 132 
 133         crm_xml_set_id(rule, "cli-ban-%s-on-%s-rule", rsc_id, host);
 134         crm_xml_add(rule, XML_RULE_ATTR_SCORE, CRM_MINUS_INFINITY_S);
 135         crm_xml_add(rule, XML_RULE_ATTR_BOOLEAN_OP, "and");
 136 
 137         crm_xml_set_id(expr, "cli-ban-%s-on-%s-expr", rsc_id, host);
 138         crm_xml_add(expr, XML_EXPR_ATTR_ATTRIBUTE, CRM_ATTR_UNAME);
 139         crm_xml_add(expr, XML_EXPR_ATTR_OPERATION, "eq");
 140         crm_xml_add(expr, XML_EXPR_ATTR_VALUE, host);
 141         crm_xml_add(expr, XML_EXPR_ATTR_TYPE, "string");
 142 
 143         expr = create_xml_node(rule, "date_expression");
 144         crm_xml_set_id(expr, "cli-ban-%s-on-%s-lifetime", rsc_id, host);
 145         crm_xml_add(expr, "operation", "lt");
 146         crm_xml_add(expr, "end", later_s);
 147     }
 148 
 149     crm_log_xml_notice(fragment, "Modify");
 150     rc = cib_conn->cmds->update(cib_conn, XML_CIB_TAG_CONSTRAINTS, fragment, cib_options);
 151     rc = pcmk_legacy2rc(rc);
 152 
 153     free_xml(fragment);
 154     free(later_s);
 155     return rc;
 156 }
 157 
 158 // \return Standard Pacemaker return code
 159 int
 160 cli_resource_prefer(pcmk__output_t *out,const char *rsc_id, const char *host,
     /* [previous][next][first][last][top][bottom][index][help] */
 161                     const char *move_lifetime, cib_t * cib_conn, int cib_options,
 162                     gboolean promoted_role_only)
 163 {
 164     char *later_s = parse_cli_lifetime(out, move_lifetime);
 165     int rc = pcmk_rc_ok;
 166     xmlNode *location = NULL;
 167     xmlNode *fragment = NULL;
 168 
 169     if(move_lifetime && later_s == NULL) {
 170         return EINVAL;
 171     }
 172 
 173     if(cib_conn == NULL) {
 174         free(later_s);
 175         return ENOTCONN;
 176     }
 177 
 178     fragment = create_xml_node(NULL, XML_CIB_TAG_CONSTRAINTS);
 179 
 180     location = create_xml_node(fragment, XML_CONS_TAG_RSC_LOCATION);
 181     crm_xml_set_id(location, "cli-prefer-%s", rsc_id);
 182 
 183     crm_xml_add(location, XML_LOC_ATTR_SOURCE, rsc_id);
 184     if(promoted_role_only) {
 185         crm_xml_add(location, XML_RULE_ATTR_ROLE, promoted_role_name());
 186     } else {
 187         crm_xml_add(location, XML_RULE_ATTR_ROLE, RSC_ROLE_STARTED_S);
 188     }
 189 
 190     if (later_s == NULL) {
 191         /* Short form */
 192         crm_xml_add(location, XML_CIB_TAG_NODE, host);
 193         crm_xml_add(location, XML_RULE_ATTR_SCORE, CRM_INFINITY_S);
 194 
 195     } else {
 196         xmlNode *rule = create_xml_node(location, XML_TAG_RULE);
 197         xmlNode *expr = create_xml_node(rule, XML_TAG_EXPRESSION);
 198 
 199         crm_xml_set_id(rule, "cli-prefer-rule-%s", rsc_id);
 200         crm_xml_add(rule, XML_RULE_ATTR_SCORE, CRM_INFINITY_S);
 201         crm_xml_add(rule, XML_RULE_ATTR_BOOLEAN_OP, "and");
 202 
 203         crm_xml_set_id(expr, "cli-prefer-expr-%s", rsc_id);
 204         crm_xml_add(expr, XML_EXPR_ATTR_ATTRIBUTE, CRM_ATTR_UNAME);
 205         crm_xml_add(expr, XML_EXPR_ATTR_OPERATION, "eq");
 206         crm_xml_add(expr, XML_EXPR_ATTR_VALUE, host);
 207         crm_xml_add(expr, XML_EXPR_ATTR_TYPE, "string");
 208 
 209         expr = create_xml_node(rule, "date_expression");
 210         crm_xml_set_id(expr, "cli-prefer-lifetime-end-%s", rsc_id);
 211         crm_xml_add(expr, "operation", "lt");
 212         crm_xml_add(expr, "end", later_s);
 213     }
 214 
 215     crm_log_xml_info(fragment, "Modify");
 216     rc = cib_conn->cmds->update(cib_conn, XML_CIB_TAG_CONSTRAINTS, fragment, cib_options);
 217     rc = pcmk_legacy2rc(rc);
 218 
 219     free_xml(fragment);
 220     free(later_s);
 221     return rc;
 222 }
 223 
 224 /* Nodes can be specified two different ways in the CIB, so we have two different
 225  * functions to try clearing out any constraints on them:
 226  *
 227  * (1) The node could be given by attribute=/value= in an expression XML node.
 228  * That's what resource_clear_node_in_expr handles.  That XML looks like this:
 229  *
 230  * <rsc_location id="cli-prefer-dummy" rsc="dummy" role="Started">
 231  *   <rule id="cli-prefer-rule-dummy" score="INFINITY" boolean-op="and">
 232  *     <expression id="cli-prefer-expr-dummy" attribute="#uname" operation="eq" value="test02" type="string"/>
 233  *     <date_expression id="cli-prefer-lifetime-end-dummy" operation="lt" end="2018-12-12 14:05:37 -05:00"/>
 234  *   </rule>
 235  * </rsc_location>
 236  *
 237  * (2) The mode could be given by node= in an rsc_location XML node.  That's
 238  * what resource_clear_node_in_location handles.  That XML looks like this:
 239  *
 240  * <rsc_location id="cli-prefer-dummy" rsc="dummy" role="Started" node="node1" score="INFINITY"/>
 241  *
 242  * \return Standard Pacemaker return code
 243  */
 244 static int
 245 resource_clear_node_in_expr(const char *rsc_id, const char *host, cib_t * cib_conn,
     /* [previous][next][first][last][top][bottom][index][help] */
 246                             int cib_options)
 247 {
 248     int rc = pcmk_rc_ok;
 249     char *xpath_string = NULL;
 250 
 251     xpath_string = crm_strdup_printf("//rsc_location[@id='cli-prefer-%s'][rule[@id='cli-prefer-rule-%s']/expression[@attribute='#uname' and @value='%s']]",
 252                                      rsc_id, rsc_id, host);
 253 
 254     rc = cib_conn->cmds->remove(cib_conn, xpath_string, NULL, cib_xpath | cib_options);
 255     if (rc == -ENXIO) {
 256         rc = pcmk_rc_ok;
 257     } else {
 258         rc = pcmk_legacy2rc(rc);
 259     }
 260 
 261     free(xpath_string);
 262     return rc;
 263 }
 264 
 265 // \return Standard Pacemaker return code
 266 static int
 267 resource_clear_node_in_location(const char *rsc_id, const char *host, cib_t * cib_conn,
     /* [previous][next][first][last][top][bottom][index][help] */
 268                                 int cib_options, bool clear_ban_constraints, gboolean force)
 269 {
 270     int rc = pcmk_rc_ok;
 271     xmlNode *fragment = NULL;
 272     xmlNode *location = NULL;
 273 
 274     fragment = create_xml_node(NULL, XML_CIB_TAG_CONSTRAINTS);
 275 
 276     if (clear_ban_constraints == TRUE) {
 277         location = create_xml_node(fragment, XML_CONS_TAG_RSC_LOCATION);
 278         crm_xml_set_id(location, "cli-ban-%s-on-%s", rsc_id, host);
 279     }
 280 
 281     location = create_xml_node(fragment, XML_CONS_TAG_RSC_LOCATION);
 282     crm_xml_set_id(location, "cli-prefer-%s", rsc_id);
 283     if (force == FALSE) {
 284         crm_xml_add(location, XML_CIB_TAG_NODE, host);
 285     }
 286 
 287     crm_log_xml_info(fragment, "Delete");
 288     rc = cib_conn->cmds->remove(cib_conn, XML_CIB_TAG_CONSTRAINTS, fragment, cib_options);
 289     if (rc == -ENXIO) {
 290         rc = pcmk_rc_ok;
 291     } else {
 292         rc = pcmk_legacy2rc(rc);
 293     }
 294 
 295     free_xml(fragment);
 296     return rc;
 297 }
 298 
 299 // \return Standard Pacemaker return code
 300 int
 301 cli_resource_clear(const char *rsc_id, const char *host, GList *allnodes, cib_t * cib_conn,
     /* [previous][next][first][last][top][bottom][index][help] */
 302                    int cib_options, bool clear_ban_constraints, gboolean force)
 303 {
 304     int rc = pcmk_rc_ok;
 305 
 306     if(cib_conn == NULL) {
 307         return ENOTCONN;
 308     }
 309 
 310     if (host) {
 311         rc = resource_clear_node_in_expr(rsc_id, host, cib_conn, cib_options);
 312 
 313         /* rc does not tell us whether the previous operation did anything, only
 314          * whether it failed or not.  Thus, as long as it did not fail, we need
 315          * to try the second clear method.
 316          */
 317         if (rc == pcmk_rc_ok) {
 318             rc = resource_clear_node_in_location(rsc_id, host, cib_conn,
 319                                                  cib_options, clear_ban_constraints,
 320                                                  force);
 321         }
 322 
 323     } else {
 324         GList *n = allnodes;
 325 
 326         /* Iterate over all nodes, attempting to clear the constraint from each.
 327          * On the first error, abort.
 328          */
 329         for(; n; n = n->next) {
 330             pe_node_t *target = n->data;
 331 
 332             rc = cli_resource_clear(rsc_id, target->details->uname, NULL,
 333                                     cib_conn, cib_options, clear_ban_constraints,
 334                                     force);
 335             if (rc != pcmk_rc_ok) {
 336                 break;
 337             }
 338         }
 339     }
 340 
 341     return rc;
 342 }
 343 
 344 static char *
 345 build_clear_xpath_string(xmlNode *constraint_node, const char *rsc, const char *node, gboolean promoted_role_only)
     /* [previous][next][first][last][top][bottom][index][help] */
 346 {
 347     int offset = 0;
 348     char *xpath_string = NULL;
 349     char *first_half = NULL;
 350     char *rsc_role_substr = NULL;
 351     char *date_substr = NULL;
 352 
 353     if (pcmk__starts_with(ID(constraint_node), "cli-ban-")) {
 354         date_substr = crm_strdup_printf("//date_expression[@id='%s-lifetime']",
 355                                         ID(constraint_node));
 356 
 357     } else if (pcmk__starts_with(ID(constraint_node), "cli-prefer-")) {
 358         date_substr = crm_strdup_printf("//date_expression[@id='cli-prefer-lifetime-end-%s']",
 359                                         crm_element_value(constraint_node, "rsc"));
 360     } else {
 361         return NULL;
 362     }
 363 
 364     first_half = calloc(1, XPATH_MAX);
 365     offset += snprintf(first_half + offset, XPATH_MAX - offset, "//rsc_location");
 366 
 367     if (node != NULL || rsc != NULL || promoted_role_only == TRUE) {
 368         offset += snprintf(first_half + offset, XPATH_MAX - offset, "[");
 369 
 370         if (node != NULL) {
 371             if (rsc != NULL || promoted_role_only == TRUE) {
 372                 offset += snprintf(first_half + offset, XPATH_MAX - offset, "@node='%s' and ", node);
 373             } else {
 374                 offset += snprintf(first_half + offset, XPATH_MAX - offset, "@node='%s'", node);
 375             }
 376         }
 377 
 378         if (rsc != NULL && promoted_role_only == TRUE) {
 379             rsc_role_substr = crm_strdup_printf("@rsc='%s' and @role='%s'",
 380                                                 rsc, promoted_role_name());
 381             offset += snprintf(first_half + offset, XPATH_MAX - offset,
 382                                "@rsc='%s' and @role='%s']",
 383                                rsc, promoted_role_name());
 384         } else if (rsc != NULL) {
 385             rsc_role_substr = crm_strdup_printf("@rsc='%s'", rsc);
 386             offset += snprintf(first_half + offset, XPATH_MAX - offset, "@rsc='%s']", rsc);
 387         } else if (promoted_role_only == TRUE) {
 388             rsc_role_substr = crm_strdup_printf("@role='%s'",
 389                                                 promoted_role_name());
 390             offset += snprintf(first_half + offset, XPATH_MAX - offset,
 391                                "@role='%s']", promoted_role_name());
 392         } else {
 393             offset += snprintf(first_half + offset, XPATH_MAX - offset, "]");
 394         }
 395     }
 396 
 397     if (node != NULL) {
 398         if (rsc_role_substr != NULL) {
 399             xpath_string = crm_strdup_printf("%s|//rsc_location[%s]/rule[expression[@attribute='#uname' and @value='%s']]%s",
 400                                              first_half, rsc_role_substr, node, date_substr);
 401         } else {
 402             xpath_string = crm_strdup_printf("%s|//rsc_location/rule[expression[@attribute='#uname' and @value='%s']]%s",
 403                                              first_half, node, date_substr);
 404         }
 405     } else {
 406         xpath_string = crm_strdup_printf("%s%s", first_half, date_substr);
 407     }
 408 
 409     free(first_half);
 410     free(date_substr);
 411     free(rsc_role_substr);
 412 
 413     return xpath_string;
 414 }
 415 
 416 // \return Standard Pacemaker return code
 417 int
 418 cli_resource_clear_all_expired(xmlNode *root, cib_t *cib_conn, int cib_options,
     /* [previous][next][first][last][top][bottom][index][help] */
 419                                const char *rsc, const char *node, gboolean promoted_role_only)
 420 {
 421     xmlXPathObject *xpathObj = NULL;
 422     xmlNode *cib_constraints = NULL;
 423     crm_time_t *now = crm_time_new(NULL);
 424     int i;
 425     int rc = pcmk_rc_ok;
 426 
 427     cib_constraints = get_object_root(XML_CIB_TAG_CONSTRAINTS, root);
 428     xpathObj = xpath_search(cib_constraints, "//" XML_CONS_TAG_RSC_LOCATION);
 429 
 430     for (i = 0; i < numXpathResults(xpathObj); i++) {
 431         xmlNode *constraint_node = getXpathResult(xpathObj, i);
 432         xmlNode *date_expr_node = NULL;
 433         crm_time_t *end = NULL;
 434         char *xpath_string = NULL;
 435 
 436         xpath_string = build_clear_xpath_string(constraint_node, rsc, node, promoted_role_only);
 437         if (xpath_string == NULL) {
 438             continue;
 439         }
 440 
 441         date_expr_node = get_xpath_object(xpath_string, constraint_node, LOG_DEBUG);
 442         if (date_expr_node == NULL) {
 443             free(xpath_string);
 444             continue;
 445         }
 446 
 447         /* And then finally, see if the date expression is expired.  If so,
 448          * clear the constraint.
 449          */
 450         end = crm_time_new(crm_element_value(date_expr_node, "end"));
 451 
 452         if (crm_time_compare(now, end) == 1) {
 453             xmlNode *fragment = NULL;
 454             xmlNode *location = NULL;
 455 
 456             fragment = create_xml_node(NULL, XML_CIB_TAG_CONSTRAINTS);
 457             location = create_xml_node(fragment, XML_CONS_TAG_RSC_LOCATION);
 458             crm_xml_set_id(location, "%s", ID(constraint_node));
 459             crm_log_xml_info(fragment, "Delete");
 460 
 461             rc = cib_conn->cmds->remove(cib_conn, XML_CIB_TAG_CONSTRAINTS,
 462                                         fragment, cib_options);
 463             rc = pcmk_legacy2rc(rc);
 464 
 465             if (rc != pcmk_rc_ok) {
 466                 free(xpath_string);
 467                 goto done;
 468             }
 469 
 470             free_xml(fragment);
 471         }
 472 
 473         crm_time_free(end);
 474         free(xpath_string);
 475     }
 476 
 477 done:
 478     freeXpathObject(xpathObj);
 479     crm_time_free(now);
 480     return rc;
 481 }

/* [previous][next][first][last][top][bottom][index][help] */