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

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