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

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