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

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