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,
  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     pcmk__xe_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         pcmk__xe_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         pcmk__xe_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         pcmk__xe_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_sync_call);
 126     rc = pcmk_legacy2rc(rc);
 127 
 128     pcmk__xml_free(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, 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,
 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     pcmk__xe_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         pcmk__xe_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         pcmk__xe_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         pcmk__xe_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_sync_call);
 206     rc = pcmk_legacy2rc(rc);
 207 
 208     pcmk__xml_free(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, 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,
     /* [previous][next][first][last][top][bottom][index][help] */
 249                             cib_t *cib_conn)
 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,
 266                                 cib_xpath|cib_sync_call);
 267     if (rc == -ENXIO) {
 268         rc = pcmk_rc_ok;
 269     } else {
 270         rc = pcmk_legacy2rc(rc);
 271     }
 272 
 273     free(xpath_string);
 274     return rc;
 275 }
 276 
 277 // \return Standard Pacemaker return code
 278 static int
 279 resource_clear_node_in_location(const char *rsc_id, const char *host, cib_t * cib_conn,
     /* [previous][next][first][last][top][bottom][index][help] */
 280                                 bool clear_ban_constraints, gboolean force)
 281 {
 282     int rc = pcmk_rc_ok;
 283     xmlNode *fragment = NULL;
 284     xmlNode *location = NULL;
 285 
 286     fragment = pcmk__xe_create(NULL, PCMK_XE_CONSTRAINTS);
 287 
 288     if (clear_ban_constraints == TRUE) {
 289         location = pcmk__xe_create(fragment, PCMK_XE_RSC_LOCATION);
 290         pcmk__xe_set_id(location, "cli-ban-%s-on-%s", rsc_id, host);
 291     }
 292 
 293     location = pcmk__xe_create(fragment, PCMK_XE_RSC_LOCATION);
 294     pcmk__xe_set_id(location, "cli-prefer-%s", rsc_id);
 295     if (force == FALSE) {
 296         crm_xml_add(location, PCMK_XE_NODE, host);
 297     }
 298 
 299     crm_log_xml_info(fragment, "Delete");
 300     rc = cib_conn->cmds->remove(cib_conn, PCMK_XE_CONSTRAINTS, fragment,
 301                                 cib_sync_call);
 302     if (rc == -ENXIO) {
 303         rc = pcmk_rc_ok;
 304     } else {
 305         rc = pcmk_legacy2rc(rc);
 306     }
 307 
 308     pcmk__xml_free(fragment);
 309     return rc;
 310 }
 311 
 312 // \return Standard Pacemaker return code
 313 int
 314 cli_resource_clear(const char *rsc_id, const char *host, GList *allnodes, cib_t * cib_conn,
     /* [previous][next][first][last][top][bottom][index][help] */
 315                    bool clear_ban_constraints, gboolean force)
 316 {
 317     int rc = pcmk_rc_ok;
 318 
 319     if(cib_conn == NULL) {
 320         return ENOTCONN;
 321     }
 322 
 323     if (host) {
 324         rc = resource_clear_node_in_expr(rsc_id, host, cib_conn);
 325 
 326         /* rc does not tell us whether the previous operation did anything, only
 327          * whether it failed or not.  Thus, as long as it did not fail, we need
 328          * to try the second clear method.
 329          */
 330         if (rc == pcmk_rc_ok) {
 331             rc = resource_clear_node_in_location(rsc_id, host, cib_conn,
 332                                                  clear_ban_constraints, 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->priv->name, NULL,
 345                                     cib_conn, clear_ban_constraints, force);
 346             if (rc != pcmk_rc_ok) {
 347                 break;
 348             }
 349         }
 350     }
 351 
 352     return rc;
 353 }
 354 
 355 static void
 356 build_clear_xpath_string(GString *buf, const xmlNode *constraint_node,
     /* [previous][next][first][last][top][bottom][index][help] */
 357                          const char *rsc, const char *node,
 358                          bool promoted_role_only)
 359 {
 360     const char *cons_id = pcmk__xe_id(constraint_node);
 361     const char *cons_rsc = crm_element_value(constraint_node, PCMK_XA_RSC);
 362     GString *rsc_role_substr = NULL;
 363     const char *promoted_role_rule = "@" PCMK_XA_ROLE "='" PCMK_ROLE_PROMOTED
 364                                      "' or @" PCMK_XA_ROLE "='"
 365                                      PCMK__ROLE_PROMOTED_LEGACY "'";
 366 
 367     pcmk__assert(buf != NULL);
 368     g_string_truncate(buf, 0);
 369 
 370     if (!pcmk__starts_with(cons_id, "cli-ban-")
 371         && !pcmk__starts_with(cons_id, "cli-prefer-")) {
 372         return;
 373     }
 374 
 375     g_string_append(buf, "//" PCMK_XE_RSC_LOCATION);
 376 
 377     if ((node != NULL) || (rsc != NULL) || promoted_role_only) {
 378         g_string_append_c(buf, '[');
 379 
 380         if (node != NULL) {
 381             pcmk__g_strcat(buf, "@" PCMK_XE_NODE "='", node, "'", NULL);
 382 
 383             if (promoted_role_only || (rsc != NULL)) {
 384                 g_string_append(buf, " and ");
 385             }
 386         }
 387 
 388         if ((rsc != NULL) && promoted_role_only) {
 389             rsc_role_substr = g_string_sized_new(64);
 390             pcmk__g_strcat(rsc_role_substr,
 391                            "@" PCMK_XA_RSC "='", rsc, "' "
 392                            "and (" , promoted_role_rule, ")", NULL);
 393 
 394         } else if (rsc != NULL) {
 395             rsc_role_substr = g_string_sized_new(64);
 396             pcmk__g_strcat(rsc_role_substr,
 397                            "@" PCMK_XA_RSC "='", rsc, "'", NULL);
 398 
 399         } else if (promoted_role_only) {
 400             rsc_role_substr = g_string_sized_new(64);
 401             g_string_append(rsc_role_substr, promoted_role_rule);
 402         }
 403 
 404         if (rsc_role_substr != NULL) {
 405             g_string_append(buf, rsc_role_substr->str);
 406         }
 407         g_string_append_c(buf, ']');
 408     }
 409 
 410     if (node != NULL) {
 411         g_string_append(buf, "|//" PCMK_XE_RSC_LOCATION);
 412 
 413         if (rsc_role_substr != NULL) {
 414             pcmk__g_strcat(buf, "[", rsc_role_substr, "]", NULL);
 415         }
 416         pcmk__g_strcat(buf,
 417                        "/" PCMK_XE_RULE "[" PCMK_XE_EXPRESSION
 418                        "[@" PCMK_XA_ATTRIBUTE "='" CRM_ATTR_UNAME "' "
 419                        "and @" PCMK_XA_VALUE "='", node, "']]", NULL);
 420     }
 421 
 422     g_string_append(buf, "//" PCMK_XE_DATE_EXPRESSION "[@" PCMK_XA_ID "='");
 423     if (pcmk__starts_with(cons_id, "cli-ban-")) {
 424         pcmk__g_strcat(buf, cons_id, "-lifetime']", NULL);
 425 
 426     } else {    // starts with "cli-prefer-"
 427         pcmk__g_strcat(buf,
 428                        "cli-prefer-lifetime-end-", cons_rsc, "']", NULL);
 429     }
 430 
 431     if (rsc_role_substr != NULL) {
 432         g_string_free(rsc_role_substr, TRUE);
 433     }
 434 }
 435 
 436 // \return Standard Pacemaker return code
 437 int
 438 cli_resource_clear_all_expired(xmlNode *root, cib_t *cib_conn, const char *rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
 439                                const char *node, gboolean promoted_role_only)
 440 {
 441     GString *buf = NULL;
 442     xmlXPathObject *xpathObj = NULL;
 443     xmlNode *cib_constraints = NULL;
 444     crm_time_t *now = crm_time_new(NULL);
 445     int i;
 446     int rc = pcmk_rc_ok;
 447 
 448     cib_constraints = pcmk_find_cib_element(root, PCMK_XE_CONSTRAINTS);
 449     xpathObj = xpath_search(cib_constraints, "//" PCMK_XE_RSC_LOCATION);
 450 
 451     for (i = 0; i < numXpathResults(xpathObj); i++) {
 452         xmlNode *constraint_node = getXpathResult(xpathObj, i);
 453         xmlNode *date_expr_node = NULL;
 454         crm_time_t *end = NULL;
 455         int rc = pcmk_rc_ok;
 456 
 457         if (buf == NULL) {
 458             buf = g_string_sized_new(1024);
 459         }
 460 
 461         build_clear_xpath_string(buf, constraint_node, rsc, node,
 462                                  promoted_role_only);
 463         if (buf->len == 0) {
 464             continue;
 465         }
 466 
 467         date_expr_node = get_xpath_object((const char *) buf->str,
 468                                           constraint_node, LOG_DEBUG);
 469         if (date_expr_node == NULL) {
 470             continue;
 471         }
 472 
 473         /* And then finally, see if the date expression is expired.  If so,
 474          * clear the constraint.
 475          */
 476         rc = pcmk__xe_get_datetime(date_expr_node, PCMK_XA_END, &end);
 477         if (rc != pcmk_rc_ok) {
 478             crm_trace("Date expression %s has invalid " PCMK_XA_END ": %s",
 479                       pcmk__s(pcmk__xe_id(date_expr_node), "without ID"),
 480                       pcmk_rc_str(rc));
 481             continue; // Treat as unexpired
 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             pcmk__xe_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_sync_call);
 495             rc = pcmk_legacy2rc(rc);
 496 
 497             if (rc != pcmk_rc_ok) {
 498                 goto done;
 499             }
 500 
 501             pcmk__xml_free(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] */