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. cli_resource_ban
  3. cli_resource_prefer
  4. resource_clear_node_in_expr
  5. resource_clear_node_in_location
  6. cli_resource_clear
  7. build_clear_xpath_string
  8. cli_resource_clear_all_expired

   1 /*
   2  * Copyright 2004-2025 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 <libxml/tree.h>    // xmlNode
  13 #include <libxml/xpath.h>   // xmlXPathObject, etc.
  14 
  15 #include <crm_resource.h>
  16 
  17 static char *
  18 parse_cli_lifetime(pcmk__output_t *out, const char *move_lifetime)
     /* [previous][next][first][last][top][bottom][index][help] */
  19 {
  20     char *later_s = NULL;
  21     crm_time_t *now = NULL;
  22     crm_time_t *later = NULL;
  23     crm_time_t *duration = NULL;
  24 
  25     if (move_lifetime == NULL) {
  26         return NULL;
  27     }
  28 
  29     duration = crm_time_parse_duration(move_lifetime);
  30     if (duration == NULL) {
  31         out->err(out, "Invalid duration specified: %s\n"
  32                       "Please refer to https://en.wikipedia.org/wiki/ISO_8601#Durations "
  33                       "for examples of valid durations", move_lifetime);
  34         return NULL;
  35     }
  36 
  37     now = crm_time_new(NULL);
  38     later = crm_time_add(now, duration);
  39     if (later == NULL) {
  40         out->err(out, "Unable to add %s to current time\n"
  41                       "Please report to " PACKAGE_BUGREPORT " as possible bug",
  42                       move_lifetime);
  43         crm_time_free(now);
  44         crm_time_free(duration);
  45         return NULL;
  46     }
  47 
  48     crm_time_log(LOG_INFO, "now     ", now,
  49                  crm_time_log_date | crm_time_log_timeofday | crm_time_log_with_timezone);
  50     crm_time_log(LOG_INFO, "later   ", later,
  51                  crm_time_log_date | crm_time_log_timeofday | crm_time_log_with_timezone);
  52     crm_time_log(LOG_INFO, "duration", duration, crm_time_log_date | crm_time_log_timeofday);
  53     later_s = crm_time_as_string(later, crm_time_log_date | crm_time_log_timeofday | crm_time_log_with_timezone);
  54     out->info(out, "Migration will take effect until: %s", later_s);
  55 
  56     crm_time_free(duration);
  57     crm_time_free(later);
  58     crm_time_free(now);
  59     return later_s;
  60 }
  61 
  62 // \return Standard Pacemaker return code
  63 int
  64 cli_resource_ban(pcmk__output_t *out, const char *rsc_id, const char *host,
     /* [previous][next][first][last][top][bottom][index][help] */
  65                  const char *move_lifetime, cib_t *cib_conn,
  66                  gboolean promoted_role_only, const char *promoted_role)
  67 {
  68     char *later_s = NULL;
  69     int rc = pcmk_rc_ok;
  70     xmlNode *fragment = NULL;
  71     xmlNode *location = NULL;
  72 
  73     later_s = parse_cli_lifetime(out, move_lifetime);
  74     if(move_lifetime && later_s == NULL) {
  75         return EINVAL;
  76     }
  77 
  78     fragment = pcmk__xe_create(NULL, PCMK_XE_CONSTRAINTS);
  79 
  80     location = pcmk__xe_create(fragment, PCMK_XE_RSC_LOCATION);
  81     pcmk__xe_set_id(location, "cli-ban-%s-on-%s", rsc_id, host);
  82 
  83     out->info(out,
  84               "WARNING: Creating " PCMK_XE_RSC_LOCATION " constraint '%s' with "
  85               "a score of " PCMK_VALUE_MINUS_INFINITY " for resource %s on %s."
  86               "\n\tThis will prevent %s from %s on %s until the constraint is "
  87               "removed using the clear option or by editing the CIB with an "
  88               "appropriate tool.\n"
  89               "\tThis will be the case even if %s is the last node in the "
  90               "cluster",
  91               pcmk__xe_id(location), rsc_id, host, rsc_id,
  92               (promoted_role_only? "being promoted" : "running"), host, host);
  93 
  94     crm_xml_add(location, PCMK_XA_RSC, rsc_id);
  95     if(promoted_role_only) {
  96         crm_xml_add(location, PCMK_XA_ROLE, promoted_role);
  97     } else {
  98         crm_xml_add(location, PCMK_XA_ROLE, PCMK_ROLE_STARTED);
  99     }
 100 
 101     if (later_s == NULL) {
 102         /* Short form */
 103         crm_xml_add(location, PCMK_XE_NODE, host);
 104         crm_xml_add(location, PCMK_XA_SCORE, PCMK_VALUE_MINUS_INFINITY);
 105 
 106     } else {
 107         xmlNode *rule = pcmk__xe_create(location, PCMK_XE_RULE);
 108         xmlNode *expr = pcmk__xe_create(rule, PCMK_XE_EXPRESSION);
 109 
 110         pcmk__xe_set_id(rule, "cli-ban-%s-on-%s-rule", rsc_id, host);
 111         crm_xml_add(rule, PCMK_XA_SCORE, PCMK_VALUE_MINUS_INFINITY);
 112         crm_xml_add(rule, PCMK_XA_BOOLEAN_OP, PCMK_VALUE_AND);
 113 
 114         pcmk__xe_set_id(expr, "cli-ban-%s-on-%s-expr", rsc_id, host);
 115         crm_xml_add(expr, PCMK_XA_ATTRIBUTE, CRM_ATTR_UNAME);
 116         crm_xml_add(expr, PCMK_XA_OPERATION, PCMK_VALUE_EQ);
 117         crm_xml_add(expr, PCMK_XA_VALUE, host);
 118         crm_xml_add(expr, PCMK_XA_TYPE, PCMK_VALUE_STRING);
 119 
 120         expr = pcmk__xe_create(rule, PCMK_XE_DATE_EXPRESSION);
 121         pcmk__xe_set_id(expr, "cli-ban-%s-on-%s-lifetime", rsc_id, host);
 122         crm_xml_add(expr, PCMK_XA_OPERATION, PCMK_VALUE_LT);
 123         crm_xml_add(expr, PCMK_XA_END, later_s);
 124     }
 125 
 126     crm_log_xml_notice(fragment, "Modify");
 127     rc = cib_conn->cmds->modify(cib_conn, PCMK_XE_CONSTRAINTS, fragment,
 128                                 cib_sync_call);
 129     rc = pcmk_legacy2rc(rc);
 130 
 131     pcmk__xml_free(fragment);
 132     free(later_s);
 133 
 134     if ((rc != pcmk_rc_ok)
 135         && promoted_role_only
 136         && (strcmp(promoted_role, PCMK_ROLE_PROMOTED) == 0)) {
 137 
 138         int banrc = cli_resource_ban(out, rsc_id, host, move_lifetime,
 139                                      cib_conn, promoted_role_only,
 140                                      PCMK__ROLE_PROMOTED_LEGACY);
 141         if (banrc == pcmk_rc_ok) {
 142             rc = banrc;
 143         }
 144     }
 145 
 146     return rc;
 147 }
 148 
 149 // \return Standard Pacemaker return code
 150 int
 151 cli_resource_prefer(pcmk__output_t *out,const char *rsc_id, const char *host,
     /* [previous][next][first][last][top][bottom][index][help] */
 152                     const char *move_lifetime, cib_t *cib_conn,
 153                     gboolean promoted_role_only, const char *promoted_role)
 154 {
 155     char *later_s = parse_cli_lifetime(out, move_lifetime);
 156     int rc = pcmk_rc_ok;
 157     xmlNode *location = NULL;
 158     xmlNode *fragment = NULL;
 159 
 160     if(move_lifetime && later_s == NULL) {
 161         return EINVAL;
 162     }
 163 
 164     if(cib_conn == NULL) {
 165         free(later_s);
 166         return ENOTCONN;
 167     }
 168 
 169     fragment = pcmk__xe_create(NULL, PCMK_XE_CONSTRAINTS);
 170 
 171     location = pcmk__xe_create(fragment, PCMK_XE_RSC_LOCATION);
 172     pcmk__xe_set_id(location, "cli-prefer-%s", rsc_id);
 173 
 174     crm_xml_add(location, PCMK_XA_RSC, rsc_id);
 175     if(promoted_role_only) {
 176         crm_xml_add(location, PCMK_XA_ROLE, promoted_role);
 177     } else {
 178         crm_xml_add(location, PCMK_XA_ROLE, PCMK_ROLE_STARTED);
 179     }
 180 
 181     if (later_s == NULL) {
 182         /* Short form */
 183         crm_xml_add(location, PCMK_XE_NODE, host);
 184         crm_xml_add(location, PCMK_XA_SCORE, PCMK_VALUE_INFINITY);
 185 
 186     } else {
 187         xmlNode *rule = pcmk__xe_create(location, PCMK_XE_RULE);
 188         xmlNode *expr = pcmk__xe_create(rule, PCMK_XE_EXPRESSION);
 189 
 190         pcmk__xe_set_id(rule, "cli-prefer-rule-%s", rsc_id);
 191         crm_xml_add(rule, PCMK_XA_SCORE, PCMK_VALUE_INFINITY);
 192         crm_xml_add(rule, PCMK_XA_BOOLEAN_OP, PCMK_VALUE_AND);
 193 
 194         pcmk__xe_set_id(expr, "cli-prefer-expr-%s", rsc_id);
 195         crm_xml_add(expr, PCMK_XA_ATTRIBUTE, CRM_ATTR_UNAME);
 196         crm_xml_add(expr, PCMK_XA_OPERATION, PCMK_VALUE_EQ);
 197         crm_xml_add(expr, PCMK_XA_VALUE, host);
 198         crm_xml_add(expr, PCMK_XA_TYPE, PCMK_VALUE_STRING);
 199 
 200         expr = pcmk__xe_create(rule, PCMK_XE_DATE_EXPRESSION);
 201         pcmk__xe_set_id(expr, "cli-prefer-lifetime-end-%s", rsc_id);
 202         crm_xml_add(expr, PCMK_XA_OPERATION, PCMK_VALUE_LT);
 203         crm_xml_add(expr, PCMK_XA_END, later_s);
 204     }
 205 
 206     crm_log_xml_info(fragment, "Modify");
 207     rc = cib_conn->cmds->modify(cib_conn, PCMK_XE_CONSTRAINTS, fragment,
 208                                 cib_sync_call);
 209     rc = pcmk_legacy2rc(rc);
 210 
 211     pcmk__xml_free(fragment);
 212     free(later_s);
 213 
 214     if ((rc != pcmk_rc_ok)
 215         && promoted_role_only
 216         && (strcmp(promoted_role, PCMK_ROLE_PROMOTED) == 0)) {
 217 
 218         int preferrc = cli_resource_prefer(out, rsc_id, host, move_lifetime,
 219                                            cib_conn, promoted_role_only,
 220                                            PCMK__ROLE_PROMOTED_LEGACY);
 221         if (preferrc == pcmk_rc_ok) {
 222             rc = preferrc;
 223         }
 224     }
 225 
 226     return rc;
 227 }
 228 
 229 /* Nodes can be specified two different ways in the CIB, so we have two different
 230  * functions to try clearing out any constraints on them:
 231  *
 232  * (1) The node could be given by attribute=/value= in an expression XML node.
 233  * That's what resource_clear_node_in_expr handles.  That XML looks like this:
 234  *
 235  * <rsc_location id="cli-prefer-dummy" rsc="dummy" role="Started">
 236  *   <rule id="cli-prefer-rule-dummy" score="INFINITY" boolean-op="and">
 237  *     <expression id="cli-prefer-expr-dummy" attribute="#uname" operation="eq" value="test02" type="string"/>
 238  *     <date_expression id="cli-prefer-lifetime-end-dummy" operation="lt" end="2018-12-12 14:05:37 -05:00"/>
 239  *   </rule>
 240  * </rsc_location>
 241  *
 242  * (2) The node could be given by node= in a PCMK_XE_RSC_LOCATION XML node.
 243  * That's what resource_clear_node_in_location handles. That XML looks like
 244  * this:
 245  *
 246  * <rsc_location id="cli-prefer-dummy" rsc="dummy" role="Started" node="node1" score="INFINITY"/>
 247  *
 248  * \return Standard Pacemaker return code
 249  */
 250 static int
 251 resource_clear_node_in_expr(const char *rsc_id, const char *host,
     /* [previous][next][first][last][top][bottom][index][help] */
 252                             cib_t *cib_conn)
 253 {
 254     int rc = pcmk_rc_ok;
 255     char *xpath_string = NULL;
 256 
 257 #define XPATH_FMT                                                   \
 258     "//" PCMK_XE_RSC_LOCATION "[@" PCMK_XA_ID "='cli-prefer-%s']"   \
 259     "[" PCMK_XE_RULE                                                \
 260         "[@" PCMK_XA_ID "='cli-prefer-rule-%s']"                    \
 261         "/" PCMK_XE_EXPRESSION                                      \
 262         "[@" PCMK_XA_ATTRIBUTE "='" CRM_ATTR_UNAME "' "             \
 263         "and @" PCMK_XA_VALUE "='%s']"                              \
 264     "]"
 265 
 266     xpath_string = crm_strdup_printf(XPATH_FMT, rsc_id, rsc_id, host);
 267 
 268     rc = cib_conn->cmds->remove(cib_conn, xpath_string, NULL,
 269                                 cib_xpath|cib_sync_call);
 270     if (rc == -ENXIO) {
 271         rc = pcmk_rc_ok;
 272     } else {
 273         rc = pcmk_legacy2rc(rc);
 274     }
 275 
 276     free(xpath_string);
 277     return rc;
 278 }
 279 
 280 // \return Standard Pacemaker return code
 281 static int
 282 resource_clear_node_in_location(const char *rsc_id, const char *host, cib_t * cib_conn,
     /* [previous][next][first][last][top][bottom][index][help] */
 283                                 bool clear_ban_constraints, gboolean force)
 284 {
 285     int rc = pcmk_rc_ok;
 286     xmlNode *fragment = NULL;
 287     xmlNode *location = NULL;
 288 
 289     fragment = pcmk__xe_create(NULL, PCMK_XE_CONSTRAINTS);
 290 
 291     if (clear_ban_constraints == TRUE) {
 292         location = pcmk__xe_create(fragment, PCMK_XE_RSC_LOCATION);
 293         pcmk__xe_set_id(location, "cli-ban-%s-on-%s", rsc_id, host);
 294     }
 295 
 296     location = pcmk__xe_create(fragment, PCMK_XE_RSC_LOCATION);
 297     pcmk__xe_set_id(location, "cli-prefer-%s", rsc_id);
 298     if (force == FALSE) {
 299         crm_xml_add(location, PCMK_XE_NODE, host);
 300     }
 301 
 302     crm_log_xml_info(fragment, "Delete");
 303     rc = cib_conn->cmds->remove(cib_conn, PCMK_XE_CONSTRAINTS, fragment,
 304                                 cib_sync_call);
 305     if (rc == -ENXIO) {
 306         rc = pcmk_rc_ok;
 307     } else {
 308         rc = pcmk_legacy2rc(rc);
 309     }
 310 
 311     pcmk__xml_free(fragment);
 312     return rc;
 313 }
 314 
 315 // \return Standard Pacemaker return code
 316 int
 317 cli_resource_clear(const char *rsc_id, const char *host, GList *allnodes, cib_t * cib_conn,
     /* [previous][next][first][last][top][bottom][index][help] */
 318                    bool clear_ban_constraints, gboolean force)
 319 {
 320     int rc = pcmk_rc_ok;
 321 
 322     if(cib_conn == NULL) {
 323         return ENOTCONN;
 324     }
 325 
 326     if (host) {
 327         rc = resource_clear_node_in_expr(rsc_id, host, cib_conn);
 328 
 329         /* rc does not tell us whether the previous operation did anything, only
 330          * whether it failed or not.  Thus, as long as it did not fail, we need
 331          * to try the second clear method.
 332          */
 333         if (rc == pcmk_rc_ok) {
 334             rc = resource_clear_node_in_location(rsc_id, host, cib_conn,
 335                                                  clear_ban_constraints, force);
 336         }
 337 
 338     } else {
 339         GList *n = allnodes;
 340 
 341         /* Iterate over all nodes, attempting to clear the constraint from each.
 342          * On the first error, abort.
 343          */
 344         for(; n; n = n->next) {
 345             pcmk_node_t *target = n->data;
 346 
 347             rc = cli_resource_clear(rsc_id, target->priv->name, NULL,
 348                                     cib_conn, clear_ban_constraints, force);
 349             if (rc != pcmk_rc_ok) {
 350                 break;
 351             }
 352         }
 353     }
 354 
 355     return rc;
 356 }
 357 
 358 static void
 359 build_clear_xpath_string(GString *buf, const xmlNode *constraint_node,
     /* [previous][next][first][last][top][bottom][index][help] */
 360                          const char *rsc, const char *node,
 361                          bool promoted_role_only)
 362 {
 363     const char *cons_id = pcmk__xe_id(constraint_node);
 364     const char *cons_rsc = crm_element_value(constraint_node, PCMK_XA_RSC);
 365     GString *rsc_role_substr = NULL;
 366     const char *promoted_role_rule = "@" PCMK_XA_ROLE "='" PCMK_ROLE_PROMOTED
 367                                      "' or @" PCMK_XA_ROLE "='"
 368                                      PCMK__ROLE_PROMOTED_LEGACY "'";
 369 
 370     pcmk__assert(buf != NULL);
 371     g_string_truncate(buf, 0);
 372 
 373     if (!pcmk__starts_with(cons_id, "cli-ban-")
 374         && !pcmk__starts_with(cons_id, "cli-prefer-")) {
 375         return;
 376     }
 377 
 378     g_string_append(buf, "//" PCMK_XE_RSC_LOCATION);
 379 
 380     if ((node != NULL) || (rsc != NULL) || promoted_role_only) {
 381         g_string_append_c(buf, '[');
 382 
 383         if (node != NULL) {
 384             pcmk__g_strcat(buf, "@" PCMK_XE_NODE "='", node, "'", NULL);
 385 
 386             if (promoted_role_only || (rsc != NULL)) {
 387                 g_string_append(buf, " and ");
 388             }
 389         }
 390 
 391         if ((rsc != NULL) && promoted_role_only) {
 392             rsc_role_substr = g_string_sized_new(64);
 393             pcmk__g_strcat(rsc_role_substr,
 394                            "@" PCMK_XA_RSC "='", rsc, "' "
 395                            "and (" , promoted_role_rule, ")", NULL);
 396 
 397         } else if (rsc != NULL) {
 398             rsc_role_substr = g_string_sized_new(64);
 399             pcmk__g_strcat(rsc_role_substr,
 400                            "@" PCMK_XA_RSC "='", rsc, "'", NULL);
 401 
 402         } else if (promoted_role_only) {
 403             rsc_role_substr = g_string_sized_new(64);
 404             g_string_append(rsc_role_substr, promoted_role_rule);
 405         }
 406 
 407         if (rsc_role_substr != NULL) {
 408             g_string_append(buf, rsc_role_substr->str);
 409         }
 410         g_string_append_c(buf, ']');
 411     }
 412 
 413     if (node != NULL) {
 414         g_string_append(buf, "|//" PCMK_XE_RSC_LOCATION);
 415 
 416         if (rsc_role_substr != NULL) {
 417             pcmk__g_strcat(buf, "[", rsc_role_substr, "]", NULL);
 418         }
 419         pcmk__g_strcat(buf,
 420                        "/" PCMK_XE_RULE "[" PCMK_XE_EXPRESSION
 421                        "[@" PCMK_XA_ATTRIBUTE "='" CRM_ATTR_UNAME "' "
 422                        "and @" PCMK_XA_VALUE "='", node, "']]", NULL);
 423     }
 424 
 425     g_string_append(buf, "//" PCMK_XE_DATE_EXPRESSION "[@" PCMK_XA_ID "='");
 426     if (pcmk__starts_with(cons_id, "cli-ban-")) {
 427         pcmk__g_strcat(buf, cons_id, "-lifetime']", NULL);
 428 
 429     } else {    // starts with "cli-prefer-"
 430         pcmk__g_strcat(buf,
 431                        "cli-prefer-lifetime-end-", cons_rsc, "']", NULL);
 432     }
 433 
 434     if (rsc_role_substr != NULL) {
 435         g_string_free(rsc_role_substr, TRUE);
 436     }
 437 }
 438 
 439 // \return Standard Pacemaker return code
 440 int
 441 cli_resource_clear_all_expired(xmlNode *root, cib_t *cib_conn, const char *rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
 442                                const char *node, gboolean promoted_role_only)
 443 {
 444     GString *buf = NULL;
 445     xmlXPathObject *xpathObj = NULL;
 446     xmlNode *cib_constraints = NULL;
 447     crm_time_t *now = crm_time_new(NULL);
 448     int num_results = 0;
 449     int rc = pcmk_rc_ok;
 450 
 451     cib_constraints = pcmk_find_cib_element(root, PCMK_XE_CONSTRAINTS);
 452     xpathObj = pcmk__xpath_search(cib_constraints->doc,
 453                                   "//" PCMK_XE_RSC_LOCATION);
 454     num_results = pcmk__xpath_num_results(xpathObj);
 455 
 456     for (int i = 0; i < num_results; i++) {
 457         xmlNode *constraint_node = pcmk__xpath_result(xpathObj, i);
 458         xmlNode *date_expr_node = NULL;
 459         crm_time_t *end = NULL;
 460         int rc = pcmk_rc_ok;
 461 
 462         if (constraint_node == NULL) {
 463             continue;
 464         }
 465 
 466         if (buf == NULL) {
 467             buf = g_string_sized_new(1024);
 468         }
 469 
 470         build_clear_xpath_string(buf, constraint_node, rsc, node,
 471                                  promoted_role_only);
 472         if (buf->len == 0) {
 473             continue;
 474         }
 475 
 476         date_expr_node = pcmk__xpath_find_one(constraint_node->doc, buf->str,
 477                                               LOG_DEBUG);
 478         if (date_expr_node == NULL) {
 479             continue;
 480         }
 481 
 482         /* And then finally, see if the date expression is expired.  If so,
 483          * clear the constraint.
 484          */
 485         rc = pcmk__xe_get_datetime(date_expr_node, PCMK_XA_END, &end);
 486         if (rc != pcmk_rc_ok) {
 487             crm_trace("Date expression %s has invalid " PCMK_XA_END ": %s",
 488                       pcmk__s(pcmk__xe_id(date_expr_node), "without ID"),
 489                       pcmk_rc_str(rc));
 490             continue; // Treat as unexpired
 491         }
 492 
 493         if (crm_time_compare(now, end) == 1) {
 494             xmlNode *fragment = NULL;
 495             xmlNode *location = NULL;
 496 
 497             fragment = pcmk__xe_create(NULL, PCMK_XE_CONSTRAINTS);
 498             location = pcmk__xe_create(fragment, PCMK_XE_RSC_LOCATION);
 499             pcmk__xe_set_id(location, "%s", pcmk__xe_id(constraint_node));
 500             crm_log_xml_info(fragment, "Delete");
 501 
 502             rc = cib_conn->cmds->remove(cib_conn, PCMK_XE_CONSTRAINTS, fragment,
 503                                         cib_sync_call);
 504             rc = pcmk_legacy2rc(rc);
 505 
 506             if (rc != pcmk_rc_ok) {
 507                 goto done;
 508             }
 509 
 510             pcmk__xml_free(fragment);
 511         }
 512 
 513         crm_time_free(end);
 514     }
 515 
 516 done:
 517     if (buf != NULL) {
 518         g_string_free(buf, TRUE);
 519     }
 520     xmlXPathFreeObject(xpathObj);
 521     crm_time_free(now);
 522     return rc;
 523 }

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